@drafthq/draft 3.0.0 → 3.2.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 (62) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +3 -3
  4. package/bin/README.md +4 -7
  5. package/cli/src/hosts/claude-code.js +4 -1
  6. package/cli/src/installer.js +12 -5
  7. package/core/shared/condensation.md +8 -8
  8. package/core/shared/draft-context-loading.md +5 -9
  9. package/core/shared/graph-query.md +170 -33
  10. package/core/shared/graph-usage-report.md +1 -1
  11. package/core/shared/pattern-learning.md +2 -2
  12. package/core/shared/red-flags.md +3 -3
  13. package/core/templates/ai-context.md +1 -1
  14. package/core/templates/architecture.md +3 -3
  15. package/integrations/agents/AGENTS.md +299 -145
  16. package/integrations/copilot/.github/copilot-instructions.md +299 -145
  17. package/package.json +2 -1
  18. package/scripts/lib.sh +11 -3
  19. package/scripts/tools/_graph_queries.sh +102 -0
  20. package/scripts/tools/adr-index.sh +2 -2
  21. package/scripts/tools/check-scope-conflicts.sh +2 -2
  22. package/scripts/tools/check-skill-line-caps.sh +2 -2
  23. package/scripts/tools/cycle-detect.sh +22 -15
  24. package/scripts/tools/diff-templates-vs-tracks.sh +2 -2
  25. package/scripts/tools/fix-whitespace.sh +15 -9
  26. package/scripts/tools/graph-arch.sh +72 -0
  27. package/scripts/tools/graph-callers.sh +71 -20
  28. package/scripts/tools/graph-deps.sh +76 -0
  29. package/scripts/tools/graph-errors.sh +97 -0
  30. package/scripts/tools/graph-hierarchy.sh +89 -0
  31. package/scripts/tools/graph-impact.sh +1 -0
  32. package/scripts/tools/graph-init.sh +3 -3
  33. package/scripts/tools/graph-query.sh +124 -0
  34. package/scripts/tools/graph-risk.sh +81 -0
  35. package/scripts/tools/graph-search.sh +84 -0
  36. package/scripts/tools/graph-snapshot.sh +63 -50
  37. package/scripts/tools/graph-snippet.sh +92 -0
  38. package/scripts/tools/graph-tests.sh +112 -0
  39. package/scripts/tools/graph-traces.sh +83 -0
  40. package/scripts/tools/hotspot-rank.sh +44 -15
  41. package/scripts/tools/manage-symlinks.sh +1 -1
  42. package/scripts/tools/mermaid-from-graph.sh +31 -10
  43. package/scripts/tools/parse-reports.sh +1 -1
  44. package/scripts/tools/verify-doc-anchors.sh +2 -2
  45. package/scripts/tools/verify-graph-binary.sh +1 -1
  46. package/skills/GRAPH.md +2 -2
  47. package/skills/bughunt/SKILL.md +1 -1
  48. package/skills/debug/SKILL.md +3 -3
  49. package/skills/decompose/SKILL.md +5 -5
  50. package/skills/deep-review/SKILL.md +2 -2
  51. package/skills/deploy-checklist/SKILL.md +2 -2
  52. package/skills/graph/SKILL.md +1 -1
  53. package/skills/implement/SKILL.md +1 -1
  54. package/skills/init/SKILL.md +62 -42
  55. package/skills/init/references/architecture-spec.md +12 -11
  56. package/skills/learn/SKILL.md +5 -5
  57. package/skills/quick-review/SKILL.md +3 -3
  58. package/skills/review/SKILL.md +7 -7
  59. package/skills/tech-debt/SKILL.md +2 -2
  60. package/scripts/tools/okf-bundle.sh +0 -141
  61. package/scripts/tools/okf-check.sh +0 -137
  62. package/scripts/tools/okf-emit.sh +0 -161
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bash
2
+ # graph-errors.sh — error-propagation paths from the knowledge graph (RAISES/THROWS).
3
+ #
4
+ # graph-tooling-v2 Phase 3. Two modes:
5
+ # --symbol NAME what error types NAME raises/throws.
6
+ # --type NAME who raises/throws error type NAME (fail-closed audits: confirm
7
+ # every site that can emit a given error).
8
+ #
9
+ # Usage:
10
+ # scripts/tools/graph-errors.sh --repo DIR (--symbol NAME | --type NAME)
11
+ #
12
+ # Output (--symbol): {symbol, raises:[{error,qualified}], status, source}
13
+ # Output (--type): {type, raisers:[{symbol,file}], status, source}
14
+ #
15
+ # Exit codes: 0 OK, 1 invocation error, 2 graph engine unavailable.
16
+ set -euo pipefail
17
+
18
+ # shellcheck source=_graph_queries.sh
19
+ source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
20
+
21
+ REPO="."
22
+ SYMBOL=""
23
+ TYPE=""
24
+
25
+ usage() {
26
+ cat <<'EOF'
27
+ graph-errors.sh — error propagation (RAISES/THROWS edges).
28
+
29
+ Usage:
30
+ scripts/tools/graph-errors.sh --repo DIR (--symbol NAME | --type NAME)
31
+
32
+ Flags:
33
+ --repo DIR Repository root (default: cwd).
34
+ --symbol NAME Error types raised/thrown by NAME.
35
+ --type NAME Symbols that raise/throw error type NAME.
36
+ --help Show this help.
37
+
38
+ Output: --symbol → {symbol, raises, status, source}; --type → {type, raisers,
39
+ status, source}. Exit 2 when engine unavailable.
40
+ EOF
41
+ }
42
+
43
+ while [[ $# -gt 0 ]]; do
44
+ case "$1" in
45
+ --repo) REPO="$2"; shift 2;;
46
+ --symbol) SYMBOL="$2"; shift 2;;
47
+ --type) TYPE="$2"; shift 2;;
48
+ --help|-h) usage; exit 0;;
49
+ *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
50
+ esac
51
+ done
52
+
53
+ [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
54
+ if [[ -n "$SYMBOL" && -n "$TYPE" ]]; then
55
+ echo "ERROR: use either --symbol or --type, not both" >&2; exit 1
56
+ fi
57
+ [[ -n "$SYMBOL" || -n "$TYPE" ]] || { echo "ERROR: provide --symbol or --type" >&2; usage >&2; exit 1; }
58
+
59
+ REPO_ABS="$(cd "$REPO" && pwd)"
60
+ SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
61
+
62
+ unavailable() {
63
+ if [[ -n "$TYPE" ]]; then
64
+ jq -n --arg t "$TYPE" '{type:$t, raisers:[], status:"unavailable", source:"unavailable"}' 2>/dev/null \
65
+ || echo '{"raisers":[],"status":"unavailable","source":"unavailable"}'
66
+ else
67
+ jq -n --arg s "$SYMBOL" '{symbol:$s, raises:[], status:"unavailable", source:"unavailable"}' 2>/dev/null \
68
+ || echo '{"raises":[],"status":"unavailable","source":"unavailable"}'
69
+ fi
70
+ exit 2
71
+ }
72
+
73
+ find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
74
+ command -v jq >/dev/null 2>&1 || unavailable
75
+
76
+ PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
77
+ [[ -n "$PROJECT" ]] || unavailable
78
+
79
+ if [[ -n "$TYPE" ]]; then
80
+ SYM_ESC="$(gq_escape "$TYPE")"
81
+ RES="$(gq_run "$PROJECT" "$(gq_q_raisers "$SYM_ESC")" || true)"
82
+ [[ -n "$RES" ]] || unavailable
83
+ STATUS="$(gq_symbol_status "$PROJECT" "$SYM_ESC" "$RES")"
84
+ echo "$RES" | jq --arg t "$TYPE" --arg st "$STATUS" '
85
+ {type:$t,
86
+ raisers: [ (.rows // [])[] | {symbol:.[0], file:.[1]} ],
87
+ status:$st, source:"memory-graph"}'
88
+ else
89
+ SYM_ESC="$(gq_escape "$SYMBOL")"
90
+ RES="$(gq_run "$PROJECT" "$(gq_q_raises "$SYM_ESC")" || true)"
91
+ [[ -n "$RES" ]] || unavailable
92
+ STATUS="$(gq_symbol_status "$PROJECT" "$SYM_ESC" "$RES")"
93
+ echo "$RES" | jq --arg s "$SYMBOL" --arg st "$STATUS" '
94
+ {symbol:$s,
95
+ raises: [ (.rows // [])[] | {error:.[0], qualified:.[1]} ],
96
+ status:$st, source:"memory-graph"}'
97
+ fi
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env bash
2
+ # graph-hierarchy.sh — class inheritance from the knowledge graph (INHERITS edges).
3
+ #
4
+ # graph-tooling-v2 Phase 3. Answers base/derived relationships and the impact of a
5
+ # base-class change.
6
+ # (default) all INHERITS edges (child → parent).
7
+ # --symbol NAME bases of class NAME (what NAME inherits from).
8
+ # --derived NAME subclasses of class NAME (who inherits from NAME — the blast
9
+ # radius of changing NAME).
10
+ #
11
+ # Usage:
12
+ # scripts/tools/graph-hierarchy.sh --repo DIR [--symbol NAME | --derived NAME]
13
+ #
14
+ # Output: JSON {edges:[{child,parent}], status, source}.
15
+ #
16
+ # Exit codes: 0 OK, 1 invocation error, 2 graph engine unavailable.
17
+ set -euo pipefail
18
+
19
+ # shellcheck source=_graph_queries.sh
20
+ source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
21
+
22
+ REPO="."
23
+ SYMBOL=""
24
+ DERIVED=""
25
+
26
+ usage() {
27
+ cat <<'EOF'
28
+ graph-hierarchy.sh — class inheritance (INHERITS edges).
29
+
30
+ Usage:
31
+ scripts/tools/graph-hierarchy.sh --repo DIR [--symbol NAME | --derived NAME]
32
+
33
+ Flags:
34
+ --repo DIR Repository root (default: cwd).
35
+ --symbol NAME Bases of class NAME (what it inherits from).
36
+ --derived NAME Subclasses of class NAME (impact of changing NAME).
37
+ --help Show this help.
38
+
39
+ Output: JSON {edges:[{child,parent}], status, source}. With no class filter,
40
+ emits the full hierarchy. Exit 2 when engine unavailable.
41
+ EOF
42
+ }
43
+
44
+ while [[ $# -gt 0 ]]; do
45
+ case "$1" in
46
+ --repo) REPO="$2"; shift 2;;
47
+ --symbol) SYMBOL="$2"; shift 2;;
48
+ --derived) DERIVED="$2"; shift 2;;
49
+ --help|-h) usage; exit 0;;
50
+ *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
51
+ esac
52
+ done
53
+
54
+ [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
55
+ if [[ -n "$SYMBOL" && -n "$DERIVED" ]]; then
56
+ echo "ERROR: use either --symbol or --derived, not both" >&2; exit 1
57
+ fi
58
+
59
+ REPO_ABS="$(cd "$REPO" && pwd)"
60
+ SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
61
+
62
+ unavailable() { echo '{"edges":[],"status":"unavailable","source":"unavailable"}'; exit 2; }
63
+
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
69
+
70
+ if [[ -n "$SYMBOL" ]]; then
71
+ SYM_ESC="$(gq_escape "$SYMBOL")"; Q="$(gq_q_inherits_sym "$SYM_ESC")"; PROBE="$SYM_ESC"
72
+ elif [[ -n "$DERIVED" ]]; then
73
+ SYM_ESC="$(gq_escape "$DERIVED")"; Q="$(gq_q_derived_sym "$SYM_ESC")"; PROBE="$SYM_ESC"
74
+ else
75
+ Q="$(gq_q_inherits)"; PROBE=""
76
+ fi
77
+
78
+ RES="$(gq_run "$PROJECT" "$Q" || true)"
79
+ [[ -n "$RES" ]] || unavailable
80
+
81
+ if [[ -n "$PROBE" ]]; then
82
+ STATUS="$(gq_symbol_status "$PROJECT" "$PROBE" "$RES")"
83
+ else
84
+ STATUS="ok"
85
+ fi
86
+
87
+ echo "$RES" | jq --arg st "$STATUS" '
88
+ {edges: [ (.rows // [])[] | {child:.[0], parent:.[1]} ],
89
+ status:$st, source:"memory-graph"}'
@@ -54,6 +54,7 @@ done
54
54
 
55
55
  [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
56
56
  [[ -n "$FILE" || -n "$SYMBOL" ]] || { echo "ERROR: provide --file or --symbol" >&2; usage >&2; exit 1; }
57
+ [[ "$DEPTH" =~ ^[0-9]+$ ]] || { echo "ERROR: --depth must be a non-negative integer" >&2; exit 1; }
57
58
 
58
59
  REPO_ABS="$(cd "$REPO" && pwd)"
59
60
  SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
@@ -104,8 +104,8 @@ engine_unavailable() {
104
104
  exit 2
105
105
  }
106
106
 
107
- # Build a committed snapshot for a repo dir; returns graph-snapshot's exit code.
108
- # Snapshot progress goes to stderr so stdout stays clean for --json consumers.
107
+ # Index a repo dir and write its schema.yaml gate marker; returns graph-snapshot's exit code.
108
+ # Progress goes to stderr so stdout stays clean for --json consumers.
109
109
  build_snapshot() {
110
110
  local rc=0
111
111
  "$TOOLS_DIR/graph-snapshot.sh" --repo "$1" 1>&2 || rc=$?
@@ -134,7 +134,7 @@ write_root_link() {
134
134
  [[ -n "$root_project" ]] || root_project="unknown"
135
135
  fi
136
136
  root_commit="$(git -C "$ROOT_ABS" rev-parse --verify --quiet HEAD 2>/dev/null || echo none)"
137
- ts="$(date -Iseconds 2>/dev/null || date)"
137
+ ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
138
138
  cat > "$mod_graph/root-link.json" <<EOF
139
139
  {
140
140
  "root_graph": "$rel",
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env bash
2
+ # graph-query.sh — generic read-only escape hatch to the knowledge-graph engine.
3
+ #
4
+ # The highest-leverage single addition (graph-tooling-v2 Phase 2): unlocks all 20
5
+ # edge types and ~30 pre-computed node properties with no new wrapper. Two modes:
6
+ #
7
+ # --cypher 'MATCH ...' run arbitrary read-only openCypher (write verbs
8
+ # are rejected before the engine ever sees them).
9
+ # --tool NAME --json '{...}' passthrough to any read-only engine tool
10
+ # (get_code_snippet, search_graph, get_graph_schema,
11
+ # trace_path, …). Destructive tools are rejected.
12
+ #
13
+ # Dialect limits (engine v0.8.x — see _graph_queries.sh for the full list):
14
+ # SAFE : fixed-length patterns, `=`, `<`, `STARTS WITH`, `NOT x STARTS WITH`,
15
+ # `AND`, `OR`, rel-type alternation `[:A|B]`, `count(x)`.
16
+ # UNSAFE : coalesce(), `<>`/`!=`/`<=`/`>=`, NOT EXISTS(...), NOT (pattern),
17
+ # WITH-grouping aggregation, multi-pattern joins.
18
+ # Passthrough returns the engine's raw error, not a silent empty result.
19
+ #
20
+ # Usage:
21
+ # scripts/tools/graph-query.sh --repo DIR --cypher 'MATCH (n) RETURN n LIMIT 5'
22
+ # scripts/tools/graph-query.sh --repo DIR --tool get_graph_schema --json '{}'
23
+ #
24
+ # Output: the raw engine JSON on success; {"source":"unavailable"} (exit 2) when
25
+ # the engine is unavailable. Exit 1 on invocation error / rejected write.
26
+ set -euo pipefail
27
+
28
+ # shellcheck source=_graph_queries.sh
29
+ source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
30
+
31
+ REPO="."
32
+ CYPHER=""
33
+ TOOL=""
34
+ TOOL_JSON="{}"
35
+
36
+ usage() {
37
+ cat <<'EOF'
38
+ graph-query.sh — generic read-only passthrough to the knowledge-graph engine.
39
+
40
+ Usage:
41
+ scripts/tools/graph-query.sh --repo DIR --cypher 'MATCH ... RETURN ...'
42
+ scripts/tools/graph-query.sh --repo DIR --tool NAME [--json '{...}']
43
+
44
+ Flags:
45
+ --repo DIR Repository root (default: cwd).
46
+ --cypher STR Read-only openCypher query (write verbs CREATE/MERGE/DELETE/SET/
47
+ REMOVE/DROP/DETACH are rejected). The {project} is injected.
48
+ --tool NAME Engine tool to call (read-only allowlist). Combine with --json.
49
+ --json STR JSON args for --tool (the project is injected if absent).
50
+ --help Show this help.
51
+
52
+ Dialect: avoid coalesce(), <>, NOT EXISTS, NOT(pattern), WITH-aggregation,
53
+ multi-pattern joins. Use =, <, STARTS WITH, AND/OR, [:A|B] alternation.
54
+
55
+ Output: raw engine JSON on success; {"source":"unavailable"} (exit 2) when the
56
+ engine is unavailable; exit 1 on invocation error or a rejected write verb.
57
+ EOF
58
+ }
59
+
60
+ while [[ $# -gt 0 ]]; do
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;;
66
+ --help|-h) usage; exit 0;;
67
+ *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
68
+ esac
69
+ done
70
+
71
+ [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
72
+ if [[ -n "$CYPHER" && -n "$TOOL" ]]; then
73
+ echo "ERROR: use either --cypher or --tool, not both" >&2; exit 1
74
+ fi
75
+ if [[ -z "$CYPHER" && -z "$TOOL" ]]; then
76
+ echo "ERROR: provide --cypher or --tool" >&2; usage >&2; exit 1
77
+ fi
78
+
79
+ # Read-only allowlist for --tool mode. Destructive/mutating tools are not exposed
80
+ # through this generic hatch (delete_project, index_repository, manage_adr,
81
+ # ingest_traces) — use the purpose-built wrappers, which guard them explicitly.
82
+ TOOL_ALLOW=" get_architecture query_graph trace_path detect_changes get_code_snippet search_graph search_code get_graph_schema index_status list_projects "
83
+
84
+ if [[ -n "$TOOL" ]]; then
85
+ [[ "$TOOL_ALLOW" == *" $TOOL "* ]] || {
86
+ echo "ERROR: tool '$TOOL' is not in the read-only allowlist" >&2
87
+ echo "Allowed:$TOOL_ALLOW" >&2
88
+ exit 1
89
+ }
90
+ fi
91
+
92
+ # Reject write verbs in --cypher BEFORE the engine sees the query.
93
+ if [[ -n "$CYPHER" ]]; then
94
+ UPPER="$(printf '%s' "$CYPHER" | tr '[:lower:]' '[:upper:]')"
95
+ if printf '%s' "$UPPER" | grep -Eqw 'CREATE|MERGE|DELETE|SET|REMOVE|DROP|DETACH'; then
96
+ echo "ERROR: write verbs are not allowed (read-only passthrough)" >&2
97
+ exit 1
98
+ fi
99
+ fi
100
+
101
+ REPO_ABS="$(cd "$REPO" && pwd)"
102
+ SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
103
+
104
+ unavailable() { echo '{"source":"unavailable"}'; exit 2; }
105
+
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
111
+
112
+ if [[ -n "$CYPHER" ]]; then
113
+ RES="$(gq_run "$PROJECT" "$CYPHER" || true)"
114
+ [[ -n "$RES" ]] || unavailable
115
+ printf '%s\n' "$RES"
116
+ else
117
+ # Inject the resolved project into the tool args unless the caller set one.
118
+ echo "$TOOL_JSON" | jq -e . >/dev/null 2>&1 || { echo "ERROR: --json is not valid JSON" >&2; exit 1; }
119
+ ARGS="$(echo "$TOOL_JSON" | jq -c --arg p "$PROJECT" 'if has("project") then . else . + {project:$p} end')"
120
+ RES="$(memory_cli "$TOOL" "$ARGS" 2>/dev/null || true)"
121
+ [[ -n "$RES" ]] || unavailable
122
+ echo "$RES" | jq -e . >/dev/null 2>&1 || unavailable
123
+ printf '%s\n' "$RES"
124
+ fi
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ # graph-risk.sh — pre-computed risk hotspots from the knowledge graph node props.
3
+ #
4
+ # graph-tooling-v2 Phase 3. The engine already computes per-symbol risk flags
5
+ # (unguarded_recursion, recursion_in_loop, alloc_in_loop, linear_scan_in_loop)
6
+ # during indexing. This wrapper surfaces them so bughunt / deep-review consume the
7
+ # engine's findings instead of re-deriving them by hand.
8
+ #
9
+ # Usage:
10
+ # scripts/tools/graph-risk.sh --repo DIR [--min-complexity N]
11
+ #
12
+ # Output: JSON {risky:[{symbol, file, complexity, flags:[...]}], total,
13
+ # truncated, source}. --min-complexity keeps only flagged symbols
14
+ # whose complexity >= N (filtered client-side; the engine dialect
15
+ # has no >= operator).
16
+ #
17
+ # Exit codes: 0 OK, 1 invocation error, 2 graph engine unavailable.
18
+ set -euo pipefail
19
+
20
+ # shellcheck source=_graph_queries.sh
21
+ source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
22
+
23
+ REPO="."
24
+ MIN_COMPLEXITY=0
25
+
26
+ usage() {
27
+ cat <<'EOF'
28
+ graph-risk.sh — pre-computed risk hotspots (recursion / in-loop allocations).
29
+
30
+ Usage:
31
+ scripts/tools/graph-risk.sh --repo DIR [--min-complexity N]
32
+
33
+ Flags:
34
+ --repo DIR Repository root (default: cwd).
35
+ --min-complexity N Keep only flagged symbols with complexity >= N (default 0).
36
+ --help Show this help.
37
+
38
+ Output: JSON {risky:[{symbol, file, complexity, flags}], total, truncated,
39
+ source}. Exit 2 when engine unavailable.
40
+ EOF
41
+ }
42
+
43
+ while [[ $# -gt 0 ]]; do
44
+ case "$1" in
45
+ --repo) REPO="$2"; shift 2;;
46
+ --min-complexity) MIN_COMPLEXITY="$2"; shift 2;;
47
+ --help|-h) usage; exit 0;;
48
+ *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
49
+ esac
50
+ done
51
+
52
+ [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
53
+ [[ "$MIN_COMPLEXITY" =~ ^[0-9]+$ ]] || { echo "ERROR: --min-complexity must be a non-negative integer" >&2; exit 1; }
54
+
55
+ REPO_ABS="$(cd "$REPO" && pwd)"
56
+ SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
57
+
58
+ unavailable() { echo '{"risky":[],"total":0,"source":"unavailable"}'; exit 2; }
59
+
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
65
+
66
+ RES="$(gq_run "$PROJECT" "$(gq_q_risk)" || true)"
67
+ [[ -n "$RES" ]] || unavailable
68
+
69
+ # Columns: symbol, file, complexity, unguarded_recursion, alloc_in_loop,
70
+ # recursion_in_loop, linear_scan_in_loop. complexity arrives as a string.
71
+ echo "$RES" | jq --argjson minc "$MIN_COMPLEXITY" '
72
+ [ (.rows // [])[]
73
+ | {symbol:.[0], file:.[1], complexity:((.[2] // "0") | tonumber? // 0),
74
+ flags: ( [ (if (.[3]|tostring) == "true" then "unguarded_recursion" else empty end),
75
+ (if (.[4]|tostring) == "true" then "alloc_in_loop" else empty end),
76
+ (if (.[5]|tostring) == "true" then "recursion_in_loop" else empty end),
77
+ (if (.[6]|tostring) == "true" then "linear_scan_in_loop" else empty end) ] )}
78
+ | select(.complexity >= $minc) ] as $r
79
+ | {risky: $r, total: ($r | length),
80
+ truncated: (((.rows // []) | length) >= 200),
81
+ source:"memory-graph"}'
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env bash
2
+ # graph-search.sh — semantic / ranked symbol search over the knowledge graph.
3
+ #
4
+ # Wraps the engine's search_graph (graph-tooling-v2 Phase 3): "find code about X"
5
+ # returns vector/text-ranked symbols, not a literal grep. Use when the user names
6
+ # an intent or concept rather than an exact symbol.
7
+ #
8
+ # Usage:
9
+ # scripts/tools/graph-search.sh --repo DIR --query "order submission to broker" [--limit N]
10
+ #
11
+ # Output: JSON {query, results:[{name, qualified_name, label, file, start_line,
12
+ # end_line, rank}], total, source}.
13
+ #
14
+ # Exit codes: 0 OK, 1 invocation error, 2 graph engine unavailable.
15
+ set -euo pipefail
16
+
17
+ # shellcheck source=_graph_queries.sh
18
+ source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
19
+
20
+ REPO="."
21
+ QUERY=""
22
+ LIMIT=10
23
+
24
+ usage() {
25
+ cat <<'EOF'
26
+ graph-search.sh — semantic / ranked symbol search.
27
+
28
+ Usage:
29
+ scripts/tools/graph-search.sh --repo DIR --query "STR" [--limit N]
30
+
31
+ Flags:
32
+ --repo DIR Repository root (default: cwd).
33
+ --query STR Natural-language or keyword query (required).
34
+ --limit N Max results (default: 10).
35
+ --help Show this help.
36
+
37
+ Output: JSON {query, results, total, source}. Exit 2 when engine unavailable.
38
+ EOF
39
+ }
40
+
41
+ while [[ $# -gt 0 ]]; do
42
+ case "$1" in
43
+ --repo) REPO="$2"; shift 2;;
44
+ --query) QUERY="$2"; shift 2;;
45
+ --limit) LIMIT="$2"; shift 2;;
46
+ --help|-h) usage; exit 0;;
47
+ *) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
48
+ esac
49
+ done
50
+
51
+ [[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
52
+ [[ -n "$QUERY" ]] || { echo "ERROR: --query is required" >&2; usage >&2; exit 1; }
53
+ [[ "$LIMIT" =~ ^[0-9]+$ ]] || { echo "ERROR: --limit must be a non-negative integer" >&2; exit 1; }
54
+
55
+ REPO_ABS="$(cd "$REPO" && pwd)"
56
+ SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
57
+
58
+ unavailable() {
59
+ jq -n --arg q "$QUERY" '{query:$q, results:[], source:"unavailable"}' 2>/dev/null \
60
+ || echo '{"results":[],"source":"unavailable"}'
61
+ exit 2
62
+ }
63
+
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
69
+
70
+ ARGS="$(jq -n --arg p "$PROJECT" --arg q "$QUERY" --argjson n "$LIMIT" \
71
+ '{project:$p, query:$q, limit:$n}')"
72
+ RES="$(memory_cli search_graph "$ARGS" 2>/dev/null || true)"
73
+ [[ -n "$RES" ]] || unavailable
74
+ echo "$RES" | jq -e . >/dev/null 2>&1 || unavailable
75
+
76
+ echo "$RES" | jq --arg q "$QUERY" '
77
+ {query:$q,
78
+ results: [ (.results // .matches // [])[]
79
+ | {name:(.name // ""), qualified_name:(.qualified_name // ""),
80
+ label:(.label // ""), file:(.file_path // .file // ""),
81
+ start_line:(.start_line // null), end_line:(.end_line // null),
82
+ rank:(.rank // .score // null)} ],
83
+ total: ((.results // .matches // []) | length),
84
+ source:"memory-graph"}'