@luizsantiago/spec-guardrails 3.6.0 → 3.9.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 +86 -201
- package/index.js +64 -0
- package/lib/constants.js +2 -0
- package/lib/cursor-hooks.js +109 -0
- package/lib/doctor.js +10 -4
- package/lib/gates.js +43 -0
- package/lib/install.js +3 -0
- package/lib/memory-doctor.js +80 -0
- package/lib/sandbox-policy.js +173 -0
- package/package.json +3 -3
- 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 +187 -1
- package/skills/agent-architecture.md +4 -1
- package/templates/GETTING_STARTED.md +2 -0
- package/templates/config.yaml.example +10 -0
- package/templates/cursor/hooks/context-guard-edit.mjs +170 -0
- package/templates/cursor/hooks/sandbox-shell.mjs +102 -0
- package/templates/cursor/hooks.json +18 -0
package/README.md
CHANGED
|
@@ -5,207 +5,139 @@
|
|
|
5
5
|
|
|
6
6
|
**Keep AI coding agents honest — specify the work, prove each step, verify independently.**
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
| --- | --- |
|
|
10
|
-
| **Problem** | Agents ship “looks good” with thin specs, missing evidence, and the same context that wrote the code declaring victory. |
|
|
11
|
-
| **Solution** | One kit, two deliberate modes: **Process** (Node only) for a flexible spec-driven workflow; **Brakes** (Node + Python) for the **full product** — structural gates that exit non-zero when paperwork or evidence is missing. You approve specs/tasks in both. |
|
|
12
|
-
| **Result** | Traceable `.specs/` memory, fewer fake finishes, cheaper turns (~70% less skill text on planning). Choose Process for light ceremony; add Python when you want the [Guarantees matrix](#guarantees-matrix) enforced automatically. |
|
|
13
|
-
|
|
14
|
-
npm: [`@luizsantiago/spec-guardrails`](https://www.npmjs.com/package/@luizsantiago/spec-guardrails) **3.6.x**
|
|
8
|
+
npm: [`@luizsantiago/spec-guardrails`](https://www.npmjs.com/package/@luizsantiago/spec-guardrails) **3.9.x**
|
|
15
9
|
|
|
16
10
|
---
|
|
17
11
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npx @luizsantiago/spec-guardrails install
|
|
22
|
-
```
|
|
12
|
+
## What is Spec Guardrails?
|
|
23
13
|
|
|
24
|
-
|
|
14
|
+
**Spec Guardrails** is a **process kit** for AI-assisted software development. It does not replace your app, framework, or tests. It gives your agent a repeatable way to work:
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
1. **Write down the goal** before coding (`spec.md`)
|
|
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”
|
|
30
20
|
|
|
31
|
-
|
|
21
|
+
Everything important lives in **`.specs/`** in your repo so the project remembers decisions across chats and teammates.
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
| --- | --- |
|
|
35
|
-
| `.cursor/skills/` + `.claude/skills/` + `.github/skills/` + `.codex/skills/` | Hub, phase references, sister skills (**shipped adapters** — same content, product-specific paths) |
|
|
36
|
-
| `.specs/guardrails/scripts/` | Python gate scripts (Brakes mode) |
|
|
37
|
-
| `.specs/STATE.md`, `.specs/features/`, … | Project memory (any agent) |
|
|
38
|
-
| `.cursor/rules/engineering-baseline.mdc` | Always-on Cursor rule |
|
|
23
|
+
**You** approve specs and tasks. **The agent** follows phase guides and runs checks. **Gates** (optional Python scripts) can **stop** the workflow when paperwork or evidence is missing.
|
|
39
24
|
|
|
40
|
-
**
|
|
41
|
-
|
|
42
|
-
Re-run `install` anytime to refresh skills; your `.specs/` decisions and `STATE.md` are kept.
|
|
43
|
-
|
|
44
|
-
### Governance focus (3.2+)
|
|
45
|
-
|
|
46
|
-
Spec Guardrails is a **single product**: governance, evidence, verification, and controlled execution for agentic software development — not a bundle of companion packages.
|
|
47
|
-
|
|
48
|
-
| Capability | What it does |
|
|
49
|
-
| --- | --- |
|
|
50
|
-
| **Artifact gates** | Structural quality for spec, tasks, and cross-artifact consistency — with **blocking / warning / info** severity |
|
|
51
|
-
| **Parallel waves** | `loop-plan` computes safe parallel groups; `workspace-prepare` isolates tasks in git worktrees |
|
|
52
|
-
| **Execution policy** | Budgets, path scope, and escalation rules in `.specs/config.yaml` — consult via `execution-policy` |
|
|
53
|
-
| **Independent verify** | Fresh-context verification with evidence-or-zero (`validate-state`) |
|
|
54
|
-
|
|
55
|
-
| Need | Command |
|
|
56
|
-
| --- | --- |
|
|
57
|
-
| First time / upgrade | `install` |
|
|
58
|
-
| Existing codebase | `project-init` (optional) |
|
|
59
|
-
| Something looks wrong | `doctor` |
|
|
60
|
-
| Full CLI list | `--help` |
|
|
25
|
+
> **Plain-language guide:** [Overview](docs/guide/Overview.md) · [How it works](docs/guide/How-it-works.md) · [Quick start](docs/guide/Quick-start.md)
|
|
61
26
|
|
|
62
27
|
---
|
|
63
28
|
|
|
64
|
-
##
|
|
29
|
+
## What it is not
|
|
65
30
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| **Spec-driven** | Written plan before code | `spec.md` + `tasks.md`; evidence before “done” |
|
|
71
|
-
| **Brakes / Gates** | Structural stop-gates | Python scripts exit non-zero when paperwork or evidence is missing |
|
|
72
|
-
| **Loop** | Execute in waves | `loop-plan` picks the next jobs; sub-agents when files don’t overlap |
|
|
73
|
-
| **Graph** | Parallel task map | `task-graph.md` — safe parallelism without file collisions |
|
|
74
|
-
| **Memory** | Persistent project state | `.specs/` — specs, decisions, and handoff survive across chats |
|
|
75
|
-
|
|
76
|
-
**You** approve specs and tasks. **The agent** runs gates and implements. **Gates** exit non-zero when paperwork or evidence is missing.
|
|
77
|
-
|
|
78
|
-
Plain-language tour: [Home](docs/guide/Home.md) · [How it works](docs/guide/How-it-works.md) · [Quick start](docs/guide/Quick-start.md)
|
|
31
|
+
- Not a code generator or a new framework
|
|
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
|
|
79
35
|
|
|
80
36
|
---
|
|
81
37
|
|
|
82
|
-
##
|
|
83
|
-
|
|
84
|
-
Two modes, one package — pick how much rigor you want:
|
|
85
|
-
|
|
86
|
-
| Mode | Runtime | What you get | Best for |
|
|
87
|
-
| --- | --- | --- | --- |
|
|
88
|
-
| **Process** | Node.js 18+ | Spec-driven workflow, `.specs/` memory, progressive loading, independent `/verify` | Flexible ceremony, exploration, teams that enforce by review |
|
|
89
|
-
| **Brakes** | Node + **Python 3.10+** | Everything in Process **plus** Python gates from the [Guarantees matrix](#guarantees-matrix) — exit ≠ 0 → stop and fix | The **full Spec Guardrails** — traceability, evidence, and structural guarantees enforced automatically |
|
|
90
|
-
|
|
91
|
-
**Gates stay Python.** That is the product: Brakes mode is the complete version with automated enforcement. Process mode is the same loop without exit-code brakes — intentional, not incomplete.
|
|
92
|
-
|
|
93
|
-
Install Python when you want gates to fire; run `doctor` to confirm Brakes are available.
|
|
38
|
+
## Install (once per project)
|
|
94
39
|
|
|
95
|
-
|
|
40
|
+
```bash
|
|
41
|
+
npx @luizsantiago/spec-guardrails install
|
|
42
|
+
```
|
|
96
43
|
|
|
97
|
-
|
|
44
|
+
| Requirement | Purpose |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| **Node.js 18+** | Required — CLI and install |
|
|
47
|
+
| **Python 3.10+** | Optional — enables **Brakes mode** (automatic gates). Without Python you still get the full workflow in **Process mode** |
|
|
98
48
|
|
|
99
|
-
|
|
49
|
+
Re-run `install` after upgrading the package. Your `.specs/` notes and `STATE.md` are kept.
|
|
100
50
|
|
|
101
|
-
|
|
102
|
-
| --- | --- | --- | --- |
|
|
103
|
-
| Intent exists before code | `validate-spec` | Brakes | Hard gate |
|
|
104
|
-
| Tasks derive from requirements | `analyze-artifacts` | Brakes | Hard gate |
|
|
105
|
-
| Task shape and graph when needed | `validate-tasks` | Brakes | Hard gate |
|
|
106
|
-
| Requirements stay traceable | `validate-traceability` | Brakes | Hard gate |
|
|
107
|
-
| Quick evidence is complete | `validate-quick` | Brakes | Hard gate |
|
|
108
|
-
| Dependencies respected in Execute | `loop-plan` | Brakes | Hard gate |
|
|
109
|
-
| Parallel work is file-safe | `task-graph.md` + `validate-tasks` | Process + Brakes | Artifact + gate |
|
|
110
|
-
| Completion cites evidence | `validate-state` | Brakes | Hard gate |
|
|
111
|
-
| Commits follow policy | `check-commit` | Brakes | Hard gate |
|
|
112
|
-
| Lessons grounded after FAIL | `lessons` | Brakes | Hard gate |
|
|
113
|
-
| Verification is independent | `/verify` + `validate.md` | Process | Phase skill |
|
|
114
|
-
| Knowledge survives chats | `.specs/` + `archive-feature` | Process | Install + CLI |
|
|
51
|
+
Check readiness: `npx @luizsantiago/spec-guardrails doctor`
|
|
115
52
|
|
|
116
|
-
|
|
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).
|
|
117
54
|
|
|
118
55
|
---
|
|
119
56
|
|
|
120
|
-
##
|
|
57
|
+
## How you use it day to day
|
|
121
58
|
|
|
122
|
-
|
|
59
|
+
You talk to the agent in **chat**, not the terminal. These are **agent commands** (phrases the agent understands):
|
|
123
60
|
|
|
124
|
-
|
|
|
125
|
-
|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
|
129
|
-
|
|
|
130
|
-
|
|
|
61
|
+
| Command | When to use |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `/specify` | Start a real feature — written requirements first |
|
|
64
|
+
| `/tasks` | Shopping list of jobs after you approve the spec |
|
|
65
|
+
| `/loop` | Implement — one wave at a time |
|
|
66
|
+
| `/verify` | Proof after the last job — use a **fresh** chat when possible |
|
|
67
|
+
| `/quick` | Tiny fix only (≤3 files, no big design decisions) |
|
|
131
68
|
|
|
132
|
-
|
|
69
|
+
**Typical path:** `/specify` → approve → `/tasks` → approve → `/loop` → `/verify` → `/archive`
|
|
133
70
|
|
|
134
|
-
|
|
71
|
+
You do **not** need to memorize CLI commands. The agent runs gates and helpers for you when **Brakes mode** is available.
|
|
135
72
|
|
|
136
|
-
|
|
73
|
+
**Full command reference:** [Agent commands](docs/guide/agent-commands.md)
|
|
137
74
|
|
|
138
|
-
|
|
75
|
+
---
|
|
139
76
|
|
|
140
|
-
|
|
77
|
+
## Two modes (same product)
|
|
141
78
|
|
|
142
|
-
|
|
|
79
|
+
| Mode | You need | What changes |
|
|
143
80
|
| --- | --- | --- |
|
|
144
|
-
| **
|
|
145
|
-
| **
|
|
146
|
-
| **Medium** | New feature, <10 tasks | `/specify` → `/tasks` → `/loop` → `/verify` → `/archive` |
|
|
147
|
-
| **Complex** | APIs, architecture, infra | + `/discuss`, `/plan`, optional security/QA on verify |
|
|
148
|
-
| **Parallel** | Splittable work | Above + `/task-graph` when 3+ tasks |
|
|
149
|
-
|
|
150
|
-
Rules and examples: [Concepts → Complexity tiers](docs/guide/concepts.md#complexity-tiers--how-the-agent-chooses-depth)
|
|
151
|
-
|
|
152
|
-
---
|
|
81
|
+
| **Process** | Node only | Full workflow + `.specs/` memory + phase skills |
|
|
82
|
+
| **Brakes** | Node + Python | Same + scripts that **exit non-zero** when specs, tasks, or evidence are incomplete |
|
|
153
83
|
|
|
154
|
-
|
|
84
|
+
Both are intentional. Add Python when you want automatic stop signs, not just checklists.
|
|
155
85
|
|
|
156
|
-
|
|
86
|
+
Details: [FAQ → Process vs Brakes](docs/guide/FAQ.md#process-vs-brakes)
|
|
157
87
|
|
|
158
|
-
|
|
159
|
-
| ---: | --- | --- | --- |
|
|
160
|
-
| 1 | **Hub** | Contract, complexity router, gate schedule | `agent-architecture.md` |
|
|
161
|
-
| 2 | **Reference** | One phase procedure per turn | `specify.md`, `implement.md`, `validate.md` |
|
|
162
|
-
| 3 | **Sister** (optional) | Cross-cutting depth, on demand | `engineering-standards.md`, `task-graph-engineering.md` |
|
|
163
|
-
| 4 | **Gate** | Automatic check at the boundary | `validate-spec`, `loop-plan`, `check-commit` |
|
|
88
|
+
---
|
|
164
89
|
|
|
165
|
-
|
|
90
|
+
## What lands in your repo
|
|
166
91
|
|
|
167
|
-
|
|
92
|
+
| Path | Role |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| `.cursor/skills/` (and Claude, Copilot, Codex trees) | Instructions the agent reads per phase |
|
|
95
|
+
| `.specs/STATE.md` | Where you are — active feature, next step |
|
|
96
|
+
| `.specs/features/NNN-slug/` | Spec, tasks, validation for each feature |
|
|
97
|
+
| `.specs/guardrails/scripts/` | Python gates (Brakes mode) |
|
|
98
|
+
| `.specs/config.yaml` | Optional project rules and policy |
|
|
168
99
|
|
|
169
100
|
---
|
|
170
101
|
|
|
171
|
-
##
|
|
102
|
+
## Optional capabilities (use when you need them)
|
|
172
103
|
|
|
173
|
-
|
|
104
|
+
Most projects start with Specify → Tasks → Loop → Verify only. Turn these on when the work warrants it:
|
|
174
105
|
|
|
175
|
-
|
|
|
106
|
+
| Capability | Plain purpose | Learn more |
|
|
176
107
|
| --- | --- | --- |
|
|
177
|
-
|
|
|
178
|
-
|
|
|
179
|
-
|
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
|
|
|
183
|
-
|
|
|
184
|
-
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Full reference: **[Gates](docs/guide/gates.md)** · [Guarantees matrix](docs/guide/Guarantees-matrix.md) · [Gates and guarantees](docs/guide/Gates-and-guarantees.md)
|
|
108
|
+
| **Memory search** | Find text in past specs and validations | [Memory guide](docs/guide/Memory.md) |
|
|
109
|
+
| **Context guards** | Check scope before edit or “done” | [Agent commands](docs/guide/agent-commands.md) · **Cursor:** auto via hooks |
|
|
110
|
+
| **Episodic memory** | Session notes → episodic → lessons | [Memory → Episodes](docs/guide/Memory.md#episodic-memory-lifecycle) |
|
|
111
|
+
| **Code index** | Lightweight brownfield file/symbol map | [Brownfield context](docs/guide/brownfield-context.md) |
|
|
112
|
+
| **Sandbox policy** | Block/warn destructive shell commands | [Overview → Safety](docs/guide/Overview.md#safety-and-limits) |
|
|
113
|
+
| **Execution policy** | Limit paths, retries, dangerous ops | [Overview → Safety](docs/guide/Overview.md#safety-and-limits) |
|
|
114
|
+
| **Solution exploration** | Compare two+ implementations before committing | [Overview → Exploration](docs/guide/Overview.md#optional-exploration-mode) |
|
|
115
|
+
| **Semantic retrieval** | Search by meaning (needs OpenAI or Ollama) | [Memory → Semantic](docs/guide/Memory.md#semantic-search-optional) |
|
|
116
|
+
|
|
117
|
+
**Default:** semantic search is **off**. FTS + graph search work without any API.
|
|
189
118
|
|
|
190
119
|
---
|
|
191
120
|
|
|
192
121
|
## Documentation
|
|
193
122
|
|
|
194
|
-
|
|
|
123
|
+
| Start here | Best for |
|
|
195
124
|
| --- | --- |
|
|
196
|
-
| [
|
|
197
|
-
| [Architecture](docs/guide/Architecture.md) | Core vs platform adapters |
|
|
198
|
-
| [Agent commands](docs/guide/agent-commands.md) | Every `/specify`, `/loop`, `/verify`, … — purpose, when, examples |
|
|
125
|
+
| **[Overview](docs/guide/Overview.md)** | Complete simple picture — workflow, layers, team use |
|
|
199
126
|
| [Quick start](docs/guide/Quick-start.md) | First ten minutes |
|
|
200
|
-
| [
|
|
201
|
-
| [
|
|
202
|
-
| [Gates](docs/guide/gates.md) | How each gate works |
|
|
203
|
-
| [Platform parity](docs/guide/Platform-parity.md) | Shipped adapters (Cursor, Claude, Copilot, Codex) — core works with any agent |
|
|
204
|
-
| [Restart PRD seed](docs/guide/Restart-prd-seed.md) | Clean-project PRD template for a single-package restart |
|
|
127
|
+
| [How it works](docs/guide/How-it-works.md) | Story from idea to archive |
|
|
128
|
+
| [Memory](docs/guide/Memory.md) | `.specs/` memory and search — when and how much |
|
|
205
129
|
| [FAQ](docs/guide/FAQ.md) | Common questions |
|
|
206
|
-
| [Changelog](docs/CHANGELOG.md) | Full version history |
|
|
207
130
|
|
|
208
|
-
|
|
131
|
+
| Go deeper | Best for |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| [Agent commands](docs/guide/agent-commands.md) | Every `/specify`, `/loop`, CLI helper |
|
|
134
|
+
| [Guarantees matrix](docs/guide/Guarantees-matrix.md) | Product promises → mechanisms |
|
|
135
|
+
| [Gates](docs/guide/gates.md) | What each gate checks |
|
|
136
|
+
| [Architecture](docs/guide/Architecture.md) | Core vs platform adapters |
|
|
137
|
+
| [Concepts](docs/guide/concepts.md) | Spec-driven, loop, graph, tiers |
|
|
138
|
+
| [Changelog](docs/CHANGELOG.md) | Version history |
|
|
139
|
+
|
|
140
|
+
Doc index: [docs/guide/README.md](docs/guide/README.md)
|
|
209
141
|
|
|
210
142
|
---
|
|
211
143
|
|
|
@@ -215,59 +147,12 @@ Start after install: [Quick start](docs/guide/Quick-start.md) · [Agent commands
|
|
|
215
147
|
npx @luizsantiago/spec-guardrails install
|
|
216
148
|
```
|
|
217
149
|
|
|
218
|
-
|
|
219
|
-
| --- | --- |
|
|
220
|
-
| **3.6.x** | Hybrid retrieval (`memory-retrieve`); chunk FTS; optional semantic embed |
|
|
221
|
-
| **3.5.x** | Solution exploration (`solution-explore`) — compare candidates from approved spec |
|
|
222
|
-
| **3.4.x** | Contextual guards (`context-guard`); FTS memory search (`memory-search`) |
|
|
223
|
-
| **3.3.x** | Intent/effect policy — `check-path --op read|write|delete` and `effects` config block |
|
|
224
|
-
| **3.2.x** | Single-package focus; artifact gate severity labels; git worktree isolation CLI; execution policy (budget/scope/escalation) |
|
|
225
|
-
| **3.1.x** | Copilot/Codex/AGENTS.md adapters; doctor Process + Brakes scores; `validate-traceability` / `validate-quick`; `classify-change` / `feature-status` |
|
|
226
|
-
| **3.0.x** | Final name Spec Guardrails; `.specs/guardrails/`; no dual-path ([Migration](docs/guide/Migration.md)) |
|
|
227
|
-
| **2.2.x** | Seatbelt-era paths & markers; `doctor` Execute hints; docs split from README |
|
|
228
|
-
| **2.1.x** | `loop-plan` + parallel `/loop` waves |
|
|
229
|
-
| **2.0.x** | Package rename → `@luizsantiago/spec-seatbelt` (superseded by 3.0) |
|
|
230
|
-
| **1.1.x** | `project-init` for brownfield repos |
|
|
231
|
-
| **0.9.x** | `archive-feature` + domain memory merge |
|
|
232
|
-
|
|
233
|
-
Full history: [CHANGELOG](docs/CHANGELOG.md) · [Releases](https://github.com/luizssantiago92/spec-guardrails/releases) · [Stability policy](docs/guide/Stability-policy.md)
|
|
234
|
-
|
|
235
|
-
Lineage: `agentic-harness` → `spec-seatbelt` → **`spec-guardrails` (final)**. Run `install` once after switching. See [Migration](docs/guide/Migration.md).
|
|
150
|
+
See [CHANGELOG](docs/CHANGELOG.md) for release notes.
|
|
236
151
|
|
|
237
152
|
---
|
|
238
153
|
|
|
239
|
-
## Contributing
|
|
240
|
-
|
|
241
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) — tests, gate freeze policy, local `npm run guardrails -- install`.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Credits
|
|
246
|
-
|
|
247
|
-
Spec Guardrails adapts open ideas; we did not invent spec-driven phases, loop design, or task-graph rules.
|
|
248
|
-
|
|
249
|
-
### Core lineage
|
|
250
|
-
|
|
251
|
-
| Source | License | How we use it |
|
|
252
|
-
| --- | --- | --- |
|
|
253
|
-
| [tlc-spec-driven](https://github.com/tech-leads-club/agent-skills/tree/main/packages/skills-catalog/skills/(development)/tlc-spec-driven) | CC-BY-4.0 | Phase model, `.specs/` memory, gate lineage |
|
|
254
|
-
| [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) | MIT | Discuss patterns, definition-of-done |
|
|
255
|
-
| [graph-engineering](https://github.com/codejunkie99/graph-engineering) | MIT | Task-graph topology, stop rules, parallel merge |
|
|
256
|
-
|
|
257
|
-
### Loop & ecosystem
|
|
258
|
-
|
|
259
|
-
| Source | License | How we use it |
|
|
260
|
-
| --- | --- | --- |
|
|
261
|
-
| [loop-engineering](https://github.com/cobusgreyling/loop-engineering) | MIT | Operational loop patterns; `doctor` score metaphor |
|
|
262
|
-
| [Addy Osmani — Loop engineering](https://addyosmani.com/blog/loop-engineering/) | — | Essay lineage |
|
|
263
|
-
| [awesome-harness-engineering](https://github.com/ai-boost/awesome-harness-engineering) | CC0 | Ecosystem taxonomy |
|
|
264
|
-
|
|
265
|
-
### Adjacent (not vendored)
|
|
266
|
-
|
|
267
|
-
[DeepCode](https://github.com/HKUDS/DeepCode) · [RepoGraph](https://github.com/ozyyshr/RepoGraph)
|
|
268
|
-
|
|
269
|
-
Extended attribution: [docs/guide/credits.md](docs/guide/credits.md)
|
|
270
|
-
|
|
271
|
-
## License
|
|
154
|
+
## Contributing · Credits · License
|
|
272
155
|
|
|
273
|
-
|
|
156
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
157
|
+
- [Credits](docs/guide/credits.md)
|
|
158
|
+
- MIT — see [LICENSE](LICENSE)
|
package/index.js
CHANGED
|
@@ -45,6 +45,11 @@ import {
|
|
|
45
45
|
recordExplorationDecision,
|
|
46
46
|
validateExplorationArtifact,
|
|
47
47
|
} from "./lib/solution-exploration.js";
|
|
48
|
+
import {
|
|
49
|
+
checkSandboxCommand,
|
|
50
|
+
formatSandboxCheck,
|
|
51
|
+
loadSandboxPolicy,
|
|
52
|
+
} from "./lib/sandbox-policy.js";
|
|
48
53
|
import {
|
|
49
54
|
initProjectConfig,
|
|
50
55
|
listPresets,
|
|
@@ -113,6 +118,13 @@ Commands:
|
|
|
113
118
|
memory-retrieve "<query>" Hybrid retrieval (FTS + graph + optional semantic)
|
|
114
119
|
[--mode fts|hybrid|semantic] Strategy (default: hybrid)
|
|
115
120
|
[--json] Machine-readable output
|
|
121
|
+
episodes record --summary "…" Capture working-session episodic memory
|
|
122
|
+
episodes list|archive|prune|promote Episodic lifecycle (working → episodic → promoted)
|
|
123
|
+
code-index rebuild [--roots src,lib] Lightweight brownfield code map (not full RepoGraph)
|
|
124
|
+
code-index search "<query>" Search indexed files/symbols/imports
|
|
125
|
+
sandbox status Show sandbox policy mode (off|warn|strict)
|
|
126
|
+
sandbox check-command "<cmd>" Soft OS sandbox — block/warn destructive shell commands
|
|
127
|
+
[--json] Machine-readable output
|
|
116
128
|
context-guard status Execute readiness from STATE + tasks.md
|
|
117
129
|
[--json] Machine-readable output
|
|
118
130
|
context-guard check-edit <path> Contextual guard before editing a file
|
|
@@ -614,6 +626,58 @@ if (command === "--version" || command === "-v" || command === "version") {
|
|
|
614
626
|
console.error(`❌ ${err.message}`);
|
|
615
627
|
process.exit(1);
|
|
616
628
|
}
|
|
629
|
+
} else if (command === "sandbox") {
|
|
630
|
+
try {
|
|
631
|
+
const sub = args[0];
|
|
632
|
+
let json = false;
|
|
633
|
+
/** @type {string[]} */
|
|
634
|
+
const rest = [];
|
|
635
|
+
|
|
636
|
+
for (let i = 1; i < args.length; i++) {
|
|
637
|
+
if (args[i] === "--json") {
|
|
638
|
+
json = true;
|
|
639
|
+
} else {
|
|
640
|
+
rest.push(args[i]);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const cwd = process.cwd();
|
|
645
|
+
const policy = await loadSandboxPolicy(cwd);
|
|
646
|
+
|
|
647
|
+
if (sub === "status") {
|
|
648
|
+
if (json) {
|
|
649
|
+
console.log(
|
|
650
|
+
JSON.stringify(
|
|
651
|
+
{
|
|
652
|
+
mode: policy.mode,
|
|
653
|
+
deny_rules: policy.deny_patterns.map((rule) => rule.id),
|
|
654
|
+
},
|
|
655
|
+
null,
|
|
656
|
+
2,
|
|
657
|
+
),
|
|
658
|
+
);
|
|
659
|
+
} else {
|
|
660
|
+
console.log(
|
|
661
|
+
`Sandbox mode: ${policy.mode} (${policy.deny_patterns.length} deny rule(s))`,
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
} else if (sub === "check-command") {
|
|
665
|
+
const shellCommand = rest.join(" ").trim();
|
|
666
|
+
if (!shellCommand) {
|
|
667
|
+
throw new Error('Usage: sandbox check-command "<shell command>" [--json]');
|
|
668
|
+
}
|
|
669
|
+
const result = checkSandboxCommand(shellCommand, policy);
|
|
670
|
+
process.stdout.write(formatSandboxCheck(result, shellCommand, { json }));
|
|
671
|
+
if (!result.allowed) {
|
|
672
|
+
process.exit(1);
|
|
673
|
+
}
|
|
674
|
+
} else {
|
|
675
|
+
throw new Error('Usage: sandbox status | check-command "<cmd>" [--json]');
|
|
676
|
+
}
|
|
677
|
+
} catch (err) {
|
|
678
|
+
console.error(`❌ ${err.message}`);
|
|
679
|
+
process.exit(1);
|
|
680
|
+
}
|
|
617
681
|
} else if (command === "context-guard") {
|
|
618
682
|
try {
|
|
619
683
|
const sub = args[0];
|
package/lib/constants.js
CHANGED
|
@@ -115,6 +115,8 @@ export const SCRIPT_ASSETS = [
|
|
|
115
115
|
{ file: "memory_retrieve.py", remotePath: "scripts/memory_retrieve.py" },
|
|
116
116
|
{ file: "_memory_config.py", remotePath: "scripts/_memory_config.py" },
|
|
117
117
|
{ file: "_memory_embed.py", remotePath: "scripts/_memory_embed.py" },
|
|
118
|
+
{ file: "episodes.py", remotePath: "scripts/episodes.py" },
|
|
119
|
+
{ file: "code_index.py", remotePath: "scripts/code_index.py" },
|
|
118
120
|
];
|
|
119
121
|
|
|
120
122
|
/** @type {{ file: string, remotePath: string }[]} */
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { packagedAssetPath } from "./assets.js";
|
|
5
|
+
import { ensureDir, readFileSafe } from "./fs-utils.js";
|
|
6
|
+
|
|
7
|
+
export const CURSOR_HOOK_EDIT = ".cursor/hooks/context-guard-edit.mjs";
|
|
8
|
+
export const CURSOR_HOOK_SANDBOX = ".cursor/hooks/sandbox-shell.mjs";
|
|
9
|
+
export const CURSOR_HOOKS_JSON = ".cursor/hooks.json";
|
|
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
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Merge shipped hook entries without removing user hooks.
|
|
25
|
+
*
|
|
26
|
+
* @param {Record<string, unknown> | null | undefined} existing
|
|
27
|
+
* @param {Record<string, unknown>} template
|
|
28
|
+
* @returns {Record<string, unknown>}
|
|
29
|
+
*/
|
|
30
|
+
export function mergeCursorHooksConfig(existing, template) {
|
|
31
|
+
/** @type {Record<string, unknown>} */
|
|
32
|
+
const result =
|
|
33
|
+
existing && typeof existing === "object"
|
|
34
|
+
? structuredClone(existing)
|
|
35
|
+
: { version: 1, hooks: {} };
|
|
36
|
+
|
|
37
|
+
result.version = result.version ?? template.version ?? 1;
|
|
38
|
+
/** @type {Record<string, unknown[]>} */
|
|
39
|
+
const hooks =
|
|
40
|
+
result.hooks && typeof result.hooks === "object"
|
|
41
|
+
? /** @type {Record<string, unknown[]>} */ (result.hooks)
|
|
42
|
+
: {};
|
|
43
|
+
result.hooks = hooks;
|
|
44
|
+
|
|
45
|
+
/** @type {Record<string, unknown[]>} */
|
|
46
|
+
const templateHooks =
|
|
47
|
+
template.hooks && typeof template.hooks === "object"
|
|
48
|
+
? /** @type {Record<string, unknown[]>} */ (template.hooks)
|
|
49
|
+
: {};
|
|
50
|
+
|
|
51
|
+
for (const [event, entries] of Object.entries(templateHooks)) {
|
|
52
|
+
if (!Array.isArray(entries)) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
if (!entry || typeof entry !== "object") {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const command = /** @type {{ command?: string }} */ (entry).command;
|
|
62
|
+
if (!command || current.some((item) => item?.command === command)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
current.push(entry);
|
|
66
|
+
}
|
|
67
|
+
hooks[event] = current;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Install Cursor hooks for context-guard and sandbox policy.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} cwd
|
|
77
|
+
* @param {{ log?: (message: string) => void }} [options]
|
|
78
|
+
*/
|
|
79
|
+
export async function installCursorHooks(cwd, options = {}) {
|
|
80
|
+
const log = options.log ?? (() => {});
|
|
81
|
+
const hooksDir = path.join(cwd, ".cursor/hooks");
|
|
82
|
+
await ensureDir(hooksDir);
|
|
83
|
+
|
|
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
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const template = JSON.parse(
|
|
95
|
+
await readFileSafe(packagedAssetPath("templates/cursor/hooks.json")),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const hooksJsonPath = path.join(cwd, CURSOR_HOOKS_JSON);
|
|
99
|
+
let existing = null;
|
|
100
|
+
try {
|
|
101
|
+
existing = JSON.parse(await readFileSafe(hooksJsonPath));
|
|
102
|
+
} catch {
|
|
103
|
+
existing = null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const merged = mergeCursorHooksConfig(existing, template);
|
|
107
|
+
await fs.writeFile(hooksJsonPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
|
|
108
|
+
log(`✅ Cursor hooks → ${CURSOR_HOOKS_JSON} (context-guard + sandbox shell checks)`);
|
|
109
|
+
}
|
package/lib/doctor.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { resolvePython, resolveScriptsDir } from "./gates.js";
|
|
12
12
|
import { readFileSafe } from "./fs-utils.js";
|
|
13
13
|
import { listFeatureIds, readActiveFeatureFromState } from "./specs-utils.js";
|
|
14
|
+
import { resolveMemoryHint } from "./memory-doctor.js";
|
|
14
15
|
|
|
15
16
|
const execFileAsync = promisify(execFile);
|
|
16
17
|
|
|
@@ -368,7 +369,7 @@ export function topDoctorSuggestions(checks, limit = 3) {
|
|
|
368
369
|
/**
|
|
369
370
|
* @param {string} cwd
|
|
370
371
|
* @param {{ suggest?: boolean, json?: boolean }} [options]
|
|
371
|
-
* @returns {Promise<{ score: number, modes: ReturnType<typeof scoreDoctorModes>, checks: DoctorCheck[], suggestions: DoctorCheck[], executeHint: string | null, pythonMissing: boolean }>}
|
|
372
|
+
* @returns {Promise<{ score: number, modes: ReturnType<typeof scoreDoctorModes>, checks: DoctorCheck[], suggestions: DoctorCheck[], executeHint: string | null, memoryHint: string | null, pythonMissing: boolean }>}
|
|
372
373
|
*/
|
|
373
374
|
export async function doctor(cwd, options = {}) {
|
|
374
375
|
const checks = await runDoctorChecks(cwd);
|
|
@@ -377,18 +378,19 @@ export async function doctor(cwd, options = {}) {
|
|
|
377
378
|
const suggestions = topDoctorSuggestions(checks);
|
|
378
379
|
const activeFeature = await readActiveFeature(cwd);
|
|
379
380
|
const executeHint = await resolveExecuteHint(cwd, activeFeature);
|
|
381
|
+
const memoryHint = await resolveMemoryHint(cwd);
|
|
380
382
|
const pythonCheck = checks.find((check) => check.id === "python");
|
|
381
383
|
const pythonMissing = pythonCheck ? !pythonCheck.pass : false;
|
|
382
384
|
|
|
383
385
|
if (options.json) {
|
|
384
386
|
console.log(
|
|
385
387
|
JSON.stringify(
|
|
386
|
-
{ score, modes, checks, suggestions, executeHint, pythonMissing },
|
|
388
|
+
{ score, modes, checks, suggestions, executeHint, memoryHint, pythonMissing },
|
|
387
389
|
null,
|
|
388
390
|
2,
|
|
389
391
|
),
|
|
390
392
|
);
|
|
391
|
-
return { score, modes, checks, suggestions, executeHint, pythonMissing };
|
|
393
|
+
return { score, modes, checks, suggestions, executeHint, memoryHint, pythonMissing };
|
|
392
394
|
}
|
|
393
395
|
|
|
394
396
|
if (pythonMissing) {
|
|
@@ -427,5 +429,9 @@ export async function doctor(cwd, options = {}) {
|
|
|
427
429
|
console.log(`\nExecute hint:\n → ${executeHint}`);
|
|
428
430
|
}
|
|
429
431
|
|
|
430
|
-
|
|
432
|
+
if (memoryHint) {
|
|
433
|
+
console.log(`\nMemory hint:\n → ${memoryHint}`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return { score, modes, checks, suggestions, executeHint, memoryHint, pythonMissing };
|
|
431
437
|
}
|