gennady 0.7.0 → 0.8.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/ai/directives/coding/result-conventions.xml +25 -7
- package/ai/directives/sdd/audit.directive.xml +1 -1
- package/ai/directives/sdd/critic-protocol.xml +78 -0
- package/ai/directives/sdd/critic.directive.xml +158 -0
- package/ai/directives/sdd/phase-execution-protocol.xml +11 -9
- package/ai/skills/alt-opinion/SKILL.md +79 -0
- package/ai/skills/alt-opinion/opinion.prompt.md +11 -0
- package/ai/skills/alt-opinion/synth.prompt.md +8 -0
- package/ai/skills/sdd-audit/SKILL.md +13 -0
- package/ai/skills/sdd-check/SKILL.md +139 -0
- package/ai/skills/sdd-continue/SKILL.md +13 -0
- package/ai/skills/sdd-critic/SKILL.md +12 -0
- package/ai/skills/sdd-discover/SKILL.md +13 -0
- package/ai/skills/sdd-execute/SKILL.md +205 -0
- package/ai/skills/sdd-execute/scripts/README.md +84 -0
- package/ai/skills/sdd-execute/scripts/check-blockers.sh +158 -0
- package/ai/skills/sdd-execute/scripts/classify-scripts.js +111 -0
- package/ai/skills/sdd-execute/scripts/classify-scripts.ts +133 -0
- package/ai/skills/sdd-execute/scripts/extract-section.sh +219 -0
- package/ai/skills/sdd-execute/scripts/lint-artifacts.sh +143 -0
- package/ai/skills/sdd-execute/scripts/scan.sh +385 -0
- package/ai/skills/sdd-execute/scripts/sdd +114 -0
- package/ai/skills/sdd-execute/scripts/verify.sh +139 -0
- package/ai/skills/sdd-execute-batch/SKILL.md +213 -0
- package/ai/skills/sdd-fix/SKILL.md +13 -0
- package/ai/skills/sdd-infra/SKILL.md +13 -0
- package/ai/skills/sdd-module-decomposition/SKILL.md +13 -0
- package/ai/skills/sdd-scaffold/SKILL.md +13 -0
- package/ai/skills/sdd-setup/SKILL.md +13 -0
- package/dist/ai/directives/coding/result-conventions.xml +25 -7
- package/dist/ai/directives/sdd/audit.directive.xml +1 -1
- package/dist/ai/directives/sdd/critic-protocol.xml +78 -0
- package/dist/ai/directives/sdd/critic.directive.xml +158 -0
- package/dist/ai/directives/sdd/phase-execution-protocol.xml +11 -9
- package/dist/ai/skills/alt-opinion/SKILL.md +79 -0
- package/dist/ai/skills/alt-opinion/opinion.prompt.md +11 -0
- package/dist/ai/skills/alt-opinion/synth.prompt.md +8 -0
- package/dist/ai/skills/sdd-audit/SKILL.md +13 -0
- package/dist/ai/skills/sdd-check/SKILL.md +139 -0
- package/dist/ai/skills/sdd-continue/SKILL.md +13 -0
- package/dist/ai/skills/sdd-critic/SKILL.md +12 -0
- package/dist/ai/skills/sdd-discover/SKILL.md +13 -0
- package/dist/ai/skills/sdd-execute/SKILL.md +205 -0
- package/dist/ai/skills/sdd-execute/scripts/README.md +84 -0
- package/dist/ai/skills/sdd-execute/scripts/check-blockers.sh +158 -0
- package/dist/ai/skills/sdd-execute/scripts/classify-scripts.js +111 -0
- package/dist/ai/skills/sdd-execute/scripts/classify-scripts.ts +133 -0
- package/dist/ai/skills/sdd-execute/scripts/extract-section.sh +219 -0
- package/dist/ai/skills/sdd-execute/scripts/lint-artifacts.sh +143 -0
- package/dist/ai/skills/sdd-execute/scripts/scan.sh +385 -0
- package/dist/ai/skills/sdd-execute/scripts/sdd +114 -0
- package/dist/ai/skills/sdd-execute/scripts/verify.sh +139 -0
- package/dist/ai/skills/sdd-execute-batch/SKILL.md +213 -0
- package/dist/ai/skills/sdd-fix/SKILL.md +13 -0
- package/dist/ai/skills/sdd-infra/SKILL.md +13 -0
- package/dist/ai/skills/sdd-module-decomposition/SKILL.md +13 -0
- package/dist/ai/skills/sdd-scaffold/SKILL.md +13 -0
- package/dist/ai/skills/sdd-setup/SKILL.md +13 -0
- package/dist/chunks/_commonjsHelpers-DaMA6jEr.js +8 -0
- package/dist/chunks/{devtools-B-7ugZhF.js → devtools-CPZ0IUNM.js} +1 -1
- package/dist/chunks/help-5qCaicff.js +6 -0
- package/dist/chunks/help-B2ETH0j6.js +6 -0
- package/dist/chunks/help-BCDURaCQ.js +6 -0
- package/dist/chunks/help-BvgYOSyQ.js +6 -0
- package/dist/chunks/help-CSPhOoI9.js +8 -0
- package/dist/chunks/help-CV1vpwrh.js +6 -0
- package/dist/chunks/help-CaBNA6Qr.js +12 -0
- package/dist/chunks/help-CjkR6SB9.js +6 -0
- package/dist/chunks/help-Coi1l5u9.js +8 -0
- package/dist/chunks/help-CsuLpZn-.js +8 -0
- package/dist/chunks/help-DL_QBMwy.js +8 -0
- package/dist/chunks/help-DqBZ2xcp.js +6 -0
- package/dist/chunks/help-_Y_TN4DZ.js +6 -0
- package/dist/chunks/{help.cmd-B_G7EWzF.js → help.cmd-BseRtoBF.js} +10 -2
- package/dist/chunks/{index-zBnnuvLA.js → index-BU1AXJGA.js} +12 -7
- package/dist/chunks/{index-CXuhZzS3.js → index-BXWDYM_9.js} +1 -1
- package/dist/chunks/index-BgKyH0dB.js +658 -0
- package/dist/chunks/{index-CaahtXiM.js → index-BghL1-Nk.js} +2 -2
- package/dist/chunks/{index-BeL1Zcbg.js → index-BlqEQEGy.js} +1 -1
- package/dist/chunks/{index-D1qsi0Uc.js → index-CDM74qdR.js} +3 -3
- package/dist/chunks/{index-D9ceRUyB.js → index-CGsIRefC.js} +2 -2
- package/dist/chunks/{index-CiEM-8nJ.js → index-CIvkaAQ3.js} +2 -2
- package/dist/chunks/{index-UbHoePfr.js → index-CkczJ__m.js} +10 -10
- package/dist/chunks/index-CtJKSOL0.js +21 -0
- package/dist/chunks/index-DEzym4Ae.js +369 -0
- package/dist/chunks/{index-CVR66voe.js → index-DoaxOhQ8.js} +401 -393
- package/dist/chunks/index-Du1xR-qO.js +752 -0
- package/dist/chunks/{index-DU6jD7SS.js → index-OtehG7uM.js} +1 -1
- package/dist/chunks/index-ZM_FonZj.js +156 -0
- package/dist/chunks/{index-g1LXlp77.js → index-k0nToACp.js} +2 -2
- package/dist/chunks/{run-review-command.logic-DpkRoEi8.js → run-review-command.logic-BeYQin-I.js} +9 -9
- package/dist/chunks/services-IgZptnTi.js +2732 -0
- package/dist/chunks/shared-DL_1LG0K.js +753 -0
- package/dist/cli/cmd/agent-mon/help.d.ts +4 -0
- package/dist/cli/cmd/agents-rules/agents-rules.cmd.d.ts +8 -0
- package/dist/cli/cmd/agents-rules/index.d.ts +1 -0
- package/dist/cli/cmd/alt-opinion/help.d.ts +4 -0
- package/dist/cli/cmd/cat/help.d.ts +4 -0
- package/dist/cli/cmd/commit/help.d.ts +4 -0
- package/dist/cli/cmd/lint/checks/anchor-class-body.check.d.ts +11 -0
- package/dist/cli/cmd/lint/checks/invariant-count.check.d.ts +11 -0
- package/dist/cli/cmd/lint/checks/utils/glob-match.d.ts +15 -0
- package/dist/cli/cmd/lint/checks/utils/strip-strings-comments.d.ts +7 -0
- package/dist/cli/cmd/lint/help.d.ts +4 -0
- package/dist/cli/cmd/lint/lint.cmd.d.ts +1 -1
- package/dist/cli/cmd/lint/lint.types.d.ts +6 -0
- package/dist/cli/cmd/orient/core/build-index.d.ts +8 -0
- package/dist/cli/cmd/orient/core/damerau-levenshtein.d.ts +26 -0
- package/dist/cli/cmd/orient/core/extract-header.d.ts +8 -0
- package/dist/cli/cmd/orient/core/hints.d.ts +9 -0
- package/dist/cli/cmd/orient/core/query-consumer.d.ts +10 -0
- package/dist/cli/cmd/orient/core/query-entity.d.ts +11 -0
- package/dist/cli/cmd/orient/core/query-graph.d.ts +18 -0
- package/dist/cli/cmd/orient/core/query-keyword.d.ts +10 -0
- package/dist/cli/cmd/orient/core/query-spec.d.ts +15 -0
- package/dist/cli/cmd/orient/core/query-task.d.ts +8 -0
- package/dist/cli/cmd/orient/core/scan-files.d.ts +8 -0
- package/dist/cli/cmd/orient/help.d.ts +5 -0
- package/dist/cli/cmd/orient/index.d.ts +1 -0
- package/dist/cli/cmd/orient/orient.cmd.d.ts +7 -0
- package/dist/cli/cmd/orient/orient.types.d.ts +136 -0
- package/dist/cli/cmd/orient/render/render-detail.d.ts +10 -0
- package/dist/cli/cmd/orient/render/render-file-list.d.ts +15 -0
- package/dist/cli/cmd/orient/render/render-graph.d.ts +13 -0
- package/dist/cli/cmd/orient/render/render-search.d.ts +8 -0
- package/dist/cli/cmd/orient/render/render-specs.d.ts +13 -0
- package/dist/cli/cmd/orient/render/render-tree.d.ts +13 -0
- package/dist/cli/cmd/remote-console/help.d.ts +4 -0
- package/dist/cli/cmd/resolve-conflicts/help.d.ts +4 -0
- package/dist/cli/cmd/review/help.d.ts +4 -0
- package/dist/cli/cmd/review-issues/help.d.ts +4 -0
- package/dist/cli/cmd/review-verify/help.d.ts +4 -0
- package/dist/cli/cmd/sync/help.d.ts +4 -0
- package/dist/cli/cmd/sync/sync-core.d.ts +3 -2
- package/dist/cli/cmd/sync/sync-formatter.d.ts +8 -9
- package/dist/cli/cmd/sync/sync.cmd.d.ts +2 -21
- package/dist/cli/cmd/sync-skills/index.d.ts +1 -0
- package/dist/cli/cmd/sync-skills/sync-skills-core.d.ts +50 -0
- package/dist/cli/cmd/sync-skills/sync-skills-formatter.d.ts +13 -0
- package/dist/cli/cmd/sync-skills/sync-skills.cmd.d.ts +14 -0
- package/dist/cli/cmd/sync-skills/sync-skills.types.d.ts +56 -0
- package/dist/cli/cmd/vcs-reply/help.d.ts +4 -0
- package/dist/gennady.js +73 -18
- package/dist/index.js +5 -5
- package/dist/services/dbc/linter/implementations/ts/dbc-ts-ast-adapter.d.ts +3 -2
- package/dist/shared/common/sync/path-normalizer.d.ts +22 -0
- package/dist/shared/common/sync/sync-core.shared.d.ts +14 -0
- package/dist/shared/common/sync/sync-deps.type.d.ts +66 -0
- package/dist/shared/common/sync/sync-formatter.shared.d.ts +19 -0
- package/package.json +6 -5
- package/dist/chunks/index-DJpVmyp2.js +0 -176
- package/dist/chunks/index-KxSZKmZn.js +0 -540
- package/dist/chunks/services-CaLOhuLV.js +0 -2889
- package/dist/chunks/shared-Bjy30TeM.js +0 -665
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @file: Emit comprehensive SDD project snapshot in a single tool call.
|
|
3
|
+
# @consumers: sdd-check, sdd-continue, sdd-execute-batch (preflight + triage).
|
|
4
|
+
# @contract: AX_BASH_NO_SILENT_EMPTY. One-shot rich-context emitter so agents
|
|
5
|
+
# avoid running multiple ad-hoc find/grep commands.
|
|
6
|
+
#
|
|
7
|
+
# Output sections (stable, machine-readable):
|
|
8
|
+
# [HEADER] — scan metadata (root, timestamp, version)
|
|
9
|
+
# [TASKS] — TSV: path, status, last_round, blocker, placeholders, warnings
|
|
10
|
+
# [TRACKERS] — TSV: tracker_path, done, todo, in_progress, blocked, total
|
|
11
|
+
# [SPECS] — TSV: spec_path, scope, modules_listed, modules_missing
|
|
12
|
+
# [WARNINGS] — severity \t location \t message (collected during scan)
|
|
13
|
+
# [SUMMARY] — key=value totals
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# scan.sh [project-root] # defaults to current directory
|
|
17
|
+
#
|
|
18
|
+
# Exit codes:
|
|
19
|
+
# 0 — snapshot emitted (may include WARNINGS — agent must inspect)
|
|
20
|
+
# 2 — structural failure (root not found, tasks/ missing, etc.); STDOUT has
|
|
21
|
+
# actionable diagnostic instructions
|
|
22
|
+
# 4 — bad invocation
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
PROG="scan"
|
|
27
|
+
VERSION="1"
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# Argument parsing
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
if [[ $# -gt 1 ]]; then
|
|
34
|
+
cat <<EOF
|
|
35
|
+
[$PROG] BAD_INVOCATION
|
|
36
|
+
expected: $PROG [project-root]
|
|
37
|
+
got: $PROG $*
|
|
38
|
+
|
|
39
|
+
Required action:
|
|
40
|
+
Pass zero or one argument — the project root. Defaults to current directory.
|
|
41
|
+
EOF
|
|
42
|
+
exit 4
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
ROOT="${1:-.}"
|
|
46
|
+
|
|
47
|
+
if [[ ! -d "$ROOT" ]]; then
|
|
48
|
+
cat <<EOF
|
|
49
|
+
[$PROG] BAD_ROOT
|
|
50
|
+
path: $ROOT
|
|
51
|
+
reason: not a directory or does not exist
|
|
52
|
+
|
|
53
|
+
Required action:
|
|
54
|
+
- Verify you passed a project root that exists.
|
|
55
|
+
- From within a project, call: $PROG (no arg) or $PROG .
|
|
56
|
+
EOF
|
|
57
|
+
exit 2
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Resolve to absolute path for stable output
|
|
61
|
+
ROOT_ABS="$(cd "$ROOT" && pwd)"
|
|
62
|
+
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
# Sanity: does this look like an SDD project?
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
has_tasks=0
|
|
68
|
+
has_specs=0
|
|
69
|
+
[[ -d "$ROOT_ABS/tasks" ]] && has_tasks=1
|
|
70
|
+
[[ -d "$ROOT_ABS/specs" ]] && has_specs=1
|
|
71
|
+
|
|
72
|
+
if [[ $has_tasks -eq 0 && $has_specs -eq 0 ]]; then
|
|
73
|
+
cat <<EOF
|
|
74
|
+
[$PROG] NOT_AN_SDD_PROJECT
|
|
75
|
+
root: $ROOT_ABS
|
|
76
|
+
reason: neither tasks/ nor specs/ directory found
|
|
77
|
+
|
|
78
|
+
Required action:
|
|
79
|
+
- If this is meant to be an SDD project, run /sdd-scaffold to bootstrap it.
|
|
80
|
+
- If you ran from the wrong directory, pass the project root explicitly:
|
|
81
|
+
$PROG /path/to/project
|
|
82
|
+
- If your project uses non-standard layout (tasks elsewhere), this tool
|
|
83
|
+
does not support that — convention is tasks/ + specs/ under root.
|
|
84
|
+
EOF
|
|
85
|
+
exit 2
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# Buffers (using temp files for portability with macOS bash 3.2)
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
TMPDIR_BUF="$(mktemp -d -t sdd-scan.XXXXXX)" || { echo "[$PROG] MKTEMP_FAILED"; exit 2; }
|
|
93
|
+
trap 'rm -rf "$TMPDIR_BUF"' EXIT
|
|
94
|
+
|
|
95
|
+
WARN_FILE="$TMPDIR_BUF/warnings"
|
|
96
|
+
: > "$WARN_FILE"
|
|
97
|
+
|
|
98
|
+
emit_warn() {
|
|
99
|
+
# severity \t location \t message
|
|
100
|
+
printf '%s\t%s\t%s\n' "$1" "$2" "$3" >> "$WARN_FILE"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# HEADER
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
printf '# sdd scan v%s\n' "$VERSION"
|
|
108
|
+
printf 'ROOT=%s\n' "$ROOT_ABS"
|
|
109
|
+
printf 'SCANNED_AT=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
110
|
+
printf 'HAS_TASKS=%d\n' "$has_tasks"
|
|
111
|
+
printf 'HAS_SPECS=%d\n' "$has_specs"
|
|
112
|
+
|
|
113
|
+
# ---------------------------------------------------------------------------
|
|
114
|
+
# Helpers
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
# Extract Meta.Status from a ticket. Echoes one of:
|
|
118
|
+
# DONE | TODO | IN_PROGRESS | BLOCKED | UNKNOWN
|
|
119
|
+
extract_status() {
|
|
120
|
+
local f="$1"
|
|
121
|
+
# Look in first 60 lines for "Status:" with [flag] STATE
|
|
122
|
+
local line
|
|
123
|
+
line=$(head -60 "$f" 2>/dev/null | grep -m1 -E '^\s*-?\s*\*?\*?Status:\*?\*?\s*\[.\]' || true)
|
|
124
|
+
if [[ -z "$line" ]]; then
|
|
125
|
+
echo "UNKNOWN"
|
|
126
|
+
return
|
|
127
|
+
fi
|
|
128
|
+
# Extract flag char between [ and ]
|
|
129
|
+
local flag
|
|
130
|
+
flag=$(echo "$line" | sed -nE 's/.*\[(.)\].*/\1/p')
|
|
131
|
+
case "$flag" in
|
|
132
|
+
x|X) echo "DONE" ;;
|
|
133
|
+
' ') echo "TODO" ;;
|
|
134
|
+
'~') echo "IN_PROGRESS" ;;
|
|
135
|
+
'!') echo "BLOCKED" ;;
|
|
136
|
+
*) echo "UNKNOWN" ;;
|
|
137
|
+
esac
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# Count Round headers in Execution Log. Echoes count (0 if none).
|
|
141
|
+
count_rounds() {
|
|
142
|
+
local f="$1"
|
|
143
|
+
grep -E '^### Round [0-9]+' "$f" 2>/dev/null | wc -l | tr -d ' '
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Detect blocker state. Echoes:
|
|
147
|
+
# none — no 🛑 BLOCKED entries
|
|
148
|
+
# active — at least one 🛑 BLOCKED with no matching later ✅ RESOLVED
|
|
149
|
+
# resolved — all 🛑 entries followed by ✅ RESOLVED (last marker is ✅)
|
|
150
|
+
blocker_state() {
|
|
151
|
+
local f="$1"
|
|
152
|
+
local log_start
|
|
153
|
+
log_start=$(grep -n "^## 7\. Execution Log" "$f" 2>/dev/null | head -1 | cut -d: -f1)
|
|
154
|
+
[[ -z "$log_start" ]] && { echo "none"; return; }
|
|
155
|
+
|
|
156
|
+
local blockers resolved last_b last_r
|
|
157
|
+
blockers=$(grep -nE '🛑.*BLOCKED|BLOCKED.*🛑' "$f" 2>/dev/null \
|
|
158
|
+
| awk -F: -v s="$log_start" '$1>=s' | wc -l | tr -d ' ')
|
|
159
|
+
resolved=$(grep -nE '✅.*RESOLVED|RESOLVED.*✅' "$f" 2>/dev/null \
|
|
160
|
+
| awk -F: -v s="$log_start" '$1>=s' | wc -l | tr -d ' ')
|
|
161
|
+
|
|
162
|
+
[[ "$blockers" -eq 0 ]] && { echo "none"; return; }
|
|
163
|
+
|
|
164
|
+
last_b=$(grep -nE '🛑.*BLOCKED|BLOCKED.*🛑' "$f" 2>/dev/null \
|
|
165
|
+
| awk -F: -v s="$log_start" '$1>=s {print $1}' | tail -1)
|
|
166
|
+
last_r=$(grep -nE '✅.*RESOLVED|RESOLVED.*✅' "$f" 2>/dev/null \
|
|
167
|
+
| awk -F: -v s="$log_start" '$1>=s {print $1}' | tail -1)
|
|
168
|
+
last_b="${last_b:-0}"
|
|
169
|
+
last_r="${last_r:-0}"
|
|
170
|
+
|
|
171
|
+
if [[ "$resolved" -ge "$blockers" && "$last_r" -gt "$last_b" ]]; then
|
|
172
|
+
echo "resolved"
|
|
173
|
+
else
|
|
174
|
+
echo "active"
|
|
175
|
+
fi
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Count placeholder markers (unfilled scaffold values) in a ticket.
|
|
179
|
+
count_placeholders() {
|
|
180
|
+
local f="$1"
|
|
181
|
+
grep -E '<YYYY-MM-DD>|<TBD>|`\[<ts>\]`|<command>|<scope>|<scenario>|<test-file>::<case>|<list or' "$f" 2>/dev/null | wc -l | tr -d ' '
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# Per-ticket warnings (string, comma-separated, "-" if none)
|
|
185
|
+
ticket_warnings() {
|
|
186
|
+
local f="$1"
|
|
187
|
+
local w=()
|
|
188
|
+
# Required structural sections
|
|
189
|
+
grep -q '^## 1\. Meta' "$f" 2>/dev/null || w+=("no-meta-section")
|
|
190
|
+
grep -q '^## 7\. Execution Log' "$f" 2>/dev/null || w+=("no-execlog-section")
|
|
191
|
+
# Anchor closure sanity
|
|
192
|
+
local opens closes
|
|
193
|
+
opens=$(grep -E '^[[:space:]]*<!--SECTION:[A-Z][A-Z0-9_]*-->' "$f" 2>/dev/null | wc -l | tr -d ' ')
|
|
194
|
+
closes=$(grep -E '^[[:space:]]*<!--/SECTION:[A-Z][A-Z0-9_]*-->' "$f" 2>/dev/null | wc -l | tr -d ' ')
|
|
195
|
+
if [[ "$opens" -ne "$closes" ]]; then
|
|
196
|
+
w+=("anchors-mismatch:$opens-open/$closes-close")
|
|
197
|
+
fi
|
|
198
|
+
if [[ ${#w[@]} -eq 0 ]]; then
|
|
199
|
+
echo "-"
|
|
200
|
+
else
|
|
201
|
+
(IFS=,; echo "${w[*]}")
|
|
202
|
+
fi
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# ---------------------------------------------------------------------------
|
|
206
|
+
# [TASKS]
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
printf '\n[TASKS]\n'
|
|
210
|
+
printf '# path\tstatus\tlast_round\tblocker\tplaceholders\twarnings\n'
|
|
211
|
+
|
|
212
|
+
if [[ $has_tasks -eq 0 ]]; then
|
|
213
|
+
printf '# (tasks/ directory not present)\n'
|
|
214
|
+
emit_warn "WARN" "tasks/" "directory missing; no tickets to scan"
|
|
215
|
+
else
|
|
216
|
+
# -L follows symlinks (project convention; see AGENTS.md in symlinked projects)
|
|
217
|
+
TASK_FILES=$(find -L "$ROOT_ABS/tasks" -name '*.task-*.md' -type f 2>/dev/null | sort || true)
|
|
218
|
+
if [[ -z "$TASK_FILES" ]]; then
|
|
219
|
+
printf '# (no *.task-*.md files found under tasks/)\n'
|
|
220
|
+
emit_warn "INFO" "tasks/" "no ticket files matching '*.task-*.md' — project may be pre-scaffold"
|
|
221
|
+
else
|
|
222
|
+
while IFS= read -r f; do
|
|
223
|
+
[[ -z "$f" ]] && continue
|
|
224
|
+
rel="${f#$ROOT_ABS/}"
|
|
225
|
+
status=$(extract_status "$f")
|
|
226
|
+
rounds=$(count_rounds "$f")
|
|
227
|
+
blocker=$(blocker_state "$f")
|
|
228
|
+
placeholders=$(count_placeholders "$f")
|
|
229
|
+
warns=$(ticket_warnings "$f")
|
|
230
|
+
|
|
231
|
+
# Promote suspicious combinations into the WARNINGS section
|
|
232
|
+
if [[ "$status" == "DONE" && "$blocker" == "active" ]]; then
|
|
233
|
+
emit_warn "ERROR" "$rel" "status=DONE but unresolved BLOCKER in Execution Log"
|
|
234
|
+
fi
|
|
235
|
+
if [[ "$status" == "DONE" && "$placeholders" -gt 0 ]]; then
|
|
236
|
+
emit_warn "WARN" "$rel" "status=DONE but $placeholders scaffold placeholder(s) remain"
|
|
237
|
+
fi
|
|
238
|
+
if [[ "$status" == "UNKNOWN" ]]; then
|
|
239
|
+
emit_warn "WARN" "$rel" "Meta.Status not parseable (expected '[x] DONE' / '[ ] TODO' / '[~] IN_PROGRESS' / '[!] BLOCKED')"
|
|
240
|
+
fi
|
|
241
|
+
if [[ "$warns" != "-" ]]; then
|
|
242
|
+
emit_warn "WARN" "$rel" "ticket structure: $warns"
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\n' \
|
|
246
|
+
"$rel" "$status" "$rounds" "$blocker" "$placeholders" "$warns"
|
|
247
|
+
done <<< "$TASK_FILES"
|
|
248
|
+
fi
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
# [TRACKERS]
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
printf '\n[TRACKERS]\n'
|
|
256
|
+
printf '# tracker_path\tdone\ttodo\tin_progress\tblocked\ttotal\n'
|
|
257
|
+
|
|
258
|
+
if [[ $has_tasks -eq 0 ]]; then
|
|
259
|
+
printf '# (tasks/ missing)\n'
|
|
260
|
+
else
|
|
261
|
+
TRACKERS=$(find -L "$ROOT_ABS/tasks" -name 'README.md' -type f 2>/dev/null | sort || true)
|
|
262
|
+
if [[ -z "$TRACKERS" ]]; then
|
|
263
|
+
printf '# (no README.md trackers found under tasks/)\n'
|
|
264
|
+
emit_warn "WARN" "tasks/" "no tracker README.md files — convention is tasks/README.md per scope"
|
|
265
|
+
else
|
|
266
|
+
while IFS= read -r tr; do
|
|
267
|
+
[[ -z "$tr" ]] && continue
|
|
268
|
+
rel="${tr#$ROOT_ABS/}"
|
|
269
|
+
# Count tracker rows matching TSK-NN pattern with status cell
|
|
270
|
+
# Match both bare and link form: `| TSK-NN |` or `| [TSK-NN](...) |`
|
|
271
|
+
done_c=$(grep -E '\|[[:space:]]*\[?TSK-[0-9]+.*`?\[x\]`?[[:space:]]+DONE' "$tr" 2>/dev/null | wc -l | tr -d ' ')
|
|
272
|
+
todo_c=$(grep -E '\|[[:space:]]*\[?TSK-[0-9]+.*`?\[ \]`?[[:space:]]+TODO' "$tr" 2>/dev/null | wc -l | tr -d ' ')
|
|
273
|
+
inpg_c=$(grep -E '\|[[:space:]]*\[?TSK-[0-9]+.*`?\[~\]`?[[:space:]]+IN_PROGRESS' "$tr" 2>/dev/null | wc -l | tr -d ' ')
|
|
274
|
+
blkd_c=$(grep -E '\|[[:space:]]*\[?TSK-[0-9]+.*`?\[!\]`?[[:space:]]+BLOCKED' "$tr" 2>/dev/null | wc -l | tr -d ' ')
|
|
275
|
+
total=$((done_c + todo_c + inpg_c + blkd_c))
|
|
276
|
+
if [[ "$total" -eq 0 ]]; then
|
|
277
|
+
# Tracker README might be the top-level overview without ticket rows — fine, but mark with hint
|
|
278
|
+
emit_warn "INFO" "$rel" "no TSK rows detected (top-level overview tracker?)"
|
|
279
|
+
fi
|
|
280
|
+
printf '%s\t%d\t%d\t%d\t%d\t%d\n' "$rel" "$done_c" "$todo_c" "$inpg_c" "$blkd_c" "$total"
|
|
281
|
+
done <<< "$TRACKERS"
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
|
|
285
|
+
# ---------------------------------------------------------------------------
|
|
286
|
+
# [SPECS]
|
|
287
|
+
# ---------------------------------------------------------------------------
|
|
288
|
+
|
|
289
|
+
printf '\n[SPECS]\n'
|
|
290
|
+
printf '# spec_path\tscope\tmodules_listed\tmodules_missing\n'
|
|
291
|
+
|
|
292
|
+
if [[ $has_specs -eq 0 ]]; then
|
|
293
|
+
printf '# (specs/ missing)\n'
|
|
294
|
+
else
|
|
295
|
+
SPECS=$(find -L "$ROOT_ABS/specs" -name '*.spec.md' -type f 2>/dev/null | sort || true)
|
|
296
|
+
if [[ -z "$SPECS" ]]; then
|
|
297
|
+
printf '# (no *.spec.md files under specs/)\n'
|
|
298
|
+
emit_warn "INFO" "specs/" "no spec files — run /sdd-discover to bootstrap"
|
|
299
|
+
else
|
|
300
|
+
while IFS= read -r sp; do
|
|
301
|
+
[[ -z "$sp" ]] && continue
|
|
302
|
+
rel="${sp#$ROOT_ABS/}"
|
|
303
|
+
# Derive scope from path: specs/<scope>/<scope>.spec.md or specs/<scope>/<module>/<module>.spec.md
|
|
304
|
+
scope=$(echo "$rel" | awk -F/ '{print $2}')
|
|
305
|
+
# Count module references: paths like (../<module>/<module>.spec.md) or relative links to *.spec.md
|
|
306
|
+
modules=$(grep -oE '[a-z][a-z0-9-]*\.spec\.md' "$sp" 2>/dev/null | sort -u | wc -l | tr -d ' ')
|
|
307
|
+
# Count missing referenced specs (link target does not exist relative to this spec)
|
|
308
|
+
missing=0
|
|
309
|
+
base_dir=$(dirname "$sp")
|
|
310
|
+
while IFS= read -r link; do
|
|
311
|
+
[[ -z "$link" ]] && continue
|
|
312
|
+
# Resolve relative
|
|
313
|
+
if [[ -e "$base_dir/$link" ]]; then
|
|
314
|
+
:
|
|
315
|
+
else
|
|
316
|
+
missing=$((missing + 1))
|
|
317
|
+
fi
|
|
318
|
+
# Only consider real markdown links: ](path.spec.md...) — preceded by ']'.
|
|
319
|
+
# Skip inline-code paths like (`path.spec.md`) — those are descriptive references, not links.
|
|
320
|
+
done < <(grep -oE '\]\([^)`]+\.spec\.md[^)]*\)' "$sp" 2>/dev/null \
|
|
321
|
+
| sed -E 's/^\]\(//; s/[#)].*$//' | sort -u)
|
|
322
|
+
if [[ "$missing" -gt 0 ]]; then
|
|
323
|
+
emit_warn "ERROR" "$rel" "$missing referenced *.spec.md link(s) do not resolve on disk"
|
|
324
|
+
fi
|
|
325
|
+
printf '%s\t%s\t%d\t%d\n' "$rel" "${scope:-unknown}" "$modules" "$missing"
|
|
326
|
+
done <<< "$SPECS"
|
|
327
|
+
fi
|
|
328
|
+
fi
|
|
329
|
+
|
|
330
|
+
# ---------------------------------------------------------------------------
|
|
331
|
+
# [WARNINGS]
|
|
332
|
+
# ---------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
printf '\n[WARNINGS]\n'
|
|
335
|
+
printf '# severity\tlocation\tmessage\n'
|
|
336
|
+
if [[ -s "$WARN_FILE" ]]; then
|
|
337
|
+
cat "$WARN_FILE"
|
|
338
|
+
else
|
|
339
|
+
printf '# (none)\n'
|
|
340
|
+
fi
|
|
341
|
+
|
|
342
|
+
# ---------------------------------------------------------------------------
|
|
343
|
+
# [SUMMARY]
|
|
344
|
+
# ---------------------------------------------------------------------------
|
|
345
|
+
|
|
346
|
+
# Aggregate from emitted [TASKS] block by re-walking files (cheap, ~ms).
|
|
347
|
+
sum_done=0; sum_todo=0; sum_inpg=0; sum_blkd=0; sum_unk=0
|
|
348
|
+
sum_active_blockers=0; sum_total_tasks=0
|
|
349
|
+
if [[ $has_tasks -eq 1 && -n "${TASK_FILES:-}" ]]; then
|
|
350
|
+
while IFS= read -r f; do
|
|
351
|
+
[[ -z "$f" ]] && continue
|
|
352
|
+
sum_total_tasks=$((sum_total_tasks + 1))
|
|
353
|
+
s=$(extract_status "$f")
|
|
354
|
+
case "$s" in
|
|
355
|
+
DONE) sum_done=$((sum_done+1)) ;;
|
|
356
|
+
TODO) sum_todo=$((sum_todo+1)) ;;
|
|
357
|
+
IN_PROGRESS) sum_inpg=$((sum_inpg+1)) ;;
|
|
358
|
+
BLOCKED) sum_blkd=$((sum_blkd+1)) ;;
|
|
359
|
+
*) sum_unk=$((sum_unk+1)) ;;
|
|
360
|
+
esac
|
|
361
|
+
b=$(blocker_state "$f")
|
|
362
|
+
[[ "$b" == "active" ]] && sum_active_blockers=$((sum_active_blockers+1))
|
|
363
|
+
done <<< "$TASK_FILES"
|
|
364
|
+
fi
|
|
365
|
+
|
|
366
|
+
warn_count=$(grep -E '^WARN' "$WARN_FILE" 2>/dev/null | wc -l | tr -d ' ')
|
|
367
|
+
err_count=$(grep -E '^ERROR' "$WARN_FILE" 2>/dev/null | wc -l | tr -d ' ')
|
|
368
|
+
info_count=$(grep -E '^INFO' "$WARN_FILE" 2>/dev/null | wc -l | tr -d ' ')
|
|
369
|
+
|
|
370
|
+
printf '\n[SUMMARY]\n'
|
|
371
|
+
printf 'tasks_total=%d\n' "$sum_total_tasks"
|
|
372
|
+
printf 'tasks_done=%d\n' "$sum_done"
|
|
373
|
+
printf 'tasks_todo=%d\n' "$sum_todo"
|
|
374
|
+
printf 'tasks_in_progress=%d\n' "$sum_inpg"
|
|
375
|
+
printf 'tasks_blocked=%d\n' "$sum_blkd"
|
|
376
|
+
printf 'tasks_unknown_status=%d\n' "$sum_unk"
|
|
377
|
+
printf 'active_blockers=%d\n' "$sum_active_blockers"
|
|
378
|
+
printf 'warnings=%d\n' "$warn_count"
|
|
379
|
+
printf 'errors=%d\n' "$err_count"
|
|
380
|
+
printf 'info=%d\n' "$info_count"
|
|
381
|
+
|
|
382
|
+
# Exit 0 always when snapshot emitted. WARNINGS/ERRORS are *data* the agent must
|
|
383
|
+
# interpret — they are not invocation failures. Use exit 2 only when we cannot
|
|
384
|
+
# emit a snapshot (handled above).
|
|
385
|
+
exit 0
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @file: SDD command dispatcher — single entry point for all SDD helper scripts.
|
|
3
|
+
# @consumers: phase agents, audit agents, orchestrator skills
|
|
4
|
+
# @contract: AX_BASH_NO_SILENT_EMPTY. Stable surface: `sdd <subcommand> <args>`.
|
|
5
|
+
#
|
|
6
|
+
# Subcommands:
|
|
7
|
+
# sdd extract <file> <NAME> — extract <!--SECTION:NAME--> ... <!--/SECTION:NAME--> block
|
|
8
|
+
# sdd lint <file>... — run gennady DBC AST lint, parse output reliably
|
|
9
|
+
# sdd verify <file>... — comprehensive gate: typecheck + gennady lint + forbidden_grep
|
|
10
|
+
# sdd check-blockers <ticket-file> — scan Execution Log for unresolved BLOCKER entries
|
|
11
|
+
# sdd scan [project-root] — emit comprehensive project snapshot (single rich call)
|
|
12
|
+
# sdd help — print this help
|
|
13
|
+
#
|
|
14
|
+
# Why this wrapper exists:
|
|
15
|
+
# - Single permission rule pattern: `Bash(scripts/sdd/sdd *)` covers ALL SDD operations.
|
|
16
|
+
# - Stable API across project lifetimes — script names underneath can change without breaking agent patterns.
|
|
17
|
+
# - Consistent error contract (AX_BASH_NO_SILENT_EMPTY) across all subcommands.
|
|
18
|
+
#
|
|
19
|
+
# Exit codes (forwarded from subcommand):
|
|
20
|
+
# 0 — success
|
|
21
|
+
# 1-N — subcommand-specific failure (see each subcommand's documented codes)
|
|
22
|
+
# 127 — unknown subcommand
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
PROG="sdd"
|
|
28
|
+
SUBCMD="${1:-help}"
|
|
29
|
+
shift 2>/dev/null || true
|
|
30
|
+
|
|
31
|
+
case "$SUBCMD" in
|
|
32
|
+
extract)
|
|
33
|
+
exec "$SCRIPT_DIR/extract-section.sh" "$@"
|
|
34
|
+
;;
|
|
35
|
+
lint)
|
|
36
|
+
exec "$SCRIPT_DIR/lint-artifacts.sh" "$@"
|
|
37
|
+
;;
|
|
38
|
+
verify)
|
|
39
|
+
exec "$SCRIPT_DIR/verify.sh" "$@"
|
|
40
|
+
;;
|
|
41
|
+
check-blockers)
|
|
42
|
+
exec "$SCRIPT_DIR/check-blockers.sh" "$@"
|
|
43
|
+
;;
|
|
44
|
+
scan)
|
|
45
|
+
exec "$SCRIPT_DIR/scan.sh" "$@"
|
|
46
|
+
;;
|
|
47
|
+
help|--help|-h)
|
|
48
|
+
cat <<'EOF'
|
|
49
|
+
sdd — SDD command dispatcher
|
|
50
|
+
|
|
51
|
+
USAGE
|
|
52
|
+
sdd <subcommand> <args>
|
|
53
|
+
|
|
54
|
+
SUBCOMMANDS
|
|
55
|
+
extract <file> <NAME> Extract anchored section from a markdown file.
|
|
56
|
+
NAME format: ^[A-Z][A-Z0-9_]*$
|
|
57
|
+
Examples: META, PHASES_OVERVIEW, PHASE_P1, BDD, VERIFICATION,
|
|
58
|
+
TEST_COVERAGE, EXECUTION_LOG
|
|
59
|
+
|
|
60
|
+
lint <file>... Run gennady DBC AST lint on listed TypeScript files.
|
|
61
|
+
Output parsed for "[linting → clean]" / "[linting → failed]".
|
|
62
|
+
Exit code reliable (unlike raw gennady).
|
|
63
|
+
|
|
64
|
+
verify <file>... Comprehensive verification gate — runs ALL of:
|
|
65
|
+
(1) npm run typecheck
|
|
66
|
+
(2) gennady DBC lint on listed files
|
|
67
|
+
(3) npm run lint
|
|
68
|
+
(4) npm run test
|
|
69
|
+
(5) npm run format check
|
|
70
|
+
RUN-ALL: every gate executes; failures accumulate.
|
|
71
|
+
SUPPRESS-ON-SUCCESS: passing gates produce zero output.
|
|
72
|
+
On all-pass: single line. On failure: only failed gates
|
|
73
|
+
with captured output shown.
|
|
74
|
+
|
|
75
|
+
scan [root] Emit one rich snapshot of an SDD project:
|
|
76
|
+
[HEADER] [TASKS] [TRACKERS] [SPECS] [WARNINGS] [SUMMARY]
|
|
77
|
+
Designed so triage/check skills make ONE call instead of
|
|
78
|
+
many ad-hoc find/grep commands. Suspicious states
|
|
79
|
+
(DONE+placeholders, DONE+active-blocker, anchor mismatch,
|
|
80
|
+
unparseable Meta.Status, broken spec links) are surfaced
|
|
81
|
+
in [WARNINGS]. Defaults to current directory.
|
|
82
|
+
|
|
83
|
+
check-blockers <ticket> Scan ticket Execution Log for unresolved BLOCKER entries.
|
|
84
|
+
A BLOCKER is considered RESOLVED if a later Round contains
|
|
85
|
+
a "✅ RESOLVED" entry referencing it. Returns list of
|
|
86
|
+
unresolved blockers, or PASS if all resolved / none.
|
|
87
|
+
|
|
88
|
+
help Print this help.
|
|
89
|
+
|
|
90
|
+
PERMISSION HINT
|
|
91
|
+
Single project permission rule covers all subcommands:
|
|
92
|
+
"Bash(scripts/sdd/sdd *)"
|
|
93
|
+
|
|
94
|
+
CONTRACT
|
|
95
|
+
All subcommands honor AX_BASH_NO_SILENT_EMPTY — empty output is never produced;
|
|
96
|
+
instead actionable diagnostic instructions are emitted with non-zero exit.
|
|
97
|
+
EOF
|
|
98
|
+
exit 0
|
|
99
|
+
;;
|
|
100
|
+
"")
|
|
101
|
+
echo "[$PROG] BAD_INVOCATION: no subcommand"
|
|
102
|
+
echo " Run: sdd help"
|
|
103
|
+
exit 127
|
|
104
|
+
;;
|
|
105
|
+
*)
|
|
106
|
+
cat <<EOF
|
|
107
|
+
[$PROG] UNKNOWN_SUBCOMMAND: $SUBCMD
|
|
108
|
+
|
|
109
|
+
Available subcommands: extract, lint, verify, check-blockers, scan, help.
|
|
110
|
+
Run: sdd help
|
|
111
|
+
EOF
|
|
112
|
+
exit 127
|
|
113
|
+
;;
|
|
114
|
+
esac
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @file: Smart verification gate — auto-discovers npm scripts via heuristic, runs them.
|
|
3
|
+
# @consumers: phase agents (STEP_5_VERIFY); orchestrators; CI hooks
|
|
4
|
+
# @contract: AX_BASH_NO_SILENT_EMPTY. All discovered gates MUST pass for exit 0.
|
|
5
|
+
# RUN-ALL: every gate executes regardless of previous failures.
|
|
6
|
+
# SUPPRESS-ON-SUCCESS: passing gates produce zero output; only failures are shown.
|
|
7
|
+
# On all-pass: single line "[verify] ALL_GATES_PASS (N/N)". Exit 0.
|
|
8
|
+
# On any-fail: each failed gate dumps its command + exit code + captured output. Exit 1.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# verify.sh <file1> [<file2> ...]
|
|
12
|
+
#
|
|
13
|
+
# Exit codes:
|
|
14
|
+
# 0 — all gates PASS
|
|
15
|
+
# 1 — one or more gates failed
|
|
16
|
+
# 4 — bad invocation
|
|
17
|
+
# 5 — environment failure
|
|
18
|
+
|
|
19
|
+
set -uo pipefail
|
|
20
|
+
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
22
|
+
PROG="verify"
|
|
23
|
+
|
|
24
|
+
# -----------------------------------------------------------
|
|
25
|
+
# Preflight
|
|
26
|
+
# -----------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
if [[ $# -lt 1 ]]; then
|
|
29
|
+
cat <<EOF
|
|
30
|
+
[$PROG] BAD_INVOCATION
|
|
31
|
+
expected: $PROG <file1> [<file2> ...]
|
|
32
|
+
got: $PROG (no args)
|
|
33
|
+
EOF
|
|
34
|
+
exit 4
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if ! command -v npm &>/dev/null; then
|
|
38
|
+
echo "[$PROG] ENV_MISSING: npm not in PATH"
|
|
39
|
+
exit 5
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if ! command -v node &>/dev/null; then
|
|
43
|
+
echo "[$PROG] ENV_MISSING: node not in PATH"
|
|
44
|
+
exit 5
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
for file in "$@"; do
|
|
48
|
+
if [[ ! -f "$file" ]]; then
|
|
49
|
+
echo "[$PROG] FILE_NOT_FOUND: $file"
|
|
50
|
+
exit 4
|
|
51
|
+
fi
|
|
52
|
+
done
|
|
53
|
+
|
|
54
|
+
# -----------------------------------------------------------
|
|
55
|
+
# Step 1: Discover scripts via heuristic (silent)
|
|
56
|
+
# -----------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
CLASSIFIER="$SCRIPT_DIR/classify-scripts.js"
|
|
59
|
+
|
|
60
|
+
if [[ ! -f "$CLASSIFIER" ]]; then
|
|
61
|
+
echo "[$PROG] ENV_MISSING: classify-scripts.js not found at $CLASSIFIER"
|
|
62
|
+
exit 5
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
classification=$(node "$CLASSIFIER" 2>/dev/null) || {
|
|
66
|
+
echo "[$PROG] ENV_FAIL: classify-scripts.js failed"
|
|
67
|
+
exit 5
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Extract selected scripts: "cls:name" lines
|
|
71
|
+
discovered=$(echo "$classification" | python3 -c "
|
|
72
|
+
import json,sys
|
|
73
|
+
d=json.load(sys.stdin)
|
|
74
|
+
sel=d.get('selected',{})
|
|
75
|
+
for cls in ['typecheck','gennady','lint','test','format']:
|
|
76
|
+
v=sel.get(cls,'')
|
|
77
|
+
if v:
|
|
78
|
+
print(f'{cls}:{v}')
|
|
79
|
+
" 2>/dev/null)
|
|
80
|
+
|
|
81
|
+
if [[ -z "$discovered" ]]; then
|
|
82
|
+
echo "[$PROG] NO_SCRIPTS_DISCOVERED"
|
|
83
|
+
exit 0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# -----------------------------------------------------------
|
|
87
|
+
# Step 2: Run each gate — accumulate failures, suppress success
|
|
88
|
+
# -----------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
total=0
|
|
91
|
+
passed=0
|
|
92
|
+
failures=""
|
|
93
|
+
FILES=("$@") # save original file args for file-specific commands
|
|
94
|
+
|
|
95
|
+
run_cmd() {
|
|
96
|
+
local label="$1"
|
|
97
|
+
local display_name="$2"
|
|
98
|
+
shift 2
|
|
99
|
+
local cmd=("$@")
|
|
100
|
+
|
|
101
|
+
total=$((total + 1))
|
|
102
|
+
|
|
103
|
+
local output exit_code
|
|
104
|
+
set +e
|
|
105
|
+
output=$("${cmd[@]}" 2>&1)
|
|
106
|
+
exit_code=$?
|
|
107
|
+
set -e
|
|
108
|
+
|
|
109
|
+
if [[ $exit_code -eq 0 ]]; then
|
|
110
|
+
passed=$((passed + 1))
|
|
111
|
+
return 0
|
|
112
|
+
else
|
|
113
|
+
failures+=$(printf '\n[%s] ❌ FAIL gate: %s\n command: %s\n exit: %s\n\n--- captured output ---\n%s\n--- end ---\n' "$PROG" "$label" "${cmd[*]}" "$exit_code" "$output")
|
|
114
|
+
return 1
|
|
115
|
+
fi
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Run ALL gates regardless of failures
|
|
119
|
+
while IFS=: read cls name; do
|
|
120
|
+
case "$cls" in
|
|
121
|
+
typecheck) run_cmd "typecheck" "npm run $name" npm run "$name" || true ;;
|
|
122
|
+
gennady) run_cmd "gennady DBC lint" "gennady lint ${#FILES[@]} files" npx tsx ~/Developer/gennady/cli/gennady.ts lint "${FILES[@]}" || true ;;
|
|
123
|
+
lint) run_cmd "lint" "npm run $name" npm run "$name" || true ;;
|
|
124
|
+
test) run_cmd "test" "npm run $name" npm run "$name" || true ;;
|
|
125
|
+
format) run_cmd "format check" "npm run $name" npm run "$name" || true ;;
|
|
126
|
+
esac
|
|
127
|
+
done <<< "$discovered"
|
|
128
|
+
|
|
129
|
+
# -----------------------------------------------------------
|
|
130
|
+
# Report: suppressed on success, full on failure
|
|
131
|
+
# -----------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
if [[ -n "$failures" ]]; then
|
|
134
|
+
echo "$failures"
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
echo "[$PROG] ALL_GATES_PASS ($passed/$total)"
|
|
139
|
+
exit 0
|