@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
@@ -6025,7 +6025,7 @@ After a phase passes review, refresh `metadata.json.impact` so future tracks can
6025
6025
  "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path>
6026
6026
  ```
6027
6027
 
6028
- Aggregate across all files: `downstream_files` = total unique downstream files (deduped), `downstream_modules` = union of `affected_modules`, `max_depth` = max across queries, `by_category` = sum of each query's `by_category`. If the graph is absent, leave these fields as zeros / empty arrays — the snapshot still records the directly-touched files.
6028
+ Aggregate across all files: `downstream_files` = count of the union of each query's `downstream_files` array, `downstream_modules` = union of `affected_modules`, `max_depth` = max across queries, `by_category` = sum of each query's `by_category`. If the graph is absent, leave these fields as zeros / empty arrays — the snapshot still records the directly-touched files.
6029
6029
 
6030
6030
  3. **Write metadata.json** with the populated `impact` block and `computed_at` set to the current timestamp.
6031
6031
 
@@ -9604,7 +9604,7 @@ Prefer `draft docs` going forward for all authoring requests. The legacy direct
9604
9604
 
9605
9605
  ## Discover Router
9606
9606
 
9607
- When user says "discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, index, etc.):
9607
+ When user says "discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, tour, impact, etc.):
9608
9608
 
9609
9609
  `draft discover` is the single front door for all investigation, auditing, pattern learning, and quality exploration activities.
9610
9610
 
@@ -17030,7 +17030,7 @@ If `draft/graph/schema.yaml` exists, the project has automated graph analysis da
17030
17030
 
17031
17031
  | File | Purpose | Content |
17032
17032
  |------|---------|---------|
17033
- | `draft/graph/schema.yaml` | Gate marker (engine + project metadata + point-of-index counts); presence gates graph use | YAML, ~15 lines |
17033
+ | `draft/graph/schema.yaml` | Gate marker (engine metadata + point-of-index counts); presence gates graph use | YAML, ~10 lines |
17034
17034
 
17035
17035
  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).
17036
17036
 
@@ -17558,7 +17558,7 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
17558
17558
  | Input | Path | Description |
17559
17559
  |-------|------|-------------|
17560
17560
  | architecture.md | `draft/architecture.md` | Comprehensive human-readable engineering reference (source of truth) |
17561
- | schema.yaml | `draft/graph/schema.yaml` | Graph metrics for tier computation (optional — skip if absent) |
17561
+ | Architecture view | `scripts/tools/graph-arch.sh --repo .` | Live graph metrics for tier computation (optional — skip when `draft/graph/schema.yaml` is absent) |
17562
17562
 
17563
17563
  ## Outputs
17564
17564
 
@@ -17571,11 +17571,12 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
17571
17571
 
17572
17572
  ## Target Size
17573
17573
 
17574
- Compute tier from `draft/graph/schema.yaml` after graph build:
17574
+ Compute tier from the live architecture view (the same metrics as `draft init` Step 1.4.5 — `schema.yaml` carries only counts, not these):
17575
17575
 
17576
- M = stats.modules
17577
- F = stats.go_functions + stats.py_functions
17578
- P = stats.proto_rpcs
17576
+ ARCH=$("$DRAFT_TOOLS/graph-arch.sh" --repo .) (DRAFT_TOOLS: resolver below)
17577
+ M = $ARCH | jq '.packages | length' (modules)
17578
+ F = $ARCH | jq '[.node_labels[] | select(.label=="Function" or .label=="Method") | .count] | add // 0' (functions + methods)
17579
+ P = $ARCH | jq '.routes | length' (routes / RPCs)
17579
17580
 
17580
17581
  | Tier | Label | Condition | Budget |
17581
17582
  |------|--------|----------------------------------------|---------------|
@@ -17585,7 +17586,7 @@ Compute tier from `draft/graph/schema.yaml` after graph build:
17585
17586
  | 4 | large | M≤100 AND F≤5000 AND P≤500 | 400–600 lines |
17586
17587
  | 5 | XL | M>100 OR F>5000 OR P>500 | 600–900 lines |
17587
17588
 
17588
- If `schema.yaml` does not exist: default to tier 2 (180–280 lines).
17589
+ If `schema.yaml` does not exist (no graph) or `graph-arch.sh` reports `source: unavailable`: default to tier 2 (180–280 lines).
17589
17590
 
17590
17591
  - Below tier minimum: incomplete condensation — ensure all sections are represented
17591
17592
  - Above tier maximum: insufficient compression — apply prioritization rules below
@@ -17705,7 +17706,7 @@ Before writing `draft/.ai-context.md`, verify:
17705
17706
  - [ ] No references to `architecture.md` (file must be self-contained)
17706
17707
  - [ ] All invariants from architecture.md are preserved
17707
17708
  - [ ] Extension cookbooks are complete (an agent can follow them without other files)
17708
- - [ ] Output is within tier budget bounds (compute from schema.yaml or default tier 2)
17709
+ - [ ] Output is within tier budget bounds (compute from `graph-arch.sh` or default tier 2)
17709
17710
  - [ ] GRAPH:HOTSPOTS present (or note "No hotspot data available" if graph absent)
17710
17711
  - [ ] GRAPH:CYCLES present ("None ✓" or cycle list; or note if graph absent)
17711
17712
  - [ ] GRAPH:MODULE-HOTSPOTS present for tier ≥ 3 (or note if no hotspot data)
@@ -18204,7 +18205,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
18204
18205
  |---|---|---|
18205
18206
  | `bash "$DRAFT_TOOLS/hotspot-rank.sh" [--top N]` | complexity-weighted hotspots | Emits `{hotspots:[],source:"unavailable"}` and exits 2 |
18206
18207
  | `bash "$DRAFT_TOOLS/cycle-detect.sh"` | call cycles | Emits `{cycles:[],source:"unavailable"}` and exits 2 |
18207
- | `bash "$DRAFT_TOOLS/mermaid-from-graph.sh" [--diagram module-deps\|co-change\|proto-map]` | diagram text | Emits an empty mermaid block and exits 2 |
18208
+ | `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 |
18208
18209
  | `bash "$DRAFT_TOOLS/graph-callers.sh" --symbol N [--transitive[=N]] [--prod-only] [--qualified]` | callers | `{callers:[],status:"unavailable",source:"unavailable"}`, exit 2 |
18209
18210
  | `bash "$DRAFT_TOOLS/graph-snippet.sh" --qualified N` | verified source + caller/callee counts | `{status:"unavailable",source:"unavailable"}`, exit 2 |
18210
18211
  | `bash "$DRAFT_TOOLS/graph-search.sh" --query "STR" [--limit N]` | semantic/ranked search | `{results:[],source:"unavailable"}`, exit 2 |
@@ -18216,7 +18217,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
18216
18217
  | `bash "$DRAFT_TOOLS/graph-query.sh" (--cypher STR \| --tool NAME --json '{...}')` | generic read-only passthrough | `{source:"unavailable"}`, exit 2 |
18217
18218
  | `bash "$DRAFT_TOOLS/graph-traces.sh" ingest --file F --experimental` | runtime traces (experimental write) | `{source:"unavailable"}`, exit 2 |
18218
18219
 
18219
- 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)).
18220
+ 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.
18220
18221
 
18221
18222
  ### Capability wrappers & dialect limits (graph-tooling-v2)
18222
18223
 
@@ -18224,8 +18225,9 @@ All Cypher lives in `scripts/tools/_graph_queries.sh` (the single source of quer
18224
18225
  truth). Wrappers are thin arg-parse → builder → fail-loud JSON. Three contracts
18225
18226
  matter when consuming them:
18226
18227
 
18227
- **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-snippet`,
18228
- `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`, `graph-errors`)
18228
+ **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-impact`,
18229
+ `graph-snippet`, `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`,
18230
+ `graph-errors`)
18229
18231
  emit a `status` field that distinguishes the three real outcomes — never read a
18230
18232
  bare `[]` as a confirmed true negative:
18231
18233
 
@@ -18234,9 +18236,11 @@ bare `[]` as a confirmed true negative:
18234
18236
  | `ok` | node found, edges returned |
18235
18237
  | `no-edges` | node exists but has no matching edge (a *real* negative) |
18236
18238
  | `no-match` | the named symbol was not found at all (check the name / try `--qualified`) |
18237
- | `unavailable` | engine could not be resolved (exit 2) |
18239
+ | `unavailable` | engine could not be resolved, or engine returned shapeless/non-row JSON (exit 2) |
18238
18240
 
18239
- **Verified engine param shapes** (engine v0.8.xthe runtime source of truth is
18241
+ **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`.
18242
+
18243
+ **Verified engine param shapes** (engine v0.9.0 — the runtime source of truth is
18240
18244
  `get_graph_schema`; do not hardcode a property set):
18241
18245
 
18242
18246
  ```bash
@@ -18249,13 +18253,18 @@ get_graph_schema '{"project":P}' # → {node_labels:[{label,count,properties}
18249
18253
 
18250
18254
  **Cypher dialect — keep queries inside the SAFE set:**
18251
18255
 
18252
- - ✅ SAFE: fixed-length patterns, single/multi-hop explicit patterns, `=`, `<`,
18253
- `STARTS WITH`, `NOT x STARTS WITH`, `AND`, `OR`, relationship-type alternation
18254
- `[:A|B]`, simple `count(x)`.
18255
- - ❌ UNSAFE (rejected or silently empty): `coalesce()`, `<>` / `!=` / `<=` / `>=`,
18256
- `NOT EXISTS(...)`, `NOT (pattern)`, `WITH`-grouping aggregation, multi-pattern
18257
- joins. `graph-query.sh --cypher` returns the engine's raw error, not a silent
18256
+ - ✅ SAFE: `=`, `<>`/`!=`, `<`, `>`, `<=`, `>=` against a literal; `STARTS WITH`,
18257
+ `NOT x STARTS WITH`, `AND`, `OR`; explicit and variable-length patterns
18258
+ (`[:R*1..3]`, fixed depth `[:R*2..2]`); relationship-type alternation `[:A|B]`;
18259
+ `coalesce()`; `DISTINCT`; `count(x)`, `count(DISTINCT x)`; `WITH`-grouping
18260
+ aggregation.
18261
+ - UNSAFE: comparing one property against another (`a.x < b.x`), `NOT EXISTS(...)`,
18262
+ `NOT (pattern)`, path variables (`p=(...)`, `length(p)`) — all rejected; and
18263
+ multi-pattern joins (`MATCH (a)…, (b)…`), which parse but ignore `RETURN` and
18264
+ `LIMIT`. `graph-query.sh --cypher` returns the engine's raw error, not a silent
18258
18265
  empty — but the builders never emit these forms.
18266
+ - ⚠️ `LIMIT` applies before `DISTINCT`: `RETURN DISTINCT … LIMIT n` can return
18267
+ fewer than n rows while more exist. Judge truncation on raw rows.
18259
18268
 
18260
18269
  **Caveats consumers must respect:**
18261
18270
 
@@ -18290,22 +18299,22 @@ The only committed file is the gate marker:
18290
18299
 
18291
18300
  | File | Role |
18292
18301
  |------|------|
18293
- | `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`. |
18302
+ | `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`. |
18294
18303
 
18295
- 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.
18304
+ 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.
18296
18305
 
18297
18306
  ### How skills query (engine is the interface; jq is optional)
18298
18307
 
18299
- - **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.
18308
+ - **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.
18300
18309
  - **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.
18301
- - **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`).
18310
+ - **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`.
18302
18311
  - **`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.
18303
18312
 
18304
18313
  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.
18305
18314
 
18306
18315
  ## Query Tools
18307
18316
 
18308
- 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.
18317
+ 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.
18309
18318
 
18310
18319
  ### Callers — who calls this function?
18311
18320
 
@@ -18318,11 +18327,11 @@ Output: `{symbol, callers[{name, file}], source}`. Use when enumerating call sit
18318
18327
  ### Impact — blast radius of a file or symbol
18319
18328
 
18320
18329
  ```bash
18321
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # changed-file impact (working-tree diff)
18322
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # transitive callers of a function
18330
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # dependents of a file: its symbols' callers + its importers
18331
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # dependents (transitive callers) of a function
18323
18332
  ```
18324
18333
 
18325
- Output: `{target, kind, impacted[{name, file, hop}], source}`. Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
18334
+ 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.
18326
18335
 
18327
18336
  ### Hotspots — fan-in ranking
18328
18337
 
@@ -18433,7 +18442,7 @@ Unlocks any edge type or node property without a purpose-built wrapper. Write ve
18433
18442
  "$DRAFT_TOOLS/graph-snapshot.sh" --repo .
18434
18443
  ```
18435
18444
 
18436
- 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.
18445
+ 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.
18437
18446
 
18438
18447
  ## Finding the Engine (Resolution + Usage Report)
18439
18448
 
@@ -18472,9 +18481,9 @@ The engine indexes C/C++, Go, Python, TypeScript/JS, and more (tree-sitter, 159
18472
18481
  | Scenario | Behavior |
18473
18482
  |----------|----------|
18474
18483
  | No engine resolvable (or `DRAFT_MEMORY_DISABLE=1`) | Skip graph indexing in init; all skills proceed without graph data; tools emit `source: unavailable` |
18475
- | Engine present but a query fails | Warn and proceed; skills work without graph data |
18484
+ | Engine present but a query fails, or returns shapeless `{}` | Treat as unavailable — never as a true-negative empty result; skills proceed without graph data |
18476
18485
  | `draft/graph/schema.yaml` exists | Engine is wired — use live query tools as needed during the run |
18477
- | 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. |
18486
+ | 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. |
18478
18487
 
18479
18488
  </core-file>
18480
18489
 
@@ -20431,7 +20440,7 @@ graph:
20431
20440
  cpp: "{high}"
20432
20441
  go: "{approximate | high}"
20433
20442
  stats:
20434
- modules: "{N from schema.yaml}"
20443
+ modules: "{N from engine: get_architecture .packages}"
20435
20444
  edges: "{total_edges from engine: get_architecture .edges}"
20436
20445
  hotspots: "{N}"
20437
20446
  notes: "{explicit fidelity summary from engine: get_architecture .languages/.packages}"
@@ -6025,7 +6025,7 @@ After a phase passes review, refresh `metadata.json.impact` so future tracks can
6025
6025
  "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path>
6026
6026
  ```
6027
6027
 
6028
- Aggregate across all files: `downstream_files` = total unique downstream files (deduped), `downstream_modules` = union of `affected_modules`, `max_depth` = max across queries, `by_category` = sum of each query's `by_category`. If the graph is absent, leave these fields as zeros / empty arrays — the snapshot still records the directly-touched files.
6028
+ Aggregate across all files: `downstream_files` = count of the union of each query's `downstream_files` array, `downstream_modules` = union of `affected_modules`, `max_depth` = max across queries, `by_category` = sum of each query's `by_category`. If the graph is absent, leave these fields as zeros / empty arrays — the snapshot still records the directly-touched files.
6029
6029
 
6030
6030
  3. **Write metadata.json** with the populated `impact` block and `computed_at` set to the current timestamp.
6031
6031
 
@@ -9604,7 +9604,7 @@ Prefer `draft docs` going forward for all authoring requests. The legacy direct
9604
9604
 
9605
9605
  ## Discover Router
9606
9606
 
9607
- When user says "discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, index, etc.):
9607
+ When user says "discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, tour, impact, etc.):
9608
9608
 
9609
9609
  `draft discover` is the single front door for all investigation, auditing, pattern learning, and quality exploration activities.
9610
9610
 
@@ -17030,7 +17030,7 @@ If `draft/graph/schema.yaml` exists, the project has automated graph analysis da
17030
17030
 
17031
17031
  | File | Purpose | Content |
17032
17032
  |------|---------|---------|
17033
- | `draft/graph/schema.yaml` | Gate marker (engine + project metadata + point-of-index counts); presence gates graph use | YAML, ~15 lines |
17033
+ | `draft/graph/schema.yaml` | Gate marker (engine metadata + point-of-index counts); presence gates graph use | YAML, ~10 lines |
17034
17034
 
17035
17035
  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).
17036
17036
 
@@ -17558,7 +17558,7 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
17558
17558
  | Input | Path | Description |
17559
17559
  |-------|------|-------------|
17560
17560
  | architecture.md | `draft/architecture.md` | Comprehensive human-readable engineering reference (source of truth) |
17561
- | schema.yaml | `draft/graph/schema.yaml` | Graph metrics for tier computation (optional — skip if absent) |
17561
+ | Architecture view | `scripts/tools/graph-arch.sh --repo .` | Live graph metrics for tier computation (optional — skip when `draft/graph/schema.yaml` is absent) |
17562
17562
 
17563
17563
  ## Outputs
17564
17564
 
@@ -17571,11 +17571,12 @@ Any skill that mutates `architecture.md` should execute this subroutine afterwar
17571
17571
 
17572
17572
  ## Target Size
17573
17573
 
17574
- Compute tier from `draft/graph/schema.yaml` after graph build:
17574
+ Compute tier from the live architecture view (the same metrics as `draft init` Step 1.4.5 — `schema.yaml` carries only counts, not these):
17575
17575
 
17576
- M = stats.modules
17577
- F = stats.go_functions + stats.py_functions
17578
- P = stats.proto_rpcs
17576
+ ARCH=$("$DRAFT_TOOLS/graph-arch.sh" --repo .) (DRAFT_TOOLS: resolver below)
17577
+ M = $ARCH | jq '.packages | length' (modules)
17578
+ F = $ARCH | jq '[.node_labels[] | select(.label=="Function" or .label=="Method") | .count] | add // 0' (functions + methods)
17579
+ P = $ARCH | jq '.routes | length' (routes / RPCs)
17579
17580
 
17580
17581
  | Tier | Label | Condition | Budget |
17581
17582
  |------|--------|----------------------------------------|---------------|
@@ -17585,7 +17586,7 @@ Compute tier from `draft/graph/schema.yaml` after graph build:
17585
17586
  | 4 | large | M≤100 AND F≤5000 AND P≤500 | 400–600 lines |
17586
17587
  | 5 | XL | M>100 OR F>5000 OR P>500 | 600–900 lines |
17587
17588
 
17588
- If `schema.yaml` does not exist: default to tier 2 (180–280 lines).
17589
+ If `schema.yaml` does not exist (no graph) or `graph-arch.sh` reports `source: unavailable`: default to tier 2 (180–280 lines).
17589
17590
 
17590
17591
  - Below tier minimum: incomplete condensation — ensure all sections are represented
17591
17592
  - Above tier maximum: insufficient compression — apply prioritization rules below
@@ -17705,7 +17706,7 @@ Before writing `draft/.ai-context.md`, verify:
17705
17706
  - [ ] No references to `architecture.md` (file must be self-contained)
17706
17707
  - [ ] All invariants from architecture.md are preserved
17707
17708
  - [ ] Extension cookbooks are complete (an agent can follow them without other files)
17708
- - [ ] Output is within tier budget bounds (compute from schema.yaml or default tier 2)
17709
+ - [ ] Output is within tier budget bounds (compute from `graph-arch.sh` or default tier 2)
17709
17710
  - [ ] GRAPH:HOTSPOTS present (or note "No hotspot data available" if graph absent)
17710
17711
  - [ ] GRAPH:CYCLES present ("None ✓" or cycle list; or note if graph absent)
17711
17712
  - [ ] GRAPH:MODULE-HOTSPOTS present for tier ≥ 3 (or note if no hotspot data)
@@ -18204,7 +18205,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
18204
18205
  |---|---|---|
18205
18206
  | `bash "$DRAFT_TOOLS/hotspot-rank.sh" [--top N]` | complexity-weighted hotspots | Emits `{hotspots:[],source:"unavailable"}` and exits 2 |
18206
18207
  | `bash "$DRAFT_TOOLS/cycle-detect.sh"` | call cycles | Emits `{cycles:[],source:"unavailable"}` and exits 2 |
18207
- | `bash "$DRAFT_TOOLS/mermaid-from-graph.sh" [--diagram module-deps\|co-change\|proto-map]` | diagram text | Emits an empty mermaid block and exits 2 |
18208
+ | `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 |
18208
18209
  | `bash "$DRAFT_TOOLS/graph-callers.sh" --symbol N [--transitive[=N]] [--prod-only] [--qualified]` | callers | `{callers:[],status:"unavailable",source:"unavailable"}`, exit 2 |
18209
18210
  | `bash "$DRAFT_TOOLS/graph-snippet.sh" --qualified N` | verified source + caller/callee counts | `{status:"unavailable",source:"unavailable"}`, exit 2 |
18210
18211
  | `bash "$DRAFT_TOOLS/graph-search.sh" --query "STR" [--limit N]` | semantic/ranked search | `{results:[],source:"unavailable"}`, exit 2 |
@@ -18216,7 +18217,7 @@ DRAFT_TOOLS="${DRAFT_PLUGIN_ROOT:-$(cat ~/.cache/draft/plugin-root 2>/dev/null)}
18216
18217
  | `bash "$DRAFT_TOOLS/graph-query.sh" (--cypher STR \| --tool NAME --json '{...}')` | generic read-only passthrough | `{source:"unavailable"}`, exit 2 |
18217
18218
  | `bash "$DRAFT_TOOLS/graph-traces.sh" ingest --file F --experimental` | runtime traces (experimental write) | `{source:"unavailable"}`, exit 2 |
18218
18219
 
18219
- 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)).
18220
+ 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.
18220
18221
 
18221
18222
  ### Capability wrappers & dialect limits (graph-tooling-v2)
18222
18223
 
@@ -18224,8 +18225,9 @@ All Cypher lives in `scripts/tools/_graph_queries.sh` (the single source of quer
18224
18225
  truth). Wrappers are thin arg-parse → builder → fail-loud JSON. Three contracts
18225
18226
  matter when consuming them:
18226
18227
 
18227
- **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-snippet`,
18228
- `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`, `graph-errors`)
18228
+ **Fail-loud status.** Symbol-scoped wrappers (`graph-callers`, `graph-impact`,
18229
+ `graph-snippet`, `graph-tests --symbol`, `graph-hierarchy --symbol/--derived`,
18230
+ `graph-errors`)
18229
18231
  emit a `status` field that distinguishes the three real outcomes — never read a
18230
18232
  bare `[]` as a confirmed true negative:
18231
18233
 
@@ -18234,9 +18236,11 @@ bare `[]` as a confirmed true negative:
18234
18236
  | `ok` | node found, edges returned |
18235
18237
  | `no-edges` | node exists but has no matching edge (a *real* negative) |
18236
18238
  | `no-match` | the named symbol was not found at all (check the name / try `--qualified`) |
18237
- | `unavailable` | engine could not be resolved (exit 2) |
18239
+ | `unavailable` | engine could not be resolved, or engine returned shapeless/non-row JSON (exit 2) |
18238
18240
 
18239
- **Verified engine param shapes** (engine v0.8.xthe runtime source of truth is
18241
+ **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`.
18242
+
18243
+ **Verified engine param shapes** (engine v0.9.0 — the runtime source of truth is
18240
18244
  `get_graph_schema`; do not hardcode a property set):
18241
18245
 
18242
18246
  ```bash
@@ -18249,13 +18253,18 @@ get_graph_schema '{"project":P}' # → {node_labels:[{label,count,properties}
18249
18253
 
18250
18254
  **Cypher dialect — keep queries inside the SAFE set:**
18251
18255
 
18252
- - ✅ SAFE: fixed-length patterns, single/multi-hop explicit patterns, `=`, `<`,
18253
- `STARTS WITH`, `NOT x STARTS WITH`, `AND`, `OR`, relationship-type alternation
18254
- `[:A|B]`, simple `count(x)`.
18255
- - ❌ UNSAFE (rejected or silently empty): `coalesce()`, `<>` / `!=` / `<=` / `>=`,
18256
- `NOT EXISTS(...)`, `NOT (pattern)`, `WITH`-grouping aggregation, multi-pattern
18257
- joins. `graph-query.sh --cypher` returns the engine's raw error, not a silent
18256
+ - ✅ SAFE: `=`, `<>`/`!=`, `<`, `>`, `<=`, `>=` against a literal; `STARTS WITH`,
18257
+ `NOT x STARTS WITH`, `AND`, `OR`; explicit and variable-length patterns
18258
+ (`[:R*1..3]`, fixed depth `[:R*2..2]`); relationship-type alternation `[:A|B]`;
18259
+ `coalesce()`; `DISTINCT`; `count(x)`, `count(DISTINCT x)`; `WITH`-grouping
18260
+ aggregation.
18261
+ - UNSAFE: comparing one property against another (`a.x < b.x`), `NOT EXISTS(...)`,
18262
+ `NOT (pattern)`, path variables (`p=(...)`, `length(p)`) — all rejected; and
18263
+ multi-pattern joins (`MATCH (a)…, (b)…`), which parse but ignore `RETURN` and
18264
+ `LIMIT`. `graph-query.sh --cypher` returns the engine's raw error, not a silent
18258
18265
  empty — but the builders never emit these forms.
18266
+ - ⚠️ `LIMIT` applies before `DISTINCT`: `RETURN DISTINCT … LIMIT n` can return
18267
+ fewer than n rows while more exist. Judge truncation on raw rows.
18259
18268
 
18260
18269
  **Caveats consumers must respect:**
18261
18270
 
@@ -18290,22 +18299,22 @@ The only committed file is the gate marker:
18290
18299
 
18291
18300
  | File | Role |
18292
18301
  |------|------|
18293
- | `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`. |
18302
+ | `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`. |
18294
18303
 
18295
- 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.
18304
+ 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.
18296
18305
 
18297
18306
  ### How skills query (engine is the interface; jq is optional)
18298
18307
 
18299
- - **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.
18308
+ - **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.
18300
18309
  - **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.
18301
- - **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`).
18310
+ - **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`.
18302
18311
  - **`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.
18303
18312
 
18304
18313
  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.
18305
18314
 
18306
18315
  ## Query Tools
18307
18316
 
18308
- 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.
18317
+ 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.
18309
18318
 
18310
18319
  ### Callers — who calls this function?
18311
18320
 
@@ -18318,11 +18327,11 @@ Output: `{symbol, callers[{name, file}], source}`. Use when enumerating call sit
18318
18327
  ### Impact — blast radius of a file or symbol
18319
18328
 
18320
18329
  ```bash
18321
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # changed-file impact (working-tree diff)
18322
- "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # transitive callers of a function
18330
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path> # dependents of a file: its symbols' callers + its importers
18331
+ "$DRAFT_TOOLS/graph-impact.sh" --repo . --symbol <name> # dependents (transitive callers) of a function
18323
18332
  ```
18324
18333
 
18325
- Output: `{target, kind, impacted[{name, file, hop}], source}`. Use when sizing risk before modifying a file or symbol, especially high-fan-in hotspots.
18334
+ 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.
18326
18335
 
18327
18336
  ### Hotspots — fan-in ranking
18328
18337
 
@@ -18433,7 +18442,7 @@ Unlocks any edge type or node property without a purpose-built wrapper. Write ve
18433
18442
  "$DRAFT_TOOLS/graph-snapshot.sh" --repo .
18434
18443
  ```
18435
18444
 
18436
- 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.
18445
+ 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.
18437
18446
 
18438
18447
  ## Finding the Engine (Resolution + Usage Report)
18439
18448
 
@@ -18472,9 +18481,9 @@ The engine indexes C/C++, Go, Python, TypeScript/JS, and more (tree-sitter, 159
18472
18481
  | Scenario | Behavior |
18473
18482
  |----------|----------|
18474
18483
  | No engine resolvable (or `DRAFT_MEMORY_DISABLE=1`) | Skip graph indexing in init; all skills proceed without graph data; tools emit `source: unavailable` |
18475
- | Engine present but a query fails | Warn and proceed; skills work without graph data |
18484
+ | Engine present but a query fails, or returns shapeless `{}` | Treat as unavailable — never as a true-negative empty result; skills proceed without graph data |
18476
18485
  | `draft/graph/schema.yaml` exists | Engine is wired — use live query tools as needed during the run |
18477
- | 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. |
18486
+ | 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. |
18478
18487
 
18479
18488
  </core-file>
18480
18489
 
@@ -20431,7 +20440,7 @@ graph:
20431
20440
  cpp: "{high}"
20432
20441
  go: "{approximate | high}"
20433
20442
  stats:
20434
- modules: "{N from schema.yaml}"
20443
+ modules: "{N from engine: get_architecture .packages}"
20435
20444
  edges: "{total_edges from engine: get_architecture .edges}"
20436
20445
  hotspots: "{N}"
20437
20446
  notes: "{explicit fidelity summary from engine: get_architecture .languages/.packages}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drafthq/draft",
3
- "version": "3.7.2",
3
+ "version": "5.0.1",
4
4
  "description": "Context-Driven Development for AI coding agents — install Draft into Claude Code, Cursor, Codex, or opencode.",
5
5
  "bin": {
6
6
  "draft": "cli/bin/draft.js"
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "scripts": {
27
27
  "test": "bash tests/test-cli.sh",
28
- "version": "bash scripts/sync-version.sh && git add .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json",
28
+ "version": "bash scripts/sync-version.sh --stage",
29
29
  "prepublishOnly": "bash scripts/build-integrations.sh"
30
30
  },
31
31
  "repository": {
@@ -37,7 +37,11 @@
37
37
  "url": "https://github.com/drafthq/draft/issues"
38
38
  },
39
39
  "license": "MIT",
40
- "author": "mayurpise",
40
+ "author": {
41
+ "name": "Mayur Pise",
42
+ "email": "mayur@getdraft.dev",
43
+ "url": "https://github.com/mayurpise"
44
+ },
41
45
  "publishConfig": {
42
46
  "access": "public"
43
47
  },
@@ -3,8 +3,9 @@
3
3
  #
4
4
  # The engine is the codebase-memory-mcp single static binary. This script fetches
5
5
  # the release archive for the host OS/arch from GitHub Releases, verifies its
6
- # SHA-256 against the published checksums.txt, extracts it, and installs the
7
- # binary to the Draft-managed location (~/.cache/draft/bin/codebase-memory-mcp),
6
+ # SHA-256 (against hashes pinned below for the default version, else against the
7
+ # release's checksums.txt), extracts it, and installs the binary to the
8
+ # Draft-managed location (~/.cache/draft/bin/codebase-memory-mcp),
8
9
  # which scripts/tools/_lib.sh:find_memory_bin resolves.
9
10
  #
10
11
  # Pinned by default for reproducibility; override with CMM_VERSION (a tag, e.g.
@@ -44,9 +45,15 @@ while [[ $# -gt 0 ]]; do
44
45
  done
45
46
 
46
47
  BIN_PATH="$DEST/codebase-memory-mcp"
48
+ # Keep an existing install only at the requested version, so a pin bump upgrades
49
+ # it. "latest" cannot be compared offline; any existing binary satisfies it.
47
50
  if [[ -x "$BIN_PATH" && $FORCE -eq 0 ]]; then
48
- echo "codebase-memory-mcp already installed at $BIN_PATH ($("$BIN_PATH" --version 2>/dev/null || echo unknown))"
49
- exit 0
51
+ have="$("$BIN_PATH" --version 2>/dev/null | awk '{print $NF}' || true)"
52
+ if [[ "$VERSION" == "latest" || "$have" == "${VERSION#v}" ]]; then
53
+ echo "codebase-memory-mcp already installed at $BIN_PATH (${have:-unknown})"
54
+ exit 0
55
+ fi
56
+ echo "Installed engine is ${have:-unknown}; replacing it with ${VERSION}."
50
57
  fi
51
58
 
52
59
  # --- Detect OS / arch (mirrors the engine's own install.sh naming) ---
@@ -80,7 +87,7 @@ TMP="$(mktemp -d)"
80
87
  trap 'rm -rf "$TMP"' EXIT
81
88
 
82
89
  echo "Fetching ${ARCHIVE} (${VERSION})..."
83
- if ! curl -fSL --max-time 300 -o "$TMP/$ARCHIVE" "$BASE/$ARCHIVE"; then
90
+ if ! curl -fSL --proto '=https' --proto-redir '=https' --max-time 300 -o "$TMP/$ARCHIVE" "$BASE/$ARCHIVE"; then
84
91
  echo "error: download failed: $BASE/$ARCHIVE" >&2
85
92
  exit 2
86
93
  fi
@@ -89,6 +96,28 @@ fi
89
96
  # A mismatch is always fatal. An *absent* checksum is fatal only under
90
97
  # DRAFT_STRICT_VERIFY=1 — otherwise it warns, so that a release without a
91
98
  # checksums.txt does not brick the install for everyone.
99
+ #
100
+ # checksums.txt ships in the same release as the archive, so it proves only that
101
+ # the download is intact: a replaced release asset passes it. The pinned
102
+ # version's archives are checked against SHA-256 values recorded here instead
103
+ # (copied from its checksums.txt when the pin was bumped) — update them with
104
+ # DEFAULT_VERSION.
105
+ pinned_sha256() {
106
+ [[ "$VERSION" == "$DEFAULT_VERSION" ]] || return 0
107
+ case "$1" in
108
+ codebase-memory-mcp-darwin-amd64.tar.gz) echo 6af3d02a27f589901fa763d3971089337bc8c9838bbed5d0cf543ca9f1a9e543 ;;
109
+ codebase-memory-mcp-darwin-arm64.tar.gz) echo faa02f0404230c451a9812230394481948f80183801fa5bf67044b41c2f25ed4 ;;
110
+ codebase-memory-mcp-linux-amd64-portable.tar.gz) echo 8459d5c9d1457f2c82de3de307ffc7641ecbba2dde893427be1e62eca8ef9b25 ;;
111
+ codebase-memory-mcp-linux-arm64-portable.tar.gz) echo b0a43fdaf534073c16707d72726b73b149d4c1212034b281ee8b7b2dac755107 ;;
112
+ esac
113
+ }
114
+ sha256_of() {
115
+ if command -v sha256sum >/dev/null 2>&1; then
116
+ sha256sum "$1" | awk '{print $1}'
117
+ else
118
+ shasum -a 256 "$1" | awk '{print $1}'
119
+ fi
120
+ }
92
121
  STRICT="${DRAFT_STRICT_VERIFY:-0}"
93
122
  unverified() {
94
123
  if [[ "$STRICT" == "1" ]]; then
@@ -98,14 +127,18 @@ unverified() {
98
127
  echo " warning: $1 — skipping verification (set DRAFT_STRICT_VERIFY=1 to make this fatal)" >&2
99
128
  }
100
129
 
101
- if curl -fsSL --max-time 60 -o "$TMP/checksums.txt" "$BASE/checksums.txt" 2>/dev/null; then
130
+ pinned="$(pinned_sha256 "$ARCHIVE")"
131
+ if [[ -n "$pinned" ]]; then
132
+ actual="$(sha256_of "$TMP/$ARCHIVE")"
133
+ if [[ "$pinned" != "$actual" ]]; then
134
+ echo "error: $ARCHIVE does not match the SHA-256 pinned for $VERSION (expected $pinned, got $actual)" >&2
135
+ exit 2
136
+ fi
137
+ echo " checksum OK (pinned $pinned)"
138
+ elif curl -fsSL --proto '=https' --proto-redir '=https' --max-time 60 -o "$TMP/checksums.txt" "$BASE/checksums.txt" 2>/dev/null; then
102
139
  expected="$(grep " $ARCHIVE\$" "$TMP/checksums.txt" 2>/dev/null | awk '{print $1}' | head -1 || true)"
103
140
  if [[ -n "$expected" ]]; then
104
- if command -v sha256sum >/dev/null 2>&1; then
105
- actual="$(sha256sum "$TMP/$ARCHIVE" | awk '{print $1}')"
106
- else
107
- actual="$(shasum -a 256 "$TMP/$ARCHIVE" | awk '{print $1}')"
108
- fi
141
+ actual="$(sha256_of "$TMP/$ARCHIVE")"
109
142
  if [[ "$expected" != "$actual" ]]; then
110
143
  echo "error: checksum mismatch for $ARCHIVE (expected $expected, got $actual)" >&2
111
144
  exit 2
package/scripts/lib.sh CHANGED
@@ -84,7 +84,7 @@ SKILL_META=(
84
84
  'plan|Plan Router|"plan feature" or "draft plan <intent>" (new-track, decompose, adr, tech-debt, change)'
85
85
  'ops|Ops Router|"ops deploy" or "draft ops <intent>" (deploy-checklist, incident, standup, status, revert)'
86
86
  'docs|Docs Router|"write docs" or "draft docs <intent>" (documentation)'
87
- 'discover|Discover Router|"discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, index, etc.)'
87
+ 'discover|Discover Router|"discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, tour, impact, etc.)'
88
88
  'jira|Jira Router|"jira preview", "jira create", or "jira review <ID>"'
89
89
  'integrations|Integrations Router|"integrations", "integrations jira-preview", or "integrations jira-create"'
90
90
  'quick-review|Quick Review Command|"quick review" or "draft quick-review [file|pr <number>]"'