@limina-labs/momentum 0.34.0 → 0.36.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/adapters/antigravity/instructions/AGENTS.md +12 -5
- package/adapters/antigravity/instructions/manifest.json +8 -0
- package/adapters/claude-code/instructions/manifest.json +8 -0
- package/adapters/codex/instructions/AGENTS.md +13 -6
- package/adapters/codex/instructions/manifest.json +8 -0
- package/adapters/codex/instructions/surfaces.md +1 -1
- package/adapters/opencode/instructions/AGENTS.md +13 -6
- package/adapters/opencode/instructions/manifest.json +8 -0
- package/adapters/opencode/instructions/surfaces.md +1 -1
- package/bin/ecosystem.js +20 -9
- package/bin/momentum.js +99 -17
- package/bin/state-commands.js +3 -4
- package/core/commands/complete-phase.md +4 -4
- package/core/commands/start-phase.md +2 -2
- package/core/commands/start-project.md +3 -4
- package/core/commands/validate.md +3 -0
- package/core/ecosystem/lib/pointer.js +42 -0
- package/core/instructions/README.md +69 -0
- package/core/instructions/rules-body.md +2 -2
- package/core/lib/instruction-compose.js +175 -0
- package/core/lib/okf-types.js +1 -0
- package/core/lib/project-rules.js +167 -0
- package/core/specs-templates/CLAUDE.md +13 -6
- package/package.json +1 -1
- package/adapters/antigravity/instructions/header.md +0 -3
- package/adapters/antigravity/instructions/vars.json +0 -1
- package/adapters/claude-code/instructions/header.md +0 -3
- package/adapters/claude-code/instructions/vars.json +0 -1
- package/adapters/codex/instructions/header.md +0 -3
- package/adapters/codex/instructions/vars.json +0 -1
- package/adapters/opencode/instructions/header.md +0 -3
- package/adapters/opencode/instructions/vars.json +0 -1
|
@@ -176,6 +176,10 @@ lives in this AGENTS.md primary-instruction text as a belt-and-braces
|
|
|
176
176
|
fallback: if a `.momentum/inbox/handoff-NNN.md` file exists at session
|
|
177
177
|
start, read it and acknowledge before continuing.
|
|
178
178
|
|
|
179
|
+
## In-Session Task Tool
|
|
180
|
+
|
|
181
|
+
When a rule mentions tracking in-session progress (Rule 2), Antigravity's task tool is the native **task list** artifact (task.md) — in-session only; the durable record is `specs/phases/<phase>/tasks.md`.
|
|
182
|
+
|
|
179
183
|
## Autonomous Behaviors (Always-On Rules)
|
|
180
184
|
|
|
181
185
|
### Rule 1: Always Orient First
|
|
@@ -197,7 +201,7 @@ After completing ANY meaningful work, automatically update:
|
|
|
197
201
|
2. **`specs/status.md`** — if phase progress, blockers, or P0 items changed (touch only your own lane's row — Rule 15)
|
|
198
202
|
3. **`specs/changelog/YYYY-MM.md`** — log what changed (one line per change, append-only)
|
|
199
203
|
|
|
200
|
-
Use
|
|
204
|
+
Use your in-session task-tracking tool (named in the "In-Session Task Tool" note above) to track in-session task progress. Do NOT wait for the user to ask you to update tracking.
|
|
201
205
|
|
|
202
206
|
#### Why
|
|
203
207
|
Tracking debt compounds invisibly. A task list one day stale is recoverable; one week stale is fiction. Status drift is how phases silently lose direction.
|
|
@@ -215,7 +219,7 @@ Tracking debt compounds invisibly. A task list one day stale is recoverable; one
|
|
|
215
219
|
#### Anti-Rationalization Counters
|
|
216
220
|
|
|
217
221
|
- "It's faster to do the work first and track at the end" — wrong: reconstruction takes 2-3× longer than real-time logging.
|
|
218
|
-
- "
|
|
222
|
+
- "Your in-session task-tracking tool is enough" — it is in-session only; `tasks.md` is the durable record.
|
|
219
223
|
- "Mid-task tracking interrupts flow" — a one-line update costs <30s; reconstructing a day later costs 30 minutes.
|
|
220
224
|
|
|
221
225
|
### Rule 3: Auto-Track Discoveries
|
|
@@ -638,6 +642,9 @@ Use `infra:` for CI, build, deploy, tooling, and release-pipeline changes that d
|
|
|
638
642
|
|
|
639
643
|
## Project Extensions
|
|
640
644
|
|
|
641
|
-
|
|
642
|
-
>
|
|
643
|
-
>
|
|
645
|
+
<!-- momentum:project-rules-pointer -->
|
|
646
|
+
> **Project-specific rules live in `specs/project-rules.md`** — read it now.
|
|
647
|
+
> Session-start self-audits, project constraints, and any project-specific
|
|
648
|
+
> guidance are there, shared identically by every agent (ADR-0010). This
|
|
649
|
+
> section is a momentum-managed pointer; edit `specs/project-rules.md`, not
|
|
650
|
+
> this file.
|
|
@@ -48,7 +48,7 @@ The shipped recipe set (one skill per recipe, all under
|
|
|
48
48
|
| log | Append a manual narrative entry to the history of the phase bound to your branch (fallback: status.md) |
|
|
49
49
|
| migrate | Onboard an existing project into momentum |
|
|
50
50
|
| validate | Check spec structure health |
|
|
51
|
-
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062)
|
|
51
|
+
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062) |
|
|
52
52
|
| ecosystem | Cross-repo ecosystem coordination |
|
|
53
53
|
| initiative | Manage cross-repo initiatives |
|
|
54
54
|
| session | Append a manual narrative entry to today's ecosystem session log |
|
|
@@ -230,6 +230,10 @@ This is the documented escalation point referenced in Phase 18 G1
|
|
|
230
230
|
plan.md; if the MCP shim is widely broken, momentum will ship a
|
|
231
231
|
minimal `core/swarm/mcp-cwd-server.js` in a follow-up.
|
|
232
232
|
|
|
233
|
+
## In-Session Task Tool
|
|
234
|
+
|
|
235
|
+
When a rule mentions tracking in-session progress (Rule 2), Codex's task tool is the built-in **plan** tool — in-session only; the durable record is `specs/phases/<phase>/tasks.md`.
|
|
236
|
+
|
|
233
237
|
## Autonomous Behaviors (Always-On Rules)
|
|
234
238
|
|
|
235
239
|
### Rule 1: Always Orient First
|
|
@@ -251,7 +255,7 @@ After completing ANY meaningful work, automatically update:
|
|
|
251
255
|
2. **`specs/status.md`** — if phase progress, blockers, or P0 items changed (touch only your own lane's row — Rule 15)
|
|
252
256
|
3. **`specs/changelog/YYYY-MM.md`** — log what changed (one line per change, append-only)
|
|
253
257
|
|
|
254
|
-
Use
|
|
258
|
+
Use your in-session task-tracking tool (named in the "In-Session Task Tool" note above) to track in-session task progress. Do NOT wait for the user to ask you to update tracking.
|
|
255
259
|
|
|
256
260
|
#### Why
|
|
257
261
|
Tracking debt compounds invisibly. A task list one day stale is recoverable; one week stale is fiction. Status drift is how phases silently lose direction.
|
|
@@ -269,7 +273,7 @@ Tracking debt compounds invisibly. A task list one day stale is recoverable; one
|
|
|
269
273
|
#### Anti-Rationalization Counters
|
|
270
274
|
|
|
271
275
|
- "It's faster to do the work first and track at the end" — wrong: reconstruction takes 2-3× longer than real-time logging.
|
|
272
|
-
- "
|
|
276
|
+
- "Your in-session task-tracking tool is enough" — it is in-session only; `tasks.md` is the durable record.
|
|
273
277
|
- "Mid-task tracking interrupts flow" — a one-line update costs <30s; reconstructing a day later costs 30 minutes.
|
|
274
278
|
|
|
275
279
|
### Rule 3: Auto-Track Discoveries
|
|
@@ -692,6 +696,9 @@ Use `infra:` for CI, build, deploy, tooling, and release-pipeline changes that d
|
|
|
692
696
|
|
|
693
697
|
## Project Extensions
|
|
694
698
|
|
|
695
|
-
|
|
696
|
-
>
|
|
697
|
-
>
|
|
699
|
+
<!-- momentum:project-rules-pointer -->
|
|
700
|
+
> **Project-specific rules live in `specs/project-rules.md`** — read it now.
|
|
701
|
+
> Session-start self-audits, project constraints, and any project-specific
|
|
702
|
+
> guidance are there, shared identically by every agent (ADR-0010). This
|
|
703
|
+
> section is a momentum-managed pointer; edit `specs/project-rules.md`, not
|
|
704
|
+
> this file.
|
|
@@ -24,7 +24,7 @@ The shipped recipe set (one skill per recipe, all under
|
|
|
24
24
|
| log | Append a manual narrative entry to the history of the phase bound to your branch (fallback: status.md) |
|
|
25
25
|
| migrate | Onboard an existing project into momentum |
|
|
26
26
|
| validate | Check spec structure health |
|
|
27
|
-
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062)
|
|
27
|
+
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062) |
|
|
28
28
|
| ecosystem | Cross-repo ecosystem coordination |
|
|
29
29
|
| initiative | Manage cross-repo initiatives |
|
|
30
30
|
| session | Append a manual narrative entry to today's ecosystem session log |
|
|
@@ -48,7 +48,7 @@ The shipped recipe set (one command per recipe):
|
|
|
48
48
|
| lanes | Work with lanes — concurrent workstreams in one repo (Rule 15) |
|
|
49
49
|
| migrate | Onboard an existing project into momentum |
|
|
50
50
|
| validate | Check spec structure health |
|
|
51
|
-
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062)
|
|
51
|
+
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062) |
|
|
52
52
|
| ecosystem | Cross-repo ecosystem coordination |
|
|
53
53
|
| initiative | Manage cross-repo initiatives |
|
|
54
54
|
| session | Append a manual narrative entry to today's ecosystem session log |
|
|
@@ -143,6 +143,10 @@ skill paths (`.claude/skills/`, `.agents/skills/`); momentum's opencode
|
|
|
143
143
|
surfaces live under `.opencode/` so multi-adapter installs stay
|
|
144
144
|
side-by-side without collisions.
|
|
145
145
|
|
|
146
|
+
## In-Session Task Tool
|
|
147
|
+
|
|
148
|
+
When a rule mentions tracking in-session progress (Rule 2), opencode's task tool is the built-in **task** tool (subagent fan-out) — in-session only; the durable record is `specs/phases/<phase>/tasks.md`.
|
|
149
|
+
|
|
146
150
|
## Autonomous Behaviors (Always-On Rules)
|
|
147
151
|
|
|
148
152
|
### Rule 1: Always Orient First
|
|
@@ -164,7 +168,7 @@ After completing ANY meaningful work, automatically update:
|
|
|
164
168
|
2. **`specs/status.md`** — if phase progress, blockers, or P0 items changed (touch only your own lane's row — Rule 15)
|
|
165
169
|
3. **`specs/changelog/YYYY-MM.md`** — log what changed (one line per change, append-only)
|
|
166
170
|
|
|
167
|
-
Use
|
|
171
|
+
Use your in-session task-tracking tool (named in the "In-Session Task Tool" note above) to track in-session task progress. Do NOT wait for the user to ask you to update tracking.
|
|
168
172
|
|
|
169
173
|
#### Why
|
|
170
174
|
Tracking debt compounds invisibly. A task list one day stale is recoverable; one week stale is fiction. Status drift is how phases silently lose direction.
|
|
@@ -182,7 +186,7 @@ Tracking debt compounds invisibly. A task list one day stale is recoverable; one
|
|
|
182
186
|
#### Anti-Rationalization Counters
|
|
183
187
|
|
|
184
188
|
- "It's faster to do the work first and track at the end" — wrong: reconstruction takes 2-3× longer than real-time logging.
|
|
185
|
-
- "
|
|
189
|
+
- "Your in-session task-tracking tool is enough" — it is in-session only; `tasks.md` is the durable record.
|
|
186
190
|
- "Mid-task tracking interrupts flow" — a one-line update costs <30s; reconstructing a day later costs 30 minutes.
|
|
187
191
|
|
|
188
192
|
### Rule 3: Auto-Track Discoveries
|
|
@@ -605,6 +609,9 @@ Use `infra:` for CI, build, deploy, tooling, and release-pipeline changes that d
|
|
|
605
609
|
|
|
606
610
|
## Project Extensions
|
|
607
611
|
|
|
608
|
-
|
|
609
|
-
>
|
|
610
|
-
>
|
|
612
|
+
<!-- momentum:project-rules-pointer -->
|
|
613
|
+
> **Project-specific rules live in `specs/project-rules.md`** — read it now.
|
|
614
|
+
> Session-start self-audits, project constraints, and any project-specific
|
|
615
|
+
> guidance are there, shared identically by every agent (ADR-0010). This
|
|
616
|
+
> section is a momentum-managed pointer; edit `specs/project-rules.md`, not
|
|
617
|
+
> this file.
|
|
@@ -24,7 +24,7 @@ The shipped recipe set (one command per recipe):
|
|
|
24
24
|
| lanes | Work with lanes — concurrent workstreams in one repo (Rule 15) |
|
|
25
25
|
| migrate | Onboard an existing project into momentum |
|
|
26
26
|
| validate | Check spec structure health |
|
|
27
|
-
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062)
|
|
27
|
+
| sync-config | Re-infer project shape, show config drift, apply on approval (ENH-062) |
|
|
28
28
|
| ecosystem | Cross-repo ecosystem coordination |
|
|
29
29
|
| initiative | Manage cross-repo initiatives |
|
|
30
30
|
| session | Append a manual narrative entry to today's ecosystem session log |
|
package/bin/ecosystem.js
CHANGED
|
@@ -24,8 +24,11 @@ const {
|
|
|
24
24
|
POINTER_END,
|
|
25
25
|
PRIMARY_INSTRUCTION_CANDIDATES,
|
|
26
26
|
findPrimaryInstructionFile,
|
|
27
|
+
findAllInstructionFiles,
|
|
27
28
|
ensurePointerInjected,
|
|
29
|
+
ensurePointerInjectedAll,
|
|
28
30
|
stripPointer,
|
|
31
|
+
stripPointerAll,
|
|
29
32
|
} = require('../core/ecosystem/lib/pointer');
|
|
30
33
|
|
|
31
34
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -616,7 +619,7 @@ function cmdAdd(args) {
|
|
|
616
619
|
// Idempotent: if the path matches, do nothing. If it differs, fail loud.
|
|
617
620
|
if (existing.path === relPath) {
|
|
618
621
|
console.log(`add: "${id}" is already registered (path ${relPath}). No changes.`);
|
|
619
|
-
|
|
622
|
+
ensurePointerInjectedAll(absRepo, root, manifest.name);
|
|
620
623
|
return;
|
|
621
624
|
}
|
|
622
625
|
throw new Error(
|
|
@@ -641,11 +644,12 @@ function cmdAdd(args) {
|
|
|
641
644
|
'utf8',
|
|
642
645
|
);
|
|
643
646
|
|
|
644
|
-
// Inject the fenced pointer into
|
|
645
|
-
|
|
647
|
+
// Inject the fenced pointer into EVERY instruction file (Phase 28 — every
|
|
648
|
+
// agent, not just the preferred one).
|
|
649
|
+
const injected = ensurePointerInjectedAll(absRepo, root, manifest.name);
|
|
646
650
|
|
|
647
651
|
console.log(`Added member "${id}" (${role}) at ${relPath}.`);
|
|
648
|
-
console.log(`Pointer injected into ${path.relative(root, path.join(absRepo,
|
|
652
|
+
console.log(`Pointer injected into ${injected.map((f) => path.relative(root, path.join(absRepo, f))).join(', ')}.`);
|
|
649
653
|
}
|
|
650
654
|
|
|
651
655
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -686,10 +690,7 @@ function cmdRemove(args) {
|
|
|
686
690
|
// remove must succeed even when the member repo is gone.
|
|
687
691
|
const absRepo = path.resolve(root, member.path);
|
|
688
692
|
if (fs.existsSync(absRepo)) {
|
|
689
|
-
|
|
690
|
-
if (primary) {
|
|
691
|
-
stripPointer(path.join(absRepo, primary));
|
|
692
|
-
}
|
|
693
|
+
stripPointerAll(absRepo); // Phase 28 — strip from every instruction file
|
|
693
694
|
}
|
|
694
695
|
|
|
695
696
|
console.log(`Removed member "${id}".`);
|
|
@@ -776,9 +777,18 @@ function detectMemberAgent(repoPath) {
|
|
|
776
777
|
const m = JSON.parse(
|
|
777
778
|
fs.readFileSync(path.join(repoPath, '.momentum', 'installed.json'), 'utf8'),
|
|
778
779
|
);
|
|
779
|
-
|
|
780
|
+
// ADR-0007: the lock is `{ agents: { <id>: … } }`; the pre-0007 lock was
|
|
781
|
+
// `{ agent }`. Read the map first (else every AGENTS.md agent — opencode,
|
|
782
|
+
// Antigravity, Codex — falls through to the `AGENTS.md ⇒ codex` heuristic
|
|
783
|
+
// below and is misidentified). ADR-0011.
|
|
784
|
+
if (m && m.agents && typeof m.agents === 'object') {
|
|
785
|
+
const ids = Object.keys(m.agents);
|
|
786
|
+
if (ids.length) return ids.includes('claude-code') ? 'claude-code' : ids.sort()[0];
|
|
787
|
+
}
|
|
788
|
+
if (m && m.agent) return m.agent; // legacy single-agent lock
|
|
780
789
|
} catch (_e) { /* fall through to heuristic */ }
|
|
781
790
|
if (fs.existsSync(path.join(repoPath, '.codex'))) return 'codex';
|
|
791
|
+
if (fs.existsSync(path.join(repoPath, '.opencode'))) return 'opencode';
|
|
782
792
|
if (fs.existsSync(path.join(repoPath, '.claude'))) return 'claude-code';
|
|
783
793
|
if (
|
|
784
794
|
fs.existsSync(path.join(repoPath, '.agents', 'hooks.json')) ||
|
|
@@ -1296,6 +1306,7 @@ module.exports = {
|
|
|
1296
1306
|
ensurePointerInjected,
|
|
1297
1307
|
stripPointer,
|
|
1298
1308
|
findPrimaryInstructionFile,
|
|
1309
|
+
detectMemberAgent,
|
|
1299
1310
|
sanitizeId,
|
|
1300
1311
|
POINTER_BEGIN,
|
|
1301
1312
|
POINTER_END,
|
package/bin/momentum.js
CHANGED
|
@@ -719,7 +719,46 @@ function resolveAdapterSource(srcRoot, adapterDir, fileSpec) {
|
|
|
719
719
|
return path.join(base, ...fileSpec.source);
|
|
720
720
|
}
|
|
721
721
|
|
|
722
|
-
|
|
722
|
+
/**
|
|
723
|
+
* ADR-0011 — the raw primary-instruction template content for `agent`.
|
|
724
|
+
* A claude-md agent (and a lone installed agents-md agent) gets the static
|
|
725
|
+
* committed template. When MORE THAN ONE agents-md agent is installed, AGENTS.md
|
|
726
|
+
* is COMPOSED — the neutral spine once + one integration section per installed
|
|
727
|
+
* AGENTS.md agent — so switching or running several agents never drops an
|
|
728
|
+
* integration (fixes the same-path collision). Project name is not yet rendered.
|
|
729
|
+
*/
|
|
730
|
+
function resolvePrimaryInstructionContent(srcRoot, targetDir, agent, srcPath) {
|
|
731
|
+
const readStatic = () => fs.readFileSync(srcPath, 'utf8');
|
|
732
|
+
let compose;
|
|
733
|
+
try {
|
|
734
|
+
compose = require('../core/lib/instruction-compose');
|
|
735
|
+
} catch (_e) {
|
|
736
|
+
return readStatic();
|
|
737
|
+
}
|
|
738
|
+
let manifest;
|
|
739
|
+
try {
|
|
740
|
+
manifest = compose.readManifest(srcRoot, agent);
|
|
741
|
+
} catch (_e) {
|
|
742
|
+
return readStatic(); // unknown / manifest-less agent → static template
|
|
743
|
+
}
|
|
744
|
+
if (manifest.surface !== 'agents-md') return readStatic(); // claude-md → static
|
|
745
|
+
|
|
746
|
+
// The set of installed AGENTS.md agents ∪ the one being (re)installed.
|
|
747
|
+
const valid = new Set(compose.discoverAgents(srcRoot));
|
|
748
|
+
const ids = new Set(valid.has(agent) ? [agent] : []);
|
|
749
|
+
const state = loadInstalledState(targetDir);
|
|
750
|
+
for (const id of Object.keys(state.agents || {})) {
|
|
751
|
+
if (!valid.has(id)) continue;
|
|
752
|
+
try {
|
|
753
|
+
if (compose.readManifest(srcRoot, id).surface === 'agents-md') ids.add(id);
|
|
754
|
+
} catch (_e) { /* skip an agent we can't read */ }
|
|
755
|
+
}
|
|
756
|
+
const sorted = [...ids].sort();
|
|
757
|
+
if (sorted.length <= 1) return readStatic(); // N=1 → static committed template
|
|
758
|
+
return compose.composeInstruction(srcRoot, sorted); // N>1 → composed
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function installPrimaryInstruction(srcRoot, targetDir, adapterDir, primaryInstruction, agent) {
|
|
723
762
|
if (!primaryInstruction) return null;
|
|
724
763
|
const srcPath = resolveAdapterSource(srcRoot, adapterDir, primaryInstruction);
|
|
725
764
|
const destPath = path.join(targetDir, ...primaryInstruction.destination);
|
|
@@ -733,7 +772,7 @@ function installPrimaryInstruction(srcRoot, targetDir, adapterDir, primaryInstru
|
|
|
733
772
|
return 'added';
|
|
734
773
|
}
|
|
735
774
|
const rendered = renderProjectName(
|
|
736
|
-
|
|
775
|
+
resolvePrimaryInstructionContent(srcRoot, targetDir, agent, srcPath),
|
|
737
776
|
getProjectName(targetDir)
|
|
738
777
|
);
|
|
739
778
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
@@ -745,7 +784,7 @@ function installPrimaryInstruction(srcRoot, targetDir, adapterDir, primaryInstru
|
|
|
745
784
|
return 'skipped';
|
|
746
785
|
}
|
|
747
786
|
|
|
748
|
-
function upgradePrimaryInstruction(srcRoot, targetDir, adapterDir, primaryInstruction) {
|
|
787
|
+
function upgradePrimaryInstruction(srcRoot, targetDir, adapterDir, primaryInstruction, agent) {
|
|
749
788
|
if (!primaryInstruction) return null;
|
|
750
789
|
const srcPath = resolveAdapterSource(srcRoot, adapterDir, primaryInstruction);
|
|
751
790
|
const destPath = path.join(targetDir, ...primaryInstruction.destination);
|
|
@@ -753,12 +792,15 @@ function upgradePrimaryInstruction(srcRoot, targetDir, adapterDir, primaryInstru
|
|
|
753
792
|
const projectName = getProjectName(targetDir);
|
|
754
793
|
recordManaged(destPath); // managed (marker-owned)
|
|
755
794
|
|
|
795
|
+
// ADR-0011: compose AGENTS.md across all installed AGENTS.md agents.
|
|
796
|
+
const rawContent = resolvePrimaryInstructionContent(srcRoot, targetDir, agent, srcPath);
|
|
797
|
+
|
|
756
798
|
console.log(`→ Upgrading ${label}...`);
|
|
757
799
|
if (primaryInstruction.markerAware) {
|
|
758
|
-
return upgradeMarkedFile(srcPath, destPath, label, targetDir, projectName);
|
|
800
|
+
return upgradeMarkedFile(srcPath, destPath, label, targetDir, projectName, rawContent);
|
|
759
801
|
}
|
|
760
802
|
|
|
761
|
-
const srcContent = renderProjectName(
|
|
803
|
+
const srcContent = renderProjectName(rawContent, projectName);
|
|
762
804
|
const relDest = path.relative(targetDir, destPath);
|
|
763
805
|
if (!fileExists(destPath)) {
|
|
764
806
|
if (_dryRun) {
|
|
@@ -895,11 +937,14 @@ function partitionByMarker(content) {
|
|
|
895
937
|
*
|
|
896
938
|
* Returns one of: 'added', 'updated', 'unchanged', 'migrated'.
|
|
897
939
|
*/
|
|
898
|
-
function upgradeMarkedFile(srcPath, destPath, label, root, projectName) {
|
|
940
|
+
function upgradeMarkedFile(srcPath, destPath, label, root, projectName, srcContentRaw) {
|
|
899
941
|
const rel = path.relative(root || process.cwd(), destPath);
|
|
900
942
|
recordManaged(destPath); // managed (marker-owned) in all branches
|
|
901
943
|
const render = (content) =>
|
|
902
944
|
projectName ? renderProjectName(content, projectName) : content;
|
|
945
|
+
// ADR-0011: callers may pass composed content (multi-agent AGENTS.md); else
|
|
946
|
+
// read the static committed template.
|
|
947
|
+
const rawSrc = srcContentRaw != null ? srcContentRaw : fs.readFileSync(srcPath, 'utf8');
|
|
903
948
|
|
|
904
949
|
if (!fileExists(destPath)) {
|
|
905
950
|
if (_dryRun) {
|
|
@@ -907,12 +952,12 @@ function upgradeMarkedFile(srcPath, destPath, label, root, projectName) {
|
|
|
907
952
|
return 'added';
|
|
908
953
|
}
|
|
909
954
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
910
|
-
fs.writeFileSync(destPath, render(
|
|
955
|
+
fs.writeFileSync(destPath, render(rawSrc));
|
|
911
956
|
console.log(` + added: ${rel}`);
|
|
912
957
|
return 'added';
|
|
913
958
|
}
|
|
914
959
|
|
|
915
|
-
const srcContent = render(
|
|
960
|
+
const srcContent = render(rawSrc);
|
|
916
961
|
const destContent = fs.readFileSync(destPath, 'utf8');
|
|
917
962
|
const destParts = partitionByMarker(destContent);
|
|
918
963
|
|
|
@@ -1215,7 +1260,7 @@ function init(targetDir, agent, opts = {}) {
|
|
|
1215
1260
|
console.log('→ Inferring project config...');
|
|
1216
1261
|
installConfig(target, { dryRun: _dryRun });
|
|
1217
1262
|
|
|
1218
|
-
installPrimaryInstruction(src, target, adapterDir, adapter.primaryInstruction);
|
|
1263
|
+
installPrimaryInstruction(src, target, adapterDir, adapter.primaryInstruction, agent);
|
|
1219
1264
|
|
|
1220
1265
|
// Adapter overlay — per-agent commands/agent-rules/scripts (additive)
|
|
1221
1266
|
applyOverlay(adapterDir, target, dests, { backup: true, root: target });
|
|
@@ -1348,11 +1393,32 @@ function upgrade(targetDir, agent, opts = {}) {
|
|
|
1348
1393
|
// would dirty files mid-upgrade and break autostash restores).
|
|
1349
1394
|
const pointerLib = require('../core/ecosystem/lib/pointer');
|
|
1350
1395
|
const primaryRel = adapter.primaryInstruction.destination.join('/');
|
|
1396
|
+
|
|
1397
|
+
// Phase 28 (ADR-0010): migrate any authored `## Project Extensions` prose into
|
|
1398
|
+
// specs/project-rules.md and rewrite that section as the managed pointer,
|
|
1399
|
+
// BEFORE the marker-aware rewrite preserves it. Migrate-never-drop, idempotent.
|
|
1400
|
+
if (!_dryRun) {
|
|
1401
|
+
const projectRules = require('../core/lib/project-rules');
|
|
1402
|
+
const migRes = projectRules.migrateProjectExtensions(
|
|
1403
|
+
path.join(target, primaryRel),
|
|
1404
|
+
path.join(target, 'specs'),
|
|
1405
|
+
{ projectName: getProjectName(target) }
|
|
1406
|
+
);
|
|
1407
|
+
if (migRes.appended) {
|
|
1408
|
+
console.log(` ↳ migrated ${primaryRel} Project Extensions → specs/project-rules.md (ADR-0010)`);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// Snapshot whether THIS agent's instruction file carries the pointer BEFORE
|
|
1413
|
+
// the managed rewrite — we only ever PRESERVE it (BUG-022), never add one the
|
|
1414
|
+
// file didn't have (adding would dirty files mid-upgrade and break autostash
|
|
1415
|
+
// restores). Phase 28: since `upgrade` now runs per installed agent (G1), this
|
|
1416
|
+
// preserves every agent's pointer; ADDING a missing pointer (cause #2) is
|
|
1417
|
+
// `momentum ecosystem add`'s job, which now injects into all instruction files.
|
|
1351
1418
|
let hadPointerBlock = false;
|
|
1352
1419
|
try {
|
|
1353
|
-
hadPointerBlock =
|
|
1354
|
-
|
|
1355
|
-
);
|
|
1420
|
+
hadPointerBlock = fs.readFileSync(path.join(target, primaryRel), 'utf8')
|
|
1421
|
+
.includes(pointerLib.POINTER_BEGIN_PREFIX);
|
|
1356
1422
|
} catch (_e) { /* absent file → nothing to preserve */ }
|
|
1357
1423
|
|
|
1358
1424
|
// Upgrade adapter-owned root instruction file
|
|
@@ -1360,7 +1426,8 @@ function upgrade(targetDir, agent, opts = {}) {
|
|
|
1360
1426
|
src,
|
|
1361
1427
|
target,
|
|
1362
1428
|
adapterDir,
|
|
1363
|
-
adapter.primaryInstruction
|
|
1429
|
+
adapter.primaryInstruction,
|
|
1430
|
+
agent
|
|
1364
1431
|
);
|
|
1365
1432
|
|
|
1366
1433
|
if (hadPointerBlock && !_dryRun) {
|
|
@@ -1983,12 +2050,27 @@ async function main() {
|
|
|
1983
2050
|
// upgrade can only ever install files as new as the CLI itself.
|
|
1984
2051
|
const staleWarning = formatStaleCliWarning(pkg.version, await updateCheckPromise);
|
|
1985
2052
|
if (staleWarning) console.log(staleWarning);
|
|
1986
|
-
//
|
|
1987
|
-
//
|
|
2053
|
+
// Phase 28 (ADR-0010): with no explicit --agent, refresh EVERY installed
|
|
2054
|
+
// agent so no agent's instruction file silently drifts (cause #1 of the
|
|
2055
|
+
// CLAUDE.md/AGENTS.md divergence). Explicit --agent targets that one.
|
|
2056
|
+
let agentsToUpgrade;
|
|
2057
|
+
if (agentExplicit) {
|
|
2058
|
+
agentsToUpgrade = [agent];
|
|
2059
|
+
} else {
|
|
2060
|
+
const installed = loadInstalledState(path.resolve(targetDir));
|
|
2061
|
+
const names = Object.keys(installed.agents || {});
|
|
2062
|
+
agentsToUpgrade = names.length ? names : [agent];
|
|
2063
|
+
}
|
|
2064
|
+
if (agentsToUpgrade.length > 1) {
|
|
2065
|
+
console.log(`Upgrading all installed agents: ${agentsToUpgrade.join(', ')}\n`);
|
|
2066
|
+
}
|
|
2067
|
+
// --autostash: stash a dirty tree, upgrade, restore — once around all
|
|
2068
|
+
// agents. Dry-run writes nothing, so it never needs to stash.
|
|
2069
|
+
const runAll = () => { for (const a of agentsToUpgrade) upgrade(targetDir, a, { dryRun }); };
|
|
1988
2070
|
if (autostash && !dryRun) {
|
|
1989
|
-
withAutostash(path.resolve(targetDir),
|
|
2071
|
+
withAutostash(path.resolve(targetDir), runAll);
|
|
1990
2072
|
} else {
|
|
1991
|
-
|
|
2073
|
+
runAll();
|
|
1992
2074
|
}
|
|
1993
2075
|
} catch (err) {
|
|
1994
2076
|
console.error(`\nError: ${err.message}`);
|
package/bin/state-commands.js
CHANGED
|
@@ -103,10 +103,9 @@ function cmdLeave(args) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
if (state === 'broken-pointer') {
|
|
106
|
-
// Pointer present but no registration.
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
109
|
-
pointerLib.stripPointer(path.join(cwd, primary));
|
|
106
|
+
// Pointer present but no registration. Strip from every instruction file.
|
|
107
|
+
const stripped = pointerLib.stripPointerAll(cwd);
|
|
108
|
+
if (stripped.length) {
|
|
110
109
|
console.log('Stripped dangling pointer. Now standalone.');
|
|
111
110
|
} else {
|
|
112
111
|
console.log('No pointer block found. No changes.');
|
|
@@ -89,7 +89,7 @@ Verify, finalize, and release a completed phase.
|
|
|
89
89
|
`[staging, main]`) in order. The plan stops at the universal git primitives
|
|
90
90
|
(`git merge` + `git tag -a` + `git push origin <tag>`); forge-specific
|
|
91
91
|
release creation and registry publishes are NOT listed here — they run
|
|
92
|
-
from
|
|
92
|
+
from `specs/project-rules.md` + `specs/config.md` (`release_command`,
|
|
93
93
|
`publish_target`, `release_flow`) after the tag is pushed.
|
|
94
94
|
```
|
|
95
95
|
Ready to release vX.Y.Z. This will:
|
|
@@ -98,7 +98,7 @@ Verify, finalize, and release a completed phase.
|
|
|
98
98
|
3. Tag vX.Y.Z and push the tag
|
|
99
99
|
|
|
100
100
|
Project-specific release/publish/deploy (forge release, npm publish, …)
|
|
101
|
-
run from
|
|
101
|
+
run from specs/project-rules.md + specs/config.md after the tag lands.
|
|
102
102
|
|
|
103
103
|
Proceed?
|
|
104
104
|
```
|
|
@@ -126,7 +126,7 @@ Verify, finalize, and release a completed phase.
|
|
|
126
126
|
git push origin <branch_flow[1]>
|
|
127
127
|
|
|
128
128
|
# final step — tag + push the tag (universal git; forge release + publish
|
|
129
|
-
# are project-specific — run them from
|
|
129
|
+
# are project-specific — run them from specs/project-rules.md now)
|
|
130
130
|
git tag -a vX.Y.Z -m "Phase N: {phase name}"
|
|
131
131
|
git push origin vX.Y.Z
|
|
132
132
|
```
|
|
@@ -154,7 +154,7 @@ Verify, finalize, and release a completed phase.
|
|
|
154
154
|
12. Report summary to user:
|
|
155
155
|
- What was delivered
|
|
156
156
|
- Tag URL (`git push origin vX.Y.Z` output) — forge release/publish
|
|
157
|
-
status from
|
|
157
|
+
status from `specs/project-rules.md` if any ran
|
|
158
158
|
- What's next
|
|
159
159
|
|
|
160
160
|
13. Clean up the spent phase branch (ENH-042 / BUG-026 — now that merge → main
|
|
@@ -153,14 +153,14 @@ defaults below) and ask the user the gate question matching the project's
|
|
|
153
153
|
reconcile --execute` cleans up the branch + worktree + state."
|
|
154
154
|
|
|
155
155
|
Then append: "Project-specific release/publish/deploy steps (e.g.
|
|
156
|
-
`npm publish`, forge release creation) run from
|
|
156
|
+
`npm publish`, forge release creation) run from `specs/project-rules.md` in
|
|
157
157
|
CLAUDE.md/AGENTS.md — consult them now if any apply."
|
|
158
158
|
|
|
159
159
|
The gate stops at the truly universal git primitives — `git merge` +
|
|
160
160
|
`git tag -a` + `git push origin <tag>`. Forge-specific release creation
|
|
161
161
|
(`gh release create`, `glab release create`, …) and registry publishes
|
|
162
162
|
(`npm publish`, `twine upload`, …) are NOT in this template; they live in
|
|
163
|
-
|
|
163
|
+
`specs/project-rules.md` + `specs/config.md` (`release_command`,
|
|
164
164
|
`publish_target`, `release_flow`).
|
|
165
165
|
|
|
166
166
|
This is the only place the engine asks. Do NOT skip it.
|
|
@@ -79,15 +79,14 @@ If you're still exploring the idea, run `/brainstorm-idea` first.
|
|
|
79
79
|
- `specs/planning/roadmap.md`
|
|
80
80
|
- `specs/config.md` (from the config table above; overwrite the
|
|
81
81
|
`momentum init`-inferred file if present — founding owns the content)
|
|
82
|
+
- `specs/project-rules.md` (project-specific prose rules; each instruction
|
|
83
|
+
file's `## Project Extensions` points here — ADR-0010)
|
|
82
84
|
- Update `specs/status.md`: Summary, Current Phase (Phase 0 `not started`),
|
|
83
85
|
Upcoming Phases, Next Actions
|
|
84
86
|
- For monorepo: `specs/architecture/` first-pass doc(s)
|
|
85
87
|
- `specs/phases/phase-0-shortname/{overview,plan,tasks,history}.md`
|
|
86
88
|
(log the founding decisions from this conversation in `history.md`)
|
|
87
|
-
- Refresh
|
|
88
|
-
`writeConfigCache('.', readConfig('specs'))` so the pre-push
|
|
89
|
-
hook's `protected_branches` matches the authored `branch_flow`;
|
|
90
|
-
then `momentum okf index` to refresh bundle listings
|
|
89
|
+
- Refresh derived cache (`writeConfigCache`) + `momentum okf index`
|
|
91
90
|
|
|
92
91
|
7. Commit the founding:
|
|
93
92
|
```bash
|
|
@@ -30,6 +30,9 @@ Run with no arguments for a fast index-first check, or pass `--deep` for a full
|
|
|
30
30
|
- Not founded with no phase directories is the VALID Installed state —
|
|
31
31
|
report informationally ("not founded yet — `/start-project` when
|
|
32
32
|
ready"), never as a failure
|
|
33
|
+
- founded AND no `specs/project-rules.md` (ADR-0010) → WARNING: "project
|
|
34
|
+
rules surface missing — run `momentum upgrade` to create it; every agent
|
|
35
|
+
instruction file's `## Project Extensions` pointer expects it"
|
|
33
36
|
|
|
34
37
|
2c. Config (ADR-0009) — WARNING, never a failure (recipes fall back to
|
|
35
38
|
npm/GitHub defaults when absent):
|
|
@@ -159,6 +159,45 @@ function ensurePointerInjected(absRepo, primaryFile, root, ecosystemName) {
|
|
|
159
159
|
fs.writeFileSync(filePath, next, 'utf8');
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
+
/**
|
|
163
|
+
* All present primary instruction files in a member repo (not just the first).
|
|
164
|
+
* Phase 28 (ADR-0010): the ecosystem pointer must reach EVERY agent's file, so
|
|
165
|
+
* a Codex/opencode session reading AGENTS.md is not blind to the ecosystem.
|
|
166
|
+
*/
|
|
167
|
+
function findAllInstructionFiles(repoPath) {
|
|
168
|
+
return PRIMARY_INSTRUCTION_CANDIDATES.filter((name) =>
|
|
169
|
+
fs.existsSync(path.join(repoPath, name)));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Inject/refresh the pointer block in EVERY present instruction file
|
|
174
|
+
* (Phase 28). Returns the list of files touched. When none exist, returns [].
|
|
175
|
+
*/
|
|
176
|
+
function ensurePointerInjectedAll(absRepo, root, ecosystemName) {
|
|
177
|
+
const files = findAllInstructionFiles(absRepo);
|
|
178
|
+
for (const primaryFile of files) {
|
|
179
|
+
ensurePointerInjected(absRepo, primaryFile, root, ecosystemName);
|
|
180
|
+
}
|
|
181
|
+
return files;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Strip the pointer block from EVERY present instruction file (Phase 28).
|
|
186
|
+
* Returns only the files that actually HAD a pointer (so callers can report
|
|
187
|
+
* "nothing to strip" accurately).
|
|
188
|
+
*/
|
|
189
|
+
function stripPointerAll(absRepo) {
|
|
190
|
+
const stripped = [];
|
|
191
|
+
for (const primaryFile of findAllInstructionFiles(absRepo)) {
|
|
192
|
+
const fp = path.join(absRepo, primaryFile);
|
|
193
|
+
let had = false;
|
|
194
|
+
try { had = fs.readFileSync(fp, 'utf8').includes(POINTER_BEGIN_PREFIX); } catch { /* absent */ }
|
|
195
|
+
stripPointer(fp);
|
|
196
|
+
if (had) stripped.push(primaryFile);
|
|
197
|
+
}
|
|
198
|
+
return stripped;
|
|
199
|
+
}
|
|
200
|
+
|
|
162
201
|
/**
|
|
163
202
|
* Strip the pointer block (any version) and one surrounding blank
|
|
164
203
|
* line from the given file. Idempotent: silently no-ops if the file
|
|
@@ -191,8 +230,11 @@ module.exports = {
|
|
|
191
230
|
POINTER_BLOCK_RE,
|
|
192
231
|
PRIMARY_INSTRUCTION_CANDIDATES,
|
|
193
232
|
findPrimaryInstructionFile,
|
|
233
|
+
findAllInstructionFiles,
|
|
194
234
|
hasPointerBlock,
|
|
195
235
|
ensurePointerInjected,
|
|
236
|
+
ensurePointerInjectedAll,
|
|
196
237
|
stripPointer,
|
|
238
|
+
stripPointerAll,
|
|
197
239
|
renderPointerBody,
|
|
198
240
|
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Instruction sources — the three-tier variation model (ADR-0011)
|
|
2
|
+
|
|
3
|
+
The primary instruction file each agent auto-loads (`CLAUDE.md` for Claude Code,
|
|
4
|
+
`AGENTS.md` for every AGENTS.md-standard agent) is **generated**, never
|
|
5
|
+
hand-edited. It is a projection of **(principal constants × per-agent variation
|
|
6
|
+
manifest)**. Regenerate with `npm run generate-instructions`; the suite's drift
|
|
7
|
+
guard fails if a committed template diverges from its sources.
|
|
8
|
+
|
|
9
|
+
## The three tiers
|
|
10
|
+
|
|
11
|
+
| Tier | What | Where | Rule |
|
|
12
|
+
|------|------|-------|------|
|
|
13
|
+
| **1 — Principal constants** (the neutral spine) | Navigation table, Rules 1–15, Naming, Constraints, Project Extensions pointer | `core/instructions/navigation.md`, `core/instructions/rules-body.md` | **Byte-identical** across every agent + surface (`CLAUDE.md` ≡ `AGENTS.md` body) |
|
|
14
|
+
| **2 — Variation manifest** | Everything that varies per agent | `adapters/<agent>/instructions/manifest.json` | One declarative record; generator auto-discovers it (OCP) |
|
|
15
|
+
| **3 — Surface delta** | Irreducibly agent-specific integration prose + the task-tool mapping line | `adapters/<agent>/instructions/surfaces.md` (optional) | A scoped delta, never the file's whole identity (SRP) |
|
|
16
|
+
|
|
17
|
+
The rules body carries **no agent-specific token** — the task tool is named only
|
|
18
|
+
in the Tier-3 delta, which every agent still loads. That is what makes the spine
|
|
19
|
+
byte-identical (and therefore a testable invariant, not a claim).
|
|
20
|
+
|
|
21
|
+
## `manifest.json` schema
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"id": "opencode",
|
|
26
|
+
"displayName": "opencode",
|
|
27
|
+
"surface": "agents-md",
|
|
28
|
+
"taskTool": "the built-in **task** tool (subagent fan-out)",
|
|
29
|
+
"taskToolName": "The task tool",
|
|
30
|
+
"hasSurfaceDelta": true
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Field | Type | Meaning |
|
|
35
|
+
|-------|------|---------|
|
|
36
|
+
| `id` | string | Adapter id — must match the `adapters/<id>/` directory name |
|
|
37
|
+
| `displayName` | string | Human name used in the header scaffold + integration heading |
|
|
38
|
+
| `surface` | `"claude-md"` \| `"agents-md"` | Which instruction file this agent reads; drives the install destination |
|
|
39
|
+
| `taskTool` | string | Rendered into the Tier-3 task-tool mapping line (may contain markdown) |
|
|
40
|
+
| `taskToolName` | string | Capitalized short form for prose references |
|
|
41
|
+
| `hasSurfaceDelta` | boolean | Whether a `surfaces.md` integration delta exists for this agent |
|
|
42
|
+
|
|
43
|
+
## Assembly order
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
header (scaffold, display name from manifest)
|
|
47
|
+
→ navigation (Tier 1)
|
|
48
|
+
→ surfaces delta (Tier 3, if hasSurfaceDelta)
|
|
49
|
+
→ rules body (Tier 1, agent-neutral)
|
|
50
|
+
→ Project Extensions pointer tail (ADR-0010)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Adding a new agent (the OCP path)
|
|
54
|
+
|
|
55
|
+
1. Create `adapters/<id>/instructions/manifest.json`.
|
|
56
|
+
2. Optionally add `adapters/<id>/instructions/surfaces.md` (integration delta).
|
|
57
|
+
3. Run `npm run generate-instructions`.
|
|
58
|
+
|
|
59
|
+
No edit to `scripts/generate-instructions.js` is required — it discovers
|
|
60
|
+
adapters by scanning `adapters/*/instructions/`. A suite test proves this by
|
|
61
|
+
generating for a synthetic adapter with zero generator edits.
|
|
62
|
+
|
|
63
|
+
## AGENTS.md when multiple AGENTS.md agents are installed
|
|
64
|
+
|
|
65
|
+
`AGENTS.md` is a shared standard, so one repo can host several AGENTS.md agents.
|
|
66
|
+
The committed per-agent template is the single-agent (N=1) projection. At
|
|
67
|
+
install/upgrade, when `installed.json.agents` holds more than one `agents-md`
|
|
68
|
+
agent, `AGENTS.md` is **composed**: the neutral spine once, plus one integration
|
|
69
|
+
section per installed AGENTS.md agent. `CLAUDE.md` is unaffected (Claude-only).
|
|
@@ -19,7 +19,7 @@ After completing ANY meaningful work, automatically update:
|
|
|
19
19
|
2. **`specs/status.md`** — if phase progress, blockers, or P0 items changed (touch only your own lane's row — Rule 15)
|
|
20
20
|
3. **`specs/changelog/YYYY-MM.md`** — log what changed (one line per change, append-only)
|
|
21
21
|
|
|
22
|
-
Use
|
|
22
|
+
Use your in-session task-tracking tool (named in the "In-Session Task Tool" note above) to track in-session task progress. Do NOT wait for the user to ask you to update tracking.
|
|
23
23
|
|
|
24
24
|
#### Why
|
|
25
25
|
Tracking debt compounds invisibly. A task list one day stale is recoverable; one week stale is fiction. Status drift is how phases silently lose direction.
|
|
@@ -37,7 +37,7 @@ Tracking debt compounds invisibly. A task list one day stale is recoverable; one
|
|
|
37
37
|
#### Anti-Rationalization Counters
|
|
38
38
|
|
|
39
39
|
- "It's faster to do the work first and track at the end" — wrong: reconstruction takes 2-3× longer than real-time logging.
|
|
40
|
-
- "
|
|
40
|
+
- "Your in-session task-tracking tool is enough" — it is in-session only; `tasks.md` is the durable record.
|
|
41
41
|
- "Mid-task tracking interrupts flow" — a one-line update costs <30s; reconstructing a day later costs 30 minutes.
|
|
42
42
|
|
|
43
43
|
### Rule 3: Auto-Track Discoveries
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ADR-0011 — the instruction file is a projection of
|
|
4
|
+
// (principal constants × per-agent variation manifest).
|
|
5
|
+
//
|
|
6
|
+
// Pure assembly logic, shipped in core/ so BOTH build-time generation
|
|
7
|
+
// (scripts/generate-instructions.js) and install-time multi-agent AGENTS.md
|
|
8
|
+
// composition (bin/momentum.js) use ONE code path. Every function takes
|
|
9
|
+
// `root` = the momentum package/repo root that holds core/ + adapters/.
|
|
10
|
+
//
|
|
11
|
+
// Three tiers:
|
|
12
|
+
// 1. Principal constants (neutral spine): core/instructions/navigation.md +
|
|
13
|
+
// rules-body.md — byte-identical across every agent + surface.
|
|
14
|
+
// 2. Variation manifest: adapters/<id>/instructions/manifest.json.
|
|
15
|
+
// 3. Surface delta: adapters/<id>/instructions/surfaces.md (optional).
|
|
16
|
+
//
|
|
17
|
+
// Assembly:
|
|
18
|
+
// marker → header → navigation → (per agent: surfaces? + task-tool note)
|
|
19
|
+
// → neutral rules body → Project Extensions pointer tail (ADR-0010)
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
|
|
24
|
+
const MARKER_COMMENT = [
|
|
25
|
+
'<!-- momentum-managed (generated) — regenerate with `npm run generate-instructions`',
|
|
26
|
+
' in the momentum repo; sources: core/instructions/ + adapters/<agent>/instructions/.',
|
|
27
|
+
" Everything above '## Project Extensions' may be replaced by `momentum upgrade`. -->",
|
|
28
|
+
].join('\n');
|
|
29
|
+
|
|
30
|
+
// ADR-0010: `## Project Extensions` is a managed POINTER to specs/project-rules.md,
|
|
31
|
+
// not an authoring surface. Kept byte-identical to core/lib/project-rules.js.
|
|
32
|
+
const EXTENSIONS_TAIL = [
|
|
33
|
+
'---',
|
|
34
|
+
'',
|
|
35
|
+
'## Project Extensions',
|
|
36
|
+
'',
|
|
37
|
+
'<!-- momentum:project-rules-pointer -->',
|
|
38
|
+
'> **Project-specific rules live in `specs/project-rules.md`** — read it now.',
|
|
39
|
+
'> Session-start self-audits, project constraints, and any project-specific',
|
|
40
|
+
'> guidance are there, shared identically by every agent (ADR-0010). This',
|
|
41
|
+
'> section is a momentum-managed pointer; edit `specs/project-rules.md`, not',
|
|
42
|
+
'> this file.',
|
|
43
|
+
].join('\n');
|
|
44
|
+
|
|
45
|
+
function readFragment(root, rel) {
|
|
46
|
+
return fs.readFileSync(path.join(root, ...rel), 'utf8');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Discover every adapter that ships an instruction manifest (OCP: add a dir, no edits). */
|
|
50
|
+
function discoverAgents(root) {
|
|
51
|
+
const dir = path.join(root, 'adapters');
|
|
52
|
+
return fs
|
|
53
|
+
.readdirSync(dir)
|
|
54
|
+
.filter((id) => fs.existsSync(path.join(dir, id, 'instructions', 'manifest.json')))
|
|
55
|
+
.sort();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readManifest(root, id) {
|
|
59
|
+
const m = JSON.parse(readFragment(root, ['adapters', id, 'instructions', 'manifest.json']));
|
|
60
|
+
for (const key of ['id', 'displayName', 'surface', 'taskTool', 'taskToolName']) {
|
|
61
|
+
if (typeof m[key] !== 'string' || !m[key]) {
|
|
62
|
+
throw new Error(`adapters/${id}/instructions/manifest.json: missing/invalid "${key}"`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (m.id !== id) throw new Error(`manifest id "${m.id}" != adapter dir "${id}"`);
|
|
66
|
+
if (m.surface !== 'claude-md' && m.surface !== 'agents-md') {
|
|
67
|
+
throw new Error(`adapters/${id}: surface "${m.surface}" — expected claude-md | agents-md`);
|
|
68
|
+
}
|
|
69
|
+
return m;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The install destination filename implied by the surface. */
|
|
73
|
+
function surfaceDestination(surface) {
|
|
74
|
+
if (surface === 'claude-md') return 'CLAUDE.md';
|
|
75
|
+
if (surface === 'agents-md') return 'AGENTS.md';
|
|
76
|
+
throw new Error(`unknown surface "${surface}"`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Where this agent's committed (N=1) template lives, derived from its surface. */
|
|
80
|
+
function templatePath(id, surface) {
|
|
81
|
+
if (surface === 'claude-md') return ['core', 'specs-templates', 'CLAUDE.md'];
|
|
82
|
+
if (surface === 'agents-md') return ['adapters', id, 'instructions', 'AGENTS.md'];
|
|
83
|
+
throw new Error(`unknown surface "${surface}" for adapter "${id}"`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Header: single scaffold constant, agent display name(s) the only variable. */
|
|
87
|
+
function renderHeader(manifests) {
|
|
88
|
+
const title = '# Project Rules: <Project Name>';
|
|
89
|
+
if (manifests.length === 1) {
|
|
90
|
+
return `${title}\n\n> ${manifests[0].displayName} configuration for this momentum-managed project.`;
|
|
91
|
+
}
|
|
92
|
+
const names = manifests.map((m) => m.displayName).join(', ');
|
|
93
|
+
return (
|
|
94
|
+
`${title}\n\n> Configuration for this momentum-managed project. This shared ` +
|
|
95
|
+
`AGENTS.md serves ${manifests.length} agents (${names}); each has its own ` +
|
|
96
|
+
`integration section below.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The one agent-specific line lifted out of the neutral spine (SRP). */
|
|
101
|
+
function renderTaskToolNote(m) {
|
|
102
|
+
return [
|
|
103
|
+
'## In-Session Task Tool',
|
|
104
|
+
'',
|
|
105
|
+
`When a rule mentions tracking in-session progress (Rule 2), ${m.displayName}'s ` +
|
|
106
|
+
`task tool is ${m.taskTool} — in-session only; the durable record is ` +
|
|
107
|
+
'`specs/phases/<phase>/tasks.md`.',
|
|
108
|
+
].join('\n');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function readSurfaces(root, m) {
|
|
112
|
+
if (!m.hasSurfaceDelta) return null;
|
|
113
|
+
const p = path.join(root, 'adapters', m.id, 'instructions', 'surfaces.md');
|
|
114
|
+
return fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function finalize(parts) {
|
|
118
|
+
const content = parts.map((p) => p.replace(/\s+$/, '')).join('\n\n') + '\n';
|
|
119
|
+
const leftover = content.match(/\{\{[A-Z_]+\}\}/);
|
|
120
|
+
if (leftover) throw new Error(`unrendered placeholder ${leftover[0]}`);
|
|
121
|
+
return content;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Compose an instruction file for one or more agents.
|
|
126
|
+
* - Single id → the committed N=1 projection (a static template).
|
|
127
|
+
* - Multiple ids → neutral spine + one integration section per agent
|
|
128
|
+
* (install-time AGENTS.md when several AGENTS.md agents are installed).
|
|
129
|
+
*/
|
|
130
|
+
function composeInstruction(root, ids) {
|
|
131
|
+
if (!ids.length) throw new Error('composeInstruction: no agents');
|
|
132
|
+
const manifests = ids.map((id) => readManifest(root, id));
|
|
133
|
+
const navigation = readFragment(root, ['core', 'instructions', 'navigation.md']);
|
|
134
|
+
const body = readFragment(root, ['core', 'instructions', 'rules-body.md']);
|
|
135
|
+
|
|
136
|
+
const parts = [MARKER_COMMENT, renderHeader(manifests), navigation];
|
|
137
|
+
for (const m of manifests) {
|
|
138
|
+
const surfaces = readSurfaces(root, m);
|
|
139
|
+
if (surfaces) parts.push(surfaces);
|
|
140
|
+
parts.push(renderTaskToolNote(m));
|
|
141
|
+
}
|
|
142
|
+
parts.push(body, EXTENSIONS_TAIL);
|
|
143
|
+
return finalize(parts);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** One committed template descriptor for a single agent. */
|
|
147
|
+
function generateFor(root, id) {
|
|
148
|
+
const m = readManifest(root, id);
|
|
149
|
+
const target = templatePath(id, m.surface);
|
|
150
|
+
return {
|
|
151
|
+
agent: id,
|
|
152
|
+
targetRel: target.join('/'),
|
|
153
|
+
targetPath: path.join(root, ...target),
|
|
154
|
+
content: composeInstruction(root, [id]),
|
|
155
|
+
manifest: m,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function generateAll(root) {
|
|
160
|
+
return discoverAgents(root).map((id) => generateFor(root, id));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = {
|
|
164
|
+
MARKER_COMMENT,
|
|
165
|
+
EXTENSIONS_TAIL,
|
|
166
|
+
discoverAgents,
|
|
167
|
+
readManifest,
|
|
168
|
+
surfaceDestination,
|
|
169
|
+
templatePath,
|
|
170
|
+
renderHeader,
|
|
171
|
+
renderTaskToolNote,
|
|
172
|
+
composeInstruction,
|
|
173
|
+
generateFor,
|
|
174
|
+
generateAll,
|
|
175
|
+
};
|
package/core/lib/okf-types.js
CHANGED
|
@@ -23,6 +23,7 @@ function isReserved(relPath) {
|
|
|
23
23
|
const RULES = [
|
|
24
24
|
[/^status\.md$/, 'Status'],
|
|
25
25
|
[/^config\.md$/, 'Config'],
|
|
26
|
+
[/^project-rules\.md$/, 'Project Rules'],
|
|
26
27
|
[/^backlog\/backlog\.md$/, 'Backlog'],
|
|
27
28
|
[/^backlog\/details\/.+\.md$/, 'Backlog Detail'],
|
|
28
29
|
[/^planning\/roadmap\.md$/, 'Roadmap'],
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `specs/project-rules.md` — the single shared home for project-specific PROSE
|
|
5
|
+
* (Phase 28, ADR-0010). Every agent instruction file's `## Project Extensions`
|
|
6
|
+
* section becomes a managed POINTER to this file, so project rules live once and
|
|
7
|
+
* cannot diverge across adapters.
|
|
8
|
+
*
|
|
9
|
+
* This module owns:
|
|
10
|
+
* - the pointer block that replaces `## Project Extensions`,
|
|
11
|
+
* - the `project-rules.md` scaffold,
|
|
12
|
+
* - `migrateProjectExtensions()` — the migrate-never-drop upgrade path that
|
|
13
|
+
* moves an instruction file's authored extensions into project-rules.md and
|
|
14
|
+
* rewrites its `## Project Extensions` as the pointer.
|
|
15
|
+
*
|
|
16
|
+
* Pure logic is exported separately from the fs-touching migration so it is
|
|
17
|
+
* unit-testable. Zero dependencies — node builtins only.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const MARKER = '## Project Extensions';
|
|
24
|
+
const POINTER_SENTINEL = '<!-- momentum:project-rules-pointer -->';
|
|
25
|
+
const PROJECT_RULES_REL = 'project-rules.md';
|
|
26
|
+
|
|
27
|
+
/** The managed pointer block that IS the `## Project Extensions` section. */
|
|
28
|
+
function renderPointerBlock() {
|
|
29
|
+
return [
|
|
30
|
+
MARKER,
|
|
31
|
+
'',
|
|
32
|
+
POINTER_SENTINEL,
|
|
33
|
+
'> **Project-specific rules live in `specs/project-rules.md`** — read it now.',
|
|
34
|
+
'> Session-start self-audits, project constraints, and any project-specific',
|
|
35
|
+
'> guidance are there, shared identically by every agent (ADR-0010). This',
|
|
36
|
+
'> section is a momentum-managed pointer; edit `specs/project-rules.md`, not',
|
|
37
|
+
'> this file.',
|
|
38
|
+
'',
|
|
39
|
+
].join('\n');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Fresh `project-rules.md` scaffold (optionally carrying migrated body). */
|
|
43
|
+
function renderProjectRules(projectName, migratedBody) {
|
|
44
|
+
const head = [
|
|
45
|
+
'---',
|
|
46
|
+
'type: Project Rules',
|
|
47
|
+
'---',
|
|
48
|
+
'',
|
|
49
|
+
`# Project Rules — ${projectName || 'this project'}`,
|
|
50
|
+
'',
|
|
51
|
+
'> Project-specific rules every agent reads — referenced by each instruction',
|
|
52
|
+
"> file's `## Project Extensions` pointer (ADR-0010). One home, shared by all",
|
|
53
|
+
'> adapters. Edit freely. For a rule that applies to only one agent, annotate',
|
|
54
|
+
'> it inline (e.g. *(Claude Code only)*).',
|
|
55
|
+
'',
|
|
56
|
+
].join('\n');
|
|
57
|
+
const body = (migratedBody && migratedBody.trim())
|
|
58
|
+
? migratedBody.trim() + '\n'
|
|
59
|
+
: '_(no project-specific rules yet)_\n';
|
|
60
|
+
return head + body;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Extract authored prose from a `## Project Extensions` region — everything
|
|
65
|
+
* after the heading and its leading `>` boilerplate note. Returns '' when the
|
|
66
|
+
* region is empty, boilerplate-only, or already the pointer.
|
|
67
|
+
*/
|
|
68
|
+
function extractAuthoredProse(extensionsRegion) {
|
|
69
|
+
if (!extensionsRegion) return '';
|
|
70
|
+
if (extensionsRegion.includes(POINTER_SENTINEL)) return ''; // already a pointer
|
|
71
|
+
const lines = extensionsRegion.replace(/^\n+/, '').split('\n');
|
|
72
|
+
// drop the `## Project Extensions` heading line
|
|
73
|
+
if (lines[0] && lines[0].trim() === MARKER) lines.shift();
|
|
74
|
+
// drop leading blank + blockquote boilerplate lines
|
|
75
|
+
while (lines.length && (lines[0].trim() === '' || lines[0].trimStart().startsWith('>'))) {
|
|
76
|
+
lines.shift();
|
|
77
|
+
}
|
|
78
|
+
return lines.join('\n').trim();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Replace a file's `## Project Extensions` region with the pointer block. */
|
|
82
|
+
function pointerizeContent(content) {
|
|
83
|
+
const idx = content.indexOf('\n' + MARKER);
|
|
84
|
+
const managed = idx === -1 ? content.replace(/\s+$/, '') : content.slice(0, idx).replace(/\s+$/, '');
|
|
85
|
+
return managed + '\n\n' + renderPointerBlock();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** True when a file's extensions region is already the managed pointer. */
|
|
89
|
+
function isPointerized(content) {
|
|
90
|
+
return content.includes(POINTER_SENTINEL);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Migrate one instruction file's Project Extensions into project-rules.md and
|
|
95
|
+
* rewrite the section as the pointer. Migrate-never-drop, idempotent.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} instructionFile absolute path (CLAUDE.md / AGENTS.md)
|
|
98
|
+
* @param {string} specsDir absolute path to the project's specs/ dir
|
|
99
|
+
* @param {object} [opts] { dryRun, projectName }
|
|
100
|
+
* @returns {{ changed, appended, alreadyPointer, proseChars }}
|
|
101
|
+
*/
|
|
102
|
+
function migrateProjectExtensions(instructionFile, specsDir, opts = {}) {
|
|
103
|
+
const dryRun = Boolean(opts.dryRun);
|
|
104
|
+
const result = { changed: false, appended: false, alreadyPointer: false, proseChars: 0 };
|
|
105
|
+
if (!fs.existsSync(instructionFile)) return result;
|
|
106
|
+
|
|
107
|
+
const content = fs.readFileSync(instructionFile, 'utf8');
|
|
108
|
+
if (isPointerized(content)) { result.alreadyPointer = true; return result; }
|
|
109
|
+
|
|
110
|
+
const idx = content.indexOf('\n' + MARKER);
|
|
111
|
+
// No `## Project Extensions` marker → a pre-marker / custom file. Leave it to
|
|
112
|
+
// the standard upgrade's pre-marker path (backup .bak + append under marker),
|
|
113
|
+
// which preserves the user's content; never pointerize it here.
|
|
114
|
+
if (idx === -1) { result.noMarker = true; return result; }
|
|
115
|
+
const extensionsRegion = content.slice(idx);
|
|
116
|
+
const prose = extractAuthoredProse(extensionsRegion);
|
|
117
|
+
result.proseChars = prose.length;
|
|
118
|
+
|
|
119
|
+
const rulesPath = path.join(specsDir, PROJECT_RULES_REL);
|
|
120
|
+
|
|
121
|
+
if (prose) {
|
|
122
|
+
const label = path.basename(instructionFile);
|
|
123
|
+
const heading = `## Migrated from ${label}`;
|
|
124
|
+
let existing = fs.existsSync(rulesPath) ? fs.readFileSync(rulesPath, 'utf8') : null;
|
|
125
|
+
if (existing === null) {
|
|
126
|
+
existing = renderProjectRules(opts.projectName, '');
|
|
127
|
+
}
|
|
128
|
+
// Idempotent: only append if this file's migration block isn't already there.
|
|
129
|
+
if (!existing.includes(heading)) {
|
|
130
|
+
const block = `\n${heading}\n\n${prose}\n`;
|
|
131
|
+
const next = existing.replace(/\s+$/, '') + '\n' + block;
|
|
132
|
+
if (!dryRun) {
|
|
133
|
+
fs.mkdirSync(specsDir, { recursive: true });
|
|
134
|
+
fs.writeFileSync(rulesPath, next);
|
|
135
|
+
}
|
|
136
|
+
result.appended = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Rewrite the instruction file's extensions region as the pointer.
|
|
141
|
+
const pointerized = pointerizeContent(content);
|
|
142
|
+
if (pointerized !== content) {
|
|
143
|
+
if (!dryRun) fs.writeFileSync(instructionFile, pointerized);
|
|
144
|
+
result.changed = true;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The pointer must resolve: ensure project-rules.md exists (scaffold if the
|
|
148
|
+
// section had no prose to migrate), so we never leave a dangling pointer.
|
|
149
|
+
if (result.changed && !fs.existsSync(rulesPath) && !dryRun) {
|
|
150
|
+
fs.mkdirSync(specsDir, { recursive: true });
|
|
151
|
+
fs.writeFileSync(rulesPath, renderProjectRules(opts.projectName, ''));
|
|
152
|
+
result.scaffolded = true;
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
MARKER,
|
|
159
|
+
POINTER_SENTINEL,
|
|
160
|
+
PROJECT_RULES_REL,
|
|
161
|
+
renderPointerBlock,
|
|
162
|
+
renderProjectRules,
|
|
163
|
+
extractAuthoredProse,
|
|
164
|
+
pointerizeContent,
|
|
165
|
+
isPointerized,
|
|
166
|
+
migrateProjectExtensions,
|
|
167
|
+
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# Project Rules: <Project Name>
|
|
6
6
|
|
|
7
|
-
> Claude Code configuration for this project.
|
|
7
|
+
> Claude Code configuration for this momentum-managed project.
|
|
8
8
|
|
|
9
9
|
## Navigation (Where to Find Things)
|
|
10
10
|
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
## In-Session Task Tool
|
|
26
|
+
|
|
27
|
+
When a rule mentions tracking in-session progress (Rule 2), Claude Code's task tool is the built-in **TodoWrite** tool — in-session only; the durable record is `specs/phases/<phase>/tasks.md`.
|
|
28
|
+
|
|
25
29
|
## Autonomous Behaviors (Always-On Rules)
|
|
26
30
|
|
|
27
31
|
### Rule 1: Always Orient First
|
|
@@ -43,7 +47,7 @@ After completing ANY meaningful work, automatically update:
|
|
|
43
47
|
2. **`specs/status.md`** — if phase progress, blockers, or P0 items changed (touch only your own lane's row — Rule 15)
|
|
44
48
|
3. **`specs/changelog/YYYY-MM.md`** — log what changed (one line per change, append-only)
|
|
45
49
|
|
|
46
|
-
Use
|
|
50
|
+
Use your in-session task-tracking tool (named in the "In-Session Task Tool" note above) to track in-session task progress. Do NOT wait for the user to ask you to update tracking.
|
|
47
51
|
|
|
48
52
|
#### Why
|
|
49
53
|
Tracking debt compounds invisibly. A task list one day stale is recoverable; one week stale is fiction. Status drift is how phases silently lose direction.
|
|
@@ -61,7 +65,7 @@ Tracking debt compounds invisibly. A task list one day stale is recoverable; one
|
|
|
61
65
|
#### Anti-Rationalization Counters
|
|
62
66
|
|
|
63
67
|
- "It's faster to do the work first and track at the end" — wrong: reconstruction takes 2-3× longer than real-time logging.
|
|
64
|
-
- "
|
|
68
|
+
- "Your in-session task-tracking tool is enough" — it is in-session only; `tasks.md` is the durable record.
|
|
65
69
|
- "Mid-task tracking interrupts flow" — a one-line update costs <30s; reconstructing a day later costs 30 minutes.
|
|
66
70
|
|
|
67
71
|
### Rule 3: Auto-Track Discoveries
|
|
@@ -484,6 +488,9 @@ Use `infra:` for CI, build, deploy, tooling, and release-pipeline changes that d
|
|
|
484
488
|
|
|
485
489
|
## Project Extensions
|
|
486
490
|
|
|
487
|
-
|
|
488
|
-
>
|
|
489
|
-
>
|
|
491
|
+
<!-- momentum:project-rules-pointer -->
|
|
492
|
+
> **Project-specific rules live in `specs/project-rules.md`** — read it now.
|
|
493
|
+
> Session-start self-audits, project constraints, and any project-specific
|
|
494
|
+
> guidance are there, shared identically by every agent (ADR-0010). This
|
|
495
|
+
> section is a momentum-managed pointer; edit `specs/project-rules.md`, not
|
|
496
|
+
> this file.
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "TASK_TOOL": "the native **task list** artifact (task.md)", "TASK_TOOL_NAME": "The task-list artifact" }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "TASK_TOOL": "the built-in **TodoWrite** tool", "TASK_TOOL_NAME": "TodoWrite" }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "TASK_TOOL": "the built-in **plan** tool", "TASK_TOOL_NAME": "The plan tool" }
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "TASK_TOOL": "the built-in **task** tool (subagent fan-out)", "TASK_TOOL_NAME": "The task tool" }
|