Releases: huuthuan-nguyen/dsh-knowcode
Release list
v0.1.18 — Crash fix, settled first query, and an idle budget
Consolidates every change since v0.1.14. One of them crashed the indexer on a real Python codebase, so upgrading is strongly recommended.
A Python import could crash the daemon and wedge a workspace
Reported against a real project: every KnowCode call there failed. The daemon log showed it starting, then dying during the first index:
[KnowCode Serve Error]: SyntaxError: Invalid regular expression: /\b(\b/: Unterminated group
at CodeParser.findLibraryAliases
Three defects behind it:
- A specifier was interpolated into a
RegExp. The alias inference builtnew RegExp('\b' + binding + '\b')from an import specifier. from x import (— the ordinary parenthesised multi-line Python import — was read as one line, so the only "specifier" was the(itself, producing the invalid pattern. That syntax appeared in seven files of the affected project.- One bad file could abort everything. The exception propagated out of
serve, taking the index and the daemon with it, and leftdaemon.jsonandserve.lockbehind — so every later attempt reclaimed a dead guard and failed the same way, making one file look like a permanently broken workspace.
Fixed: whole-identifier matching no longer uses a RegExp at all; parenthesised imports are gathered to their closing paren with as aliases stripped and only identifiers kept; and the per-file work is guarded in the full index, the incremental reindex and the watcher batch, so a file is skipped and logged (skippedFiles appears in the index summary). serve now stops the daemon when a reconcile fails, leaving nothing half-served.
Verified on the affected repository: 5234 files parsed, 0 exceptions, and a full index of its 348 files completes with 279 code files, 1418 symbols and 9466 call edges.
The first query no longer runs against a half-built index
A daemon answers /status as soon as its port binds, which is before it reconciles the graph, and auto-start only waited for an answer. Measured on a 300-file fixture: the first call reported 120 files / 240 symbols, the second 125 / 250, and only after six seconds 300 / 600. Every exploring tool answered from a fraction of the workspace, authoritatively.
Clients now wait for /status to report indexing: false before the first call returns. isIndexing covers the whole startup reconcile, so /status can report it — and performFullIndex shares the run already in flight instead of throwing Indexing already in progress., which previously surfaced to the agent as [KnowCode Error: …] when sync arrived mid-startup.
The call that starts a daemon now says so, once:
> ⚙️ KnowCode daemon auto-started for `/path/to/workspace` — indexed 300 files, 600 symbols in 0.5s.
If the wait exceeds its budget the note says the counts may be incomplete instead, so a partial answer is never presented as a settled one.
An idle daemon can let go
Daemons are spawned per workspace on first use and otherwise live until the harness exits, so a long session touching many workspaces keeps one daemon, watcher and embedded database for each. New idleTimeoutMinutes (default 0, disabled) bounds that; any contact — a tool call or a watcher event — resets the budget, and an idle timer never fires mid-index. serve gained -i, --idle-timeout <minutes>.
Restarting is cheap: the database persists and the stale check skips unchanged files. Verified that a self-stopping daemon also stops its embedded FalkorDB child.
Smaller fixes
- A stale guard that could not be removed claimed a live daemon. On a read-only data directory the failure to delete a dead guard was swallowed, so the error named a pid that had been gone for hours. It now says the guard is stale and names the file to delete.
- ORM models were read out of any file.
parseMongoSchemascans content with a regex, so a Mongoose schema written inside a test fixture string was ingested as a real collection. It now runs only for a file whose imports name an ORM.
Documentation
New "Which command do I need?" section in CLI Usage. serve indexes the workspace itself — index is optional — with a table of what serve does for each workspace state and a table of which command suits which goal. It also warns that the stale check compares content hashes and therefore cannot detect a graph built by an older parser, which is what --force-index is for. Both command entries say so too, and knowcode index --help marks the command optional.
Compatibility
No runtime dependency on any @deepseek-ai/* package: tool definitions are plain objects, every schema is standard JSON Schema, and Config is a hand-written Standard Schema v1 validator.
Known harness defect — not caused by this plugin:
Cannot read properties of undefined (reading 'prepare')aborts every tool call on harnessv0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a privateSymbol()rather thanSymbol.for(). See Compatibility & Troubleshooting in the README for workarounds.
Upgrading
Restart any running daemon so it loads the new code, and rebuild the graph — a workspace whose index was built by an older parser keeps a copy that the stale check cannot recognise as outdated. Delete <dataDir>/falkordb.rdb, or run knowcode serve . --force-index.
Tests
69 suites, covering the tool catalog end to end, parser robustness against awkward inputs, the settled-index guarantee, the idle budget, database exclusion, and the schema and tool-output contracts.
Full Changelog: v0.1.14...v0.1.18
v0.1.14 — Tool correctness, database safety, daemon lifecycle
This release consolidates everything since v0.1.9. Several defects made the plugin unusable or silently wrong, so upgrading is strongly recommended.
Tools were failing or quietly wrong
INVALID_TOOL_OUTPUTon every call. The dispatcher returned an undeclaredrawfield against an output schema withadditionalProperties: false, so the harness rejected every response before rendering it.- The call graph lost every call written on its declaration's line.
export function beta() { return gamma(); }produced no call, which leftcallers,callees,blast_radius(reporting "no callers" for a function sitting inside a call cycle),call_path(reporting no path across an obvious two-hop chain) andgit_diff_impact(zero callers at risk) all silently empty. - Static methods had no symbol at all, so
CodeParser,StorageParser,DocParserandFalkorDBManagercontributed a handful of symbols where they have dozens. - A regex literal with unbalanced braces hid a whole class body.
/[^{\n]*\{([\s\S]*?)\}/opens two braces and closes one; the class-body depth drifted and every later method vanished. - Interface members were read as calls, producing a phantom
Shape -> areaedge that ranked a declaration as a hub symbol. - Query strings and comments were mined for calls. A Cypher fragment became a call to
MATCH; a note reading "Comma-delimited (and wrapped)…" became a call todelimited. - Interface member signatures are now indexed, which gives a generated trait real methods and lets an implementing method be recognised as satisfying a contract.
A new suite exercises all 25 tools against a fixture whose correct answer is known in advance, so a tool returning plausible-looking text still fails.
Graph accuracy
On this repository: 326 symbols / 2163 call edges → 630 / 1401, with no real declaration lost and no phantom symbol, duplicate node or bogus import remaining.
Fixed along the way: declarations whose parameters wrap were not indexed; endLine always equalled the signature line; braces in a parameter default or return type truncated a body; Python class scope leaked; imports produced zero :IMPORTS edges; relative imports of compiled lib/ output never linked back to src/; schema knowledge had no data source at all, so the contract-to-storage and migration-impact tools could only answer "not found"; parseElasticMapping invented an index for any JSON file.
Ingestion, imports and documentation
- Concurrent ingestion duplicated everything. Four overlapping calls for one file produced 48 symbols and 4
Filenodes instead of 3 and 1. Ingestion is serialized per path, and deletion removes orphans by property. - Imports now resolve properly, including the build-to-source mapping for compiled output (
../lib/x.js→src/x.ts). That turned 0 local import edges into 85 and made affected-test discovery work. - Documentation links use real references. Scanning prose for symbol names linked
Tracer.linefrom the phrase "one line per phase" andwalk/clean/startfrom ordinary words; a feature flow opened with test helpers. Only backticked identifiers,name()mentions and PascalCase names count now. knowledge_docresolves a bare file name as well as a full path, and the blueprint renders the traits it counts instead of only counting them.
Databases are never read
Three ordered gates, applied before any content is read and shared by discovery, the stale check and the file watcher:
- an extension allowlist — code, documentation and schema text only, so every extensionless data file is out of scope by construction;
- a non-text denylist naming each engine's artifacts: SQLite (
.db,.db3,.sqlite,.sqlite3,-wal/-shm/-journal), Redis (.rdb,.aof), MySQL/MariaDB (.ibd,.frm,.myd,.myi), MongoDB/WiredTiger (.wt,.bson,.ns), Elasticsearch/Lucene (.cfs,.cfe,.si,.fdt,.fdx, …), LevelDB/RocksDB (.sst,.ldb), DuckDB, archives, media and compiled objects; - a NUL-byte sniff of the first 512 bytes, git's own heuristic, which catches a database dumped behind an allowed name.
Schema knowledge is then read only from text committed to the repository: .sql/.cql migrations, Prisma schemas, Mongoose models in .ts/.js, .proto contracts, .xsd schemas, GraphQL SDL, OpenAPI documents and Elasticsearch mappings. A running database is never contacted.
Daemon lifecycle
- No more orphaned
knowcode serve. Daemons are spawned detached, and nothing used to stop them: quitting the harness left one running per project. The plugin now stops the daemons it spawned from its disposal effect, which the harness runs onSIGINT/SIGTERM. A pid is signalled only while itsdaemon.jsonstill names it, so a recycled pid is never mistaken for ours, and a daemon you started by hand is never touched.SIGKILLskips disposal and remains the one case that orphans. - One daemon per workspace, guarded by a
serve.lockclaimed with an exclusive create. A secondserveexits with a notice, and a guard left by a crashed daemon is reclaimed. autoStartDaemonnow works. It was declared and documented but never read, so every tool answered with a "runknowcode serve ." notice until a daemon was started by hand.maxFileSizeis enforced before a file is read, in the indexer and the watcher. It was previously a documented no-op that could not even reach the daemon.- A second workspace no longer dies with
EADDRINUSE, a failed start no longer leaks the embedded FalkorDB process, a client refuses a daemon serving a different workspace, andservewaits for watcher readiness before reconciling.
Compatibility
No runtime dependency on any @deepseek-ai/* package: tool definitions are plain objects, every schema is standard JSON Schema, and Config is a hand-written Standard Schema v1 validator.
Known harness defect — not caused by this plugin:
Cannot read properties of undefined (reading 'prepare')aborts every tool call on harnessv0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a privateSymbol()rather thanSymbol.for(). See Compatibility & Troubleshooting in the README for workarounds.
Upgrading
Installing from this repository or a DSH profile link. Restart any running daemon so it loads the new code, and re-index: a graph built by an older parser is not repaired by the stale check, which only compares file hashes. Deleting <dataDir>/falkordb.rdb, or running knowcode serve . --force-index, forces a clean rebuild.
Tests
63 suites covering the tool catalog end to end, the schema and tool-output contracts, graph integrity, daemon lifecycle, database exclusion and [trace] logging.
Full Changelog: v0.1.9...v0.1.14
v0.1.9 — Consolidated reliability release
This release folds in every fix since v0.1.2. Two of them left the plugin unusable or silently wrong, so upgrading is strongly recommended.
Critical: tools no longer fail with INVALID_TOOL_OUTPUT
Every KnowCode tool returned an error instead of a result:
tool "knowcode_check_index_health_and_stats" returned invalid output:
"value.raw" is not a declared property (additionalProperties: false)
The dispatcher attached a raw payload that no caller ever read, while the declared output schema sets additionalProperties: false — so the harness rejected every response before rendering it. The field is gone, and a new suite now validates every return branch of every action against the schema using the harness's own validateJsonSchemaValue.
Graph accuracy: eight silent defects
The AST graph was quietly inventing symbols and missing real ones. On this repository the index went from 326 symbols / 2163 call edges to 210 / 1062, with no real declaration lost — cross-checked against an independent count of the source (55 keyword functions + 14 arrow functions = 69 function symbols, 10 classes, 43 interfaces, 6 type aliases, each matching the graph exactly).
| Defect | Effect |
|---|---|
| Declarations whose parameters wrap across lines were never indexed — 7 of 45 functions (15.6%), including a 380-line central function | Definition lookup answered "not found"; callers, callees, blast radius, porting contracts and clone detection silently skipped them |
endLine always equalled the signature line |
Structural hashing compared only signatures; git-diff range mapping could never match an edit inside a body |
Braces in a parameter default (= {}) or a return type (Promise<T>) closed the body count immediately |
A 380-line function was recorded as ending on its own signature line |
A class whose { sits on a wrapped implements line had that brace counted twice |
Body depth became 2 and every method inside was invisible |
| Call extraction ran over raw source, so query text was mined for calls | 602 phantom edges to MATCH alone made SQL keywords the top "hub symbols" in the architecture overview |
The in-class member pattern accepted any identifier( at line start |
40 phantom methods named MATCH / MERGE / CREATE / while / rej, which then filled dead-code output |
| Code inside template literals was parsed as source | A class that existed only inside a test fixture string was indexed |
const files = (res.data ?? []).map((row) => ({…})) was read as an arrow function |
Assignments appeared as functions named after the variable |
Ingestion is now safe against concurrent reindexing
Ingestion is a delete-then-create sequence with many awaits, so overlapping calls for one path interleaved and every CREATE survived. Four concurrent calls for a single file produced 48 symbols and 4 File nodes instead of 3 and 1, each symbol duplicated 16 times. This is reachable in normal use — the file watcher fires on save while a manual reindex or the startup stale check is running.
Ingestion is now serialized per path, and deletion removes symbols by their own file property as well as through the :CONTAINS edge so orphans cannot survive an interrupted ingest.
Daemon lifecycle
- No more orphaned
knowcode serve. Daemons are spawned detached so they outlive a tool call, and nothing used to stop them: quitting the harness left one running per project, each holding an embedded FalkorDB process, an HTTP server, a file watcher and a database file. The plugin now records the pids it spawned and stops them from its disposal effect, which the harness runs onSIGINT/SIGTERM. Only daemons this process started are touched — one you launched yourself is never killed. A pid is signalled only while itsdaemon.jsonstill names it, so a recycled pid can never be mistaken for ours. NewstopDaemonOnExitoption to opt out. autoStartDaemonnow actually works. It was declared and documented since the first release but never read, so every tool answered with a "runknowcode serve ." notice until you started a daemon by hand. A daemon is now spawned on demand from this package's own CLI, with output appended to<dataDir>/serve.log.- A second workspace no longer dies with
EADDRINUSE. The HTTP port is a preference, not a requirement: an occupied port falls back to the next free one and is recorded per workspace. - A failed start no longer leaks the embedded FalkorDB process, and a client refuses a daemon that serves a different workspace, so one project can never be answered with another's graph.
servewaits for the file watcher's initial scan before reconciling, closing a window where a file created during startup was indexed by neither the watcher nor the stale check.
Imports and affected tests
Relative imports produced zero :IMPORTS edges between local files, because resolution returned a single extension-less guess that never matched a real File.path. With no edges, TESTS_FOR linking found nothing and code_find_affected_test_files degraded to naming conventions alone.
Resolution now offers every plausible candidate, including the build-to-source mapping for compiled output (../lib/x.js → src/x.ts). On this repository that turns 0 local import edges into 85, and affected-test discovery resolves real suites — for example src/server/daemon.ts reports the four test files that actually cover it.
Compatibility
No runtime dependency on any @deepseek-ai/* package: tool definitions are plain objects, every schema is standard JSON Schema, and Config is a hand-written Standard Schema v1 validator.
Known harness defect — not caused by this plugin:
Cannot read properties of undefined (reading 'prepare')aborts every tool call on harnessv0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a privateSymbol()rather thanSymbol.for(), so two evaluated copies of@deepseek-ai/dsh-toolsproduce two different keys. See Compatibility & Troubleshooting in the README for workarounds.
Upgrading
The package is not published to npm; install from this repository or a DSH profile link. If a daemon is already running for a workspace, restart it so the new code is loaded — the daemon executes from lib/ at startup.
Tests
50 suites covering the schema contract, tool-output contract, graph integrity, daemon lifecycle and [trace] logging.
Full Changelog: v0.1.2...v0.1.9
v0.1.2 — First public release
First public release of dsh-knowcode — a DeepSeek Harness plugin that unifies an AST code graph and a Markdown knowledge base inside an embedded FalkorDB (Cypher) database, exposed to the agent as 25 tools.
Highlights
- Embedded FalkorDB — no Docker, no cloud. The GraphBLAS engine runs from bundled binaries over a dynamic loopback port, with an external
FALKORDB_URLfallback. - 25 agent tools for architectural orientation, safe refactoring (transitive blast radius, call paths, cycle detection, affected tests), cross-language and cross-paradigm porting, AST clone detection, bidirectional spec ↔ code traceability, and polyglot contract → storage mapping.
tgrep-style daemon —knowcode serveruns a persistent indexer with a debounced file watcher.--traceemitstgrep-style[trace]diagnostics for startup, index phases, stale checks, RPC/search calls and live watcher activity.- Knowledge graph — ADRs, READMEs and coding standards are indexed and automatically cross-linked to code symbols (
(:DocSection)-[:DOCUMENTS]->(:Symbol)).
CLI
knowcode index, serve, status, query, stop. The command ships inside this package as bin/knowcode.js — see Installation & Quickstart in the README for the four ways to obtain it.
Platform support
| Platform | Status |
|---|---|
macOS Apple Silicon (darwin-arm64) |
✅ bundled — native embedded |
Linux x64 (linux-x64) |
✅ bundled — native embedded |
Linux ARM64 (linux-arm64) |
🔄 Docker, or supply custom binaries |
| Windows x64 / ARM64 |
The embedded FalkorDB binaries are stored with Git LFS. Install git-lfs before cloning, otherwise bin/**/*.so arrives as pointer files and the database will not start.
Compatibility
No runtime dependency on any @deepseek-ai/* package: tool definitions are plain objects, every schema is standard JSON Schema, and Config is a hand-written Standard Schema v1 validator.
Known harness defect — not caused by this plugin:
Cannot read properties of undefined (reading 'prepare')aborts every tool call on harnessv0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a privateSymbol()rather thanSymbol.for(), so two evaluated copies of@deepseek-ai/dsh-toolsproduce two different keys. See Compatibility & Troubleshooting in the README for the workarounds.
Requirements
- Node.js
^22.19.0 || >=24.0.0 - Git LFS, for the embedded FalkorDB binaries
Full Changelog: https://github.com/huuthuan-nguyen/dsh-knowcode/commits/v0.1.2