Skip to content

Releases: huuthuan-nguyen/dsh-knowcode

v0.1.18 — Crash fix, settled first query, and an idle budget

Choose a tag to compare

@huuthuan-nguyen huuthuan-nguyen released this 18 Sep 06:54

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 built new 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 left daemon.json and serve.lock behind — 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. parseMongoSchema scans 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 harness v0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a private Symbol() rather than Symbol.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

Choose a tag to compare

@huuthuan-nguyen huuthuan-nguyen released this 18 Sep 04:40

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_OUTPUT on every call. The dispatcher returned an undeclared raw field against an output schema with additionalProperties: 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 left callers, 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) and git_diff_impact (zero callers at risk) all silently empty.
  • Static methods had no symbol at all, so CodeParser, StorageParser, DocParser and FalkorDBManager contributed 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 -> area edge 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 to delimited.
  • 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 File nodes 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.jssrc/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.line from the phrase "one line per phase" and walk/clean/start from ordinary words; a feature flow opened with test helpers. Only backticked identifiers, name() mentions and PascalCase names count now.
  • knowledge_doc resolves 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:

  1. an extension allowlist — code, documentation and schema text only, so every extensionless data file is out of scope by construction;
  2. 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;
  3. 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 on SIGINT/SIGTERM. A pid is signalled only while its daemon.json still names it, so a recycled pid is never mistaken for ours, and a daemon you started by hand is never touched. SIGKILL skips disposal and remains the one case that orphans.
  • One daemon per workspace, guarded by a serve.lock claimed with an exclusive create. A second serve exits with a notice, and a guard left by a crashed daemon is reclaimed.
  • autoStartDaemon now works. It was declared and documented but never read, so every tool answered with a "run knowcode serve ." notice until a daemon was started by hand.
  • maxFileSize is 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, and serve waits 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 harness v0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a private Symbol() rather than Symbol.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

Choose a tag to compare

@huuthuan-nguyen huuthuan-nguyen released this 18 Sep 03:43

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 on SIGINT/SIGTERM. Only daemons this process started are touched — one you launched yourself is never killed. A pid is signalled only while its daemon.json still names it, so a recycled pid can never be mistaken for ours. New stopDaemonOnExit option to opt out.
  • autoStartDaemon now actually works. It was declared and documented since the first release but never read, so every tool answered with a "run knowcode 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.
  • serve waits 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.jssrc/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 harness v0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a private Symbol() rather than Symbol.for(), so two evaluated copies of @deepseek-ai/dsh-tools produce 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

Choose a tag to compare

@huuthuan-nguyen huuthuan-nguyen released this 17 Sep 17:28

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_URL fallback.
  • 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 daemonknowcode serve runs a persistent indexer with a debounced file watcher. --trace emits tgrep-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 ⚠️ via WSL2 or Docker

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 harness v0.1.6-alpha.2. It reproduces on a profile with zero plugins installed: the harness keys its tool scheduler on a private Symbol() rather than Symbol.for(), so two evaluated copies of @deepseek-ai/dsh-tools produce 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