@open-agent-toolkit/cli 0.1.63 → 0.1.65
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/assets/agents/oat-phase-implementer.md +9 -1
- package/assets/agents/oat-reviewer.md +8 -1
- package/assets/docs/cli-utilities/backlog-lifecycle.md +2 -2
- package/assets/docs/cli-utilities/config-and-local-state.md +20 -6
- package/assets/docs/cli-utilities/tool-packs.md +1 -0
- package/assets/docs/cli-utilities/workflow-gates.md +3 -1
- package/assets/docs/contributing/code.md +18 -0
- package/assets/docs/contributing/skills.md +22 -0
- package/assets/docs/workflows/projects/artifacts.md +30 -0
- package/assets/docs/workflows/projects/dispatch-ceiling.md +13 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-pjm-add-backlog-item/SKILL.md +17 -72
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
- package/assets/skills/oat-project-document/SKILL.md +8 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-plan-writing/SKILL.md +48 -12
- package/assets/skills/oat-project-pr-final/SKILL.md +9 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-quick-start/SKILL.md +8 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-review-provide/SKILL.md +9 -1
- package/assets/skills/oat-project-review-receive/SKILL.md +9 -1
- package/assets/skills/oat-project-summary/SKILL.md +16 -4
- package/assets/templates/plan.md +2 -0
- package/dist/commands/backlog/archive.d.ts.map +1 -1
- package/dist/commands/backlog/archive.js +5 -5
- package/dist/commands/backlog/index.d.ts +4 -0
- package/dist/commands/backlog/index.d.ts.map +1 -1
- package/dist/commands/backlog/index.js +55 -0
- package/dist/commands/backlog/new.d.ts +27 -0
- package/dist/commands/backlog/new.d.ts.map +1 -0
- package/dist/commands/backlog/new.js +158 -0
- package/dist/commands/backlog/regenerate-index.d.ts.map +1 -1
- package/dist/commands/backlog/regenerate-index.js +42 -3
- package/dist/commands/decision/index.d.ts.map +1 -1
- package/dist/commands/decision/index.js +4 -0
- package/dist/commands/decision/new.d.ts +2 -0
- package/dist/commands/decision/new.d.ts.map +1 -1
- package/dist/commands/decision/new.js +4 -2
- package/dist/commands/doctor/index.d.ts +1 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +3 -0
- package/dist/commands/doctor/stale-invocations.d.ts +3 -0
- package/dist/commands/doctor/stale-invocations.d.ts.map +1 -0
- package/dist/commands/doctor/stale-invocations.js +173 -0
- package/dist/commands/gate/index.d.ts +1 -0
- package/dist/commands/gate/index.d.ts.map +1 -1
- package/dist/commands/gate/index.js +9 -2
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +24 -9
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +22 -12
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-review-provide
|
|
3
|
-
version: 1.3.
|
|
3
|
+
version: 1.3.16
|
|
4
4
|
description: Use when the user explicitly asks to review an OAT project — e.g. "review project", "review the project", "run project review", or confirms a previously offered review. Do NOT auto-invoke on completed work alone. Resolves a project review scope and offers before running.
|
|
5
5
|
disable-model-invocation: false
|
|
6
6
|
user-invocable: true
|
|
@@ -92,6 +92,14 @@ Run the `oat-project-review-provide` skill and it will:
|
|
|
92
92
|
2. Ask scope (task/phase/final/range)
|
|
93
93
|
3. Confirm before running
|
|
94
94
|
|
|
95
|
+
## Artifact Hygiene
|
|
96
|
+
|
|
97
|
+
Artifact hygiene contract: Before finishing or committing, format every file you created or edited. Use the concrete write/fix formatting command supplied by the governing plan, task, or brief. If none is usable, discover the repository's documented write/fix command from applicable `AGENTS.md`/`CLAUDE.md` instructions and relevant package manifests; do not infer or hardcode a formatter. Prefer a file-scoped invocation when supported, and avoid rewriting unrelated files. If no command is discoverable, warn once with `no format command discovered in repo instructions; skipping`, then continue.
|
|
98
|
+
|
|
99
|
+
After formatting, run only repository checks relevant to the files changed;
|
|
100
|
+
writing prose artifacts or review bookkeeping does not imply unrelated full
|
|
101
|
+
test suites.
|
|
102
|
+
|
|
95
103
|
## Process
|
|
96
104
|
|
|
97
105
|
### Step 0: Resolve Project or Explicit Review Target
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-review-receive
|
|
3
|
-
version: 1.5.
|
|
3
|
+
version: 1.5.7
|
|
4
4
|
description: Use when the user explicitly asks to receive review findings for an OAT project — e.g. "receive review", "process review", "process the project review", or confirms a previously offered review-receive step. Do NOT auto-invoke merely because a review file exists. Resolves the latest review and offers before acting.
|
|
5
5
|
disable-model-invocation: false
|
|
6
6
|
user-invocable: true
|
|
@@ -70,6 +70,14 @@ When executing this skill, provide lightweight progress feedback so the user can
|
|
|
70
70
|
- Routing to oat-project-implement
|
|
71
71
|
- For `artifact` reviews: updating reviewed artifact files directly after user confirmation
|
|
72
72
|
|
|
73
|
+
## Artifact Hygiene
|
|
74
|
+
|
|
75
|
+
Artifact hygiene contract: Before finishing or committing, format every file you created or edited. Use the concrete write/fix formatting command supplied by the governing plan, task, or brief. If none is usable, discover the repository's documented write/fix command from applicable `AGENTS.md`/`CLAUDE.md` instructions and relevant package manifests; do not infer or hardcode a formatter. Prefer a file-scoped invocation when supported, and avoid rewriting unrelated files. If no command is discoverable, warn once with `no format command discovered in repo instructions; skipping`, then continue.
|
|
76
|
+
|
|
77
|
+
After formatting, run only repository checks relevant to the files changed;
|
|
78
|
+
writing prose artifacts or review bookkeeping does not imply unrelated full
|
|
79
|
+
test suites.
|
|
80
|
+
|
|
73
81
|
## Process
|
|
74
82
|
|
|
75
83
|
### Step 0: Resolve Active Project
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-summary
|
|
3
|
-
version: 1.3.
|
|
3
|
+
version: 1.3.2
|
|
4
4
|
description: Use when the user requests or confirms summarizing an active OAT project — e.g. "summarize the project", "generate the summary", "run oat-project-summary", or confirms a previously offered summary run. Do NOT auto-invoke when implementation completes. Generates summary.md from project artifacts as institutional memory.
|
|
5
5
|
disable-model-invocation: false
|
|
6
6
|
user-invocable: true
|
|
@@ -69,6 +69,13 @@ If you catch yourself:
|
|
|
69
69
|
2. Return to summary generation
|
|
70
70
|
3. Keep content grounded in artifacts
|
|
71
71
|
|
|
72
|
+
## Artifact Hygiene
|
|
73
|
+
|
|
74
|
+
Artifact hygiene contract: Before finishing or committing, format every file you created or edited. Use the concrete write/fix formatting command supplied by the governing plan, task, or brief. If none is usable, discover the repository's documented write/fix command from applicable `AGENTS.md`/`CLAUDE.md` instructions and relevant package manifests; do not infer or hardcode a formatter. Prefer a file-scoped invocation when supported, and avoid rewriting unrelated files. If no command is discoverable, warn once with `no format command discovered in repo instructions; skipping`, then continue.
|
|
75
|
+
|
|
76
|
+
After formatting, run only repository checks relevant to the files changed;
|
|
77
|
+
writing prose artifacts does not imply unrelated full test suites.
|
|
78
|
+
|
|
72
79
|
## Process
|
|
73
80
|
|
|
74
81
|
### Step 0: Resolve Active Project
|
|
@@ -271,7 +278,12 @@ test -f .oat/repo/reference/decisions/index.md || oat decision init
|
|
|
271
278
|
|
|
272
279
|
**6.4 — Idempotent, date-independent promotion (critical).** For each decision in `## Key Decisions`:
|
|
273
280
|
|
|
274
|
-
1. **Derive title +
|
|
281
|
+
1. **Derive title + complete sections.** Ground every value in the Key Decision and its project artifacts:
|
|
282
|
+
- The bold lead-in / first clause becomes the **title** (a short noun phrase).
|
|
283
|
+
- The problem, constraint, or motivating rationale becomes **context**.
|
|
284
|
+
- The choice that was made becomes the **decision**.
|
|
285
|
+
- The resulting tradeoffs, follow-on effects, or operational implications become **consequences**.
|
|
286
|
+
Each section must contain concrete grounded prose; do not pass placeholder content.
|
|
275
287
|
2. **Compute the slug the CLI would use.** The CLI generates the record ID as `DR-<YYMMDD>-<slug>`, where `<slug>` is the lowercased, ASCII-folded, hyphen-collapsed form of the title, capped at 30 characters at the last whole-word boundary with trailing stop-words (`a, an, the, of, for, and, to, in, on, as, with`) trimmed (the same slug rule the CLI applies). Compute that `<slug>` for the title.
|
|
276
288
|
3. **Dedup on the exact slug, ignoring only the date prefix.** A record ID is `DR-<YYMMDD>-<slug>`, where the date is exactly six digits. Check whether a record for this slug already exists by stripping that fixed `DR-<6 digits>-` prefix from existing record IDs and comparing the remaining slug for **exact equality**. Anchor the date to exactly six characters so the slug must match in full:
|
|
277
289
|
|
|
@@ -287,10 +299,10 @@ test -f .oat/repo/reference/decisions/index.md || oat decision init
|
|
|
287
299
|
- Otherwise → create it:
|
|
288
300
|
|
|
289
301
|
```bash
|
|
290
|
-
oat decision new "<title>" --status accepted --context "<
|
|
302
|
+
oat decision new "<title>" --status accepted --context "<context>" --decision "<decision>" --consequences "<consequences>"
|
|
291
303
|
```
|
|
292
304
|
|
|
293
|
-
The command generates the deterministic `DR-YYMMDD-slug` ID,
|
|
305
|
+
The command generates the deterministic `DR-YYMMDD-slug` ID, fills every decision body section, and regenerates the managed index automatically — do not hand-edit `index.md`. Optionally pass `--created-at "<project completion date>"` when a project completion date is available, so the record's date reflects when the decision was made.
|
|
294
306
|
|
|
295
307
|
Because of the date-independent slug dedup, this step is **safe to run every time `summary.md` is (re)generated** — including the pr-final refresh and revision re-runs — without ever creating duplicate decision records. Already-promoted decisions are skipped; only genuinely new Key Decisions become new records.
|
|
296
308
|
|
package/assets/templates/plan.md
CHANGED
|
@@ -65,6 +65,8 @@ Codex effort values are preferred controls. `oat-project-implement` caps them wh
|
|
|
65
65
|
|
|
66
66
|
---
|
|
67
67
|
|
|
68
|
+
RED/GREEN/Refactor is the recommended default where work is testable, not a validator requirement. Other task-body shapes, including non-TDD shapes, are allowed when appropriate, provided the plan preserves stable `pNN-tNN` IDs, per-task verification, and atomic commits.
|
|
69
|
+
|
|
68
70
|
## Phase 1: {Phase Name}
|
|
69
71
|
|
|
70
72
|
### Task p01-t01: {Task Name}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/archive.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,iBAAiB,EAGvB,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/archive.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,iBAAiB,EAGvB,MAAM,sBAAsB,CAAC;AAmB9B,mDAAmD;AACnD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IAC5B,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IACrD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,CAAC,GAAG,CAAK;CAKjD;AAkID;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAqHnC"}
|
|
@@ -8,7 +8,6 @@ import { regenerateBacklogIndex } from './regenerate-index.js';
|
|
|
8
8
|
import { BACKLOG_ITEM_STATUSES, extractBacklogStatus, isValidBacklogStatus, } from './shared/item-status.js';
|
|
9
9
|
const execFileAsync = promisify(execFile);
|
|
10
10
|
const COMPLETED_HEADING = '## Completed Items';
|
|
11
|
-
const TODO_SUMMARY = 'TODO: summarize outcome';
|
|
12
11
|
const STARTER_COMPLETED = [
|
|
13
12
|
'# OAT Backlog Completed',
|
|
14
13
|
'',
|
|
@@ -178,6 +177,10 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
|
|
|
178
177
|
throw new BacklogArchiveError(`Backlog item ${itemsPath} has invalid status "${currentStatus ?? ''}". Valid statuses: ${BACKLOG_ITEM_STATUSES.join(', ')}. Fix: correct the \`status\` field manually, then re-run \`oat backlog archive ${id}\`.`);
|
|
179
178
|
}
|
|
180
179
|
const targetStatus = options.wontDo ? 'wont_do' : 'closed';
|
|
180
|
+
const summary = options.summary?.trim() ?? '';
|
|
181
|
+
if (targetStatus === 'closed' && summary.length === 0) {
|
|
182
|
+
throw new BacklogArchiveError(`Closing backlog item ${id} requires a non-empty outcome summary. Fix: rerun with \`--summary "<outcome>"\`.`);
|
|
183
|
+
}
|
|
181
184
|
const now = options.now ?? new Date();
|
|
182
185
|
const updatedIso = now.toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
183
186
|
const entryDate = updatedIso.slice(0, 10);
|
|
@@ -188,11 +191,8 @@ export async function archiveBacklogItem(backlogRoot, id, options = {}) {
|
|
|
188
191
|
// 5. completed.md entry — always for `closed`; only with a summary for
|
|
189
192
|
// `wont_do`.
|
|
190
193
|
let completedEntry = 'skipped';
|
|
191
|
-
const shouldWriteEntry = targetStatus === 'closed' ||
|
|
194
|
+
const shouldWriteEntry = targetStatus === 'closed' || summary.length > 0;
|
|
192
195
|
if (shouldWriteEntry) {
|
|
193
|
-
const summary = options.summary?.trim()
|
|
194
|
-
? options.summary.trim()
|
|
195
|
-
: TODO_SUMMARY;
|
|
196
196
|
const entryLine = `- ${entryDate} — ${id} — ${title} — ${summary}`;
|
|
197
197
|
const completedPath = join(backlogRoot, 'completed.md');
|
|
198
198
|
let scaffoldedFile = false;
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { buildCommandContext } from '../../app/command-context.js';
|
|
2
|
+
import { resolveAssetsRoot } from '../../fs/assets.js';
|
|
2
3
|
import { resolveProjectRoot } from '../../fs/paths.js';
|
|
3
4
|
import { Command } from 'commander';
|
|
4
5
|
import { archiveBacklogItem } from './archive.js';
|
|
5
6
|
import { initializeBacklog } from './init.js';
|
|
7
|
+
import { createBacklogItem } from './new.js';
|
|
6
8
|
import { regenerateBacklogIndex } from './regenerate-index.js';
|
|
7
9
|
interface BacklogCommandDependencies {
|
|
8
10
|
buildCommandContext: typeof buildCommandContext;
|
|
9
11
|
resolveProjectRoot: typeof resolveProjectRoot;
|
|
12
|
+
resolveAssetsRoot: typeof resolveAssetsRoot;
|
|
10
13
|
initializeBacklog: typeof initializeBacklog;
|
|
11
14
|
regenerateBacklogIndex: typeof regenerateBacklogIndex;
|
|
15
|
+
createBacklogItem: typeof createBacklogItem;
|
|
12
16
|
archiveBacklogItem: typeof archiveBacklogItem;
|
|
13
17
|
pathExists: (path: string) => Promise<boolean>;
|
|
14
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAuB,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAuB,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AA8B5D,UAAU,0BAA0B;IAClC,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AA4CD,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAClD,OAAO,CAqQT"}
|
|
@@ -2,10 +2,12 @@ import { access } from 'node:fs/promises';
|
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { buildCommandContext } from '../../app/command-context.js';
|
|
4
4
|
import { readGlobalOptions } from '../shared/shared.utils.js';
|
|
5
|
+
import { resolveAssetsRoot } from '../../fs/assets.js';
|
|
5
6
|
import { resolveProjectRoot } from '../../fs/paths.js';
|
|
6
7
|
import { Command } from 'commander';
|
|
7
8
|
import { archiveBacklogItem, BacklogArchiveError } from './archive.js';
|
|
8
9
|
import { initializeBacklog } from './init.js';
|
|
10
|
+
import { createBacklogItem } from './new.js';
|
|
9
11
|
import { regenerateBacklogIndex } from './regenerate-index.js';
|
|
10
12
|
import { generateBacklogId } from './shared/generate-id.js';
|
|
11
13
|
async function pathExistsDefault(path) {
|
|
@@ -26,8 +28,10 @@ async function pathExistsDefault(path) {
|
|
|
26
28
|
const DEFAULT_DEPENDENCIES = {
|
|
27
29
|
buildCommandContext,
|
|
28
30
|
resolveProjectRoot,
|
|
31
|
+
resolveAssetsRoot,
|
|
29
32
|
initializeBacklog,
|
|
30
33
|
regenerateBacklogIndex,
|
|
34
|
+
createBacklogItem,
|
|
31
35
|
archiveBacklogItem,
|
|
32
36
|
pathExists: pathExistsDefault,
|
|
33
37
|
};
|
|
@@ -60,6 +64,57 @@ export function createBacklogCommand(overrides = {}) {
|
|
|
60
64
|
}
|
|
61
65
|
process.exitCode = 0;
|
|
62
66
|
});
|
|
67
|
+
cmd
|
|
68
|
+
.command('new')
|
|
69
|
+
.description('Create a new file-backed backlog item')
|
|
70
|
+
.argument('<title>', 'Backlog item title')
|
|
71
|
+
.option('--priority <priority>', 'Item priority', 'medium')
|
|
72
|
+
.option('--scope <scope>', 'Item scope', 'task')
|
|
73
|
+
.option('--scope-estimate <size>', 'Optional scope estimate (XS|S|M|L|XL|XXL)')
|
|
74
|
+
.option('--labels <labels>', 'Comma-delimited labels', (value) => value.split(','))
|
|
75
|
+
.option('--description <text>', 'Initial description body text')
|
|
76
|
+
.option('--backlog-root <path>', 'Backlog root directory (defaults to .oat/repo/pjm/backlog)')
|
|
77
|
+
.action(async (title, options, command) => {
|
|
78
|
+
const context = dependencies.buildCommandContext(readGlobalOptions(command));
|
|
79
|
+
try {
|
|
80
|
+
const projectRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
81
|
+
const backlogRoot = await resolveBacklogRoot(context, options.backlogRoot, dependencies);
|
|
82
|
+
const assetsRoot = await dependencies.resolveAssetsRoot();
|
|
83
|
+
const result = await dependencies.createBacklogItem({
|
|
84
|
+
backlogRoot,
|
|
85
|
+
assetsRoot,
|
|
86
|
+
templatesRoot: resolve(projectRoot, '.oat', 'templates'),
|
|
87
|
+
title,
|
|
88
|
+
priority: options.priority,
|
|
89
|
+
scope: options.scope,
|
|
90
|
+
scopeEstimate: options.scopeEstimate,
|
|
91
|
+
labels: options.labels,
|
|
92
|
+
description: options.description,
|
|
93
|
+
});
|
|
94
|
+
if (context.json) {
|
|
95
|
+
context.logger.json({ status: 'ok', ...result });
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
for (const warning of result.index.warnings) {
|
|
99
|
+
context.logger.warn(warning);
|
|
100
|
+
}
|
|
101
|
+
context.logger.info(`Created backlog item ${result.id}`);
|
|
102
|
+
context.logger.info(`Wrote ${result.filePath}`);
|
|
103
|
+
context.logger.info(`Regenerated backlog index with ${result.index.itemCount} ${result.index.itemCount === 1 ? 'item' : 'items'}.`);
|
|
104
|
+
}
|
|
105
|
+
process.exitCode = 0;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
109
|
+
if (context.json) {
|
|
110
|
+
context.logger.json({ status: 'error', message });
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
context.logger.error(message);
|
|
114
|
+
}
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
63
118
|
cmd
|
|
64
119
|
.command('regenerate-index')
|
|
65
120
|
.description('Regenerate the managed backlog index table')
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { initializeBacklog } from './init.js';
|
|
2
|
+
import { regenerateBacklogIndex, type RegenerateBacklogIndexResult } from './regenerate-index.js';
|
|
3
|
+
export interface CreateBacklogItemOptions {
|
|
4
|
+
backlogRoot: string;
|
|
5
|
+
assetsRoot: string;
|
|
6
|
+
templatesRoot?: string;
|
|
7
|
+
title: string;
|
|
8
|
+
priority?: string;
|
|
9
|
+
scope?: string;
|
|
10
|
+
scopeEstimate?: string;
|
|
11
|
+
labels?: string[];
|
|
12
|
+
description?: string;
|
|
13
|
+
createdAt?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CreateBacklogItemResult {
|
|
16
|
+
id: string;
|
|
17
|
+
backlogRoot: string;
|
|
18
|
+
filePath: string;
|
|
19
|
+
templatePath: string;
|
|
20
|
+
index: RegenerateBacklogIndexResult;
|
|
21
|
+
}
|
|
22
|
+
export interface CreateBacklogItemDependencies {
|
|
23
|
+
initializeBacklog: typeof initializeBacklog;
|
|
24
|
+
regenerateBacklogIndex: typeof regenerateBacklogIndex;
|
|
25
|
+
}
|
|
26
|
+
export declare function createBacklogItem(options: CreateBacklogItemOptions, overrides?: Partial<CreateBacklogItemDependencies>): Promise<CreateBacklogItemResult>;
|
|
27
|
+
//# sourceMappingURL=new.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/new.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EACL,sBAAsB,EACtB,KAAK,4BAA4B,EAClC,MAAM,oBAAoB,CAAC;AAS5B,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,4BAA4B,CAAC;CACrC;AAED,MAAM,WAAW,6BAA6B;IAC5C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AAuLD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,SAAS,GAAE,OAAO,CAAC,6BAA6B,CAAM,GACrD,OAAO,CAAC,uBAAuB,CAAC,CAqDlC"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { access, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { stripTemplateFrontmatter } from '../shared/strip-template-frontmatter.js';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
import { initializeBacklog } from './init.js';
|
|
6
|
+
import { regenerateBacklogIndex, } from './regenerate-index.js';
|
|
7
|
+
import { generateBacklogId } from './shared/generate-id.js';
|
|
8
|
+
const PRIORITIES = ['urgent', 'high', 'medium', 'low', 'none'];
|
|
9
|
+
const SCOPES = ['idea', 'task', 'feature', 'initiative'];
|
|
10
|
+
const SCOPE_ESTIMATES = ['XS', 'S', 'M', 'L', 'XL', 'XXL'];
|
|
11
|
+
const DESCRIPTION_PLACEHOLDER = '{Describe the problem, request, or capability tracked by this backlog item.}';
|
|
12
|
+
const DEFAULT_DEPENDENCIES = {
|
|
13
|
+
initializeBacklog,
|
|
14
|
+
regenerateBacklogIndex,
|
|
15
|
+
};
|
|
16
|
+
async function pathExists(path) {
|
|
17
|
+
try {
|
|
18
|
+
await access(path);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const code = error && typeof error === 'object' && 'code' in error
|
|
23
|
+
? String(error.code)
|
|
24
|
+
: null;
|
|
25
|
+
if (code !== 'ENOENT') {
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function resolveBacklogTemplate(assetsRoot, templatesRoot) {
|
|
32
|
+
const candidates = [
|
|
33
|
+
...(templatesRoot ? [join(templatesRoot, 'backlog-item.md')] : []),
|
|
34
|
+
join(assetsRoot, 'templates', 'backlog-item.md'),
|
|
35
|
+
];
|
|
36
|
+
for (const path of candidates) {
|
|
37
|
+
try {
|
|
38
|
+
return { content: await readFile(path, 'utf8'), path };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const code = error && typeof error === 'object' && 'code' in error
|
|
42
|
+
? String(error.code)
|
|
43
|
+
: null;
|
|
44
|
+
if (code !== 'ENOENT') {
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw new Error('Template backlog-item.md was not found in repo-local templates or bundled assets.');
|
|
50
|
+
}
|
|
51
|
+
function normalizeChoice(field, value, fallback, allowed, transform) {
|
|
52
|
+
const normalized = transform(value ?? fallback);
|
|
53
|
+
if (!allowed.includes(normalized)) {
|
|
54
|
+
throw new Error(`Invalid backlog ${field} "${value}". Valid values: ${allowed.join(', ')}.`);
|
|
55
|
+
}
|
|
56
|
+
return normalized;
|
|
57
|
+
}
|
|
58
|
+
function normalizeInputs(options) {
|
|
59
|
+
const title = options.title.trim();
|
|
60
|
+
if (!title ||
|
|
61
|
+
title.includes('\r') ||
|
|
62
|
+
title.includes('\n') ||
|
|
63
|
+
title.includes('\0')) {
|
|
64
|
+
throw new Error('Backlog title must be a non-empty single line.');
|
|
65
|
+
}
|
|
66
|
+
const date = new Date(options.createdAt ?? new Date().toISOString());
|
|
67
|
+
if (Number.isNaN(date.getTime())) {
|
|
68
|
+
throw new Error(`Invalid backlog creation timestamp: ${options.createdAt}`);
|
|
69
|
+
}
|
|
70
|
+
const priority = normalizeChoice('priority', options.priority, 'medium', PRIORITIES, (value) => value.trim().toLowerCase());
|
|
71
|
+
const scope = normalizeChoice('scope', options.scope, 'task', SCOPES, (value) => value.trim().toLowerCase());
|
|
72
|
+
const scopeEstimate = options.scopeEstimate === undefined
|
|
73
|
+
? null
|
|
74
|
+
: normalizeChoice('scope estimate', options.scopeEstimate, '', SCOPE_ESTIMATES, (value) => value.trim().toUpperCase());
|
|
75
|
+
const labels = (options.labels ?? []).map((label) => label.trim());
|
|
76
|
+
if (labels.some((label) => !label ||
|
|
77
|
+
label.includes('\r') ||
|
|
78
|
+
label.includes('\n') ||
|
|
79
|
+
label.includes('\0'))) {
|
|
80
|
+
throw new Error('Backlog labels must be non-empty single-line values.');
|
|
81
|
+
}
|
|
82
|
+
if (options.description !== undefined &&
|
|
83
|
+
(!options.description.trim() || options.description.includes('\0'))) {
|
|
84
|
+
throw new Error('Backlog description must contain non-empty text.');
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
title,
|
|
88
|
+
timestamp: date.toISOString(),
|
|
89
|
+
priority,
|
|
90
|
+
scope,
|
|
91
|
+
scopeEstimate,
|
|
92
|
+
labels,
|
|
93
|
+
description: options.description,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function renderBacklogItem(template, values) {
|
|
97
|
+
let body = stripTemplateFrontmatter(template).replace(/^\r?\n+/, '');
|
|
98
|
+
if (values.description !== undefined) {
|
|
99
|
+
body = body.replace(DESCRIPTION_PLACEHOLDER, () => values.description);
|
|
100
|
+
}
|
|
101
|
+
const frontmatter = YAML.stringify({
|
|
102
|
+
id: values.id,
|
|
103
|
+
title: values.title,
|
|
104
|
+
status: 'open',
|
|
105
|
+
priority: values.priority,
|
|
106
|
+
scope: values.scope,
|
|
107
|
+
scope_estimate: values.scopeEstimate,
|
|
108
|
+
labels: values.labels,
|
|
109
|
+
assignee: null,
|
|
110
|
+
created: values.timestamp,
|
|
111
|
+
updated: values.timestamp,
|
|
112
|
+
associated_issues: [],
|
|
113
|
+
external_plans: [],
|
|
114
|
+
}).trimEnd();
|
|
115
|
+
return `---\n${frontmatter}\n---\n\n${body.trimEnd()}\n`;
|
|
116
|
+
}
|
|
117
|
+
export async function createBacklogItem(options, overrides = {}) {
|
|
118
|
+
const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
|
|
119
|
+
const values = normalizeInputs(options);
|
|
120
|
+
const id = generateBacklogId(values.title, values.timestamp);
|
|
121
|
+
const filePath = join(options.backlogRoot, 'items', `${id}.md`);
|
|
122
|
+
const archivedPath = join(options.backlogRoot, 'archived', `${id}.md`);
|
|
123
|
+
if ((await pathExists(filePath)) || (await pathExists(archivedPath))) {
|
|
124
|
+
throw new Error(`Backlog item ${id} already exists. Use a more specific title to disambiguate.`);
|
|
125
|
+
}
|
|
126
|
+
const template = await resolveBacklogTemplate(options.assetsRoot, options.templatesRoot);
|
|
127
|
+
const content = renderBacklogItem(template.content, { id, ...values });
|
|
128
|
+
await dependencies.initializeBacklog(options.backlogRoot);
|
|
129
|
+
const indexPath = join(options.backlogRoot, 'index.md');
|
|
130
|
+
const originalIndex = await readFile(indexPath, 'utf8');
|
|
131
|
+
await writeFile(filePath, content, { encoding: 'utf8', flag: 'wx' });
|
|
132
|
+
let index;
|
|
133
|
+
try {
|
|
134
|
+
index = await dependencies.regenerateBacklogIndex(options.backlogRoot);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const rollback = await Promise.allSettled([
|
|
138
|
+
rm(filePath),
|
|
139
|
+
writeFile(indexPath, originalIndex, 'utf8'),
|
|
140
|
+
]);
|
|
141
|
+
const rollbackErrors = rollback
|
|
142
|
+
.filter((result) => result.status === 'rejected')
|
|
143
|
+
.map((result) => result.reason);
|
|
144
|
+
if (rollbackErrors.length > 0) {
|
|
145
|
+
const rollbackError = new AggregateError([error, ...rollbackErrors], `Backlog item ${id} creation failed and rollback was incomplete.`);
|
|
146
|
+
rollbackError.cause = error;
|
|
147
|
+
throw rollbackError;
|
|
148
|
+
}
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
id,
|
|
153
|
+
backlogRoot: options.backlogRoot,
|
|
154
|
+
filePath,
|
|
155
|
+
templatePath: template.path,
|
|
156
|
+
index,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"regenerate-index.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/regenerate-index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"regenerate-index.d.ts","sourceRoot":"","sources":["../../../src/commands/backlog/regenerate-index.ts"],"names":[],"mappings":"AAgJA,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,4BAA4B,CAAC,CA6DvC"}
|
|
@@ -52,9 +52,29 @@ function compareItems(a, b) {
|
|
|
52
52
|
}
|
|
53
53
|
return 0;
|
|
54
54
|
}
|
|
55
|
+
function encodeMarkdownTableCell(value) {
|
|
56
|
+
return value
|
|
57
|
+
.replaceAll('&', '&')
|
|
58
|
+
.replaceAll('\\', '\\\\')
|
|
59
|
+
.replaceAll('|', '\\|')
|
|
60
|
+
.replaceAll('<', '<')
|
|
61
|
+
.replaceAll('>', '>')
|
|
62
|
+
.replaceAll('\r', ' ')
|
|
63
|
+
.replaceAll('\n', ' ');
|
|
64
|
+
}
|
|
55
65
|
function renderManagedSection(items) {
|
|
56
66
|
const rows = items.length > 0
|
|
57
|
-
? items.map((item) =>
|
|
67
|
+
? items.map((item) => {
|
|
68
|
+
const cells = [
|
|
69
|
+
item.id,
|
|
70
|
+
item.title,
|
|
71
|
+
item.status,
|
|
72
|
+
item.priority,
|
|
73
|
+
item.scope,
|
|
74
|
+
item.scopeEstimate,
|
|
75
|
+
].map(encodeMarkdownTableCell);
|
|
76
|
+
return `| ${cells.join(' | ')} |`;
|
|
77
|
+
})
|
|
58
78
|
: ['| _No backlog items yet_ | - | - | - | - | - |'];
|
|
59
79
|
return [
|
|
60
80
|
INDEX_START,
|
|
@@ -64,6 +84,23 @@ function renderManagedSection(items) {
|
|
|
64
84
|
INDEX_END,
|
|
65
85
|
].join('\n');
|
|
66
86
|
}
|
|
87
|
+
function findStandaloneMarker(content, marker, fromIndex = 0) {
|
|
88
|
+
let lineStart = 0;
|
|
89
|
+
while (lineStart <= content.length) {
|
|
90
|
+
const lineEnd = content.indexOf('\n', lineStart);
|
|
91
|
+
const contentEnd = lineEnd === -1 ? content.length : lineEnd;
|
|
92
|
+
const rawLine = content.slice(lineStart, contentEnd);
|
|
93
|
+
const line = rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine;
|
|
94
|
+
if (lineStart >= fromIndex && line === marker) {
|
|
95
|
+
return lineStart;
|
|
96
|
+
}
|
|
97
|
+
if (lineEnd === -1) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
lineStart = lineEnd + 1;
|
|
101
|
+
}
|
|
102
|
+
return -1;
|
|
103
|
+
}
|
|
67
104
|
export async function regenerateBacklogIndex(backlogRoot) {
|
|
68
105
|
const itemsDir = join(backlogRoot, 'items');
|
|
69
106
|
const indexPath = join(backlogRoot, 'index.md');
|
|
@@ -85,8 +122,10 @@ export async function regenerateBacklogIndex(backlogRoot) {
|
|
|
85
122
|
}
|
|
86
123
|
items.sort(compareItems);
|
|
87
124
|
const content = await readFile(indexPath, 'utf8');
|
|
88
|
-
const startIndex = content
|
|
89
|
-
const endIndex =
|
|
125
|
+
const startIndex = findStandaloneMarker(content, INDEX_START);
|
|
126
|
+
const endIndex = startIndex === -1
|
|
127
|
+
? -1
|
|
128
|
+
: findStandaloneMarker(content, INDEX_END, startIndex + INDEX_START.length);
|
|
90
129
|
if (startIndex === -1 || endIndex === -1) {
|
|
91
130
|
throw new Error(`Managed backlog index markers missing in ${indexPath}. Expected the exact marker pair:\n${INDEX_START}\n${INDEX_END}\nRun \`oat backlog init\` if the backlog scaffold is missing, or restore those exact markers in \`backlog/index.md\` before rerunning \`oat backlog regenerate-index\`.`);
|
|
92
131
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/decision/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/decision/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,sBAAsB,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,yBAAyB,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAyB7D,UAAU,2BAA2B;IACnC,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,yBAAyB,EAAE,OAAO,yBAAyB,CAAC;IAC5D,uBAAuB,EAAE,OAAO,uBAAuB,CAAC;IACxD,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAClD,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;CACvD;AA8CD,wBAAgB,qBAAqB,CACnC,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAmMT"}
|
|
@@ -102,6 +102,8 @@ export function createDecisionCommand(overrides = {}) {
|
|
|
102
102
|
.option('--decisions-root <path>', 'Decisions root directory (defaults to .oat/repo/reference/decisions)')
|
|
103
103
|
.option('--status <status>', 'Decision status', 'proposed')
|
|
104
104
|
.option('--context <text>', 'Initial context body text')
|
|
105
|
+
.option('--decision <text>', 'Initial decision body text')
|
|
106
|
+
.option('--consequences <text>', 'Initial consequences body text')
|
|
105
107
|
.option('--created-at <timestamp>', 'Creation timestamp seed for reproducible ID generation')
|
|
106
108
|
.action(async (title, options, command) => {
|
|
107
109
|
const context = dependencies.buildCommandContext(readGlobalOptions(command));
|
|
@@ -116,6 +118,8 @@ export function createDecisionCommand(overrides = {}) {
|
|
|
116
118
|
title,
|
|
117
119
|
status: options.status,
|
|
118
120
|
context: options.context,
|
|
121
|
+
decision: options.decision,
|
|
122
|
+
consequences: options.consequences,
|
|
119
123
|
createdAt: options.createdAt,
|
|
120
124
|
});
|
|
121
125
|
if (context.json) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/decision/new.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/decision/new.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA4HD,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CAgDrC"}
|
|
@@ -68,8 +68,8 @@ function renderDecisionRecord(template, options) {
|
|
|
68
68
|
date: options.date,
|
|
69
69
|
status: options.status,
|
|
70
70
|
context: options.context,
|
|
71
|
-
decision:
|
|
72
|
-
consequences:
|
|
71
|
+
decision: options.decision,
|
|
72
|
+
consequences: options.consequences,
|
|
73
73
|
});
|
|
74
74
|
const frontmatter = YAML.stringify({
|
|
75
75
|
id: options.id,
|
|
@@ -95,6 +95,8 @@ export async function createDecisionRecord(options) {
|
|
|
95
95
|
date: createdAt.date,
|
|
96
96
|
status,
|
|
97
97
|
context: options.context ?? 'TODO',
|
|
98
|
+
decision: options.decision ?? 'TODO',
|
|
99
|
+
consequences: options.consequences ?? 'TODO',
|
|
98
100
|
});
|
|
99
101
|
await assertDecisionIndexScaffold(options.decisionsRoot);
|
|
100
102
|
await mkdir(dirname(filePath), { recursive: true });
|
|
@@ -29,6 +29,7 @@ interface DoctorDependencies {
|
|
|
29
29
|
processEnv: NodeJS.ProcessEnv;
|
|
30
30
|
runPjmDoctorChecks: (repoRoot: string, options?: PjmDoctorOptions) => Promise<DoctorCheck[]>;
|
|
31
31
|
checkSkillVersions: (scopeRoot: string, assetsRoot: string, pathExists: (path: string) => Promise<boolean>) => Promise<SkillVersionReport>;
|
|
32
|
+
checkStaleInvocations: (repoRoot: string) => Promise<DoctorCheck>;
|
|
32
33
|
}
|
|
33
34
|
interface OutdatedSkillVersion {
|
|
34
35
|
skill: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAY9B,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAO9D,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAY9B,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAgB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAO9D,OAAO,EAGL,KAAK,8BAA8B,EACnC,KAAK,yBAAyB,EAC/B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC3B,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,KAAK,WAAW,EAAuB,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,UAAU,kBAAkB;IAC1B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,gBAAgB,EAAE,CAChB,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,cAAc,KACpB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1D,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,cAAc,EAAE,CACd,SAAS,EAAE,MAAM,KACd,OAAO,CACV,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CACnE,CAAC;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5C,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC7C,mCAAmC,EAAE,OAAO,mCAAmC,CAAC;IAChF,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,gBAAgB,KACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5B,kBAAkB,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,KAC3C,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACjC,qBAAqB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACnE;AAED,UAAU,oBAAoB;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,UAAU,kBAAkB;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,0BAA0B,EAAE,MAAM,CAAC;IACnC,cAAc,EAAE,oBAAoB,EAAE,CAAC;CACxC;AAqxBD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,kBAAkB,CAAM,GAC1C,OAAO,CAcT"}
|
|
@@ -23,6 +23,7 @@ import { validateMatrixCell, } from '../../providers/identity/availability.js';
|
|
|
23
23
|
import { createDispatchValidationPassContext, validateDispatchMatrixRefs, } from '../../providers/identity/dispatch-validation.js';
|
|
24
24
|
import { formatDoctorResults } from '../../ui/output.js';
|
|
25
25
|
import { Command } from 'commander';
|
|
26
|
+
import { checkStaleInvocations } from './stale-invocations.js';
|
|
26
27
|
async function pathExistsDefault(path) {
|
|
27
28
|
try {
|
|
28
29
|
await access(path);
|
|
@@ -143,6 +144,7 @@ function createDependencies() {
|
|
|
143
144
|
// Default binding remains self-contained, but still honors the caller-
|
|
144
145
|
// provided pathExists dependency from runChecksForScope when available.
|
|
145
146
|
checkSkillVersions: (scopeRoot, assetsRoot, pathExists = pathExistsDefault) => checkSkillVersionsDefault(scopeRoot, assetsRoot, pathExists),
|
|
147
|
+
checkStaleInvocations,
|
|
146
148
|
};
|
|
147
149
|
}
|
|
148
150
|
function collectDispatchMatrixCellRefs(layers) {
|
|
@@ -505,6 +507,7 @@ async function runChecksForScope(scope, scopeRoot, userConfigDir, dependencies)
|
|
|
505
507
|
});
|
|
506
508
|
checks.push(...(await runCodexChecksForScope(scope, scopeRoot, dirname(userConfigDir), dependencies)));
|
|
507
509
|
if (scope === 'project') {
|
|
510
|
+
checks.push(await dependencies.checkStaleInvocations(scopeRoot));
|
|
508
511
|
try {
|
|
509
512
|
const assetsRoot = await dependencies.resolveAssetsRoot();
|
|
510
513
|
const skillVersions = await dependencies.checkSkillVersions(scopeRoot, assetsRoot, dependencies.pathExists);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale-invocations.d.ts","sourceRoot":"","sources":["../../../src/commands/doctor/stale-invocations.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAoK9C,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,CAAC,CA6CtB"}
|