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,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sdd-critic
|
|
3
|
+
description: Autonomous multi-round critique of SDD artifacts — scope specs or task tickets. Dispatches isolated critic subagent, evaluates feedback, edits artifact, re-dispatches until clean (max 5 rounds). Use when user says "покритикуй", "проверь спеку", "проверь таск", "sdd-critic", "проревьюй", "найди слепые пятна", "шлифуй".
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: opencode
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
1. Extract intent. Resolve artifact path — spec or task. If ambiguous, ask.
|
|
9
|
+
|
|
10
|
+
2. Load directive: `ai/directives/sdd/critic.directive.xml`. Announce: `🔒 SddCritic`. You ARE this directive.
|
|
11
|
+
|
|
12
|
+
3. Apply. Follow Execution_Plan. Do not deviate.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sdd-discover
|
|
3
|
+
description: Start or evolve a scope spec — for any scope-type (infrastructure, contracts, library, product). Creates specs/<scope>/<scope>.spec.md. Use when designing a new scope or refining/pivoting an existing scope's vision, requirements, or architecture. Modes auto-detected: greenfield (no spec), refine (add to existing), pivot (replace decisions).
|
|
4
|
+
compatibility: opencode
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
1. **Extract intent.** Operator wants {greenfield | refine | pivot} for scope `<name>` (`<scope-type>` if greenfield). If `<name>` missing — ask.
|
|
8
|
+
|
|
9
|
+
2. **Load & activate directive.** Read in full: `~/Developer/gennady/ai/directives/sdd/discovery.directive.xml`
|
|
10
|
+
Announce: `🔒 DIRECTIVE ACTIVATED: SddDiscovery`
|
|
11
|
+
You ARE this directive now.
|
|
12
|
+
|
|
13
|
+
3. **Apply directive to intent.** Mode auto-detected per `AX_MODE_AUTO_DETECT_OR_HALT`. Follow Execution_Plan end-to-end. Do not deviate.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sdd-execute
|
|
3
|
+
description: Execute ONE task ticket end-to-end. Reads ticket header + Phases Overview + Execution Log to plan; dispatches one phase-subagent per pending phase (sequential), threading typed Handoff between them; closes Round and dispatches audit-subagent (fresh-eyes). On audit FAIL — re-runs ONLY failing-phases as `fix` kind, max 1 retry. Use when operator passes a Task-ID, ticket path, or "next" / "следующую" / "выбери" / "pick one".
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: opencode
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<SddExecuteOrchestrator role="orchestrator-only">
|
|
9
|
+
You are an ORCHESTRATOR. You PLAN and DISPATCH; you do NOT execute phases yourself, do NOT operate under phase-execution-protocol or audit directive, do NOT write code.
|
|
10
|
+
|
|
11
|
+
**Environment: macOS.** All bash commands dispatched to subagents must be macOS-compatible. No `grep -P` → use `rg`. No GNU-only flags.
|
|
12
|
+
|
|
13
|
+
You DO read the ticket — but only its planning surface: section 1 Meta, section 2 Phases Overview, current Round of section 7 Execution Log. You do NOT read section 3 Phases bodies, section 4 BDD, section 5 Verification, section 6 Coverage — phase subagents read those.
|
|
14
|
+
|
|
15
|
+
Each subagent runs in a FRESH ISOLATED CONTEXT.
|
|
16
|
+
|
|
17
|
+
<ProgressReporting>
|
|
18
|
+
Black-box: subagents are opaque to the orchestrator. Emit one progress line per state transition.
|
|
19
|
+
|
|
20
|
+
Format: `[<bar>] <pct>% | TSK-NN | <stage>`
|
|
21
|
+
|
|
22
|
+
Stages:
|
|
23
|
+
|
|
24
|
+
- `0% ⏳ resolving + planning`
|
|
25
|
+
- `<pct>% 🔧 phase <P<N>> (<kind>) executing`
|
|
26
|
+
- `<pct>% ✅ phase <P<N>> done → next`
|
|
27
|
+
- `<pct>% ✅ all phases done → 🔍 audit`
|
|
28
|
+
- `100% ✅ audit PASS`
|
|
29
|
+
|
|
30
|
+
Retry path:
|
|
31
|
+
|
|
32
|
+
- `<pct>% ❌ audit FAIL → 🔄 re-run phases <list> as fix`
|
|
33
|
+
- `<pct>% ✅ phases re-run done → 🔍 audit R2`
|
|
34
|
+
- `100% ✅ audit R2 PASS — OR — ❌ FAIL after 2 attempts (manual intervention)`
|
|
35
|
+
|
|
36
|
+
Pause path (distinguish from failure — skill is awaiting operator, not broken):
|
|
37
|
+
|
|
38
|
+
- `<pct>% ✋ PAUSED awaiting operator decision` — unresolved BLOCKER in Execution Log per AX_BLOCKER_RESOLUTION_TRAIL, or phase returned BLOCKED. NOT a skill failure. Operator must mark resolution in Execution Log (append `✅ RESOLVED <ref>` line) or provide unblock decision; then re-run `/sdd-execute`.
|
|
39
|
+
</ProgressReporting>
|
|
40
|
+
|
|
41
|
+
<Protocol>
|
|
42
|
+
1. **Resolve task:**
|
|
43
|
+
- Operator passed Task-ID or ticket path → use it.
|
|
44
|
+
- Operator said "next" / "следующую" / "выбери" / "pick one" / no pointer → read tasks/README.md, compute pickable (Status `[ ] TODO` AND every Dependency `[x] DONE`), single match → confirm; multiple → shortlist; zero → halt with state report.
|
|
45
|
+
- No `tasks/` directory → halt: "No tasks/ — wrong cwd or scaffolding not done".
|
|
46
|
+
|
|
47
|
+
2. **Plan:** Read ONLY ticket sections 1, 2, and 7-current-Round.
|
|
48
|
+
- **Preflight: scan for unresolved blockers** (per `AX_BLOCKER_RESOLUTION_TRAIL`). Skill ships its own helper scripts at `~/Developer/gennady/ai/skills/sdd-execute/scripts/`. Run `~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd check-blockers <ticket-path>`.
|
|
49
|
+
- exit 0 (CLEAR) → continue to state detection.
|
|
50
|
+
- exit 2 (UNRESOLVED_BLOCKERS) → emit **✋ AWAITING OPERATOR DECISION** message with the script's output, then halt. This is a PAUSED state, NOT a skill failure — be clear in the message. Operator must either (a) mark resolution in Execution Log if blocker is no longer active, or (b) provide unblock decision.
|
|
51
|
+
- State detection from Phases Overview Status column:
|
|
52
|
+
- all `[ ]` → fresh task; plan = all phases in declared order respecting `Deps`.
|
|
53
|
+
- some `[x]`, some `[ ]` → resume; plan = remaining phases in declared order.
|
|
54
|
+
- all `[x]` AND no audit yet → plan = audit only.
|
|
55
|
+
- all `[x]` AND audit PASS in current Round → halt: "nothing to do".
|
|
56
|
+
- any `[!] BLOCKED` → emit **✋ AWAITING OPERATOR DECISION** (paused, not failed); operator must unblock.
|
|
57
|
+
- Open new Round in section 7 if this is a fresh attempt or resume after closure: append `### Round N — <YYYY-MM-DD>, <reason>`. Reason for Round 1 = `initial`. Subsequent rounds: `audit-driven fix`, `late-detected bug`, etc.
|
|
58
|
+
|
|
59
|
+
3. **Phase dispatch loop** — sequential, one phase at a time:
|
|
60
|
+
|
|
61
|
+
For each phase in plan:
|
|
62
|
+
|
|
63
|
+
a. Dispatch PHASE subagent (`subagent_type: general-purpose`, **`model: "sonnet"`** — phase work requires capable code-generation model; haiku-class is insufficient for structured JSDoc/anchor discipline and was empirically observed to skip closing anchors and put @param on type declarations), fresh context, with prompt:
|
|
64
|
+
|
|
65
|
+
````
|
|
66
|
+
Step 1 — Read the directive. Use Read tool directly on:
|
|
67
|
+
ai/directives/sdd/phase-execution-protocol.xml
|
|
68
|
+
On failure → halt, report exact path.
|
|
69
|
+
|
|
70
|
+
Step 2 — Activate. Announce: "🔒 DIRECTIVE ACTIVATED: SddPhaseExecution"
|
|
71
|
+
You ARE this directive.
|
|
72
|
+
|
|
73
|
+
Step 3 — Apply to intent.
|
|
74
|
+
Ticket: <absolute ticket path>
|
|
75
|
+
Phase: <P<N>>, kind: <kind>
|
|
76
|
+
Reason: <"initial" | "fix: address audit findings F-NNN, F-MMM" | "resume after blocker">
|
|
77
|
+
Inputs: <verbatim prior Handoff lines OR "none — first phase">
|
|
78
|
+
SDD tooling available at: ~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd
|
|
79
|
+
(run "~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd help" for surface; use these
|
|
80
|
+
for any extraction/lint/verify operations the directive references).
|
|
81
|
+
MANDATORY before EMIT_HANDOFF: sdd verify <target-files> — auto-discovers and runs
|
|
82
|
+
typecheck, gennady lint, linter, tests, and format check for the project.
|
|
83
|
+
|
|
84
|
+
Follow the directive. This ONE phase only. Do not invoke audit.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
b. Branch on phase status:
|
|
88
|
+
- `BLOCKED` or `FAIL` → STOP loop. Audit not invoked (round not closed). Show operator the blocker. Done.
|
|
89
|
+
- `DONE` → record Handoff (artifacts, decisions, open). Continue to next phase.
|
|
90
|
+
|
|
91
|
+
c. Thread next phase's Inputs from this phase's Handoff (verbatim).
|
|
92
|
+
|
|
93
|
+
````
|
|
94
|
+
|
|
95
|
+
4. **Close Round** — append to ticket section 7:
|
|
96
|
+
```
|
|
97
|
+
#### Round close
|
|
98
|
+
- [x] `<ts>` sync <scope>+root
|
|
99
|
+
- [x] `<ts>` DONE
|
|
100
|
+
```
|
|
101
|
+
Set ticket Meta Status → `[x] DONE`.
|
|
102
|
+
|
|
103
|
+
4a. **Sync Trackers** (MANDATORY, cannot skip):
|
|
104
|
+
|
|
105
|
+
- Read `tasks/<scope>/README.md`. Find the Tracker row for this Task-ID. Set its Status → `[x] DONE`. Write back.
|
|
106
|
+
- Read `tasks/README.md` Tracker Index. Update the scope's aggregate counts (done/total). Write back.
|
|
107
|
+
- Verify: re-read both files, confirm the changes took effect. If not → retry once.
|
|
108
|
+
|
|
109
|
+
5. **Dispatch AUDIT** (MANDATORY, always runs). Dispatch ONE subagent (`subagent_type: general-purpose`, **`model: "haiku"`** — audit is mechanical verification + fact-checking against artifacts; sonnet capability is overkill, haiku is faster and cheaper for this read-heavy task). Include in prompt the SDD tooling location: `~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd` (audit may use `lint`, `verify`, `check-blockers` subcommands). With this prompt:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Step 1 — Read the directive. Use Read tool directly on:
|
|
113
|
+
ai/directives/sdd/audit.directive.xml
|
|
114
|
+
On failure → halt, report exact path.
|
|
115
|
+
|
|
116
|
+
Step 2 — Activate. Announce: "🔒 DIRECTIVE ACTIVATED: SddAudit"
|
|
117
|
+
You ARE this directive. Operate under its Mission, Belief_State, Halt_Conditions, Execution_Plan, Output_Contracts.
|
|
118
|
+
|
|
119
|
+
Step 3 — Apply to intent.
|
|
120
|
+
Task: <TSK-NN>
|
|
121
|
+
Ticket: <absolute ticket path>
|
|
122
|
+
Round: <N>
|
|
123
|
+
Artifacts: <union of all phase Handoff artifacts>
|
|
124
|
+
Mode: per-task
|
|
125
|
+
|
|
126
|
+
Follow the directive's Execution_Plan. Report findings per AUDIT_SESSION_SUMMARY_FORMAT.
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Wait for return. If dispatch fails → retry once. If fails again → mark task FAILED.
|
|
130
|
+
|
|
131
|
+
6. **Branch on audit status:**
|
|
132
|
+
- `PASS` or `PASS_WITH_ACKNOWLEDGED_RISKS` → ticket verified; jump to step 9 (summary).
|
|
133
|
+
- `FAIL` AND `audit_attempt = 1` → step 7 (resolve findings).
|
|
134
|
+
- `FAIL` AND `audit_attempt = 2` → STOP; jump to step 9 (summary with FAIL after 2 attempts).
|
|
135
|
+
|
|
136
|
+
7. **Resolve audit findings (max one retry, total 2 audit attempts):**
|
|
137
|
+
|
|
138
|
+
Two resolution paths depending on `phases_to_fix`:
|
|
139
|
+
|
|
140
|
+
**Path A — phases_to_fix is non-empty** (findings require code changes):
|
|
141
|
+
Show: "Audit FAIL: <top findings>. Auto re-running phases <phases_to_fix> as fix."
|
|
142
|
+
Open new Round: `### Round N — <YYYY-MM-DD>, audit-driven fix: F-NNN, F-MMM`.
|
|
143
|
+
For each phase in `phases_to_fix` (sequential, declared-order subset):
|
|
144
|
+
Dispatch PHASE subagent (fresh context) with same shape as step 3a, but: - Reason: `fix: address audit findings F-NNN, F-MMM` - Inputs include audit findings relevant to this phase's Target Files.
|
|
145
|
+
Wait. BLOCKED/FAIL → STOP; jump to step 9. DONE → continue.
|
|
146
|
+
After all fix phases DONE → close Round (step 4) → dispatch AUDIT R2 → return to step 6.
|
|
147
|
+
|
|
148
|
+
**Path B — phases_to_fix is EMPTY** (findings are ticket-update / spec-edit only):
|
|
149
|
+
Do NOT open a new Round. Apply fixes DIRECTLY:
|
|
150
|
+
- For each finding with `route=ticket-update`: edit the ticket file yourself (add missing intro/cov lines, fix tracker sync, update Meta fields).
|
|
151
|
+
- For each finding with `route=spec-edit`: edit the spec file yourself (update entity surface, DbC contract, add missing parameter).
|
|
152
|
+
- For each finding with `route=decision-log`: add Decision Log entry to scope spec.
|
|
153
|
+
After all fixes applied → dispatch AUDIT subagent again (round 2, fresh context). Return to step 6.
|
|
154
|
+
Rationale: these are paper-fixes that don't need a phase subagent. Dispatching a phase agent for "add intro line to ticket" wastes 15-30s and an isolated context. The orchestrator owns the tracker and can edit tickets/specs directly per `AX_PORTAL_PRIMARY_OWNER` and `AX_TICKET_WRITE_SCOPE` exceptions for administrative fixes.
|
|
155
|
+
|
|
156
|
+
8. **Aggregate TELEMETRY and present to operator:**
|
|
157
|
+
Collect TELEMETRY blocks from all phase and audit subagents. Compute:
|
|
158
|
+
- `wall_total` = sum of all phase `wall_ms` + audit time
|
|
159
|
+
- `tools_total` = sum of all `tools`
|
|
160
|
+
- Aggregate problems from all phases
|
|
161
|
+
- List any phase where `ok=false` with the `why` explanation
|
|
162
|
+
|
|
163
|
+
9. **Final summary to operator:**
|
|
164
|
+
|
|
165
|
+
````
|
|
166
|
+
📊 Task <TSK-NN> — Execute + Audit Summary
|
|
167
|
+
|
|
168
|
+
🔧 Round 1 phases:
|
|
169
|
+
P1 (impl) ✅ DONE | 4.2s | r=5 b=2 w=1 | ok=true
|
|
170
|
+
P2 (test) ✅ DONE | 8.1s | r=7 b=3 w=2 | problems=P1:test-failed-on-first-run
|
|
171
|
+
🔍 Round 1 audit: <✅ PASS | ❌ FAIL — 🔴 N · 🟠 M>
|
|
172
|
+
audit: ✅ PASS | 3.5s | r=8 b=4 w=0
|
|
173
|
+
|
|
174
|
+
🔄 Round 2 (if any):
|
|
175
|
+
P2 (fix) ✅ DONE | 2.1s | r=3 b=1 w=1 | ok=true
|
|
176
|
+
🔍 Round 2 audit: ✅ PASS
|
|
177
|
+
|
|
178
|
+
📊 Final: <✅ PASS | ⚠️ PASS_RISK | ❌ FAIL>
|
|
179
|
+
|
|
180
|
+
⏱️ Total wall time: 17.9s across 4 subagents
|
|
181
|
+
|
|
182
|
+
📍 Files touched:
|
|
183
|
+
✏️ src/foo.ts (2 edits)
|
|
184
|
+
✏️ src/foo.test.ts (3 edits)
|
|
185
|
+
|
|
186
|
+
⚠️ Problems encountered:
|
|
187
|
+
P1: test failed on first run — missing null guard, fixed in same phase
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
</Protocol>
|
|
191
|
+
````
|
|
192
|
+
|
|
193
|
+
<HardForbidden>
|
|
194
|
+
- Reading phase-execution-protocol.xml or audit.directive.xml yourself. (Subagents do.)
|
|
195
|
+
- Reading ticket sections 3 (Phases bodies), 4 (BDD), 5 (Verification), 6 (Coverage). Phase subagents do.
|
|
196
|
+
- Reading specs, rule files, or code. (Subagents do.)
|
|
197
|
+
- Writing code, audit reports, or phase blocks in Execution Log. (Subagents do.)
|
|
198
|
+
- Skipping audit after all phases DONE. Audit dispatch is mandatory; this is the safety net.
|
|
199
|
+
- Sharing context between phase subagents and audit subagent. Each gets a fresh prompt; orchestrator threads only typed Handoff payloads.
|
|
200
|
+
- Audit retry beyond 2 total attempts. Hard cap.
|
|
201
|
+
- Re-running phases not flagged in `phases_to_fix`. The map finding-location → phase is the contract; do not "just re-run everything".
|
|
202
|
+
- Auto-reopening on phase BLOCKED/FAIL. Only on audit FAIL after all phases DONE the retry kicks in.
|
|
203
|
+
- Parallel dispatch of phases of the SAME task. Phases are sequential by declared `Deps`. Cross-task parallelism is the job of `sdd-execute-batch`.
|
|
204
|
+
</HardForbidden>
|
|
205
|
+
</SddExecuteOrchestrator>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# sdd-execute / scripts
|
|
2
|
+
|
|
3
|
+
Helper scripts bundled with the `sdd-execute` skill. They live alongside the skill, not in any project — invoke via absolute path `~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd`.
|
|
4
|
+
|
|
5
|
+
All scripts honor the contract: **never produce silent empty output**. On miss, they emit actionable diagnostic instructions and a non-zero exit code. Phase agents and audit agents can therefore rely on either content-on-stdout (exit 0) or instruction-on-stdout (exit ≠ 0) to drive their next decision.
|
|
6
|
+
|
|
7
|
+
## Entry point — `sdd` dispatcher
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd help # surface
|
|
11
|
+
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd extract <file> <SECTION_NAME> # extract anchored section
|
|
12
|
+
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd lint <file>... # gennady DBC AST contract lint
|
|
13
|
+
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd verify <file>... # comprehensive gate (typecheck + lint + grep)
|
|
14
|
+
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd check-blockers <ticket-file> # scan Execution Log per AX_BLOCKER_RESOLUTION_TRAIL
|
|
15
|
+
~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd scan [project-root] # one-shot rich snapshot for triage skills
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Single permission rule covers all subcommands:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
"Bash(~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd *)"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Or broader:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
"Bash(~/Developer/gennady/ai/skills/sdd-execute/scripts/*)"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Files
|
|
31
|
+
|
|
32
|
+
| File | Purpose |
|
|
33
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `sdd` | Command dispatcher (single entry point) |
|
|
35
|
+
| `extract-section.sh` | Extract `<!--SECTION:NAME-->...<!--/SECTION:NAME-->` block from markdown |
|
|
36
|
+
| `lint-artifacts.sh` | Run gennady DBC AST contract lint; parse output reliably |
|
|
37
|
+
| `verify.sh` | Three-gate verification: typecheck + gennady DBC lint + forbidden-construct grep |
|
|
38
|
+
| `check-blockers.sh` | Detect unresolved BLOCKER entries in ticket Execution Log per `AX_BLOCKER_RESOLUTION_TRAIL` |
|
|
39
|
+
| `scan.sh` | Emit comprehensive project snapshot ([HEADER]/[TASKS]/[TRACKERS]/[SPECS]/[WARNINGS]/[SUMMARY]). Designed so triage skills make ONE call instead of many ad-hoc find/grep. Surfaces suspicious states automatically (DONE+placeholders, DONE+active-blocker, anchor mismatch, unparseable Status, broken spec links). |
|
|
40
|
+
|
|
41
|
+
## Anchor convention (used by `extract`)
|
|
42
|
+
|
|
43
|
+
Markdown sections delimited by HTML-comment anchors:
|
|
44
|
+
|
|
45
|
+
```markdown
|
|
46
|
+
<!--SECTION:META-->
|
|
47
|
+
|
|
48
|
+
## 1. Meta
|
|
49
|
+
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
<!--/SECTION:META-->
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Grammar:
|
|
56
|
+
|
|
57
|
+
- Open: `<!--SECTION:<NAME>-->`
|
|
58
|
+
- Close: `<!--/SECTION:<NAME>-->`
|
|
59
|
+
- `<NAME>` matches `^[A-Z][A-Z0-9_]*$`
|
|
60
|
+
|
|
61
|
+
Canonical names: `META`, `PHASES_OVERVIEW`, `PHASE_P<N>`, `BDD`, `VERIFICATION`, `TEST_COVERAGE`, `EXECUTION_LOG`.
|
|
62
|
+
|
|
63
|
+
## Blocker resolution convention (used by `check-blockers`)
|
|
64
|
+
|
|
65
|
+
A BLOCKER entry (line containing `🛑` and `BLOCKED`) is considered RESOLVED if a later Round entry contains both `✅` and `RESOLVED`. The script verifies the latest marker is `✅` and counts match, otherwise reports unresolved.
|
|
66
|
+
|
|
67
|
+
To resolve a blocker, append to ticket Execution Log:
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
### Round N — <date>, <reason>
|
|
71
|
+
|
|
72
|
+
- ✅ `<timestamp>` RESOLVED Round M BLOCKER (<short reference>): <reason for resolution>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Project-agnostic by design
|
|
76
|
+
|
|
77
|
+
These scripts know nothing about any specific project. They expect:
|
|
78
|
+
|
|
79
|
+
- A markdown file with anchored sections (for `extract`).
|
|
80
|
+
- A TypeScript file to lint (for `lint`).
|
|
81
|
+
- A ticket file with `## 7. Execution Log` section (for `check-blockers`).
|
|
82
|
+
- A working directory with `npm` and `tsc` available (for `verify`).
|
|
83
|
+
|
|
84
|
+
If the project doesn't satisfy these expectations, scripts emit actionable diagnostics; they never silently fail.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# @file: Scan ticket Execution Log for unresolved BLOCKER entries.
|
|
3
|
+
# @consumers: orchestrator (preflight before phase dispatch); audit; CI hooks.
|
|
4
|
+
# @contract: AX_BASH_NO_SILENT_EMPTY. Implements AX_BLOCKER_RESOLUTION_TRAIL —
|
|
5
|
+
# BLOCKER from earlier Round is ACTIVE unless a later Round explicitly
|
|
6
|
+
# marks it RESOLVED.
|
|
7
|
+
#
|
|
8
|
+
# Heuristic detection:
|
|
9
|
+
# - Lines containing both "🛑" AND "BLOCKED" → blocker entry (with line number, context).
|
|
10
|
+
# - Lines containing both "✅" AND "RESOLVED" → resolution marker.
|
|
11
|
+
# - If RESOLVED markers count >= BLOCKED markers count, AND the last 🛑/✅ marker
|
|
12
|
+
# in the log is ✅ → all blockers considered resolved.
|
|
13
|
+
# - Otherwise → list latest blocker entries as ACTIVE; orchestrator must decide.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# check-blockers.sh <ticket-file>
|
|
17
|
+
#
|
|
18
|
+
# Exit codes:
|
|
19
|
+
# 0 — no unresolved blockers (clear to dispatch)
|
|
20
|
+
# 2 — one or more unresolved blockers (orchestrator MUST resolve before dispatch)
|
|
21
|
+
# 4 — bad invocation
|
|
22
|
+
# 1 — file not found
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
PROG="check-blockers"
|
|
27
|
+
|
|
28
|
+
if [[ $# -ne 1 ]]; then
|
|
29
|
+
cat <<EOF
|
|
30
|
+
[$PROG] BAD_INVOCATION
|
|
31
|
+
expected: $PROG <ticket-file>
|
|
32
|
+
got: $PROG $*
|
|
33
|
+
|
|
34
|
+
Required action (ORCHESTRATOR):
|
|
35
|
+
Pass exactly one ticket file path.
|
|
36
|
+
EOF
|
|
37
|
+
exit 4
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
ticket="$1"
|
|
41
|
+
|
|
42
|
+
if [[ ! -f "$ticket" ]]; then
|
|
43
|
+
cat <<EOF
|
|
44
|
+
[$PROG] FILE_NOT_FOUND
|
|
45
|
+
file: $ticket
|
|
46
|
+
|
|
47
|
+
Required action:
|
|
48
|
+
Verify ticket path; task may need re-resolution.
|
|
49
|
+
EOF
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Locate Execution Log section start. Convention: "## 7. Execution Log".
|
|
54
|
+
log_start=$(grep -n "^## 7\. Execution Log" "$ticket" | head -1 | cut -d: -f1)
|
|
55
|
+
if [[ -z "$log_start" ]]; then
|
|
56
|
+
cat <<EOF
|
|
57
|
+
[$PROG] NO_EXECUTION_LOG
|
|
58
|
+
file: $ticket
|
|
59
|
+
|
|
60
|
+
The ticket lacks a "## 7. Execution Log" section. Either the ticket is malformed
|
|
61
|
+
or uses a different section number/title.
|
|
62
|
+
|
|
63
|
+
Required action (ORCHESTRATOR):
|
|
64
|
+
Verify ticket structure follows scaffold.directive convention. If structure is
|
|
65
|
+
correct but section title differs, update this script's heuristic accordingly.
|
|
66
|
+
|
|
67
|
+
Treating as clear (no blockers detectable).
|
|
68
|
+
EOF
|
|
69
|
+
exit 0
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Extract Execution Log content
|
|
73
|
+
log_content=$(tail -n +"$log_start" "$ticket")
|
|
74
|
+
|
|
75
|
+
# Find blocker and resolution lines (with line numbers).
|
|
76
|
+
# Use awk with proper line numbering, output to temp files for portability with macOS bash 3.2 (no mapfile).
|
|
77
|
+
blocker_lines=()
|
|
78
|
+
resolved_lines=()
|
|
79
|
+
blocker_count=0
|
|
80
|
+
resolved_count=0
|
|
81
|
+
|
|
82
|
+
while IFS= read -r line; do
|
|
83
|
+
[[ -n "$line" ]] && blocker_lines+=("$line") && blocker_count=$((blocker_count + 1))
|
|
84
|
+
done < <(grep -n "🛑.*BLOCKED\|BLOCKED.*🛑" "$ticket" 2>/dev/null | awk -F: -v start="$log_start" '$1 >= start { print $0 }')
|
|
85
|
+
|
|
86
|
+
while IFS= read -r line; do
|
|
87
|
+
[[ -n "$line" ]] && resolved_lines+=("$line") && resolved_count=$((resolved_count + 1))
|
|
88
|
+
done < <(grep -n "✅.*RESOLVED\|RESOLVED.*✅" "$ticket" 2>/dev/null | awk -F: -v start="$log_start" '$1 >= start { print $0 }')
|
|
89
|
+
|
|
90
|
+
# Determine last marker (BLOCKER or RESOLUTION) by line number
|
|
91
|
+
last_blocker_line=0
|
|
92
|
+
last_resolved_line=0
|
|
93
|
+
if [[ $blocker_count -gt 0 ]]; then
|
|
94
|
+
last_blocker_line=$(echo "${blocker_lines[$((blocker_count - 1))]}" | cut -d: -f1)
|
|
95
|
+
fi
|
|
96
|
+
if [[ $resolved_count -gt 0 ]]; then
|
|
97
|
+
last_resolved_line=$(echo "${resolved_lines[$((resolved_count - 1))]}" | cut -d: -f1)
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
# Verdict
|
|
102
|
+
# ---------------------------------------------------------------------------
|
|
103
|
+
|
|
104
|
+
if [[ $blocker_count -eq 0 ]]; then
|
|
105
|
+
cat <<EOF
|
|
106
|
+
[$PROG] CLEAR
|
|
107
|
+
ticket: $ticket
|
|
108
|
+
blockers found: 0
|
|
109
|
+
status: no BLOCKER entries in Execution Log; clear to dispatch phase.
|
|
110
|
+
EOF
|
|
111
|
+
exit 0
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
if [[ $resolved_count -ge $blocker_count ]] && [[ $last_resolved_line -gt $last_blocker_line ]]; then
|
|
115
|
+
cat <<EOF
|
|
116
|
+
[$PROG] CLEAR
|
|
117
|
+
ticket: $ticket
|
|
118
|
+
blockers found: $blocker_count
|
|
119
|
+
resolutions: $resolved_count
|
|
120
|
+
last marker: ✅ RESOLVED on line $last_resolved_line
|
|
121
|
+
status: AX_BLOCKER_RESOLUTION_TRAIL satisfied — all blockers resolved.
|
|
122
|
+
EOF
|
|
123
|
+
exit 0
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Unresolved blockers — emit list
|
|
127
|
+
cat <<EOF
|
|
128
|
+
[$PROG] UNRESOLVED_BLOCKERS
|
|
129
|
+
ticket: $ticket
|
|
130
|
+
blockers found: $blocker_count
|
|
131
|
+
resolutions: $resolved_count
|
|
132
|
+
last 🛑 line: $last_blocker_line
|
|
133
|
+
last ✅ line: $last_resolved_line
|
|
134
|
+
status: AX_BLOCKER_RESOLUTION_TRAIL violated — latest marker is BLOCKER, not RESOLUTION.
|
|
135
|
+
|
|
136
|
+
Active BLOCKER entries (last 5):
|
|
137
|
+
EOF
|
|
138
|
+
# Print last 5 elements portably (no slicing syntax that requires bash 4)
|
|
139
|
+
start_idx=$((blocker_count - 5))
|
|
140
|
+
[[ $start_idx -lt 0 ]] && start_idx=0
|
|
141
|
+
i=$start_idx
|
|
142
|
+
while [[ $i -lt $blocker_count ]]; do
|
|
143
|
+
echo " ${blocker_lines[$i]}" | cut -c1-200
|
|
144
|
+
i=$((i + 1))
|
|
145
|
+
done
|
|
146
|
+
|
|
147
|
+
cat <<EOF
|
|
148
|
+
|
|
149
|
+
Required action (ORCHESTRATOR):
|
|
150
|
+
1. Read the listed BLOCKER entries in the ticket.
|
|
151
|
+
2. Verify whether each blocker is still ACTIVE in current environment:
|
|
152
|
+
- If actually still blocking → escalate to operator with unblock options.
|
|
153
|
+
- If actually resolved (e.g. environment fixed, decision made) → append
|
|
154
|
+
Round N entry with "✅ RESOLVED <Round-M BLOCKER ref>: <reason>" line
|
|
155
|
+
to Execution Log; then re-run this script.
|
|
156
|
+
3. DO NOT dispatch phase agent while any blocker is unresolved.
|
|
157
|
+
EOF
|
|
158
|
+
exit 2
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @file: Heuristic npm script classifier — reads package.json, outputs discovered commands as JSON.
|
|
3
|
+
// @consumers: verify.sh
|
|
4
|
+
// @tasks: TSK-55, TSK-61
|
|
5
|
+
//
|
|
6
|
+
// Usage: node classify-scripts.js [project-root]
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
|
|
11
|
+
function hasCategory(body, category) {
|
|
12
|
+
const patterns = {
|
|
13
|
+
tsc: /\b(tsc|tsgo)\b/,
|
|
14
|
+
lint: /\b(eslint|mail-core-lint\b|biome check\b|standard\b)\b/,
|
|
15
|
+
test: /\b(jest|vitest|playwright test|mocha\b|--test\b)\b/,
|
|
16
|
+
format: /\b(prettier|biome format)\b/,
|
|
17
|
+
};
|
|
18
|
+
return patterns[category] ? patterns[category].test(body) : false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function classify(name, body) {
|
|
22
|
+
const isUmbrellaName = /^(check|ci-check|check:all|verify)$/.test(name);
|
|
23
|
+
if (isUmbrellaName && body.length > 0) return ['umbrella'];
|
|
24
|
+
|
|
25
|
+
const tc = hasCategory(body, 'tsc') ? 1 : 0;
|
|
26
|
+
const lc = hasCategory(body, 'lint') ? 1 : 0;
|
|
27
|
+
const sc = hasCategory(body, 'test') ? 1 : 0;
|
|
28
|
+
const hasGennady = /\bgennady\b/.test(body) || /lint:contracts/.test(body);
|
|
29
|
+
const hasChain = body.includes('&&');
|
|
30
|
+
const hasParallel = /[&]/.test(body) && /;\s*wait/.test(body);
|
|
31
|
+
|
|
32
|
+
if (tc + lc + sc >= 2 && (hasChain || hasParallel)) return ['umbrella'];
|
|
33
|
+
if (hasGennady && hasChain) return ['umbrella'];
|
|
34
|
+
|
|
35
|
+
const classes = [];
|
|
36
|
+
if (hasGennady) classes.push('gennady');
|
|
37
|
+
|
|
38
|
+
const typecheckName = /^(type-?check|typecheck|typecheck:|tsc)$/.test(name);
|
|
39
|
+
const typecheckBody = /\b(tsc|tsgo)\b/.test(body) && /--noEmit/.test(body);
|
|
40
|
+
if (
|
|
41
|
+
(typecheckName || typecheckBody || name === 'lint:ts') &&
|
|
42
|
+
!name.startsWith('build:') &&
|
|
43
|
+
!name.startsWith('prepublish')
|
|
44
|
+
)
|
|
45
|
+
classes.push('typecheck');
|
|
46
|
+
|
|
47
|
+
const lintName = /^(lint|lint:all|lint-check|eslint|mc:eslint|stylist:lint|stylelint)$/.test(
|
|
48
|
+
name
|
|
49
|
+
);
|
|
50
|
+
const lintBody = hasCategory(body, 'lint');
|
|
51
|
+
if (
|
|
52
|
+
(lintName || lintBody) &&
|
|
53
|
+
!name.startsWith('lint:fix') &&
|
|
54
|
+
!name.startsWith('lint:contracts') &&
|
|
55
|
+
!name.startsWith('lint:ts') &&
|
|
56
|
+
!hasGennady
|
|
57
|
+
)
|
|
58
|
+
classes.push('lint');
|
|
59
|
+
|
|
60
|
+
const testName = /^(test|test:|mc:test|mc:jest|jest)$/.test(name);
|
|
61
|
+
const testBody = hasCategory(body, 'test') || /\bnode\b.*--test\b/.test(body);
|
|
62
|
+
if (testName || (testBody && !name.startsWith('build:') && !name.startsWith('prepublish')))
|
|
63
|
+
classes.push('test');
|
|
64
|
+
|
|
65
|
+
const formatName = /^format(:?check)?$/.test(name);
|
|
66
|
+
const formatBody = hasCategory(body, 'format');
|
|
67
|
+
if (formatName || formatBody) classes.push('format');
|
|
68
|
+
|
|
69
|
+
return classes.length > 0 ? classes : ['unknown'];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function selectBest(entries, cls) {
|
|
73
|
+
const priority = {
|
|
74
|
+
typecheck: { 'type-check': 10, typecheck: 10, tsc: 5 },
|
|
75
|
+
gennady: { 'lint:contracts': 10 },
|
|
76
|
+
lint: { lint: 10, eslint: 7, 'lint:all': 5, 'lint-check': 5, 'mc:eslint': 3 },
|
|
77
|
+
test: { test: 10, 'test:unit': 7, 'mc:test': 5, 'mc:jest': 3, jest: 3 },
|
|
78
|
+
format: { 'format:check': 10, format: 5 },
|
|
79
|
+
};
|
|
80
|
+
const candidates = entries.filter((e) => e.classes.includes(cls));
|
|
81
|
+
if (candidates.length === 0) return undefined;
|
|
82
|
+
const rank = (name) => (priority[cls] && priority[cls][name]) || 1;
|
|
83
|
+
candidates.sort((a, b) => rank(b.name) - rank(a.name));
|
|
84
|
+
return candidates[0] ? candidates[0].name : undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// --- Main ---
|
|
88
|
+
const rootArg = process.argv.slice(2).find((a) => !a.startsWith('-'));
|
|
89
|
+
const root = rootArg ? resolve(rootArg) : process.cwd();
|
|
90
|
+
|
|
91
|
+
let pkg;
|
|
92
|
+
try {
|
|
93
|
+
pkg = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf-8'));
|
|
94
|
+
} catch {
|
|
95
|
+
console.error(`[classify-scripts] package.json not found at ${root}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const entries = Object.entries(pkg.scripts || {}).map(([name, body]) => ({
|
|
100
|
+
name,
|
|
101
|
+
body,
|
|
102
|
+
classes: classify(name, body),
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
const selected = {};
|
|
106
|
+
for (const cls of ['typecheck', 'gennady', 'lint', 'test', 'format']) {
|
|
107
|
+
const best = selectBest(entries, cls);
|
|
108
|
+
if (best) selected[cls] = best;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
process.stdout.write(JSON.stringify({ scripts: entries, selected }));
|