@drafthq/draft 3.7.2 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.claude-plugin/marketplace.json +6 -3
  2. package/.claude-plugin/plugin.json +4 -2
  3. package/.cursor-plugin/plugin.json +4 -2
  4. package/LICENSE +1 -1
  5. package/README.md +7 -7
  6. package/bin/README.md +14 -13
  7. package/cli/src/cli.js +2 -2
  8. package/cli/src/installer.js +4 -2
  9. package/cli/src/lib/cursor-registry.js +2 -2
  10. package/cli/src/lib/graph.js +2 -1
  11. package/core/shared/condensation.md +8 -7
  12. package/core/shared/draft-context-loading.md +1 -1
  13. package/core/shared/graph-query.md +31 -23
  14. package/core/templates/architecture.md +1 -1
  15. package/integrations/agents/AGENTS.md +43 -34
  16. package/integrations/copilot/.github/copilot-instructions.md +43 -34
  17. package/package.json +7 -3
  18. package/scripts/fetch-memory-engine.sh +44 -11
  19. package/scripts/lib.sh +1 -1
  20. package/scripts/tools/_graph_queries.sh +28 -12
  21. package/scripts/tools/_lib.sh +38 -29
  22. package/scripts/tools/check-repo-size.sh +5 -1
  23. package/scripts/tools/cycle-detect.sh +11 -7
  24. package/scripts/tools/graph-callers.sh +1 -1
  25. package/scripts/tools/graph-impact.sh +63 -35
  26. package/scripts/tools/graph-init.sh +5 -10
  27. package/scripts/tools/graph-preflight.sh +6 -3
  28. package/scripts/tools/graph-query.sh +8 -7
  29. package/scripts/tools/graph-snapshot.sh +13 -24
  30. package/scripts/tools/graph-traces.sh +2 -2
  31. package/scripts/tools/hotspot-rank.sh +10 -1
  32. package/scripts/tools/mermaid-from-graph.sh +1 -1
  33. package/scripts/tools/okf-emit-catalog.sh +3 -0
  34. package/scripts/tools/okf-render-views.sh +6 -4
  35. package/scripts/tools/okf-validate.sh +5 -0
  36. package/scripts/tools/resolve-tools.sh +5 -3
  37. package/scripts/tools/scan-markers.sh +9 -1
  38. package/scripts/tools/verify-graph-binary.sh +14 -2
  39. package/skills/draft/intent-mapping.md +1 -1
  40. package/skills/implement/SKILL.md +1 -1
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "draft-plugins",
3
3
  "owner": {
4
- "name": "mayurpise",
4
+ "name": "Mayur Pise",
5
+ "email": "mayur@getdraft.dev",
5
6
  "url": "https://github.com/mayurpise"
6
7
  },
7
8
  "metadata": {
@@ -12,9 +13,11 @@
12
13
  "name": "draft",
13
14
  "source": "./",
14
15
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
15
- "version": "3.7.2",
16
+ "version": "5.0.1",
16
17
  "author": {
17
- "name": "mayurpise"
18
+ "name": "Mayur Pise",
19
+ "email": "mayur@getdraft.dev",
20
+ "url": "https://github.com/mayurpise"
18
21
  },
19
22
  "homepage": "https://github.com/drafthq/draft",
20
23
  "license": "MIT",
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "draft",
3
3
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
4
- "version": "3.7.2",
4
+ "version": "5.0.1",
5
5
  "author": {
6
- "name": "mayurpise"
6
+ "name": "Mayur Pise",
7
+ "email": "mayur@getdraft.dev",
8
+ "url": "https://github.com/mayurpise"
7
9
  },
8
10
  "homepage": "https://github.com/drafthq/draft",
9
11
  "license": "MIT",
@@ -2,11 +2,13 @@
2
2
  "name": "draft",
3
3
  "displayName": "Draft",
4
4
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
5
- "version": "3.7.2",
5
+ "version": "5.0.1",
6
6
  "skills": "./skills/",
7
7
  "agents": "./core/agents/",
8
8
  "author": {
9
- "name": "mayurpise"
9
+ "name": "Mayur Pise",
10
+ "email": "mayur@getdraft.dev",
11
+ "url": "https://github.com/mayurpise"
10
12
  },
11
13
  "homepage": "https://github.com/drafthq/draft",
12
14
  "license": "MIT",
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Mayur Pise
3
+ Copyright (c) 2025-2026 Mayur Pise
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -68,12 +68,12 @@ Each host installs the way that host actually loads extensions — no manual ste
68
68
 
69
69
  | Host | `draft install …` | What it does |
70
70
  |------|-------------------|--------------|
71
- | **Claude Code** | `claude-code` | Registers the plugin via `claude plugin marketplace add` + `claude plugin install` (user scope). Restart Claude Code. |
72
- | **Cursor** | `cursor` | Copies the plugin into `~/.cursor/plugins/local/draft/`, writes `.cursor-plugin/plugin.json`, registers `draft@draft-plugins` in Cursor's plugin registry, and enables it. Restart Cursor (or Developer: Reload Window). Existing installs upgrade with `draft install cursor --force`. |
73
- | **Codex** | `codex` | Writes `./AGENTS.md`, which Codex reads automatically. |
74
- | **opencode** | `opencode` | Writes `./AGENTS.md` + `~/.agents/skills/draft/`, both auto-discovered. |
71
+ | **Claude Code** | `claude-code` | Registers the plugin via `claude plugin marketplace add` + `claude plugin install` (default `--global` / user scope). Restart Claude Code. |
72
+ | **Cursor** | `cursor` | Default `--global`. Copies the plugin into `~/.cursor/plugins/local/draft/`, writes `.cursor-plugin/plugin.json`, registers `draft@draft-plugins` in Cursor's plugin registry, and enables it. Restart Cursor (or Developer: Reload Window). Existing installs upgrade with `draft install cursor --force`. |
73
+ | **Codex** | `codex` | Default `--project`. Writes `./AGENTS.md`, which Codex reads automatically. |
74
+ | **opencode** | `opencode` | Default `--project`. Writes `./AGENTS.md` + `~/.agents/skills/draft/`, both auto-discovered. |
75
75
 
76
- Flags: `--global` / `--project` to pick scope, `--dry-run` to preview, `--force` to overwrite, `--no-graph` to skip the graph-engine fetch.
76
+ Flags: `--global` / `--project` to pick scope (`--global` is the default for `claude-code` and `cursor`; `--project` is the default for `codex` and `opencode`), `--dry-run` to preview, `--force` to overwrite, `--no-graph` to skip the graph-engine fetch.
77
77
 
78
78
  Then, in Claude Code (after restarting):
79
79
 
@@ -164,7 +164,7 @@ The graph powers `/draft:graph` and `/draft:impact`, enriches `/draft:bughunt` a
164
164
 
165
165
  ### Deterministic helper tools
166
166
 
167
- Skills also call into **shell helpers** under `scripts/tools/` for mechanical work — git metadata, file classification, test-framework detection, hotspot ranking, freshness checks, ADR indexing, and live graph queries (`graph-callers.sh`, `graph-impact.sh`, `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`). All emit JSON or markdown, follow a uniform exit-code contract, and degrade gracefully when their input source is unavailable.
167
+ Skills also call into **shell helpers** under `scripts/tools/` for mechanical work — git metadata, file classification, test-framework detection, hotspot ranking, freshness checks, ADR indexing, and live graph queries (`graph-callers.sh`, `graph-impact.sh`, `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`). All emit JSON or markdown and follow a uniform exit-code contract. Graph wrappers fail loud: shapeless `{}` (no `.rows` array) is `source:"unavailable"` with a non-zero exit, not a measured empty result.
168
168
 
169
169
  ---
170
170
 
@@ -270,7 +270,7 @@ Each layer narrows the solution space. By the time AI writes code, decisions are
270
270
 
271
271
  ---
272
272
 
273
- <p align="center">MIT License &middot; Graph engine: <a href="https://github.com/DeusData/codebase-memory-mcp" target="_blank" rel="noopener noreferrer">codebase-memory-mcp</a> by <a href="https://github.com/DeusData" target="_blank" rel="noopener noreferrer">DeusData</a></p>
273
+ <p align="center">MIT License &middot; Created &amp; maintained by <a href="https://www.linkedin.com/in/mayurpise/" target="_blank" rel="noopener noreferrer">Mayur Pise</a> &middot; Graph engine: <a href="https://github.com/DeusData/codebase-memory-mcp" target="_blank" rel="noopener noreferrer">codebase-memory-mcp</a> by <a href="https://github.com/DeusData" target="_blank" rel="noopener noreferrer">DeusData</a></p>
274
274
 
275
275
  <p align="center">
276
276
  <strong>Credits:</strong> Inspired by <a href="https://github.com/gemini-cli-extensions/conductor">gemini-cli-extensions/conductor</a>
package/bin/README.md CHANGED
@@ -17,7 +17,7 @@ This installs the binary to the **Draft-managed location**:
17
17
  ~/.cache/draft/bin/codebase-memory-mcp
18
18
  ```
19
19
 
20
- The fetch script picks the right release archive for the host OS/arch, verifies its SHA-256 against the published `checksums.txt`, extracts it, and installs it there. `draft install claude-code` / `draft install cursor` run this automatically (best-effort, network-gated); skip it with `--no-graph`.
20
+ The fetch script picks the right release archive for the host OS/arch, verifies its SHA-256 (against hashes pinned in the script for the default version, else against the release's `checksums.txt`), extracts it, and installs it there. An existing install is kept only when its version matches the requested one, so a pin bump upgrades it. Downloads pin `curl --proto '=https' --proto-redir '=https'` so a compromised redirect cannot downgrade to HTTP. `draft install claude-code` / `draft install cursor` run this automatically (best-effort, network-gated); skip it with `--no-graph`.
21
21
 
22
22
  ## Resolution order
23
23
 
@@ -39,10 +39,10 @@ Set `DRAFT_MEMORY_DISABLE=1` to force the engine off. All graph-backed skills an
39
39
  ## How tools use it
40
40
 
41
41
  Shell helpers under `scripts/tools/` drive the engine via its CLI
42
- (`codebase-memory-mcp cli <tool> '<json>'`) and shape results into Draft's
42
+ (`codebase-memory-mcp cli <tool>`, JSON args on stdin) and shape results into Draft's
43
43
  contracts — see `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`,
44
44
  and `verify-graph-binary.sh`. The shared wrappers (`memory_cli`,
45
- `memory_ensure_index`, `memory_project_for_repo`) live in `_lib.sh`.
45
+ `memory_ensure_index`, `memory_index_bounded`) live in `_lib.sh`.
46
46
 
47
47
  ## Snapshot artifacts
48
48
 
@@ -50,9 +50,9 @@ and `verify-graph-binary.sh`. The shared wrappers (`memory_cli`,
50
50
 
51
51
  | Artifact | Content |
52
52
  |----------|---------|
53
- | `schema.yaml` | Engine + project metadata, node/edge counts, point-of-index counts (gates graph use). |
53
+ | `schema.yaml` | Engine metadata and point-of-index node/edge counts (gates graph use). No machine- or run-specific fields, so it only changes when the engine or the codebase does. |
54
54
 
55
- Structural graph data (architecture, hotspots, module deps, service routes) is queried **live** from the `codebase-memory-mcp` engine — either via the wrapper scripts under `scripts/tools/` (`graph-callers.sh`, `graph-impact.sh`, `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`) or directly with `codebase-memory-mcp cli <tool> '<json>'`.
55
+ Structural graph data (architecture, hotspots, module deps, service routes) is queried **live** from the `codebase-memory-mcp` engine — either via the wrapper scripts under `scripts/tools/` (`graph-callers.sh`, `graph-impact.sh`, `hotspot-rank.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh`), or, for any other read-only engine tool, `graph-query.sh --tool <name> --json '{...}'`.
56
56
 
57
57
  ## Offline / air-gapped distributions
58
58
 
@@ -68,22 +68,23 @@ Draft's differentiator depends on a binary published by a third party ([DeusData
68
68
 
69
69
  | Property | Status |
70
70
  |---|---|
71
- | Version pinned | Yes — `DEFAULT_VERSION` in `scripts/fetch-memory-engine.sh`. Bumps are deliberate commits, never floating. `CMM_VERSION` overrides per-install. |
72
- | SHA-256 verified | Yes when the release publishes `checksums.txt` and lists the archive. A **mismatch is always fatal.** |
73
- | Missing checksum | **Warns and installs by default.** Set `DRAFT_STRICT_VERIFY=1` to make an unverifiable download fatal instead. |
71
+ | Version pinned | Yes — `DEFAULT_VERSION` in `scripts/fetch-memory-engine.sh`. Bumps are deliberate commits, never floating. `CMM_VERSION` overrides per-install. An install at another version is replaced on the next fetch, and `verify-graph-binary.sh` / `graph-preflight.sh` warn when the resolved engine (e.g. one on `$PATH`) is off-pin. |
72
+ | SHA-256 verified | Yes. The default version's four platform archives are checked against hashes **pinned in the script** (a replaced release asset fails even if the release's own `checksums.txt` agrees). Other versions are checked against the release's `checksums.txt` when it lists the archive. A **mismatch is always fatal.** |
73
+ | Missing checksum | Applies only to non-default versions: **warns and installs by default.** Set `DRAFT_STRICT_VERIFY=1` to make an unverifiable download fatal instead. |
74
74
  | Signature / attestation | **No.** There is no code signing or SLSA provenance today. Verification is checksum-only. |
75
75
  | Source available | Yes — the engine is open source at [DeusData/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp). |
76
76
  | Reproducible build | Not verified by Draft. We check the archive matches the publisher's checksum, not that the checksum matches the source. |
77
77
 
78
- Be explicit about the residual risk: a checksum proves the download matches what the publisher released. It does not prove the publisher released what the source says.
78
+ Be explicit about the residual risk: a pinned checksum proves the download matches what the publisher released when Draft pinned it. It does not prove the publisher released what the source says.
79
79
 
80
80
  ### What the engine does at runtime
81
81
 
82
82
  - Reads the repository you point it at and writes a SQLite graph under its own cache.
83
- - Runs entirely locally. No API key, no telemetry endpoint, no outbound calls during indexing or querying.
84
- - Network is used exactly once, by `fetch-memory-engine.sh`, to download the release archive.
83
+ - Runs entirely locally as Draft invokes it (`cli` mode). No API key, no telemetry endpoint, no outbound calls during indexing or querying — verified under `strace` for `index_repository` and queries on 0.9.0 (zero `connect()` calls, no spawned `curl`).
84
+ - The binary does embed an update checker (`api.github.com/.../releases/latest`) and `update` / `install` subcommands for its own MCP-server workflow. Draft never runs the server or those subcommands.
85
+ - Draft uses the network exactly once, in `fetch-memory-engine.sh`, to download the release archive.
85
86
 
86
- Draft invokes it only through `codebase-memory-mcp cli <tool> '<json>'` (see `_lib.sh:memory_cli`). It is never given credentials and never writes into your source tree.
87
+ Draft invokes it only through `codebase-memory-mcp cli <tool>` with JSON args on stdin (see `_lib.sh:memory_cli`). It is never given credentials and never writes into your source tree.
87
88
 
88
89
  ### If you cannot run an unvetted binary
89
90
 
@@ -97,7 +98,7 @@ Three supported postures, in increasing strictness:
97
98
 
98
99
  The dependency is bounded by design, which is what makes this survivable:
99
100
 
100
- - **The interface is small.** Draft consumes a documented CLI (`cli <tool> '<json>'`), not a library. The entire coupling lives in `scripts/tools/_lib.sh` (`memory_cli`, `memory_ensure_index`, `memory_project_for_repo`) and `_graph_queries.sh`. Swapping engines means reimplementing those, not rewriting skills.
101
+ - **The interface is small.** Draft consumes a documented CLI (`cli <tool>`, JSON on stdin), not a library. The entire coupling lives in `scripts/tools/_lib.sh` (`memory_cli`, `memory_ensure_index`, `memory_index_bounded`) and `_graph_queries.sh`. Swapping engines means reimplementing those, not rewriting skills.
101
102
  - **Skills never call the engine directly.** They call `graph-*.sh` wrappers, all of which already fail loud with `source: "unavailable"`. An engine that disappears degrades the product; it does not break it.
102
103
  - **Pinning buys time.** A stalled upstream keeps working at the pinned version; only new language support would be lost.
103
104
  - **The graph contract is replaceable.** The queries are ordinary Cypher-shaped structural lookups (callers, callees, fan-in, cycles, routes) over a tree-sitter/LSP index — reproducible on another indexer.
package/cli/src/cli.js CHANGED
@@ -47,8 +47,8 @@ Usage:
47
47
  Hosts: ${hosts.map((h) => h.id).join(', ')}
48
48
 
49
49
  Flags:
50
- --global Install to the user-level location (default for cursor)
51
- --project Install into the current project (default for claude-code, codex, opencode)
50
+ --global Install to the user-level location (default for claude-code, cursor)
51
+ --project Install into the current project (default for codex, opencode)
52
52
  --dry-run Print planned writes without touching disk
53
53
  --no-graph Skip the knowledge-graph engine fetch
54
54
  --force Overwrite an existing target
@@ -15,9 +15,11 @@ const CHECK_TIMEOUT_MS = 10000;
15
15
  const USE_SHELL = process.platform === 'win32';
16
16
 
17
17
  function hasBinary(name) {
18
- // ENOENT on the error means the binary is not on PATH.
18
+ // Windows `.cmd` shims (shell: true) report a missing binary as status 127
19
+ // with no `error`. Treat any spawn error or non-zero status as absent.
19
20
  const r = spawnSync(name, ['--version'], { stdio: 'ignore', timeout: CHECK_TIMEOUT_MS, shell: USE_SHELL });
20
- return !(r.error && r.error.code === 'ENOENT');
21
+ if (r.error) return false;
22
+ return r.status === 0;
21
23
  }
22
24
 
23
25
  // Per-step ceiling so a stalled network op (e.g. the `git clone` behind
@@ -78,7 +78,7 @@ function registerCursorPlugin(opts) {
78
78
  // --- installed_plugins.json: merge our key, preserve installedAt on upgrade. ---
79
79
  const ip = readJson(paths.ipPath, { version: 2, plugins: {} });
80
80
  if (typeof ip.version !== 'number') ip.version = 2;
81
- if (!ip.plugins || typeof ip.plugins !== 'object') ip.plugins = {};
81
+ if (!ip.plugins || typeof ip.plugins !== 'object' || Array.isArray(ip.plugins)) ip.plugins = {};
82
82
  const existing = Array.isArray(ip.plugins[PLUGIN_KEY]) ? ip.plugins[PLUGIN_KEY][0] : null;
83
83
  const installedAt = existing && existing.installedAt ? existing.installedAt : now;
84
84
  ip.plugins[PLUGIN_KEY] = [
@@ -93,7 +93,7 @@ function registerCursorPlugin(opts) {
93
93
 
94
94
  // --- settings.json: flip our enabledPlugins flag, preserve everything else. ---
95
95
  const settings = readJson(paths.settingsPath, {});
96
- if (!settings.enabledPlugins || typeof settings.enabledPlugins !== 'object') {
96
+ if (!settings.enabledPlugins || typeof settings.enabledPlugins !== 'object' || Array.isArray(settings.enabledPlugins)) {
97
97
  settings.enabledPlugins = {};
98
98
  }
99
99
  settings.enabledPlugins[PLUGIN_KEY] = true;
@@ -14,7 +14,8 @@ function fetchGraph() {
14
14
  return;
15
15
  }
16
16
  log.note('Fetching knowledge-graph engine (best-effort)...');
17
- const result = spawnSync('bash', [script], { stdio: 'inherit' });
17
+ const timeout = Number(process.env.DRAFT_INSTALL_TIMEOUT_MS) || 300000;
18
+ const result = spawnSync('bash', [script], { stdio: 'inherit', timeout });
18
19
  if (result.status !== 0) {
19
20
  log.warn('Graph engine fetch skipped (offline or unsupported platform) — features degrade gracefully.');
20
21
  }
@@ -24,7 +24,7 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
24
24
  | Input | Path | Description |
25
25
  |-------|------|-------------|
26
26
  | architecture.md | `draft/architecture.md` | Comprehensive human-readable engineering reference (source of truth) |
27
- | schema.yaml | `draft/graph/schema.yaml` | Graph metrics for tier computation (optional — skip if absent) |
27
+ | Architecture view | `scripts/tools/graph-arch.sh --repo .` | Live graph metrics for tier computation (optional — skip when `draft/graph/schema.yaml` is absent) |
28
28
 
29
29
  ## Outputs
30
30
 
@@ -37,11 +37,12 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
37
37
 
38
38
  ## Target Size
39
39
 
40
- Compute tier from `draft/graph/schema.yaml` after graph build:
40
+ Compute tier from the live architecture view (the same metrics as `/draft:init` Step 1.4.5 — `schema.yaml` carries only counts, not these):
41
41
 
42
- M = stats.modules
43
- F = stats.go_functions + stats.py_functions
44
- P = stats.proto_rpcs
42
+ ARCH=$("$DRAFT_TOOLS/graph-arch.sh" --repo .) (DRAFT_TOOLS: resolver below)
43
+ M = $ARCH | jq '.packages | length' (modules)
44
+ F = $ARCH | jq '[.node_labels[] | select(.label=="Function" or .label=="Method") | .count] | add // 0' (functions + methods)
45
+ P = $ARCH | jq '.routes | length' (routes / RPCs)
45
46
 
46
47
  | Tier | Label | Condition | Budget |
47
48
  |------|--------|----------------------------------------|---------------|
@@ -51,7 +52,7 @@ Compute tier from `draft/graph/schema.yaml` after graph build:
51
52
  | 4 | large | M≤100 AND F≤5000 AND P≤500 | 400–600 lines |
52
53
  | 5 | XL | M>100 OR F>5000 OR P>500 | 600–900 lines |
53
54
 
54
- If `schema.yaml` does not exist: default to tier 2 (180–280 lines).
55
+ If `schema.yaml` does not exist (no graph) or `graph-arch.sh` reports `source: unavailable`: default to tier 2 (180–280 lines).
55
56
 
56
57
  - Below tier minimum: incomplete condensation — ensure all sections are represented
57
58
  - Above tier maximum: insufficient compression — apply prioritization rules below
@@ -171,7 +172,7 @@ Before writing `draft/.ai-context.md`, verify:
171
172
  - [ ] No references to `architecture.md` (file must be self-contained)
172
173
  - [ ] All invariants from architecture.md are preserved
173
174
  - [ ] Extension cookbooks are complete (an agent can follow them without other files)
174
- - [ ] Output is within tier budget bounds (compute from schema.yaml or default tier 2)
175
+ - [ ] Output is within tier budget bounds (compute from `graph-arch.sh` or default tier 2)
175
176
  - [ ] GRAPH:HOTSPOTS present (or note "No hotspot data available" if graph absent)
176
177
  - [ ] GRAPH:CYCLES present ("None ✓" or cycle list; or note if graph absent)
177
178
  - [ ] GRAPH:MODULE-HOTSPOTS present for tier ≥ 3 (or note if no hotspot data)
@@ -71,7 +71,7 @@ If `draft/graph/schema.yaml` exists, the project has automated graph analysis da
71
71
 
72
72
  | File | Purpose | Content |
73
73
  |------|---------|---------|
74
- | `draft/graph/schema.yaml` | Gate marker (engine + project metadata + point-of-index counts); presence gates graph use | YAML, ~15 lines |
74
+ | `draft/graph/schema.yaml` | Gate marker (engine metadata + point-of-index counts); presence gates graph use | YAML, ~10 lines |
75
75
 
76
76
  Note: `.ai-context.md` embeds a condensed graph summary (`GRAPH:MODULES`, `GRAPH:HOTSPOTS`, `GRAPH:CYCLES`) for first-pass structural ground truth. Deep structural data is queried live from the engine (see Live structural queries below).
77
77
 
@@ -103,7 +103,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
103
103
  |---|---|---|
104
104
  | `bash "$DRAFT_TOOLS/hotspot-rank.sh" [--top N]` | complexity-weighted hotspots | Emits `{hotspots:[],source:"unavailable"}` and exits 2 |
105
105
  | `bash "$DRAFT_TOOLS/cycle-detect.sh"` | call cycles | Emits `{cycles:[],source:"unavailable"}` and exits 2 |
106
- | `bash "$DRAFT_TOOLS/mermaid-from-graph.sh" [--diagram module-deps\|co-change\|proto-map]` | diagram text | Emits an empty mermaid block and exits 2 |
106
+ | `bash "$DRAFT_TOOLS/mermaid-from-graph.sh" [--diagram module-deps\|co-change\|proto-map]` | diagram text | Unavailable stub (`graph not built`) vs empty-graph stub (`no edges`); both exit 2 |
107
107
  | `bash "$DRAFT_TOOLS/graph-callers.sh" --symbol N [--transitive[=N]] [--prod-only] [--qualified]` | callers | `{callers:[],status:"unavailable",source:"unavailable"}`, exit 2 |
108
108
  | `bash "$DRAFT_TOOLS/graph-snippet.sh" --qualified N` | verified source + caller/callee counts | `{status:"unavailable",source:"unavailable"}`, exit 2 |
109
109
  | `bash "$DRAFT_TOOLS/graph-search.sh" --query "STR" [--limit N]` | semantic/ranked search | `{results:[],source:"unavailable"}`, exit 2 |
@@ -115,7 +115,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
115
115
  | `bash "$DRAFT_TOOLS/graph-query.sh" (--cypher STR \| --tool NAME --json '{...}')` | generic read-only passthrough | `{source:"unavailable"}`, exit 2 |
116
116
  | `bash "$DRAFT_TOOLS/graph-traces.sh" ingest --file F --experimental` | runtime traces (experimental write) | `{source:"unavailable"}`, exit 2 |
117
117
 
118
- For lower-level modes, call the engine directly: `codebase-memory-mcp cli <tool> '<json>'` (see the tool list in [bin/README.md](../../bin/README.md)).
118
+ For an engine tool without a dedicated wrapper, use the read-only passthrough `graph-query.sh --repo . --tool <name> --json '{...}'` (project injected, write tools refused, `source:"unavailable"` on failure). Never call the engine binary directly — that skips engine resolution, the index refresh before each query, and the fail-loud contract.
119
119
 
120
120
  ### Capability wrappers & dialect limits (graph-tooling-v2)
121
121
 
@@ -123,8 +123,9 @@ All Cypher lives in `scripts/tools/_graph_queries.sh` (the single source of quer
123
123
  truth). Wrappers are thin arg-parse → builder → fail-loud JSON. Three contracts
124
124
  matter when consuming them:
125
125
 
126
- **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-snippet`,
127
- `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`, `graph-errors`)
126
+ **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-impact`,
127
+ `graph-snippet`, `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`,
128
+ `graph-errors`)
128
129
  emit a `status` field that distinguishes the three real outcomes — never read a
129
130
  bare `[]` as a confirmed true negative:
130
131
 
@@ -133,9 +134,11 @@ bare `[]` as a confirmed true negative:
133
134
  | `ok` | node found, edges returned |
134
135
  | `no-edges` | node exists but has no matching edge (a *real* negative) |
135
136
  | `no-match` | the named symbol was not found at all (check the name / try `--qualified`) |
136
- | `unavailable` | engine could not be resolved (exit 2) |
137
+ | `unavailable` | engine could not be resolved, or engine returned shapeless/non-row JSON (exit 2) |
137
138
 
138
- **Verified engine param shapes** (engine v0.8.xthe runtime source of truth is
139
+ **Shapeless JSON is unavailable.** `gq_run` requires `has("rows") and (.rows|type=="array")`. A bare `{}` (or any object without a `.rows` array) is not a measured empty result wrappers emit `source:"unavailable"` and a non-zero exit. Do not read `{}` as "no callers / no cycles / no edges". `graph-impact`, `graph-callers`, and `mermaid-from-graph` also require their tool-shaped object; a failed snapshot refresh does not rewrite `schema.yaml`.
140
+
141
+ **Verified engine param shapes** (engine v0.9.0 — the runtime source of truth is
139
142
  `get_graph_schema`; do not hardcode a property set):
140
143
 
141
144
  ```bash
@@ -148,13 +151,18 @@ get_graph_schema '{"project":P}' # → {node_labels:[{label,count,properties}
148
151
 
149
152
  **Cypher dialect — keep queries inside the SAFE set:**
150
153
 
151
- - ✅ SAFE: fixed-length patterns, single/multi-hop explicit patterns, `=`, `<`,
152
- `STARTS WITH`, `NOT x STARTS WITH`, `AND`, `OR`, relationship-type alternation
153
- `[:A|B]`, simple `count(x)`.
154
- - ❌ UNSAFE (rejected or silently empty): `coalesce()`, `<>` / `!=` / `<=` / `>=`,
155
- `NOT EXISTS(...)`, `NOT (pattern)`, `WITH`-grouping aggregation, multi-pattern
156
- joins. `graph-query.sh --cypher` returns the engine's raw error, not a silent
154
+ - ✅ SAFE: `=`, `<>`/`!=`, `<`, `>`, `<=`, `>=` against a literal; `STARTS WITH`,
155
+ `NOT x STARTS WITH`, `AND`, `OR`; explicit and variable-length patterns
156
+ (`[:R*1..3]`, fixed depth `[:R*2..2]`); relationship-type alternation `[:A|B]`;
157
+ `coalesce()`; `DISTINCT`; `count(x)`, `count(DISTINCT x)`; `WITH`-grouping
158
+ aggregation.
159
+ - UNSAFE: comparing one property against another (`a.x < b.x`), `NOT EXISTS(...)`,
160
+ `NOT (pattern)`, path variables (`p=(...)`, `length(p)`) — all rejected; and
161
+ multi-pattern joins (`MATCH (a)…, (b)…`), which parse but ignore `RETURN` and
162
+ `LIMIT`. `graph-query.sh --cypher` returns the engine's raw error, not a silent
157
163
  empty — but the builders never emit these forms.
164
+ - ⚠️ `LIMIT` applies before `DISTINCT`: `RETURN DISTINCT … LIMIT n` can return
165
+ fewer than n rows while more exist. Judge truncation on raw rows.
158
166
 
159
167
  **Caveats consumers must respect:**
160
168
 
@@ -189,22 +197,22 @@ The only committed file is the gate marker:
189
197
 
190
198
  | File | Role |
191
199
  |------|------|
192
- | `draft/graph/schema.yaml` | Engine + project metadata and point-of-index counts (provenance, not authoritative). Carries **no graph data**. Its presence is the **gate** (see Pre-Check) — it signals the engine is wired for this repo. Written by `scripts/tools/graph-snapshot.sh`. |
200
+ | `draft/graph/schema.yaml` | Engine metadata and point-of-index counts (provenance, not authoritative). Carries **no graph data**. Its presence is the **gate** (see Pre-Check) — it signals the engine is wired for this repo. Written by `scripts/tools/graph-snapshot.sh`. |
193
201
 
194
- All structural data is obtained live by shelling out to the engine — either through the query-tool wrappers under `scripts/tools/` or directly via `codebase-memory-mcp cli <tool> '<json>'`. The shell tools auto-index the repo into the engine's own cache on demand, so no committed files are required.
202
+ All structural data is obtained live through the query-tool wrappers under `scripts/tools/`; `graph-query.sh --tool` covers every read-only engine tool that has no dedicated wrapper. The wrappers refresh the repo's index in the engine's own cache before each query, so no committed files are required.
195
203
 
196
204
  ### How skills query (engine is the interface; jq is optional)
197
205
 
198
- - **The engine is the query.** `codebase-memory-mcp cli <tool> '<json>'` (and the wrappers that call it) is how you ask it takes JSON args and returns JSON. There is no other query surface.
206
+ - **The engine is the query; the wrappers are the interface.** Every wrapper drives the engine's CLI and returns JSON. There is no other query surface.
199
207
  - **Prefer the wrappers — they resolve the engine for you.** `graph-arch.sh` (architecture view: packages/routes/layers/hotspots), `graph-callers.sh`, `hotspot-rank.sh`, `graph-impact.sh`, `cycle-detect.sh`, `mermaid-from-graph.sh` return already-shaped JSON. The engine binary is usually **not on `$PATH`** (it lives under `~/.cache/draft/bin/`); the wrappers locate it via `_lib.sh:find_memory_bin`, so a skill using a wrapper needs no resolution step.
200
- - **Raw `codebase-memory-mcp cli` requires resolving the binary first** (it is not on `$PATH`): `CM="${DRAFT_MEMORY_BIN:-$HOME/.cache/draft/bin/codebase-memory-mcp}"; "$CM" cli <tool> '<json>'`. Reach for this only for tools without a wrapper (`search_graph`, `search_code`, `trace_path`).
208
+ - **Tools without a dedicated wrapper go through `graph-query.sh`:** `"$DRAFT_TOOLS/graph-query.sh" --repo . --tool search_code --json '{"pattern":"..."}'` (likewise `trace_path`, `get_graph_schema`, `index_status`). `search_graph` has its own wrapper, `graph-search.sh`.
201
209
  - **`jq` is not a query tool — it only trims output.** Reach for it solely to slice a *large* response (chiefly the `get_architecture` blob) down to the field you need, for token economy. The agent can read raw JSON directly; jq is an optimization, not a requirement. Don't pipe wrapper output through jq unless you genuinely need a sub-field.
202
210
 
203
211
  The engine uses a **unified, language-agnostic** node model — `Function`, `Method`, `Class`, `Module`, `File`, `Folder`, `Route`, `Section`, `Variable` (language is inferred from file extension) — and edges `CALLS`, `DEFINES`, `CONTAINS_FILE`, `IMPORTS`, `HTTP_CALLS`, `FILE_CHANGES_WITH`, `SEMANTICALLY_RELATED`, `SIMILAR_TO`. Each node carries `file_path` + `start_line`/`end_line` and rich `properties` (complexity, signature, parent_class), and the engine exposes full-text (`search_code`) and semantic search — none of which a committed snapshot reproduced.
204
212
 
205
213
  ## Query Tools
206
214
 
207
- Live queries go through the shell tools under `scripts/tools/`, which drive the engine and shape results into stable JSON. Each tool resolves the engine (see Finding the Engine), indexes the repo on demand, and emits `source: "memory-graph"` on success or `source: "unavailable"` (non-zero exit) when the engine cannot be resolved. Set `DRAFT_MEMORY_DISABLE=1` to force the engine off; all tools then degrade gracefully.
215
+ Live queries go through the shell tools under `scripts/tools/`, which drive the engine and shape results into stable JSON. Each tool resolves the engine (see Finding the Engine), indexes the repo on demand, and emits `source: "memory-graph"` on success or `source: "unavailable"` (non-zero exit) when the engine cannot be resolved **or returns shapeless JSON** (missing `.rows` array). Set `DRAFT_MEMORY_DISABLE=1` to force the engine off; all tools then degrade to `unavailable` rather than inventing empty results.
208
216
 
209
217
  ### Callers — who calls this function?
210
218
 
@@ -217,11 +225,11 @@ Output: `{symbol, callers[{name, file}], source}`. Use when enumerating call sit
217
225
  ### Impact — blast radius of a file or symbol
218
226
 
219
227
  ```bash
220
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # changed-file impact (working-tree diff)
221
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # transitive callers of a function
228
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # dependents of a file: its symbols' callers + its importers
229
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # dependents (transitive callers) of a function
222
230
  ```
223
231
 
224
- Output: `{target, kind, impacted[{name, file, hop}], source}`. Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
232
+ Output: `{target, kind, impacted[{name, file, qualified, hop}], downstream_files, affected_modules, max_depth, by_category{code,test}, status, truncated, source}`. `impacted` lists each dependent once at its nearest hop (default depth 3), capped at 200 with `truncated:true`; the aggregates always cover the full set. `status` is `ok`, `no-edges` (target known, nothing depends on it), or `no-match` (target unknown to the graph — check the path). Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
225
233
 
226
234
  ### Hotspots — fan-in ranking
227
235
 
@@ -332,7 +340,7 @@ Unlocks any edge type or node property without a purpose-built wrapper. Write ve
332
340
  "$DRAFT_TOOLS/graph-snapshot.sh" --repo .
333
341
  ```
334
342
 
335
- Indexes the repo into the engine and writes the `draft/graph/schema.yaml` gate marker (now including the `detect_changes` delta: `changed_files`/`impacted_symbols`). It writes **no** graph data. Run during `/draft:init` and `/draft:graph`, or whenever the index should be refreshed.
343
+ Indexes the repo into the engine and writes the `draft/graph/schema.yaml` gate marker, and prints the `detect_changes` delta (`changed_files`/`impacted_symbols`) without committing it. It writes **no** graph data. Run during `/draft:init` and `/draft:graph`, or whenever the index should be refreshed.
336
344
 
337
345
  ## Finding the Engine (Resolution + Usage Report)
338
346
 
@@ -371,6 +379,6 @@ The engine indexes C/C++, Go, Python, TypeScript/JS, and more (tree-sitter, 159
371
379
  | Scenario | Behavior |
372
380
  |----------|----------|
373
381
  | No engine resolvable (or `DRAFT_MEMORY_DISABLE=1`) | Skip graph indexing in init; all skills proceed without graph data; tools emit `source: unavailable` |
374
- | Engine present but a query fails | Warn and proceed; skills work without graph data |
382
+ | Engine present but a query fails, or returns shapeless `{}` | Treat as unavailable — never as a true-negative empty result; skills proceed without graph data |
375
383
  | `draft/graph/schema.yaml` exists | Engine is wired — use live query tools as needed during the run |
376
- | Engine index out of date | The engine indexes incrementally (content-based, git-aware) on each query, so it self-freshens. Re-run `graph-snapshot.sh` (or init) to force a reindex and refresh the marker. |
384
+ | Engine index out of date | Every wrapper re-indexes incrementally (content-based, git-aware) before it queries — ~0.1 s on an unchanged repo — so live answers track the working tree, including the run's own edits. `graph-snapshot.sh` (or init) also refreshes the `schema.yaml` marker. |
@@ -29,7 +29,7 @@ graph:
29
29
  cpp: "{high}"
30
30
  go: "{approximate | high}"
31
31
  stats:
32
- modules: "{N from schema.yaml}"
32
+ modules: "{N from engine: get_architecture .packages}"
33
33
  edges: "{total_edges from engine: get_architecture .edges}"
34
34
  hotspots: "{N}"
35
35
  notes: "{explicit fidelity summary from engine: get_architecture .languages/.packages}"