@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
@@ -226,7 +226,7 @@ render_architecture() {
226
226
  if [[ -x "$SCRIPT_DIR/okf-fix-links.sh" ]]; then
227
227
  draft_dir="$(cd "$(dirname "$out")" && pwd)"
228
228
  if [[ -d "$draft_dir/wiki" || "$(basename "$BUNDLE")" == "wiki" ]]; then
229
- "$SCRIPT_DIR/okf-fix-links.sh" --file "$out" --wiki "$BUNDLE" --fix >/dev/null 2>&1 || true
229
+ "$SCRIPT_DIR/okf-fix-links.sh" --file "$out" --wiki "$BUNDLE" --fix >/dev/null 2>&1 || exit 1
230
230
  fi
231
231
  fi
232
232
  echo "rendered architecture view → $out ($(ordered_pages | grep -c . ) pages)"
@@ -411,13 +411,15 @@ function resolve(base, href){
411
411
  if(p==='..') out.pop(); else if(p!=='.'&&p!=='') out.push(p); }
412
412
  return out.join('/');
413
413
  }
414
+ function escAttr(s){return esc(s).replace(/"/g,'"');}
414
415
  function inline(s, base){
415
416
  s = s.replace(/`([^`]+)`/g, function(m,c){return '<code>'+esc(c)+'</code>';});
416
417
  s = s.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, function(m,t,u){
417
- if(/^[a-z]+:\/\//.test(u)) return '<a href="'+u+'" target="_blank" rel="noopener">'+t+'</a>';
418
+ if(/^(https?|mailto):/i.test(u)) return '<a href="'+escAttr(u)+'" target="_blank" rel="noopener">'+t+'</a>';
419
+ if(/^[a-z][a-z0-9+.-]*:/i.test(u)) return t;
418
420
  var key=resolve(base,u);
419
421
  if(PAGES[key]) return '<a href="#'+key+'" data-nav="'+key+'">'+t+'</a>';
420
- return '<span title="'+esc(u)+'">'+t+'</span>';
422
+ return '<span title="'+escAttr(u)+'">'+t+'</span>';
421
423
  });
422
424
  s = s.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');
423
425
  s = s.replace(/(^|[^*])\*([^*\n]+)\*/g,'$1<em>$2</em>');
@@ -438,7 +440,7 @@ function render(md, base){
438
440
  out+='<table>';
439
441
  rows.forEach(function(r,ri){
440
442
  var cells=r.replace(/^\||\|$/g,'').split('|');
441
- out+='<tr>'+cells.map(function(c){var t=ri===0?'th':'td';return '<'+t+'>'+inline(c.trim(),base)+'</'+t+'>';}).join('')+'</tr>';
443
+ out+='<tr>'+cells.map(function(c){var t=ri===0?'th':'td';return '<'+t+'>'+inline(esc(c.trim()),base)+'</'+t+'>';}).join('')+'</tr>';
442
444
  });
443
445
  out+='</table>'; tbl=[];
444
446
  }
@@ -252,6 +252,11 @@ if [[ -n "$PATH_INDEX" ]]; then
252
252
  # span lines and a line-oriented match would silently skip them.
253
253
  while IFS= read -r ref; do
254
254
  [[ -z "$ref" ]] && continue
255
+ case "$ref" in
256
+ /*|*..*)
257
+ add_error "path-index escapes the bundle: $ref"
258
+ continue ;;
259
+ esac
255
260
  if [[ ! -f "$BUNDLE/$ref" ]]; then
256
261
  add_error "path-index references missing concept page: $ref"
257
262
  fi
@@ -53,8 +53,10 @@ resolve() {
53
53
  # 2. Install marker written by `draft install` (authoritative).
54
54
  local marker="$HOME/.cache/draft/plugin-root"
55
55
  if [ -f "$marker" ]; then
56
- d="$(cat "$marker" 2>/dev/null)/scripts/tools"
57
- [ -d "$d" ] && { printf '%s' "$d"; return 0; }
56
+ local root
57
+ root="$(cat "$marker" 2>/dev/null || true)"
58
+ d="${root}/scripts/tools"
59
+ [ -n "$root" ] && [ -d "$d" ] && { printf '%s' "$d"; return 0; }
58
60
  fi
59
61
 
60
62
  # 3. ${CLAUDE_PLUGIN_ROOT} — set in hook/MCP contexts; harmless to probe.
@@ -66,7 +68,7 @@ resolve() {
66
68
  if command -v jq >/dev/null 2>&1 && [ -f "$reg" ]; then
67
69
  local ip
68
70
  ip="$(jq -r '.plugins | to_entries[] | select(.key|startswith("draft@")) | .value[0].installPath' \
69
- "$reg" 2>/dev/null | head -1)"
71
+ "$reg" 2>/dev/null | head -1 || true)"
70
72
  [ -n "$ip" ] && [ -d "$ip/scripts/tools" ] && { printf '%s' "$ip/scripts/tools"; return 0; }
71
73
  fi
72
74
 
@@ -78,7 +78,15 @@ emit_array() {
78
78
  local first=true
79
79
  printf '['
80
80
  while IFS= read -r _ln; do
81
- path="${_ln%%:*}"; rest="${_ln#*:}"; linenum="${rest%%:*}"; rest="${rest#*:}"
81
+ # Path may contain colons (e.g. src/foo:bar.js). The line number is the
82
+ # last :<digits>: separator git-grep/rg -n emit.
83
+ if [[ "$_ln" =~ ^(.+):([0-9]+):(.*)$ ]]; then
84
+ path="${BASH_REMATCH[1]}"
85
+ linenum="${BASH_REMATCH[2]}"
86
+ rest="${BASH_REMATCH[3]}"
87
+ else
88
+ continue
89
+ fi
82
90
  [[ -z "$path" || -z "$linenum" ]] && continue
83
91
  # Skip binaries or tool's own output directory.
84
92
  [[ "$path" == */.git/* ]] && continue
@@ -137,12 +137,24 @@ fi
137
137
 
138
138
  STATUS="ok"
139
139
 
140
+ # The wrappers are verified against the pinned DEFAULT_VERSION. Another version
141
+ # (e.g. a global install on PATH, which outranks the managed one) stays usable,
142
+ # but may speak a different dialect or CLI — say so rather than run it silently.
143
+ VERSION="$("$MEMORY_BIN" --version 2>/dev/null | awk '{print $NF}')"
144
+ PINNED="$(sed -n 's/^DEFAULT_VERSION="v\{0,1\}\([^"]*\)".*/\1/p' "$SELF_REPO/scripts/fetch-memory-engine.sh" 2>/dev/null | head -1)"
145
+ if [[ -n "$PINNED" && "$VERSION" != "$PINNED" ]]; then
146
+ echo "WARNING: engine version $VERSION differs from the pinned $PINNED that Draft's graph tools are verified against." >&2
147
+ echo " Install the pinned engine: scripts/fetch-memory-engine.sh --force" >&2
148
+ fi
149
+
140
150
  if [[ $EMIT_JSON -eq 1 ]]; then
141
- printf '{"status":"%s","engine_bin":"%s","source":"%s","arch":"%s"}\n' \
142
- "$(json_escape "$STATUS")" "$(json_escape "$MEMORY_BIN")" "$(json_escape "$SOURCE")" "$(json_escape "$ARCH")"
151
+ printf '{"status":"%s","engine_bin":"%s","source":"%s","arch":"%s","version":"%s","pinned_version":"%s"}\n' \
152
+ "$(json_escape "$STATUS")" "$(json_escape "$MEMORY_BIN")" "$(json_escape "$SOURCE")" "$(json_escape "$ARCH")" \
153
+ "$(json_escape "$VERSION")" "$(json_escape "$PINNED")"
143
154
  else
144
155
  echo "Draft graph engine: $MEMORY_BIN"
145
156
  echo " source: $SOURCE (arch=$ARCH)"
157
+ echo " version: $VERSION (pinned: ${PINNED:-unknown})"
146
158
  echo " status: $STATUS"
147
159
  fi
148
160
 
@@ -27,7 +27,7 @@ Draft commands can be invoked using natural language. If you describe your goal
27
27
  | "debug this issue", "investigate test failure", "fix crash" | `/draft:debug` | Structured 4-stage debugging workflow |
28
28
  | "deploy checklist", "release checks", "pre-flight checks" | `/draft:deploy-checklist` | Pre-deployment verification checklist |
29
29
  | "test strategy", "design test suite", "testing targets" | `/draft:testing-strategy` | Design standard testing plan |
30
- | "tech debt analysis", "catalog debt", "code debt" | `/draft:tech-debt` | Technical debt audit across 6 dimensions |
30
+ | "tech debt analysis", "catalog debt", "code debt" | `/draft:tech-debt` | Technical debt audit across 7 dimensions |
31
31
  | "weekly standup", "what did I do today", "activity summary" | `/draft:standup` | Summarize recent Git and file contributions |
32
32
  | "incident", "production outage", "mitigate bug" | `/draft:incident-response` | Triage, mitigation, and postmortem incident flow |
33
33
  | "write docs", "create readme", "api documentation" | `/draft:documentation` | Generate professional, structured docs |
@@ -671,7 +671,7 @@ After a phase passes review, refresh `metadata.json.impact` so future tracks can
671
671
  "$DRAFT_TOOLS/graph-impact.sh" --repo . --file <path>
672
672
  ```
673
673
 
674
- 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.
674
+ 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.
675
675
 
676
676
  3. **Write metadata.json** with the populated `impact` block and `computed_at` set to the current timestamp.
677
677