@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 CHANGED
@@ -3,153 +3,175 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@luizsantiago/spec-guardrails.svg)](https://www.npmjs.com/package/@luizsantiago/spec-guardrails)
4
4
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
5
 
6
- **Keep AI coding agents honest — specify the work, prove each step, verify independently.**
6
+ **Governed spec-driven development for AI coding agents.**
7
7
 
8
- npm: [`@luizsantiago/spec-guardrails`](https://www.npmjs.com/package/@luizsantiago/spec-guardrails) **3.8.x**
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
- ## What is Spec Guardrails?
12
+ ---
13
13
 
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:
14
+ ## What it is
15
15
 
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”
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
- Everything important lives in **`.specs/`** in your repo so the project remembers decisions across chats and teammates.
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
- **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.
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
- > **Plain-language guide:** [Overview](docs/guide/Overview.md) · [How it works](docs/guide/How-it-works.md) · [Quick start](docs/guide/Quick-start.md)
26
+ Full narrative: [Overview](docs/guide/Overview.md) · [How it works](docs/guide/How-it-works.md)
26
27
 
27
28
  ---
28
29
 
29
- ## What it is not
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
- - 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
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 (once per project)
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
- | Requirement | Purpose |
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 **Brakes mode** (automatic gates). Without Python you still get the full workflow in **Process mode** |
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
- Check readiness: `npx @luizsantiago/spec-guardrails doctor`
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 use it day to day
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
- You talk to the agent in **chat**, not the terminal. These are **agent commands** (phrases the agent understands):
68
+ ### Core commands
60
69
 
61
- | Command | When to use |
70
+ | Command | Use when |
62
71
  | --- | --- |
63
- | `/specify` | Start a real feature — written requirements first |
64
- | `/tasks` | Shopping list of jobs after you approve the spec |
65
- | `/loop` | Implementone 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) |
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
- You do **not** need to memorize CLI commands. The agent runs gates and helpers for you when **Brakes mode** is available.
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
- **Full command reference:** [Agent commands](docs/guide/agent-commands.md)
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
- ## Two modes (same product)
88
+ Enable when the work warrants them — most teams start with the core loop only.
78
89
 
79
- | Mode | You need | What changes |
80
- | --- | --- | --- |
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 |
83
-
84
- Both are intentional. Add Python when you want automatic stop signs, not just checklists.
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
- Details: [FAQ Process vs Brakes](docs/guide/FAQ.md#process-vs-brakes)
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 repo
104
+ ## What lands in your repository
91
105
 
92
106
  | Path | Role |
93
107
  | --- | --- |
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 |
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
- ## Optional capabilities (use when you need them)
118
+ ## Documentation
103
119
 
104
- Most projects start with Specify Tasks Loop Verify only. Turn these on when the work warrants it:
120
+ Start with the guide that matches your question; each page links deeper where needed.
105
121
 
106
- | Capability | Plain purpose | Learn more |
122
+ | Topic | Start here | Go deeper |
107
123
  | --- | --- | --- |
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) |
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
- **Default:** semantic search is **off**. FTS + graph search work without any API.
131
+ Full index: [docs/guide/README.md](docs/guide/README.md)
115
132
 
116
133
  ---
117
134
 
118
- ## Documentation
135
+ ## Contributing
119
136
 
120
- | Start here | Best for |
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
- | 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 |
139
+ ### Use Spec Guardrails to build your contribution
136
140
 
137
- Doc index: [docs/guide/README.md](docs/guide/README.md)
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
- ## Upgrading
152
+ **In this source repository** — work against the branch you are developing, not the published tarball:
142
153
 
143
154
  ```bash
144
- npx @luizsantiago/spec-guardrails install
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
- See [CHANGELOG](docs/CHANGELOG.md) for release notes.
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
- ## Contributing · Credits · License
175
+ ## License
152
176
 
153
- - [CONTRIBUTING.md](CONTRIBUTING.md)
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 }[]} */
@@ -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 CURSOR_HOOK_SCRIPT = ".cursor/hooks/context-guard-edit.mjs";
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 that auto-run context-guard before write/edit tools.
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 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.
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 on write/edit tools)`);
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 };