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,143 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @file: Run gennady lint on phase artifacts; parse output for success/failure (gennady exit-code unreliable).
|
|
3
|
+
# @consumers: phase agents (STEP_5_VERIFY); audit subagents (STEP_1 pre-pass cross-check)
|
|
4
|
+
# @contract: AX_BASH_NO_SILENT_EMPTY — never produces empty stdout. On miss → actionable instruction.
|
|
5
|
+
#
|
|
6
|
+
# Why this wrapper exists:
|
|
7
|
+
# - gennady CLI lives outside the project (canonical: ~/Developer/gennady/cli/gennady.ts).
|
|
8
|
+
# - gennady requires `node --experimental-strip-types` (Node 22+) — the bare `node` invocation is non-obvious.
|
|
9
|
+
# - gennady returns exit code 0 even when lint reports errors (the failure signal is the literal token
|
|
10
|
+
# "[linting → failed]" in stdout). We must parse output, not trust exit code.
|
|
11
|
+
# - Success signal is the literal token "[linting → clean] no errors".
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# lint-artifacts.sh <file1> [<file2> ...]
|
|
15
|
+
#
|
|
16
|
+
# Exit codes:
|
|
17
|
+
# 0 — all files clean (gennady output contains "[linting → clean]" and NOT "[linting → failed]")
|
|
18
|
+
# 1 — gennady CLI not found / unable to invoke
|
|
19
|
+
# 2 — lint reported errors (one or more violations); full output emitted
|
|
20
|
+
# 3 — ambiguous output (neither "clean" nor "failed" token present); investigate gennady CLI changes
|
|
21
|
+
# 4 — bad invocation (no args)
|
|
22
|
+
|
|
23
|
+
set -uo pipefail
|
|
24
|
+
|
|
25
|
+
PROG="lint-artifacts"
|
|
26
|
+
GENNADY_CLI=~/Developer/gennady/cli/gennady.ts
|
|
27
|
+
|
|
28
|
+
if [[ $# -lt 1 ]]; then
|
|
29
|
+
cat <<EOF
|
|
30
|
+
[$PROG] BAD_INVOCATION
|
|
31
|
+
expected: $PROG <file1> [<file2> ...]
|
|
32
|
+
got: $PROG (no args)
|
|
33
|
+
|
|
34
|
+
Required action (ORCHESTRATOR / phase agent):
|
|
35
|
+
Pass at least one TypeScript file path to lint.
|
|
36
|
+
Typically: phase's Target Files list.
|
|
37
|
+
EOF
|
|
38
|
+
exit 4
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if [[ ! -f "$GENNADY_CLI" ]]; then
|
|
42
|
+
cat <<EOF
|
|
43
|
+
[$PROG] GENNADY_CLI_NOT_FOUND
|
|
44
|
+
expected at: $GENNADY_CLI
|
|
45
|
+
|
|
46
|
+
Diagnosis: the gennady AST DbC linter is unreachable from this environment.
|
|
47
|
+
|
|
48
|
+
Required action (ORCHESTRATOR):
|
|
49
|
+
1. Verify the gennady project is checked out at ~/Developer/gennady.
|
|
50
|
+
2. If gennady moved → update GENNADY_CLI variable in this script.
|
|
51
|
+
3. If on a CI/sandbox without gennady → this is a HARD blocker; phase cannot verify.
|
|
52
|
+
Report to operator: cannot complete phase without DBC contract verification.
|
|
53
|
+
|
|
54
|
+
DO NOT proceed to EMIT_HANDOFF without gennady lint passing.
|
|
55
|
+
EOF
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# Run gennady lint and capture output
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
# Use a temp file to capture full output; we need both grep on it and to emit on failure.
|
|
64
|
+
tmp_out=$(mktemp -t lint-artifacts.XXXXXX)
|
|
65
|
+
trap 'rm -f "$tmp_out"' EXIT
|
|
66
|
+
|
|
67
|
+
# Run gennady. We intentionally ignore its exit code (unreliable per contract above).
|
|
68
|
+
npx tsx "$GENNADY_CLI" lint "$@" > "$tmp_out" 2>&1 || true
|
|
69
|
+
|
|
70
|
+
has_clean=$(grep -c '\[linting → clean\]' "$tmp_out" 2>/dev/null || echo 0)
|
|
71
|
+
has_failed=$(grep -c '\[linting → failed\]' "$tmp_out" 2>/dev/null || echo 0)
|
|
72
|
+
|
|
73
|
+
# Convert to single-line numbers (grep -c with || echo 0 may produce odd output on some shells)
|
|
74
|
+
has_clean=${has_clean//[^0-9]/}
|
|
75
|
+
has_failed=${has_failed//[^0-9]/}
|
|
76
|
+
has_clean=${has_clean:-0}
|
|
77
|
+
has_failed=${has_failed:-0}
|
|
78
|
+
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
# Branch on tokens
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
if [[ "$has_failed" -gt 0 ]]; then
|
|
84
|
+
# Extract just the error lines (those with file:line: error: format) + final summary
|
|
85
|
+
cat <<EOF
|
|
86
|
+
[$PROG] LINT_FAILED
|
|
87
|
+
files lint'd: $#
|
|
88
|
+
gennady output token: [linting → failed]
|
|
89
|
+
|
|
90
|
+
Full lint output (gennady DBC AST findings):
|
|
91
|
+
EOF
|
|
92
|
+
# Print error lines and the failure summary line
|
|
93
|
+
grep -E '\[linting → failed\]|: error: |^---|^References:|^ ' "$tmp_out" | head -200
|
|
94
|
+
echo
|
|
95
|
+
cat <<EOF
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
Required action (PHASE AGENT, before EMIT_HANDOFF):
|
|
99
|
+
1. Read each error: format is <file>:<line>:<col>: error: <ERR_CODE>: <message>
|
|
100
|
+
2. Common categories:
|
|
101
|
+
- ERR_DBC_LINT_PARAM_MISSING — add @param <name> <coherent-role-description>
|
|
102
|
+
- ERR_DBC_LINT_RETURNS_MISSING — add @returns <business-purpose-of-value>
|
|
103
|
+
- ERR_DBC_ORDER — reorder JSDoc tags per AX_BASE_CONTRACT_SHAPE:
|
|
104
|
+
@purpose → @consumers → @implements → @invariant → @pre →
|
|
105
|
+
@param → @throws → @returns → @post → @sideEffect
|
|
106
|
+
3. Fix all violations in the target files.
|
|
107
|
+
4. Re-run this script. Repeat until clean.
|
|
108
|
+
5. Do NOT EMIT_HANDOFF with lint failures present — that is fabricated DONE.
|
|
109
|
+
|
|
110
|
+
References:
|
|
111
|
+
/Users/k.lebedev/Developer/vkt/ai/directives/coding/typescript-rules.xml
|
|
112
|
+
— AX_TAG_USAGE_MATRIX, AX_BASE_CONTRACT_SHAPE, AX_FLAT_JSDOC_FOR_PROPERTIES
|
|
113
|
+
EOF
|
|
114
|
+
exit 2
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
if [[ "$has_clean" -gt 0 ]]; then
|
|
118
|
+
# Clean. Emit concise PASS line to keep parent context light.
|
|
119
|
+
file_count=$#
|
|
120
|
+
echo "[$PROG] LINT_PASS"
|
|
121
|
+
echo " files lint'd: $file_count"
|
|
122
|
+
echo " gennady output token: [linting → clean]"
|
|
123
|
+
exit 0
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Neither token found — gennady output format may have changed.
|
|
127
|
+
cat <<EOF
|
|
128
|
+
[$PROG] AMBIGUOUS_OUTPUT
|
|
129
|
+
files lint'd: $#
|
|
130
|
+
gennady output token: (neither "linting → clean" nor "linting → failed" found)
|
|
131
|
+
|
|
132
|
+
Diagnosis: gennady CLI output format may have changed (or there is an unhandled edge case).
|
|
133
|
+
|
|
134
|
+
Captured output:
|
|
135
|
+
$(cat "$tmp_out" | head -50)
|
|
136
|
+
|
|
137
|
+
Required action (ORCHESTRATOR):
|
|
138
|
+
1. Re-read gennady source: ~/Developer/gennady/cli/gennady.ts
|
|
139
|
+
2. Check command output tokens in the LintCommand implementation.
|
|
140
|
+
3. Update this script's parsing to match new tokens.
|
|
141
|
+
4. Until resolved → treat as a HARD blocker; DO NOT assume PASS.
|
|
142
|
+
EOF
|
|
143
|
+
exit 3
|
|
@@ -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
|