@luizsantiago/spec-guardrails 3.8.0 → 4.0.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/README.md +105 -83
- package/index.js +134 -0
- package/lib/constants.js +3 -0
- package/lib/cursor-hooks.js +24 -11
- package/lib/gates.js +2 -0
- package/lib/req-analysis.js +274 -0
- package/lib/sandbox-policy.js +173 -0
- package/package.json +2 -2
- package/scripts/_memory_config.py +62 -0
- package/scripts/code_index.py +228 -0
- package/scripts/episodes.py +274 -0
- package/scripts/memory_index.py +113 -0
- package/skills/agent-architecture.md +12 -2
- package/skills/references/elicitation.md +197 -0
- package/skills/references/explore.md +1 -0
- package/skills/references/specify.md +1 -0
- package/templates/config.yaml.example +19 -0
- package/templates/cursor/hooks/sandbox-shell.mjs +102 -0
- package/templates/cursor/hooks.json +6 -0
package/README.md
CHANGED
|
@@ -3,153 +3,175 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@luizsantiago/spec-guardrails)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**Governed spec-driven development for AI coding agents.**
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Spec Guardrails is a process kit that installs phase guides, persistent project memory, and optional structural checks into your repository. Teams keep ownership of requirements and approval gates; agents follow a repeatable path from written intent to verified delivery.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
npm: [`@luizsantiago/spec-guardrails`](https://www.npmjs.com/package/@luizsantiago/spec-guardrails) **4.0.x**
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
---
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## What it is
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
2. **Break work into small jobs** (`tasks.md`)
|
|
18
|
-
3. **Implement in waves** with real checks
|
|
19
|
-
4. **Verify with fresh eyes** — proof, not “trust me”
|
|
16
|
+
Spec Guardrails is **not** an application framework, a vector database, or a replacement for your stack. It is an **operating model** for AI-assisted engineering:
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
| Layer | Role |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| **Phase guides** | Instructions the agent loads one step at a time |
|
|
21
|
+
| **Project memory** | `.specs/` — specs, tasks, validation, and state that survive chat sessions |
|
|
22
|
+
| **Structural checks** | Optional Python gates that enforce document shape and evidence hooks |
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
The default loop is **Specify → Tasks → Execute → Verify → Archive**. You approve specs and task plans; the agent implements in waves and produces proof before work is considered done.
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
Full narrative: [Overview](docs/guide/Overview.md) · [How it works](docs/guide/How-it-works.md)
|
|
26
27
|
|
|
27
28
|
---
|
|
28
29
|
|
|
29
|
-
##
|
|
30
|
+
## Why teams use it
|
|
31
|
+
|
|
32
|
+
| Benefit | Outcome |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| **Traceability** | Requirements, jobs, and verification live in version-controlled artifacts |
|
|
35
|
+
| **Controlled autonomy** | Agents propose and execute; humans approve scope, design forks, and git tiers |
|
|
36
|
+
| **Progressive depth** | Quick fixes skip ceremony; complex work gets discuss, design, and task graphs |
|
|
37
|
+
| **Platform-agnostic** | Cursor, Claude Code, GitHub Copilot, Codex, and other agents via root `AGENTS.md` |
|
|
38
|
+
| **Token efficiency** | One phase guide per turn instead of dumping the entire playbook |
|
|
39
|
+
| **Optional enforcement** | Process mode (Node only) or Brakes mode (Node + Python gates) |
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
- Not a vector database or “AI memory” that reads your whole codebase
|
|
33
|
-
- Not automatic push/merge/deploy — you stay in control of git tiers
|
|
34
|
-
- Not a replacement for code review or product judgment
|
|
41
|
+
Process vs Brakes: [FAQ](docs/guide/FAQ.md#process-vs-brakes) · Guarantees: [Guarantees matrix](docs/guide/Guarantees-matrix.md)
|
|
35
42
|
|
|
36
43
|
---
|
|
37
44
|
|
|
38
|
-
## Install
|
|
45
|
+
## Install
|
|
46
|
+
|
|
47
|
+
In your project root:
|
|
39
48
|
|
|
40
49
|
```bash
|
|
41
50
|
npx @luizsantiago/spec-guardrails install
|
|
42
51
|
```
|
|
43
52
|
|
|
44
|
-
|
|
53
|
+
Re-run after upgrading the package; existing `.specs/` notes are preserved. Check readiness with `doctor`.
|
|
54
|
+
|
|
55
|
+
| Requirement | Role |
|
|
45
56
|
| --- | --- |
|
|
46
57
|
| **Node.js 18+** | Required — CLI and install |
|
|
47
|
-
| **Python 3.10+** | Optional — enables
|
|
48
|
-
|
|
49
|
-
Re-run `install` after upgrading the package. Your `.specs/` notes and `STATE.md` are kept.
|
|
58
|
+
| **Python 3.10+** | Optional — enables Brakes mode (automatic gates) |
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Works with **Cursor, Claude Code, GitHub Copilot, OpenAI Codex**, and other agents via root `AGENTS.md`. See [Platform parity](docs/guide/Platform-parity.md).
|
|
60
|
+
Platform setup: [Platform parity](docs/guide/Platform-parity.md) · Release notes: [CHANGELOG](docs/CHANGELOG.md)
|
|
54
61
|
|
|
55
62
|
---
|
|
56
63
|
|
|
57
|
-
## How you
|
|
64
|
+
## How you work day to day
|
|
65
|
+
|
|
66
|
+
Interaction happens in **agent chat**, not the terminal. The agent runs CLI helpers and gates when needed.
|
|
58
67
|
|
|
59
|
-
|
|
68
|
+
### Core commands
|
|
60
69
|
|
|
61
|
-
| Command |
|
|
70
|
+
| Command | Use when |
|
|
62
71
|
| --- | --- |
|
|
63
|
-
| `/specify` |
|
|
64
|
-
| `/
|
|
65
|
-
| `/
|
|
66
|
-
| `/
|
|
67
|
-
| `/
|
|
72
|
+
| `/specify` | Starting any non-trivial feature — requirements in writing first |
|
|
73
|
+
| `/elicit` | Kickoff or request is vague — structured Q&A before Specify *(optional)* |
|
|
74
|
+
| `/tasks` | Spec approved — break work into a job list |
|
|
75
|
+
| `/loop` | Implementation — one wave at a time |
|
|
76
|
+
| `/verify` | All jobs done — independent proof *(prefer a fresh chat)* |
|
|
77
|
+
| `/archive` | Feature validated — fold into project memory |
|
|
78
|
+
| `/quick` | Tiny fix only (≤3 files, no design fork) |
|
|
68
79
|
|
|
69
80
|
**Typical path:** `/specify` → approve → `/tasks` → approve → `/loop` → `/verify` → `/archive`
|
|
70
81
|
|
|
71
|
-
|
|
82
|
+
When input is still exploratory, `/explore` or `/elicit` may come first. The agent suggests depth; it does not block Specify without approval.
|
|
72
83
|
|
|
73
|
-
|
|
84
|
+
Command reference: [Agent commands](docs/guide/agent-commands.md) · Entry paths: [Overview → Three ways to start](docs/guide/Overview.md#three-ways-to-start-pick-one)
|
|
74
85
|
|
|
75
|
-
|
|
86
|
+
### Optional capabilities
|
|
76
87
|
|
|
77
|
-
|
|
88
|
+
Enable when the work warrants them — most teams start with the core loop only.
|
|
78
89
|
|
|
79
|
-
|
|
|
80
|
-
| --- | --- |
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
| Capability | Purpose |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| Memory search | Retrieve past specs, validations, and kickoff briefs |
|
|
93
|
+
| Context guards | Scope check before edit or “done” (Cursor: hooks) |
|
|
94
|
+
| Episodic memory | Session notes → lessons for future runs |
|
|
95
|
+
| Code index | Lightweight brownfield file and symbol map |
|
|
96
|
+
| Solution exploration | Compare implementation options before committing |
|
|
97
|
+
| Sandbox / execution policy | Warn or block destructive shell commands and path drift |
|
|
98
|
+
| Semantic retrieval | Search by meaning — off by default |
|
|
85
99
|
|
|
86
|
-
|
|
100
|
+
Guides: [Memory](docs/guide/Memory.md) · [Brownfield context](docs/guide/brownfield-context.md) · [Safety & exploration](docs/guide/Overview.md)
|
|
87
101
|
|
|
88
102
|
---
|
|
89
103
|
|
|
90
|
-
## What lands in your
|
|
104
|
+
## What lands in your repository
|
|
91
105
|
|
|
92
106
|
| Path | Role |
|
|
93
107
|
| --- | --- |
|
|
94
|
-
| `.cursor/skills/` (
|
|
95
|
-
| `.specs/STATE.md` |
|
|
96
|
-
| `.specs/features/NNN-slug/` | Spec, tasks, validation
|
|
108
|
+
| `.cursor/skills/` (+ Claude, Copilot, Codex trees) | Phase instructions for the agent |
|
|
109
|
+
| `.specs/STATE.md` | Active feature and next step |
|
|
110
|
+
| `.specs/features/NNN-slug/` | Spec, tasks, and validation per feature |
|
|
97
111
|
| `.specs/guardrails/scripts/` | Python gates (Brakes mode) |
|
|
98
|
-
| `.specs/config.yaml` | Optional project rules and policy |
|
|
112
|
+
| `.specs/config.yaml` | Optional project rules and execution policy |
|
|
113
|
+
|
|
114
|
+
Architecture: [Skills and hub](docs/guide/skills-and-hub.md) · [Architecture](docs/guide/Architecture.md)
|
|
99
115
|
|
|
100
116
|
---
|
|
101
117
|
|
|
102
|
-
##
|
|
118
|
+
## Documentation
|
|
103
119
|
|
|
104
|
-
|
|
120
|
+
Start with the guide that matches your question; each page links deeper where needed.
|
|
105
121
|
|
|
106
|
-
|
|
|
122
|
+
| Topic | Start here | Go deeper |
|
|
107
123
|
| --- | --- | --- |
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
124
|
+
| Orientation | [Overview](docs/guide/Overview.md) | [Concepts](docs/guide/concepts.md) |
|
|
125
|
+
| First session | [Quick start](docs/guide/Quick-start.md) | [Agent commands](docs/guide/agent-commands.md) |
|
|
126
|
+
| Process model | [How it works](docs/guide/How-it-works.md) | [Loop patterns](docs/guide/loop-patterns.md) |
|
|
127
|
+
| Enforcement | [Gates](docs/guide/gates.md) | [Gates and guarantees](docs/guide/Gates-and-guarantees.md) |
|
|
128
|
+
| Long-running projects | [Memory](docs/guide/Memory.md) | [Brownfield context](docs/guide/brownfield-context.md) |
|
|
129
|
+
| Questions | [FAQ](docs/guide/FAQ.md) | [Stability policy](docs/guide/Stability-policy.md) |
|
|
113
130
|
|
|
114
|
-
|
|
131
|
+
Full index: [docs/guide/README.md](docs/guide/README.md)
|
|
115
132
|
|
|
116
133
|
---
|
|
117
134
|
|
|
118
|
-
##
|
|
135
|
+
## Contributing
|
|
119
136
|
|
|
120
|
-
|
|
121
|
-
| --- | --- |
|
|
122
|
-
| **[Overview](docs/guide/Overview.md)** | Complete simple picture — workflow, layers, team use |
|
|
123
|
-
| [Quick start](docs/guide/Quick-start.md) | First ten minutes |
|
|
124
|
-
| [How it works](docs/guide/How-it-works.md) | Story from idea to archive |
|
|
125
|
-
| [Memory](docs/guide/Memory.md) | `.specs/` memory and search — when and how much |
|
|
126
|
-
| [FAQ](docs/guide/FAQ.md) | Common questions |
|
|
137
|
+
We welcome focused improvements — skills, gates, CLI, docs, and tests. See [CONTRIBUTING.md](CONTRIBUTING.md) for layout, gate stability rules, and local checks.
|
|
127
138
|
|
|
128
|
-
|
|
129
|
-
| --- | --- |
|
|
130
|
-
| [Agent commands](docs/guide/agent-commands.md) | Every `/specify`, `/loop`, CLI helper |
|
|
131
|
-
| [Guarantees matrix](docs/guide/Guarantees-matrix.md) | Product promises → mechanisms |
|
|
132
|
-
| [Gates](docs/guide/gates.md) | What each gate checks |
|
|
133
|
-
| [Architecture](docs/guide/Architecture.md) | Core vs platform adapters |
|
|
134
|
-
| [Concepts](docs/guide/concepts.md) | Spec-driven, loop, graph, tiers |
|
|
135
|
-
| [Changelog](docs/CHANGELOG.md) | Version history |
|
|
139
|
+
### Use Spec Guardrails to build your contribution
|
|
136
140
|
|
|
137
|
-
|
|
141
|
+
The recommended workflow is to **dogfood the product**: install Spec Guardrails, describe your change through the agent phases, implement against approved artifacts, and verify before opening a PR.
|
|
138
142
|
|
|
139
|
-
|
|
143
|
+
**In your own project or fork** — use the latest stable release from npm:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npx @luizsantiago/spec-guardrails@latest install
|
|
147
|
+
npx @luizsantiago/spec-guardrails doctor
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Then in chat: `/specify` (or `/elicit` if scope is unclear) → `/tasks` → `/loop` → `/verify`. Your `.specs/` folder holds the spec and proof that guided the change.
|
|
140
151
|
|
|
141
|
-
|
|
152
|
+
**In this source repository** — work against the branch you are developing, not the published tarball:
|
|
142
153
|
|
|
143
154
|
```bash
|
|
144
|
-
|
|
155
|
+
git clone https://github.com/luizssantiago92/spec-guardrails.git
|
|
156
|
+
cd spec-guardrails
|
|
157
|
+
npm install
|
|
158
|
+
npm run guardrails -- install
|
|
159
|
+
npm run guardrails -- doctor
|
|
145
160
|
```
|
|
146
161
|
|
|
147
|
-
|
|
162
|
+
Edit source under `skills/`, `lib/`, `scripts/`, and `rules/`; re-run `npm run guardrails -- install` after skill or gate changes. Run `npm test` before every PR.
|
|
163
|
+
|
|
164
|
+
| Path | Role |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| `skills/` | Hub and sister skills shipped to consumers |
|
|
167
|
+
| `skills/references/` | Phase procedures (`specify.md`, `elicit.md`, …) |
|
|
168
|
+
| `scripts/` | Deterministic Python gates |
|
|
169
|
+
| `test/` | Node install tests and Python gate suites |
|
|
170
|
+
|
|
171
|
+
Gate changes follow the adversarial test policy in [CONTRIBUTING.md](CONTRIBUTING.md). Credits: [docs/guide/credits.md](docs/guide/credits.md)
|
|
148
172
|
|
|
149
173
|
---
|
|
150
174
|
|
|
151
|
-
##
|
|
175
|
+
## License
|
|
152
176
|
|
|
153
|
-
|
|
154
|
-
- [Credits](docs/guide/credits.md)
|
|
155
|
-
- MIT — see [LICENSE](LICENSE)
|
|
177
|
+
MIT — see [LICENSE](LICENSE).
|
package/index.js
CHANGED
|
@@ -35,6 +35,11 @@ import {
|
|
|
35
35
|
listWorkspaces,
|
|
36
36
|
prepareWorkspaces,
|
|
37
37
|
} from "./lib/workspace-isolation.js";
|
|
38
|
+
import {
|
|
39
|
+
formatDiscoverReport,
|
|
40
|
+
formatPromoteMessage,
|
|
41
|
+
reqAnalysisInit,
|
|
42
|
+
} from "./lib/req-analysis.js";
|
|
38
43
|
import {
|
|
39
44
|
formatExplorationInit,
|
|
40
45
|
formatExplorationStatus,
|
|
@@ -45,6 +50,11 @@ import {
|
|
|
45
50
|
recordExplorationDecision,
|
|
46
51
|
validateExplorationArtifact,
|
|
47
52
|
} from "./lib/solution-exploration.js";
|
|
53
|
+
import {
|
|
54
|
+
checkSandboxCommand,
|
|
55
|
+
formatSandboxCheck,
|
|
56
|
+
loadSandboxPolicy,
|
|
57
|
+
} from "./lib/sandbox-policy.js";
|
|
48
58
|
import {
|
|
49
59
|
initProjectConfig,
|
|
50
60
|
listPresets,
|
|
@@ -71,6 +81,12 @@ Commands:
|
|
|
71
81
|
feature-init "<description>" Allocate NNN-slug feature, STATE, local branch (Tier 0)
|
|
72
82
|
[--no-branch] Skip git checkout -b
|
|
73
83
|
[--no-spec] Skip spec.md stub
|
|
84
|
+
req-analysis init "<description>" Scaffold requirements brief (/elicit phase)
|
|
85
|
+
[--scope project|feature] project = macro brief; feature = one delivery (default: feature)
|
|
86
|
+
[--force] Replace existing brief scaffold
|
|
87
|
+
req-analysis discover List local kickoff sources (prd.md, kickoff.md, …)
|
|
88
|
+
req-analysis promote Print next steps after brief approval
|
|
89
|
+
[--scope project|feature] Match the brief scope
|
|
74
90
|
archive-feature [feature] Fold verified feature into ROADMAP + domain spec; reset STATE
|
|
75
91
|
[--domain <slug>] Domain folder under .specs/domains/ (default: feature slug)
|
|
76
92
|
[--skip-verify] Skip validate-state (tests / recovery only)
|
|
@@ -113,6 +129,13 @@ Commands:
|
|
|
113
129
|
memory-retrieve "<query>" Hybrid retrieval (FTS + graph + optional semantic)
|
|
114
130
|
[--mode fts|hybrid|semantic] Strategy (default: hybrid)
|
|
115
131
|
[--json] Machine-readable output
|
|
132
|
+
episodes record --summary "…" Capture working-session episodic memory
|
|
133
|
+
episodes list|archive|prune|promote Episodic lifecycle (working → episodic → promoted)
|
|
134
|
+
code-index rebuild [--roots src,lib] Lightweight brownfield code map (not full RepoGraph)
|
|
135
|
+
code-index search "<query>" Search indexed files/symbols/imports
|
|
136
|
+
sandbox status Show sandbox policy mode (off|warn|strict)
|
|
137
|
+
sandbox check-command "<cmd>" Soft OS sandbox — block/warn destructive shell commands
|
|
138
|
+
[--json] Machine-readable output
|
|
116
139
|
context-guard status Execute readiness from STATE + tasks.md
|
|
117
140
|
[--json] Machine-readable output
|
|
118
141
|
context-guard check-edit <path> Contextual guard before editing a file
|
|
@@ -614,6 +637,58 @@ if (command === "--version" || command === "-v" || command === "version") {
|
|
|
614
637
|
console.error(`❌ ${err.message}`);
|
|
615
638
|
process.exit(1);
|
|
616
639
|
}
|
|
640
|
+
} else if (command === "sandbox") {
|
|
641
|
+
try {
|
|
642
|
+
const sub = args[0];
|
|
643
|
+
let json = false;
|
|
644
|
+
/** @type {string[]} */
|
|
645
|
+
const rest = [];
|
|
646
|
+
|
|
647
|
+
for (let i = 1; i < args.length; i++) {
|
|
648
|
+
if (args[i] === "--json") {
|
|
649
|
+
json = true;
|
|
650
|
+
} else {
|
|
651
|
+
rest.push(args[i]);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const cwd = process.cwd();
|
|
656
|
+
const policy = await loadSandboxPolicy(cwd);
|
|
657
|
+
|
|
658
|
+
if (sub === "status") {
|
|
659
|
+
if (json) {
|
|
660
|
+
console.log(
|
|
661
|
+
JSON.stringify(
|
|
662
|
+
{
|
|
663
|
+
mode: policy.mode,
|
|
664
|
+
deny_rules: policy.deny_patterns.map((rule) => rule.id),
|
|
665
|
+
},
|
|
666
|
+
null,
|
|
667
|
+
2,
|
|
668
|
+
),
|
|
669
|
+
);
|
|
670
|
+
} else {
|
|
671
|
+
console.log(
|
|
672
|
+
`Sandbox mode: ${policy.mode} (${policy.deny_patterns.length} deny rule(s))`,
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
} else if (sub === "check-command") {
|
|
676
|
+
const shellCommand = rest.join(" ").trim();
|
|
677
|
+
if (!shellCommand) {
|
|
678
|
+
throw new Error('Usage: sandbox check-command "<shell command>" [--json]');
|
|
679
|
+
}
|
|
680
|
+
const result = checkSandboxCommand(shellCommand, policy);
|
|
681
|
+
process.stdout.write(formatSandboxCheck(result, shellCommand, { json }));
|
|
682
|
+
if (!result.allowed) {
|
|
683
|
+
process.exit(1);
|
|
684
|
+
}
|
|
685
|
+
} else {
|
|
686
|
+
throw new Error('Usage: sandbox status | check-command "<cmd>" [--json]');
|
|
687
|
+
}
|
|
688
|
+
} catch (err) {
|
|
689
|
+
console.error(`❌ ${err.message}`);
|
|
690
|
+
process.exit(1);
|
|
691
|
+
}
|
|
617
692
|
} else if (command === "context-guard") {
|
|
618
693
|
try {
|
|
619
694
|
const sub = args[0];
|
|
@@ -826,6 +901,65 @@ if (command === "--version" || command === "-v" || command === "version") {
|
|
|
826
901
|
console.error(`❌ ${err.message}`);
|
|
827
902
|
process.exit(1);
|
|
828
903
|
}
|
|
904
|
+
} else if (command === "req-analysis") {
|
|
905
|
+
try {
|
|
906
|
+
const sub = args[0];
|
|
907
|
+
const rest = args.slice(1);
|
|
908
|
+
|
|
909
|
+
if (sub === "init") {
|
|
910
|
+
let scope = "feature";
|
|
911
|
+
let force = false;
|
|
912
|
+
const positional = [];
|
|
913
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
914
|
+
const arg = rest[i];
|
|
915
|
+
if (arg === "--force") {
|
|
916
|
+
force = true;
|
|
917
|
+
} else if (arg === "--scope" && rest[i + 1]) {
|
|
918
|
+
scope = rest[i + 1];
|
|
919
|
+
i += 1;
|
|
920
|
+
} else if (arg.startsWith("--scope=")) {
|
|
921
|
+
scope = arg.slice("--scope=".length);
|
|
922
|
+
} else if (!arg.startsWith("--")) {
|
|
923
|
+
positional.push(arg);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const description = positional.join(" ").trim();
|
|
927
|
+
if (scope === "feature" && !description) {
|
|
928
|
+
throw new Error(
|
|
929
|
+
'Usage: req-analysis init "<description>" [--scope project|feature] [--force]',
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
const result = await reqAnalysisInit(description, { scope, force });
|
|
933
|
+
console.log(`✅ Elicitation scaffold (${result.scope})`);
|
|
934
|
+
for (const briefPath of result.paths) {
|
|
935
|
+
console.log(` ${briefPath}`);
|
|
936
|
+
}
|
|
937
|
+
console.log("");
|
|
938
|
+
console.log(await formatDiscoverReport(process.cwd()));
|
|
939
|
+
} else if (sub === "discover") {
|
|
940
|
+
console.log(await formatDiscoverReport(process.cwd()));
|
|
941
|
+
} else if (sub === "promote") {
|
|
942
|
+
let scope = "feature";
|
|
943
|
+
let description = "";
|
|
944
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
945
|
+
const arg = rest[i];
|
|
946
|
+
if (arg.startsWith("--scope=")) {
|
|
947
|
+
scope = arg.slice("--scope=".length);
|
|
948
|
+
} else if (arg === "--scope" && rest[i + 1]) {
|
|
949
|
+
scope = rest[i + 1];
|
|
950
|
+
i += 1;
|
|
951
|
+
} else if (!arg.startsWith("--")) {
|
|
952
|
+
description = `${description} ${arg}`.trim();
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
console.log(formatPromoteMessage({ scope, description }));
|
|
956
|
+
} else {
|
|
957
|
+
throw new Error("Usage: req-analysis init | discover | promote");
|
|
958
|
+
}
|
|
959
|
+
} catch (err) {
|
|
960
|
+
console.error(`❌ ${err.message}`);
|
|
961
|
+
process.exit(1);
|
|
962
|
+
}
|
|
829
963
|
} else if (command === "classify-change") {
|
|
830
964
|
try {
|
|
831
965
|
let json = false;
|
package/lib/constants.js
CHANGED
|
@@ -77,6 +77,7 @@ export const SKILL_ASSETS = [
|
|
|
77
77
|
/** @type {{ file: string, remotePath: string }[]} */
|
|
78
78
|
export const REFERENCE_ASSETS = [
|
|
79
79
|
{ file: "explore.md", remotePath: "skills/references/explore.md" },
|
|
80
|
+
{ file: "elicitation.md", remotePath: "skills/references/elicitation.md" },
|
|
80
81
|
{ file: "solution-exploration.md", remotePath: "skills/references/solution-exploration.md" },
|
|
81
82
|
{ file: "project-init.md", remotePath: "skills/references/project-init.md" },
|
|
82
83
|
{ file: "constitution.md", remotePath: "skills/references/constitution.md" },
|
|
@@ -115,6 +116,8 @@ export const SCRIPT_ASSETS = [
|
|
|
115
116
|
{ file: "memory_retrieve.py", remotePath: "scripts/memory_retrieve.py" },
|
|
116
117
|
{ file: "_memory_config.py", remotePath: "scripts/_memory_config.py" },
|
|
117
118
|
{ file: "_memory_embed.py", remotePath: "scripts/_memory_embed.py" },
|
|
119
|
+
{ file: "episodes.py", remotePath: "scripts/episodes.py" },
|
|
120
|
+
{ file: "code_index.py", remotePath: "scripts/code_index.py" },
|
|
118
121
|
];
|
|
119
122
|
|
|
120
123
|
/** @type {{ file: string, remotePath: string }[]} */
|
package/lib/cursor-hooks.js
CHANGED
|
@@ -4,9 +4,22 @@ import path from "node:path";
|
|
|
4
4
|
import { packagedAssetPath } from "./assets.js";
|
|
5
5
|
import { ensureDir, readFileSafe } from "./fs-utils.js";
|
|
6
6
|
|
|
7
|
-
export const
|
|
7
|
+
export const CURSOR_HOOK_EDIT = ".cursor/hooks/context-guard-edit.mjs";
|
|
8
|
+
export const CURSOR_HOOK_SANDBOX = ".cursor/hooks/sandbox-shell.mjs";
|
|
8
9
|
export const CURSOR_HOOKS_JSON = ".cursor/hooks.json";
|
|
9
10
|
|
|
11
|
+
/** @type {readonly { source: string, dest: string }[]} */
|
|
12
|
+
export const CURSOR_HOOK_SCRIPTS = [
|
|
13
|
+
{
|
|
14
|
+
source: "templates/cursor/hooks/context-guard-edit.mjs",
|
|
15
|
+
dest: CURSOR_HOOK_EDIT,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
source: "templates/cursor/hooks/sandbox-shell.mjs",
|
|
19
|
+
dest: CURSOR_HOOK_SANDBOX,
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
10
23
|
/**
|
|
11
24
|
* Merge shipped hook entries without removing user hooks.
|
|
12
25
|
*
|
|
@@ -58,7 +71,7 @@ export function mergeCursorHooksConfig(existing, template) {
|
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
/**
|
|
61
|
-
* Install Cursor hooks
|
|
74
|
+
* Install Cursor hooks for context-guard and sandbox policy.
|
|
62
75
|
*
|
|
63
76
|
* @param {string} cwd
|
|
64
77
|
* @param {{ log?: (message: string) => void }} [options]
|
|
@@ -68,14 +81,14 @@ export async function installCursorHooks(cwd, options = {}) {
|
|
|
68
81
|
const hooksDir = path.join(cwd, ".cursor/hooks");
|
|
69
82
|
await ensureDir(hooksDir);
|
|
70
83
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
for (const script of CURSOR_HOOK_SCRIPTS) {
|
|
85
|
+
const scriptDest = path.join(cwd, script.dest);
|
|
86
|
+
await fs.copyFile(packagedAssetPath(script.source), scriptDest);
|
|
87
|
+
try {
|
|
88
|
+
await fs.chmod(scriptDest, 0o755);
|
|
89
|
+
} catch {
|
|
90
|
+
// Windows may ignore chmod; node can still execute the script.
|
|
91
|
+
}
|
|
79
92
|
}
|
|
80
93
|
|
|
81
94
|
const template = JSON.parse(
|
|
@@ -92,5 +105,5 @@ export async function installCursorHooks(cwd, options = {}) {
|
|
|
92
105
|
|
|
93
106
|
const merged = mergeCursorHooksConfig(existing, template);
|
|
94
107
|
await fs.writeFile(hooksJsonPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
|
|
95
|
-
log(`✅ Cursor hooks → ${CURSOR_HOOKS_JSON} (context-guard
|
|
108
|
+
log(`✅ Cursor hooks → ${CURSOR_HOOKS_JSON} (context-guard + sandbox shell checks)`);
|
|
96
109
|
}
|
package/lib/gates.js
CHANGED
|
@@ -45,6 +45,8 @@ const AUX_SCRIPTS = {
|
|
|
45
45
|
"memory-query": "memory_query.py",
|
|
46
46
|
"memory-search": "memory_search.py",
|
|
47
47
|
"memory-retrieve": "memory_retrieve.py",
|
|
48
|
+
episodes: "episodes.py",
|
|
49
|
+
"code-index": "code_index.py",
|
|
48
50
|
};
|
|
49
51
|
|
|
50
52
|
const GUARDRAILS_SCRIPTS = { ...GATE_SCRIPTS, ...AUX_SCRIPTS };
|