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 +17 -14
- package/docs/adoption.md +6 -6
- package/docs/commands.md +36 -231
- package/docs/templates.md +1 -1
- package/docs/workflows.md +13 -14
- package/extensions/arey-pi/bootstrap.ts +198 -0
- package/extensions/arey-pi/core.ts +26 -80
- package/extensions/arey-pi/index.ts +5 -270
- package/extensions/arey-pi/paths.ts +28 -0
- package/extensions/arey-pi/workflow-runtime.ts +112 -0
- package/package.json +1 -1
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
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
150
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
88
|
+
Ask naturally:
|
|
89
89
|
|
|
90
90
|
```txt
|
|
91
|
-
|
|
91
|
+
Evalúa este repo contra Arey Pi
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
152
|
+
## Natural-language development workflows
|
|
162
153
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
174
|
-
/arey-feature Add password reset with expiring email links
|
|
175
|
-
```
|
|
178
|
+
The injected harness guidance emphasises:
|
|
176
179
|
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
|
|
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
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
11
|
+
Example request:
|
|
13
12
|
|
|
14
13
|
```txt
|
|
15
|
-
|
|
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
|
-
|
|
43
|
+
Example request:
|
|
45
44
|
|
|
46
45
|
```txt
|
|
47
|
-
|
|
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
|
-
|
|
63
|
+
Example request:
|
|
65
64
|
|
|
66
65
|
```txt
|
|
67
|
-
|
|
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
|
-
|
|
103
|
+
Example request:
|
|
105
104
|
|
|
106
105
|
```txt
|
|
107
|
-
|
|
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
|
-
|
|
130
|
+
Example request:
|
|
132
131
|
|
|
133
132
|
```txt
|
|
134
|
-
|
|
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
|
|
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
|
|
64
|
-
|
|
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
|
-
|
|
95
|
-
"",
|
|
96
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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.
|
|
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",
|