@warnyin/sdlc 0.5.2 → 0.6.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/CHANGELOG.md +28 -0
- package/LICENSE +21 -21
- package/README.md +1 -1
- package/bin/cli.mjs +49 -4
- package/lib/caps.mjs +45 -45
- package/lib/config.mjs +41 -41
- package/lib/delta.mjs +227 -227
- package/lib/frontmatter.mjs +59 -59
- package/lib/glob.mjs +29 -29
- package/lib/journal.mjs +128 -0
- package/lib/manifest.mjs +99 -99
- package/lib/observe.mjs +19 -16
- package/lib/settings-merge.mjs +63 -63
- package/lib/validate.mjs +196 -196
- package/package.json +1 -1
- package/payload/adapters/agents-md.md +8 -8
- package/payload/adapters/claude/agents/sdlc-architect.md +12 -12
- package/payload/adapters/claude/agents/sdlc-builder.md +14 -14
- package/payload/adapters/claude/agents/sdlc-contractor.md +13 -13
- package/payload/adapters/claude/agents/sdlc-evaluator.md +13 -13
- package/payload/adapters/claude/agents/sdlc-learner.md +16 -16
- package/payload/adapters/claude/agents/sdlc-ops.md +11 -11
- package/payload/adapters/claude/agents/sdlc-quality.md +13 -13
- package/payload/adapters/claude/agents/sdlc-security.md +12 -12
- package/payload/adapters/claude/commands/sdlc/converge.md +5 -5
- package/payload/adapters/claude/commands/sdlc/init.md +4 -4
- package/payload/adapters/claude/commands/sdlc/next.md +4 -4
- package/payload/adapters/claude/commands/sdlc/observe.md +4 -4
- package/payload/adapters/claude/commands/sdlc/steer.md +4 -4
- package/payload/adapters/claude/skills/contract-writing/SKILL.md +26 -26
- package/payload/adapters/claude/skills/delta-spec-format/SKILL.md +36 -36
- package/payload/adapters/claude/skills/sdlc-conventions/SKILL.md +2 -1
- package/payload/adapters/cline.md +8 -8
- package/payload/adapters/copilot.md +8 -8
- package/payload/adapters/cursor.mdc +7 -7
- package/payload/adapters/gemini.md +8 -8
- package/payload/adapters/windsurf.md +4 -4
- package/payload/hooks/_shared.mjs +150 -154
- package/payload/hooks/guard-writes.mjs +83 -83
- package/payload/hooks/inject-context.mjs +55 -55
- package/payload/hooks/journal.mjs +58 -58
- package/payload/hooks/session-summary.mjs +50 -50
- package/payload/hooks/validate-artifact.mjs +80 -80
- package/payload/playbook/auto.md +12 -0
- package/payload/playbook/context.md +26 -26
- package/payload/playbook/converge.md +19 -19
- package/payload/playbook/init.md +22 -22
- package/payload/playbook/observe.md +20 -20
- package/payload/playbook/principles.md +28 -28
- package/payload/playbook/routing.md +19 -19
- package/payload/playbook/rules-card.md +16 -16
- package/payload/playbook/ship.md +35 -35
- package/payload/playbook/steer.md +21 -21
- package/payload/templates/change-deep.md +29 -29
- package/payload/templates/change-standard.md +28 -28
- package/payload/templates/change-vibe.md +19 -19
- package/payload/templates/config.yaml +8 -8
- package/payload/templates/constitution.md +14 -14
- package/payload/templates/contract-evals.md +9 -9
- package/payload/templates/contract-tests.md +9 -9
- package/payload/templates/harness.md +33 -33
- package/payload/templates/spec.md +14 -14
- package/payload/templates/steering.md +9 -9
- package/scripts/validate.mjs +47 -47
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: <kebab-id>
|
|
3
|
-
tier: deep
|
|
4
|
-
status: new
|
|
5
|
-
---
|
|
6
|
-
# Change: <title>
|
|
7
|
-
<!-- cap:150 · deep tier: hard-floor surface (security/payments/data-loss/irreversible) or new/multi-capability delta. Human gates apply per Autonomy policy. -->
|
|
8
|
-
|
|
9
|
-
## Why (≤5 lines)
|
|
10
|
-
<problem + outcome. No solutioning.>
|
|
11
|
-
|
|
12
|
-
## Assumptions
|
|
13
|
-
- <assumption made instead of asking, with the reason it is safe>
|
|
14
|
-
|
|
15
|
-
## Delta: <capability>
|
|
16
|
-
|
|
17
|
-
### ADDED Requirement: <name>
|
|
18
|
-
The system SHALL <behavior>.
|
|
19
|
-
|
|
20
|
-
#### Scenario: <name>
|
|
21
|
-
- WHEN <condition>
|
|
22
|
-
- THEN <observable outcome>
|
|
23
|
-
|
|
24
|
-
## Design
|
|
25
|
-
<!-- decisions & trade-offs only — never restate the delta. Escalate irreversible decisions per Autonomy policy. -->
|
|
26
|
-
- decision: <what> · alternatives: <a/b> · because: <why>
|
|
27
|
-
|
|
28
|
-
## Tasks
|
|
29
|
-
- [ ] T1 <task> [P] [tier:balanced]
|
|
1
|
+
---
|
|
2
|
+
id: <kebab-id>
|
|
3
|
+
tier: deep
|
|
4
|
+
status: new
|
|
5
|
+
---
|
|
6
|
+
# Change: <title>
|
|
7
|
+
<!-- cap:150 · deep tier: hard-floor surface (security/payments/data-loss/irreversible) or new/multi-capability delta. Human gates apply per Autonomy policy. -->
|
|
8
|
+
|
|
9
|
+
## Why (≤5 lines)
|
|
10
|
+
<problem + outcome. No solutioning.>
|
|
11
|
+
|
|
12
|
+
## Assumptions
|
|
13
|
+
- <assumption made instead of asking, with the reason it is safe>
|
|
14
|
+
|
|
15
|
+
## Delta: <capability>
|
|
16
|
+
|
|
17
|
+
### ADDED Requirement: <name>
|
|
18
|
+
The system SHALL <behavior>.
|
|
19
|
+
|
|
20
|
+
#### Scenario: <name>
|
|
21
|
+
- WHEN <condition>
|
|
22
|
+
- THEN <observable outcome>
|
|
23
|
+
|
|
24
|
+
## Design
|
|
25
|
+
<!-- decisions & trade-offs only — never restate the delta. Escalate irreversible decisions per Autonomy policy. -->
|
|
26
|
+
- decision: <what> · alternatives: <a/b> · because: <why>
|
|
27
|
+
|
|
28
|
+
## Tasks
|
|
29
|
+
- [ ] T1 <task> [P] [tier:balanced]
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: <kebab-id>
|
|
3
|
-
tier: standard
|
|
4
|
-
status: new
|
|
5
|
-
---
|
|
6
|
-
# Change: <title>
|
|
7
|
-
<!-- cap:100 · standard tier. Delta sections ARE the spec change — merged mechanically at ship, never re-narrated. -->
|
|
8
|
-
|
|
9
|
-
## Why (≤5 lines)
|
|
10
|
-
<problem + outcome. No solutioning.>
|
|
11
|
-
|
|
12
|
-
## Assumptions
|
|
13
|
-
- <assumption made instead of asking, with the reason it is safe — delete section if none>
|
|
14
|
-
<!-- unresolved ambiguity the agent cannot assume → [NEEDS CLARIFICATION: question] inline where it bites -->
|
|
15
|
-
|
|
16
|
-
## Delta: <capability>
|
|
17
|
-
|
|
18
|
-
### ADDED Requirement: <name>
|
|
19
|
-
The system SHALL <behavior>.
|
|
20
|
-
|
|
21
|
-
#### Scenario: <name>
|
|
22
|
-
- WHEN <condition>
|
|
23
|
-
- THEN <observable outcome>
|
|
24
|
-
|
|
25
|
-
## Tasks
|
|
26
|
-
<!-- [P] = parallelizable wave · [tier:cheap|balanced|deepest] per the routing table in harness.md -->
|
|
27
|
-
- [ ] T1 <task> [P] [tier:balanced]
|
|
28
|
-
- [ ] T2 <task> [tier:cheap]
|
|
1
|
+
---
|
|
2
|
+
id: <kebab-id>
|
|
3
|
+
tier: standard
|
|
4
|
+
status: new
|
|
5
|
+
---
|
|
6
|
+
# Change: <title>
|
|
7
|
+
<!-- cap:100 · standard tier. Delta sections ARE the spec change — merged mechanically at ship, never re-narrated. -->
|
|
8
|
+
|
|
9
|
+
## Why (≤5 lines)
|
|
10
|
+
<problem + outcome. No solutioning.>
|
|
11
|
+
|
|
12
|
+
## Assumptions
|
|
13
|
+
- <assumption made instead of asking, with the reason it is safe — delete section if none>
|
|
14
|
+
<!-- unresolved ambiguity the agent cannot assume → [NEEDS CLARIFICATION: question] inline where it bites -->
|
|
15
|
+
|
|
16
|
+
## Delta: <capability>
|
|
17
|
+
|
|
18
|
+
### ADDED Requirement: <name>
|
|
19
|
+
The system SHALL <behavior>.
|
|
20
|
+
|
|
21
|
+
#### Scenario: <name>
|
|
22
|
+
- WHEN <condition>
|
|
23
|
+
- THEN <observable outcome>
|
|
24
|
+
|
|
25
|
+
## Tasks
|
|
26
|
+
<!-- [P] = parallelizable wave · [tier:cheap|balanced|deepest] per the routing table in harness.md -->
|
|
27
|
+
- [ ] T1 <task> [P] [tier:balanced]
|
|
28
|
+
- [ ] T2 <task> [tier:cheap]
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: <kebab-id>
|
|
3
|
-
tier: vibe
|
|
4
|
-
status: new
|
|
5
|
-
---
|
|
6
|
-
# Change: <title>
|
|
7
|
-
<!-- cap:40 · vibe tier = one file, no contract. Reversible, no behavior-contract change, ≤2 files. -->
|
|
8
|
-
|
|
9
|
-
## Why (≤3 lines)
|
|
10
|
-
<problem + outcome>
|
|
11
|
-
|
|
12
|
-
## Assumptions
|
|
13
|
-
- <assumption the agent made instead of asking — delete section if none>
|
|
14
|
-
|
|
15
|
-
## Tasks
|
|
16
|
-
- [ ] T1 <task>
|
|
17
|
-
|
|
18
|
-
## Receipt (filled at ship)
|
|
19
|
-
- files touched: · test result: · cost:
|
|
1
|
+
---
|
|
2
|
+
id: <kebab-id>
|
|
3
|
+
tier: vibe
|
|
4
|
+
status: new
|
|
5
|
+
---
|
|
6
|
+
# Change: <title>
|
|
7
|
+
<!-- cap:40 · vibe tier = one file, no contract. Reversible, no behavior-contract change, ≤2 files. -->
|
|
8
|
+
|
|
9
|
+
## Why (≤3 lines)
|
|
10
|
+
<problem + outcome>
|
|
11
|
+
|
|
12
|
+
## Assumptions
|
|
13
|
+
- <assumption the agent made instead of asking — delete section if none>
|
|
14
|
+
|
|
15
|
+
## Tasks
|
|
16
|
+
- [ ] T1 <task>
|
|
17
|
+
|
|
18
|
+
## Receipt (filled at ship)
|
|
19
|
+
- files touched: · test result: · cost:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# @warnyin/sdlc project config — machine-read; keep minimal.
|
|
2
|
-
language: en
|
|
3
|
-
tools: [] # filled by `warnyin-sdlc init`
|
|
4
|
-
# Optional price table for cost reporting (USD per 1M tokens).
|
|
5
|
-
# prices:
|
|
6
|
-
# claude-sonnet-5: { input: 3, output: 15, cacheRead: 0.3 }
|
|
7
|
-
# Optional per-artifact cap overrides (see lib/caps.mjs for defaults).
|
|
8
|
-
# caps: {}
|
|
1
|
+
# @warnyin/sdlc project config — machine-read; keep minimal.
|
|
2
|
+
language: en
|
|
3
|
+
tools: [] # filled by `warnyin-sdlc init`
|
|
4
|
+
# Optional price table for cost reporting (USD per 1M tokens).
|
|
5
|
+
# prices:
|
|
6
|
+
# claude-sonnet-5: { input: 3, output: 15, cacheRead: 0.3 }
|
|
7
|
+
# Optional per-artifact cap overrides (see lib/caps.mjs for defaults).
|
|
8
|
+
# caps: {}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Constitution — <project>
|
|
2
|
-
<!-- cap:30 · ALWAYS LOADED: every line here costs tokens in EVERY turn. Prove residency or move it to steering. -->
|
|
3
|
-
|
|
4
|
-
## Stack (facts only, max 3 lines)
|
|
5
|
-
- <runtime / framework / db — one line each>
|
|
6
|
-
|
|
7
|
-
## Hard rules (SHALL / SHALL NOT only — no advice)
|
|
8
|
-
- The agent SHALL NOT edit `sdlc/specs/**` or `sdlc/changes/archive/**` outside `/sdlc:ship`.
|
|
9
|
-
- The agent SHALL record every assumption in the change's `## Assumptions` before acting on it.
|
|
10
|
-
- <add a rule each time the agent misbehaves; each new rule must displace an old one once the always-budget (60 lines) is full>
|
|
11
|
-
|
|
12
|
-
## Workflow
|
|
13
|
-
- Changes flow: new → [design] → contract → build → verify → [review] → ship.
|
|
14
|
-
- Tier by stakes: vibe | standard | deep — triage table lives in `sdlc/harness.md`.
|
|
1
|
+
# Constitution — <project>
|
|
2
|
+
<!-- cap:30 · ALWAYS LOADED: every line here costs tokens in EVERY turn. Prove residency or move it to steering. -->
|
|
3
|
+
|
|
4
|
+
## Stack (facts only, max 3 lines)
|
|
5
|
+
- <runtime / framework / db — one line each>
|
|
6
|
+
|
|
7
|
+
## Hard rules (SHALL / SHALL NOT only — no advice)
|
|
8
|
+
- The agent SHALL NOT edit `sdlc/specs/**` or `sdlc/changes/archive/**` outside `/sdlc:ship`.
|
|
9
|
+
- The agent SHALL record every assumption in the change's `## Assumptions` before acting on it.
|
|
10
|
+
- <add a rule each time the agent misbehaves; each new rule must displace an old one once the always-budget (60 lines) is full>
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
- Changes flow: new → [design] → contract → build → verify → [review] → ship.
|
|
14
|
+
- Tier by stakes: vibe | standard | deep — triage table lives in `sdlc/harness.md`.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# Eval contract — <change-id>
|
|
2
|
-
<!-- cap:40 · verifies the non-deterministic half: trajectory + quality. Scored by sdlc-evaluator (cheap tier). -->
|
|
3
|
-
|
|
4
|
-
## Rubric (score 1–5 each)
|
|
5
|
-
- Trajectory: did the agent read the contract before writing code? run tests before claiming done?
|
|
6
|
-
- Quality: <change-specific bars>
|
|
7
|
-
|
|
8
|
-
## Pass bar
|
|
9
|
-
- all scores ≥ 4 · failures route back to `/sdlc:build` with a cluster note
|
|
1
|
+
# Eval contract — <change-id>
|
|
2
|
+
<!-- cap:40 · verifies the non-deterministic half: trajectory + quality. Scored by sdlc-evaluator (cheap tier). -->
|
|
3
|
+
|
|
4
|
+
## Rubric (score 1–5 each)
|
|
5
|
+
- Trajectory: did the agent read the contract before writing code? run tests before claiming done?
|
|
6
|
+
- Quality: <change-specific bars>
|
|
7
|
+
|
|
8
|
+
## Pass bar
|
|
9
|
+
- all scores ≥ 4 · failures route back to `/sdlc:build` with a cluster note
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# Test contract — <change-id>
|
|
2
|
-
<!-- cap:60 · written BEFORE code. This is the deterministic half of the contract with the AI; failing tests are generated from this table. -->
|
|
3
|
-
|
|
4
|
-
| # | Given / When / Then | Kind (unit/int/e2e) | Maps to requirement |
|
|
5
|
-
|---|---|---|---|
|
|
6
|
-
| 1 | <g/w/t> | unit | <requirement name> |
|
|
7
|
-
|
|
8
|
-
## Out of scope (explicitly untested + why)
|
|
9
|
-
- <item — reason>
|
|
1
|
+
# Test contract — <change-id>
|
|
2
|
+
<!-- cap:60 · written BEFORE code. This is the deterministic half of the contract with the AI; failing tests are generated from this table. -->
|
|
3
|
+
|
|
4
|
+
| # | Given / When / Then | Kind (unit/int/e2e) | Maps to requirement |
|
|
5
|
+
|---|---|---|---|
|
|
6
|
+
| 1 | <g/w/t> | unit | <requirement name> |
|
|
7
|
+
|
|
8
|
+
## Out of scope (explicitly untested + why)
|
|
9
|
+
- <item — reason>
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
# Harness — <project>
|
|
2
|
-
<!-- cap:60 · CONFIGURE-phase output; reviewed like code. Registry + tables only — doctrine lives in sdlc/.playbook/. -->
|
|
3
|
-
|
|
4
|
-
## Tools & MCP (what the agent may call)
|
|
5
|
-
| Tool/MCP | Scope | Notes |
|
|
6
|
-
|---|---|---|
|
|
7
|
-
| <tool> | <what it may touch> | <when to use> |
|
|
8
|
-
|
|
9
|
-
## Sandbox & execution
|
|
10
|
-
- test command: `<cmd>`
|
|
11
|
-
- sandbox notes: <where code runs, what it cannot reach>
|
|
12
|
-
|
|
13
|
-
## Guardrails (mirror of installed hooks — deterministic, the agent cannot skip them)
|
|
14
|
-
- `sdlc/specs/**` and archive are write-locked outside ship.
|
|
15
|
-
- Artifact line caps validated on every write.
|
|
16
|
-
- Session token/cost journaled per change.
|
|
17
|
-
|
|
18
|
-
## Model routing (generic tiers — the harness adapter maps them to real models)
|
|
19
|
-
| Task kind | Tier |
|
|
20
|
-
|---|---|
|
|
21
|
-
| requirements, architecture, deep design | deepest |
|
|
22
|
-
| standard implementation | balanced |
|
|
23
|
-
| test generation, review passes, mechanical/scaffold, eval judging, learning | cheap |
|
|
24
|
-
|
|
25
|
-
## Tier triage (stakes → tier)
|
|
26
|
-
- vibe: reversible, no behavior-contract change, ≤2 files.
|
|
27
|
-
- deep: security / payments / data-loss / irreversible surface, new capability, or multi-capability delta.
|
|
28
|
-
- otherwise: standard.
|
|
29
|
-
|
|
30
|
-
## Autonomy policy
|
|
31
|
-
- auto-ship: vibe, standard.
|
|
32
|
-
- escalate to human: hard-floor (security, payments, data-loss, irreversible), verify failed > 3 rounds,
|
|
33
|
-
token budget exceeded, information the agent cannot obtain or safely assume.
|
|
1
|
+
# Harness — <project>
|
|
2
|
+
<!-- cap:60 · CONFIGURE-phase output; reviewed like code. Registry + tables only — doctrine lives in sdlc/.playbook/. -->
|
|
3
|
+
|
|
4
|
+
## Tools & MCP (what the agent may call)
|
|
5
|
+
| Tool/MCP | Scope | Notes |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| <tool> | <what it may touch> | <when to use> |
|
|
8
|
+
|
|
9
|
+
## Sandbox & execution
|
|
10
|
+
- test command: `<cmd>`
|
|
11
|
+
- sandbox notes: <where code runs, what it cannot reach>
|
|
12
|
+
|
|
13
|
+
## Guardrails (mirror of installed hooks — deterministic, the agent cannot skip them)
|
|
14
|
+
- `sdlc/specs/**` and archive are write-locked outside ship.
|
|
15
|
+
- Artifact line caps validated on every write.
|
|
16
|
+
- Session token/cost journaled per change.
|
|
17
|
+
|
|
18
|
+
## Model routing (generic tiers — the harness adapter maps them to real models)
|
|
19
|
+
| Task kind | Tier |
|
|
20
|
+
|---|---|
|
|
21
|
+
| requirements, architecture, deep design | deepest |
|
|
22
|
+
| standard implementation | balanced |
|
|
23
|
+
| test generation, review passes, mechanical/scaffold, eval judging, learning | cheap |
|
|
24
|
+
|
|
25
|
+
## Tier triage (stakes → tier)
|
|
26
|
+
- vibe: reversible, no behavior-contract change, ≤2 files.
|
|
27
|
+
- deep: security / payments / data-loss / irreversible surface, new capability, or multi-capability delta.
|
|
28
|
+
- otherwise: standard.
|
|
29
|
+
|
|
30
|
+
## Autonomy policy
|
|
31
|
+
- auto-ship: vibe, standard.
|
|
32
|
+
- escalate to human: hard-floor (security, payments, data-loss, irreversible), verify failed > 3 rounds,
|
|
33
|
+
token budget exceeded, information the agent cannot obtain or safely assume.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Spec: <capability>
|
|
2
|
-
<!-- cap:150 (soft) · living spec — observable behavior only, no implementation detail. Split the capability beyond the cap. -->
|
|
3
|
-
|
|
4
|
-
## Purpose
|
|
5
|
-
<!-- one or two lines; commands grep this header first (progressive disclosure) -->
|
|
6
|
-
|
|
7
|
-
## Requirements
|
|
8
|
-
|
|
9
|
-
### Requirement: <name>
|
|
10
|
-
The system SHALL <behavior>.
|
|
11
|
-
|
|
12
|
-
#### Scenario: <name>
|
|
13
|
-
- WHEN <condition>
|
|
14
|
-
- THEN <observable outcome>
|
|
1
|
+
# Spec: <capability>
|
|
2
|
+
<!-- cap:150 (soft) · living spec — observable behavior only, no implementation detail. Split the capability beyond the cap. -->
|
|
3
|
+
|
|
4
|
+
## Purpose
|
|
5
|
+
<!-- one or two lines; commands grep this header first (progressive disclosure) -->
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: <name>
|
|
10
|
+
The system SHALL <behavior>.
|
|
11
|
+
|
|
12
|
+
#### Scenario: <name>
|
|
13
|
+
- WHEN <condition>
|
|
14
|
+
- THEN <observable outcome>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: <topic>
|
|
3
|
-
inclusion: paths # always | paths | manual | agent
|
|
4
|
-
pathMatch: ["src/<area>/**"]
|
|
5
|
-
---
|
|
6
|
-
# Steering: <topic>
|
|
7
|
-
<!-- cap:40 · loaded only when pathMatch hits or explicitly requested. Dense, scoped conventions — no generalities. -->
|
|
8
|
-
|
|
9
|
-
<conventions, gotchas, and reference patterns for this scope>
|
|
1
|
+
---
|
|
2
|
+
name: <topic>
|
|
3
|
+
inclusion: paths # always | paths | manual | agent
|
|
4
|
+
pathMatch: ["src/<area>/**"]
|
|
5
|
+
---
|
|
6
|
+
# Steering: <topic>
|
|
7
|
+
<!-- cap:40 · loaded only when pathMatch hits or explicitly requested. Dense, scoped conventions — no generalities. -->
|
|
8
|
+
|
|
9
|
+
<conventions, gotchas, and reference patterns for this scope>
|
package/scripts/validate.mjs
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Thin CLI wrapper — validator core lives in lib/validate.mjs so that
|
|
3
|
-
// installed hooks (sdlc/.hooks/lib/) can share the exact same logic.
|
|
4
|
-
// Exit codes: 0 = clean, 1 = errors, 2 = usage/setup error.
|
|
5
|
-
|
|
6
|
-
import fs from 'node:fs';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import process from 'node:process';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import { validateAll, formatIssues } from '../lib/validate.mjs';
|
|
11
|
-
|
|
12
|
-
export * from '../lib/validate.mjs';
|
|
13
|
-
|
|
14
|
-
function main() {
|
|
15
|
-
const args = process.argv.slice(2);
|
|
16
|
-
const strict = args.includes('--strict');
|
|
17
|
-
const rootIdx = args.indexOf('--root');
|
|
18
|
-
const projectRoot = rootIdx !== -1 ? args[rootIdx + 1] : process.cwd();
|
|
19
|
-
const positional = args.filter((a, i) => !a.startsWith('--') && args[i - 1] !== '--root');
|
|
20
|
-
const changeId = positional[0] && positional[0] !== '--all' ? positional[0] : null;
|
|
21
|
-
|
|
22
|
-
const sdlcRoot = path.join(projectRoot, 'sdlc');
|
|
23
|
-
if (!fs.existsSync(sdlcRoot)) {
|
|
24
|
-
console.error(`No sdlc/ directory at ${projectRoot} — run \`npx @warnyin/sdlc init\` first.`);
|
|
25
|
-
process.exit(2);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const issues = validateAll(sdlcRoot, { strict, changeId });
|
|
29
|
-
if (issues.length) console.log(formatIssues(issues));
|
|
30
|
-
const errors = issues.filter((i) => i.level === 'error').length;
|
|
31
|
-
const warns = issues.length - errors;
|
|
32
|
-
console.log(`validate: ${errors} error(s), ${warns} warning(s)${strict ? ' [strict]' : ''}`);
|
|
33
|
-
process.exit(errors > 0 ? 1 : 0);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function isEntrypoint() {
|
|
37
|
-
if (!process.argv[1]) return false;
|
|
38
|
-
try {
|
|
39
|
-
return fs.realpathSync.native(path.resolve(process.argv[1])) === fileURLToPath(import.meta.url);
|
|
40
|
-
} catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (isEntrypoint()) {
|
|
46
|
-
main();
|
|
47
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Thin CLI wrapper — validator core lives in lib/validate.mjs so that
|
|
3
|
+
// installed hooks (sdlc/.hooks/lib/) can share the exact same logic.
|
|
4
|
+
// Exit codes: 0 = clean, 1 = errors, 2 = usage/setup error.
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import process from 'node:process';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { validateAll, formatIssues } from '../lib/validate.mjs';
|
|
11
|
+
|
|
12
|
+
export * from '../lib/validate.mjs';
|
|
13
|
+
|
|
14
|
+
function main() {
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const strict = args.includes('--strict');
|
|
17
|
+
const rootIdx = args.indexOf('--root');
|
|
18
|
+
const projectRoot = rootIdx !== -1 ? args[rootIdx + 1] : process.cwd();
|
|
19
|
+
const positional = args.filter((a, i) => !a.startsWith('--') && args[i - 1] !== '--root');
|
|
20
|
+
const changeId = positional[0] && positional[0] !== '--all' ? positional[0] : null;
|
|
21
|
+
|
|
22
|
+
const sdlcRoot = path.join(projectRoot, 'sdlc');
|
|
23
|
+
if (!fs.existsSync(sdlcRoot)) {
|
|
24
|
+
console.error(`No sdlc/ directory at ${projectRoot} — run \`npx @warnyin/sdlc init\` first.`);
|
|
25
|
+
process.exit(2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const issues = validateAll(sdlcRoot, { strict, changeId });
|
|
29
|
+
if (issues.length) console.log(formatIssues(issues));
|
|
30
|
+
const errors = issues.filter((i) => i.level === 'error').length;
|
|
31
|
+
const warns = issues.length - errors;
|
|
32
|
+
console.log(`validate: ${errors} error(s), ${warns} warning(s)${strict ? ' [strict]' : ''}`);
|
|
33
|
+
process.exit(errors > 0 ? 1 : 0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isEntrypoint() {
|
|
37
|
+
if (!process.argv[1]) return false;
|
|
38
|
+
try {
|
|
39
|
+
return fs.realpathSync.native(path.resolve(process.argv[1])) === fileURLToPath(import.meta.url);
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (isEntrypoint()) {
|
|
46
|
+
main();
|
|
47
|
+
}
|