@drafthq/draft 3.5.2 → 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.
Files changed (86) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.cursor-plugin/plugin.json +1 -1
  4. package/cli/src/hosts/cursor.js +3 -0
  5. package/cli/src/installer.js +7 -3
  6. package/cli/src/lib/fsx.js +3 -0
  7. package/cli/src/lib/marker.js +8 -3
  8. package/core/shared/condensation.md +1 -1
  9. package/core/shared/git-report-metadata.md +1 -1
  10. package/core/shared/graph-query.md +24 -24
  11. package/core/shared/tool-resolver.md +20 -15
  12. package/core/templates/okf/index.md +6 -10
  13. package/core/templates/okf/section-index.md +2 -11
  14. package/core/templates/plan.md +1 -1
  15. package/integrations/agents/AGENTS.md +142 -492
  16. package/integrations/copilot/.github/copilot-instructions.md +142 -492
  17. package/package.json +1 -1
  18. package/scripts/fetch-memory-engine.sh +1 -1
  19. package/scripts/lib.sh +79 -2
  20. package/scripts/tools/_graph_queries.sh +12 -5
  21. package/scripts/tools/_lib.sh +56 -8
  22. package/scripts/tools/adr-index.sh +1 -1
  23. package/scripts/tools/check-skill-line-caps.sh +2 -2
  24. package/scripts/tools/check-track-hygiene.sh +41 -21
  25. package/scripts/tools/classify-files.sh +1 -1
  26. package/scripts/tools/cycle-detect.sh +2 -9
  27. package/scripts/tools/detect-test-framework.sh +1 -1
  28. package/scripts/tools/emit-skill-metrics.sh +7 -2
  29. package/scripts/tools/freshness-check.sh +2 -2
  30. package/scripts/tools/git-metadata.sh +6 -6
  31. package/scripts/tools/graph-arch.sh +4 -10
  32. package/scripts/tools/graph-callers.sh +4 -12
  33. package/scripts/tools/graph-deps.sh +3 -10
  34. package/scripts/tools/graph-errors.sh +4 -11
  35. package/scripts/tools/graph-hierarchy.sh +4 -11
  36. package/scripts/tools/graph-impact.sh +17 -17
  37. package/scripts/tools/graph-init.sh +1 -1
  38. package/scripts/tools/graph-preflight.sh +1 -1
  39. package/scripts/tools/graph-query.sh +5 -12
  40. package/scripts/tools/graph-risk.sh +3 -10
  41. package/scripts/tools/graph-search.sh +4 -11
  42. package/scripts/tools/graph-snapshot.sh +9 -4
  43. package/scripts/tools/graph-snippet.sh +3 -10
  44. package/scripts/tools/graph-tests.sh +3 -10
  45. package/scripts/tools/graph-traces.sh +3 -10
  46. package/scripts/tools/hotspot-rank.sh +4 -11
  47. package/scripts/tools/mermaid-from-graph.sh +3 -10
  48. package/scripts/tools/migrate-track-frontmatter.sh +3 -1
  49. package/scripts/tools/okf-coverage-check.sh +24 -6
  50. package/scripts/tools/okf-emit-catalog.sh +256 -0
  51. package/scripts/tools/okf-fix-links.sh +326 -0
  52. package/scripts/tools/okf-plan-concepts.sh +245 -14
  53. package/scripts/tools/okf-render-views.sh +65 -13
  54. package/scripts/tools/okf-validate-all.sh +20 -9
  55. package/scripts/tools/okf-validate-quality.sh +2 -8
  56. package/scripts/tools/okf-validate.sh +20 -10
  57. package/scripts/tools/parse-git-log.sh +4 -4
  58. package/scripts/tools/parse-reports.sh +1 -1
  59. package/scripts/tools/render-track.sh +1 -1
  60. package/scripts/tools/run-coverage.sh +1 -1
  61. package/scripts/tools/scan-markers.sh +4 -4
  62. package/scripts/tools/validate-frontmatter.sh +2 -2
  63. package/scripts/tools/verify-citations.sh +2 -2
  64. package/scripts/tools/verify-doc-anchors.sh +1 -1
  65. package/scripts/tools/verify-graph-binary.sh +2 -2
  66. package/skills/adr/SKILL.md +1 -1
  67. package/skills/bughunt/SKILL.md +1 -1
  68. package/skills/coverage/SKILL.md +2 -2
  69. package/skills/debug/SKILL.md +1 -1
  70. package/skills/decompose/SKILL.md +3 -3
  71. package/skills/deep-review/SKILL.md +2 -2
  72. package/skills/deploy-checklist/SKILL.md +1 -1
  73. package/skills/graph/SKILL.md +1 -1
  74. package/skills/impact/SKILL.md +1 -1
  75. package/skills/implement/SKILL.md +2 -2
  76. package/skills/init/SKILL.md +10 -7
  77. package/skills/init/references/architecture-spec.md +1 -1
  78. package/skills/init/references/okf-emitter.md +51 -76
  79. package/skills/learn/SKILL.md +1 -1
  80. package/skills/quick-review/SKILL.md +1 -1
  81. package/skills/review/SKILL.md +3 -3
  82. package/skills/standup/SKILL.md +1 -1
  83. package/skills/status/SKILL.md +1 -1
  84. package/skills/tech-debt/SKILL.md +2 -2
  85. package/skills/upload/SKILL.md +1 -1
  86. package/core/templates/track-architecture.md +0 -311
@@ -43,10 +43,10 @@ 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;;
48
- --symbol) SYMBOL="$2"; shift 2;;
49
- --depth) DEPTH="$2"; shift 2;;
46
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
47
+ --file) FILE="${2:?--file requires a value}"; shift 2;;
48
+ --symbol) SYMBOL="${2:?--symbol requires a value}"; shift 2;;
49
+ --depth) DEPTH="${2:?--depth requires a value}"; shift 2;;
50
50
  --help|-h) usage; exit 0;;
51
51
  *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
52
52
  esac
@@ -56,9 +56,6 @@ done
56
56
  [[ -n "$FILE" || -n "$SYMBOL" ]] || { echo "ERROR: provide --file or --symbol" >&2; usage >&2; exit 1; }
57
57
  [[ "$DEPTH" =~ ^[0-9]+$ ]] || { echo "ERROR: --depth must be a non-negative integer" >&2; exit 1; }
58
58
 
59
- REPO_ABS="$(cd "$REPO" && pwd)"
60
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
61
-
62
59
  unavailable() {
63
60
  local t="$1" k="$2"
64
61
  jq -n --arg t "$t" --arg k "$k" '{target:$t, kind:$k, impacted:[], source:"unavailable"}' 2>/dev/null \
@@ -68,24 +65,27 @@ unavailable() {
68
65
 
69
66
  if [[ -n "$SYMBOL" ]]; then TARGET="$SYMBOL"; KIND="symbol"; else TARGET="$FILE"; KIND="file"; fi
70
67
 
71
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable "$TARGET" "$KIND"
72
- command -v jq >/dev/null 2>&1 || unavailable "$TARGET" "$KIND"
73
-
74
- PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
75
- [[ -n "$PROJECT" ]] || unavailable "$TARGET" "$KIND"
68
+ graph_bootstrap "$REPO" || unavailable "$TARGET" "$KIND"
76
69
 
77
70
  if [[ -n "$SYMBOL" ]]; then
78
71
  # direction:"both" is the reliable form (the "callers" value returns empty in this engine);
79
- # we read the .callers array from it.
80
- RES="$(memory_cli trace_path "{\"project\":\"$PROJECT\",\"function_name\":\"$SYMBOL\",\"depth\":$DEPTH,\"direction\":\"both\"}" || echo '{}')"
81
- echo "${RES:-{\}}" | jq --arg t "$TARGET" '
72
+ # we read the .callers array from it. Payload built with jq so a quote in
73
+ # --symbol can never corrupt the JSON; an engine failure is unavailable,
74
+ # never a fabricated empty-impact success.
75
+ PAYLOAD="$(jq -n --arg p "$PROJECT" --arg f "$SYMBOL" --argjson d "$DEPTH" \
76
+ '{project:$p, function_name:$f, depth:$d, direction:"both"}')"
77
+ RES="$(memory_cli trace_path "$PAYLOAD" 2>/dev/null || true)"
78
+ echo "$RES" | jq -e . >/dev/null 2>&1 || unavailable "$TARGET" "$KIND"
79
+ echo "$RES" | jq --arg t "$TARGET" '
82
80
  {target:$t, kind:"symbol",
83
81
  impacted: [ (.callers // [])[] | {name:.name, file:(.qualified_name // ""), hop:(.hop // 1)} ],
84
82
  source:"memory-graph"}'
85
83
  else
86
84
  # File impact: detect_changes maps the working-tree diff to impacted symbols.
87
- RES="$(memory_cli detect_changes "{\"project\":\"$PROJECT\"}" || echo '{}')"
88
- echo "${RES:-{\}}" | jq --arg t "$TARGET" '
85
+ PAYLOAD="$(jq -n --arg p "$PROJECT" '{project:$p}')"
86
+ RES="$(memory_cli detect_changes "$PAYLOAD" 2>/dev/null || true)"
87
+ echo "$RES" | jq -e . >/dev/null 2>&1 || unavailable "$TARGET" "$KIND"
88
+ echo "$RES" | jq --arg t "$TARGET" '
89
89
  {target:$t, kind:"file",
90
90
  impacted: [ (.impacted_symbols // [])[]
91
91
  | select((.file // "") | endswith($t) or (. == $t))
@@ -52,7 +52,7 @@ EOF
52
52
 
53
53
  while [[ $# -gt 0 ]]; do
54
54
  case "$1" in
55
- --scope) SCOPE="$2"; shift 2;;
55
+ --scope) SCOPE="${2:?--scope requires a value}"; shift 2;;
56
56
  --module-only) MODULE_ONLY=1; shift;;
57
57
  --no-fetch) NO_FETCH=1; shift;;
58
58
  --json) EMIT_JSON=1; shift;;
@@ -170,7 +170,7 @@ if find_memory_bin "$REPO_ABS" "$SELF_REPO"; then
170
170
  ok "Engine: $ENGINE ($VER)"
171
171
  LIMIT="$("$ENGINE" config list 2>/dev/null | awk '/auto_index_limit/{print $3}' || true)"
172
172
  [[ -n "$LIMIT" ]] && info "auto_index_limit: $LIMIT (governs AUTO-index only; explicit index_repository should bypass)" || true
173
- if [[ "$GIT_OK" -eq 1 && -n "${LIMIT:-}" && "$TRACKED" -gt "$LIMIT" ]]; then
173
+ if [[ "$GIT_OK" -eq 1 && "${LIMIT:-}" =~ ^[0-9]+$ && "$TRACKED" -gt "$LIMIT" ]]; then
174
174
  warn "Tracked files ($TRACKED) > auto_index_limit ($LIMIT) — confirm the explicit index isn't truncated near $LIMIT."
175
175
  fi
176
176
  else
@@ -59,10 +59,10 @@ EOF
59
59
 
60
60
  while [[ $# -gt 0 ]]; do
61
61
  case "$1" in
62
- --repo) REPO="$2"; shift 2;;
63
- --cypher) CYPHER="$2"; shift 2;;
64
- --tool) TOOL="$2"; shift 2;;
65
- --json) TOOL_JSON="$2"; shift 2;;
62
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
63
+ --cypher) CYPHER="${2:?--cypher requires a value}"; shift 2;;
64
+ --tool) TOOL="${2:?--tool requires a value}"; shift 2;;
65
+ --json) TOOL_JSON="${2:?--json requires a value}"; shift 2;;
66
66
  --help|-h) usage; exit 0;;
67
67
  *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
68
68
  esac
@@ -98,16 +98,9 @@ if [[ -n "$CYPHER" ]]; then
98
98
  fi
99
99
  fi
100
100
 
101
- REPO_ABS="$(cd "$REPO" && pwd)"
102
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
103
-
104
101
  unavailable() { echo '{"source":"unavailable"}'; exit 2; }
105
102
 
106
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
107
- command -v jq >/dev/null 2>&1 || unavailable
108
-
109
- PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
110
- [[ -n "$PROJECT" ]] || unavailable
103
+ graph_bootstrap "$REPO" || unavailable
111
104
 
112
105
  if [[ -n "$CYPHER" ]]; then
113
106
  RES="$(gq_run "$PROJECT" "$CYPHER" || true)"
@@ -42,8 +42,8 @@ EOF
42
42
 
43
43
  while [[ $# -gt 0 ]]; do
44
44
  case "$1" in
45
- --repo) REPO="$2"; shift 2;;
46
- --min-complexity) MIN_COMPLEXITY="$2"; shift 2;;
45
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
46
+ --min-complexity) MIN_COMPLEXITY="${2:?--min-complexity requires a value}"; shift 2;;
47
47
  --help|-h) usage; exit 0;;
48
48
  *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
49
49
  esac
@@ -52,16 +52,9 @@ done
52
52
  [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
53
53
  [[ "$MIN_COMPLEXITY" =~ ^[0-9]+$ ]] || { echo "ERROR: --min-complexity must be a non-negative integer" >&2; exit 1; }
54
54
 
55
- REPO_ABS="$(cd "$REPO" && pwd)"
56
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
57
-
58
55
  unavailable() { echo '{"risky":[],"total":0,"source":"unavailable"}'; exit 2; }
59
56
 
60
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
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_risk)" || true)"
67
60
  [[ -n "$RES" ]] || unavailable
@@ -40,9 +40,9 @@ EOF
40
40
 
41
41
  while [[ $# -gt 0 ]]; do
42
42
  case "$1" in
43
- --repo) REPO="$2"; shift 2;;
44
- --query) QUERY="$2"; shift 2;;
45
- --limit) LIMIT="$2"; shift 2;;
43
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
44
+ --query) QUERY="${2:?--query requires a value}"; shift 2;;
45
+ --limit) LIMIT="${2:?--limit 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
@@ -52,20 +52,13 @@ done
52
52
  [[ -n "$QUERY" ]] || { echo "ERROR: --query is required" >&2; usage >&2; exit 1; }
53
53
  [[ "$LIMIT" =~ ^[0-9]+$ ]] || { echo "ERROR: --limit must be a non-negative integer" >&2; exit 1; }
54
54
 
55
- REPO_ABS="$(cd "$REPO" && pwd)"
56
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
57
-
58
55
  unavailable() {
59
56
  jq -n --arg q "$QUERY" '{query:$q, results:[], source:"unavailable"}' 2>/dev/null \
60
57
  || echo '{"results":[],"source":"unavailable"}'
61
58
  exit 2
62
59
  }
63
60
 
64
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
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
  ARGS="$(jq -n --arg p "$PROJECT" --arg q "$QUERY" --argjson n "$LIMIT" \
71
64
  '{project:$p, query:$q, limit:$n}')"
@@ -52,8 +52,8 @@ EOF
52
52
 
53
53
  while [[ $# -gt 0 ]]; do
54
54
  case "$1" in
55
- --repo) REPO="$2"; shift 2;;
56
- --out) OUT_DIR="$2"; shift 2;;
55
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
56
+ --out) OUT_DIR="${2:?--out requires a value}"; shift 2;;
57
57
  --help|-h) usage; exit 0;;
58
58
  *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
59
59
  esac
@@ -99,6 +99,11 @@ echo "$CHANGES_JSON" | jq -e . >/dev/null 2>&1 || CHANGES_JSON='{}'
99
99
  CHANGED_FILES="$(echo "$CHANGES_JSON" | jq -r '.changed_count // (.changed_files | length?) // 0' 2>/dev/null || echo 0)"
100
100
  IMPACTED="$(echo "$CHANGES_JSON" | jq -r '(.impacted_symbols | length?) // 0' 2>/dev/null || echo 0)"
101
101
 
102
+ # YAML double-quoted scalars: escape backslashes then quotes so an unusual
103
+ # project name or engine version string can never corrupt the marker.
104
+ PROJECT_Y="${PROJECT//\\/\\\\}"; PROJECT_Y="${PROJECT_Y//\"/\\\"}"
105
+ VER_Y="${VER//\\/\\\\}"; VER_Y="${VER_Y//\"/\\\"}"
106
+
102
107
  cat > "$OUT/schema.yaml" <<EOF
103
108
  # Draft graph gate marker — written by scripts/tools/graph-snapshot.sh
104
109
  # Draft is engine-only: this file carries NO graph data. Its presence signals that
@@ -106,8 +111,8 @@ cat > "$OUT/schema.yaml" <<EOF
106
111
  # live via the graph-*.sh wrappers (or \`codebase-memory-mcp cli <tool>\`).
107
112
  # Counts below are point-of-index provenance; the live engine is authoritative.
108
113
  engine: codebase-memory-mcp
109
- engine_version: "$VER"
110
- project: "$PROJECT"
114
+ engine_version: "$VER_Y"
115
+ project: "$PROJECT_Y"
111
116
  generated_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
112
117
  indexed_nodes: $NODES
113
118
  indexed_edges: $EDGES
@@ -44,8 +44,8 @@ EOF
44
44
 
45
45
  while [[ $# -gt 0 ]]; do
46
46
  case "$1" in
47
- --repo) REPO="$2"; shift 2;;
48
- --qualified|--symbol) QNAME="$2"; shift 2;;
47
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
48
+ --qualified|--symbol) QNAME="${2:?--qualified|--symbol 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
@@ -54,20 +54,13 @@ done
54
54
  [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
55
55
  [[ -n "$QNAME" ]] || { echo "ERROR: --qualified is required" >&2; usage >&2; exit 1; }
56
56
 
57
- REPO_ABS="$(cd "$REPO" && pwd)"
58
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
59
-
60
57
  unavailable() {
61
58
  jq -n --arg q "$QNAME" '{qualified_name:$q, status:"unavailable", source:"unavailable"}' 2>/dev/null \
62
59
  || echo '{"status":"unavailable","source":"unavailable"}'
63
60
  exit 2
64
61
  }
65
62
 
66
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
67
- command -v jq >/dev/null 2>&1 || unavailable
68
-
69
- PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
70
- [[ -n "$PROJECT" ]] || unavailable
63
+ graph_bootstrap "$REPO" || unavailable
71
64
 
72
65
  ARGS="$(jq -n --arg p "$PROJECT" --arg q "$QNAME" '{project:$p, qualified_name:$q}')"
73
66
  RES="$(memory_cli get_code_snippet "$ARGS" 2>/dev/null || true)"
@@ -48,8 +48,8 @@ EOF
48
48
 
49
49
  while [[ $# -gt 0 ]]; do
50
50
  case "$1" in
51
- --repo) REPO="$2"; shift 2;;
52
- --symbol) SYMBOL="$2"; shift 2;;
51
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
52
+ --symbol) SYMBOL="${2:?--symbol requires a value}"; shift 2;;
53
53
  --untested) UNTESTED=1; shift;;
54
54
  --help|-h) usage; exit 0;;
55
55
  *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
@@ -59,9 +59,6 @@ done
59
59
  [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
60
60
  [[ -n "$SYMBOL" || "$UNTESTED" -eq 1 ]] || { echo "ERROR: provide --symbol or --untested" >&2; usage >&2; exit 1; }
61
61
 
62
- REPO_ABS="$(cd "$REPO" && pwd)"
63
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
64
-
65
62
  unavailable() {
66
63
  if [[ "$UNTESTED" -eq 1 ]]; then
67
64
  echo '{"untested":[],"total":0,"source":"unavailable"}'
@@ -72,11 +69,7 @@ unavailable() {
72
69
  exit 2
73
70
  }
74
71
 
75
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
76
- command -v jq >/dev/null 2>&1 || unavailable
77
-
78
- PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
79
- [[ -n "$PROJECT" ]] || unavailable
72
+ graph_bootstrap "$REPO" || unavailable
80
73
 
81
74
  if [[ "$UNTESTED" -eq 1 ]]; then
82
75
  EXP="$(gq_run "$PROJECT" "$(gq_q_exported)" || true)"
@@ -49,8 +49,8 @@ EOF
49
49
  while [[ $# -gt 0 ]]; do
50
50
  case "$1" in
51
51
  ingest) ACTION="ingest"; shift;;
52
- --repo) REPO="$2"; shift 2;;
53
- --file) FILE="$2"; shift 2;;
52
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
53
+ --file) FILE="${2:?--file requires a value}"; shift 2;;
54
54
  --experimental) EXPERIMENTAL=1; shift;;
55
55
  --help|-h) usage; exit 0;;
56
56
  *) echo "Unknown argument: $1" >&2; usage >&2; exit 1;;
@@ -63,19 +63,12 @@ done
63
63
  [[ -n "$FILE" ]] || { echo "ERROR: --file is required" >&2; usage >&2; exit 1; }
64
64
  [[ -f "$FILE" ]] || { echo "ERROR: --file '$FILE' does not exist" >&2; exit 1; }
65
65
 
66
- REPO_ABS="$(cd "$REPO" && pwd)"
67
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
68
-
69
66
  unavailable() { echo '{"source":"unavailable"}'; exit 2; }
70
67
 
71
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
72
- command -v jq >/dev/null 2>&1 || unavailable
68
+ graph_bootstrap "$REPO" || unavailable
73
69
 
74
70
  jq -e . "$FILE" >/dev/null 2>&1 || { echo "ERROR: --file is not valid JSON" >&2; exit 1; }
75
71
 
76
- PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
77
- [[ -n "$PROJECT" ]] || unavailable
78
-
79
72
  ARGS="$(jq -n --arg p "$PROJECT" --slurpfile t "$FILE" '{project:$p, traces:($t[0])}')"
80
73
  RES="$(memory_cli ingest_traces "$ARGS" 2>/dev/null || true)"
81
74
  [[ -n "$RES" ]] || unavailable
@@ -47,8 +47,8 @@ EOF
47
47
 
48
48
  while [[ $# -gt 0 ]]; do
49
49
  case "$1" in
50
- --repo) REPO="$2"; shift 2;;
51
- --top) TOP="$2"; shift 2;;
50
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
51
+ --top) TOP="${2:?--top requires a value}"; shift 2;;
52
52
  --help|-h) usage; exit 0;;
53
53
  *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
54
54
  esac
@@ -61,16 +61,9 @@ if [[ ! -d "$REPO" ]]; then
61
61
  exit 1
62
62
  fi
63
63
 
64
- REPO_ABS="$(cd "$REPO" && pwd)"
65
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
66
-
67
64
  unavailable() { echo '{"hotspots":[],"source":"unavailable"}'; exit 2; }
68
65
 
69
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
70
- command -v jq >/dev/null 2>&1 || unavailable
71
-
72
- PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
73
- [[ -n "$PROJECT" ]] || unavailable
66
+ graph_bootstrap "$REPO" || unavailable
74
67
 
75
68
  ARCH_JSON="$(memory_cli get_architecture "{\"project\":\"$PROJECT\",\"aspects\":[\"hotspots\"]}" || true)"
76
69
  [[ -n "$ARCH_JSON" ]] || unavailable
@@ -87,7 +80,7 @@ printf '%s' "$ARCH_JSON" > "$TMP_ARCH"
87
80
  printf '%s' "$PROPS_JSON" > "$TMP_PROPS"
88
81
 
89
82
  jq -n --slurpfile arch "$TMP_ARCH" --slurpfile props "$TMP_PROPS" --argjson top "$TOP" '
90
- (($props[0].rows) // []) as $prows
83
+ ((($props[0].rows) // []) | map(select(.[0] != null))) as $prows
91
84
  | (reduce $prows[] as $r ({};
92
85
  .[$r[0]] = {c:((($r[1]) // "0") | tonumber? // 0),
93
86
  cog:((($r[2]) // "0") | tonumber? // 0),
@@ -45,8 +45,8 @@ EOF
45
45
 
46
46
  while [[ $# -gt 0 ]]; do
47
47
  case "$1" in
48
- --repo) REPO="$2"; shift 2;;
49
- --diagram) DIAGRAM="$2"; shift 2;;
48
+ --repo) REPO="${2:?--repo requires a value}"; shift 2;;
49
+ --diagram) DIAGRAM="${2:?--diagram requires a value}"; shift 2;;
50
50
  --help|-h) usage; exit 0;;
51
51
  *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
52
52
  esac
@@ -57,9 +57,6 @@ if [[ ! -d "$REPO" ]]; then
57
57
  exit 1
58
58
  fi
59
59
 
60
- REPO_ABS="$(cd "$REPO" && pwd)"
61
- SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
62
-
63
60
  stub() {
64
61
  cat <<'EOF'
65
62
  ```mermaid
@@ -71,11 +68,7 @@ EOF
71
68
  exit 2
72
69
  }
73
70
 
74
- find_memory_bin "$REPO_ABS" "$SELF_REPO" || stub
75
- command -v jq >/dev/null 2>&1 || stub
76
-
77
- PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
78
- [[ -n "$PROJECT" ]] || stub
71
+ graph_bootstrap "$REPO" || stub
79
72
 
80
73
  # module-deps: real IMPORTS edges (the auto-derived dependency graph). Self-imports
81
74
  # (src == dst) are dropped so the diagram is a true cross-file graph. Capped at 40
@@ -223,7 +223,9 @@ EOF
223
223
  printf 'migrate: would strip ephemeral frontmatter from %s\n' "$path"
224
224
  else
225
225
  (( BACKUP )) && cp "$path" "$path.bak"
226
- local _tmp; _tmp="$(mktemp "${path}.XXXXXX")"; printf '%s' "$after" > "$_tmp" && mv -f "$_tmp" "$path"
226
+ # %s\n restores the EOF newline stripped by command substitution
227
+ # (same pattern as fix-whitespace.sh).
228
+ local _tmp; _tmp="$(mktemp "${path}.XXXXXX")"; printf '%s\n' "$after" > "$_tmp" && mv -f "$_tmp" "$path"
227
229
  printf 'migrate: stripped ephemeral frontmatter from %s\n' "$path"
228
230
  fi
229
231
  fi
@@ -57,12 +57,12 @@ EOF
57
57
 
58
58
  while [[ $# -gt 0 ]]; do
59
59
  case "$1" in
60
- --plan) PLAN="$2"; shift 2;;
61
- --bundle) BUNDLE="$2"; shift 2;;
62
- --min-stub-lines) MIN_STUB_LINES="$2"; shift 2;;
60
+ --plan) PLAN="${2:?--plan requires a value}"; shift 2;;
61
+ --bundle) BUNDLE="${2:?--bundle requires a value}"; shift 2;;
62
+ --min-stub-lines) MIN_STUB_LINES="${2:?--min-stub-lines requires a value}"; shift 2;;
63
63
  --no-coverage-page) WRITE_PAGE=0; shift;;
64
64
  --json) JSON=1; shift;;
65
- --report) REPORT="$2"; shift 2;;
65
+ --report) REPORT="${2:?--report requires a value}"; shift 2;;
66
66
  --help|-h) usage; exit 0;;
67
67
  -*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
68
68
  *) echo "Unexpected arg: $1" >&2; usage >&2; exit 1;;
@@ -122,6 +122,17 @@ write_coverage_page() {
122
122
  mkdir -p "$BUNDLE/systems"
123
123
  local tmp; tmp="$(mktemp)"
124
124
  {
125
+ # OKF §9.1/§9.2: every non-reserved .md needs parseable frontmatter with a
126
+ # non-empty `type`. coverage.md is tool-generated and not a code concept, so
127
+ # it uses a descriptive (non-frozen) type; okf-validate.sh exempts it from the
128
+ # frozen-vocab check via is_meta_page (basename + the marker below).
129
+ echo "---"
130
+ echo "type: Report"
131
+ echo "title: Component Coverage"
132
+ echo "description: Coverage of required components by wiki pages — which are documented, stubbed, or missing."
133
+ echo "resource: ."
134
+ echo "---"
135
+ echo ""
125
136
  echo "<!-- okf:coverage-generated -->"
126
137
  echo "# Component Coverage"
127
138
  echo ""
@@ -130,14 +141,21 @@ write_coverage_page() {
130
141
  echo ""
131
142
  echo "| Component | Wiki page | Status | Fan-in |"
132
143
  echo "|-----------|-----------|--------|--------|"
133
- local cid status fanin
144
+ local cid status fanin link
134
145
  while IFS=$'\t' read -r cid required reason ftype fanin; do
135
146
  [[ -z "$cid" ]] && continue
136
147
  if [[ "$required" == "true" ]]; then
137
148
  if [[ -f "$BUNDLE/$cid" ]]; then
138
149
  bl="$(body_lines "$BUNDLE/$cid")"
150
+ # coverage.md lives in systems/; link relative to it. Concepts in
151
+ # other sections (entrypoints/, reference/, …) need a ../ prefix,
152
+ # otherwise the link dangles and fails structure validation.
153
+ case "$cid" in
154
+ systems/*) link="${cid#systems/}";;
155
+ *) link="../$cid";;
156
+ esac
139
157
  if [[ "$bl" -ge "$MIN_STUB_LINES" ]]; then
140
- echo "| \`${cid%.md}\` | [page](${cid#systems/}) | Full | ${fanin} |"
158
+ echo "| \`${cid%.md}\` | [page](${link}) | Full | ${fanin} |"
141
159
  else
142
160
  echo "| \`${cid%.md}\` | ${cid} | **STUB (${bl} lines)** | ${fanin} |"
143
161
  fi