arey-pi 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -119,7 +119,7 @@ Or load the readiness skill directly:
119
119
  /skill:project-readiness
120
120
  ```
121
121
 
122
- Arey Pi also ships an extension with native workflow commands.
122
+ Arey Pi also ships an extension with native setup commands and automatic natural-language harness activation.
123
123
 
124
124
  When the Arey Pi agents are available to `pi-subagents`, the project evaluator runtime name is:
125
125
 
@@ -129,7 +129,7 @@ arey-pi.project-evaluator
129
129
 
130
130
  ## Extension-backed workflow
131
131
 
132
- Arey Pi includes a polished extension-backed workflow:
132
+ Arey Pi includes a polished extension-backed setup and natural-language workflow harness:
133
133
 
134
134
  ```txt
135
135
  /arey-doctor # check package, subagent, prompt, skill, and project readiness setup
@@ -139,19 +139,23 @@ Arey Pi includes a polished extension-backed workflow:
139
139
  /arey-bootstrap --docs # scaffold starter docs directory
140
140
  /arey-bootstrap --full # explicit alias for the default full bootstrap
141
141
  /arey-bootstrap --force # full bootstrap and overwrite selected project-local files
142
- /arey-feature # run spec TDD → sync → review for a feature
143
- /arey-bugfix # run regression-test-first bug fixing
144
- /arey-sync # verify specs, docs, tests, code, DBML, ADRs, and glossary alignment
145
- /arey-review # run adversarial engineering review
146
- /arey-assess # assess project readiness against Arey Pi rules
142
+ # Development workflows are natural-language first:
143
+ # "Implementa password reset siguiendo Arey Pi"
144
+ # "Corrige este bug con Arey Pi"
145
+ # "Revisa el current diff contra Arey Pi"
147
146
  ```
148
147
 
149
- The goal is that users can either speak naturally or use explicit commands,
150
- while Arey Pi handles the disciplined workflow behind the scenes.
148
+ The goal is that users work naturally without development commands.
149
+ Requests such as `Implementa password reset siguiendo Arey Pi` automatically activate quiet harness context behind the scenes.
150
+ Arey Pi lets the parent agent infer the work mode,
151
+ act as orchestrator,
152
+ use specialist subagents when available,
153
+ inject the relevant delivery guidance,
154
+ and apply simple event-based guardrails for protected paths.
151
155
 
152
156
  See:
153
157
 
154
- - `docs/commands.md` for detailed command behaviour, options, and examples;
158
+ - `docs/commands.md` for setup commands and natural-language workflow behaviour;
155
159
  - `docs/adoption.md` for adopting Arey Pi in an existing repository;
156
160
  - `docs/workflows.md` for workflow expectations;
157
161
  - `docs/workflow-diagram.md` for the visual framework workflow;
@@ -195,14 +199,13 @@ The policy layer,
195
199
  readiness workflow,
196
200
  documentation sync rule,
197
201
  core subagent role definitions,
198
- and professional extension commands exist.
202
+ and professional setup extension commands exist.
199
203
 
200
- Arey Pi now includes stronger workflow command contracts,
204
+ Arey Pi now includes natural-language harness activation,
201
205
  focused prompts,
202
206
  TDD/spec-sync/review skills,
203
207
  and extension-core tests.
204
208
 
205
- Next improvements include guided interactive workflows,
206
- stronger bootstrap scaffolding,
209
+ Next improvements include stronger bootstrap scaffolding,
207
210
  custom Arey Pi tools,
208
211
  and deeper enforcement through Pi extension events.
package/docs/adoption.md CHANGED
@@ -85,13 +85,13 @@ agents should be able to discover:
85
85
 
86
86
  ### 5. Assess readiness
87
87
 
88
- Run:
88
+ Ask naturally:
89
89
 
90
90
  ```txt
91
- /arey-assess
91
+ Evalúa este repo contra Arey Pi
92
92
  ```
93
93
 
94
- or:
94
+ You can also use the focused prompt template if desired:
95
95
 
96
96
  ```txt
97
97
  /assess-project
@@ -155,7 +155,7 @@ Use this for active product repositories.
155
155
  - Add Gherkin specs for core behaviours.
156
156
  - Add DBML if persistence exists.
157
157
  - Add ADRs for significant decisions.
158
- - Require `/arey-sync` before completing non-trivial work.
158
+ - Require natural Arey Pi sync review before completing non-trivial work.
159
159
 
160
160
  ### Strict adoption
161
161
 
@@ -202,11 +202,11 @@ Do not:
202
202
  ## Completion Standard
203
203
 
204
204
  After adoption work,
205
- run:
205
+ run setup diagnostics and ask for a natural readiness assessment:
206
206
 
207
207
  ```txt
208
208
  /arey-doctor
209
- /arey-assess
209
+ Evalúa este repo contra Arey Pi
210
210
  ```
211
211
 
212
212
  A good first adoption result is not perfection.
package/docs/commands.md CHANGED
@@ -1,23 +1,14 @@
1
1
  # Arey Pi Commands
2
2
 
3
- Arey Pi ships a Pi extension that registers native slash commands.
4
- The extension uses Pi's documented directory style at `extensions/arey-pi/index.ts`.
5
-
6
- The commands are designed for two modes of use:
7
-
8
- - quick explicit workflows such as `/arey-feature` or `/arey-sync`;
9
- - natural-language work where the parent agent acts as the Arey Pi tech lead and uses the same workflow expectations.
3
+ Arey Pi ships a Pi extension that registers native setup slash commands.
4
+ Development workflows are intentionally natural-language first:
5
+ the extension recognises explicit Arey Pi opt-in and injects quiet harness guidance automatically.
10
6
 
11
7
  ## Command overview
12
8
 
13
9
  ```txt
14
10
  /arey-doctor
15
11
  /arey-bootstrap [--agentsmd] [--specs] [--docs] [--full] [--force]
16
- /arey-feature <feature request>
17
- /arey-bugfix <bug description>
18
- /arey-sync [scope]
19
- /arey-review [scope]
20
- /arey-assess [scope]
21
12
  ```
22
13
 
23
14
  ## `/arey-doctor`
@@ -158,235 +149,49 @@ Examples:
158
149
 
159
150
  Use this command after installing Arey Pi and `pi-subagents` in a repository where you want the Arey Pi agents to be discoverable by `pi-subagents`.
160
151
 
161
- ## `/arey-feature`
152
+ ## Natural-language development workflows
162
153
 
163
- Starts the Arey Pi feature delivery workflow.
164
-
165
- Usage:
154
+ Arey Pi is designed to work without development slash commands.
155
+ When the user explicitly opts into Arey Pi in normal language,
156
+ for example:
166
157
 
167
158
  ```txt
168
- /arey-feature <feature request>
159
+ Implementa password reset siguiendo Arey Pi
160
+ Corrige este bug con Arey Pi
161
+ Revisa el current diff contra Arey Pi
162
+ Evalúa este repo contra Arey Pi
169
163
  ```
170
164
 
171
- Example:
165
+ The extension injects quiet harness context automatically before the agent turn.
166
+ The harness is not meant to add ceremony for the user.
167
+ It asks the agent to infer whether the request is a feature,
168
+ bugfix,
169
+ sync,
170
+ review,
171
+ assessment,
172
+ or mixed task,
173
+ then apply the corresponding Arey Pi posture.
174
+ The parent agent should act as orchestrator,
175
+ use specialist Arey Pi subagents when available,
176
+ and continue conversationally while reporting evidence naturally.
172
177
 
173
- ```txt
174
- /arey-feature Add password reset with expiring email links
175
- ```
178
+ The injected harness guidance emphasises:
176
179
 
177
- The command sends a structured request to the parent agent to act as the Arey Pi tech lead.
178
- The expected workflow is:
180
+ - `arey-pi.spec-author` for canonical specs;
181
+ - `arey-pi.tdd-implementer` for Red → Green → Refactor;
182
+ - `arey-pi.spec-syncer` for alignment;
183
+ - `arey-pi.engineering-reviewer` for fresh review;
184
+ - `arey-pi.project-evaluator` for readiness assessment;
185
+ - builtin scout/planner/reviewer/oracle-style agents for discovery, planning, and second opinions.
179
186
 
180
- ```txt
181
- spec-author tdd-implementer spec-syncer → engineering-reviewer
182
- ```
183
-
184
- The command now sends a stronger execution contract.
185
- The workflow should:
186
-
187
- - identify scope, non-goals, risk, and unknowns;
188
- - confirm or update canonical specs before production behaviour changes;
189
- - preserve TDD through Red → Green → Refactor;
190
- - keep tests outside production source directories by default;
191
- - synchronise specs, docs, tests, code, DBML, ADRs, glossary, README files, AGENTS.md, skills, prompts, rules, agents, commands, templates, and tooling instructions when affected;
192
- - run fresh-context engineering review when risk warrants it;
193
- - report validation evidence and residual risks using the Arey Pi final evidence format.
187
+ While Arey Pi is active,
188
+ Arey Pi applies simple tool-call guardrails:
194
189
 
195
- ## `/arey-bugfix`
196
-
197
- Starts the Arey Pi regression-test-first bugfix workflow.
198
-
199
- Usage:
190
+ - writes or edits to protected paths such as `.env`, `.git/`, and `node_modules/` are blocked.
200
191
 
201
- ```txt
202
- /arey-bugfix <bug description>
203
- ```
204
-
205
- Example:
206
-
207
- ```txt
208
- /arey-bugfix Users can bypass email verification by refreshing the session
209
- ```
210
-
211
- The command now sends a regression-test-first execution contract.
212
- The workflow should:
213
-
214
- - identify expected versus actual behaviour and affected scope;
215
- - reproduce the bug with a meaningful failing regression test before production changes;
216
- - implement the smallest high-quality fix;
217
- - keep Red → Green → Refactor evidence visible;
218
- - update Gherkin, docs, DBML, ADRs, glossary, or architecture docs when affected;
219
- - request fresh engineering review for security, data-loss, concurrency, auth, payment, migration, or public API bugs;
220
- - run validation and report residual risks.
221
-
222
- ## `/arey-sync`
223
-
224
- Runs Arey Pi sync review for the current repository or a specific scope.
225
-
226
- Usage:
227
-
228
- ```txt
229
- /arey-sync
230
- /arey-sync <scope>
231
- ```
232
-
233
- Examples:
234
-
235
- ```txt
236
- /arey-sync
237
- /arey-sync authentication flow
238
- /arey-sync current diff
239
- ```
240
-
241
- The command asks the parent agent to verify alignment across:
242
-
243
- - Gherkin specs;
244
- - tests;
245
- - code;
246
- - DBML;
247
- - ADRs;
248
- - glossary;
249
- - architecture docs;
250
- - README files;
251
- - `docs/`;
252
- - `AGENTS.md`;
253
- - skills, prompts, rules, agents, examples, templates;
254
- - command and tooling instructions.
255
-
256
- The sync contract asks the agent to classify drift as blocking,
257
- recommended,
258
- or unaffected.
259
- It may fix safe drift directly when canonical intent is clear,
260
- but it must ask before changing intent.
261
-
262
- The final report should include both:
263
-
264
- ```txt
265
- Specs updated
266
- ```
267
-
268
- or:
269
-
270
- ```txt
271
- Specs unaffected: <reason>
272
- ```
273
-
274
- and:
275
-
276
- ```txt
277
- Docs updated
278
- ```
279
-
280
- or:
281
-
282
- ```txt
283
- Docs unaffected: <reason>
284
- ```
285
-
286
- ## `/arey-review`
287
-
288
- Runs an adversarial Arey Pi engineering review.
289
-
290
- Usage:
291
-
292
- ```txt
293
- /arey-review
294
- /arey-review <scope>
295
- ```
296
-
297
- Examples:
298
-
299
- ```txt
300
- /arey-review
301
- /arey-review current diff
302
- /arey-review persistence layer
303
- ```
304
-
305
- The review should examine:
306
-
307
- - architecture and code quality;
308
- - test quality;
309
- - quality tooling and validation evidence;
310
- - security and privacy;
311
- - reliability and operability;
312
- - maintainability;
313
- - spec, ADR, DBML, glossary, and documentation sync concerns;
314
- - generated-code or agent-authored-code slop.
315
-
316
- Findings should be classified by severity.
317
-
318
- ## `/arey-assess`
319
-
320
- Runs Arey Pi project readiness assessment.
321
-
322
- Usage:
323
-
324
- ```txt
325
- /arey-assess
326
- /arey-assess <scope>
327
- ```
328
-
329
- Examples:
330
-
331
- ```txt
332
- /arey-assess
333
- /arey-assess backend package only
334
- ```
335
-
336
- The assessment is read-only by default.
337
- It should score the repository against Arey Pi rules,
338
- provide evidence with file paths,
339
- identify blockers and quick wins,
340
- and propose a prioritised improvement plan.
341
-
342
- Use this when adopting Arey Pi in an existing repository or checking whether a project remains aligned.
343
192
 
344
193
  ## Prompt templates and skills
345
194
 
346
- Arey Pi also ships focused prompt templates:
347
-
348
- ```txt
349
- /feature-spec
350
- /red-green-refactor
351
- /sync-drift
352
- /engineering-review
353
- /adr-review
354
- /assess-project
355
- ```
356
-
357
- And focused skills:
358
-
359
- ```txt
360
- /skill:tdd-red-green-refactor
361
- /skill:spec-sync
362
- /skill:engineering-review
363
- /skill:project-readiness
364
- ```
365
-
366
- Use slash commands for full workflow orchestration,
367
- prompts for targeted one-off work,
368
- and skills when you want the model to load specialised Arey Pi instructions on demand.
369
-
370
- ## Busy agent behaviour
371
-
372
- Workflow commands send a user message to the current Pi session.
373
-
374
- If the agent is idle,
375
- the workflow starts immediately.
376
-
377
- If the agent is already working,
378
- the workflow is queued as a follow-up message.
379
-
380
- ## Relationship to natural language
381
-
382
- Commands are optional.
383
-
384
- Users can also work naturally:
385
-
386
- ```txt
387
- Implement magic links following Arey Pi.
388
- ```
389
-
390
- The commands exist to make common workflows explicit,
391
- repeatable,
392
- and easier to discover.
195
+ Arey Pi still ships focused prompt templates and skills for targeted use,
196
+ but they are optional.
197
+ The intended default is natural language plus automatic harness injection.
package/docs/templates.md CHANGED
@@ -133,7 +133,7 @@ The readiness report template captures Arey Pi assessment output.
133
133
 
134
134
  Use it when you want a persistent audit snapshot under `docs/`.
135
135
  For routine checks,
136
- `/arey-assess` can report directly in the session.
136
+ ask naturally: `Evalúa este repo contra Arey Pi`.
137
137
 
138
138
  ## Template Maintenance
139
139
 
package/docs/workflows.md CHANGED
@@ -2,17 +2,16 @@
2
2
 
3
3
  See `docs/workflow-diagram.md` for a visual overview of the framework workflow.
4
4
 
5
- Arey Pi workflows can be started with slash commands or natural language.
6
-
7
- The slash commands make the intended process explicit.
8
- Natural language should still follow the same rules when the user asks to work following Arey Pi.
5
+ Arey Pi development workflows are natural-language first.
6
+ When the user asks to work following Arey Pi,
7
+ the extension injects quiet harness guidance automatically before the agent turn.
9
8
 
10
9
  ## Feature Workflow
11
10
 
12
- Command:
11
+ Example request:
13
12
 
14
13
  ```txt
15
- /arey-feature <feature request>
14
+ Implementa password reset siguiendo Arey Pi
16
15
  ```
17
16
 
18
17
  Expected flow:
@@ -41,10 +40,10 @@ The workflow should:
41
40
 
42
41
  ## Bugfix Workflow
43
42
 
44
- Command:
43
+ Example request:
45
44
 
46
45
  ```txt
47
- /arey-bugfix <bug description>
46
+ Corrige el bug de verificación de email con Arey Pi
48
47
  ```
49
48
 
50
49
  Use this when behaviour is wrong.
@@ -61,10 +60,10 @@ and it should live outside production source directories by default.
61
60
 
62
61
  ## Sync Workflow
63
62
 
64
- Command:
63
+ Example request:
65
64
 
66
65
  ```txt
67
- /arey-sync [scope]
66
+ Sincroniza specs y docs con Arey Pi para el current diff
68
67
  ```
69
68
 
70
69
  Use this before completing non-trivial work or when drift is suspected.
@@ -101,10 +100,10 @@ or justified unaffected statuses.
101
100
 
102
101
  ## Review Workflow
103
102
 
104
- Command:
103
+ Example request:
105
104
 
106
105
  ```txt
107
- /arey-review [scope]
106
+ Revisa el current diff contra Arey Pi
108
107
  ```
109
108
 
110
109
  Use this for adversarial engineering review.
@@ -128,10 +127,10 @@ Findings should be classified by severity.
128
127
 
129
128
  ## Assessment Workflow
130
129
 
131
- Command:
130
+ Example request:
132
131
 
133
132
  ```txt
134
- /arey-assess [scope]
133
+ Evalúa este repo contra Arey Pi
135
134
  ```
136
135
 
137
136
  Use this to assess project readiness.
@@ -0,0 +1,198 @@
1
+ import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, relative } from "node:path";
3
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
4
+ import {
5
+ buildDoctorReport,
6
+ docsScaffoldFiles,
7
+ parseBootstrapFlags,
8
+ requiredAgents,
9
+ specScaffoldFiles,
10
+ type ScaffoldFile,
11
+ } from "./core.ts";
12
+ import { agentSourceDir, cwdFrom, dirExists, fileExists, packageRoot, rulesDir, templatesDir } from "./paths.ts";
13
+
14
+ type AgentCopyResult = { copied: string[]; skipped: string[]; missing: string[] };
15
+ type ScaffoldResult = { created: string[]; skipped: string[] };
16
+
17
+ function copyAgents(targetDir: string, force: boolean): AgentCopyResult {
18
+ mkdirSync(targetDir, { recursive: true });
19
+
20
+ const copied: string[] = [];
21
+ const skipped: string[] = [];
22
+ const missing: string[] = [];
23
+
24
+ for (const agent of requiredAgents) {
25
+ const source = join(agentSourceDir, agent);
26
+ const target = join(targetDir, agent);
27
+
28
+ if (!fileExists(source)) {
29
+ missing.push(agent);
30
+ continue;
31
+ }
32
+
33
+ if (fileExists(target) && !force) {
34
+ skipped.push(agent);
35
+ continue;
36
+ }
37
+
38
+ copyFileSync(source, target);
39
+ copied.push(agent);
40
+ }
41
+
42
+ return { copied, skipped, missing };
43
+ }
44
+
45
+ function templateContent(name: string): string {
46
+ return readFileSync(join(templatesDir, name), "utf8");
47
+ }
48
+
49
+ function writeTemplateIfMissing(file: ScaffoldFile, force: boolean, cwd: string, result: ScaffoldResult): void {
50
+ const target = join(cwd, file.target);
51
+ mkdirSync(dirname(target), { recursive: true });
52
+
53
+ if (fileExists(target) && !force) {
54
+ result.skipped.push(file.target);
55
+ return;
56
+ }
57
+
58
+ writeFileSync(target, templateContent(file.template));
59
+ result.created.push(file.target);
60
+ }
61
+
62
+ function scaffoldFiles(cwd: string, force: boolean, files: ScaffoldFile[]): ScaffoldResult {
63
+ const result: ScaffoldResult = { created: [], skipped: [] };
64
+
65
+ for (const file of files) {
66
+ writeTemplateIfMissing(file, force, cwd, result);
67
+ }
68
+
69
+ return result;
70
+ }
71
+
72
+ function packageVersion(): string {
73
+ try {
74
+ const pkg = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")) as { version?: string };
75
+ return pkg.version ?? "unknown";
76
+ } catch {
77
+ return "unknown";
78
+ }
79
+ }
80
+
81
+ function buildBootstrapReport(input: {
82
+ cwd: string;
83
+ targetDir: string;
84
+ agents: AgentCopyResult;
85
+ specs: ScaffoldResult;
86
+ docs: ScaffoldResult;
87
+ agentsMd: string;
88
+ }): string {
89
+ const createdScaffold = [...input.specs.created, ...input.docs.created];
90
+ const skippedScaffold = [...input.specs.skipped, ...input.docs.skipped];
91
+
92
+ return [
93
+ "# Arey Pi Bootstrap",
94
+ "",
95
+ `- Target: ${relative(input.cwd, input.targetDir)}`,
96
+ `- Copied agents: ${input.agents.copied.length}`,
97
+ `- Skipped existing agents: ${input.agents.skipped.length}`,
98
+ `- Missing package agents: ${input.agents.missing.length}`,
99
+ `- AGENTS.md: ${input.agentsMd}`,
100
+ `- Spec scaffold created: ${input.specs.created.length}`,
101
+ `- Spec scaffold skipped: ${input.specs.skipped.length}`,
102
+ `- Docs scaffold created: ${input.docs.created.length}`,
103
+ `- Docs scaffold skipped: ${input.docs.skipped.length}`,
104
+ "",
105
+ "## Copied agents",
106
+ input.agents.copied.length ? input.agents.copied.map((agent) => `- ${agent}`).join("\n") : "- none",
107
+ "",
108
+ "## Skipped agents",
109
+ input.agents.skipped.length ? input.agents.skipped.map((agent) => `- ${agent}`).join("\n") : "- none",
110
+ "",
111
+ "## Created scaffold files",
112
+ createdScaffold.length ? createdScaffold.map((path) => `- ${path}`).join("\n") : "- none",
113
+ "",
114
+ "## Skipped scaffold files",
115
+ skippedScaffold.length ? skippedScaffold.map((path) => `- ${path}`).join("\n") : "- none",
116
+ "",
117
+ "Run `/arey-doctor` to verify setup.",
118
+ ].join("\n");
119
+ }
120
+
121
+ function handleDoctor(pi: ExtensionAPI, ctx: ExtensionCommandContext): void {
122
+ const cwd = cwdFrom(ctx);
123
+ const projectAgentDir = join(cwd, ".pi", "agents", "arey-pi");
124
+ const commands = pi.getCommands();
125
+ const installedAgents = requiredAgents.filter((agent) => fileExists(join(projectAgentDir, agent)));
126
+ const missingAgents = requiredAgents.filter((agent) => !fileExists(join(projectAgentDir, agent)));
127
+ const packageAgents = requiredAgents.filter((agent) => fileExists(join(agentSourceDir, agent)));
128
+ const prompts = commands.filter(
129
+ (command) => command.source === "prompt" && command.sourceInfo?.source?.includes("arey-pi"),
130
+ );
131
+ const skills = commands.filter(
132
+ (command) => command.source === "skill" && command.sourceInfo?.source?.includes("arey-pi"),
133
+ );
134
+
135
+ pi.sendMessage({
136
+ customType: "arey-pi-doctor",
137
+ content: buildDoctorReport({
138
+ packageVersion: packageVersion(),
139
+ cwd,
140
+ packageRulesPresent: dirExists(rulesDir),
141
+ packageTemplatesPresent: dirExists(templatesDir),
142
+ packageAgentsCount: packageAgents.length,
143
+ requiredAgentsCount: requiredAgents.length,
144
+ hasSubagentsCommand: commands.some((command) => command.name.startsWith("subagents-doctor")),
145
+ installedAgentsCount: installedAgents.length,
146
+ hasRootAgentsMd: fileExists(join(cwd, "AGENTS.md")),
147
+ hasPiSettings: fileExists(join(cwd, ".pi", "settings.json")),
148
+ promptsCount: prompts.length,
149
+ skillsCount: skills.length,
150
+ missingAgents,
151
+ }),
152
+ display: true,
153
+ details: {},
154
+ });
155
+ }
156
+
157
+ function handleBootstrap(pi: ExtensionAPI, args: string, ctx: ExtensionCommandContext): void {
158
+ const cwd = cwdFrom(ctx);
159
+ const { force, createAgentsMd, createSpecs, createDocs } = parseBootstrapFlags(args);
160
+ const targetDir = join(cwd, ".pi", "agents", "arey-pi");
161
+ const agents = copyAgents(targetDir, force);
162
+ const specs = createSpecs ? scaffoldFiles(cwd, force, specScaffoldFiles) : { created: [], skipped: [] };
163
+ const docs = createDocs ? scaffoldFiles(cwd, force, docsScaffoldFiles) : { created: [], skipped: [] };
164
+ const agentsMdPath = join(cwd, "AGENTS.md");
165
+ let agentsMd = "unchanged";
166
+
167
+ if (!fileExists(agentsMdPath) && (createAgentsMd || force)) {
168
+ writeFileSync(agentsMdPath, templateContent("AGENTS.md"));
169
+ agentsMd = "created";
170
+ } else if (fileExists(agentsMdPath) && createAgentsMd && !force) {
171
+ agentsMd = "skipped existing";
172
+ }
173
+
174
+ pi.sendMessage({
175
+ customType: "arey-pi-bootstrap",
176
+ content: buildBootstrapReport({ cwd, targetDir, agents, specs, docs, agentsMd }),
177
+ display: true,
178
+ details: { agents, specs, docs, agentsMd },
179
+ });
180
+ }
181
+
182
+ export function registerBootstrapCommands(pi: ExtensionAPI): void {
183
+ pi.registerCommand("arey-doctor", {
184
+ description: "Check Arey Pi package, project bootstrap, and subagent readiness",
185
+ handler: (_args, ctx) => {
186
+ handleDoctor(pi, ctx);
187
+ return Promise.resolve();
188
+ },
189
+ });
190
+
191
+ pi.registerCommand("arey-bootstrap", {
192
+ description: "Install Arey Pi subagents and optionally scaffold specs/docs",
193
+ handler: (args, ctx) => {
194
+ handleBootstrap(pi, args, ctx);
195
+ return Promise.resolve();
196
+ },
197
+ });
198
+ }
@@ -56,96 +56,42 @@ export function parseBootstrapFlags(args: string): BootstrapPlan {
56
56
  };
57
57
  }
58
58
 
59
- export type WorkflowKind = "feature" | "bugfix" | "sync" | "review" | "assess" | string;
59
+ export function shouldActivateAreyPiHarness(prompt: string): boolean {
60
+ const normalized = prompt.toLowerCase();
61
+ if (normalized.includes("arey pi harness is active")) return false;
62
+ return /\barey(?:\s+pi)?\b/.test(normalized);
63
+ }
60
64
 
61
65
  const evidenceSummary = `Final evidence format:\n- Behaviour/spec impact:\n- Tests/TDD, including test location:\n- Validation commands and results:\n- Quality tooling:\n- Spec sync:\n- Documentation sync:\n- Architecture/ADR/glossary impact:\n- Database/DBML impact:\n- Residual risks:`;
62
66
 
63
- function commonWorkflowMessage(): string {
64
- return [
65
- "Act as the Arey Pi tech lead.",
66
- "Use pi-subagents when available and appropriate.",
67
- "Keep orchestration authority in the parent session, give child agents bounded tasks, and keep one writer in the active worktree at a time.",
68
- "Clarify blocking ambiguity before editing; otherwise proceed incrementally.",
69
- "Follow Arey Pi rules, preserve TDD for behaviour changes, and report evidence clearly.",
70
- ].join(" ");
71
- }
72
-
73
- function featureWorkflow(target: string): string {
74
- return [
75
- commonWorkflowMessage(),
76
- "",
77
- `Run the Arey Pi feature workflow for: ${target}`,
78
- "",
79
- "Execution contract:",
80
- "1. Scope: identify behaviour, impacted users, non-goals, risk level, and unknowns.",
81
- "2. Specs: confirm or update canonical Gherkin before production behaviour changes; use arey-pi.spec-author when available.",
82
- "3. TDD: use arey-pi.tdd-implementer for Red → Green → Refactor; tests must live outside production source directories by default.",
83
- "4. Implementation: make the smallest high-quality change; avoid speculative architecture.",
84
- "5. Sync: use arey-pi.spec-syncer to align specs, tests, code, DBML, ADRs, glossary, README, docs, AGENTS.md, skills, prompts, rules, agents, commands, and tooling instructions when affected.",
85
- "6. Review: use fresh-context arey-pi.engineering-reviewer or reviewers when risk warrants it.",
86
- "",
87
- "Use scout/context-builder/planner first if codebase context is not clear.",
88
- evidenceSummary,
89
- ].join("\n");
90
- }
67
+ export function areyPiHarnessContext(prompt: string): string {
68
+ const target = prompt.trim() || "the current request";
91
69
 
92
- function bugfixWorkflow(target: string): string {
93
70
  return [
94
- commonWorkflowMessage(),
95
- "",
96
- `Run the Arey Pi bugfix workflow for: ${target}`,
71
+ "Arey Pi harness is active for this request.",
72
+ "Act as the parent Arey Pi orchestrator: infer intent, choose the workflow, delegate bounded work to specialist subagents when available, then synthesise and finalise.",
73
+ "Work naturally; do not expose workflow ceremony unless it helps the user.",
74
+ "Infer the user's intent yourself. The work may be a feature, bugfix, sync, review, assessment, or a mixed task.",
75
+ "Use the workflow as an internal operating loop: clarify → spec/plan → TDD or audit → sync → review → evidence.",
76
+ "Select the matching Arey Pi posture:",
77
+ "- Feature or behaviour change: clarify scope, update/confirm canonical Gherkin first, then preserve Red → Green → Refactor.",
78
+ "- Bugfix: reproduce with a meaningful failing regression test before production changes.",
79
+ "- Sync: inspect drift across specs, tests, code, DBML, ADRs, glossary, README, docs, AGENTS.md, skills, prompts, rules, agents, commands, templates, and tooling instructions.",
80
+ "- Review: perform adversarial engineering review with severity-classified findings.",
81
+ "- Assessment: audit readiness with evidence, blockers, quick wins, and a prioritised improvement plan.",
82
+ "Subagents are a first-class part of Arey Pi when available: use arey-pi.spec-author for specs, arey-pi.tdd-implementer for Red → Green → Refactor, arey-pi.spec-syncer for alignment, arey-pi.engineering-reviewer for fresh review, and arey-pi.project-evaluator for readiness assessment.",
83
+ "Use builtin scout/context-builder/planner/reviewer/oracle agents when they fit the task, especially for discovery, planning, second opinions, and fresh review.",
84
+ "Keep orchestration in the parent session, give child agents concrete bounded tasks, and keep one writer in the active worktree.",
85
+ "Tests should live outside production source directories by default.",
86
+ "Do not rewrite specs to hide implementation defects.",
87
+ "Report evidence and residual risks clearly before finalising.",
97
88
  "",
98
- "Execution contract:",
99
- "1. Reproduce: identify expected vs actual behaviour and affected scope.",
100
- "2. Regression test first: add or update a meaningful failing test that proves the bug before changing production code.",
101
- "3. Fix: implement the smallest high-quality correction without broad rewrites unless necessary.",
102
- "4. Refactor: improve design only while regression tests and existing tests remain green.",
103
- "5. Sync: update Gherkin, docs, DBML, ADRs, glossary, or architecture docs when the intended behaviour or design contract changed.",
104
- "6. Review: request fresh engineering review for security, data-loss, concurrency, auth, payment, migration, or public API bugs.",
89
+ `User request: ${target}`,
105
90
  "",
106
- "If a failing regression test cannot be demonstrated, state the blocker explicitly and do not claim TDD evidence.",
107
91
  evidenceSummary,
108
92
  ].join("\n");
109
93
  }
110
94
 
111
- function syncWorkflow(target: string): string {
112
- return [
113
- commonWorkflowMessage(),
114
- "",
115
- `Run Arey Pi spec and documentation sync for: ${target}`,
116
- "",
117
- "Sync contract:",
118
- "1. Inspect the requested scope and current diff before editing.",
119
- "2. Verify alignment across canonical Gherkin, tests, production code, DBML, ADRs, glossary, architecture docs, README files, docs, AGENTS.md, skills, prompts, rules, agents, commands, templates, and tooling instructions.",
120
- "3. Classify drift as blocking, recommended, or unaffected.",
121
- "4. Fix safe drift directly when the intended behaviour is clear; otherwise ask for a decision.",
122
- "5. Do not rewrite specs to hide implementation defects.",
123
- "6. Run relevant validation after changes.",
124
- "",
125
- "End with both statuses exactly: `Specs updated` or `Specs unaffected`; `Docs updated` or `Docs unaffected`, with evidence.",
126
- evidenceSummary,
127
- ].join("\n");
128
- }
129
-
130
- export function workflowMessage(kind: WorkflowKind, args: string): string {
131
- const target = args.trim() || "the current repository/task";
132
-
133
- switch (kind) {
134
- case "feature":
135
- return featureWorkflow(target);
136
- case "bugfix":
137
- return bugfixWorkflow(target);
138
- case "sync":
139
- return syncWorkflow(target);
140
- case "review":
141
- return `${commonWorkflowMessage()}\n\nRun an Arey Pi engineering review for: ${target}\n\nPrefer fresh-context review. Review architecture, code quality, test quality and location, quality tooling, security, privacy, operability, maintainability, and spec/ADR/DBML/documentation concerns. Classify findings by severity.`;
142
- case "assess":
143
- return `${commonWorkflowMessage()}\n\nAssess this repository against Arey Pi Project Readiness. Audit only by default. Produce scores, evidence, blockers, quick wins, and a prioritised improvement plan.`;
144
- default:
145
- return `${commonWorkflowMessage()}\n\nWork on: ${target}`;
146
- }
147
- }
148
-
149
95
  export type DoctorReportInput = {
150
96
  packageVersion: string;
151
97
  cwd: string;
@@ -183,7 +129,7 @@ export function buildDoctorReport(input: DoctorReportInput): string {
183
129
  "",
184
130
  "## Recommended next step",
185
131
  input.installedAgentsCount === input.requiredAgentsCount
186
- ? "- Project-local Arey Pi subagents are installed. Use `/arey-feature`, `/arey-bugfix`, `/arey-sync`, `/arey-review`, or natural language."
132
+ ? "- Project-local Arey Pi subagents are installed. Use natural language such as `Implementa password reset siguiendo Arey Pi`."
187
133
  : "- Run `/arey-bootstrap` to install project-local Arey Pi subagents.",
188
134
  ].join("\n");
189
135
  }
@@ -1,273 +1,8 @@
1
- import { copyFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
- import { dirname, join, relative } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
5
- import {
6
- buildDoctorReport,
7
- docsScaffoldFiles,
8
- parseBootstrapFlags,
9
- requiredAgents,
10
- specScaffoldFiles,
11
- workflowMessage,
12
- type ScaffoldFile,
13
- } from "./core.ts";
2
+ import { registerBootstrapCommands } from "./bootstrap.ts";
3
+ import { registerWorkflowRuntime } from "./workflow-runtime.ts";
14
4
 
15
- const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
16
- const agentSourceDir = join(packageRoot, "agents");
17
- const rulesDir = join(packageRoot, "rules");
18
- const templatesDir = join(packageRoot, "templates");
19
-
20
- function cwdFrom(ctx: unknown): string {
21
- const maybe = ctx as { cwd?: string };
22
- return maybe.cwd ?? process.cwd();
23
- }
24
-
25
- function fileExists(path: string): boolean {
26
- try {
27
- return existsSync(path) && statSync(path).isFile();
28
- } catch {
29
- return false;
30
- }
31
- }
32
-
33
- function dirExists(path: string): boolean {
34
- try {
35
- return existsSync(path) && statSync(path).isDirectory();
36
- } catch {
37
- return false;
38
- }
39
- }
40
-
41
- function copyAgents(targetDir: string, force: boolean): { copied: string[]; skipped: string[]; missing: string[] } {
42
- mkdirSync(targetDir, { recursive: true });
43
-
44
- const copied: string[] = [];
45
- const skipped: string[] = [];
46
- const missing: string[] = [];
47
-
48
- for (const agent of requiredAgents) {
49
- const source = join(agentSourceDir, agent);
50
- const target = join(targetDir, agent);
51
-
52
- if (!fileExists(source)) {
53
- missing.push(agent);
54
- continue;
55
- }
56
-
57
- if (fileExists(target) && !force) {
58
- skipped.push(agent);
59
- continue;
60
- }
61
-
62
- copyFileSync(source, target);
63
- copied.push(agent);
64
- }
65
-
66
- return { copied, skipped, missing };
67
- }
68
-
69
- type ScaffoldResult = { created: string[]; skipped: string[] };
70
-
71
- function templateContent(name: string): string {
72
- return readFileSync(join(templatesDir, name), "utf8");
73
- }
74
-
75
- function writeTemplateIfMissing(file: ScaffoldFile, force: boolean, cwd: string, result: ScaffoldResult) {
76
- const target = join(cwd, file.target);
77
- mkdirSync(dirname(target), { recursive: true });
78
-
79
- if (fileExists(target) && !force) {
80
- result.skipped.push(file.target);
81
- return;
82
- }
83
-
84
- writeFileSync(target, templateContent(file.template));
85
- result.created.push(file.target);
86
- }
87
-
88
- function scaffoldFiles(cwd: string, force: boolean, files: ScaffoldFile[]): ScaffoldResult {
89
- const result: ScaffoldResult = { created: [], skipped: [] };
90
-
91
- for (const file of files) {
92
- writeTemplateIfMissing(file, force, cwd, result);
93
- }
94
-
95
- return result;
96
- }
97
-
98
- function scaffoldSpecs(cwd: string, force: boolean): ScaffoldResult {
99
- return scaffoldFiles(cwd, force, specScaffoldFiles);
100
- }
101
-
102
- function scaffoldDocs(cwd: string, force: boolean): ScaffoldResult {
103
- return scaffoldFiles(cwd, force, docsScaffoldFiles);
104
- }
105
-
106
- function starterAgentsMd(): string {
107
- return templateContent("AGENTS.md");
108
- }
109
-
110
- function sendWorkflow(
111
- pi: ExtensionAPI,
112
- args: string,
113
- ctx: {
114
- ui: { notify(message: string, level?: string): void };
115
- isIdle(): boolean;
116
- },
117
- kind: string,
118
- usage: string,
119
- ) {
120
- if (!args.trim()) {
121
- ctx.ui.notify(usage, "warning");
122
- return;
123
- }
124
-
125
- const message = workflowMessage(kind, args);
126
- if (ctx.isIdle()) {
127
- pi.sendUserMessage(message);
128
- } else {
129
- pi.sendUserMessage(message, { deliverAs: "followUp" });
130
- ctx.ui.notify("Arey Pi workflow queued as follow-up", "info");
131
- }
132
- }
133
-
134
- function packageVersion(): string {
135
- try {
136
- const pkg = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")) as { version?: string };
137
- return pkg.version ?? "unknown";
138
- } catch {
139
- return "unknown";
140
- }
141
- }
142
-
143
- export default function areyPi(pi: ExtensionAPI) {
144
- pi.registerCommand("arey-doctor", {
145
- description: "Check Arey Pi package, project bootstrap, and subagent readiness",
146
- handler: async (_args, ctx) => {
147
- const cwd = cwdFrom(ctx);
148
- const projectAgentDir = join(cwd, ".pi", "agents", "arey-pi");
149
- const commands = pi.getCommands();
150
- const hasSubagentsCommand = commands.some((command) => command.name.startsWith("subagents-doctor"));
151
- const installedAgents = requiredAgents.filter((agent) => fileExists(join(projectAgentDir, agent)));
152
- const missingAgents = requiredAgents.filter((agent) => !fileExists(join(projectAgentDir, agent)));
153
- const packageAgents = requiredAgents.filter((agent) => fileExists(join(agentSourceDir, agent)));
154
- const prompts = commands.filter(
155
- (command) => command.source === "prompt" && command.sourceInfo?.source?.includes("arey-pi"),
156
- );
157
- const skills = commands.filter(
158
- (command) => command.source === "skill" && command.sourceInfo?.source?.includes("arey-pi"),
159
- );
160
-
161
- const report = buildDoctorReport({
162
- packageVersion: packageVersion(),
163
- cwd,
164
- packageRulesPresent: dirExists(rulesDir),
165
- packageTemplatesPresent: dirExists(templatesDir),
166
- packageAgentsCount: packageAgents.length,
167
- requiredAgentsCount: requiredAgents.length,
168
- hasSubagentsCommand,
169
- installedAgentsCount: installedAgents.length,
170
- hasRootAgentsMd: fileExists(join(cwd, "AGENTS.md")),
171
- hasPiSettings: fileExists(join(cwd, ".pi", "settings.json")),
172
- promptsCount: prompts.length,
173
- skillsCount: skills.length,
174
- missingAgents,
175
- });
176
-
177
- pi.sendMessage({
178
- customType: "arey-pi-doctor",
179
- content: report,
180
- display: true,
181
- details: {},
182
- });
183
- },
184
- });
185
-
186
- pi.registerCommand("arey-bootstrap", {
187
- description: "Install Arey Pi subagents and optionally scaffold specs/docs",
188
- handler: async (args, ctx) => {
189
- const cwd = cwdFrom(ctx);
190
- const { force, createAgentsMd, createSpecs, createDocs } = parseBootstrapFlags(args);
191
- const targetDir = join(cwd, ".pi", "agents", "arey-pi");
192
- const result = copyAgents(targetDir, force);
193
- const specsResult = createSpecs ? scaffoldSpecs(cwd, force) : { created: [], skipped: [] };
194
- const docsResult = createDocs ? scaffoldDocs(cwd, force) : { created: [], skipped: [] };
195
- const agentsMdPath = join(cwd, "AGENTS.md");
196
- let agentsMdStatus = "unchanged";
197
-
198
- if (!fileExists(agentsMdPath) && (createAgentsMd || force)) {
199
- writeFileSync(agentsMdPath, starterAgentsMd());
200
- agentsMdStatus = "created";
201
- } else if (fileExists(agentsMdPath) && createAgentsMd && !force) {
202
- agentsMdStatus = "skipped existing";
203
- }
204
-
205
- const report = [
206
- "# Arey Pi Bootstrap",
207
- "",
208
- `- Target: ${relative(cwd, targetDir)}`,
209
- `- Copied agents: ${result.copied.length}`,
210
- `- Skipped existing agents: ${result.skipped.length}`,
211
- `- Missing package agents: ${result.missing.length}`,
212
- `- AGENTS.md: ${agentsMdStatus}`,
213
- `- Spec scaffold created: ${specsResult.created.length}`,
214
- `- Spec scaffold skipped: ${specsResult.skipped.length}`,
215
- `- Docs scaffold created: ${docsResult.created.length}`,
216
- `- Docs scaffold skipped: ${docsResult.skipped.length}`,
217
- "",
218
- "## Copied agents",
219
- result.copied.length ? result.copied.map((agent) => `- ${agent}`).join("\n") : "- none",
220
- "",
221
- "## Skipped agents",
222
- result.skipped.length ? result.skipped.map((agent) => `- ${agent}`).join("\n") : "- none",
223
- "",
224
- "## Created scaffold files",
225
- [...specsResult.created, ...docsResult.created].length
226
- ? [...specsResult.created, ...docsResult.created].map((path) => `- ${path}`).join("\n")
227
- : "- none",
228
- "",
229
- "## Skipped scaffold files",
230
- [...specsResult.skipped, ...docsResult.skipped].length
231
- ? [...specsResult.skipped, ...docsResult.skipped].map((path) => `- ${path}`).join("\n")
232
- : "- none",
233
- "",
234
- "Run `/arey-doctor` to verify setup.",
235
- ].join("\n");
236
-
237
- pi.sendMessage({
238
- customType: "arey-pi-bootstrap",
239
- content: report,
240
- display: true,
241
- details: { agents: result, specs: specsResult, docs: docsResult, agentsMd: agentsMdStatus },
242
- });
243
- },
244
- });
245
-
246
- pi.registerCommand("arey-feature", {
247
- description: "Run an Arey Pi spec-to-TDD feature workflow",
248
- handler: async (args, ctx) => sendWorkflow(pi, args, ctx, "feature", "Usage: /arey-feature <feature request>"),
249
- });
250
-
251
- pi.registerCommand("arey-bugfix", {
252
- description: "Run an Arey Pi regression-test-first bugfix workflow",
253
- handler: async (args, ctx) => sendWorkflow(pi, args, ctx, "bugfix", "Usage: /arey-bugfix <bug description>"),
254
- });
255
-
256
- pi.registerCommand("arey-sync", {
257
- description: "Run Arey Pi spec, test, code, DBML, ADR, and glossary sync",
258
- handler: async (args, ctx) =>
259
- sendWorkflow(pi, args || "the current repository", ctx, "sync", "Usage: /arey-sync [scope]"),
260
- });
261
-
262
- pi.registerCommand("arey-review", {
263
- description: "Run an Arey Pi adversarial engineering review",
264
- handler: async (args, ctx) =>
265
- sendWorkflow(pi, args || "the current diff", ctx, "review", "Usage: /arey-review [scope]"),
266
- });
267
-
268
- pi.registerCommand("arey-assess", {
269
- description: "Assess project readiness against Arey Pi rules",
270
- handler: async (args, ctx) =>
271
- sendWorkflow(pi, args || "the current repository", ctx, "assess", "Usage: /arey-assess [scope]"),
272
- });
5
+ export default function areyPi(pi: ExtensionAPI): void {
6
+ registerBootstrapCommands(pi);
7
+ registerWorkflowRuntime(pi);
273
8
  }
@@ -0,0 +1,28 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ export const packageRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
6
+ export const agentSourceDir = join(packageRoot, "agents");
7
+ export const rulesDir = join(packageRoot, "rules");
8
+ export const templatesDir = join(packageRoot, "templates");
9
+
10
+ export function cwdFrom(ctx: { cwd: string }): string {
11
+ return ctx.cwd;
12
+ }
13
+
14
+ export function fileExists(path: string): boolean {
15
+ try {
16
+ return existsSync(path) && statSync(path).isFile();
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ export function dirExists(path: string): boolean {
23
+ try {
24
+ return existsSync(path) && statSync(path).isDirectory();
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
@@ -0,0 +1,112 @@
1
+ import {
2
+ isToolCallEventType,
3
+ type ExtensionAPI,
4
+ type ExtensionContext,
5
+ type ToolCallEvent,
6
+ type ToolCallEventResult,
7
+ } from "@earendil-works/pi-coding-agent";
8
+ import { areyPiHarnessContext, shouldActivateAreyPiHarness } from "./core.ts";
9
+
10
+ const areyPiSessionEntryType = "arey-pi-session";
11
+
12
+ type HarnessSessionData = { active: boolean };
13
+ type HarnessStore = { active: boolean };
14
+ type SessionEntry = ReturnType<ExtensionContext["sessionManager"]["getEntries"]>[number];
15
+ type CustomSessionEntry = Extract<SessionEntry, { type: "custom" }>;
16
+ type HarnessCustomEntry = CustomSessionEntry & { customType: typeof areyPiSessionEntryType };
17
+
18
+ function isObject(value: unknown): value is Record<string, unknown> {
19
+ return typeof value === "object" && value !== null;
20
+ }
21
+
22
+ function isHarnessSessionData(value: unknown): value is HarnessSessionData {
23
+ return isObject(value) && typeof value.active === "boolean";
24
+ }
25
+
26
+ function isHarnessCustomEntry(value: unknown): value is HarnessCustomEntry {
27
+ return isObject(value) && value.type === "custom" && value.customType === areyPiSessionEntryType;
28
+ }
29
+
30
+ function latestHarnessSessionState(ctx: ExtensionContext): boolean {
31
+ const entry = ctx.sessionManager.getEntries().filter(isHarnessCustomEntry).at(-1);
32
+ return entry && isHarnessSessionData(entry.data) ? entry.data.active : false;
33
+ }
34
+
35
+ function persistHarnessState(pi: ExtensionAPI, active: boolean): void {
36
+ pi.appendEntry(areyPiSessionEntryType, { active } satisfies HarnessSessionData);
37
+ }
38
+
39
+ function updateHarnessUi(active: boolean, ctx: ExtensionContext): void {
40
+ ctx.ui.setStatus("arey-pi", active ? ctx.ui.theme.fg("accent", "Arey Pi") : undefined);
41
+ ctx.ui.setWidget(
42
+ "arey-pi-harness",
43
+ active
44
+ ? [
45
+ "Arey Pi active: natural-language harness guidance is injected.",
46
+ "The agent infers feature/bugfix/sync/review/assessment intent.",
47
+ ]
48
+ : undefined,
49
+ );
50
+ }
51
+
52
+ function activateHarness(pi: ExtensionAPI, store: HarnessStore, ctx: ExtensionContext): void {
53
+ store.active = true;
54
+ persistHarnessState(pi, true);
55
+ updateHarnessUi(true, ctx);
56
+ }
57
+
58
+ function isSensitivePath(path: string): boolean {
59
+ return [".env", ".git/", "node_modules/"].some((protectedPath) => path.includes(protectedPath));
60
+ }
61
+
62
+ function editedPath(event: ToolCallEvent): string | undefined {
63
+ if (isToolCallEventType("write", event) || isToolCallEventType("edit", event)) {
64
+ return event.input.path;
65
+ }
66
+
67
+ return undefined;
68
+ }
69
+
70
+ function handleMutationGuardrails(
71
+ store: HarnessStore,
72
+ event: ToolCallEvent,
73
+ ctx: ExtensionContext,
74
+ ): ToolCallEventResult | undefined {
75
+ const path = editedPath(event);
76
+ if (!path) return undefined;
77
+
78
+ if (isSensitivePath(path)) {
79
+ ctx.ui.notify(`Blocked write to protected path: ${path}`, "warning");
80
+ return { block: true, reason: `Arey Pi guardrail: path is protected: ${path}` };
81
+ }
82
+
83
+ if (!store.active) return undefined;
84
+
85
+ return undefined;
86
+ }
87
+
88
+ export function registerWorkflowRuntime(pi: ExtensionAPI): void {
89
+ const store: HarnessStore = { active: false };
90
+
91
+ pi.on("session_start", (_event, ctx) => {
92
+ store.active = latestHarnessSessionState(ctx);
93
+ updateHarnessUi(store.active, ctx);
94
+ });
95
+
96
+ pi.on("before_agent_start", (event, ctx) => {
97
+ const requestedAreyPi = shouldActivateAreyPiHarness(event.prompt);
98
+ if (!store.active && !requestedAreyPi) return undefined;
99
+ if (requestedAreyPi) activateHarness(pi, store, ctx);
100
+
101
+ return {
102
+ message: {
103
+ customType: "arey-pi-harness-context",
104
+ content: areyPiHarnessContext(event.prompt),
105
+ display: false,
106
+ details: { source: requestedAreyPi ? "natural-language" : "active-session" },
107
+ },
108
+ };
109
+ });
110
+
111
+ pi.on("tool_call", (event, ctx) => handleMutationGuardrails(store, event, ctx));
112
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arey-pi",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A Pi package for canonical Gherkin specs, non-negotiable TDD, spec synchronisation, AI harness readiness, and senior-quality software delivery.",
5
5
  "license": "MIT",
6
6
  "author": "Alejandro Rey Leyva",