@drafthq/draft 3.5.3 → 3.6.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/cli/src/hosts/cursor.js +3 -0
- package/cli/src/installer.js +7 -3
- package/cli/src/lib/fsx.js +3 -0
- package/cli/src/lib/marker.js +8 -3
- package/core/shared/condensation.md +1 -1
- package/core/shared/git-report-metadata.md +1 -1
- package/core/shared/graph-query.md +24 -24
- package/core/shared/tool-resolver.md +20 -15
- package/core/templates/plan.md +1 -1
- package/integrations/agents/AGENTS.md +122 -470
- package/integrations/copilot/.github/copilot-instructions.md +122 -470
- package/package.json +1 -1
- package/scripts/fetch-memory-engine.sh +1 -1
- package/scripts/lib.sh +79 -2
- package/scripts/tools/_graph_queries.sh +12 -5
- package/scripts/tools/_lib.sh +56 -8
- package/scripts/tools/adr-index.sh +1 -1
- package/scripts/tools/check-skill-line-caps.sh +2 -2
- package/scripts/tools/check-track-hygiene.sh +41 -21
- package/scripts/tools/classify-files.sh +1 -1
- package/scripts/tools/cycle-detect.sh +2 -9
- package/scripts/tools/detect-test-framework.sh +1 -1
- package/scripts/tools/emit-skill-metrics.sh +7 -2
- package/scripts/tools/freshness-check.sh +2 -2
- package/scripts/tools/git-metadata.sh +6 -6
- package/scripts/tools/graph-arch.sh +4 -10
- package/scripts/tools/graph-callers.sh +4 -12
- package/scripts/tools/graph-deps.sh +3 -10
- package/scripts/tools/graph-errors.sh +4 -11
- package/scripts/tools/graph-hierarchy.sh +4 -11
- package/scripts/tools/graph-impact.sh +17 -17
- package/scripts/tools/graph-init.sh +1 -1
- package/scripts/tools/graph-preflight.sh +1 -1
- package/scripts/tools/graph-query.sh +5 -12
- package/scripts/tools/graph-risk.sh +3 -10
- package/scripts/tools/graph-search.sh +4 -11
- package/scripts/tools/graph-snapshot.sh +9 -4
- package/scripts/tools/graph-snippet.sh +3 -10
- package/scripts/tools/graph-tests.sh +3 -10
- package/scripts/tools/graph-traces.sh +3 -10
- package/scripts/tools/hotspot-rank.sh +4 -11
- package/scripts/tools/mermaid-from-graph.sh +3 -10
- package/scripts/tools/migrate-track-frontmatter.sh +3 -1
- package/scripts/tools/okf-coverage-check.sh +4 -4
- package/scripts/tools/okf-emit-catalog.sh +256 -0
- package/scripts/tools/okf-fix-links.sh +326 -0
- package/scripts/tools/okf-plan-concepts.sh +245 -14
- package/scripts/tools/okf-render-views.sh +46 -9
- package/scripts/tools/okf-validate-all.sh +20 -9
- package/scripts/tools/okf-validate-quality.sh +2 -8
- package/scripts/tools/okf-validate.sh +7 -4
- package/scripts/tools/parse-git-log.sh +4 -4
- package/scripts/tools/parse-reports.sh +1 -1
- package/scripts/tools/render-track.sh +1 -1
- package/scripts/tools/run-coverage.sh +1 -1
- package/scripts/tools/scan-markers.sh +4 -4
- package/scripts/tools/validate-frontmatter.sh +2 -2
- package/scripts/tools/verify-citations.sh +2 -2
- package/scripts/tools/verify-doc-anchors.sh +1 -1
- package/scripts/tools/verify-graph-binary.sh +2 -2
- package/skills/adr/SKILL.md +1 -1
- package/skills/bughunt/SKILL.md +1 -1
- package/skills/coverage/SKILL.md +2 -2
- package/skills/debug/SKILL.md +1 -1
- package/skills/decompose/SKILL.md +3 -3
- package/skills/deep-review/SKILL.md +2 -2
- package/skills/deploy-checklist/SKILL.md +1 -1
- package/skills/graph/SKILL.md +1 -1
- package/skills/impact/SKILL.md +1 -1
- package/skills/implement/SKILL.md +2 -2
- package/skills/init/SKILL.md +10 -7
- package/skills/init/references/architecture-spec.md +1 -1
- package/skills/init/references/okf-emitter.md +39 -75
- package/skills/learn/SKILL.md +1 -1
- package/skills/quick-review/SKILL.md +1 -1
- package/skills/review/SKILL.md +3 -3
- package/skills/standup/SKILL.md +1 -1
- package/skills/status/SKILL.md +1 -1
- package/skills/tech-debt/SKILL.md +2 -2
- package/skills/upload/SKILL.md +1 -1
- package/core/templates/track-architecture.md +0 -311
package/package.json
CHANGED
package/scripts/lib.sh
CHANGED
|
@@ -57,6 +57,76 @@ SKILL_ORDER=(
|
|
|
57
57
|
assist-review
|
|
58
58
|
)
|
|
59
59
|
|
|
60
|
+
# ─────────────────────────────────────────────────────────
|
|
61
|
+
# Skill metadata: one row per skill — "<name>|<display header>|<copilot trigger>"
|
|
62
|
+
# The name and header never contain "|"; the trigger may (e.g. "[file|pr N]"),
|
|
63
|
+
# so lookups split on the FIRST TWO pipes only. Coverage against SKILL_ORDER is
|
|
64
|
+
# enforced by tests/test-trigger-functions.sh.
|
|
65
|
+
# ─────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
SKILL_META=(
|
|
68
|
+
'draft|Draft Overview|"help" or "draft"'
|
|
69
|
+
'init|Init Command|"init draft", "build the code graph", or "draft init [refresh] [--graph-only] [--module-only]"'
|
|
70
|
+
'graph|Graph Command|"build graph", "refresh graph", or "draft graph [path]"'
|
|
71
|
+
'new-track|New Track Command|"new feature" or "draft new-track <description>"'
|
|
72
|
+
'decompose|Decompose Command|"break into modules" or "draft decompose"'
|
|
73
|
+
'implement|Implement Command|"implement" or "draft implement"'
|
|
74
|
+
'coverage|Coverage Command|"check coverage" or "draft coverage"'
|
|
75
|
+
'deploy-checklist|Deploy Checklist Command|"deploy checklist" or "draft deploy-checklist [track <id>]"'
|
|
76
|
+
'bughunt|Bug Hunt Command|"hunt bugs" or "draft bughunt [--track <id>]"'
|
|
77
|
+
'review|Review Command|"review code" or "draft review [--track <id>] [--full]"'
|
|
78
|
+
'upload|Upload Command|"upload for review" or "draft upload [track <id>]"'
|
|
79
|
+
'plan|Plan Router|"plan feature" or "draft plan <intent>" (new-track, decompose, adr, tech-debt, change)'
|
|
80
|
+
'ops|Ops Router|"ops deploy" or "draft ops <intent>" (deploy-checklist, incident, standup, status, revert)'
|
|
81
|
+
'docs|Docs Router|"write docs" or "draft docs <intent>" (documentation)'
|
|
82
|
+
'discover|Discover Router|"discover debug" or "draft discover <intent>" (debug, bughunt, reviews, coverage, learn, index, etc.)'
|
|
83
|
+
'jira|Jira Router|"jira preview", "jira create", or "jira review <ID>"'
|
|
84
|
+
'integrations|Integrations Router|"integrations", "integrations jira-preview", or "integrations jira-create"'
|
|
85
|
+
'quick-review|Quick Review Command|"quick review" or "draft quick-review [file|pr <number>]"'
|
|
86
|
+
'deep-review|Deep Review Command|"deep review" or "draft deep-review [module]"'
|
|
87
|
+
'testing-strategy|Testing Strategy Command|"test strategy" or "draft testing-strategy [track <id>|path]"'
|
|
88
|
+
'learn|Learn Command|"learn patterns" or "draft learn [promote|migrate|path]"'
|
|
89
|
+
'adr|ADR Command|"document decision" or "draft adr [title]"'
|
|
90
|
+
'debug|Debug Command|"debug bug" or "draft debug [description|track <id>]"'
|
|
91
|
+
'standup|Standup Command|"standup" or "draft standup [date|week|save]"'
|
|
92
|
+
'tech-debt|Tech Debt Command|"tech debt" or "draft tech-debt [path|track <id>]"'
|
|
93
|
+
'incident-response|Incident Response Command|"incident" or "draft incident-response [new|update|postmortem]"'
|
|
94
|
+
'documentation|Documentation Command|"write docs" or "draft documentation [readme|runbook|api|onboarding]"'
|
|
95
|
+
'status|Status Command|"status" or "draft status"'
|
|
96
|
+
'revert|Revert Command|"revert" or "draft revert"'
|
|
97
|
+
'change|Change Command|"handle change" or "draft change <description>"'
|
|
98
|
+
'tour|Tour Command|"tour" or "draft tour"'
|
|
99
|
+
'impact|Impact Command|"impact" or "draft impact"'
|
|
100
|
+
'assist-review|Assist Review Command|"assist review" or "draft assist-review"'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# get_skill_header <skill> — display header for integration sections.
|
|
104
|
+
get_skill_header() {
|
|
105
|
+
local skill="$1" row rest
|
|
106
|
+
for row in "${SKILL_META[@]}"; do
|
|
107
|
+
if [[ "${row%%|*}" == "$skill" ]]; then
|
|
108
|
+
rest="${row#*|}"
|
|
109
|
+
printf '%s\n' "${rest%%|*}"
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
112
|
+
done
|
|
113
|
+
printf '%s\n' "$(echo "${skill:0:1}" | tr '[:lower:]' '[:upper:]')${skill:1} Command"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# get_copilot_trigger <skill> — natural-language trigger for the Copilot header.
|
|
117
|
+
get_copilot_trigger() {
|
|
118
|
+
local skill="$1" row rest
|
|
119
|
+
for row in "${SKILL_META[@]}"; do
|
|
120
|
+
if [[ "${row%%|*}" == "$skill" ]]; then
|
|
121
|
+
rest="${row#*|}"
|
|
122
|
+
printf '%s\n' "${rest#*|}"
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
printf '"draft %s"\n' "$skill"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
60
130
|
# ─────────────────────────────────────────────────────────
|
|
61
131
|
# Core reference files (inlined by Claude plugin at runtime)
|
|
62
132
|
# ─────────────────────────────────────────────────────────
|
|
@@ -91,7 +161,6 @@ CORE_FILES=(
|
|
|
91
161
|
"templates/ai-context.md"
|
|
92
162
|
"templates/ai-profile.md"
|
|
93
163
|
"templates/architecture.md"
|
|
94
|
-
"templates/track-architecture.md"
|
|
95
164
|
"templates/jira.md"
|
|
96
165
|
"templates/product.md"
|
|
97
166
|
"templates/tech-stack.md"
|
|
@@ -202,6 +271,8 @@ TOOLS=(
|
|
|
202
271
|
"okf-validate-quality.sh"
|
|
203
272
|
"okf-coverage-check.sh"
|
|
204
273
|
"okf-validate-all.sh"
|
|
274
|
+
"okf-emit-catalog.sh"
|
|
275
|
+
"okf-fix-links.sh"
|
|
205
276
|
)
|
|
206
277
|
|
|
207
278
|
# ─────────────────────────────────────────────────────────
|
|
@@ -264,12 +335,18 @@ extract_body() {
|
|
|
264
335
|
}
|
|
265
336
|
|
|
266
337
|
# Validate body format: line 1 blank, line 2 starts with #, line 3 blank.
|
|
338
|
+
# $3 (optional): pre-extracted body — skips a second extract_body parse when
|
|
339
|
+
# the caller already has it.
|
|
267
340
|
validate_skill_body_format() {
|
|
268
341
|
local skill="$1"
|
|
269
342
|
local skill_file="$2"
|
|
270
343
|
|
|
271
344
|
local body_head line1 line2 line3
|
|
272
|
-
|
|
345
|
+
if (( $# >= 3 )); then
|
|
346
|
+
body_head=$(printf '%s\n' "$3" | sed -n '1,3p')
|
|
347
|
+
else
|
|
348
|
+
body_head=$(extract_body "$skill_file" | sed -n '1,3p' || true)
|
|
349
|
+
fi
|
|
273
350
|
line1=$(echo "$body_head" | sed -n '1p')
|
|
274
351
|
line2=$(echo "$body_head" | sed -n '2p')
|
|
275
352
|
line3=$(echo "$body_head" | sed -n '3p')
|
|
@@ -28,9 +28,12 @@ if [[ "$(type -t memory_cli 2>/dev/null)" != function ]]; then
|
|
|
28
28
|
source "$_GQ_DIR/_lib.sh"
|
|
29
29
|
fi
|
|
30
30
|
|
|
31
|
-
# Escape
|
|
31
|
+
# Escape for embedding inside a Cypher single-quoted string literal.
|
|
32
|
+
# Backslashes must be doubled BEFORE quotes are escaped, otherwise an input
|
|
33
|
+
# ending in `\` yields `\'` — an escaped quote that never closes the literal.
|
|
32
34
|
gq_escape() {
|
|
33
35
|
local s="$1"
|
|
36
|
+
s="${s//\\/\\\\}"
|
|
34
37
|
printf '%s' "${s//\'/\\\'}"
|
|
35
38
|
}
|
|
36
39
|
|
|
@@ -82,19 +85,23 @@ gq_rows_len() {
|
|
|
82
85
|
printf '%s' "$1" | jq -r '(.rows // []) | length' 2>/dev/null || printf '0'
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
# gq_symbol_status <project> <sym_esc> <result_json>
|
|
88
|
+
# gq_symbol_status <project> <sym_esc> <result_json>
|
|
89
|
+
# -> ok | no-edges | no-match | probe-failed
|
|
86
90
|
# Fail-loud disambiguation (Guardrail 4): an empty result is only a true negative
|
|
87
91
|
# ("no-edges") when the node actually exists; otherwise it is "no-match". An
|
|
88
92
|
# existence probe runs only when the primary result is empty (hot path stays one
|
|
89
|
-
# query).
|
|
93
|
+
# query). If the probe itself errors, that is NOT a true negative — report
|
|
94
|
+
# "probe-failed" so callers never mistake an engine failure for absence.
|
|
90
95
|
gq_symbol_status() {
|
|
91
96
|
local project="$1" sym="$2" result="$3"
|
|
92
97
|
if [[ "$(gq_rows_len "$result")" -gt 0 ]]; then
|
|
93
98
|
printf 'ok'; return
|
|
94
99
|
fi
|
|
95
100
|
local ex
|
|
96
|
-
ex="$(gq_run "$project" "$(gq_q_exists "$sym")"
|
|
97
|
-
|
|
101
|
+
if ! ex="$(gq_run "$project" "$(gq_q_exists "$sym")")"; then
|
|
102
|
+
printf 'probe-failed'; return
|
|
103
|
+
fi
|
|
104
|
+
if [[ "$(gq_rows_len "$ex")" -gt 0 ]]; then
|
|
98
105
|
printf 'no-edges'
|
|
99
106
|
else
|
|
100
107
|
printf 'no-match'
|
package/scripts/tools/_lib.sh
CHANGED
|
@@ -113,6 +113,47 @@ get_yaml_field() {
|
|
|
113
113
|
# Sets MEMORY_BIN globally; returns 0 if found, 1 otherwise.
|
|
114
114
|
# Preference: PATH > Draft-managed install (~/.cache/draft/bin) > vendored bin/<arch> under known roots.
|
|
115
115
|
# No legacy fallbacks: the Aether `graph`/`graph-clang` binaries are retired.
|
|
116
|
+
|
|
117
|
+
# GitHub-flavored-markdown-ish heading id (shared by TOC, anchors, link fixers).
|
|
118
|
+
# Keeps underscores; strips other punctuation; collapses whitespace to '-'.
|
|
119
|
+
gfm_slug() {
|
|
120
|
+
local s="${1:-}"
|
|
121
|
+
# shellcheck disable=SC2001
|
|
122
|
+
s="$(printf '%s' "$s" | tr '[:upper:]' '[:lower:]')"
|
|
123
|
+
s="$(printf '%s' "$s" | sed -E 's/[^[:alnum:]_[:space:]-]+//g; s/[[:space:]]+/-/g; s/-+/-/g; s/^-+//; s/-+$//')"
|
|
124
|
+
printf '%s' "$s"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Count entries in an x-grounded-paths frontmatter field.
|
|
128
|
+
# Supports both inline (`x-grounded-paths: ["a", "b"]`) and block lists:
|
|
129
|
+
# x-grounded-paths:
|
|
130
|
+
# - "a"
|
|
131
|
+
# - "b"
|
|
132
|
+
grounded_paths_count() {
|
|
133
|
+
local file="$1"
|
|
134
|
+
[[ -f "$file" ]] || { echo 0; return; }
|
|
135
|
+
awk '
|
|
136
|
+
NR==1 && /^---$/ { fm=1; next }
|
|
137
|
+
fm && /^---$/ { exit }
|
|
138
|
+
fm && /^x-grounded-paths:[[:space:]]*\[/ {
|
|
139
|
+
line=$0
|
|
140
|
+
sub(/^x-grounded-paths:[[:space:]]*\[/, "", line)
|
|
141
|
+
sub(/\].*$/, "", line)
|
|
142
|
+
gsub(/[[:space:]]/, "", line)
|
|
143
|
+
if (line=="") { print 0; exit }
|
|
144
|
+
n=split(line, a, ",")
|
|
145
|
+
print n
|
|
146
|
+
exit
|
|
147
|
+
}
|
|
148
|
+
fm && /^x-grounded-paths:[[:space:]]*$/ { collect=1; next }
|
|
149
|
+
fm && collect {
|
|
150
|
+
if ($0 ~ /^[[:space:]]*-[[:space:]]*/) { n++; next }
|
|
151
|
+
if ($0 ~ /^[A-Za-z0-9_-]+:/) { print n+0; exit }
|
|
152
|
+
}
|
|
153
|
+
END { if (collect) print n+0 }
|
|
154
|
+
' "$file"
|
|
155
|
+
}
|
|
156
|
+
|
|
116
157
|
find_memory_bin() {
|
|
117
158
|
local repo_abs="$1"
|
|
118
159
|
local self_repo="$2"
|
|
@@ -157,14 +198,6 @@ find_memory_bin() {
|
|
|
157
198
|
local roots=()
|
|
158
199
|
[[ -n "$repo_abs" && -d "$repo_abs" ]] && roots+=("$repo_abs")
|
|
159
200
|
[[ -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
201
|
|
|
169
202
|
for pr in "${roots[@]}"; do
|
|
170
203
|
local cand="$pr/bin/$ARCH/$bin_name"
|
|
@@ -177,6 +210,21 @@ find_memory_bin() {
|
|
|
177
210
|
return 1
|
|
178
211
|
}
|
|
179
212
|
|
|
213
|
+
# graph_bootstrap <repo-dir>
|
|
214
|
+
# Shared engine bootstrap for the graph-*.sh wrappers: resolves the repo path,
|
|
215
|
+
# locates the engine binary, checks jq, and ensures the index. Sets the globals
|
|
216
|
+
# REPO_ABS and PROJECT. Returns 1 on any failure so each wrapper routes it to
|
|
217
|
+
# its own unavailable-JSON shape (the shapes differ per tool).
|
|
218
|
+
graph_bootstrap() {
|
|
219
|
+
local repo="$1" self_repo
|
|
220
|
+
REPO_ABS="$(cd "$repo" 2>/dev/null && pwd)" || return 1
|
|
221
|
+
self_repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
222
|
+
find_memory_bin "$REPO_ABS" "$self_repo" || return 1
|
|
223
|
+
command -v jq >/dev/null 2>&1 || return 1
|
|
224
|
+
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
225
|
+
[[ -n "$PROJECT" ]] || return 1
|
|
226
|
+
}
|
|
227
|
+
|
|
180
228
|
# Run a codebase-memory-mcp CLI tool. Echoes the JSON result (stdout); the engine's
|
|
181
229
|
# `level=...` log lines go to stderr and are discarded unless DRAFT_MEMORY_DEBUG is set.
|
|
182
230
|
# Usage: memory_cli <tool> [json-args]
|
|
@@ -51,8 +51,8 @@ while (($#)); do
|
|
|
51
51
|
-h|--help) USAGE_HELP_MODE=1 usage ;;
|
|
52
52
|
--enforce) ENFORCE=1; shift ;;
|
|
53
53
|
--json) EMIT_JSON=1; shift ;;
|
|
54
|
-
--caps) CAPS_CONF="$2"; shift 2 ;;
|
|
55
|
-
--skills-dir) SKILLS_DIR="$2"; shift 2 ;;
|
|
54
|
+
--caps) CAPS_CONF="${2:?--caps requires a value}"; shift 2 ;;
|
|
55
|
+
--skills-dir) SKILLS_DIR="${2:?--skills-dir requires a value}"; shift 2 ;;
|
|
56
56
|
-*) printf 'Unknown flag: %s\n' "$1" >&2; usage ;;
|
|
57
57
|
*) printf 'Unexpected arg: %s\n' "$1" >&2; usage ;;
|
|
58
58
|
esac
|
|
@@ -74,6 +74,29 @@ record() {
|
|
|
74
74
|
violation_count=$((violation_count + 1))
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
# Numeric reader for metadata.json:hygiene_budget.* (read_json_str is string-only).
|
|
78
|
+
read_json_num() {
|
|
79
|
+
local file="$1" key="$2"
|
|
80
|
+
[[ -f "$file" ]] || return 0
|
|
81
|
+
grep -oE "\"$key\"[[:space:]]*:[[:space:]]*-?[0-9]+" "$file" 2>/dev/null \
|
|
82
|
+
| head -1 | grep -oE '\-?[0-9]+$' || true
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# TBD budget for every top-level doc of one track; cap < 0 means unlimited.
|
|
86
|
+
check_tbd_budget() {
|
|
87
|
+
local track_dir="$1" rel_track="$2" cap="$3" kind="$4" detail_suffix="$5"
|
|
88
|
+
(( cap >= 0 )) || return 0
|
|
89
|
+
while IFS= read -r f; do
|
|
90
|
+
local rel_file="${f#"$track_dir/"}"
|
|
91
|
+
local count
|
|
92
|
+
count="$(grep -oE "$TBD_RE" "$f" 2>/dev/null | wc -l | tr -d ' ' || true)"
|
|
93
|
+
if (( count > cap )); then
|
|
94
|
+
record "$rel_track" "$kind" "$rel_file" "0" \
|
|
95
|
+
"$count TBD sentinel(s) $detail_suffix"
|
|
96
|
+
fi
|
|
97
|
+
done < <(find "$track_dir" -maxdepth 1 -type f -name '*.md')
|
|
98
|
+
}
|
|
99
|
+
|
|
77
100
|
scan_one_track() {
|
|
78
101
|
local track_dir="$1"
|
|
79
102
|
local rel_track="${track_dir#"$REPO_ROOT/"}"
|
|
@@ -138,31 +161,28 @@ scan_one_track() {
|
|
|
138
161
|
done < "$f"
|
|
139
162
|
done < <(find "$track_dir" -maxdepth 1 -type f -name '*.md')
|
|
140
163
|
|
|
141
|
-
# 4. TBD budget per-doc.
|
|
164
|
+
# 4. TBD budget per-doc. Caps come from metadata.json:hygiene_budget when
|
|
165
|
+
# present (draft_tbd_cap / ready_for_review_tbd_cap; -1 = unlimited),
|
|
166
|
+
# falling back to the historical defaults.
|
|
142
167
|
case "$meta_status" in
|
|
143
|
-
|
|
168
|
+
archived) ;;
|
|
169
|
+
draft)
|
|
170
|
+
local draft_cap
|
|
171
|
+
draft_cap="$(read_json_num "$meta" "draft_tbd_cap")"
|
|
172
|
+
[[ "$draft_cap" =~ ^-?[0-9]+$ ]] || draft_cap=-1
|
|
173
|
+
check_tbd_budget "$track_dir" "$rel_track" "$draft_cap" "tbd-over-cap" \
|
|
174
|
+
"(cap $draft_cap at status=$meta_status)"
|
|
175
|
+
;;
|
|
144
176
|
ready-for-review|in_progress)
|
|
145
|
-
local tbd_cap
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (( count > tbd_cap )); then
|
|
151
|
-
record "$rel_track" "tbd-over-cap" "$rel_file" "0" \
|
|
152
|
-
"$count TBD sentinel(s) (cap $tbd_cap at status=$meta_status)"
|
|
153
|
-
fi
|
|
154
|
-
done < <(find "$track_dir" -maxdepth 1 -type f -name '*.md')
|
|
177
|
+
local tbd_cap
|
|
178
|
+
tbd_cap="$(read_json_num "$meta" "ready_for_review_tbd_cap")"
|
|
179
|
+
[[ "$tbd_cap" =~ ^-?[0-9]+$ ]] || tbd_cap=3
|
|
180
|
+
check_tbd_budget "$track_dir" "$rel_track" "$tbd_cap" "tbd-over-cap" \
|
|
181
|
+
"(cap $tbd_cap at status=$meta_status)"
|
|
155
182
|
;;
|
|
156
183
|
completed)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
local count
|
|
160
|
-
count="$(grep -oE "$TBD_RE" "$f" 2>/dev/null | wc -l | tr -d ' ')"
|
|
161
|
-
if (( count > 0 )); then
|
|
162
|
-
record "$rel_track" "tbd-in-completed" "$rel_file" "0" \
|
|
163
|
-
"$count TBD sentinel(s) at status=completed"
|
|
164
|
-
fi
|
|
165
|
-
done < <(find "$track_dir" -maxdepth 1 -type f -name '*.md')
|
|
184
|
+
check_tbd_budget "$track_dir" "$rel_track" 0 "tbd-in-completed" \
|
|
185
|
+
"at status=completed"
|
|
166
186
|
;;
|
|
167
187
|
esac
|
|
168
188
|
|
|
@@ -40,7 +40,7 @@ EOF
|
|
|
40
40
|
|
|
41
41
|
while [[ $# -gt 0 ]]; do
|
|
42
42
|
case "$1" in
|
|
43
|
-
--repo) REPO="$2"; shift 2;;
|
|
43
|
+
--repo) REPO="${2:?--repo requires a value}"; shift 2;;
|
|
44
44
|
--help|-h) usage; exit 0;;
|
|
45
45
|
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
46
46
|
esac
|
|
@@ -51,16 +51,9 @@ if [[ ! -d "$REPO" ]]; then
|
|
|
51
51
|
exit 1
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
55
|
-
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
56
|
-
|
|
57
54
|
unavailable() { echo '{"cycles":[],"source":"unavailable"}'; exit 2; }
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
command -v jq >/dev/null 2>&1 || unavailable
|
|
61
|
-
|
|
62
|
-
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
63
|
-
[[ -n "$PROJECT" ]] || unavailable
|
|
56
|
+
graph_bootstrap "$REPO" || unavailable
|
|
64
57
|
|
|
65
58
|
# 2- and 3-node CALLS cycles. Cypher lives in _graph_queries.sh (label-agnostic;
|
|
66
59
|
# the Phase 0 fix — code units are mostly :Method, and CALLS only connects
|
|
@@ -37,8 +37,13 @@ LOCK_FILE="${METRICS_DIR}/metrics.lock"
|
|
|
37
37
|
|
|
38
38
|
payload="${1:-}"
|
|
39
39
|
|
|
40
|
-
#
|
|
41
|
-
|
|
40
|
+
# Trim trailing whitespace/newlines so the closing-brace strip below matches
|
|
41
|
+
# even when the caller passes a payload with a stray trailing newline.
|
|
42
|
+
payload="${payload%"${payload##*[![:space:]]}"}"
|
|
43
|
+
|
|
44
|
+
# Validate that a payload was provided and looks like a JSON object — a
|
|
45
|
+
# payload not ending in '}' would corrupt the NDJSON file, so drop it.
|
|
46
|
+
if [[ -z "${payload}" || "${payload}" != *\} ]]; then
|
|
42
47
|
exit 0
|
|
43
48
|
fi
|
|
44
49
|
|
|
@@ -46,8 +46,8 @@ EOF
|
|
|
46
46
|
|
|
47
47
|
while [[ $# -gt 0 ]]; do
|
|
48
48
|
case "$1" in
|
|
49
|
-
--state) STATE_FILE="$2"; shift 2;;
|
|
50
|
-
--root) ROOT="$2"; shift 2;;
|
|
49
|
+
--state) STATE_FILE="${2:?--state requires a value}"; shift 2;;
|
|
50
|
+
--root) ROOT="${2:?--root requires a value}"; shift 2;;
|
|
51
51
|
--help|-h) usage; exit 0;;
|
|
52
52
|
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
53
53
|
esac
|
|
@@ -66,12 +66,12 @@ while [[ $# -gt 0 ]]; do
|
|
|
66
66
|
--yaml) FORMAT="yaml"; shift;;
|
|
67
67
|
--json) FORMAT="json"; shift;;
|
|
68
68
|
--project-metadata) WRITE_PROJECT_METADATA=1; shift;;
|
|
69
|
-
--project) PROJECT="$2"; shift 2;;
|
|
70
|
-
--module) MODULE="$2"; shift 2;;
|
|
71
|
-
--track-id) TRACK_ID="$2"; shift 2;;
|
|
72
|
-
--generated-by) GENERATED_BY="$2"; shift 2;;
|
|
73
|
-
--base) BASE_BRANCH="$2"; shift 2;;
|
|
74
|
-
--output-dir) OUTPUT_DIR="$2"; shift 2;;
|
|
69
|
+
--project) PROJECT="${2:?--project requires a value}"; shift 2;;
|
|
70
|
+
--module) MODULE="${2:?--module requires a value}"; shift 2;;
|
|
71
|
+
--track-id) TRACK_ID="${2:?--track-id requires a value}"; shift 2;;
|
|
72
|
+
--generated-by) GENERATED_BY="${2:?--generated-by requires a value}"; shift 2;;
|
|
73
|
+
--base) BASE_BRANCH="${2:?--base requires a value}"; shift 2;;
|
|
74
|
+
--output-dir) OUTPUT_DIR="${2:?--output-dir requires a value}"; shift 2;;
|
|
75
75
|
--help|-h) usage; exit 0;;
|
|
76
76
|
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
77
77
|
esac
|
|
@@ -42,7 +42,7 @@ EOF
|
|
|
42
42
|
|
|
43
43
|
while [[ $# -gt 0 ]]; do
|
|
44
44
|
case "$1" in
|
|
45
|
-
--repo) REPO="$2"; shift 2;;
|
|
45
|
+
--repo) REPO="${2:?--repo requires a value}"; shift 2;;
|
|
46
46
|
--help|-h) usage; exit 0;;
|
|
47
47
|
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
48
48
|
esac
|
|
@@ -53,20 +53,14 @@ if [[ ! -d "$REPO" ]]; then
|
|
|
53
53
|
exit 1
|
|
54
54
|
fi
|
|
55
55
|
|
|
56
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
57
|
-
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
58
|
-
|
|
59
56
|
unavailable() { echo '{"source":"unavailable"}'; exit 2; }
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
command -v jq >/dev/null 2>&1 || unavailable
|
|
63
|
-
|
|
64
|
-
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
65
|
-
[[ -n "$PROJECT" ]] || unavailable
|
|
58
|
+
graph_bootstrap "$REPO" || unavailable
|
|
66
59
|
|
|
67
60
|
ARCH_JSON="$(memory_cli get_architecture "{\"project\":\"$PROJECT\",\"aspects\":[\"all\"]}" || true)"
|
|
68
61
|
[[ -n "$ARCH_JSON" ]] || unavailable
|
|
69
62
|
|
|
70
63
|
# Validate it parses and looks like an architecture object before emitting.
|
|
64
|
+
# Tag with source like every other graph-*.sh wrapper (universal contract).
|
|
71
65
|
echo "$ARCH_JSON" | jq -e '.total_nodes != null' >/dev/null 2>&1 || unavailable
|
|
72
|
-
echo "$ARCH_JSON" | jq '.'
|
|
66
|
+
echo "$ARCH_JSON" | jq '. + {source:"memory-graph"}'
|
|
@@ -54,8 +54,8 @@ EOF
|
|
|
54
54
|
|
|
55
55
|
while [[ $# -gt 0 ]]; do
|
|
56
56
|
case "$1" in
|
|
57
|
-
--repo) REPO="$2"; shift 2;;
|
|
58
|
-
--symbol) SYMBOL="$2"; shift 2;;
|
|
57
|
+
--repo) REPO="${2:?--repo requires a value}"; shift 2;;
|
|
58
|
+
--symbol) SYMBOL="${2:?--symbol requires a value}"; shift 2;;
|
|
59
59
|
--transitive) TRANSITIVE=1; shift;;
|
|
60
60
|
--transitive=*) TRANSITIVE=1; DEPTH="${1#*=}"; shift;;
|
|
61
61
|
--prod-only) PROD_ONLY=1; shift;;
|
|
@@ -69,20 +69,13 @@ done
|
|
|
69
69
|
[[ -n "$SYMBOL" ]] || { echo "ERROR: --symbol is required" >&2; usage >&2; exit 1; }
|
|
70
70
|
[[ "$DEPTH" =~ ^[0-9]+$ ]] || { echo "ERROR: --transitive depth must be a non-negative integer" >&2; exit 1; }
|
|
71
71
|
|
|
72
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
73
|
-
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
74
|
-
|
|
75
72
|
unavailable() {
|
|
76
73
|
jq -n --arg s "$SYMBOL" '{symbol:$s, callers:[], status:"unavailable", source:"unavailable"}' 2>/dev/null \
|
|
77
74
|
|| echo '{"callers":[],"status":"unavailable","source":"unavailable"}'
|
|
78
75
|
exit 2
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
command -v jq >/dev/null 2>&1 || unavailable
|
|
83
|
-
|
|
84
|
-
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
85
|
-
[[ -n "$PROJECT" ]] || unavailable
|
|
78
|
+
graph_bootstrap "$REPO" || unavailable
|
|
86
79
|
|
|
87
80
|
SYM_ESC="$(gq_escape "$SYMBOL")"
|
|
88
81
|
|
|
@@ -95,8 +88,7 @@ if [[ "$TRANSITIVE" -eq 1 ]]; then
|
|
|
95
88
|
echo "$RES" | jq -e . >/dev/null 2>&1 || unavailable
|
|
96
89
|
N="$(echo "$RES" | jq -r '(.callers // []) | length' 2>/dev/null || echo 0)"
|
|
97
90
|
if [[ "$N" -gt 0 ]]; then STATUS="ok"; else
|
|
98
|
-
|
|
99
|
-
if [[ -n "$EX" && "$(gq_rows_len "$EX")" -gt 0 ]]; then STATUS="no-edges"; else STATUS="no-match"; fi
|
|
91
|
+
STATUS="$(gq_symbol_status "$PROJECT" "$SYM_ESC" '{"rows":[]}')"
|
|
100
92
|
fi
|
|
101
93
|
echo "$RES" | jq --arg s "$SYMBOL" --arg st "$STATUS" '
|
|
102
94
|
{symbol:$s,
|
|
@@ -43,8 +43,8 @@ EOF
|
|
|
43
43
|
|
|
44
44
|
while [[ $# -gt 0 ]]; do
|
|
45
45
|
case "$1" in
|
|
46
|
-
--repo) REPO="$2"; shift 2;;
|
|
47
|
-
--file) FILE="$2"; shift 2;;
|
|
46
|
+
--repo) REPO="${2:?--repo requires a value}"; shift 2;;
|
|
47
|
+
--file) FILE="${2:?--file requires a value}"; shift 2;;
|
|
48
48
|
--help|-h) usage; exit 0;;
|
|
49
49
|
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
50
50
|
esac
|
|
@@ -52,16 +52,9 @@ done
|
|
|
52
52
|
|
|
53
53
|
[[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
|
|
54
54
|
|
|
55
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
56
|
-
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
57
|
-
|
|
58
55
|
unavailable() { echo '{"imports":[],"total":0,"source":"unavailable"}'; exit 2; }
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
command -v jq >/dev/null 2>&1 || unavailable
|
|
62
|
-
|
|
63
|
-
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
64
|
-
[[ -n "$PROJECT" ]] || unavailable
|
|
57
|
+
graph_bootstrap "$REPO" || unavailable
|
|
65
58
|
|
|
66
59
|
RES="$(gq_run "$PROJECT" "$(gq_q_imports)" || true)"
|
|
67
60
|
[[ -n "$RES" ]] || unavailable
|
|
@@ -42,9 +42,9 @@ EOF
|
|
|
42
42
|
|
|
43
43
|
while [[ $# -gt 0 ]]; do
|
|
44
44
|
case "$1" in
|
|
45
|
-
--repo) REPO="$2"; shift 2;;
|
|
46
|
-
--symbol) SYMBOL="$2"; shift 2;;
|
|
47
|
-
--type) TYPE="$2"; shift 2;;
|
|
45
|
+
--repo) REPO="${2:?--repo requires a value}"; shift 2;;
|
|
46
|
+
--symbol) SYMBOL="${2:?--symbol requires a value}"; shift 2;;
|
|
47
|
+
--type) TYPE="${2:?--type requires a value}"; shift 2;;
|
|
48
48
|
--help|-h) usage; exit 0;;
|
|
49
49
|
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
50
50
|
esac
|
|
@@ -56,9 +56,6 @@ if [[ -n "$SYMBOL" && -n "$TYPE" ]]; then
|
|
|
56
56
|
fi
|
|
57
57
|
[[ -n "$SYMBOL" || -n "$TYPE" ]] || { echo "ERROR: provide --symbol or --type" >&2; usage >&2; exit 1; }
|
|
58
58
|
|
|
59
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
60
|
-
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
61
|
-
|
|
62
59
|
unavailable() {
|
|
63
60
|
if [[ -n "$TYPE" ]]; then
|
|
64
61
|
jq -n --arg t "$TYPE" '{type:$t, raisers:[], status:"unavailable", source:"unavailable"}' 2>/dev/null \
|
|
@@ -70,11 +67,7 @@ unavailable() {
|
|
|
70
67
|
exit 2
|
|
71
68
|
}
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
command -v jq >/dev/null 2>&1 || unavailable
|
|
75
|
-
|
|
76
|
-
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
77
|
-
[[ -n "$PROJECT" ]] || unavailable
|
|
70
|
+
graph_bootstrap "$REPO" || unavailable
|
|
78
71
|
|
|
79
72
|
if [[ -n "$TYPE" ]]; then
|
|
80
73
|
SYM_ESC="$(gq_escape "$TYPE")"
|
|
@@ -43,9 +43,9 @@ EOF
|
|
|
43
43
|
|
|
44
44
|
while [[ $# -gt 0 ]]; do
|
|
45
45
|
case "$1" in
|
|
46
|
-
--repo) REPO="$2"; shift 2;;
|
|
47
|
-
--symbol) SYMBOL="$2"; shift 2;;
|
|
48
|
-
--derived) DERIVED="$2"; shift 2;;
|
|
46
|
+
--repo) REPO="${2:?--repo requires a value}"; shift 2;;
|
|
47
|
+
--symbol) SYMBOL="${2:?--symbol requires a value}"; shift 2;;
|
|
48
|
+
--derived) DERIVED="${2:?--derived requires a value}"; shift 2;;
|
|
49
49
|
--help|-h) usage; exit 0;;
|
|
50
50
|
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
51
51
|
esac
|
|
@@ -56,16 +56,9 @@ if [[ -n "$SYMBOL" && -n "$DERIVED" ]]; then
|
|
|
56
56
|
echo "ERROR: use either --symbol or --derived, not both" >&2; exit 1
|
|
57
57
|
fi
|
|
58
58
|
|
|
59
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
60
|
-
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
61
|
-
|
|
62
59
|
unavailable() { echo '{"edges":[],"status":"unavailable","source":"unavailable"}'; exit 2; }
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
command -v jq >/dev/null 2>&1 || unavailable
|
|
66
|
-
|
|
67
|
-
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
68
|
-
[[ -n "$PROJECT" ]] || unavailable
|
|
61
|
+
graph_bootstrap "$REPO" || unavailable
|
|
69
62
|
|
|
70
63
|
if [[ -n "$SYMBOL" ]]; then
|
|
71
64
|
SYM_ESC="$(gq_escape "$SYMBOL")"; Q="$(gq_q_inherits_sym "$SYM_ESC")"; PROBE="$SYM_ESC"
|