@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -3
- package/bin/README.md +4 -7
- package/cli/src/hosts/claude-code.js +4 -1
- package/cli/src/installer.js +12 -5
- package/core/shared/condensation.md +8 -8
- package/core/shared/draft-context-loading.md +5 -9
- package/core/shared/graph-query.md +170 -33
- package/core/shared/graph-usage-report.md +1 -1
- package/core/shared/pattern-learning.md +2 -2
- package/core/shared/red-flags.md +3 -3
- package/core/templates/ai-context.md +1 -1
- package/core/templates/architecture.md +3 -3
- package/integrations/agents/AGENTS.md +299 -145
- package/integrations/copilot/.github/copilot-instructions.md +299 -145
- package/package.json +2 -1
- package/scripts/lib.sh +11 -3
- package/scripts/tools/_graph_queries.sh +102 -0
- package/scripts/tools/adr-index.sh +2 -2
- package/scripts/tools/check-scope-conflicts.sh +2 -2
- package/scripts/tools/check-skill-line-caps.sh +2 -2
- package/scripts/tools/cycle-detect.sh +22 -15
- package/scripts/tools/diff-templates-vs-tracks.sh +2 -2
- package/scripts/tools/fix-whitespace.sh +15 -9
- package/scripts/tools/graph-arch.sh +72 -0
- 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-impact.sh +1 -0
- package/scripts/tools/graph-init.sh +3 -3
- 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 +63 -50
- 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 +44 -15
- package/scripts/tools/manage-symlinks.sh +1 -1
- package/scripts/tools/mermaid-from-graph.sh +31 -10
- package/scripts/tools/parse-reports.sh +1 -1
- package/scripts/tools/verify-doc-anchors.sh +2 -2
- package/scripts/tools/verify-graph-binary.sh +1 -1
- package/skills/GRAPH.md +2 -2
- package/skills/bughunt/SKILL.md +1 -1
- package/skills/debug/SKILL.md +3 -3
- package/skills/decompose/SKILL.md +5 -5
- package/skills/deep-review/SKILL.md +2 -2
- package/skills/deploy-checklist/SKILL.md +2 -2
- package/skills/graph/SKILL.md +1 -1
- package/skills/implement/SKILL.md +1 -1
- package/skills/init/SKILL.md +62 -42
- package/skills/init/references/architecture-spec.md +12 -11
- package/skills/learn/SKILL.md +5 -5
- package/skills/quick-review/SKILL.md +3 -3
- package/skills/review/SKILL.md +7 -7
- package/skills/tech-debt/SKILL.md +2 -2
- package/scripts/tools/okf-bundle.sh +0 -141
- package/scripts/tools/okf-check.sh +0 -137
- package/scripts/tools/okf-emit.sh +0 -161
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# okf-bundle.sh — make a draft/ context directory an Open Knowledge Format bundle.
|
|
3
|
-
#
|
|
4
|
-
# OKF (Google Cloud, open spec) treats a directory of markdown files with YAML
|
|
5
|
-
# frontmatter as a knowledge bundle: one file per concept, the file path is the
|
|
6
|
-
# concept's identity, concepts cross-link with markdown links, and index.md is
|
|
7
|
-
# the navigable root. Draft's project-doc files already carry the required
|
|
8
|
-
# `type` frontmatter (architecture.md, .ai-context.md, product.md, ...); this
|
|
9
|
-
# tool writes the bundle's root index.md so the whole draft/ tree is a portable,
|
|
10
|
-
# vendor-neutral OKF bundle.
|
|
11
|
-
# https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing
|
|
12
|
-
#
|
|
13
|
-
# Writes <dir>/index.md (the OKF bundle-root index, §6/§11) linking every concept
|
|
14
|
-
# file present, the tracks, and the graph sub-bundle (graph/okf/). Validate the
|
|
15
|
-
# result with okf-check.sh (the OKF v0.1 conformance checker).
|
|
16
|
-
#
|
|
17
|
-
# Usage: scripts/tools/okf-bundle.sh [--dir DIR]
|
|
18
|
-
# Exit codes: 0 OK, 1 invocation error, 2 dir missing.
|
|
19
|
-
set -euo pipefail
|
|
20
|
-
|
|
21
|
-
DIR="draft"
|
|
22
|
-
|
|
23
|
-
usage() {
|
|
24
|
-
cat <<'EOF'
|
|
25
|
-
okf-bundle.sh — write the OKF bundle-root index.md for a draft/ context bundle.
|
|
26
|
-
|
|
27
|
-
Usage:
|
|
28
|
-
scripts/tools/okf-bundle.sh [--dir DIR]
|
|
29
|
-
|
|
30
|
-
Flags:
|
|
31
|
-
--dir DIR Bundle root (default: draft).
|
|
32
|
-
--help Show this help.
|
|
33
|
-
|
|
34
|
-
Writes <dir>/index.md cross-linking every concept present, the tracks, and the
|
|
35
|
-
graph sub-bundle. Validate conformance with `okf-check.sh --dir <dir>`.
|
|
36
|
-
Exit 0 OK, 1 invocation error, 2 when <dir> is absent.
|
|
37
|
-
EOF
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
while [[ $# -gt 0 ]]; do
|
|
41
|
-
case "$1" in
|
|
42
|
-
--dir) DIR="$2"; shift 2;;
|
|
43
|
-
--help|-h) usage; exit 0;;
|
|
44
|
-
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
45
|
-
*) echo "Unexpected arg: $1" >&2; usage >&2; exit 1;;
|
|
46
|
-
esac
|
|
47
|
-
done
|
|
48
|
-
|
|
49
|
-
[[ -d "$DIR" ]] || { echo "ERROR: --dir '$DIR' is not a directory" >&2; exit 2; }
|
|
50
|
-
|
|
51
|
-
# Canonical Draft concepts, ordered: "filename|label|expected-type".
|
|
52
|
-
# Each present file is linked from index.md and checked for `type:` under --check.
|
|
53
|
-
CONCEPTS=(
|
|
54
|
-
".ai-profile.md|AI Profile|Profile"
|
|
55
|
-
".ai-context.md|AI Context Map|ContextMap"
|
|
56
|
-
"architecture.md|Architecture|Architecture"
|
|
57
|
-
"product.md|Product|Product"
|
|
58
|
-
"tech-stack.md|Tech Stack|TechStack"
|
|
59
|
-
"workflow.md|Workflow|Workflow"
|
|
60
|
-
"guardrails.md|Guardrails|Guardrails"
|
|
61
|
-
"service-index.md|Service Index|ServiceIndex"
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# read_fm_field FILE FIELD — print a top-level frontmatter scalar (quotes stripped).
|
|
65
|
-
read_fm_field() {
|
|
66
|
-
awk -v field="$2" '
|
|
67
|
-
NR==1 { if ($0 != "---") exit; next }
|
|
68
|
-
/^---[[:space:]]*$/ { exit }
|
|
69
|
-
{
|
|
70
|
-
if (index($0, field ":") == 1) {
|
|
71
|
-
sub("^" field ":[[:space:]]*", "")
|
|
72
|
-
gsub(/^"|"$/, "")
|
|
73
|
-
print
|
|
74
|
-
exit
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
' "$1"
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
# --- write the bundle root index.md ---
|
|
81
|
-
PROJECT=""
|
|
82
|
-
for probe in architecture.md .ai-context.md product.md .ai-profile.md; do
|
|
83
|
-
if [[ -f "$DIR/$probe" ]]; then
|
|
84
|
-
PROJECT="$(read_fm_field "$DIR/$probe" project)"
|
|
85
|
-
[[ -n "$PROJECT" ]] && break
|
|
86
|
-
fi
|
|
87
|
-
done
|
|
88
|
-
[[ -n "$PROJECT" && "$PROJECT" != "{PROJECT_NAME}" ]] || PROJECT="$(basename "$(cd "$DIR/.." && pwd)")"
|
|
89
|
-
|
|
90
|
-
INDEX="$DIR/index.md"
|
|
91
|
-
{
|
|
92
|
-
# Bundle-root index.md: frontmatter is permitted only to declare okf_version (§11).
|
|
93
|
-
printf -- '---\n'
|
|
94
|
-
printf 'okf_version: "0.1"\n'
|
|
95
|
-
printf -- '---\n\n'
|
|
96
|
-
printf '# %s — Draft Context Bundle\n\n' "$PROJECT"
|
|
97
|
-
printf 'Open Knowledge Format (OKF) bundle root. Each concept below is a markdown file with a `type` frontmatter field; the links form the navigable knowledge graph.\n'
|
|
98
|
-
|
|
99
|
-
# Context concepts (only if at least one is present)
|
|
100
|
-
context=""
|
|
101
|
-
for entry in "${CONCEPTS[@]}"; do
|
|
102
|
-
IFS='|' read -r fname label expected <<< "$entry"
|
|
103
|
-
[[ -f "$DIR/$fname" ]] || continue
|
|
104
|
-
t="$(read_fm_field "$DIR/$fname" type)"
|
|
105
|
-
[[ -n "$t" ]] || t="$expected"
|
|
106
|
-
d="$(read_fm_field "$DIR/$fname" description)"
|
|
107
|
-
desc="${d:-$t concept}"
|
|
108
|
-
context+="$(printf -- '* [%s](%s) - %s' "$label" "$fname" "$desc")"$'\n'
|
|
109
|
-
done
|
|
110
|
-
if [[ -n "$context" ]]; then
|
|
111
|
-
printf '\n# Context\n\n%s' "$context"
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
# Tracks
|
|
115
|
-
if [[ -f "$DIR/tracks.md" || -d "$DIR/tracks" ]]; then
|
|
116
|
-
printf '\n# Tracks\n\n'
|
|
117
|
-
[[ -f "$DIR/tracks.md" ]] && printf -- '* [Track Index](tracks.md) - active, completed, and archived tracks\n'
|
|
118
|
-
if [[ -d "$DIR/tracks" ]]; then
|
|
119
|
-
for td in "$DIR"/tracks/*/; do
|
|
120
|
-
[[ -d "$td" ]] || continue
|
|
121
|
-
id="$(basename "$td")"
|
|
122
|
-
[[ -f "$td/spec.md" ]] || continue
|
|
123
|
-
title="$id"
|
|
124
|
-
if command -v jq >/dev/null 2>&1 && [[ -f "$td/metadata.json" ]]; then
|
|
125
|
-
mt="$(jq -r '.title // empty' "$td/metadata.json" 2>/dev/null || true)"
|
|
126
|
-
[[ -n "$mt" ]] && title="$mt"
|
|
127
|
-
fi
|
|
128
|
-
printf -- '* [%s](tracks/%s/spec.md) - track %s\n' "$title" "$id" "$id"
|
|
129
|
-
done
|
|
130
|
-
fi
|
|
131
|
-
fi
|
|
132
|
-
|
|
133
|
-
# Knowledge graph sub-bundle
|
|
134
|
-
if [[ -f "$DIR/graph/okf/index.md" ]]; then
|
|
135
|
-
printf '\n# Knowledge graph\n\n'
|
|
136
|
-
printf -- '* [Graph bundle](graph/okf/index.md) - structural knowledge graph (modules, dependencies, hotspots)\n'
|
|
137
|
-
fi
|
|
138
|
-
} > "$INDEX"
|
|
139
|
-
|
|
140
|
-
echo "OKF bundle root written to $INDEX"
|
|
141
|
-
exit 0
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# okf-check.sh — validate a directory against the Open Knowledge Format v0.1 spec.
|
|
3
|
-
#
|
|
4
|
-
# Implements the §9 conformance criteria of OKF v0.1
|
|
5
|
-
# (https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md):
|
|
6
|
-
#
|
|
7
|
-
# §9.1 Every non-reserved .md file has a parseable YAML frontmatter block.
|
|
8
|
-
# §9.2 Every such frontmatter block has a non-empty `type` field.
|
|
9
|
-
# §9.3 Reserved files follow their structure when present:
|
|
10
|
-
# index.md (§6) — contains NO frontmatter, EXCEPT the bundle-root
|
|
11
|
-
# index.md MAY carry frontmatter holding only
|
|
12
|
-
# `okf_version` (§11).
|
|
13
|
-
# log.md (§7) — `## ` date headings MUST be ISO 8601 (YYYY-MM-DD).
|
|
14
|
-
#
|
|
15
|
-
# Consumers are required to be permissive, so this checker only enforces the
|
|
16
|
-
# three hard rules above; everything else in the spec is soft guidance.
|
|
17
|
-
#
|
|
18
|
-
# Usage: scripts/tools/okf-check.sh [--dir DIR] [--quiet]
|
|
19
|
-
# Exit codes: 0 conformant, 1 violations found, 2 dir missing.
|
|
20
|
-
set -euo pipefail
|
|
21
|
-
|
|
22
|
-
DIR="draft"
|
|
23
|
-
QUIET=0
|
|
24
|
-
|
|
25
|
-
usage() {
|
|
26
|
-
cat <<'EOF'
|
|
27
|
-
okf-check.sh — validate a directory against Open Knowledge Format v0.1 (§9).
|
|
28
|
-
|
|
29
|
-
Usage:
|
|
30
|
-
scripts/tools/okf-check.sh [--dir DIR] [--quiet]
|
|
31
|
-
|
|
32
|
-
Flags:
|
|
33
|
-
--dir DIR Bundle root to validate (default: draft).
|
|
34
|
-
--quiet Print only the summary line, not per-file violations.
|
|
35
|
-
--help Show this help.
|
|
36
|
-
|
|
37
|
-
Exit 0 when conformant, 1 when violations are found, 2 when DIR is absent.
|
|
38
|
-
EOF
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
while [[ $# -gt 0 ]]; do
|
|
42
|
-
case "$1" in
|
|
43
|
-
--dir) DIR="$2"; shift 2;;
|
|
44
|
-
--quiet) QUIET=1; shift;;
|
|
45
|
-
--help|-h) usage; exit 0;;
|
|
46
|
-
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
47
|
-
*) echo "Unexpected arg: $1" >&2; usage >&2; exit 1;;
|
|
48
|
-
esac
|
|
49
|
-
done
|
|
50
|
-
|
|
51
|
-
[[ -d "$DIR" ]] || { echo "ERROR: --dir '$DIR' is not a directory" >&2; exit 2; }
|
|
52
|
-
DIR="${DIR%/}"
|
|
53
|
-
|
|
54
|
-
# fm_scan FILE -> "STATUS|TYPE|KEYS" (pipe-delimited; '|' is not IFS-whitespace,
|
|
55
|
-
# so empty TYPE/KEYS fields survive `read` instead of collapsing).
|
|
56
|
-
# STATUS: nofm (no frontmatter) | ok (closed block) | unterminated
|
|
57
|
-
# TYPE: value of the top-level `type:` key, if any
|
|
58
|
-
# KEYS: comma-separated top-level frontmatter keys
|
|
59
|
-
fm_scan() {
|
|
60
|
-
awk '
|
|
61
|
-
NR==1 { if ($0 != "---") { print "nofm||"; exit } ; inblock=1; next }
|
|
62
|
-
inblock && /^---[[:space:]]*$/ { print "ok|" type "|" keys; closed=1; exit }
|
|
63
|
-
inblock {
|
|
64
|
-
if (match($0, /^[A-Za-z_][A-Za-z0-9_]*:/)) {
|
|
65
|
-
k = substr($0, 1, RLENGTH-1)
|
|
66
|
-
keys = keys (keys=="" ? "" : ",") k
|
|
67
|
-
if (k == "type") {
|
|
68
|
-
v = substr($0, RLENGTH+1)
|
|
69
|
-
gsub(/^[ \t]+|[ \t]+$/, "", v)
|
|
70
|
-
gsub(/^"|"$/, "", v)
|
|
71
|
-
type = v
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
next
|
|
75
|
-
}
|
|
76
|
-
END { if (inblock && !closed) print "unterminated|" type "|" keys }
|
|
77
|
-
' "$1"
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
violations=0
|
|
81
|
-
concepts=0
|
|
82
|
-
reserved=0
|
|
83
|
-
|
|
84
|
-
report() { # relpath message
|
|
85
|
-
violations=$((violations + 1))
|
|
86
|
-
[[ "$QUIET" == "1" ]] || echo "FAIL $1: $2" >&2
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
while IFS= read -r -d '' file; do
|
|
90
|
-
rel="${file#"$DIR"/}"
|
|
91
|
-
base="$(basename "$file")"
|
|
92
|
-
|
|
93
|
-
case "$base" in
|
|
94
|
-
index.md)
|
|
95
|
-
reserved=$((reserved + 1))
|
|
96
|
-
IFS='|' read -r status _ keys < <(fm_scan "$file")
|
|
97
|
-
if [[ "$status" != "nofm" ]]; then
|
|
98
|
-
if [[ "$rel" == "index.md" ]]; then
|
|
99
|
-
# Bundle-root index.md: frontmatter allowed, but only okf_version (§11).
|
|
100
|
-
IFS=',' read -ra ks <<< "$keys"
|
|
101
|
-
for k in "${ks[@]}"; do
|
|
102
|
-
[[ -z "$k" || "$k" == "okf_version" ]] && continue
|
|
103
|
-
report "$rel" "root index.md frontmatter may only hold 'okf_version' (§11); found '$k'"
|
|
104
|
-
done
|
|
105
|
-
else
|
|
106
|
-
report "$rel" "index.md must not contain frontmatter (§6)"
|
|
107
|
-
fi
|
|
108
|
-
fi
|
|
109
|
-
;;
|
|
110
|
-
log.md)
|
|
111
|
-
reserved=$((reserved + 1))
|
|
112
|
-
while IFS= read -r h; do
|
|
113
|
-
if ! [[ "$h" =~ ^##[[:space:]]+[0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then
|
|
114
|
-
report "$rel" "log.md date heading not ISO 8601 (§7): '$h'"
|
|
115
|
-
fi
|
|
116
|
-
done < <(grep -E '^## ' "$file" 2>/dev/null || true)
|
|
117
|
-
;;
|
|
118
|
-
*)
|
|
119
|
-
concepts=$((concepts + 1))
|
|
120
|
-
IFS='|' read -r status type _ < <(fm_scan "$file")
|
|
121
|
-
case "$status" in
|
|
122
|
-
nofm) report "$rel" "missing YAML frontmatter block (§9.1)";;
|
|
123
|
-
unterminated) report "$rel" "unterminated frontmatter block — no closing '---' (§9.1)";;
|
|
124
|
-
ok)
|
|
125
|
-
[[ -n "$type" ]] || report "$rel" "frontmatter missing required non-empty 'type' (§9.2)"
|
|
126
|
-
;;
|
|
127
|
-
esac
|
|
128
|
-
;;
|
|
129
|
-
esac
|
|
130
|
-
done < <(find "$DIR" -type f -name '*.md' -print0 | sort -z)
|
|
131
|
-
|
|
132
|
-
if [[ "$violations" -eq 0 ]]; then
|
|
133
|
-
echo "OKF v0.1 conformant — $concepts concept file(s), $reserved reserved file(s), 0 violations. ($DIR)"
|
|
134
|
-
exit 0
|
|
135
|
-
fi
|
|
136
|
-
echo "OKF v0.1 NON-CONFORMANT — $violations violation(s) across $concepts concept + $reserved reserved file(s). ($DIR)" >&2
|
|
137
|
-
exit 1
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# okf-emit.sh — emit an Open Knowledge Format (OKF) bundle from a graph snapshot.
|
|
3
|
-
#
|
|
4
|
-
# OKF is an open, vendor-neutral spec (Google Cloud): a directory of markdown
|
|
5
|
-
# files with YAML frontmatter, one file per concept, where the file path is the
|
|
6
|
-
# concept's identity and concepts cross-link with normal markdown links. The
|
|
7
|
-
# only required frontmatter field is `type`. This makes Draft's knowledge graph
|
|
8
|
-
# portable — consumable by any OKF reader (visualizers, catalogs, other agents).
|
|
9
|
-
# https://cloud.google.com/blog/products/data-analytics/how-the-open-knowledge-format-can-improve-data-sharing
|
|
10
|
-
#
|
|
11
|
-
# Reads a graph snapshot's architecture.json and writes a conformant bundle:
|
|
12
|
-
# index.md type: Repository — bundle root + progressive disclosure
|
|
13
|
-
# modules/<slug>.md type: Module — one concept per package, cross-linked
|
|
14
|
-
#
|
|
15
|
-
# Degrades gracefully: with no packages/boundaries in the snapshot it still emits
|
|
16
|
-
# index.md (counts, languages, hotspots) and an empty modules/ directory.
|
|
17
|
-
#
|
|
18
|
-
# Usage: scripts/tools/okf-emit.sh [--repo DIR] [--snapshot DIR] [--out DIR]
|
|
19
|
-
# Exit codes: 0 OK, 1 invocation error, 2 snapshot/architecture.json unavailable.
|
|
20
|
-
set -euo pipefail
|
|
21
|
-
|
|
22
|
-
REPO="."
|
|
23
|
-
SNAPSHOT=""
|
|
24
|
-
OUT=""
|
|
25
|
-
|
|
26
|
-
usage() {
|
|
27
|
-
cat <<'EOF'
|
|
28
|
-
okf-emit.sh — emit an Open Knowledge Format (OKF) bundle from a graph snapshot.
|
|
29
|
-
|
|
30
|
-
Usage:
|
|
31
|
-
scripts/tools/okf-emit.sh [--repo DIR] [--snapshot DIR] [--out DIR]
|
|
32
|
-
|
|
33
|
-
Flags:
|
|
34
|
-
--repo DIR Repository root (default: cwd).
|
|
35
|
-
--snapshot DIR Snapshot dir holding architecture.json (default: <repo>/draft/graph).
|
|
36
|
-
--out DIR Bundle output dir (default: <snapshot>/okf).
|
|
37
|
-
--help Show this help.
|
|
38
|
-
|
|
39
|
-
Writes index.md + modules/<slug>.md (OKF v0.1). Exit 0 on success, 2 when no
|
|
40
|
-
architecture.json is available (nothing emitted).
|
|
41
|
-
EOF
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
while [[ $# -gt 0 ]]; do
|
|
45
|
-
case "$1" in
|
|
46
|
-
--repo) REPO="$2"; shift 2;;
|
|
47
|
-
--snapshot) SNAPSHOT="$2"; shift 2;;
|
|
48
|
-
--out) OUT="$2"; shift 2;;
|
|
49
|
-
--help|-h) usage; exit 0;;
|
|
50
|
-
-*) echo "Unknown flag: $1" >&2; usage >&2; exit 1;;
|
|
51
|
-
*) echo "Unexpected arg: $1" >&2; usage >&2; exit 1;;
|
|
52
|
-
esac
|
|
53
|
-
done
|
|
54
|
-
|
|
55
|
-
[[ -d "$REPO" ]] || { echo "ERROR: --repo '$REPO' is not a directory" >&2; exit 1; }
|
|
56
|
-
REPO_ABS="$(cd "$REPO" && pwd)"
|
|
57
|
-
SNAP="${SNAPSHOT:-$REPO_ABS/draft/graph}"
|
|
58
|
-
ARCH="$SNAP/architecture.json"
|
|
59
|
-
OUT="${OUT:-$SNAP/okf}"
|
|
60
|
-
|
|
61
|
-
command -v jq >/dev/null 2>&1 || { echo "jq required for OKF emit" >&2; exit 2; }
|
|
62
|
-
[[ -f "$ARCH" ]] || { echo "no architecture.json at $ARCH — nothing to emit" >&2; exit 2; }
|
|
63
|
-
|
|
64
|
-
# slugify a concept name into a filesystem- and link-safe identifier.
|
|
65
|
-
slug() {
|
|
66
|
-
local s
|
|
67
|
-
s="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' \
|
|
68
|
-
| sed -e 's#[^a-z0-9]\{1,\}#-#g' -e 's#^-##' -e 's#-$##')"
|
|
69
|
-
[[ -n "$s" ]] || s="module"
|
|
70
|
-
printf '%s' "$s"
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
# escape a value for a YAML double-quoted scalar.
|
|
74
|
-
yesc() {
|
|
75
|
-
local s="$1"
|
|
76
|
-
s="${s//\\/\\\\}"
|
|
77
|
-
s="${s//\"/\\\"}"
|
|
78
|
-
printf '%s' "$s"
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
TS="$(date -Iseconds 2>/dev/null || date)"
|
|
82
|
-
PROJECT="$(jq -r '.project // "repository"' "$ARCH")"
|
|
83
|
-
TOTAL_NODES="$(jq -r '.total_nodes // 0' "$ARCH")"
|
|
84
|
-
TOTAL_EDGES="$(jq -r '.total_edges // 0' "$ARCH")"
|
|
85
|
-
|
|
86
|
-
PKGS="$(jq -r '.packages[]? | [.name, (.node_count//0), (.fan_in//0), (.fan_out//0)] | @tsv' "$ARCH")"
|
|
87
|
-
BOUNDS="$(jq -r '.boundaries[]? | [.from, .to, (.call_count//0)] | @tsv' "$ARCH")"
|
|
88
|
-
|
|
89
|
-
mkdir -p "$OUT/modules"
|
|
90
|
-
|
|
91
|
-
# --- One concept file per package, cross-linked via boundaries ---
|
|
92
|
-
PKG_COUNT=0
|
|
93
|
-
if [[ -n "$PKGS" ]]; then
|
|
94
|
-
while IFS=$'\t' read -r name nc fi fo; do
|
|
95
|
-
[[ -n "$name" ]] || continue
|
|
96
|
-
PKG_COUNT=$((PKG_COUNT + 1))
|
|
97
|
-
s="$(slug "$name")"
|
|
98
|
-
{
|
|
99
|
-
printf -- '---\n'
|
|
100
|
-
printf 'type: Module\n'
|
|
101
|
-
printf 'title: "%s"\n' "$(yesc "$name")"
|
|
102
|
-
printf 'description: "Code module %s: %s nodes, fan-in %s, fan-out %s."\n' \
|
|
103
|
-
"$(yesc "$name")" "$nc" "$fi" "$fo"
|
|
104
|
-
printf 'tags: [module, knowledge-graph]\n'
|
|
105
|
-
printf 'timestamp: "%s"\n' "$TS"
|
|
106
|
-
printf -- '---\n\n'
|
|
107
|
-
printf '# %s\n\n' "$name"
|
|
108
|
-
printf 'Structural module derived from the knowledge graph. Nodes: %s, fan-in: %s, fan-out: %s.\n' \
|
|
109
|
-
"$nc" "$fi" "$fo"
|
|
110
|
-
|
|
111
|
-
# Depends on (outbound boundaries)
|
|
112
|
-
outs="$(awk -F'\t' -v n="$name" '$1==n && $2!="" {print $2"\t"$3}' <<< "$BOUNDS")"
|
|
113
|
-
if [[ -n "$outs" ]]; then
|
|
114
|
-
printf '\n## Depends on\n\n'
|
|
115
|
-
while IFS=$'\t' read -r to cc; do
|
|
116
|
-
[[ -n "$to" ]] || continue
|
|
117
|
-
printf -- '- [%s](%s.md) — %s call(s)\n' "$to" "$(slug "$to")" "$cc"
|
|
118
|
-
done <<< "$outs"
|
|
119
|
-
fi
|
|
120
|
-
|
|
121
|
-
# Depended on by (inbound boundaries)
|
|
122
|
-
ins="$(awk -F'\t' -v n="$name" '$2==n && $1!="" {print $1"\t"$3}' <<< "$BOUNDS")"
|
|
123
|
-
if [[ -n "$ins" ]]; then
|
|
124
|
-
printf '\n## Depended on by\n\n'
|
|
125
|
-
while IFS=$'\t' read -r from cc; do
|
|
126
|
-
[[ -n "$from" ]] || continue
|
|
127
|
-
printf -- '- [%s](%s.md) — %s call(s)\n' "$from" "$(slug "$from")" "$cc"
|
|
128
|
-
done <<< "$ins"
|
|
129
|
-
fi
|
|
130
|
-
} > "$OUT/modules/$s.md"
|
|
131
|
-
done <<< "$PKGS"
|
|
132
|
-
fi
|
|
133
|
-
|
|
134
|
-
# --- Bundle index.md (reserved file: no frontmatter, §6 progressive disclosure) ---
|
|
135
|
-
{
|
|
136
|
-
printf '# %s\n\n' "$PROJECT"
|
|
137
|
-
printf 'Open Knowledge Format bundle generated by Draft from the codebase-memory-mcp knowledge graph. Nodes: %s, edges: %s, modules: %s.\n' \
|
|
138
|
-
"$TOTAL_NODES" "$TOTAL_EDGES" "$PKG_COUNT"
|
|
139
|
-
|
|
140
|
-
langs="$(jq -r '.languages[]? | "* \(.language) - \(.file_count) file(s)"' "$ARCH")"
|
|
141
|
-
if [[ -n "$langs" ]]; then
|
|
142
|
-
printf '\n# Languages\n\n%s\n' "$langs"
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
if [[ "$PKG_COUNT" -gt 0 ]]; then
|
|
146
|
-
printf '\n# Modules\n\n'
|
|
147
|
-
while IFS=$'\t' read -r name nc fi fo; do
|
|
148
|
-
[[ -n "$name" ]] || continue
|
|
149
|
-
printf -- '* [%s](modules/%s.md) - %s nodes, fan-in %s, fan-out %s\n' \
|
|
150
|
-
"$name" "$(slug "$name")" "$nc" "$fi" "$fo"
|
|
151
|
-
done < <(printf '%s\n' "$PKGS" | sort)
|
|
152
|
-
fi
|
|
153
|
-
|
|
154
|
-
hot="$(jq -r '.hotspots[:10][]? | "* \(.name) - fan-in \(.fan_in)"' "$ARCH")"
|
|
155
|
-
if [[ -n "$hot" ]]; then
|
|
156
|
-
printf '\n# Top hotspots\n\n%s\n' "$hot"
|
|
157
|
-
fi
|
|
158
|
-
} > "$OUT/index.md"
|
|
159
|
-
|
|
160
|
-
echo "OKF bundle written to $OUT (modules=$PKG_COUNT)"
|
|
161
|
-
exit 0
|