@drafthq/draft 2.7.0

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 (158) hide show
  1. package/.claude-plugin/marketplace.json +38 -0
  2. package/.claude-plugin/plugin.json +26 -0
  3. package/LICENSE +21 -0
  4. package/README.md +272 -0
  5. package/bin/README.md +49 -0
  6. package/cli/bin/draft.js +13 -0
  7. package/cli/src/cli.js +113 -0
  8. package/cli/src/hosts/claude-code.js +46 -0
  9. package/cli/src/hosts/codex.js +33 -0
  10. package/cli/src/hosts/cursor.js +50 -0
  11. package/cli/src/hosts/index.js +24 -0
  12. package/cli/src/hosts/opencode.js +39 -0
  13. package/cli/src/installer.js +61 -0
  14. package/cli/src/lib/fsx.js +34 -0
  15. package/cli/src/lib/graph.js +23 -0
  16. package/cli/src/lib/log.js +32 -0
  17. package/cli/src/lib/paths.js +14 -0
  18. package/core/agents/architect.md +338 -0
  19. package/core/agents/debugger.md +193 -0
  20. package/core/agents/ops.md +104 -0
  21. package/core/agents/planner.md +158 -0
  22. package/core/agents/rca.md +314 -0
  23. package/core/agents/reviewer.md +256 -0
  24. package/core/agents/writer.md +110 -0
  25. package/core/guardrails/README.md +4 -0
  26. package/core/guardrails/code-quality.md +4 -0
  27. package/core/guardrails/dependency-triage.md +4 -0
  28. package/core/guardrails/design-norms.md +4 -0
  29. package/core/guardrails/language-standards.md +4 -0
  30. package/core/guardrails/review-checks.md +4 -0
  31. package/core/guardrails/secure-patterns.md +4 -0
  32. package/core/guardrails/security.md +4 -0
  33. package/core/guardrails.md +22 -0
  34. package/core/knowledge-base.md +127 -0
  35. package/core/methodology.md +1221 -0
  36. package/core/shared/condensation.md +224 -0
  37. package/core/shared/context-verify.md +44 -0
  38. package/core/shared/cross-skill-dispatch.md +127 -0
  39. package/core/shared/discovery-schema.md +75 -0
  40. package/core/shared/draft-context-loading.md +282 -0
  41. package/core/shared/git-report-metadata.md +106 -0
  42. package/core/shared/graph-query.md +239 -0
  43. package/core/shared/graph-usage-report.md +22 -0
  44. package/core/shared/jira-sync.md +170 -0
  45. package/core/shared/parallel-analysis.md +386 -0
  46. package/core/shared/parallel-fanout.md +10 -0
  47. package/core/shared/pattern-learning.md +146 -0
  48. package/core/shared/red-flags.md +58 -0
  49. package/core/shared/template-contract.md +22 -0
  50. package/core/shared/template-hygiene.md +10 -0
  51. package/core/shared/tool-resolver.md +10 -0
  52. package/core/shared/vcs-commands.md +97 -0
  53. package/core/shared/verification-gates.md +47 -0
  54. package/core/templates/CHANGELOG.md +70 -0
  55. package/core/templates/ai-context-export.md +8 -0
  56. package/core/templates/ai-context.md +270 -0
  57. package/core/templates/ai-profile.md +41 -0
  58. package/core/templates/architecture.md +203 -0
  59. package/core/templates/dependency-graph.md +103 -0
  60. package/core/templates/discovery.md +79 -0
  61. package/core/templates/guardrails.md +143 -0
  62. package/core/templates/hld.md +327 -0
  63. package/core/templates/intake-questions.md +403 -0
  64. package/core/templates/jira.md +119 -0
  65. package/core/templates/lld.md +283 -0
  66. package/core/templates/metadata.json +66 -0
  67. package/core/templates/plan.md +130 -0
  68. package/core/templates/product.md +110 -0
  69. package/core/templates/rca.md +86 -0
  70. package/core/templates/root-architecture.md +127 -0
  71. package/core/templates/root-product.md +53 -0
  72. package/core/templates/root-tech-stack.md +117 -0
  73. package/core/templates/service-index.md +55 -0
  74. package/core/templates/session-summary.md +8 -0
  75. package/core/templates/spec.md +165 -0
  76. package/core/templates/tech-matrix.md +101 -0
  77. package/core/templates/tech-stack.md +169 -0
  78. package/core/templates/track-architecture.md +311 -0
  79. package/core/templates/workflow.md +187 -0
  80. package/integrations/agents/AGENTS.md +24384 -0
  81. package/integrations/copilot/.github/copilot-instructions.md +24384 -0
  82. package/integrations/gemini/.gemini.md +26 -0
  83. package/package.json +53 -0
  84. package/scripts/fetch-memory-engine.sh +116 -0
  85. package/scripts/lib.sh +256 -0
  86. package/scripts/tools/_lib.sh +220 -0
  87. package/scripts/tools/adr-index.sh +117 -0
  88. package/scripts/tools/check-graph-usage-report.sh +95 -0
  89. package/scripts/tools/check-scope-conflicts.sh +139 -0
  90. package/scripts/tools/check-skill-line-caps.sh +115 -0
  91. package/scripts/tools/check-template-noop.sh +87 -0
  92. package/scripts/tools/check-track-hygiene.sh +230 -0
  93. package/scripts/tools/classify-files.sh +231 -0
  94. package/scripts/tools/cycle-detect.sh +75 -0
  95. package/scripts/tools/detect-test-framework.sh +135 -0
  96. package/scripts/tools/diff-templates-vs-tracks.sh +176 -0
  97. package/scripts/tools/emit-skill-metrics.sh +71 -0
  98. package/scripts/tools/fix-whitespace.sh +192 -0
  99. package/scripts/tools/freshness-check.sh +143 -0
  100. package/scripts/tools/git-metadata.sh +203 -0
  101. package/scripts/tools/graph-callers.sh +74 -0
  102. package/scripts/tools/graph-impact.sh +93 -0
  103. package/scripts/tools/graph-snapshot.sh +102 -0
  104. package/scripts/tools/hotspot-rank.sh +75 -0
  105. package/scripts/tools/manage-symlinks.sh +85 -0
  106. package/scripts/tools/mermaid-from-graph.sh +92 -0
  107. package/scripts/tools/migrate-track-frontmatter.sh +241 -0
  108. package/scripts/tools/parse-git-log.sh +135 -0
  109. package/scripts/tools/parse-reports.sh +114 -0
  110. package/scripts/tools/render-track.sh +145 -0
  111. package/scripts/tools/run-coverage.sh +153 -0
  112. package/scripts/tools/scan-markers.sh +144 -0
  113. package/scripts/tools/skill-caps.conf +24 -0
  114. package/scripts/tools/validate-frontmatter.sh +125 -0
  115. package/scripts/tools/verify-citations.sh +250 -0
  116. package/scripts/tools/verify-doc-anchors.sh +204 -0
  117. package/scripts/tools/verify-graph-binary.sh +154 -0
  118. package/skills/GRAPH.md +332 -0
  119. package/skills/adr/SKILL.md +374 -0
  120. package/skills/assist-review/SKILL.md +49 -0
  121. package/skills/bughunt/SKILL.md +668 -0
  122. package/skills/bughunt/references/regression-tests.md +399 -0
  123. package/skills/change/SKILL.md +267 -0
  124. package/skills/coverage/SKILL.md +336 -0
  125. package/skills/debug/SKILL.md +201 -0
  126. package/skills/decompose/SKILL.md +656 -0
  127. package/skills/deep-review/SKILL.md +326 -0
  128. package/skills/deploy-checklist/SKILL.md +254 -0
  129. package/skills/discover/SKILL.md +66 -0
  130. package/skills/docs/SKILL.md +42 -0
  131. package/skills/documentation/SKILL.md +197 -0
  132. package/skills/draft/SKILL.md +177 -0
  133. package/skills/draft/context-files.md +57 -0
  134. package/skills/draft/intent-mapping.md +37 -0
  135. package/skills/draft/quality-guide.md +51 -0
  136. package/skills/graph/SKILL.md +107 -0
  137. package/skills/impact/SKILL.md +86 -0
  138. package/skills/implement/SKILL.md +794 -0
  139. package/skills/incident-response/SKILL.md +245 -0
  140. package/skills/index/SKILL.md +848 -0
  141. package/skills/init/SKILL.md +1784 -0
  142. package/skills/init/references/architecture-spec.md +1259 -0
  143. package/skills/integrations/SKILL.md +53 -0
  144. package/skills/jira/SKILL.md +577 -0
  145. package/skills/jira/references/review.md +1322 -0
  146. package/skills/learn/SKILL.md +478 -0
  147. package/skills/new-track/SKILL.md +841 -0
  148. package/skills/ops/SKILL.md +57 -0
  149. package/skills/plan/SKILL.md +60 -0
  150. package/skills/quick-review/SKILL.md +216 -0
  151. package/skills/revert/SKILL.md +178 -0
  152. package/skills/review/SKILL.md +1114 -0
  153. package/skills/standup/SKILL.md +183 -0
  154. package/skills/status/SKILL.md +183 -0
  155. package/skills/tech-debt/SKILL.md +318 -0
  156. package/skills/testing-strategy/SKILL.md +195 -0
  157. package/skills/tour/SKILL.md +38 -0
  158. package/skills/upload/SKILL.md +117 -0
@@ -0,0 +1,26 @@
1
+ # Draft Methodology - Global Instructions
2
+
3
+ You are equipped with the **Draft Methodology** for Context-Driven Development.
4
+
5
+ **Core Principles:**
6
+ - **Measure twice, code once.**
7
+ - **Context -> Spec & Plan -> Implement.**
8
+ - Never implement without a plan.
9
+ - Never plan without a specification.
10
+ - Never specify without codebase context.
11
+
12
+ **Skill Locations:**
13
+ The authoritative Draft implementation skills are located at:
14
+ `~/.gemini/antigravity/skills/draft/skills`
15
+
16
+ **Automatic Workflow:**
17
+ 1. If the current directory does NOT have a `draft/` folder, recommend running `draft-init` (alias) or `@draft init`.
18
+ 2. Follow the protocols in `~/.gemini/antigravity/skills/draft/skills/<command>/SKILL.md` for all `@draft` commands.
19
+ 3. Use the standard markers: `[ ]` (Pending), `[~]` (In Progress), `[x]` (Completed), `[!]` (Blocked).
20
+
21
+ **Project Context:**
22
+ Always prioritize information in:
23
+ - `draft/.ai-context.md` (Dense AI context)
24
+ - `draft/architecture.md` (Human-readable guide)
25
+ - `draft/product.md` (Vision)
26
+ - `draft/tech-stack.md` (Constraints)
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@drafthq/draft",
3
+ "version": "2.7.0",
4
+ "description": "Context-Driven Development for AI coding agents — install Draft into Claude Code, Cursor, Codex, or opencode.",
5
+ "bin": {
6
+ "draft": "cli/bin/draft.js"
7
+ },
8
+ "type": "commonjs",
9
+ "engines": {
10
+ "node": ">=18"
11
+ },
12
+ "files": [
13
+ "cli/",
14
+ "skills/",
15
+ "core/",
16
+ ".claude-plugin/",
17
+ "integrations/",
18
+ "bin/",
19
+ "scripts/tools/",
20
+ "scripts/fetch-memory-engine.sh",
21
+ "scripts/lib.sh",
22
+ "LICENSE",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "test": "bash tests/test-cli.sh",
27
+ "prepublishOnly": "bash scripts/build-integrations.sh"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/drafthq/draft.git"
32
+ },
33
+ "homepage": "https://github.com/drafthq/draft",
34
+ "bugs": {
35
+ "url": "https://github.com/drafthq/draft/issues"
36
+ },
37
+ "license": "MIT",
38
+ "author": "mayurpise",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "keywords": [
43
+ "claude-code",
44
+ "cursor",
45
+ "codex",
46
+ "opencode",
47
+ "ai",
48
+ "agents",
49
+ "context-driven-development",
50
+ "draft",
51
+ "spec-driven-development"
52
+ ]
53
+ }
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env bash
2
+ # fetch-memory-engine.sh — download and verify the Draft knowledge-graph engine.
3
+ #
4
+ # The engine is the codebase-memory-mcp single static binary. This script fetches
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),
8
+ # which scripts/tools/_lib.sh:find_memory_bin resolves.
9
+ #
10
+ # Pinned by default for reproducibility; override with CMM_VERSION (a tag, e.g.
11
+ # "v0.7.0", or "latest").
12
+ #
13
+ # Usage:
14
+ # scripts/fetch-memory-engine.sh [--dest DIR] [--force]
15
+ #
16
+ # Env:
17
+ # CMM_VERSION Release tag to fetch (default: pinned DEFAULT_VERSION).
18
+ # CMM_DOWNLOAD_URL Override the release base URL (testing).
19
+ #
20
+ # Exit codes: 0 installed/already-present, 1 invocation error, 2 fetch/verify failure.
21
+ set -euo pipefail
22
+
23
+ REPO="DeusData/codebase-memory-mcp"
24
+ DEFAULT_VERSION="v0.7.0" # pinned; bump deliberately
25
+ VERSION="${CMM_VERSION:-$DEFAULT_VERSION}"
26
+ DEST="$HOME/.cache/draft/bin"
27
+ FORCE=0
28
+
29
+ usage() { sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//'; }
30
+
31
+ while [[ $# -gt 0 ]]; do
32
+ case "$1" in
33
+ --dest) DEST="$2"; shift 2 ;;
34
+ --force) FORCE=1; shift ;;
35
+ --help|-h) usage; exit 0 ;;
36
+ *) echo "Unknown flag: $1" >&2; usage >&2; exit 1 ;;
37
+ esac
38
+ done
39
+
40
+ BIN_PATH="$DEST/codebase-memory-mcp"
41
+ if [[ -x "$BIN_PATH" && $FORCE -eq 0 ]]; then
42
+ echo "codebase-memory-mcp already installed at $BIN_PATH ($("$BIN_PATH" --version 2>/dev/null || echo unknown))"
43
+ exit 0
44
+ fi
45
+
46
+ # --- Detect OS / arch (mirrors the engine's own install.sh naming) ---
47
+ case "$(uname -s)" in
48
+ Darwin) OS="darwin" ;;
49
+ Linux) OS="linux" ;;
50
+ *) echo "error: unsupported OS: $(uname -s)" >&2; exit 2 ;;
51
+ esac
52
+ case "$(uname -m)" in
53
+ x86_64|amd64) ARCH="amd64" ;;
54
+ arm64|aarch64) ARCH="arm64" ;;
55
+ *) echo "error: unsupported arch: $(uname -m)" >&2; exit 2 ;;
56
+ esac
57
+
58
+ # Linux ships a fully-static "-portable" build; macOS has no such variant.
59
+ PORTABLE=""
60
+ [[ "$OS" = "linux" ]] && PORTABLE="-portable"
61
+ ARCHIVE="codebase-memory-mcp-${OS}-${ARCH}${PORTABLE}.tar.gz"
62
+
63
+ if [[ -n "${CMM_DOWNLOAD_URL:-}" ]]; then
64
+ BASE="$CMM_DOWNLOAD_URL"
65
+ elif [[ "$VERSION" = "latest" ]]; then
66
+ BASE="https://github.com/${REPO}/releases/latest/download"
67
+ else
68
+ BASE="https://github.com/${REPO}/releases/download/${VERSION}"
69
+ fi
70
+
71
+ case "$BASE" in https://*) ;; *) echo "error: refusing non-HTTPS URL: $BASE" >&2; exit 2 ;; esac
72
+
73
+ TMP="$(mktemp -d)"
74
+ trap 'rm -rf "$TMP"' EXIT
75
+
76
+ echo "Fetching ${ARCHIVE} (${VERSION})..."
77
+ if ! curl -fSL --max-time 300 -o "$TMP/$ARCHIVE" "$BASE/$ARCHIVE"; then
78
+ echo "error: download failed: $BASE/$ARCHIVE" >&2
79
+ exit 2
80
+ fi
81
+
82
+ # --- Verify checksum (best-effort: hard-fail only if the archive is listed) ---
83
+ if curl -fsSL --max-time 60 -o "$TMP/checksums.txt" "$BASE/checksums.txt" 2>/dev/null; then
84
+ expected="$(grep " $ARCHIVE\$" "$TMP/checksums.txt" 2>/dev/null | awk '{print $1}' | head -1 || true)"
85
+ if [[ -n "$expected" ]]; then
86
+ if command -v sha256sum >/dev/null 2>&1; then
87
+ actual="$(sha256sum "$TMP/$ARCHIVE" | awk '{print $1}')"
88
+ else
89
+ actual="$(shasum -a 256 "$TMP/$ARCHIVE" | awk '{print $1}')"
90
+ fi
91
+ if [[ "$expected" != "$actual" ]]; then
92
+ echo "error: checksum mismatch for $ARCHIVE (expected $expected, got $actual)" >&2
93
+ exit 2
94
+ fi
95
+ echo " checksum OK"
96
+ else
97
+ echo " warning: $ARCHIVE not found in checksums.txt — skipping verification" >&2
98
+ fi
99
+ else
100
+ echo " warning: checksums.txt unavailable — skipping verification" >&2
101
+ fi
102
+
103
+ # --- Extract and install ---
104
+ tar -xzf "$TMP/$ARCHIVE" -C "$TMP"
105
+ SRC="$(find "$TMP" -maxdepth 2 -type f -name codebase-memory-mcp | head -1)"
106
+ if [[ -z "$SRC" ]]; then
107
+ echo "error: codebase-memory-mcp binary not found in archive" >&2
108
+ exit 2
109
+ fi
110
+
111
+ mkdir -p "$DEST"
112
+ install -m 0755 "$SRC" "$BIN_PATH" 2>/dev/null || { cp -f "$SRC" "$BIN_PATH"; chmod +x "$BIN_PATH"; }
113
+
114
+ echo "Installed: $("$BIN_PATH" --version 2>/dev/null || echo "$BIN_PATH")"
115
+ echo " -> $BIN_PATH"
116
+ exit 0
package/scripts/lib.sh ADDED
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Shared validation library for Draft skill files.
4
+ #
5
+ # Sourced by test suites. Defines constants and validation functions
6
+ # but does not execute anything when sourced.
7
+ #
8
+ # Usage:
9
+ # source scripts/lib.sh
10
+ #
11
+
12
+ set -euo pipefail
13
+
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ ROOT_DIR="$(dirname "$SCRIPT_DIR")"
16
+ SKILLS_DIR="$ROOT_DIR/skills"
17
+ CORE_DIR="$ROOT_DIR/core"
18
+ TOOLS_DIR="$ROOT_DIR/scripts/tools"
19
+
20
+ # ─────────────────────────────────────────────────────────
21
+ # Skill ordering (canonical order for all references)
22
+ # ─────────────────────────────────────────────────────────
23
+
24
+ SKILL_ORDER=(
25
+ draft
26
+ init
27
+ index
28
+ graph
29
+ new-track
30
+ decompose
31
+ implement
32
+ coverage
33
+ deploy-checklist
34
+ bughunt
35
+ review
36
+ upload
37
+ plan
38
+ ops
39
+ docs
40
+ discover
41
+ jira
42
+ integrations
43
+ quick-review
44
+ deep-review
45
+ testing-strategy
46
+ learn
47
+ adr
48
+ debug
49
+ standup
50
+ tech-debt
51
+ incident-response
52
+ documentation
53
+ status
54
+ revert
55
+ change
56
+ tour
57
+ impact
58
+ assist-review
59
+ )
60
+
61
+ # ─────────────────────────────────────────────────────────
62
+ # Core reference files (inlined by Claude plugin at runtime)
63
+ # ─────────────────────────────────────────────────────────
64
+
65
+ CORE_FILES=(
66
+ # Methodology
67
+ "methodology.md"
68
+ "knowledge-base.md"
69
+ # Shared procedures
70
+ "shared/draft-context-loading.md"
71
+ "shared/git-report-metadata.md"
72
+ "shared/pattern-learning.md"
73
+ "shared/condensation.md"
74
+ "shared/cross-skill-dispatch.md"
75
+ "shared/jira-sync.md"
76
+ "shared/graph-query.md"
77
+ "shared/parallel-analysis.md"
78
+ # Foundations additions (Phase 0)
79
+ "shared/context-verify.md"
80
+ "shared/discovery-schema.md"
81
+ "shared/graph-usage-report.md"
82
+ "shared/parallel-fanout.md"
83
+ "shared/red-flags.md"
84
+ "shared/template-contract.md"
85
+ "shared/template-hygiene.md"
86
+ "shared/tool-resolver.md"
87
+ "shared/verification-gates.md"
88
+ # Templates
89
+ "templates/guardrails.md"
90
+ "templates/intake-questions.md"
91
+ "templates/ai-context.md"
92
+ "templates/ai-profile.md"
93
+ "templates/architecture.md"
94
+ "templates/track-architecture.md"
95
+ "templates/jira.md"
96
+ "templates/product.md"
97
+ "templates/tech-stack.md"
98
+ "templates/workflow.md"
99
+ "templates/spec.md"
100
+ "templates/plan.md"
101
+ "templates/metadata.json"
102
+ # Foundations additions
103
+ "templates/ai-context-export.md"
104
+ "templates/session-summary.md"
105
+ # Index templates (monorepo)
106
+ "templates/service-index.md"
107
+ "templates/dependency-graph.md"
108
+ "templates/tech-matrix.md"
109
+ "templates/root-product.md"
110
+ "templates/root-architecture.md"
111
+ "templates/root-tech-stack.md"
112
+ "templates/rca.md"
113
+ # Foundations / internal design templates (added during quality tooling work)
114
+ "templates/CHANGELOG.md"
115
+ "templates/discovery.md"
116
+ "templates/hld.md"
117
+ "templates/lld.md"
118
+ # Agents
119
+ "agents/architect.md"
120
+ "agents/debugger.md"
121
+ "agents/planner.md"
122
+ "agents/rca.md"
123
+ "agents/reviewer.md"
124
+ "agents/writer.md"
125
+ "agents/ops.md"
126
+ # VCS abstraction
127
+ "shared/vcs-commands.md"
128
+ # Guardrails system (Foundations)
129
+ "guardrails.md"
130
+ "guardrails/README.md"
131
+ "guardrails/security.md"
132
+ "guardrails/code-quality.md"
133
+ "guardrails/design-norms.md"
134
+ "guardrails/review-checks.md"
135
+ "guardrails/secure-patterns.md"
136
+ "guardrails/dependency-triage.md"
137
+ "guardrails/language-standards.md"
138
+ )
139
+
140
+ # ─────────────────────────────────────────────────────────
141
+ # Deterministic tool scripts (under scripts/tools/)
142
+ # Skills invoke these for mechanical work — git metadata,
143
+ # file classification, TODO aging, hotspot ranking, etc.
144
+ # ─────────────────────────────────────────────────────────
145
+
146
+ TOOLS=(
147
+ "git-metadata.sh"
148
+ "classify-files.sh"
149
+ "parse-git-log.sh"
150
+ "scan-markers.sh"
151
+ "hotspot-rank.sh"
152
+ "cycle-detect.sh"
153
+ "parse-reports.sh"
154
+ "detect-test-framework.sh"
155
+ "run-coverage.sh"
156
+ "freshness-check.sh"
157
+ "adr-index.sh"
158
+ "manage-symlinks.sh"
159
+ "mermaid-from-graph.sh"
160
+ "graph-snapshot.sh"
161
+ "graph-impact.sh"
162
+ "graph-callers.sh"
163
+ "validate-frontmatter.sh"
164
+ # Foundations hygiene/verification tools
165
+ "check-graph-usage-report.sh"
166
+ "check-scope-conflicts.sh"
167
+ "check-skill-line-caps.sh"
168
+ "check-template-noop.sh"
169
+ "check-track-hygiene.sh"
170
+ "diff-templates-vs-tracks.sh"
171
+ "emit-skill-metrics.sh"
172
+ "fix-whitespace.sh"
173
+ "migrate-track-frontmatter.sh"
174
+ "render-track.sh"
175
+ "verify-citations.sh"
176
+ "verify-doc-anchors.sh"
177
+ "verify-graph-binary.sh"
178
+ )
179
+
180
+ # ─────────────────────────────────────────────────────────
181
+ # Validation functions
182
+ # ─────────────────────────────────────────────────────────
183
+
184
+ # Validate a skill name against kebab-case regex.
185
+ # Prevents path traversal, uppercase, special chars.
186
+ is_valid_skill_name() {
187
+ local name="$1"
188
+ [[ "$name" =~ ^[a-z][a-z0-9]*(-[a-z0-9]+)*$ ]]
189
+ }
190
+
191
+ # Extract body content from a SKILL.md file (strip YAML frontmatter).
192
+ # Returns non-zero and prints to stderr on validation failure.
193
+ extract_body() {
194
+ local file="$1"
195
+
196
+ if [[ "$(sed -n '1p' "$file")" != "---" ]]; then
197
+ echo "ERROR: Missing YAML frontmatter in $file" >&2
198
+ echo " Skill files must start with --- delimiter on line 1" >&2
199
+ return 1
200
+ fi
201
+
202
+ if ! awk 'NR > 1 && /^---$/ { found=1; exit } END { exit !found }' "$file"; then
203
+ echo "ERROR: Missing closing YAML frontmatter delimiter in $file" >&2
204
+ return 1
205
+ fi
206
+
207
+ local frontmatter
208
+ frontmatter=$(awk '
209
+ NR == 1 && /^---$/ { in_frontmatter=1; next }
210
+ /^---$/ && in_frontmatter { exit }
211
+ in_frontmatter { print }
212
+ ' "$file")
213
+
214
+ if ! printf '%s\n' "$frontmatter" | grep -q "^name:"; then
215
+ echo "ERROR: Missing 'name:' field in frontmatter of $file" >&2
216
+ return 1
217
+ fi
218
+
219
+ if ! printf '%s\n' "$frontmatter" | grep -q "^description:"; then
220
+ echo "ERROR: Missing 'description:' field in frontmatter of $file" >&2
221
+ return 1
222
+ fi
223
+
224
+ awk '
225
+ BEGIN { in_frontmatter = 0; found_end = 0 }
226
+ /^---$/ {
227
+ if (NR == 1 && in_frontmatter == 0) {
228
+ in_frontmatter = 1
229
+ next
230
+ } else if (found_end == 0) {
231
+ found_end = 1
232
+ next
233
+ }
234
+ }
235
+ found_end == 1 { print }
236
+ ' "$file"
237
+ }
238
+
239
+ # Validate body format: line 1 blank, line 2 starts with #, line 3 blank.
240
+ validate_skill_body_format() {
241
+ local skill="$1"
242
+ local skill_file="$2"
243
+
244
+ local body_head line1 line2 line3
245
+ body_head=$(extract_body "$skill_file" | sed -n '1,3p' || true)
246
+ line1=$(echo "$body_head" | sed -n '1p')
247
+ line2=$(echo "$body_head" | sed -n '2p')
248
+ line3=$(echo "$body_head" | sed -n '3p')
249
+ if [[ -n "$line1" ]] || [[ ! "$line2" =~ ^#\ ]] || [[ -n "$line3" ]]; then
250
+ echo "ERROR: Skill '$skill' body format invalid (expected: blank, '# Title', blank). Got:" >&2
251
+ echo " Line 1: '${line1}'" >&2
252
+ echo " Line 2: '${line2}'" >&2
253
+ echo " Line 3: '${line3}'" >&2
254
+ return 1
255
+ fi
256
+ }
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env bash
2
+ # Shared helpers for scripts/tools/*.sh.
3
+ #
4
+ # Sourced, not executed. No side effects at source time.
5
+
6
+ # shellcheck shell=bash
7
+
8
+ json_escape() {
9
+ local s="$1"
10
+ s="${s//\\/\\\\}"
11
+ s="${s//\"/\\\"}"
12
+ s="${s//$'\n'/\\n}"
13
+ s="${s//$'\t'/\\t}"
14
+ s="${s//$'\r'/}"
15
+ printf '%s' "$s"
16
+ }
17
+
18
+ # Extract a top-level YAML frontmatter field value from a Markdown file.
19
+ # Discover track directories under a repo root (default: caller's Draft repo).
20
+ discover_track_dirs() {
21
+ local repo_root="${1:-}"
22
+ [[ -n "$repo_root" ]] || return 0
23
+ find "$repo_root" -type d -path '*/tracks/*' -maxdepth 4 -mindepth 2 \
24
+ -not -path '*/.*' 2>/dev/null | sort
25
+ }
26
+
27
+ # Extract a string value from minified or pretty-printed JSON.
28
+ read_json_str() {
29
+ local file="$1" key="$2"
30
+ [[ -f "$file" ]] || return 0
31
+ awk -v key="$key" '
32
+ {
33
+ pat = "\""key"\"[[:space:]]*:[[:space:]]*\"[^\"]*\""
34
+ if (match($0, pat)) {
35
+ s = substr($0, RSTART, RLENGTH)
36
+ sub("^\""key"\"[[:space:]]*:[[:space:]]*\"", "", s)
37
+ sub("\"$", "", s)
38
+ print s
39
+ exit
40
+ }
41
+ }' "$file"
42
+ }
43
+
44
+ # Parse scope_includes / scope_excludes from metadata.json or spec.md frontmatter.
45
+ read_scope_array() {
46
+ local file="$1" key="$2"
47
+ [[ -f "$file" ]] || return 0
48
+ case "$file" in
49
+ *.json)
50
+ awk -v key="$key" '
51
+ {
52
+ pat = "\""key"\"[[:space:]]*:[[:space:]]*\\[[^]]*\\]"
53
+ if (match($0, pat)) {
54
+ s = substr($0, RSTART, RLENGTH)
55
+ sub("^\""key"\"[[:space:]]*:[[:space:]]*", "", s)
56
+ gsub(/[\[\]",]/, " ", s)
57
+ print s
58
+ exit
59
+ }
60
+ }' "$file"
61
+ ;;
62
+ *.md)
63
+ awk -v key="$key" '
64
+ NR==1 && /^---$/ { in_fm=1; next }
65
+ in_fm && /^---$/ { exit }
66
+ in_fm && $0 ~ "^"key":" {
67
+ sub("^"key":[[:space:]]*", "", $0)
68
+ gsub(/[\[\]",]/, " ", $0)
69
+ print $0
70
+ exit
71
+ }' "$file"
72
+ ;;
73
+ esac
74
+ }
75
+
76
+ # Return the per-skill line cap from a caps config file (or GLOBAL_CAP).
77
+ skill_line_cap() {
78
+ local skill_name="$1" caps_conf="$2" global_cap="$3"
79
+ local name cap
80
+ [[ -f "$caps_conf" ]] || { printf '%s' "$global_cap"; return; }
81
+ while read -r name cap; do
82
+ [[ -z "$name" || "$name" == \#* ]] && continue
83
+ if [[ "$name" == "*" ]]; then
84
+ global_cap="$cap"
85
+ elif [[ "$name" == "$skill_name" ]]; then
86
+ printf '%s' "$cap"
87
+ return
88
+ fi
89
+ done < "$caps_conf"
90
+ printf '%s' "$global_cap"
91
+ }
92
+
93
+ get_yaml_field() {
94
+ local file="$1"
95
+ local key="$2"
96
+ awk -v key="$key" '
97
+ NR == 1 && /^---$/ { in_fm = 1; next }
98
+ in_fm && /^---$/ { exit }
99
+ in_fm {
100
+ if ($0 ~ "^"key":[[:space:]]*") {
101
+ val = $0
102
+ sub("^"key":[[:space:]]*", "", val)
103
+ if (val ~ /^".*"$/) { val = substr(val, 2, length(val)-2) }
104
+ sub(/[[:space:]]+$/, "", val)
105
+ print val
106
+ exit
107
+ }
108
+ }
109
+ ' "$file"
110
+ }
111
+
112
+ # Locate the `codebase-memory-mcp` binary (Draft knowledge-graph engine).
113
+ # Sets MEMORY_BIN globally; returns 0 if found, 1 otherwise.
114
+ # Preference: PATH > Draft-managed install (~/.cache/draft/bin) > vendored bin/<arch> under known roots.
115
+ # No legacy fallbacks: the Aether `graph`/`graph-clang` binaries are retired.
116
+ find_memory_bin() {
117
+ local repo_abs="$1"
118
+ local self_repo="$2"
119
+ MEMORY_BIN=""
120
+ local bin_name="codebase-memory-mcp"
121
+
122
+ # 0. Hard opt-out: force the graph engine off (tests, air-gapped, opt-out users).
123
+ if [[ -n "${DRAFT_MEMORY_DISABLE:-}" ]]; then
124
+ return 1
125
+ fi
126
+
127
+ # 1. Explicit override (testing / pinned install).
128
+ if [[ -n "${DRAFT_MEMORY_BIN:-}" && -x "${DRAFT_MEMORY_BIN}" ]]; then
129
+ MEMORY_BIN="$DRAFT_MEMORY_BIN"
130
+ return 0
131
+ fi
132
+
133
+ # 2. PATH (highest for dev / global installs).
134
+ if command -v "$bin_name" >/dev/null 2>&1; then
135
+ MEMORY_BIN="$bin_name"
136
+ return 0
137
+ fi
138
+
139
+ # 3. Draft-managed install location (install.sh fetches the binary here).
140
+ local managed="$HOME/.cache/draft/bin/$bin_name"
141
+ if [[ -x "$managed" ]]; then
142
+ MEMORY_BIN="$managed"
143
+ return 0
144
+ fi
145
+
146
+ # 4. Optional vendored arch-specific binary under known roots.
147
+ local os arch norm
148
+ os=$(uname -s | tr '[:upper:]' '[:lower:]')
149
+ arch=$(uname -m)
150
+ case "$arch" in
151
+ x86_64|amd64) norm="amd64" ;;
152
+ aarch64|arm64) norm="arm64" ;;
153
+ *) norm="$arch" ;;
154
+ esac
155
+ local ARCH="${os}-${norm}"
156
+
157
+ local roots=()
158
+ [[ -n "$repo_abs" && -d "$repo_abs" ]] && roots+=("$repo_abs")
159
+ [[ -n "$self_repo" && -d "$self_repo" ]] && roots+=("$self_repo")
160
+ for bc in \
161
+ "$HOME/.cursor/plugins/local/draft/.draft-install-path" \
162
+ "$HOME/.claude/plugins/draft/.draft-install-path"; do
163
+ if [[ -f "$bc" ]]; then
164
+ local pr; pr="$(cat "$bc" 2>/dev/null || true)"
165
+ [[ -n "$pr" && -d "$pr" ]] && roots+=("$pr")
166
+ fi
167
+ done
168
+
169
+ for pr in "${roots[@]}"; do
170
+ local cand="$pr/bin/$ARCH/$bin_name"
171
+ if [[ -x "$cand" ]]; then
172
+ MEMORY_BIN="$cand"
173
+ return 0
174
+ fi
175
+ done
176
+
177
+ return 1
178
+ }
179
+
180
+ # Run a codebase-memory-mcp CLI tool. Echoes the JSON result (stdout); the engine's
181
+ # `level=...` log lines go to stderr and are discarded unless DRAFT_MEMORY_DEBUG is set.
182
+ # Usage: memory_cli <tool> [json-args]
183
+ memory_cli() {
184
+ local tool="$1"
185
+ local args="${2:-{\}}"
186
+ if [[ -z "${MEMORY_BIN:-}" ]]; then
187
+ return 1
188
+ fi
189
+ if [[ -n "${DRAFT_MEMORY_DEBUG:-}" ]]; then
190
+ "$MEMORY_BIN" cli "$tool" "$args"
191
+ else
192
+ "$MEMORY_BIN" cli "$tool" "$args" 2>/dev/null
193
+ fi
194
+ }
195
+
196
+ # Resolve the engine's project name for a repository absolute path via list_projects.
197
+ # Echoes the project name, or nothing if the repo has not been indexed yet.
198
+ memory_project_for_repo() {
199
+ local repo_abs="$1"
200
+ command -v jq >/dev/null 2>&1 || return 1
201
+ memory_cli list_projects '{}' 2>/dev/null \
202
+ | jq -r --arg p "$repo_abs" '.projects[]? | select(.root_path == $p) | .name' 2>/dev/null \
203
+ | head -1
204
+ }
205
+
206
+ # Ensure a repository is indexed in the engine; echo its project name.
207
+ # Indexes on demand when absent. Returns 1 if the engine is unavailable.
208
+ memory_ensure_index() {
209
+ local repo_abs="$1"
210
+ [[ -n "${MEMORY_BIN:-}" ]] || return 1
211
+ command -v jq >/dev/null 2>&1 || return 1
212
+ local proj
213
+ proj="$(memory_project_for_repo "$repo_abs" 2>/dev/null || true)"
214
+ if [[ -z "$proj" ]]; then
215
+ proj="$(memory_cli index_repository "{\"repo_path\":\"$repo_abs\"}" 2>/dev/null \
216
+ | jq -r '.project // empty' 2>/dev/null || true)"
217
+ fi
218
+ [[ -n "$proj" ]] || return 1
219
+ printf '%s' "$proj"
220
+ }