@open-agent-toolkit/cli 0.1.64 → 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.
Files changed (38) hide show
  1. package/assets/docs/cli-utilities/backlog-lifecycle.md +2 -2
  2. package/assets/docs/cli-utilities/config-and-local-state.md +20 -6
  3. package/assets/docs/cli-utilities/tool-packs.md +1 -0
  4. package/assets/docs/cli-utilities/workflow-gates.md +3 -1
  5. package/assets/docs/contributing/code.md +18 -0
  6. package/assets/public-package-versions.json +4 -4
  7. package/assets/skills/oat-pjm-add-backlog-item/SKILL.md +17 -72
  8. package/assets/skills/oat-project-summary/SKILL.md +9 -4
  9. package/assets/templates/plan.md +2 -0
  10. package/dist/commands/backlog/archive.d.ts.map +1 -1
  11. package/dist/commands/backlog/archive.js +5 -5
  12. package/dist/commands/backlog/index.d.ts +4 -0
  13. package/dist/commands/backlog/index.d.ts.map +1 -1
  14. package/dist/commands/backlog/index.js +55 -0
  15. package/dist/commands/backlog/new.d.ts +27 -0
  16. package/dist/commands/backlog/new.d.ts.map +1 -0
  17. package/dist/commands/backlog/new.js +158 -0
  18. package/dist/commands/backlog/regenerate-index.d.ts.map +1 -1
  19. package/dist/commands/backlog/regenerate-index.js +42 -3
  20. package/dist/commands/decision/index.d.ts.map +1 -1
  21. package/dist/commands/decision/index.js +4 -0
  22. package/dist/commands/decision/new.d.ts +2 -0
  23. package/dist/commands/decision/new.d.ts.map +1 -1
  24. package/dist/commands/decision/new.js +4 -2
  25. package/dist/commands/doctor/index.d.ts +1 -0
  26. package/dist/commands/doctor/index.d.ts.map +1 -1
  27. package/dist/commands/doctor/index.js +3 -0
  28. package/dist/commands/doctor/stale-invocations.d.ts +3 -0
  29. package/dist/commands/doctor/stale-invocations.d.ts.map +1 -0
  30. package/dist/commands/doctor/stale-invocations.js +173 -0
  31. package/dist/commands/gate/index.d.ts +1 -0
  32. package/dist/commands/gate/index.d.ts.map +1 -1
  33. package/dist/commands/gate/index.js +5 -1
  34. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  35. package/dist/commands/project/new/scaffold.js +24 -9
  36. package/dist/commands/tools/update/index.d.ts.map +1 -1
  37. package/dist/commands/tools/update/index.js +22 -12
  38. package/package.json +2 -2
@@ -42,11 +42,11 @@ oat backlog archive BL-260705-example --wont-do --summary "superseded by BL-2607
42
42
  A single `oat backlog archive` run performs the whole close-out so its parts cannot drift apart:
43
43
 
44
44
  1. Sets the terminal `status` (`closed` by default, `wont_do` with `--wont-do`) and stamps `updated`.
45
- 2. Appends a canonical newest-first entry to `completed.md`. `closed` items always get an entry (with a visible `TODO: summarize outcome` placeholder when `--summary` is omitted, so the gap stays visible); `wont_do` items get one only when you pass `--summary`.
45
+ 2. Validates and trims a nonblank `--summary` before mutating a `closed` item, then appends its canonical newest-first `completed.md` entry. `wont_do` items may omit the summary and get an entry only when one is provided.
46
46
  3. Moves `items/<id>.md` into `archived/` — with `git mv` inside a work tree, or a plain rename outside git.
47
47
  4. Regenerates the managed backlog index.
48
48
 
49
- The command is safe to re-run: an item already in `archived/` produces a no-op warning with no writes. An out-of-enum current status (for example a hand-set `done`) is a hard error that names the file, lists the valid statuses, and tells you how to recover. See the [command reference](config-and-local-state.md#oat-backlog-archive) for exit codes and the `--json` payload.
49
+ The command is safe to re-run: an item already in `archived/` produces a no-op warning with no writes. A missing closed-item summary or an out-of-enum current status (for example a hand-set `done`) is a hard error before mutation and includes recovery guidance. See the [command reference](config-and-local-state.md#oat-backlog-archive) for exit codes and the `--json` payload.
50
50
 
51
51
  ## Catching lifecycle drift
52
52
 
@@ -14,6 +14,7 @@ Use these commands when you need operational support around the toolkit rather t
14
14
  Use the `oat backlog` group when you want direct CLI support for the file-backed backlog under `.oat/repo/pjm/backlog/`.
15
15
 
16
16
  - `oat backlog init` - scaffold `.oat/repo/pjm/backlog/` with starter files and directories for a fresh repo
17
+ - `oat backlog new <title>` - validate and create a file-backed backlog item from the canonical template, then regenerate the managed index
17
18
  - `oat backlog generate-id <title>` - generate a deterministic `BL-YYMMDD-slug` backlog ID from a title
18
19
  - `oat backlog generate-id <title> --created-at <timestamp>` - generate a reproducible ID for a known creation timestamp
19
20
  - `oat backlog archive <id>` - atomic close-out: set a terminal status, record the completion in `completed.md`, move the item into `archived/`, and regenerate the index in one step
@@ -21,10 +22,18 @@ Use the `oat backlog` group when you want direct CLI support for the file-backed
21
22
 
22
23
  Backlog IDs are deterministic date+slug identifiers (`BL-YYMMDD-slug`) derived from the creation date and title, so two machines or worktrees produce the same ID for the same record without scanning the local checkout. The slug is capped at 30 characters at the last whole-word boundary (with trailing stop-words trimmed), so prefer concise, meaningful titles. Index regeneration is deterministic and safe to re-run when resolving an index merge conflict.
23
24
 
24
- Run `oat backlog init` first when the local backlog scaffold does not exist yet in a fresh repo. This command group is primarily used by the `oat-pjm-*` project-management skills, but it is also available directly when you need to inspect or repair backlog metadata by hand.
25
+ Run `oat backlog init` directly when you need to create or repair only the backlog scaffold. `oat backlog new` initializes a missing scaffold automatically after validating its inputs. This command group is primarily used by the `oat-pjm-*` project-management skills, but it is also available directly when you need to inspect or repair backlog metadata by hand.
25
26
 
26
27
  For the end-to-end states an item moves through — and how `oat backlog archive` and `oat pjm doctor` keep the backlog honest — see [Backlog Lifecycle](backlog-lifecycle.md).
27
28
 
29
+ ### `oat backlog new`
30
+
31
+ `oat backlog new <title> [--priority <priority>] [--scope <scope>] [--scope-estimate <size>] [--labels <labels>] [--description <text>] [--backlog-root <path>]` creates one active item and refreshes the managed backlog index.
32
+
33
+ Defaults are `priority: medium` and `scope: task`. Valid priorities are `urgent`, `high`, `medium`, `low`, and `none`; valid scopes are `idea`, `task`, `feature`, and `initiative`; scope estimates accept `XS`, `S`, `M`, `L`, `XL`, or `XXL`. Pass labels as a comma-delimited list.
34
+
35
+ The command validates all inputs before creating the scaffold or writing an item. It uses the repo-local canonical backlog template when available, falls back to the CLI's bundled template, writes structured YAML frontmatter, and rejects an ID collision in either `items/` or `archived/` without overwriting the existing record. If managed-index regeneration fails after the item write, OAT removes only the new item and restores the prior index.
36
+
28
37
  ### `oat backlog archive`
29
38
 
30
39
  `oat backlog archive <id> [--wont-do] [--summary <text>] [--json] [--backlog-root <path>]` performs the full close-out for a backlog item so status flip, completed-log entry, file move, and index regeneration never drift apart.
@@ -33,22 +42,23 @@ For the end-to-end states an item moves through — and how `oat backlog archive
33
42
 
34
43
  - `<id>` (required) - the backlog item id (`BL-YYMMDD-slug`); the item file must live under `items/`.
35
44
  - `--wont-do` - close the item as `wont_do` instead of the default terminal status `closed`.
36
- - `--summary <text>` - one-line outcome summary recorded in `completed.md`.
45
+ - `--summary <text>` - one-line outcome summary recorded in `completed.md`; required and nonblank for the default `closed` path.
37
46
  - `--backlog-root <path>` - override the backlog root (defaults to `.oat/repo/pjm/backlog`).
38
47
  - `--json` - emit the machine-readable result payload instead of human log lines.
39
48
 
40
49
  **Behavior:**
41
50
 
42
51
  - Validates the item's current `status` against the enum (`open | in_progress | closed | wont_do`); an out-of-enum value such as `done` is a hard error with a fix hint. Archiving is legal from any valid status — a `closed` item still in `items/` just gets its move finished.
52
+ - For the default `closed` path, validates and trims a nonblank `--summary` before any file or index mutation. The `wont_do` path may omit the summary and completion-ledger entry.
43
53
  - Rewrites only the `status:` and `updated:` frontmatter lines (preserving any inline enum comment), then moves the item from `items/` to `archived/` with `git mv` inside a work tree, falling back to a plain rename (with a warning) outside git or if `git mv` fails.
44
- - `closed` archives always append a canonical newest-first `completed.md` entry (`YYYY-MM-DD — <id> — Title — summary`); when `--summary` is omitted the entry carries a visible `TODO: summarize outcome` placeholder. `wont_do` archives append an entry only when `--summary` is provided. A missing `completed.md` is scaffolded from the starter template; a missing `## Completed Items` heading is scaffolded with a warning.
54
+ - `closed` archives append a canonical newest-first `completed.md` entry (`YYYY-MM-DD — <id> — Title — summary`). `wont_do` archives append an entry only when `--summary` is provided. A missing `completed.md` is scaffolded from the starter template; a missing `## Completed Items` heading is scaffolded with a warning.
45
55
  - Regenerates the managed backlog index after the move.
46
56
  - Idempotent: re-running on an item already in `archived/` is a no-op warning with no writes.
47
57
 
48
58
  **Exit codes:**
49
59
 
50
60
  - `0` - item archived, or already-archived no-op.
51
- - `1` - actionable error: unknown id (no file under `items/`) or an out-of-enum current status. The message names the file path, the valid statuses, and the fix.
61
+ - `1` - actionable error: unknown id, out-of-enum current status, duplicate active/archived ID, or a missing closed-item summary. The message includes the recovery action.
52
62
  - `2` - reserved for unexpected system/runtime failures.
53
63
 
54
64
  **JSON payload (`--json`):**
@@ -76,10 +86,12 @@ For full project-management repo-reference setup, use [`oat pjm init`](tool-pack
76
86
  Use the `oat decision` group for file-per-record decisions under `.oat/repo/reference/decisions/`. Each decision is its own file with a deterministic `DR-YYMMDD-slug` ID (the slug is capped at 30 characters at the last whole-word boundary, with trailing stop-words trimmed), and the human-facing index is a committed generated view.
77
87
 
78
88
  - `oat decision init` - scaffold `.oat/repo/reference/decisions/` and the managed decision index
79
- - `oat decision new <title>` - create a new decision record; supports `--status`, `--context`, and `--created-at`
89
+ - `oat decision new <title>` - create a new decision record; supports `--status`, `--context`, `--decision`, `--consequences`, and `--created-at`
80
90
  - `oat decision regenerate-index` - rebuild the managed decision index table from record frontmatter
81
91
  - `oat decision migrate` - convert a legacy single `decision-record.md` into file-per-record decisions, preserving each old `ADR-NNN`/`DR-NNN` ID as `legacy_id`; applies by default, so pass `--dry-run` to preview the legacy-to-new mappings without writing, and `--delete-legacy` to remove the source file after a verified migration (unlike `oat pjm migrate`, which defaults to dry-run)
82
92
 
93
+ Pass `--context`, `--decision`, and `--consequences` together when creating a resolved decision so every substantive template section is complete in the same atomic creation step. Callers that omit them retain the template's placeholder content for later editing.
94
+
83
95
  The decision index uses managed marker pairs and is deterministic, so an index merge conflict can be resolved by re-running `oat decision regenerate-index` and staging the result. Decision records replace the legacy single `decision-record.md`; repos still on the old layout migrate with `oat decision migrate` (or the broader `oat pjm migrate`).
84
96
 
85
97
  ## `oat local ...`
@@ -239,4 +251,6 @@ For the full state model, repair semantics, and examples, see [Instruction Sync]
239
251
  - `oat internal validate-oat-skills` - validate `oat-*` skill contracts and metadata
240
252
  - `oat doctor` - run environment and setup diagnostics, including installed-vs-bundled skill version checks
241
253
 
242
- `oat doctor` is the quickest way to confirm that your runtime, directory structure, and installed OAT assets are healthy before deeper debugging. The `/oat-doctor` skill (installed via the core pack) provides richer diagnostics with check and summary modes, including config explanations sourced from bundled documentation.
254
+ `oat doctor` is the quickest way to confirm that your runtime, directory structure, and installed OAT assets are healthy before deeper debugging. At project scope it also scans bounded repository script and documentation surfaces for known-stale CLI grammar, such as `oat --scope all sync`, and reports file/line evidence plus the current `oat sync --scope all` form. Generated provider views, OAT lifecycle artifacts, archived content, dependencies, build output, and nested worktrees are excluded.
255
+
256
+ The `/oat-doctor` skill (installed via the core pack) provides richer diagnostics with check and summary modes, including config explanations sourced from bundled documentation.
@@ -171,6 +171,7 @@ Purpose:
171
171
  Key behavior:
172
172
 
173
173
  - Accepts a tool name, `--pack <pack>`, or `--all` (mutually exclusive)
174
+ - With no target, exits without mutation and suggests the explicit bulk command `oat tools update --all`; invalid packs and conflicting targets keep their own targeted diagnostics
174
175
  - Compares installed versions against bundled versions and copies updated assets
175
176
  - For `--pack <pack>` and `--all`, an already-installed pack is reconciled to include newly added bundled skills or agents in that same scope
176
177
  - Pack-targeted updates intentionally rewrite bundled template and script companions in place, even when the pack's installed skills are already current
@@ -451,7 +451,9 @@ By default the dispatcher:
451
451
  5. Applies `--avoid same-family`.
452
452
  6. Checks candidate availability in descending priority order, with target id as
453
453
  the tie-breaker.
454
- 7. Runs the chosen `baseCommand` with the selected model and prompt appended.
454
+ 7. Runs the chosen `baseCommand` with the selected model and prompt appended. The
455
+ prompt is carried in argv, stdin is closed, and stdout/stderr remain captured
456
+ for diagnostics and liveness tracking.
455
457
  8. Exits with the child process status.
456
458
 
457
459
  Use `--target <id>` to pin one target and skip detection/avoidance:
@@ -96,6 +96,24 @@ not the steady-state path.
96
96
  - Make verification explicit in commit-adjacent notes or review artifacts.
97
97
  - If docs behavior changes, update the docs surface in the same change window.
98
98
 
99
+ ### Breaking CLI grammar changes
100
+
101
+ When a command, option name, or option placement changes, make the migration visible to both reviewers and release-note readers:
102
+
103
+ - Use a `BREAKING:` PR title or prominent callout so generated release notes retain the warning.
104
+ - Include the old and new copy-pasteable commands.
105
+ - State the migration action for scripts, docs, and automation that use the old grammar.
106
+
107
+ For example:
108
+
109
+ **Breaking change title/callout:** `BREAKING: move scope after the sync command`
110
+
111
+ **Before:** `oat --scope all sync` <!-- oat-doctor: allow-stale-invocation -->
112
+
113
+ **After:** `oat sync --scope all`
114
+
115
+ **Migration action:** Update repository scripts and documentation, then run `oat doctor --scope project` to find known-stale invocations.
116
+
99
117
  ## Related Guides
100
118
 
101
119
  - [Contributing Docs](documentation.md)
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli": "0.1.64",
3
- "docs-config": "0.1.64",
4
- "docs-theme": "0.1.64",
5
- "docs-transforms": "0.1.64"
2
+ "cli": "0.1.65",
3
+ "docs-config": "0.1.65",
4
+ "docs-theme": "0.1.65",
5
+ "docs-transforms": "0.1.65"
6
6
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: oat-pjm-add-backlog-item
3
- version: 1.3.0
3
+ version: 1.3.1
4
4
  description: Use when the user requests or confirms adding a new repo backlog item — e.g. "add a backlog item for X", "capture that as backlog", "track that follow-up", "file a backlog ticket", or confirms a previously offered backlog capture. Do NOT auto-invoke when a follow-up is mentioned. Creates the item file in the file-per-item backlog structure, regenerates the index, and prompts for curated overview updates.
5
5
  disable-model-invocation: false
6
6
  user-invocable: true
@@ -29,8 +29,8 @@ When executing this skill, provide lightweight progress feedback so the user can
29
29
 
30
30
  - Before multi-step work, print short step indicators, e.g.:
31
31
  - `[1/4] Resolving item details…`
32
- - `[2/4] Generating ID + populating template…`
33
- - `[3/4] Regenerating backlog index…`
32
+ - `[2/4] Creating the item + managed index atomically…`
33
+ - `[3/4] Enriching acceptance criteria…`
34
34
  - `[4/4] Updating curated overview guidance…`
35
35
 
36
36
  ## Process
@@ -47,85 +47,30 @@ Collect the item details from the user or surrounding context:
47
47
  If the title is missing, ask the user.
48
48
  If the description is missing, ask for 1-3 sentences of context.
49
49
 
50
- ### Step 2: Ensure Backlog Scaffold
50
+ Propose an initial scope estimate (`XS`, `S`, `M`, `L`, `XL`, or `XXL`) from the described work, then ask the user to confirm or adjust it before creation.
51
51
 
52
- Before generating IDs or editing backlog files, run:
52
+ ### Step 2: Create the Backlog Item Atomically
53
53
 
54
- ```bash
55
- oat backlog init
56
- ```
57
-
58
- This command is idempotent. Use it even in existing repos so the canonical backlog scaffold and exact managed index markers are present before `oat backlog regenerate-index` runs.
59
-
60
- Do not hand-create the managed marker block in `backlog/index.md`. The scaffold writes the exact markers required by the CLI:
61
-
62
- ```md
63
- <!-- OAT BACKLOG-INDEX -->
64
- <!-- END OAT BACKLOG-INDEX -->
65
- ```
66
-
67
- ### Step 3: Generate ID
68
-
69
- Run:
70
-
71
- ```bash
72
- oat backlog generate-id "{title}"
73
- ```
74
-
75
- The CLI returns a deterministic `BL-YYMMDD-slug` value derived from the creation date and the title. It performs no scan, hash, counter, or random allocation.
76
-
77
- If the command reports a same-day same-slug filename collision against an existing `items/<id>.md` or `archived/<id>.md`, do not overwrite the existing record. Disambiguate by using a more specific title and re-running `oat backlog generate-id`.
78
-
79
- ### Step 4: Prepare Output Path
80
-
81
- Set the output path using the returned ID so the filename stem equals the ID:
54
+ Run the single creation command with the confirmed values:
82
55
 
83
56
  ```bash
84
- ITEM_PATH=".oat/repo/pjm/backlog/items/{id}.md"
57
+ oat backlog new "{title}" --priority "<priority>" --scope "<scope>" --scope-estimate "<confirmed-scope-estimate>" --labels "<comma-delimited-labels>" --description "<description>"
85
58
  ```
86
59
 
87
- ### Step 5: Copy Template and Fill Frontmatter
88
-
89
- 1. Use `.oat/templates/backlog-item.md` as the source template.
90
- 2. Fill:
91
- - `id`
92
- - `title`
93
- - `created`
94
- - `updated`
95
- - `status` (default `open`)
96
- - `priority` (default `medium` unless the user says otherwise)
97
- - `scope` (default `task` unless the user says otherwise)
98
- - `labels`
99
- - `assignee`
100
- - `associated_issues`
101
- - `external_plans` (initialize to `[]`; `oat-repo-improve` owns later reverse-link additions)
102
- 3. Keep `external_plans` as a YAML string array of repo-relative paths under `.oat/repo/reference/external-plans/`. Do not pre-populate it from discussion or anticipated work.
103
- 4. The agent should propose an initial `scope_estimate` based on the described work, then ask the user to confirm or adjust it.
104
- 5. Write the item body with:
105
- - `## Description`
106
- - `## Acceptance Criteria`
107
-
108
- ### Step 6: Write the Backlog Item
109
-
110
- Write the completed file to the path resolved in Step 4:
60
+ The command validates all inputs, initializes the scaffold when needed, generates and collision-checks the `BL-YYMMDD-slug` ID, renders the canonical template, writes the item, and regenerates the managed index. If it reports a collision, do not overwrite the existing active or archived record; use a more specific title and rerun the same command.
111
61
 
112
- ```bash
113
- .oat/repo/pjm/backlog/items/{id}.md
114
- ```
115
-
116
- Use the template field order from `.oat/templates/backlog-item.md`.
62
+ Use the item path and ID reported by the command. Do not hand-author frontmatter or edit the managed index block. The command initializes `external_plans: []`; `oat-repo-improve` owns later reverse-link additions.
117
63
 
118
- ### Step 7: Regenerate Managed Index
64
+ ### Step 3: Enrich Acceptance Criteria
119
65
 
120
- Run:
66
+ Read the created item and replace only the placeholder bullets under `## Acceptance Criteria` with the acceptance criteria confirmed in Step 1. Preserve the command-generated frontmatter and description. This post-create enrichment is safe because Acceptance Criteria are not index-visible fields.
121
67
 
122
- ```bash
123
- oat backlog regenerate-index
124
- ```
68
+ The item must retain both required body sections:
125
69
 
126
- This refreshes the managed table inside `.oat/repo/pjm/backlog/index.md`.
70
+ - `## Description`
71
+ - `## Acceptance Criteria`
127
72
 
128
- ### Step 8: Update Curated Overview
73
+ ### Step 4: Update Curated Overview
129
74
 
130
75
  Read `.oat/repo/pjm/backlog/index.md` and update the `## Curated Overview` section with a brief human-written note when helpful, for example:
131
76
 
@@ -135,7 +80,7 @@ Read `.oat/repo/pjm/backlog/index.md` and update the `## Curated Overview` secti
135
80
 
136
81
  Do not edit inside the managed marker section.
137
82
 
138
- ### Step 9: Summarize to the User
83
+ ### Step 5: Summarize to the User
139
84
 
140
85
  Report:
141
86
 
@@ -151,5 +96,5 @@ Report:
151
96
  - Item includes populated frontmatter and both required body sections
152
97
  - Item initializes `external_plans: []` for future reverse links
153
98
  - `scope_estimate` was proposed and confirmed
154
- - `oat backlog regenerate-index` ran successfully
99
+ - `oat backlog new` created the item and regenerated the managed index successfully
155
100
  - `.oat/repo/pjm/backlog/index.md` remains valid, with managed section untouched except by regeneration
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: oat-project-summary
3
- version: 1.3.1
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
@@ -278,7 +278,12 @@ test -f .oat/repo/reference/decisions/index.md || oat decision init
278
278
 
279
279
  **6.4 — Idempotent, date-independent promotion (critical).** For each decision in `## Key Decisions`:
280
280
 
281
- 1. **Derive title + rationale.** The decision's bold lead-in / first clause becomes the **title** (a short noun phrase). The remaining explanatory text becomes the **rationale**, passed verbatim as `--context`.
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.
282
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.
283
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:
284
289
 
@@ -294,10 +299,10 @@ test -f .oat/repo/reference/decisions/index.md || oat decision init
294
299
  - Otherwise → create it:
295
300
 
296
301
  ```bash
297
- oat decision new "<title>" --status accepted --context "<rationale>"
302
+ oat decision new "<title>" --status accepted --context "<context>" --decision "<decision>" --consequences "<consequences>"
298
303
  ```
299
304
 
300
- The command generates the deterministic `DR-YYMMDD-slug` ID, seeds the body from `.oat/templates/decision.md`, 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.
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.
301
306
 
302
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.
303
308
 
@@ -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;AAoB9B,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,CAkHnC"}
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' || Boolean(options.summary);
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;AAqB5D,UAAU,0BAA0B;IAClC,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IACtD,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AA0CD,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAClD,OAAO,CAiMT"}
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"}