@sallmarta/eye-hate-agent 1.0.5 → 1.0.6
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 +11 -8
- package/bin/eha.js +117 -71
- package/docs/templates/reusable-prompts/00-project-docs-bootstrap.md +23 -3
- package/docs/templates/reusable-prompts/00-project-docs-refresh.md +63 -18
- package/docs/templates/skills/parity-audit/SKILL.md +58 -14
- package/package.json +1 -1
- package/src/engine/install.js +140 -20
- package/src/engine/runtime-adapters.js +3 -3
- package/src/engine/state.js +10 -3
- package/src/engine/workflow-registry.js +0 -6
- package/docs/templates/reusable-prompts/00-project-docs-parity.md +0 -86
package/README.md
CHANGED
|
@@ -31,11 +31,14 @@ Once initialized, EHA projects a series of interactive workflows directly into y
|
|
|
31
31
|
|
|
32
32
|
| Slash Trigger | Primary Purpose |
|
|
33
33
|
| :--- | :--- |
|
|
34
|
-
| **`/eha-bootstrap`** | Initializes the
|
|
35
|
-
| **`/eha-refresh`** |
|
|
36
|
-
| **`/
|
|
37
|
-
| **`/sdd-
|
|
38
|
-
|
|
34
|
+
| **`/eha-bootstrap`** | Initializes the SDD document set for repos with **no existing documentation**. Scans codebase complexity, asks for a Taxonomy Tier (Lite/Standard/Enterprise), generates the tailored doc set. Stops and redirects to Refresh if existing docs or legacy docs are detected. |
|
|
35
|
+
| **`/eha-refresh`** | The main workhorse for repos with **any existing documentation**. Updates active SDD docs, migrates legacy docs, converts non-SDD docs, and creates missing SDD files — all by cross-referencing the actual codebase alongside existing material. Auto-detects the appropriate Taxonomy Tier for migration scenarios. Prompts the user to resolve any drift between codebase and docs. |
|
|
36
|
+
| **`/sdd-discuss`** | Collaborative brainstorming. Interviews you about edge cases, API shapes, data models, and constraints, then drafts spec snippets ready for injection into project docs. No code output. |
|
|
37
|
+
| **`/sdd-execute`** | Spec-Driven code generation via strict TDD. Reads specs → generates tests → generates code → validates against architecture. Refuses to code features not in the spec. |
|
|
38
|
+
|
|
39
|
+
> **Looking for parity audits?** Use the `parity-audit` skill directly:
|
|
40
|
+
> `@agent use parity-audit on this repository`
|
|
41
|
+
> This provides a full drift analysis without needing a dedicated command.
|
|
39
42
|
|
|
40
43
|
---
|
|
41
44
|
|
|
@@ -48,7 +51,7 @@ The EHA CLI provides a lightweight, frictionless setup and maintenance toolbelt:
|
|
|
48
51
|
| `eha init` (or `npx...`) | Automatically scans your repo root, lets you choose your target AI agent, and projects standard rules/skills. |
|
|
49
52
|
| `eha init <agent>` | Directly initiates the EHA project setup for a specific agent (e.g. `copilot`, `claude`, `antigravity`) |
|
|
50
53
|
| `eha doctor` | Performs a health check verifying that all projected rules, stubs, and workflows are present and intact. |
|
|
51
|
-
| `eha remove` | Safely deletes EHA's
|
|
54
|
+
| `eha remove [agent]` | Safely deletes EHA's generated contract files for the specified agent (or all agents if omitted), along with configuration files. |
|
|
52
55
|
|
|
53
56
|
---
|
|
54
57
|
|
|
@@ -67,9 +70,9 @@ in your repository. The engine will detect the version mismatch automatically, p
|
|
|
67
70
|
To completely remove EHA from your project and device:
|
|
68
71
|
|
|
69
72
|
### 1. Remove project files
|
|
70
|
-
|
|
73
|
+
To clean up projected AI files, run the following command in your project root. You can optionally specify a target agent (e.g. `claude`, `copilot`, `antigravity`) to remove only that agent's files while preserving other active installations:
|
|
71
74
|
```bash
|
|
72
|
-
$ eha remove
|
|
75
|
+
$ eha remove [agent]
|
|
73
76
|
```
|
|
74
77
|
|
|
75
78
|
### 2. Uninstall the CLI globally
|
package/bin/eha.js
CHANGED
|
@@ -24,22 +24,34 @@ const pkg = require('../package.json');
|
|
|
24
24
|
async function promptAgentChoice(currentAgent) {
|
|
25
25
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
26
26
|
try {
|
|
27
|
-
const
|
|
27
|
+
const runtimes = listSupportedRuntimes();
|
|
28
|
+
const defaultIndex = currentAgent
|
|
29
|
+
? runtimes.findIndex(r => r.id === currentAgent) + 1
|
|
30
|
+
: 1;
|
|
31
|
+
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log('Which agent?');
|
|
34
|
+
for (let i = 0; i < runtimes.length; i++) {
|
|
35
|
+
console.log(` ${i + 1}. ${runtimes[i].name}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
const answer = await rl.question(
|
|
29
|
-
`
|
|
39
|
+
`Choose [1-${runtimes.length}] (default: ${defaultIndex}): `,
|
|
30
40
|
);
|
|
31
|
-
const
|
|
32
|
-
return normalized || defaultLabel;
|
|
33
|
-
} finally {
|
|
34
|
-
rl.close();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
41
|
+
const trimmed = answer.trim();
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
+
// Accept number or name
|
|
44
|
+
if (!trimmed) return runtimes[defaultIndex - 1].id;
|
|
45
|
+
|
|
46
|
+
const num = parseInt(trimmed, 10);
|
|
47
|
+
if (num >= 1 && num <= runtimes.length) return runtimes[num - 1].id;
|
|
48
|
+
|
|
49
|
+
// Fallback: try to match by name (backward compat)
|
|
50
|
+
const normalized = trimmed.toLowerCase();
|
|
51
|
+
const match = runtimes.find(r => r.id === normalized);
|
|
52
|
+
if (match) return match.id;
|
|
53
|
+
|
|
54
|
+
return trimmed.toLowerCase(); // Let it fall through
|
|
43
55
|
} finally {
|
|
44
56
|
rl.close();
|
|
45
57
|
}
|
|
@@ -101,7 +113,11 @@ function printDoctorSummary(result) {
|
|
|
101
113
|
console.log(chalk.blue('EHA doctor'));
|
|
102
114
|
console.log(` Root : ${result.rootDir}`);
|
|
103
115
|
console.log(` Config : ${result.paths.configPath}`);
|
|
104
|
-
|
|
116
|
+
const agents = result.config.agents || (result.config.agent ? [result.config.agent] : []);
|
|
117
|
+
const agentDisplay = agents.length > 0
|
|
118
|
+
? agents.map(a => chalk.cyan(a)).join(', ')
|
|
119
|
+
: chalk.yellow('not set — run eha init');
|
|
120
|
+
console.log(` Agents : ${agentDisplay}`);
|
|
105
121
|
console.log('');
|
|
106
122
|
|
|
107
123
|
if (!result.isInitialized) {
|
|
@@ -128,60 +144,60 @@ function printDoctorSummary(result) {
|
|
|
128
144
|
async function runInitWizard(agentIdArg) {
|
|
129
145
|
const rootDir = resolveRootDir();
|
|
130
146
|
const config = readConfig(rootDir);
|
|
147
|
+
const manifest = readProjectManifest(rootDir);
|
|
131
148
|
|
|
132
149
|
let agentId = agentIdArg ? String(agentIdArg).trim().toLowerCase() : null;
|
|
150
|
+
const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
|
|
133
151
|
|
|
134
152
|
if (!agentId) {
|
|
135
|
-
if (
|
|
136
|
-
const manifest = readProjectManifest(rootDir);
|
|
137
|
-
if (manifest.packageVersion !== pkg.version && process.stdin.isTTY && process.stdout.isTTY) {
|
|
138
|
-
const regenerate = await promptConfirm(
|
|
139
|
-
`EHA files were generated by v${manifest.packageVersion ?? 'unknown'}, current is v${pkg.version}. Regenerate?`,
|
|
140
|
-
true,
|
|
141
|
-
);
|
|
142
|
-
if (!regenerate) {
|
|
143
|
-
console.log('Skipped.');
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
const result = initProject({ rootDir, agentId: config.agent });
|
|
147
|
-
printInitSummary(result);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
151
|
-
const overwrite = await promptOverwriteOrDiscard(
|
|
152
|
-
`EHA already set up (agent: ${chalk.cyan(config.agent)}).`,
|
|
153
|
-
);
|
|
154
|
-
if (!overwrite) {
|
|
155
|
-
console.log('Discarded.');
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
153
|
+
if (isInteractive) {
|
|
162
154
|
agentId = await promptAgentChoice(config.agent);
|
|
163
155
|
} else {
|
|
164
156
|
agentId = config.agent || SUPPORTED_AGENT_IDS[0];
|
|
165
157
|
}
|
|
166
|
-
} else if (config.agent && config.agent !== agentId) {
|
|
167
|
-
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
168
|
-
const overwrite = await promptOverwriteOrDiscard(
|
|
169
|
-
`EHA already set up (agent: ${chalk.cyan(config.agent)}). Switch to ${chalk.cyan(agentId)}?`,
|
|
170
|
-
);
|
|
171
|
-
if (!overwrite) {
|
|
172
|
-
console.log('Discarded.');
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
158
|
}
|
|
177
159
|
|
|
178
|
-
|
|
160
|
+
const normalized = String(agentId).trim().toLowerCase();
|
|
161
|
+
if (!SUPPORTED_AGENT_IDS.includes(normalized)) {
|
|
162
|
+
const runtimes = listSupportedRuntimes();
|
|
163
|
+
const list = runtimes.map((r, i) => `${i + 1}. ${r.name}`).join(', ');
|
|
179
164
|
console.error(
|
|
180
|
-
chalk.red(`Unsupported agent: ${agentId}.`) +
|
|
181
|
-
` Choose one of: ${
|
|
165
|
+
chalk.red(`Unsupported agent: ${agentIdArg || agentId}.`) +
|
|
166
|
+
` Choose one of: ${list}.`,
|
|
182
167
|
);
|
|
183
168
|
process.exit(1);
|
|
184
169
|
}
|
|
170
|
+
agentId = normalized;
|
|
171
|
+
|
|
172
|
+
const installedAgents = config.agents || (config.agent ? [config.agent] : []);
|
|
173
|
+
const isAlreadyInstalled = installedAgents.includes(agentId);
|
|
174
|
+
|
|
175
|
+
if (isInteractive) {
|
|
176
|
+
if (isAlreadyInstalled) {
|
|
177
|
+
const currentVer = manifest.packageVersion || 'unknown';
|
|
178
|
+
let msg = '';
|
|
179
|
+
if (currentVer !== pkg.version) {
|
|
180
|
+
msg = `EHA is already set up for ${chalk.cyan(agentId)} (v${currentVer}). Regenerate with v${pkg.version}?`;
|
|
181
|
+
} else {
|
|
182
|
+
msg = `EHA is already set up for ${chalk.cyan(agentId)}. Regenerate/overwrite?`;
|
|
183
|
+
}
|
|
184
|
+
const confirm = await promptConfirm(msg, true);
|
|
185
|
+
if (!confirm) {
|
|
186
|
+
console.log('Skipped.');
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
} else if (installedAgents.length > 0) {
|
|
190
|
+
const listStr = installedAgents.map(a => chalk.cyan(a)).join(', ');
|
|
191
|
+
const confirm = await promptConfirm(
|
|
192
|
+
`EHA is set up for: ${listStr}. Add ${chalk.cyan(agentId)}?`,
|
|
193
|
+
true,
|
|
194
|
+
);
|
|
195
|
+
if (!confirm) {
|
|
196
|
+
console.log('Skipped.');
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
185
201
|
|
|
186
202
|
const result = initProject({ rootDir, agentId });
|
|
187
203
|
printInitSummary(result);
|
|
@@ -204,34 +220,64 @@ program
|
|
|
204
220
|
});
|
|
205
221
|
|
|
206
222
|
program
|
|
207
|
-
.command('remove')
|
|
208
|
-
.description('Remove EHA-generated files and config from this project')
|
|
209
|
-
.action(async () => {
|
|
223
|
+
.command('remove [agent]')
|
|
224
|
+
.description('Remove EHA-generated files and config from this project (optionally for a specific agent)')
|
|
225
|
+
.action(async (agentArg) => {
|
|
210
226
|
const rootDir = resolveRootDir();
|
|
211
227
|
const config = readConfig(rootDir);
|
|
228
|
+
const installedAgents = config.agents || (config.agent ? [config.agent] : []);
|
|
212
229
|
|
|
213
|
-
if (
|
|
230
|
+
if (installedAgents.length === 0) {
|
|
214
231
|
console.log(chalk.yellow('EHA is not initialized here. Nothing to remove.'));
|
|
215
232
|
return;
|
|
216
233
|
}
|
|
217
234
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
console.log('Aborted.');
|
|
235
|
+
let targetAgent = agentArg ? String(agentArg).trim().toLowerCase() : null;
|
|
236
|
+
|
|
237
|
+
if (targetAgent) {
|
|
238
|
+
if (!installedAgents.includes(targetAgent)) {
|
|
239
|
+
console.log(chalk.yellow(`Agent ${chalk.cyan(targetAgent)} is not currently set up in this project.`));
|
|
224
240
|
return;
|
|
225
241
|
}
|
|
226
|
-
}
|
|
227
242
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
243
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
244
|
+
const confirmed = await promptConfirm(
|
|
245
|
+
`Remove EHA for agent: ${chalk.cyan(targetAgent)} from this project?`,
|
|
246
|
+
true,
|
|
247
|
+
);
|
|
248
|
+
if (!confirmed) {
|
|
249
|
+
console.log('Aborted.');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const result = removeProject({ rootDir, agentId: targetAgent });
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log(chalk.green(`✓ EHA files for ${chalk.cyan(targetAgent)} removed.`));
|
|
257
|
+
for (const f of result.removedFiles) {
|
|
258
|
+
console.log(` ${chalk.gray(f)}`);
|
|
259
|
+
}
|
|
260
|
+
console.log('');
|
|
261
|
+
} else {
|
|
262
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
263
|
+
const listStr = installedAgents.map(a => chalk.cyan(a)).join(', ');
|
|
264
|
+
const confirmed = await promptConfirm(
|
|
265
|
+
`Remove EHA (all agents: ${listStr}) from this project?`,
|
|
266
|
+
);
|
|
267
|
+
if (!confirmed) {
|
|
268
|
+
console.log('Aborted.');
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const result = removeProject({ rootDir });
|
|
274
|
+
console.log('');
|
|
275
|
+
console.log(chalk.green('✓ EHA removed.'));
|
|
276
|
+
for (const f of result.removedFiles) {
|
|
277
|
+
console.log(` ${chalk.gray(f)}`);
|
|
278
|
+
}
|
|
279
|
+
console.log('');
|
|
233
280
|
}
|
|
234
|
-
console.log('');
|
|
235
281
|
});
|
|
236
282
|
|
|
237
283
|
program
|
|
@@ -3,8 +3,28 @@
|
|
|
3
3
|
Generate the **initial project documentation set** for a repository.
|
|
4
4
|
You must dynamically adjust your behavior based on the current state of the repository.
|
|
5
5
|
|
|
6
|
-
## Step
|
|
7
|
-
|
|
6
|
+
## Step 0: Pre-Flight Check
|
|
7
|
+
|
|
8
|
+
Before analyzing complexity, scan the repository for existing documentation:
|
|
9
|
+
|
|
10
|
+
1. Check for `docs/project-docs/` with any content.
|
|
11
|
+
2. Check for `docs-legacy/`, `docs-old/`, `archive/`, or `reference/` folders with content.
|
|
12
|
+
3. Check for any `docs/` folder containing structured markdown beyond a bare root README.
|
|
13
|
+
|
|
14
|
+
**If ANY of the above exist:**
|
|
15
|
+
STOP. Do not proceed with bootstrap. Inform the user:
|
|
16
|
+
|
|
17
|
+
"I found existing documentation in this repository:
|
|
18
|
+
- [list what was found]
|
|
19
|
+
|
|
20
|
+
Bootstrap is for repos with no documentation. For repos with existing docs (even legacy or non-SDD format), use the **Refresh** workflow instead — it can migrate, update, and create SDD docs from your existing content combined with codebase analysis.
|
|
21
|
+
|
|
22
|
+
Should I switch to the Refresh workflow?"
|
|
23
|
+
|
|
24
|
+
**If NONE exist (only code and/or a bare root README):** Proceed to Step 1.
|
|
25
|
+
|
|
26
|
+
## Step 1: Complexity Detection (The Adaptive Taxonomy)
|
|
27
|
+
Analyze the workspace to determine its complexity by inspecting the codebase.
|
|
8
28
|
|
|
9
29
|
Based on the repository's complexity, you MUST recommend one of the following **Taxonomy Tiers**:
|
|
10
30
|
|
|
@@ -37,4 +57,4 @@ Before finishing, check that:
|
|
|
37
57
|
3. The generated documents strictly match the approved Taxonomy Tier and structural definitions cataloged in the master registry.
|
|
38
58
|
|
|
39
59
|
## Inputs
|
|
40
|
-
Use the project brief, codebase, and constraints provided below to begin your
|
|
60
|
+
Use the project brief, codebase, and constraints provided below to begin your analysis.
|
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
# Project Docs Refresh Reusable Prompt
|
|
2
2
|
|
|
3
|
-
Refresh
|
|
3
|
+
Refresh, migrate, or create project documentation by combining the **codebase** and any **existing documentation** (active SDD docs, legacy docs, or non-SDD markdown).
|
|
4
4
|
|
|
5
5
|
## Goal
|
|
6
6
|
|
|
7
|
-
Update **only the docs that own the changed information** while keeping the documentation set consistent.
|
|
7
|
+
Update **only the docs that own the changed information** while keeping the documentation set consistent. When creating docs for the first time from existing material, combine codebase evidence with legacy/existing content to produce accurate SDD-compliant documentation.
|
|
8
|
+
|
|
9
|
+
## Step 0: Doc State Detection
|
|
10
|
+
|
|
11
|
+
Before refreshing, classify the repository's documentation state:
|
|
12
|
+
|
|
13
|
+
| State | Condition | Action Path |
|
|
14
|
+
|:---|:---|:---|
|
|
15
|
+
| **Active SDD** | `docs/project-docs/` exists with SDD-format files (stable headings, 4-layer taxonomy) | Standard refresh: update owning docs, sync dependents |
|
|
16
|
+
| **Legacy Only** | `docs-legacy/`, `docs-old/`, `archive/`, or `reference/` exist, but no `docs/project-docs/` | Migration refresh: create SDD docs from legacy content + codebase |
|
|
17
|
+
| **Non-SDD Docs** | `docs/` exists with unstructured markdown (no stable headings, no taxonomy) | Conversion refresh: treat as legacy input, create SDD docs from content + codebase |
|
|
18
|
+
| **Mixed** | `docs/project-docs/` exists AND legacy/reference folders also exist | Hybrid refresh: update active SDD docs + migrate unmapped legacy content + codebase |
|
|
19
|
+
|
|
20
|
+
For **Legacy Only** and **Non-SDD Docs** states, auto-detect the Taxonomy Tier:
|
|
21
|
+
- Examine the breadth and depth of the existing documentation + codebase complexity.
|
|
22
|
+
- If content covers only core concerns (identity, architecture, status) → Tier 1 (Lite).
|
|
23
|
+
- If content includes testing, API, database, or CI/CD concerns → Tier 2 (Standard).
|
|
24
|
+
- If content includes governance, security, compliance, observability, i18n → Tier 3 (Enterprise).
|
|
25
|
+
- When uncertain, choose the lower tier and note what would trigger upgrade.
|
|
26
|
+
- State the auto-detected tier in your output so the user can override it if needed.
|
|
27
|
+
|
|
28
|
+
**Dynamic Generation from Registry:** You MUST read the master registry file `docs/templates/project-docs-template/index.md` to obtain the universal stable headings schema and the unique domain-specific headings for each document type within the detected or applicable tier. Generate each document dynamically using this structural mapping.
|
|
29
|
+
|
|
30
|
+
Proceed to the applicable action path.
|
|
8
31
|
|
|
9
32
|
## Required Behavior
|
|
10
33
|
|
|
@@ -19,30 +42,48 @@ Update **only the docs that own the changed information** while keeping the docu
|
|
|
19
42
|
9. If the change affects an optional regular doc or its metadata, update `docs/project-docs/index.md` when present.
|
|
20
43
|
10. If the change affects domain-specific technical guidance, update the owning guideline and `technical-guidelines/index.md` when present.
|
|
21
44
|
11. When legacy or reference docs are being mapped into the active owner-doc set, classify them by the durable concern they govern rather than by the legacy folder or filename; legacy names are hints only.
|
|
22
|
-
12. Normalize non-standard legacy labels by meaning when they map cleanly to an active owner. For example, `epic`, `milestone`, or `roadmap` material may map to `docs/project-docs/foundation/phases
|
|
45
|
+
12. Normalize non-standard legacy labels by meaning when they map cleanly to an active owner. For example, `epic`, `milestone`, or `roadmap` material may map to `docs/project-docs/foundation/phases.md`, while `protocol`, `procedure`, `policy`, or `standard` material may map to `docs/project-docs/technical-guidelines/` when the content is domain-specific technical guidance.
|
|
23
46
|
13. When legacy or reference docs show that a justified optional doc should become active under `docs/project-docs/`, promote it into the active owner-doc set instead of leaving it stranded in reference-only folders.
|
|
24
47
|
14. When legacy or reference docs contain domain-specific technical guidance that is still valid, create or update the relevant files under `docs/project-docs/technical-guidelines/` and create `technical-guidelines/index.md` when any guideline becomes active.
|
|
25
|
-
15. When legacy or reference docs contain explicit phased planning, epic tracking, or execution-map detail that should stay active, create or update
|
|
48
|
+
15. When legacy or reference docs contain explicit phased planning, epic tracking, or execution-map detail that should stay active, create or update `docs/project-docs/foundation/phases.md` and register the active optional doc in `docs/project-docs/index.md`.
|
|
26
49
|
16. If a legacy artifact could plausibly map to more than one active owner, or if preserving the legacy label may be intentional, ask the user for direction instead of guessing.
|
|
27
50
|
17. Preserve valuable legacy sections (e.g., 'Decision Rationale') that do not exist in the starter templates. Decide whether this information belongs as a new custom section in an existing document or warrants a new separate file entirely. Ask the user if the best approach is ambiguous. Do not discard domain-specific knowledge just because it lacks a standard template heading.
|
|
28
51
|
18. When asking for that direction, prefer a concise question that states the inferred owner and the fallback choices. Example: `I found legacy "protocol" docs that look like technical guidance. Should I 1. skip them, 2. migrate them into active guideline docs, or 3. preserve "protocol" as a project-specific doc type?`
|
|
29
|
-
19. When docs are being created for the first time against an existing codebase with no prior documentation, inspect code, comments, configs, tests, and repository structure for valuable domain knowledge that goes beyond standard template headings. Surface these findings as new custom sections or new files where justified. Mark codebase-inferred facts as `Inferred from
|
|
52
|
+
19. When docs are being created for the first time against an existing codebase with no prior documentation, inspect code, comments, configs, tests, and repository structure for valuable domain knowledge that goes beyond standard template headings. Surface these findings as new custom sections or new files where justified. Mark codebase-inferred facts as `Inferred from codebase` or `Open Question` until the user confirms them.
|
|
53
|
+
20. **Always cross-reference the codebase.** When creating or updating any SDD document, inspect the relevant source code, configs, tests, package manifests, CI/CD pipelines, and runtime artifacts to verify and enrich the documentation. Do not rely solely on existing docs or legacy material — the codebase is evidence.
|
|
54
|
+
21. **When codebase evidence contradicts existing documentation or legacy material, do NOT silently choose one side.** Instead, prompt the user with a concise question and option selection. Example: `I found a drift between the codebase and the docs: [describe the conflict]. Which is correct? 1. The codebase (update the docs to match), 2. The docs (flag the code as needing a fix), or 3. Both are intentionally different (document the exception).` Always present at least these three options. Do not proceed until the user resolves the conflict.
|
|
55
|
+
22. When creating SDD docs from legacy + codebase, actively mine:
|
|
56
|
+
- `package.json` / dependency manifests → architecture, stack, testing frameworks
|
|
57
|
+
- CI/CD configs (`.github/workflows/`, `Dockerfile`, etc.) → operations/ci-cd
|
|
58
|
+
- Test directories and test runners → development/testing
|
|
59
|
+
- Database schemas, migrations, ORM configs → development/database
|
|
60
|
+
- API route definitions, controllers, middleware → development/api-contract
|
|
61
|
+
- Environment variables, secrets management → operations/production-runbook
|
|
62
|
+
- Error handling patterns, logging setup → operations/observability-error-handling
|
|
63
|
+
- Auth/RBAC implementations → operations/security-compliance
|
|
64
|
+
- i18n config, locale files → development/internationalization
|
|
65
|
+
- README, inline comments, decision rationale → foundation/prd, architecture
|
|
66
|
+
23. Mark all codebase-inferred facts as `Inferred from codebase` until the user confirms them.
|
|
30
67
|
|
|
31
68
|
### Review Sequence
|
|
32
69
|
|
|
33
|
-
1.
|
|
34
|
-
2. Read the
|
|
35
|
-
3.
|
|
36
|
-
4. Read
|
|
37
|
-
5.
|
|
38
|
-
6. Read
|
|
39
|
-
7.
|
|
40
|
-
8.
|
|
41
|
-
9.
|
|
42
|
-
10.
|
|
70
|
+
1. Run Step 0 (Doc State Detection).
|
|
71
|
+
2. Read the change summary (if provided) or the user's intent.
|
|
72
|
+
3. **Scan the codebase** — inspect source code, configs, tests, CI/CD pipelines, and package manifests for current truth. This step is NOT optional.
|
|
73
|
+
4. Read the owning project docs (if Active SDD or Mixed state).
|
|
74
|
+
5. Read `docs/project-docs/index.md` and `docs/project-docs/technical-guidelines/index.md` when present.
|
|
75
|
+
6. Read legacy/reference folders when present.
|
|
76
|
+
7. Read relevant guideline docs when the change touches technical rules.
|
|
77
|
+
8. Identify impacted dependent docs.
|
|
78
|
+
9. Cross-reference codebase findings against doc/legacy claims — resolve conflicts by prompting the user (see rule 21).
|
|
79
|
+
10. Refresh/create the owning docs first (using combined codebase + docs evidence).
|
|
80
|
+
11. Refresh summary or index docs second.
|
|
81
|
+
12. Run a consistency pass.
|
|
43
82
|
|
|
44
83
|
## Ownership Examples
|
|
45
84
|
|
|
85
|
+
For each mapping below, also inspect the corresponding codebase artifacts (source files, configs, tests) to verify and enrich the documentation.
|
|
86
|
+
|
|
46
87
|
- stack or dependency changes → `foundation/architecture.md`, `development/testing.md`
|
|
47
88
|
- feature scope changes → `foundation/prd.md`, `foundation/status.md`
|
|
48
89
|
- detailed requirements or acceptance changes → `foundation/prd.md`, `foundation/status.md`
|
|
@@ -63,10 +104,12 @@ Update **only the docs that own the changed information** while keeping the docu
|
|
|
63
104
|
|
|
64
105
|
Your result should state:
|
|
65
106
|
|
|
66
|
-
1. which docs were updated
|
|
67
|
-
2. why each doc was updated
|
|
107
|
+
1. which docs were updated or created
|
|
108
|
+
2. why each doc was updated or created
|
|
68
109
|
3. which docs were intentionally left unchanged
|
|
69
110
|
4. any remaining consistency risks or open questions
|
|
111
|
+
5. which codebase-vs-doc conflicts were resolved and how (per user direction)
|
|
112
|
+
6. the auto-detected tier (for Legacy Only / Non-SDD states), if applicable
|
|
70
113
|
|
|
71
114
|
## Final Pass
|
|
72
115
|
|
|
@@ -75,7 +118,9 @@ Before finishing, check that:
|
|
|
75
118
|
1. the updated docs still match the EHA Project Doc Rules above
|
|
76
119
|
2. platform instruction surfaces and skills would now read the correct project-specific truth
|
|
77
120
|
3. no stale summary remains in `foundation/status.md`, `docs/project-docs/index.md`, `technical-guidelines/index.md`, or other index docs
|
|
121
|
+
4. codebase-inferred facts are clearly marked and do not silently override user-confirmed truths
|
|
122
|
+
5. the auto-detected tier (for Legacy Only / Non-SDD states) is stated in the output so the user can override it if needed
|
|
78
123
|
|
|
79
124
|
## Inputs
|
|
80
125
|
|
|
81
|
-
Use the change summary, affected artifacts,
|
|
126
|
+
Use the change summary, affected artifacts, current project docs, legacy/reference docs, AND the current codebase (source code, configs, tests, CI/CD, package manifests) provided below.
|
|
@@ -8,7 +8,7 @@ argument-hint: "Describe the scope to audit: full repository, docs only, reusabl
|
|
|
8
8
|
|
|
9
9
|
Performs an expert repository-wide drift audit to find contradictions, stale summaries, duplicated ownership, code-vs-doc authority conflicts, and historical artifacts that should be classified rather than confused with active truth.
|
|
10
10
|
|
|
11
|
-
This skill is the
|
|
11
|
+
This skill is the dedicated parity audit capability for the EHA system. Use it when the task is analytical rather than generative.
|
|
12
12
|
|
|
13
13
|
## Required Project Inputs
|
|
14
14
|
|
|
@@ -33,17 +33,33 @@ This skill is the reusable complement to the parity reusable prompt. Use it when
|
|
|
33
33
|
| Template maintenance | "Audit platform instruction surfaces and skills after changing the contract" |
|
|
34
34
|
| Handoff preparation | "Find contradictions before handing this repo to another maintainer" |
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
## Scope
|
|
37
|
+
|
|
38
|
+
Check at least these areas when present:
|
|
39
|
+
|
|
40
|
+
- `docs/project-docs/`
|
|
41
|
+
- `docs/project-docs/index.md`
|
|
42
|
+
- `docs/project-docs/technical-guidelines/`
|
|
43
|
+
- relevant code, tests, configs, or runtime-facing artifacts when a finding depends on current implementation behavior or source-of-truth ownership
|
|
44
|
+
- clearly named reference or archive folders such as `docs-legacy/`, `docs-old/`, `archive/`, or `reference/`
|
|
45
|
+
- platform instruction surfaces (mirrored rule files)
|
|
46
|
+
- skills and reusable prompts
|
|
47
|
+
- workflow docs and handoff docs
|
|
48
|
+
|
|
49
|
+
### High-Value Drift Categories
|
|
37
50
|
|
|
38
|
-
- project identity and naming
|
|
39
51
|
- stack and dependency choices
|
|
40
|
-
- architecture and dependency rules
|
|
41
52
|
- test commands and quality gates
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
53
|
+
- architecture and dependency rules
|
|
54
|
+
- optional and conditional regular-doc inventory mismatches
|
|
55
|
+
- API / integration ownership
|
|
56
|
+
- technical guideline ownership, overlap, and missing guideline index coverage
|
|
57
|
+
- semantic legacy-to-owner mapping mismatches where content is relevant but naming differs
|
|
58
|
+
- code-vs-doc authority mismatches where current implementation and active docs disagree
|
|
59
|
+
- workflow expectations
|
|
60
|
+
- roadmap / phase naming
|
|
61
|
+
- project identity and naming
|
|
62
|
+
- undocumented domain knowledge embedded in the codebase (e.g., decision rationale in comments, architectural constraints in configs, domain rules in validation logic) that should be surfaced into project docs
|
|
47
63
|
|
|
48
64
|
## Procedure
|
|
49
65
|
|
|
@@ -51,6 +67,10 @@ Check for disagreement across:
|
|
|
51
67
|
|
|
52
68
|
Use the active EHA rules as the default source of truth for documentation ownership unless the repository explicitly states otherwise.
|
|
53
69
|
|
|
70
|
+
Treat `docs/project-docs/index.md` and `docs/project-docs/technical-guidelines/index.md` as the authoritative inventories for optional regular docs and guideline docs when present.
|
|
71
|
+
|
|
72
|
+
Treat clearly named reference or archive folders such as `docs-legacy/`, `docs-old/`, `archive/`, or `reference/` as migration input only, not as owner-doc paths.
|
|
73
|
+
|
|
54
74
|
### Step 2 — Compare dependent layers
|
|
55
75
|
|
|
56
76
|
Compare the source-of-truth docs against:
|
|
@@ -64,6 +84,8 @@ Compare the source-of-truth docs against:
|
|
|
64
84
|
|
|
65
85
|
If code and docs conflict and the repository does not explicitly define which side is authoritative for that fact, surface the conflict and ask the user before choosing the fix path.
|
|
66
86
|
|
|
87
|
+
When a repo is migrating from another documentation format, use those reference folders to map legacy topics into the correct owner docs under `docs/project-docs/`.
|
|
88
|
+
|
|
67
89
|
### Step 3 — Classify each mismatch
|
|
68
90
|
|
|
69
91
|
Every mismatch should be classified as one of:
|
|
@@ -74,7 +96,20 @@ Every mismatch should be classified as one of:
|
|
|
74
96
|
- historical artifact
|
|
75
97
|
- optional module not active in the current repo
|
|
76
98
|
|
|
77
|
-
|
|
99
|
+
When evaluating legacy material, classify it by the durable concern it governs rather than by its legacy name or path. Treat names such as `epic`, `milestone`, `roadmap`, `protocol`, `procedure`, `policy`, or `standard` as hints only.
|
|
100
|
+
|
|
101
|
+
Report likely mappings when content points to an active owner even if naming differs, such as phased-planning content that should map to `foundation/phases.md` or technical-rule content that should map to `technical-guidelines/`.
|
|
102
|
+
|
|
103
|
+
### Step 4 — Apply structural drift rules
|
|
104
|
+
|
|
105
|
+
Check for these specific structural drift conditions:
|
|
106
|
+
|
|
107
|
+
- A missing `docs/project-docs/index.md` when optional or conditional regular docs exist beyond the always-required core set.
|
|
108
|
+
- A missing `technical-guidelines/index.md` when guideline files exist.
|
|
109
|
+
- Registry entries without matching files and files without matching registry entries, unless the registry explicitly marks them deprecated or archived.
|
|
110
|
+
- A missing recommended guideline only when the repo already claims that domain is covered or the repo claims to be fully documented for that domain.
|
|
111
|
+
|
|
112
|
+
### Step 5 — Assess impact
|
|
78
113
|
|
|
79
114
|
Determine whether the mismatch affects:
|
|
80
115
|
|
|
@@ -85,10 +120,16 @@ Determine whether the mismatch affects:
|
|
|
85
120
|
- release or verification confidence
|
|
86
121
|
- authority certainty between docs and implementation
|
|
87
122
|
|
|
88
|
-
### Step
|
|
123
|
+
### Step 6 — Recommend ownership-level fixes
|
|
89
124
|
|
|
90
125
|
Recommend which owning file or layer should be updated. Do not spread the same fact across more files than necessary.
|
|
91
126
|
|
|
127
|
+
If a legacy artifact could plausibly map to more than one active owner, or if preserving the legacy label may be intentional, ask the user for direction instead of silently classifying it.
|
|
128
|
+
|
|
129
|
+
When asking for that direction, prefer a concise question that names the fact in conflict and the likely choices. Example: `I found a conflict between the code and the docs for the active API behavior. Should I treat 1. the code as correct and update the docs, 2. the docs as correct and treat the code as drift, or 3. mark this as an intentional temporary mismatch?`
|
|
130
|
+
|
|
131
|
+
If strong repository evidence already establishes the authority order for that fact, state that evidence explicitly instead of asking.
|
|
132
|
+
|
|
92
133
|
## Quality Check
|
|
93
134
|
|
|
94
135
|
- Do not confuse reference material with active truth
|
|
@@ -96,6 +137,8 @@ Recommend which owning file or layer should be updated. Do not spread the same f
|
|
|
96
137
|
- Distinguish blocking contradictions from harmless historical leftovers
|
|
97
138
|
- Keep the audit actionable, not just descriptive
|
|
98
139
|
- Do not assume docs or code win when authority for the disputed fact is not explicit
|
|
140
|
+
- Treat missing code evidence the same as missing doc evidence: reduce confidence, state the limitation, and avoid guessing
|
|
141
|
+
- Do not fix anything unless explicitly asked
|
|
99
142
|
|
|
100
143
|
## Anti-Pattern
|
|
101
144
|
|
|
@@ -114,9 +157,10 @@ For each finding, include:
|
|
|
114
157
|
3. source-of-truth location
|
|
115
158
|
4. conflicting location
|
|
116
159
|
5. classification
|
|
117
|
-
6.
|
|
118
|
-
7.
|
|
119
|
-
8.
|
|
160
|
+
6. whether the conflict is doc-vs-doc, code-vs-doc, or registry-vs-file
|
|
161
|
+
7. why it matters
|
|
162
|
+
8. recommended owner to update
|
|
163
|
+
9. whether user direction is required before deciding the fix path
|
|
120
164
|
|
|
121
165
|
End with:
|
|
122
166
|
1. highest-priority drift items
|
package/package.json
CHANGED
package/src/engine/install.js
CHANGED
|
@@ -27,13 +27,40 @@ function resolveAgentId(agentId) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function readManifest(manifestPath) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
const data = readJsonIfExists(manifestPath);
|
|
31
|
+
if (!data) {
|
|
32
|
+
return {
|
|
33
|
+
manifestVersion: 2,
|
|
33
34
|
agent: null,
|
|
35
|
+
agents: [],
|
|
34
36
|
files: [],
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const manifestVersion = data.manifestVersion || 1;
|
|
41
|
+
const agent = data.agent || null;
|
|
42
|
+
let agents = data.agents || [];
|
|
43
|
+
let files = data.files || [];
|
|
44
|
+
|
|
45
|
+
if (manifestVersion === 1 && agent && agents.length === 0) {
|
|
46
|
+
agents = [
|
|
47
|
+
{
|
|
48
|
+
id: agent,
|
|
49
|
+
files: [...files],
|
|
50
|
+
updatedAt: data.updatedAt || new Date().toISOString(),
|
|
51
|
+
packageVersion: data.packageVersion || EHA_PACKAGE_VERSION,
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
manifestVersion: 2,
|
|
58
|
+
agent,
|
|
59
|
+
agents,
|
|
60
|
+
files,
|
|
61
|
+
updatedAt: data.updatedAt,
|
|
62
|
+
packageVersion: data.packageVersion,
|
|
63
|
+
};
|
|
37
64
|
}
|
|
38
65
|
|
|
39
66
|
function initProject({ rootDir, agentId }) {
|
|
@@ -50,41 +77,134 @@ function initProject({ rootDir, agentId }) {
|
|
|
50
77
|
}
|
|
51
78
|
|
|
52
79
|
const enginePaths = getEnginePaths(rootDir);
|
|
80
|
+
const existingManifest = readManifest(enginePaths.manifestPath);
|
|
81
|
+
const existingAgents = existingManifest.agents || [];
|
|
82
|
+
|
|
83
|
+
const agentEntry = {
|
|
84
|
+
id: normalizedAgentId,
|
|
85
|
+
files: files.map((f) => f.relativePath),
|
|
86
|
+
updatedAt: new Date().toISOString(),
|
|
87
|
+
packageVersion: EHA_PACKAGE_VERSION,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const updatedAgents = existingAgents.filter((a) => a.id !== normalizedAgentId);
|
|
91
|
+
updatedAgents.push(agentEntry);
|
|
92
|
+
|
|
93
|
+
const allFiles = new Set();
|
|
94
|
+
for (const agent of updatedAgents) {
|
|
95
|
+
for (const f of agent.files || []) {
|
|
96
|
+
allFiles.add(f);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
53
100
|
const manifest = {
|
|
54
|
-
manifestVersion:
|
|
101
|
+
manifestVersion: 2,
|
|
55
102
|
agent: normalizedAgentId,
|
|
56
|
-
|
|
103
|
+
agents: updatedAgents,
|
|
104
|
+
files: [...allFiles],
|
|
57
105
|
updatedAt: new Date().toISOString(),
|
|
58
106
|
packageVersion: EHA_PACKAGE_VERSION,
|
|
59
107
|
};
|
|
60
108
|
writeJson(enginePaths.manifestPath, manifest);
|
|
61
|
-
|
|
109
|
+
|
|
110
|
+
const existingConfig = readConfig(rootDir);
|
|
111
|
+
const allAgents = [...new Set([...(existingConfig.agents || []), normalizedAgentId])];
|
|
112
|
+
const config = writeConfig(rootDir, { agent: normalizedAgentId, agents: allAgents });
|
|
62
113
|
|
|
63
114
|
return {
|
|
64
115
|
rootDir,
|
|
65
116
|
agentId: normalizedAgentId,
|
|
66
117
|
config,
|
|
67
|
-
files:
|
|
118
|
+
files: files.map((f) => f.relativePath),
|
|
68
119
|
fileCount: files.length,
|
|
69
120
|
};
|
|
70
121
|
}
|
|
71
122
|
|
|
72
|
-
function removeProject({ rootDir }) {
|
|
123
|
+
function removeProject({ rootDir, agentId = null }) {
|
|
73
124
|
const enginePaths = getEnginePaths(rootDir);
|
|
74
125
|
const manifest = readManifest(enginePaths.manifestPath);
|
|
75
126
|
const removedFiles = [];
|
|
76
127
|
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
128
|
+
if (agentId) {
|
|
129
|
+
const normalizedAgentId = resolveAgentId(agentId);
|
|
130
|
+
const agentEntry = manifest.agents.find((a) => a.id === normalizedAgentId);
|
|
131
|
+
if (!agentEntry) {
|
|
132
|
+
return { rootDir, removedFiles };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const otherAgents = manifest.agents.filter((a) => a.id !== normalizedAgentId);
|
|
136
|
+
const otherFiles = new Set();
|
|
137
|
+
for (const other of otherAgents) {
|
|
138
|
+
for (const f of other.files || []) {
|
|
139
|
+
otherFiles.add(f);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const filesToRemove = (agentEntry.files || []).filter((f) => !otherFiles.has(f));
|
|
83
144
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
145
|
+
for (const relativePath of filesToRemove) {
|
|
146
|
+
const absolutePath = path.join(rootDir, relativePath);
|
|
147
|
+
removeFileIfExists(absolutePath);
|
|
148
|
+
removeEmptyParents(path.dirname(absolutePath), rootDir);
|
|
149
|
+
removedFiles.push(relativePath);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const remainingAgents = otherAgents;
|
|
153
|
+
if (remainingAgents.length === 0) {
|
|
154
|
+
removeFileIfExists(enginePaths.manifestPath);
|
|
155
|
+
removeEmptyParents(path.dirname(enginePaths.manifestPath), rootDir);
|
|
156
|
+
removeFileIfExists(enginePaths.configPath);
|
|
157
|
+
removeEmptyParents(path.dirname(enginePaths.configPath), rootDir);
|
|
158
|
+
} else {
|
|
159
|
+
const lastAgent = remainingAgents[remainingAgents.length - 1].id;
|
|
160
|
+
const allFiles = new Set();
|
|
161
|
+
for (const other of remainingAgents) {
|
|
162
|
+
for (const f of other.files || []) {
|
|
163
|
+
allFiles.add(f);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const updatedManifest = {
|
|
168
|
+
manifestVersion: 2,
|
|
169
|
+
agent: lastAgent,
|
|
170
|
+
agents: remainingAgents,
|
|
171
|
+
files: [...allFiles],
|
|
172
|
+
updatedAt: new Date().toISOString(),
|
|
173
|
+
packageVersion: EHA_PACKAGE_VERSION,
|
|
174
|
+
};
|
|
175
|
+
writeJson(enginePaths.manifestPath, updatedManifest);
|
|
176
|
+
|
|
177
|
+
writeConfig(rootDir, {
|
|
178
|
+
agent: lastAgent,
|
|
179
|
+
agents: remainingAgents.map((a) => a.id),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
const allFiles = new Set();
|
|
184
|
+
if (manifest.agents && manifest.agents.length > 0) {
|
|
185
|
+
for (const agentEntry of manifest.agents) {
|
|
186
|
+
for (const f of agentEntry.files || []) {
|
|
187
|
+
allFiles.add(f);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
for (const f of manifest.files || []) {
|
|
192
|
+
allFiles.add(f);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const relativePath of allFiles) {
|
|
197
|
+
const absolutePath = path.join(rootDir, relativePath);
|
|
198
|
+
removeFileIfExists(absolutePath);
|
|
199
|
+
removeEmptyParents(path.dirname(absolutePath), rootDir);
|
|
200
|
+
removedFiles.push(relativePath);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
removeFileIfExists(enginePaths.manifestPath);
|
|
204
|
+
removeEmptyParents(path.dirname(enginePaths.manifestPath), rootDir);
|
|
205
|
+
removeFileIfExists(enginePaths.configPath);
|
|
206
|
+
removeEmptyParents(path.dirname(enginePaths.configPath), rootDir);
|
|
207
|
+
}
|
|
88
208
|
|
|
89
209
|
return { rootDir, removedFiles };
|
|
90
210
|
}
|
|
@@ -215,7 +215,7 @@ const RUNTIME_ADAPTERS = {
|
|
|
215
215
|
}
|
|
216
216
|
files.push({
|
|
217
217
|
relativePath: path.join('.claude', 'commands', 'eha', 'README.md'),
|
|
218
|
-
content: `# EHA Claude commands\n\nGenerated by \`eha init\`. Use \`/eha-bootstrap\`, \`/eha-refresh\`, \`/
|
|
218
|
+
content: `# EHA Claude commands\n\nGenerated by \`eha init\`. Use \`/eha-bootstrap\`, \`/eha-refresh\`, \`/sdd-discuss\`, or \`/sdd-execute\` in Claude.\n`,
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
for (const skill of skills) {
|
|
@@ -268,7 +268,7 @@ const RUNTIME_ADAPTERS = {
|
|
|
268
268
|
antigravity: {
|
|
269
269
|
id: 'antigravity',
|
|
270
270
|
name: 'Antigravity',
|
|
271
|
-
description: 'Generates Antigravity-compatible skills in .agents/skills/',
|
|
271
|
+
description: 'Generates Antigravity-compatible skills in .agents/skills/ and rules in .agents/rules/',
|
|
272
272
|
generateFiles(rootDir, workflows, skills) {
|
|
273
273
|
const files = [];
|
|
274
274
|
for (const workflow of workflows) {
|
|
@@ -290,7 +290,7 @@ const RUNTIME_ADAPTERS = {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
files.push({
|
|
293
|
-
relativePath: path.join('.agents', '
|
|
293
|
+
relativePath: path.join('.agents', 'rules', 'eha-agent-rules.md'),
|
|
294
294
|
content: buildAntigravityRuleFile(),
|
|
295
295
|
});
|
|
296
296
|
|
package/src/engine/state.js
CHANGED
|
@@ -3,8 +3,9 @@ const path = require('node:path');
|
|
|
3
3
|
|
|
4
4
|
const ROOT_MARKERS = ['package.json', '.git'];
|
|
5
5
|
const DEFAULT_CONFIG = {
|
|
6
|
-
configVersion:
|
|
6
|
+
configVersion: 2,
|
|
7
7
|
agent: null,
|
|
8
|
+
agents: [],
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
function ensureDir(dirPath) {
|
|
@@ -72,9 +73,15 @@ function getEnginePaths(rootDir) {
|
|
|
72
73
|
|
|
73
74
|
function normalizeConfig(config) {
|
|
74
75
|
const candidate = config && typeof config === 'object' ? config : {};
|
|
76
|
+
const agent = candidate.agent ? String(candidate.agent).trim().toLowerCase() : null;
|
|
77
|
+
let agents = Array.isArray(candidate.agents) ? candidate.agents : [];
|
|
78
|
+
if (agent && agents.length === 0) {
|
|
79
|
+
agents = [agent];
|
|
80
|
+
}
|
|
75
81
|
return {
|
|
76
|
-
configVersion:
|
|
77
|
-
agent
|
|
82
|
+
configVersion: 2,
|
|
83
|
+
agent,
|
|
84
|
+
agents: [...new Set(agents)],
|
|
78
85
|
};
|
|
79
86
|
}
|
|
80
87
|
|
|
@@ -13,12 +13,6 @@ const WORKFLOW_DEFINITIONS = {
|
|
|
13
13
|
description: 'Refresh project docs after a change in scope, stack, or behavior',
|
|
14
14
|
repoRelativePath: path.join('docs', 'templates', 'reusable-prompts', '00-project-docs-refresh.md'),
|
|
15
15
|
},
|
|
16
|
-
parity: {
|
|
17
|
-
id: 'parity',
|
|
18
|
-
commandName: 'parity',
|
|
19
|
-
description: 'Audit for documentation drift and ownership mismatches',
|
|
20
|
-
repoRelativePath: path.join('docs', 'templates', 'reusable-prompts', '00-project-docs-parity.md'),
|
|
21
|
-
},
|
|
22
16
|
discuss: {
|
|
23
17
|
id: 'discuss',
|
|
24
18
|
commandName: 'discuss',
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# Project Docs Parity Reusable Prompt
|
|
2
|
-
|
|
3
|
-
Audit the repository for **documentation-system drift**.
|
|
4
|
-
|
|
5
|
-
## Goal
|
|
6
|
-
|
|
7
|
-
Find mismatches where project docs, platform instruction surfaces (mirrored rule files), skills, reusable prompts, workflow docs, quick-reference material, or relevant implementation evidence disagree about the same fact.
|
|
8
|
-
|
|
9
|
-
## Scope
|
|
10
|
-
|
|
11
|
-
Check at least these areas when present:
|
|
12
|
-
|
|
13
|
-
- `docs/project-docs/`
|
|
14
|
-
- `docs/project-docs/index.md`
|
|
15
|
-
- `docs/project-docs/technical-guidelines/`
|
|
16
|
-
- relevant code, tests, configs, or runtime-facing artifacts when a finding depends on current implementation behavior or source-of-truth ownership
|
|
17
|
-
- clearly named reference or archive folders such as `docs-legacy/`, `docs-old/`, `archive/`, or `reference/`
|
|
18
|
-
- platform instruction surfaces (mirrored rule files)
|
|
19
|
-
- `docs/templates/skills/`
|
|
20
|
-
- `docs/templates/reusable-prompts/`
|
|
21
|
-
- workflow docs and handoff docs
|
|
22
|
-
|
|
23
|
-
### High-Value Drift Categories
|
|
24
|
-
|
|
25
|
-
- stack and dependency choices
|
|
26
|
-
- test commands and quality gates
|
|
27
|
-
- architecture and dependency rules
|
|
28
|
-
- optional and conditional regular-doc inventory mismatches
|
|
29
|
-
- API / integration ownership
|
|
30
|
-
- technical guideline ownership, overlap, and missing guideline index coverage
|
|
31
|
-
- semantic legacy-to-owner mapping mismatches where content is relevant but naming differs
|
|
32
|
-
- code-vs-doc authority mismatches where current implementation and active docs disagree
|
|
33
|
-
- workflow expectations
|
|
34
|
-
- roadmap / phase naming
|
|
35
|
-
- project identity and naming
|
|
36
|
-
- undocumented domain knowledge embedded in the codebase (e.g., decision rationale in comments, architectural constraints in configs, domain rules in validation logic) that should be surfaced into project docs
|
|
37
|
-
|
|
38
|
-
## Required Behavior
|
|
39
|
-
|
|
40
|
-
1. Use project docs as the primary source of truth for documentation ownership and doc-to-doc drift unless the repository explicitly states otherwise.
|
|
41
|
-
2. Use the EHA Project Doc Rules above as the ownership map.
|
|
42
|
-
3. Treat `docs/project-docs/index.md` and `docs/project-docs/technical-guidelines/index.md` as the authoritative inventories for optional regular docs and guideline docs when present.
|
|
43
|
-
4. Treat clearly named reference or archive folders such as `docs-legacy/`, `docs-old/`, `archive/`, or `reference/` as migration input only, not as owner-doc paths.
|
|
44
|
-
5. When evaluating legacy material, classify it by the durable concern it governs rather than by its legacy name or path. Treat names such as `epic`, `milestone`, `roadmap`, `protocol`, `procedure`, `policy`, or `standard` as hints only.
|
|
45
|
-
6. Report likely mappings when content points to an active owner even if naming differs, such as phased-planning content that should map to `foundation/phases.md` or technical-rule content that should map to `technical-guidelines/`.
|
|
46
|
-
7. Distinguish between:
|
|
47
|
-
- true contradiction
|
|
48
|
-
- stale summary
|
|
49
|
-
- historical artifact
|
|
50
|
-
- optional module not currently active
|
|
51
|
-
8. When a repo is migrating from another documentation format, use those reference folders to map legacy topics into the correct owner docs under `docs/project-docs/`.
|
|
52
|
-
9. Treat a missing `docs/project-docs/index.md` as drift when optional or conditional regular docs exist beyond the always-required core set.
|
|
53
|
-
10. Treat a missing `technical-guidelines/index.md` as drift when guideline files exist.
|
|
54
|
-
11. Treat registry entries without matching files and files without matching registry entries as drift unless the registry explicitly marks them deprecated or archived.
|
|
55
|
-
12. Treat a missing recommended guideline as drift only when the repo already claims that domain is covered or the repo claims to be fully documented for that domain.
|
|
56
|
-
13. When a finding depends on whether the implementation or the documentation is authoritative, inspect the relevant code, tests, configs, or runtime-facing artifacts before classifying the issue.
|
|
57
|
-
14. If code and documentation conflict and the repository does not explicitly declare which source is authoritative for that fact, do not assume. Report the conflict and ask the user for direction before recommending a fix path.
|
|
58
|
-
15. If a legacy artifact could plausibly map to more than one active owner, or if preserving the legacy label may be intentional, ask the user for direction instead of silently classifying it.
|
|
59
|
-
16. When asking for that direction, prefer a concise question that names the fact in conflict and the likely choices. Example: `I found a conflict between the code and the docs for the active API behavior. Should I treat 1. the code as correct and update the docs, 2. the docs as correct and treat the code as drift, or 3. mark this as an intentional temporary mismatch?`
|
|
60
|
-
17. If strong repository evidence already establishes the authority order for that fact, state that evidence explicitly instead of asking.
|
|
61
|
-
18. Treat missing code evidence the same as missing doc evidence: reduce confidence, state the limitation, and avoid guessing.
|
|
62
|
-
19. Do not fix anything unless explicitly asked.
|
|
63
|
-
|
|
64
|
-
## Output Contract
|
|
65
|
-
|
|
66
|
-
For each finding, include:
|
|
67
|
-
|
|
68
|
-
- severity
|
|
69
|
-
- fact in conflict
|
|
70
|
-
- source-of-truth file
|
|
71
|
-
- conflicting file
|
|
72
|
-
- whether the conflict is doc-vs-doc, code-vs-doc, or registry-vs-file
|
|
73
|
-
- why it matters
|
|
74
|
-
- recommended owner to update
|
|
75
|
-
- whether user direction is required before deciding the fix path
|
|
76
|
-
|
|
77
|
-
## Final Pass
|
|
78
|
-
|
|
79
|
-
End with:
|
|
80
|
-
|
|
81
|
-
1. a short list of highest-priority drift items
|
|
82
|
-
2. a short list of acceptable historical artifacts that should not be treated as blockers
|
|
83
|
-
|
|
84
|
-
## Inputs
|
|
85
|
-
|
|
86
|
-
Use the current repository docs, platform instruction surfaces, skills, reusable prompts, and any relevant workflow or summary artifacts available below.
|