@drafthq/draft 3.1.5 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/cli/src/hosts/claude-code.js +4 -1
- package/cli/src/installer.js +20 -5
- package/cli/src/lib/marker.js +93 -0
- package/core/shared/condensation.md +3 -2
- package/core/shared/git-report-metadata.md +3 -2
- package/core/shared/graph-query.md +140 -8
- package/core/shared/tool-resolver.md +71 -4
- package/core/templates/plan.md +3 -2
- package/integrations/agents/AGENTS.md +481 -106
- package/integrations/copilot/.github/copilot-instructions.md +481 -106
- package/package.json +2 -1
- package/scripts/lib.sh +11 -0
- package/scripts/tools/_graph_queries.sh +102 -0
- package/scripts/tools/cycle-detect.sh +18 -15
- package/scripts/tools/graph-callers.sh +71 -20
- package/scripts/tools/graph-deps.sh +76 -0
- package/scripts/tools/graph-errors.sh +97 -0
- package/scripts/tools/graph-hierarchy.sh +89 -0
- package/scripts/tools/graph-query.sh +124 -0
- package/scripts/tools/graph-risk.sh +81 -0
- package/scripts/tools/graph-search.sh +84 -0
- package/scripts/tools/graph-snapshot.sh +13 -1
- package/scripts/tools/graph-snippet.sh +92 -0
- package/scripts/tools/graph-tests.sh +112 -0
- package/scripts/tools/graph-traces.sh +83 -0
- package/scripts/tools/hotspot-rank.sh +43 -16
- package/scripts/tools/mermaid-from-graph.sh +31 -10
- package/scripts/tools/resolve-tools.sh +78 -0
- package/skills/adr/SKILL.md +3 -2
- package/skills/bughunt/SKILL.md +10 -1
- package/skills/coverage/SKILL.md +8 -3
- package/skills/debug/SKILL.md +16 -5
- package/skills/decompose/SKILL.md +29 -12
- package/skills/deep-review/SKILL.md +19 -6
- package/skills/deploy-checklist/SKILL.md +6 -5
- package/skills/graph/SKILL.md +15 -6
- package/skills/impact/SKILL.md +12 -1
- package/skills/implement/SKILL.md +20 -4
- package/skills/init/SKILL.md +17 -10
- package/skills/init/references/architecture-spec.md +17 -6
- package/skills/learn/SKILL.md +15 -4
- package/skills/quick-review/SKILL.md +13 -3
- package/skills/review/SKILL.md +32 -8
- package/skills/standup/SKILL.md +3 -2
- package/skills/status/SKILL.md +3 -2
- package/skills/tech-debt/SKILL.md +20 -6
- package/skills/upload/SKILL.md +3 -2
|
@@ -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"}'
|
|
@@ -89,6 +89,16 @@ STATUS_JSON="$(memory_cli index_status "{\"project\":\"$PROJECT\"}" || echo '{}'
|
|
|
89
89
|
NODES="$(echo "$STATUS_JSON" | jq -r '.nodes // .node_count // .total_nodes // 0' 2>/dev/null || echo 0)"
|
|
90
90
|
EDGES="$(echo "$STATUS_JSON" | jq -r '.edges // .edge_count // .total_edges // 0' 2>/dev/null || echo 0)"
|
|
91
91
|
VER="$("$MEMORY_BIN" --version 2>/dev/null | awk '{print $NF}' || echo unknown)"
|
|
92
|
+
|
|
93
|
+
# Incremental-refresh provenance (graph-tooling-v2 Phase 5): the engine indexes
|
|
94
|
+
# incrementally (content-based, git-aware), so re-indexing only touches changed
|
|
95
|
+
# files. detect_changes reports that working-tree delta — recorded as provenance
|
|
96
|
+
# and echoed so a refresh shows what moved. Best-effort: never aborts the write.
|
|
97
|
+
CHANGES_JSON="$(memory_cli detect_changes "{\"project\":\"$PROJECT\"}" 2>/dev/null || echo '{}')"
|
|
98
|
+
echo "$CHANGES_JSON" | jq -e . >/dev/null 2>&1 || CHANGES_JSON='{}'
|
|
99
|
+
CHANGED_FILES="$(echo "$CHANGES_JSON" | jq -r '.changed_count // (.changed_files | length?) // 0' 2>/dev/null || echo 0)"
|
|
100
|
+
IMPACTED="$(echo "$CHANGES_JSON" | jq -r '(.impacted_symbols | length?) // 0' 2>/dev/null || echo 0)"
|
|
101
|
+
|
|
92
102
|
cat > "$OUT/schema.yaml" <<EOF
|
|
93
103
|
# Draft graph gate marker — written by scripts/tools/graph-snapshot.sh
|
|
94
104
|
# Draft is engine-only: this file carries NO graph data. Its presence signals that
|
|
@@ -101,8 +111,10 @@ project: "$PROJECT"
|
|
|
101
111
|
generated_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
102
112
|
indexed_nodes: $NODES
|
|
103
113
|
indexed_edges: $EDGES
|
|
114
|
+
changed_files: $CHANGED_FILES
|
|
115
|
+
impacted_symbols: $IMPACTED
|
|
104
116
|
access: engine-live
|
|
105
117
|
EOF
|
|
106
118
|
|
|
107
|
-
echo "Indexed $PROJECT and wrote gate marker to $OUT/schema.yaml (nodes=$NODES edges=$EDGES)"
|
|
119
|
+
echo "Indexed $PROJECT and wrote gate marker to $OUT/schema.yaml (nodes=$NODES edges=$EDGES, changed_files=$CHANGED_FILES impacted_symbols=$IMPACTED)"
|
|
108
120
|
exit 0
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# graph-snippet.sh — verified source + caller/callee counts for a symbol.
|
|
3
|
+
#
|
|
4
|
+
# Wraps the engine's get_code_snippet (graph-tooling-v2 Phase 3). Replaces the
|
|
5
|
+
# grep-then-Read dance with the engine's own attributed source plus its
|
|
6
|
+
# pre-computed caller/callee counts and loop-depth risk signal.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# scripts/tools/graph-snippet.sh --repo DIR --qualified NAME
|
|
10
|
+
#
|
|
11
|
+
# NAME is a fully qualified_name (e.g. pkg.module.Class.method). Use
|
|
12
|
+
# graph-search.sh or `graph-arch.sh | jq` to discover qualified names.
|
|
13
|
+
#
|
|
14
|
+
# Output: JSON {qualified_name, file, start_line, end_line, callers, callees,
|
|
15
|
+
# transitive_loop_depth, complexity, code, status, source}.
|
|
16
|
+
# status = "ok" | "no-match" | "unavailable"
|
|
17
|
+
#
|
|
18
|
+
# Exit codes: 0 OK, 1 invocation error, 2 graph engine unavailable.
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
# shellcheck source=_graph_queries.sh
|
|
22
|
+
source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
|
|
23
|
+
|
|
24
|
+
REPO="."
|
|
25
|
+
QNAME=""
|
|
26
|
+
|
|
27
|
+
usage() {
|
|
28
|
+
cat <<'EOF'
|
|
29
|
+
graph-snippet.sh — verified source + caller/callee counts for a symbol.
|
|
30
|
+
|
|
31
|
+
Usage:
|
|
32
|
+
scripts/tools/graph-snippet.sh --repo DIR --qualified NAME
|
|
33
|
+
|
|
34
|
+
Flags:
|
|
35
|
+
--repo DIR Repository root (default: cwd).
|
|
36
|
+
--qualified NAME Fully qualified symbol name (required).
|
|
37
|
+
--help Show this help.
|
|
38
|
+
|
|
39
|
+
Output: JSON {qualified_name, file, start_line, end_line, callers, callees,
|
|
40
|
+
transitive_loop_depth, complexity, code, status, source}. Exit 2 when engine
|
|
41
|
+
unavailable.
|
|
42
|
+
EOF
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
while [[ $# -gt 0 ]]; do
|
|
46
|
+
case "$1" in
|
|
47
|
+
--repo) REPO="$2"; shift 2;;
|
|
48
|
+
--qualified|--symbol) QNAME="$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
|
+
[[ -n "$QNAME" ]] || { echo "ERROR: --qualified is required" >&2; usage >&2; exit 1; }
|
|
56
|
+
|
|
57
|
+
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
58
|
+
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
59
|
+
|
|
60
|
+
unavailable() {
|
|
61
|
+
jq -n --arg q "$QNAME" '{qualified_name:$q, status:"unavailable", source:"unavailable"}' 2>/dev/null \
|
|
62
|
+
|| echo '{"status":"unavailable","source":"unavailable"}'
|
|
63
|
+
exit 2
|
|
64
|
+
}
|
|
65
|
+
|
|
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
|
|
71
|
+
|
|
72
|
+
ARGS="$(jq -n --arg p "$PROJECT" --arg q "$QNAME" '{project:$p, qualified_name:$q}')"
|
|
73
|
+
RES="$(memory_cli get_code_snippet "$ARGS" 2>/dev/null || true)"
|
|
74
|
+
[[ -n "$RES" ]] || unavailable
|
|
75
|
+
echo "$RES" | jq -e . >/dev/null 2>&1 || unavailable
|
|
76
|
+
|
|
77
|
+
# The engine field `source` carries the code text; rename to `code` to avoid
|
|
78
|
+
# colliding with our provenance `source`. no-match when the engine returns an
|
|
79
|
+
# object without a qualified_name/source for the requested symbol.
|
|
80
|
+
echo "$RES" | jq --arg q "$QNAME" '
|
|
81
|
+
(.qualified_name // .name // null) as $found
|
|
82
|
+
| {qualified_name: ($found // $q),
|
|
83
|
+
file: (.file_path // ""),
|
|
84
|
+
start_line: (.start_line // null),
|
|
85
|
+
end_line: (.end_line // null),
|
|
86
|
+
callers: (.callers // null),
|
|
87
|
+
callees: (.callees // null),
|
|
88
|
+
transitive_loop_depth: (.transitive_loop_depth // null),
|
|
89
|
+
complexity: (.complexity // null),
|
|
90
|
+
code: (.source // ""),
|
|
91
|
+
status: (if ($found != null or (.source // "") != "") then "ok" else "no-match" end),
|
|
92
|
+
source: "memory-graph"}'
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# graph-tests.sh — test→symbol coverage from the knowledge graph (TESTS edges).
|
|
3
|
+
#
|
|
4
|
+
# graph-tooling-v2 Phase 3. Two modes:
|
|
5
|
+
# --symbol NAME tests that cover a symbol (who tests this?).
|
|
6
|
+
# --untested exported symbols with NO TESTS edge (what's untested?).
|
|
7
|
+
#
|
|
8
|
+
# The engine dialect has no anti-join (NOT EXISTS / NOT(pattern) are rejected), so
|
|
9
|
+
# --untested is computed as a set difference in jq: exported symbols minus the
|
|
10
|
+
# set of TESTS targets. Honest about partiality — TESTS coverage depends on the
|
|
11
|
+
# engine resolving test→symbol links, which varies by language/framework.
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# scripts/tools/graph-tests.sh --repo DIR --symbol NAME
|
|
15
|
+
# scripts/tools/graph-tests.sh --repo DIR --untested
|
|
16
|
+
#
|
|
17
|
+
# Output (--symbol): {symbol, tests:[{test,file}], status, source}
|
|
18
|
+
# Output (--untested): {untested:[{symbol,file}], total, truncated, source}
|
|
19
|
+
#
|
|
20
|
+
# Exit codes: 0 OK, 1 invocation error, 2 graph engine unavailable.
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
# shellcheck source=_graph_queries.sh
|
|
24
|
+
source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
|
|
25
|
+
|
|
26
|
+
REPO="."
|
|
27
|
+
SYMBOL=""
|
|
28
|
+
UNTESTED=0
|
|
29
|
+
|
|
30
|
+
usage() {
|
|
31
|
+
cat <<'EOF'
|
|
32
|
+
graph-tests.sh — test coverage edges from the knowledge graph.
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
scripts/tools/graph-tests.sh --repo DIR --symbol NAME
|
|
36
|
+
scripts/tools/graph-tests.sh --repo DIR --untested
|
|
37
|
+
|
|
38
|
+
Flags:
|
|
39
|
+
--repo DIR Repository root (default: cwd).
|
|
40
|
+
--symbol NAME List tests covering this symbol.
|
|
41
|
+
--untested List exported symbols with no TESTS edge.
|
|
42
|
+
--help Show this help.
|
|
43
|
+
|
|
44
|
+
Output: --symbol → {symbol, tests, status, source}; --untested →
|
|
45
|
+
{untested, total, truncated, source}. Exit 2 when engine unavailable.
|
|
46
|
+
EOF
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
while [[ $# -gt 0 ]]; do
|
|
50
|
+
case "$1" in
|
|
51
|
+
--repo) REPO="$2"; shift 2;;
|
|
52
|
+
--symbol) SYMBOL="$2"; shift 2;;
|
|
53
|
+
--untested) UNTESTED=1; shift;;
|
|
54
|
+
--help|-h) usage; exit 0;;
|
|
55
|
+
*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
56
|
+
esac
|
|
57
|
+
done
|
|
58
|
+
|
|
59
|
+
[[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
|
|
60
|
+
[[ -n "$SYMBOL" || "$UNTESTED" -eq 1 ]] || { echo "ERROR: provide --symbol or --untested" >&2; usage >&2; exit 1; }
|
|
61
|
+
|
|
62
|
+
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
63
|
+
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
64
|
+
|
|
65
|
+
unavailable() {
|
|
66
|
+
if [[ "$UNTESTED" -eq 1 ]]; then
|
|
67
|
+
echo '{"untested":[],"total":0,"source":"unavailable"}'
|
|
68
|
+
else
|
|
69
|
+
jq -n --arg s "$SYMBOL" '{symbol:$s, tests:[], status:"unavailable", source:"unavailable"}' 2>/dev/null \
|
|
70
|
+
|| echo '{"tests":[],"status":"unavailable","source":"unavailable"}'
|
|
71
|
+
fi
|
|
72
|
+
exit 2
|
|
73
|
+
}
|
|
74
|
+
|
|
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
|
|
80
|
+
|
|
81
|
+
if [[ "$UNTESTED" -eq 1 ]]; then
|
|
82
|
+
EXP="$(gq_run "$PROJECT" "$(gq_q_exported)" || true)"
|
|
83
|
+
[[ -n "$EXP" ]] || unavailable
|
|
84
|
+
TST="$(gq_run "$PROJECT" "$(gq_q_tested_all)" || echo '{"rows":[]}')"
|
|
85
|
+
echo "$TST" | jq -e . >/dev/null 2>&1 || TST='{"rows":[]}'
|
|
86
|
+
# Pass results via temp files, not argv: the exported set can exceed the
|
|
87
|
+
# ARG_MAX limit for --argjson on large repos.
|
|
88
|
+
TMP_EXP="$(mktemp)"; TMP_TST="$(mktemp)"
|
|
89
|
+
trap 'rm -f "$TMP_EXP" "$TMP_TST"' EXIT
|
|
90
|
+
printf '%s' "$EXP" > "$TMP_EXP"
|
|
91
|
+
printf '%s' "$TST" > "$TMP_TST"
|
|
92
|
+
jq -n --slurpfile exp "$TMP_EXP" --slurpfile tst "$TMP_TST" '
|
|
93
|
+
($exp[0] // {}) as $e | ($tst[0] // {}) as $t
|
|
94
|
+
| ((($t.rows) // []) | map(.[0])) as $tested
|
|
95
|
+
| [ (($e.rows) // [])[]
|
|
96
|
+
| select(((.[0]) as $q | $tested | index($q)) | not)
|
|
97
|
+
| {symbol:.[0], file:.[1]} ] as $u
|
|
98
|
+
| {untested: $u, total: ($u | length),
|
|
99
|
+
truncated: (((($e.rows) // []) | length) >= 2000),
|
|
100
|
+
source:"memory-graph"}'
|
|
101
|
+
exit 0
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
SYM_ESC="$(gq_escape "$SYMBOL")"
|
|
105
|
+
RES="$(gq_run "$PROJECT" "$(gq_q_tests "$SYM_ESC")" || true)"
|
|
106
|
+
[[ -n "$RES" ]] || unavailable
|
|
107
|
+
STATUS="$(gq_symbol_status "$PROJECT" "$SYM_ESC" "$RES")"
|
|
108
|
+
|
|
109
|
+
echo "$RES" | jq --arg s "$SYMBOL" --arg st "$STATUS" '
|
|
110
|
+
{symbol:$s,
|
|
111
|
+
tests: [ (.rows // [])[] | {test:.[0], file:.[1]} ],
|
|
112
|
+
status:$st, source:"memory-graph"}'
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# graph-traces.sh — fold runtime call traces into the knowledge graph (EXPERIMENTAL).
|
|
3
|
+
#
|
|
4
|
+
# graph-tooling-v2 Phase 6. Wraps the engine's ingest_traces to close the
|
|
5
|
+
# static/dynamic gap — dynamic dispatch the static graph misses (e.g. closures,
|
|
6
|
+
# reflection, virtual calls). This is a WRITE path and is gated behind
|
|
7
|
+
# --experimental. NOTE: in engine v0.8.x ingest_traces is accepted but runtime
|
|
8
|
+
# edge creation is "not yet implemented" — the engine returns its status verbatim.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# scripts/tools/graph-traces.sh ingest --repo DIR --file TRACES.json --experimental
|
|
12
|
+
#
|
|
13
|
+
# TRACES.json is a JSON array of trace records (engine-defined shape).
|
|
14
|
+
#
|
|
15
|
+
# Output: the engine's raw ingest_traces JSON; {"source":"unavailable"} (exit 2)
|
|
16
|
+
# when the engine is unavailable; exit 1 on invocation error or missing
|
|
17
|
+
# --experimental.
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# shellcheck source=_graph_queries.sh
|
|
21
|
+
source "$(dirname "${BASH_SOURCE[0]}")/_graph_queries.sh"
|
|
22
|
+
|
|
23
|
+
REPO="."
|
|
24
|
+
FILE=""
|
|
25
|
+
ACTION=""
|
|
26
|
+
EXPERIMENTAL=0
|
|
27
|
+
|
|
28
|
+
usage() {
|
|
29
|
+
cat <<'EOF'
|
|
30
|
+
graph-traces.sh — fold runtime traces into the graph (EXPERIMENTAL, write path).
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
scripts/tools/graph-traces.sh ingest --repo DIR --file TRACES.json --experimental
|
|
34
|
+
|
|
35
|
+
Flags:
|
|
36
|
+
ingest The action (currently the only one).
|
|
37
|
+
--repo DIR Repository root (default: cwd).
|
|
38
|
+
--file PATH JSON array of trace records (required for ingest).
|
|
39
|
+
--experimental Required acknowledgement — this is a write/experimental path.
|
|
40
|
+
--help Show this help.
|
|
41
|
+
|
|
42
|
+
NOTE: engine v0.8.x accepts traces but runtime edge creation is not yet
|
|
43
|
+
implemented; the engine's status is returned verbatim.
|
|
44
|
+
|
|
45
|
+
Output: raw engine JSON; {"source":"unavailable"} (exit 2) when unavailable.
|
|
46
|
+
EOF
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
while [[ $# -gt 0 ]]; do
|
|
50
|
+
case "$1" in
|
|
51
|
+
ingest) ACTION="ingest"; shift;;
|
|
52
|
+
--repo) REPO="$2"; shift 2;;
|
|
53
|
+
--file) FILE="$2"; shift 2;;
|
|
54
|
+
--experimental) EXPERIMENTAL=1; shift;;
|
|
55
|
+
--help|-h) usage; exit 0;;
|
|
56
|
+
*) echo "Unknown argument: $1" >&2; usage >&2; exit 1;;
|
|
57
|
+
esac
|
|
58
|
+
done
|
|
59
|
+
|
|
60
|
+
[[ "$ACTION" == "ingest" ]] || { echo "ERROR: action 'ingest' is required" >&2; usage >&2; exit 1; }
|
|
61
|
+
[[ "$EXPERIMENTAL" -eq 1 ]] || { echo "ERROR: --experimental is required (this writes to the graph)" >&2; exit 1; }
|
|
62
|
+
[[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
|
|
63
|
+
[[ -n "$FILE" ]] || { echo "ERROR: --file is required" >&2; usage >&2; exit 1; }
|
|
64
|
+
[[ -f "$FILE" ]] || { echo "ERROR: --file '$FILE' does not exist" >&2; exit 1; }
|
|
65
|
+
|
|
66
|
+
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
67
|
+
SELF_REPO="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
68
|
+
|
|
69
|
+
unavailable() { echo '{"source":"unavailable"}'; exit 2; }
|
|
70
|
+
|
|
71
|
+
find_memory_bin "$REPO_ABS" "$SELF_REPO" || unavailable
|
|
72
|
+
command -v jq >/dev/null 2>&1 || unavailable
|
|
73
|
+
|
|
74
|
+
jq -e . "$FILE" >/dev/null 2>&1 || { echo "ERROR: --file is not valid JSON" >&2; exit 1; }
|
|
75
|
+
|
|
76
|
+
PROJECT="$(memory_ensure_index "$REPO_ABS" || true)"
|
|
77
|
+
[[ -n "$PROJECT" ]] || unavailable
|
|
78
|
+
|
|
79
|
+
ARGS="$(jq -n --arg p "$PROJECT" --slurpfile t "$FILE" '{project:$p, traces:($t[0])}')"
|
|
80
|
+
RES="$(memory_cli ingest_traces "$ARGS" 2>/dev/null || true)"
|
|
81
|
+
[[ -n "$RES" ]] || unavailable
|
|
82
|
+
echo "$RES" | jq -e . >/dev/null 2>&1 || unavailable
|
|
83
|
+
printf '%s\n' "$RES"
|