@luizsantiago/spec-guardrails 3.5.0 → 3.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/README.md +83 -200
- package/index.js +5 -1
- package/lib/constants.js +3 -0
- package/lib/cursor-hooks.js +96 -0
- package/lib/doctor.js +10 -4
- package/lib/gates.js +42 -0
- package/lib/install.js +3 -0
- package/lib/memory-doctor.js +80 -0
- package/lib/memory-index.js +36 -0
- package/package.json +3 -3
- package/scripts/_memory_config.py +97 -0
- package/scripts/_memory_embed.py +116 -0
- package/scripts/memory_index.py +398 -12
- package/scripts/memory_retrieve.py +289 -0
- package/scripts/memory_search.py +60 -12
- package/skills/agent-architecture.md +3 -2
- package/skills/references/memory.md +32 -0
- package/templates/GETTING_STARTED.md +2 -0
- package/templates/config.yaml.example +11 -0
- package/templates/cursor/hooks/context-guard-edit.mjs +170 -0
- package/templates/cursor/hooks.json +12 -0
package/README.md
CHANGED
|
@@ -5,207 +5,136 @@
|
|
|
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.5.x**
|
|
8
|
+
npm: [`@luizsantiago/spec-guardrails`](https://www.npmjs.com/package/@luizsantiago/spec-guardrails) **3.8.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
|
-
| Before “done” | `validate-state` | Fake PASS without test evidence |
|
|
185
|
-
| After Verify FAIL | `lessons` | Ungrounded “lessons learned” |
|
|
186
|
-
| After Verify PASS | `archive-feature` | (CLI) folds feature into domain memory |
|
|
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 (3.8+) |
|
|
110
|
+
| **Execution policy** | Limit paths, retries, dangerous ops | [Overview → Safety](docs/guide/Overview.md#safety-and-limits) |
|
|
111
|
+
| **Solution exploration** | Compare two+ implementations before committing | [Overview → Exploration](docs/guide/Overview.md#optional-exploration-mode) |
|
|
112
|
+
| **Semantic retrieval** | Search by meaning (needs OpenAI or Ollama) | [Memory → Semantic](docs/guide/Memory.md#semantic-search-optional) |
|
|
113
|
+
|
|
114
|
+
**Default:** semantic search is **off**. FTS + graph search work without any API.
|
|
189
115
|
|
|
190
116
|
---
|
|
191
117
|
|
|
192
118
|
## Documentation
|
|
193
119
|
|
|
194
|
-
|
|
|
120
|
+
| Start here | Best for |
|
|
195
121
|
| --- | --- |
|
|
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 |
|
|
122
|
+
| **[Overview](docs/guide/Overview.md)** | Complete simple picture — workflow, layers, team use |
|
|
199
123
|
| [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 |
|
|
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 |
|
|
205
126
|
| [FAQ](docs/guide/FAQ.md) | Common questions |
|
|
206
|
-
| [Changelog](docs/CHANGELOG.md) | Full version history |
|
|
207
127
|
|
|
208
|
-
|
|
128
|
+
| Go deeper | Best for |
|
|
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 |
|
|
136
|
+
|
|
137
|
+
Doc index: [docs/guide/README.md](docs/guide/README.md)
|
|
209
138
|
|
|
210
139
|
---
|
|
211
140
|
|
|
@@ -215,58 +144,12 @@ Start after install: [Quick start](docs/guide/Quick-start.md) · [Agent commands
|
|
|
215
144
|
npx @luizsantiago/spec-guardrails install
|
|
216
145
|
```
|
|
217
146
|
|
|
218
|
-
|
|
219
|
-
| --- | --- |
|
|
220
|
-
| **3.5.x** | Solution exploration (`solution-explore`) — compare candidates from approved spec |
|
|
221
|
-
| **3.4.x** | Contextual guards (`context-guard`); FTS memory search (`memory-search`) |
|
|
222
|
-
| **3.3.x** | Intent/effect policy — `check-path --op read|write|delete` and `effects` config block |
|
|
223
|
-
| **3.2.x** | Single-package focus; artifact gate severity labels; git worktree isolation CLI; execution policy (budget/scope/escalation) |
|
|
224
|
-
| **3.1.x** | Copilot/Codex/AGENTS.md adapters; doctor Process + Brakes scores; `validate-traceability` / `validate-quick`; `classify-change` / `feature-status` |
|
|
225
|
-
| **3.0.x** | Final name Spec Guardrails; `.specs/guardrails/`; no dual-path ([Migration](docs/guide/Migration.md)) |
|
|
226
|
-
| **2.2.x** | Seatbelt-era paths & markers; `doctor` Execute hints; docs split from README |
|
|
227
|
-
| **2.1.x** | `loop-plan` + parallel `/loop` waves |
|
|
228
|
-
| **2.0.x** | Package rename → `@luizsantiago/spec-seatbelt` (superseded by 3.0) |
|
|
229
|
-
| **1.1.x** | `project-init` for brownfield repos |
|
|
230
|
-
| **0.9.x** | `archive-feature` + domain memory merge |
|
|
231
|
-
|
|
232
|
-
Full history: [CHANGELOG](docs/CHANGELOG.md) · [Releases](https://github.com/luizssantiago92/spec-guardrails/releases) · [Stability policy](docs/guide/Stability-policy.md)
|
|
233
|
-
|
|
234
|
-
Lineage: `agentic-harness` → `spec-seatbelt` → **`spec-guardrails` (final)**. Run `install` once after switching. See [Migration](docs/guide/Migration.md).
|
|
147
|
+
See [CHANGELOG](docs/CHANGELOG.md) for release notes.
|
|
235
148
|
|
|
236
149
|
---
|
|
237
150
|
|
|
238
|
-
## Contributing
|
|
239
|
-
|
|
240
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) — tests, gate freeze policy, local `npm run guardrails -- install`.
|
|
241
|
-
|
|
242
|
-
---
|
|
243
|
-
|
|
244
|
-
## Credits
|
|
245
|
-
|
|
246
|
-
Spec Guardrails adapts open ideas; we did not invent spec-driven phases, loop design, or task-graph rules.
|
|
247
|
-
|
|
248
|
-
### Core lineage
|
|
249
|
-
|
|
250
|
-
| Source | License | How we use it |
|
|
251
|
-
| --- | --- | --- |
|
|
252
|
-
| [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 |
|
|
253
|
-
| [addyosmani/agent-skills](https://github.com/addyosmani/agent-skills) | MIT | Discuss patterns, definition-of-done |
|
|
254
|
-
| [graph-engineering](https://github.com/codejunkie99/graph-engineering) | MIT | Task-graph topology, stop rules, parallel merge |
|
|
255
|
-
|
|
256
|
-
### Loop & ecosystem
|
|
257
|
-
|
|
258
|
-
| Source | License | How we use it |
|
|
259
|
-
| --- | --- | --- |
|
|
260
|
-
| [loop-engineering](https://github.com/cobusgreyling/loop-engineering) | MIT | Operational loop patterns; `doctor` score metaphor |
|
|
261
|
-
| [Addy Osmani — Loop engineering](https://addyosmani.com/blog/loop-engineering/) | — | Essay lineage |
|
|
262
|
-
| [awesome-harness-engineering](https://github.com/ai-boost/awesome-harness-engineering) | CC0 | Ecosystem taxonomy |
|
|
263
|
-
|
|
264
|
-
### Adjacent (not vendored)
|
|
265
|
-
|
|
266
|
-
[DeepCode](https://github.com/HKUDS/DeepCode) · [RepoGraph](https://github.com/ozyyshr/RepoGraph)
|
|
267
|
-
|
|
268
|
-
Extended attribution: [docs/guide/credits.md](docs/guide/credits.md)
|
|
269
|
-
|
|
270
|
-
## License
|
|
151
|
+
## Contributing · Credits · License
|
|
271
152
|
|
|
272
|
-
|
|
153
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
154
|
+
- [Credits](docs/guide/credits.md)
|
|
155
|
+
- MIT — see [LICENSE](LICENSE)
|
package/index.js
CHANGED
|
@@ -103,12 +103,16 @@ Commands:
|
|
|
103
103
|
execution-policy record-retry <task> Increment retry counter for a task id (blocks at limit)
|
|
104
104
|
execution-policy record-run Increment agent-run counter (blocks at budget)
|
|
105
105
|
memory-index rebuild Rebuild SQLite memory index from .specs/ artifacts
|
|
106
|
+
memory-index embed [--force] Optional semantic embeddings (requires config + provider)
|
|
106
107
|
memory-query --from <id> Bounded context package from the knowledge graph
|
|
107
108
|
[--depth N] Traversal depth (default 2)
|
|
108
109
|
[--json] Machine-readable output
|
|
109
|
-
memory-search <query> Full-text search over
|
|
110
|
+
memory-search <query> Full-text search over indexed artifact chunks (FTS5)
|
|
110
111
|
[--limit N] Max results (default 10)
|
|
111
112
|
[--json] Machine-readable output
|
|
113
|
+
memory-retrieve "<query>" Hybrid retrieval (FTS + graph + optional semantic)
|
|
114
|
+
[--mode fts|hybrid|semantic] Strategy (default: hybrid)
|
|
115
|
+
[--json] Machine-readable output
|
|
112
116
|
context-guard status Execute readiness from STATE + tasks.md
|
|
113
117
|
[--json] Machine-readable output
|
|
114
118
|
context-guard check-edit <path> Contextual guard before editing a file
|
package/lib/constants.js
CHANGED
|
@@ -112,6 +112,9 @@ export const SCRIPT_ASSETS = [
|
|
|
112
112
|
{ file: "memory_index.py", remotePath: "scripts/memory_index.py" },
|
|
113
113
|
{ file: "memory_query.py", remotePath: "scripts/memory_query.py" },
|
|
114
114
|
{ file: "memory_search.py", remotePath: "scripts/memory_search.py" },
|
|
115
|
+
{ file: "memory_retrieve.py", remotePath: "scripts/memory_retrieve.py" },
|
|
116
|
+
{ file: "_memory_config.py", remotePath: "scripts/_memory_config.py" },
|
|
117
|
+
{ file: "_memory_embed.py", remotePath: "scripts/_memory_embed.py" },
|
|
115
118
|
];
|
|
116
119
|
|
|
117
120
|
/** @type {{ file: string, remotePath: string }[]} */
|
|
@@ -0,0 +1,96 @@
|
|
|
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_SCRIPT = ".cursor/hooks/context-guard-edit.mjs";
|
|
8
|
+
export const CURSOR_HOOKS_JSON = ".cursor/hooks.json";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Merge shipped hook entries without removing user hooks.
|
|
12
|
+
*
|
|
13
|
+
* @param {Record<string, unknown> | null | undefined} existing
|
|
14
|
+
* @param {Record<string, unknown>} template
|
|
15
|
+
* @returns {Record<string, unknown>}
|
|
16
|
+
*/
|
|
17
|
+
export function mergeCursorHooksConfig(existing, template) {
|
|
18
|
+
/** @type {Record<string, unknown>} */
|
|
19
|
+
const result =
|
|
20
|
+
existing && typeof existing === "object"
|
|
21
|
+
? structuredClone(existing)
|
|
22
|
+
: { version: 1, hooks: {} };
|
|
23
|
+
|
|
24
|
+
result.version = result.version ?? template.version ?? 1;
|
|
25
|
+
/** @type {Record<string, unknown[]>} */
|
|
26
|
+
const hooks =
|
|
27
|
+
result.hooks && typeof result.hooks === "object"
|
|
28
|
+
? /** @type {Record<string, unknown[]>} */ (result.hooks)
|
|
29
|
+
: {};
|
|
30
|
+
result.hooks = hooks;
|
|
31
|
+
|
|
32
|
+
/** @type {Record<string, unknown[]>} */
|
|
33
|
+
const templateHooks =
|
|
34
|
+
template.hooks && typeof template.hooks === "object"
|
|
35
|
+
? /** @type {Record<string, unknown[]>} */ (template.hooks)
|
|
36
|
+
: {};
|
|
37
|
+
|
|
38
|
+
for (const [event, entries] of Object.entries(templateHooks)) {
|
|
39
|
+
if (!Array.isArray(entries)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const current = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
44
|
+
for (const entry of entries) {
|
|
45
|
+
if (!entry || typeof entry !== "object") {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const command = /** @type {{ command?: string }} */ (entry).command;
|
|
49
|
+
if (!command || current.some((item) => item?.command === command)) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
current.push(entry);
|
|
53
|
+
}
|
|
54
|
+
hooks[event] = current;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Install Cursor hooks that auto-run context-guard before write/edit tools.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} cwd
|
|
64
|
+
* @param {{ log?: (message: string) => void }} [options]
|
|
65
|
+
*/
|
|
66
|
+
export async function installCursorHooks(cwd, options = {}) {
|
|
67
|
+
const log = options.log ?? (() => {});
|
|
68
|
+
const hooksDir = path.join(cwd, ".cursor/hooks");
|
|
69
|
+
await ensureDir(hooksDir);
|
|
70
|
+
|
|
71
|
+
const scriptSource = packagedAssetPath("templates/cursor/hooks/context-guard-edit.mjs");
|
|
72
|
+
const scriptDest = path.join(cwd, CURSOR_HOOK_SCRIPT);
|
|
73
|
+
await fs.copyFile(scriptSource, scriptDest);
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
await fs.chmod(scriptDest, 0o755);
|
|
77
|
+
} catch {
|
|
78
|
+
// Windows may ignore chmod; node can still execute the script.
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const template = JSON.parse(
|
|
82
|
+
await readFileSafe(packagedAssetPath("templates/cursor/hooks.json")),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const hooksJsonPath = path.join(cwd, CURSOR_HOOKS_JSON);
|
|
86
|
+
let existing = null;
|
|
87
|
+
try {
|
|
88
|
+
existing = JSON.parse(await readFileSafe(hooksJsonPath));
|
|
89
|
+
} catch {
|
|
90
|
+
existing = null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const merged = mergeCursorHooksConfig(existing, template);
|
|
94
|
+
await fs.writeFile(hooksJsonPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
|
|
95
|
+
log(`✅ Cursor hooks → ${CURSOR_HOOKS_JSON} (context-guard on write/edit tools)`);
|
|
96
|
+
}
|
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
|
}
|
package/lib/gates.js
CHANGED
|
@@ -44,6 +44,7 @@ const AUX_SCRIPTS = {
|
|
|
44
44
|
"memory-index": "memory_index.py",
|
|
45
45
|
"memory-query": "memory_query.py",
|
|
46
46
|
"memory-search": "memory_search.py",
|
|
47
|
+
"memory-retrieve": "memory_retrieve.py",
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
const GUARDRAILS_SCRIPTS = { ...GATE_SCRIPTS, ...AUX_SCRIPTS };
|
|
@@ -204,6 +205,47 @@ export async function runGuardrailsScript(command, args, options = {}) {
|
|
|
204
205
|
});
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Run a guardrails Python script and capture stdout/stderr.
|
|
210
|
+
*
|
|
211
|
+
* @param {string} command gate or aux command name
|
|
212
|
+
* @param {string[]} args forwarded to the Python script
|
|
213
|
+
* @param {{ cwd?: string }} [options]
|
|
214
|
+
* @returns {Promise<{ code: number, stdout: string, stderr: string }>}
|
|
215
|
+
*/
|
|
216
|
+
export async function runGuardrailsScriptCapture(command, args, options = {}) {
|
|
217
|
+
const scriptName = GUARDRAILS_SCRIPTS[command];
|
|
218
|
+
|
|
219
|
+
if (!scriptName) {
|
|
220
|
+
throw new Error(`Unknown guardrails command: ${command}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const cwd = options.cwd ?? process.cwd();
|
|
224
|
+
const scriptsDir = await resolveScriptsDir(cwd);
|
|
225
|
+
const scriptPath = path.join(cwd, scriptsDir, scriptName);
|
|
226
|
+
const python = await resolvePython();
|
|
227
|
+
|
|
228
|
+
if (!python) {
|
|
229
|
+
throw new Error("Python 3.10+ not found.");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return new Promise((resolve, reject) => {
|
|
233
|
+
let stdout = "";
|
|
234
|
+
let stderr = "";
|
|
235
|
+
const child = spawn(python.command, [...python.args, scriptPath, ...args], { cwd });
|
|
236
|
+
child.stdout.on("data", (chunk) => {
|
|
237
|
+
stdout += chunk;
|
|
238
|
+
});
|
|
239
|
+
child.stderr.on("data", (chunk) => {
|
|
240
|
+
stderr += chunk;
|
|
241
|
+
});
|
|
242
|
+
child.on("error", reject);
|
|
243
|
+
child.on("close", (code) => {
|
|
244
|
+
resolve({ code: code ?? 1, stdout, stderr });
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
207
249
|
/**
|
|
208
250
|
* Run a structural gate script installed under `.specs/guardrails/scripts/`.
|
|
209
251
|
*
|
package/lib/install.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
resolveAssetOverride,
|
|
14
14
|
} from "./constants.js";
|
|
15
15
|
import { installPlatformAdapters } from "./adapters.js";
|
|
16
|
+
import { installCursorHooks } from "./cursor-hooks.js";
|
|
16
17
|
import { ensureDir, readFileSafe, writeFileIfMissing } from "./fs-utils.js";
|
|
17
18
|
import { hasPython } from "./gates.js";
|
|
18
19
|
import { initGuardrailsMemory } from "./memory.js";
|
|
@@ -118,6 +119,8 @@ export async function install(options = {}) {
|
|
|
118
119
|
await installPlatformAdapters(cwd);
|
|
119
120
|
log("✅ Adapters → .cursorrules, CLAUDE.md, copilot-instructions.md, AGENTS.md, .codex/AGENTS.md");
|
|
120
121
|
|
|
122
|
+
await installCursorHooks(cwd, { log });
|
|
123
|
+
|
|
121
124
|
const gettingStartedCreated = await writeFileIfMissing(
|
|
122
125
|
path.join(cwd, ".specs/GETTING_STARTED.md"),
|
|
123
126
|
await readFileSafe(packagedAssetPath("templates/GETTING_STARTED.md")),
|