nitm-ai-dev-toolkit 3.0.23 → 5.0.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.
Files changed (187) hide show
  1. package/CHANGELOG.md +44 -3
  2. package/README.md +93 -6
  3. package/bin/cli.js +155 -25
  4. package/bin/factory-archive.js +71 -0
  5. package/bin/factory-prune.js +79 -0
  6. package/bin/factory-watch.js +509 -0
  7. package/package.json +1 -1
  8. package/src/monorepo/.agents/skills/factory/SKILL.md +593 -0
  9. package/src/monorepo/.claude/agents/admin-portal.agent.md +1 -1
  10. package/src/monorepo/.claude/agents/api-specialist.agent.md +1 -1
  11. package/src/monorepo/.claude/agents/backend-architect.agent.md +1 -1
  12. package/src/monorepo/.claude/agents/documenter.agent.md +1 -1
  13. package/src/monorepo/.claude/agents/factory-approver.agent.md +12 -0
  14. package/src/monorepo/.claude/agents/factory-builder.agent.md +12 -0
  15. package/src/monorepo/.claude/agents/factory-reviewer-code.agent.md +12 -0
  16. package/src/monorepo/.claude/agents/factory-reviewer-security.agent.md +12 -0
  17. package/src/monorepo/.claude/agents/factory-reviewer-ui.agent.md +12 -0
  18. package/src/monorepo/.claude/agents/factory-reviewer-ux.agent.md +12 -0
  19. package/src/monorepo/.claude/agents/factory-spec-writer.agent.md +12 -0
  20. package/src/monorepo/.claude/agents/feature-builder.agent.md +1 -1
  21. package/src/monorepo/.claude/agents/frontend-developer.agent.md +1 -1
  22. package/src/monorepo/.claude/agents/green.agent.md +1 -1
  23. package/src/monorepo/.claude/agents/implementer.agent.md +1 -1
  24. package/src/monorepo/.claude/agents/planner.agent.md +1 -1
  25. package/src/monorepo/.claude/agents/red.agent.md +1 -1
  26. package/src/monorepo/.claude/agents/refactor.agent.md +1 -1
  27. package/src/monorepo/.claude/agents/researcher.agent.md +1 -1
  28. package/src/monorepo/.claude/agents/reviewer.agent.md +1 -1
  29. package/src/monorepo/.claude/agents/tdd.agent.md +1 -1
  30. package/src/monorepo/.claude/agents-snippets/factory-approver.md +72 -0
  31. package/src/monorepo/.claude/agents-snippets/factory-builder.md +101 -0
  32. package/src/monorepo/.claude/agents-snippets/factory-reviewer-code.md +57 -0
  33. package/src/monorepo/.claude/agents-snippets/factory-reviewer-security.md +48 -0
  34. package/src/monorepo/.claude/agents-snippets/factory-reviewer-ui.md +64 -0
  35. package/src/monorepo/.claude/agents-snippets/factory-reviewer-ux.md +64 -0
  36. package/src/monorepo/.claude/agents-snippets/factory-spec-writer.md +119 -0
  37. package/src/monorepo/.claude/agents-snippets/feature-builder.md +1 -1
  38. package/src/monorepo/.claude/commands/factory.md +24 -0
  39. package/src/monorepo/.claude/commands/ponytail-audit.md +4 -6
  40. package/src/monorepo/.claude/commands/ponytail-debt.md +3 -5
  41. package/src/monorepo/.claude/commands/ponytail-gain.md +2 -4
  42. package/src/monorepo/.claude/commands/ponytail-help.md +2 -4
  43. package/src/monorepo/.claude/commands/ponytail-review.md +3 -5
  44. package/src/monorepo/.claude/commands/ponytail.md +3 -5
  45. package/src/monorepo/.claude/prompt-snippets/assign-tasks.md +6 -2
  46. package/src/monorepo/.claude/prompt-snippets/bootstrap-patch.md +32 -17
  47. package/src/monorepo/.claude/prompt-snippets/bootstrap-upgrade.md +76 -51
  48. package/src/monorepo/.claude/prompt-snippets/bootstrap.md +57 -29
  49. package/src/monorepo/.claude/prompt-snippets/factory.md +20 -0
  50. package/src/monorepo/.claude/prompt-snippets/implement-feature.md +2 -2
  51. package/src/monorepo/.claude/prompt-snippets/tasks.md +49 -87
  52. package/src/monorepo/.claude/rules-snippets/documentation.md +8 -6
  53. package/src/monorepo/.cursor/agents/factory-approver.agent.md +11 -0
  54. package/src/monorepo/.cursor/agents/factory-builder.agent.md +11 -0
  55. package/src/monorepo/.cursor/agents/factory-reviewer-code.agent.md +11 -0
  56. package/src/monorepo/.cursor/agents/factory-reviewer-security.agent.md +11 -0
  57. package/src/monorepo/.cursor/agents/factory-reviewer-ui.agent.md +11 -0
  58. package/src/monorepo/.cursor/agents/factory-reviewer-ux.agent.md +11 -0
  59. package/src/monorepo/.cursor/agents/factory-spec-writer.agent.md +11 -0
  60. package/src/monorepo/.cursor/commands/factory.md +24 -0
  61. package/src/monorepo/.github/agents/factory-approver.agent.md +11 -0
  62. package/src/monorepo/.github/agents/factory-builder.agent.md +11 -0
  63. package/src/monorepo/.github/agents/factory-reviewer-code.agent.md +11 -0
  64. package/src/monorepo/.github/agents/factory-reviewer-security.agent.md +11 -0
  65. package/src/monorepo/.github/agents/factory-reviewer-ui.agent.md +11 -0
  66. package/src/monorepo/.github/agents/factory-reviewer-ux.agent.md +11 -0
  67. package/src/monorepo/.github/agents/factory-spec-writer.agent.md +11 -0
  68. package/src/monorepo/.github/prompts/factory.prompt.md +25 -0
  69. package/src/monorepo/.github/prompts/review-staged.prompt.md +1 -1
  70. package/src/monorepo/.opencode/agents/admin-portal.md +10 -4
  71. package/src/monorepo/.opencode/agents/api-specialist.md +10 -4
  72. package/src/monorepo/.opencode/agents/backend-architect.md +10 -4
  73. package/src/monorepo/.opencode/agents/documenter.md +10 -4
  74. package/src/monorepo/.opencode/agents/factory-approver.md +20 -0
  75. package/src/monorepo/.opencode/agents/factory-builder.md +20 -0
  76. package/src/monorepo/.opencode/agents/factory-reviewer-code.md +20 -0
  77. package/src/monorepo/.opencode/agents/factory-reviewer-security.md +20 -0
  78. package/src/monorepo/.opencode/agents/factory-reviewer-ui.md +20 -0
  79. package/src/monorepo/.opencode/agents/factory-reviewer-ux.md +20 -0
  80. package/src/monorepo/.opencode/agents/factory-spec-writer.md +20 -0
  81. package/src/monorepo/.opencode/agents/feature-builder.md +13 -5
  82. package/src/monorepo/.opencode/agents/frontend-developer.md +10 -4
  83. package/src/monorepo/.opencode/agents/green.md +10 -4
  84. package/src/monorepo/.opencode/agents/implementer.md +10 -4
  85. package/src/monorepo/.opencode/agents/planner.md +10 -4
  86. package/src/monorepo/.opencode/agents/red.md +10 -4
  87. package/src/monorepo/.opencode/agents/refactor.md +10 -4
  88. package/src/monorepo/.opencode/agents/researcher.md +10 -4
  89. package/src/monorepo/.opencode/agents/reviewer.md +10 -4
  90. package/src/monorepo/.opencode/agents/tdd.md +13 -5
  91. package/src/monorepo/.opencode/commands/factory.md +29 -0
  92. package/src/monorepo/.opencode/opencode.jsonc +8 -10
  93. package/src/monorepo/AGENTS.md +30 -1
  94. package/src/monorepo/CLAUDE.md +1 -1
  95. package/src/monorepo/README.md +20 -0
  96. package/src/monorepo/factory/README.md +194 -0
  97. package/src/monorepo/factory/_gitignore +5 -0
  98. package/src/repo/.agents/skills/factory/SKILL.md +593 -0
  99. package/src/repo/.claude/agents/admin-portal.agent.md +1 -1
  100. package/src/repo/.claude/agents/api-specialist.agent.md +1 -1
  101. package/src/repo/.claude/agents/backend-architect.agent.md +1 -1
  102. package/src/repo/.claude/agents/documenter.agent.md +2 -2
  103. package/src/repo/.claude/agents/factory-approver.agent.md +12 -0
  104. package/src/repo/.claude/agents/factory-builder.agent.md +12 -0
  105. package/src/repo/.claude/agents/factory-reviewer-code.agent.md +12 -0
  106. package/src/repo/.claude/agents/factory-reviewer-security.agent.md +12 -0
  107. package/src/repo/.claude/agents/factory-reviewer-ui.agent.md +12 -0
  108. package/src/repo/.claude/agents/factory-reviewer-ux.agent.md +12 -0
  109. package/src/repo/.claude/agents/factory-spec-writer.agent.md +12 -0
  110. package/src/repo/.claude/agents/feature-builder.agent.md +1 -1
  111. package/src/repo/.claude/agents/frontend-developer.agent.md +1 -1
  112. package/src/repo/.claude/agents/green.agent.md +1 -1
  113. package/src/repo/.claude/agents/implementer.agent.md +1 -1
  114. package/src/repo/.claude/agents/planner.agent.md +2 -2
  115. package/src/repo/.claude/agents/red.agent.md +1 -1
  116. package/src/repo/.claude/agents/refactor.agent.md +1 -1
  117. package/src/repo/.claude/agents/researcher.agent.md +2 -2
  118. package/src/repo/.claude/agents/reviewer.agent.md +2 -2
  119. package/src/repo/.claude/agents/tdd.agent.md +1 -1
  120. package/src/repo/.claude/agents-snippets/factory-approver.md +72 -0
  121. package/src/repo/.claude/agents-snippets/factory-builder.md +100 -0
  122. package/src/repo/.claude/agents-snippets/factory-reviewer-code.md +57 -0
  123. package/src/repo/.claude/agents-snippets/factory-reviewer-security.md +48 -0
  124. package/src/repo/.claude/agents-snippets/factory-reviewer-ui.md +64 -0
  125. package/src/repo/.claude/agents-snippets/factory-reviewer-ux.md +64 -0
  126. package/src/repo/.claude/agents-snippets/factory-spec-writer.md +117 -0
  127. package/src/repo/.claude/agents-snippets/feature-builder.md +1 -1
  128. package/src/repo/.claude/commands/factory.md +24 -0
  129. package/src/repo/.claude/commands/ponytail-audit.md +4 -6
  130. package/src/repo/.claude/commands/ponytail-debt.md +3 -5
  131. package/src/repo/.claude/commands/ponytail-gain.md +2 -4
  132. package/src/repo/.claude/commands/ponytail-help.md +2 -4
  133. package/src/repo/.claude/commands/ponytail-review.md +3 -5
  134. package/src/repo/.claude/commands/ponytail.md +3 -5
  135. package/src/repo/.claude/prompt-snippets/assign-tasks.md +6 -2
  136. package/src/repo/.claude/prompt-snippets/bootstrap-patch.md +32 -17
  137. package/src/repo/.claude/prompt-snippets/bootstrap-upgrade.md +87 -77
  138. package/src/repo/.claude/prompt-snippets/bootstrap.md +71 -74
  139. package/src/repo/.claude/prompt-snippets/factory.md +20 -0
  140. package/src/repo/.claude/prompt-snippets/implement-feature.md +2 -2
  141. package/src/repo/.claude/prompt-snippets/tasks.md +49 -112
  142. package/src/repo/.cursor/agents/factory-approver.agent.md +11 -0
  143. package/src/repo/.cursor/agents/factory-builder.agent.md +11 -0
  144. package/src/repo/.cursor/agents/factory-reviewer-code.agent.md +11 -0
  145. package/src/repo/.cursor/agents/factory-reviewer-security.agent.md +11 -0
  146. package/src/repo/.cursor/agents/factory-reviewer-ui.agent.md +11 -0
  147. package/src/repo/.cursor/agents/factory-reviewer-ux.agent.md +11 -0
  148. package/src/repo/.cursor/agents/factory-spec-writer.agent.md +11 -0
  149. package/src/repo/.cursor/commands/factory.md +24 -0
  150. package/src/repo/.github/agents/factory-approver.agent.md +11 -0
  151. package/src/repo/.github/agents/factory-builder.agent.md +11 -0
  152. package/src/repo/.github/agents/factory-reviewer-code.agent.md +11 -0
  153. package/src/repo/.github/agents/factory-reviewer-security.agent.md +11 -0
  154. package/src/repo/.github/agents/factory-reviewer-ui.agent.md +11 -0
  155. package/src/repo/.github/agents/factory-reviewer-ux.agent.md +11 -0
  156. package/src/repo/.github/agents/factory-spec-writer.agent.md +11 -0
  157. package/src/repo/.github/prompts/factory.prompt.md +25 -0
  158. package/src/repo/.opencode/agents/admin-portal.md +10 -4
  159. package/src/repo/.opencode/agents/api-specialist.md +10 -4
  160. package/src/repo/.opencode/agents/backend-architect.md +10 -4
  161. package/src/repo/.opencode/agents/documenter.md +10 -4
  162. package/src/repo/.opencode/agents/factory-approver.md +20 -0
  163. package/src/repo/.opencode/agents/factory-builder.md +20 -0
  164. package/src/repo/.opencode/agents/factory-reviewer-code.md +20 -0
  165. package/src/repo/.opencode/agents/factory-reviewer-security.md +20 -0
  166. package/src/repo/.opencode/agents/factory-reviewer-ui.md +20 -0
  167. package/src/repo/.opencode/agents/factory-reviewer-ux.md +20 -0
  168. package/src/repo/.opencode/agents/factory-spec-writer.md +20 -0
  169. package/src/repo/.opencode/agents/feature-builder.md +13 -5
  170. package/src/repo/.opencode/agents/frontend-developer.md +10 -4
  171. package/src/repo/.opencode/agents/green.md +10 -4
  172. package/src/repo/.opencode/agents/implementer.md +10 -4
  173. package/src/repo/.opencode/agents/planner.md +10 -4
  174. package/src/repo/.opencode/agents/red.md +10 -4
  175. package/src/repo/.opencode/agents/refactor.md +10 -4
  176. package/src/repo/.opencode/agents/researcher.md +10 -4
  177. package/src/repo/.opencode/agents/reviewer.md +10 -4
  178. package/src/repo/.opencode/agents/tdd.md +13 -5
  179. package/src/repo/.opencode/commands/factory.md +29 -0
  180. package/src/repo/.opencode/opencode.jsonc +8 -10
  181. package/src/repo/AGENTS.md +27 -1
  182. package/src/repo/CLAUDE.md +1 -1
  183. package/src/repo/README.md +20 -0
  184. package/src/repo/factory/README.md +194 -0
  185. package/src/repo/factory/_gitignore +5 -0
  186. package/src/monorepo/.opencode/plugins/ponytail.mjs +0 -93
  187. package/src/repo/.opencode/plugins/ponytail.mjs +0 -93
package/CHANGELOG.md CHANGED
@@ -7,8 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [5.0.0]
11
+
12
+ ### Breaking
13
+
14
+ - `/tasks` now writes a slim `tasks.md`: one checklist line per task (checkbox, ID, short description, optional `[P]`, optional ` — <ticket key>`). Details stay in `spec.md` and `plan.md`. `/assign-tasks` and `/implement-feature` read the slim lines and write ticket keys back after the em dash. Existing `tasks.md` files are not rewritten; re-run `/tasks` on a feature to convert one.
15
+ - `upgrade` no longer overwrites your files. It used to run `install --force`. It now adds missing files and keeps existing ones (`patch` semantics), then re-emits the bootstrap handoff. Overwriting needs an explicit `upgrade --force`, which prints a warning first. Scripts that relied on `upgrade` to refresh templates must add `--force`.
16
+
17
+ ### Added
18
+
19
+ - Factory pipeline (opt-in): `/factory <request>` takes a feature through intake, spec gate, plan gate, build, parallel lens reviews, approval, and local merge, with subcommands `start`, `approve`, `rework`, `status`, and `watch`. Nothing runs until `/factory` is invoked. Job state lives in `factory/jobs/`, which is git-ignored. Entry points ship for Claude Code, GitHub Copilot, Cursor, and OpenCode; Codex uses the orchestrator skill `.agents/skills/factory/SKILL.md`.
20
+ - Factory gates and intake: intake reads `AGENTS.md`, related features in `docs/features/`, and the code it touches, asks at most five glaring questions, and saves the answers to `intake.md`. Every job stops at `awaiting-spec` and `awaiting-plan` in every approval mode, and nothing is ticketed or built before the operator confirms the plan. Change requests are saved as `spec-<k>.md` or `plan-<k>.md`.
21
+ - Factory feature folders and tracking: each job belongs to `docs/features/<KEY>-<slug>/` (or `docs/features/<slug>/` before a ticket exists). `job.json` records `feature_dir`, `issue_key`, `size`, `tracking`, and `worktree`. Large work with a documented tracker creates tickets through `/assign-tasks`, writes their keys onto `tasks.md`, and moves them through In Progress, In Review, and Done. Done only follows `/factory approve`.
22
+ - Factory builds run in a git worktree on `factory/<id>`. `--in-place` builds in the main checkout instead, and only from a clean default branch with no other in-place job open. The builder writes `qa.md` with suggested checks, printed before approval; it never gates anything.
23
+ - Factory agents: `factory-spec-writer`, `factory-builder`, `factory-reviewer-code`, `factory-reviewer-security`, `factory-reviewer-ux`, `factory-reviewer-ui`, and `factory-approver`, with shared snippets in `.claude/agents-snippets/`. Their OpenCode wrappers use native V2 `permissions`.
24
+ - `factory/` template folder (README, `_gitignore`) installed by default. CLI `factory status` (one-shot table) and `factory watch` (live view) are read-only over `factory/jobs/*/job.json`; `abandoned` jobs are shown as closed.
25
+ - Factory `/factory archive [--all] [--dry-run]` moves closed jobs (`merged` or `abandoned`) from `factory/jobs/` to `factory/archive/`. By default only jobs last updated more than 7 days ago move. `--all` moves every closed job, and open jobs never move. CLI `factory archive` makes the same moves. `/factory archive` also asks before deleting an archived abandoned job's branch and worktree, and never pushes. Archived jobs are not resumable (`/factory start` says they are closed), and their ids count toward job numbering until a prune removes them.
26
+ - Factory `/factory prune [--all] [--abandoned] [--dry-run]` permanently deletes closed jobs from `factory/jobs/` and `factory/archive/`. By default only `merged` jobs are deleted, and only those last updated more than 7 days ago. `--all` drops the age limit for merged jobs. `--abandoned` also deletes `abandoned` jobs under the same age rule. Without it, abandoned jobs are kept and counted, and the flow offers to rerun with `--abandoned`. It lists the candidates, asks once in chat, and deletes only after a yes. When abandoned jobs are pruned, it then asks, per abandoned branch, whether to delete the branch and its worktree, as archive does. Open jobs and `docs/features/` are never touched. `/factory archive` keeps its selection: merged and abandoned. CLI `factory prune` lists by default and deletes with `--yes`; `--abandoned` is accepted only by `factory prune`.
27
+ - Link checks in `test/links.test.js`: every relative markdown link and `@path` import in `src/repo` and `src/monorepo` must resolve, and so must every scoped install (each `--env` × layout).
28
+
29
+ ### Changed
30
+
31
+ - Factory dashboard: `factory status` and `factory watch` hide closed jobs by default and print `N closed jobs hidden (--all to show)`. `--all` shows closed jobs and archived ones.
32
+ - Scoped installs (`--env`) also copy `.agents/skills/`, `.claude/agents-snippets/`, `.claude/prompt-snippets/`, `.claude/rules-snippets/`, and `factory/`, the shared references every harness's wrappers use.
33
+ - Templates named `_gitignore` install as `.gitignore`, because npm drops `.gitignore` files from packages.
34
+ - Releases publish through npm trusted publishing (OIDC, with provenance) from `.github/workflows/publish.yml`, which now also does the auto patch bump and tag (`auto-patch-tag.yml` removed). The `NPM_TOKEN` secret is no longer used.
35
+
36
+ ### Fixed
37
+
38
+ - Scoped Cursor, Copilot, and OpenCode installs no longer leave dangling links: `.claude/rules-snippets/` is now shared by every scope, since Cursor rules link there.
39
+ - Scoped installs share only `.agents/skills/`, so Claude/Codex ambient rules in `.agents/rules/` no longer appear in Cursor, Copilot, or OpenCode installs.
40
+ - `doctor` placeholder scan matches names with digits (for example `{{DB2_HOST}}`), skips `templates/` blank forms, and ignores runtime-only placeholders (`ISSUE_ID`, `ISSUE_KEY`, `FEATURE_NAME`, `NAME`), so a bootstrapped project can report clean.
41
+ - `.cursor/rules/orient-to-recent-work.mdc` skill link now resolves (`../` → `../../`).
42
+
43
+ ## [4.0.0]
44
+
45
+ ### Migration
46
+
47
+ - Toolkit v4 / package 4.0.0 requires native OpenCode V2 for OpenCode use; oh-my-opencode-slim >=3.0.0 requires OpenCode >=2.0.7. Native host config uses plural maps, ordered permissions, skill directories, and `mcp.servers`. Back up V1 (converted files are incompatible with it), preserve user/global settings and Slim's separate strict schema, leave the external starter unchanged, and activate on the next run/restart. Config `instructions` are ignored, ambient `AGENTS.md` works, `@refs` require explicit reads, and `request.body.temperature` is preserved but not currently sent by the runner. Preserve `lsp: true`; use project checks, not unavailable V2 LSP tools. Plugin/runtime integration is untested; publishing has not been executed.
48
+ - Removed all three local Ponytail plugin copies (root, repo template, monorepo template). Both templates register the author's native V2 `@dietrichgebert/ponytail@4.12.0` package locally; `opentmux` remains commented as V1-only. No API port or bridge was added. The toolkit root alone omits `plugins` to inherit existing user/global registrations; no global config was changed. Existing targets remove only known obsolete toolkit Ponytail `.js`/`.mjs`/`.ts` copies after backup and approval, preserving unrelated plugins and deduplicating upstream registration locally.
49
+
10
50
  ### Fixed
11
51
 
52
+ - All six repo/monorepo bootstrap flows now require a fail-closed `opencode --version` preflight before changes for confirmed OpenCode use: accept an optional leading `v`, reject V1 before OpenCode customization/migration or toolkit 4.0.0 marking, and stop on a missing CLI or unparseable version. Slim >=3.0.0 enforces OpenCode >=2.0.7; other-harness-only setup is unaffected. Focused instruction-order/version-metadata regression checks cover the guard.
12
53
  - Bootstrap audit/creation examples are explicitly conditional on confirmed Copilot use; completion reports describe performed harness-specific checks without assuming OpenCode configuration, Codex trust/loading, or Copilot/Cursor readiness. Codex-scoped install documentation now includes linked standards and their wrapper sources without implying other harness activation.
13
54
 
14
55
  ### Added
@@ -32,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
32
73
  - `bkt-cli` skill — Bitbucket CLI (`bkt`) command reference for repo and monorepo templates
33
74
  - `glab-cli` skill — GitLab CLI (`glab`) command reference for repo and monorepo templates
34
75
  - Ponytail skill bundle — 6 YAGNI/stdlib/native/minimum-discipline skills (`ponytail`, `ponytail-audit`, `ponytail-debt`, `ponytail-gain`, `ponytail-help`, `ponytail-review`)
35
- - Ponytail OpenCode plugin (`.opencode/plugins/ponytail.mjs`) loaded via the `plugin` config key
76
+ - Author's upstream Ponytail OpenCode plugin registration (`@dietrichgebert/ponytail@4.12.0`) in both template configs; no bundled local plugin source
36
77
  - 7 ponytail hooks in `hooks/` (`ponytail-activate`, `ponytail-config`, `ponytail-instructions`, `ponytail-mode-tracker`, `ponytail-runtime`, `ponytail-statusline.sh`, `ponytail-statusline.ps1`), plus 2 hook configuration files for Claude/Codex and Copilot (`claude-codex-hooks.json`, `copilot-hooks.json`)
37
78
  - Ponytail rule in `.agents/rules/` and `.cursor/rules/` for ambient agent context
38
79
  - 6 ponytail commands (`ponytail`, `ponytail-audit`, `ponytail-debt`, `ponytail-gain`, `ponytail-help`, `ponytail-review`) with thin-wrapper sources in `.claude/prompt-snippets/`
@@ -74,7 +115,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
74
115
  - Added missing templates referenced in templates/README.md
75
116
  - `scripts/pre-commit-check.sh` — early-exit when no files are staged so the CHANGELOG check no longer fires on empty commits (PR #20 review)
76
117
  - `scripts/check-parity.sh` — removed duplicate `check_body_match` call for the cursor ↔ github prompt body (PR #20 review)
77
- - `.opencode/plugins/ponytail.mjs` — wrapped `writeMode` in a try/catch so an unwritable state directory no longer crashes the plugin (PR #20 review)
78
118
  - `ponytail-help` SKILL.md and prompt snippet — added missing `ponytail-audit`, `ponytail-debt`, and `/ponytail-gain` entries; corrected OpenCode slash-command count to six; rewrote the Claude-Code-specific Update section to describe the template update flow (PR #20 review)
79
119
  - `ponytail-gain` SKILL.md and prompt snippet (repo, monorepo, root) — replaced the phantom `benchmarks/` source reference with the upstream `DietrichGebert/ponytail` repo link (PR #20 review)
80
120
  - `README.md` — corrected "8 activation hooks" to "7 activation hooks" to match CHANGELOG and the actual hook count (PR #20 review)
@@ -113,5 +153,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
113
153
  - Documentation structure templates (`docs/features/`, `docs/fixes/`)
114
154
  - Monthly fix log template
115
155
 
116
- [Unreleased]: https://github.com/ninjasitm/ai-assisted-dev-toolkit/compare/v0.1.0...HEAD
156
+ [Unreleased]: https://github.com/ninjasitm/ai-assisted-dev-toolkit/compare/v4.0.0...HEAD
157
+ [4.0.0]: https://github.com/ninjasitm/ai-assisted-dev-toolkit/compare/v3.0.23...v4.0.0
117
158
  [0.1.0]: https://github.com/ninjasitm/ai-assisted-dev-toolkit/releases/tag/v0.1.0
package/README.md CHANGED
@@ -36,6 +36,7 @@ This repository serves as a **template source** for AI development instructions
36
36
  │ │ │ ├── rules/ # Agent behavior rules (ponytail, etc.)
37
37
  │ │ │ └── skills/ # Bundled skills (TDD, debugging, etc.)
38
38
  │ │ ├── docs/ # Documentation structure templates
39
+ │ │ ├── factory/ # Opt-in factory pipeline (README, .gitignore)
39
40
  │ │ ├── templates/ # Reusable document templates
40
41
  │ │ ├── AGENTS.md # AI agent context template
41
42
  │ │ └── README.md # Project README template
@@ -59,13 +60,14 @@ This repository serves as a **template source** for AI development instructions
59
60
  │ ├── apps/ # App-specific templates
60
61
  │ ├── packages/ # Package-specific templates
61
62
  │ ├── docs/ # Documentation structure templates
63
+ │ ├── factory/ # Opt-in factory pipeline (README, .gitignore)
62
64
  │ ├── templates/ # Reusable document templates
63
65
  │ └── AGENTS.md # Monorepo agent context
64
66
  │
65
67
  ├── .cursor/ # This toolkit's own Cursor config
66
68
  ├── .github/ # This toolkit's own GitHub config
67
69
  ├── .opencode/ # This toolkit's own OpenCode config
68
- │ └── plugins/ # OpenCode plugins (ponytail mode manager, etc.)
70
+ │ └── plugins/ # Other local OpenCode plugins (Ponytail uses upstream)
69
71
  ├── hooks/ # Claude Code / Copilot activation hooks (ponytail, etc.)
70
72
  └── AGENTS.md # This toolkit's agent context
71
73
  ```
@@ -172,18 +174,52 @@ npx nitm-ai-dev-toolkit patch
172
174
  # Inspect the install: missing files and unresolved placeholders
173
175
  npx nitm-ai-dev-toolkit doctor
174
176
 
175
- # Upgrade to the latest published version
177
+ # Upgrade to the latest published version: adds missing files and keeps your existing ones
176
178
  npx nitm-ai-dev-toolkit upgrade
177
179
 
180
+ # Also overwrite existing files with the latest templates (back up customisations first)
181
+ npx nitm-ai-dev-toolkit upgrade --force
182
+
178
183
  # Scaffold the oh-my-opencode-slim starter (delegates to `npx nitm-opencode-starter install`)
179
184
  npx nitm-ai-dev-toolkit omo-slim-starter install
180
185
  ```
181
186
 
182
187
  After `install`, ask your AI agent to read and follow `.nitm/BOOTSTRAP.md`. Harnesses that supply a toolkit `/bootstrap` wrapper can also use it; **Codex has no native toolkit `/bootstrap` command**. Use `doctor` to verify the result.
183
188
 
189
+ ### Native OpenCode V2 (toolkit 4.0.0)
190
+
191
+ Toolkit v4 requires **OpenCode V2 (major >=2)** for OpenCode use; **oh-my-opencode-slim >=3.0.0** requires **OpenCode >=2.0.7**. All repo/monorepo bootstrap, patch, and upgrade flows run `opencode --version` before changes, accepting an optional leading `v`. V1 must stop before OpenCode customization/migration or marking toolkit 4.0.0: upgrade OpenCode or remain on toolkit 3.x. A missing CLI or unparseable version stops OpenCode steps and version marking until the user confirms/installs a supported version. Confirm harness use from runtime evidence or ask; scaffold directories are not proof. Other-harness-only setup is unaffected.
192
+
193
+ Publishing 4.0.0 has not been executed. The external `nitm-opencode-starter` is a separate package, not automatically upgraded.
194
+
195
+ With user approval, merge this reproducible registration without replacing user/global plugins:
196
+
197
+ ```jsonc
198
+ { "plugins": ["oh-my-opencode-slim@3.0.0"] }
199
+ ```
200
+
201
+ The toolkit root deliberately omits `plugins` to inherit user/global registrations; its existing upstream Ponytail registration is unchanged. Both target templates register the author's native V2 package `@dietrichgebert/ponytail@4.12.0` in their local `plugins` arrays, alongside `opencode-mem` and `@tarquinen/opencode-dcp@latest`; `opentmux` remains commented as V1-only. The local `ponytail.mjs` files are removed from the toolkit root and both templates; no local API port or bridge replaces them.
202
+
203
+ For existing targets, back up and obtain approval before removing only known obsolete toolkit `.opencode/plugins/ponytail.js`, `.mjs`, or `.ts` copies. Preserve unrelated local plugins; merge/deduplicate upstream package registration locally where appropriate, without editing or overwriting global plugins. Native local plugins are discovered automatically, so do not register them twice. Slim's separate strict schema keeps its own `prompt`, `permission`, and `variant`: do not host-migrate plugin-owned fields. The root's no-`plugins` policy is specific to this toolkit repository, not target templates.
204
+
205
+ | Setting | Native V2 form |
206
+ | --- | --- |
207
+ | Agents | `agents`, JSON `system` (Markdown body for file agents), `request.body.temperature`, model `provider/model#variant` |
208
+ | Permissions | Ordered `permissions` with `action`, `resource`, `effect`; `shell`/`subagent` actions; last match wins |
209
+ | Commands | `commands`; delegated commands use `subagent` |
210
+ | Plugins | `plugins`; options use `{ "package": "…", "options": {} }` |
211
+ | Skills | Directory-path array, e.g. `[".agents/skills"]`, not Markdown globs |
212
+ | MCP | `mcp.servers`; `disabled` inverts legacy `enabled`; `timeout.catalog`/`timeout.execution` |
213
+
214
+ Follow official V2 [migration](https://opencode.ai/v2/docs/migrate-v1), [config](https://opencode.ai/v2/docs/config), [agents](https://opencode.ai/v2/docs/agents), and [plugins](https://opencode.ai/v2/docs/build/plugins) docs. The published `https://opencode.ai/config.json` schema may describe V1; it is not native V2 validation authority.
215
+
216
+ **Runtime limits:** config `instructions` entries are accepted but not loaded; ambient `AGENTS.md` works. `@refs` are ordinary text, not automatic attachments: agents must read referenced snippets explicitly. `request.body.temperature` remains in config, but the current runner does not send it. Plugin/runtime integration has not been tested. Preserve `lsp: true`, but V2 currently exposes no LSP tools or diagnostics: use project lint/typecheck/compiler commands.
217
+
218
+ `--env opencode` includes minimal shared references for wrappers and AGENTS-linked standards without activating other harnesses. Merge, don't clobber: back up V1, preserve models/providers and user/global plugins, and ask before changes. Native-converted files are **incompatible with V1**. Activate on the next run/restart, then check behavior with the installed V2 runtime. `doctor` checks scaffold files/placeholders, not runtime behavior; do not substitute a V1 API/schema fetch or claim checks that were not run.
219
+
184
220
  ### Native Codex
185
221
 
186
- - `--env codex` includes `.codex/`, `.agents/`, and shared context, plus `.github/instructions/` and `.claude/rules-snippets/` solely to supply the standards linked from `AGENTS.md` and those standards' wrapper source content. It does not install other harness command, agent, or config directories. These reference files neither establish Copilot/Claude use nor load automatically as Codex rules; other harness scopes are unchanged.
222
+ - `--env codex` includes `.codex/`, `.agents/`, and shared context, plus `.github/instructions/` solely to supply the standards linked from `AGENTS.md`. The shared `.claude/rules-snippets/` standards are installed in every scope, so those standards' wrapper source content is present too. It does not install other harness command, agent, or config directories. These reference files neither establish Copilot/Claude use nor load automatically as Codex rules; other harness scopes are unchanged.
187
223
  - Codex reads `AGENTS.md` and discovers `.agents/skills/*/SKILL.md` natively. Monorepos can use more-specific nested `AGENTS.md` files for app/package guidance. Linked standards are references to read as needed, not automatically loaded Claude/Copilot rules.
188
224
  - The optional `.codex/config.toml` is deliberately comment-only: it preserves the user's inherited defaults. Codex loads project config only after the user explicitly trusts the project; the toolkit does not grant trust or set model/provider, approval, sandbox, security, or feature overrides.
189
225
  - For manual copies without `.nitm/`, provide `.claude/prompt-snippets/bootstrap.md` to Codex directly. For incremental AI-guided merges, provide `.claude/prompt-snippets/bootstrap-patch.md` as a task document; the CLI `patch` command only adds missing files.
@@ -207,6 +243,7 @@ Templates use `{{PLACEHOLDER}}` syntax for values you need to customize. The boo
207
243
  | `{{PACKAGE_MANAGER}}` | Package manager | `npm`, `composer`, `dotnet`, `pip`, `bundler`, `go`, `cargo`, `maven` |
208
244
  | `{{FILE_EXTENSION}}` | File extension | `ts`, `php`, `cs`, `py`, `rb`, `go`, `rs`, `java` |
209
245
  | `{{DEV_PORT}}` | Development server port | `3000`, `8000`, `5000` |
246
+ | `{{DEV_PORT_FLAG}}` | Flag or env var that sets the dev port | `--port 3000`, `PORT=3000` |
210
247
  | `{{DEPLOY_PLATFORM}}` | Deployment target | `Vercel`, `AWS`, `Azure`, `Heroku` |
211
248
  | `{{DATABASE}}` | Database/ORM | `Prisma`, `Eloquent`, `EF Core`, `SQLAlchemy`, `ActiveRecord` |
212
249
  | `{{TEST_FRAMEWORK}}` | Testing framework | `Vitest`, `PHPUnit`, `xUnit`, `pytest`, `RSpec`, `JUnit` |
@@ -229,6 +266,7 @@ Templates use `{{PLACEHOLDER}}` syntax for values you need to customize. The boo
229
266
  | Placeholder | Description | Example |
230
267
  | --------------------- | -------------------- | --------------------------------------------------- |
231
268
  | `{{INSTALL_COMMAND}}` | Install dependencies | `npm install`, `composer install`, `dotnet restore` |
269
+ | `{{SETUP_COMMAND}}` | Install command | `npm install`, `composer install`, `dotnet restore` |
232
270
  | `{{DEV_COMMAND}}` | Run dev server | `npm run dev`, `php artisan serve`, `dotnet watch` |
233
271
  | `{{BUILD_COMMAND}}` | Build for production | `npm run build`, `dotnet publish` |
234
272
  | `{{TEST_COMMAND}}` | Run tests | `npm test`, `./vendor/bin/phpunit`, `dotnet test` |
@@ -260,6 +298,17 @@ Templates use `{{PLACEHOLDER}}` syntax for values you need to customize. The boo
260
298
  | `{{APP_3}}` | Third app name | `web`, `marketing`, `landing` |
261
299
  | `{{PROJECT_STRUCTURE}}` | Full project structure | (Generated tree diagram) |
262
300
 
301
+ ### Factory Placeholders
302
+
303
+ Used by the opt-in factory (`.agents/skills/factory/SKILL.md` and `.claude/agents-snippets/factory-*.md`).
304
+
305
+ | Placeholder | Description | Example |
306
+ | --------------------------- | ------------------------------------------------------------------------------------------- | ------------------------ |
307
+ | `{{DEFAULT_BRANCH}}` | Branch factory jobs start from and merge into | `main`, `master` |
308
+ | `{{FACTORY_PORT_BASE}}` | Base dev-server port. Job `n` uses `base + 10n` for the builder, `+1` for ux, and `+2` for ui | `5100` |
309
+ | `{{APPROVAL_MODE}}` | `human` stops at `awaiting-human`; `auto` merges on approval | `human`, `auto` |
310
+ | `{{FACTORY_REVIEW_LENSES}}` | Review lenses to run. `ux` and `ui` need browser tooling | `code, security` |
311
+
263
312
  ## Supported Ecosystems
264
313
 
265
314
  The bootstrap command supports the following language ecosystems:
@@ -585,6 +634,43 @@ The bundled skills complement custom agents:
585
634
  - **`writing-plans`** — Creating the plans that Feature Builder and TDD agents execute
586
635
  - **`test-driven-development`** — The TDD methodology that Red/Green/Refactor agents implement
587
636
 
637
+ ## Factory (opt-in, v5)
638
+
639
+ The factory is an opt-in pipeline for feature-sized work. One orchestrator takes a feature through spec → plan gate → build → review → approve → merge. Each job belongs to a feature folder, `docs/features/<KEY>-<slug>/` (or `docs/features/<slug>/` before a ticket exists), which holds `spec.md` and, for large work, a slim `tasks.md`. Job state lives under `factory/jobs/<NNN-slug>/`, and each job builds in its own git worktree on branch `factory/<id>` (or in place on the job's branch, when you ask for `--in-place`).
640
+
641
+ It runs alongside Feature Builder and TDD and does not replace them. Nothing runs until you invoke `/factory`. Quick fixes stay direct.
642
+
643
+ - **Agents:** `factory-spec-writer`, `factory-builder`, `factory-reviewer-code`, `factory-reviewer-security`, `factory-reviewer-ux`, `factory-reviewer-ui`, and `factory-approver`. Wrappers ship for Claude Code, GitHub Copilot, Cursor, and OpenCode. Codex uses the orchestrator skill only.
644
+ - **Setup and reference:** [`src/repo/factory/README.md`](src/repo/factory/README.md) (the monorepo copy is in `src/monorepo/factory/`). The installer copies `factory/` into your project.
645
+
646
+ | Command | What it does |
647
+ | --- | --- |
648
+ | `/factory <feature request>` | Intake: reads the project and the features it touches, asks only the glaring questions, then asks you to continue or change the answers. Nothing is created before you continue |
649
+ | `/factory start <KEY \| slug \| job-id>` | Pick up an existing feature folder by ticket key or slug, or resume its open job (for example one waiting at a gate). Add `--in-place` only for a new job |
650
+ | `/factory approve <job-id>` | Merge a job waiting for you (`awaiting-human` or `needs-human`); on a `merged` job, move its tickets to Done |
651
+ | `/factory rework <job-id> <note>` | Restart the builder with your note and a fresh retry budget |
652
+ | `/factory status [--all]` | Summarize open jobs (read-only). Closed jobs are hidden unless `--all` |
653
+ | `/factory watch [--all]` | Show the status table here, then print the command to run the live view in your terminal (read-only). Closed jobs are hidden unless `--all` |
654
+ | `/factory archive [--all] [--dry-run]` | Move closed jobs (`merged` or `abandoned`) from `factory/jobs/` to `factory/archive/`. By default only jobs not updated for 7 days move. `--dry-run` moves nothing. Open jobs never move |
655
+ | `/factory prune [--all] [--abandoned] [--dry-run]` | Permanently delete closed jobs from `factory/jobs/` and `factory/archive/`, after one confirmation. By default only `merged` jobs (same 7-day age rule as archive; `--all` drops the age limit). `--abandoned` also deletes `abandoned` jobs. Open jobs and `docs/features/` are never touched |
656
+
657
+ **Two gates are mandatory.** Every job stops at `awaiting-spec` after the spec, and at `awaiting-plan` after the plan. At the spec gate you confirm the spec or request changes. At the plan gate you review the tasks, the size, the tracking proposal, and the build mode, then confirm or request changes. Leave either gate for later with `/factory start <job-id>`. Nothing is ticketed or built before you confirm the plan.
658
+
659
+ **The builder also writes `qa.md`.** It lists how to run the feature, numbered manual checks tied to the acceptance criteria, and what the automated tests do not cover. The orchestrator shows it before approval as suggested checks. It is not a gate.
660
+
661
+ **The approval gate is on by default.** With `{{APPROVAL_MODE}}` set to `human`, an approved job stops at `awaiting-human` until you run `/factory approve <job-id>`. Set `{{APPROVAL_MODE}}` to `auto` to merge on approval instead. The approver still escalates changes to auth, payments, migrations, and similar high-risk areas. Merges are local, and the factory never pushes.
662
+
663
+ Check jobs from a terminal:
664
+
665
+ ```bash
666
+ npx nitm-ai-dev-toolkit factory status [--all] # one-shot table of open jobs (--all: closed too)
667
+ npx nitm-ai-dev-toolkit factory watch [--all] # live view: ↑/↓ to select, Enter to open, q to quit
668
+ npx nitm-ai-dev-toolkit factory archive [--all] [--dry-run] # move closed jobs to factory/archive/
669
+ npx nitm-ai-dev-toolkit factory prune [--all] [--abandoned] [--dry-run] # list merged jobs to delete; add --yes to delete them
670
+ ```
671
+
672
+ `status` and `watch` read `factory/jobs/*/job.json`, write nothing, and hide closed jobs unless `--all` is passed. `archive` moves closed jobs and touches no git state. `prune` permanently deletes merged jobs from both folders, after you confirm. Pass `--abandoned` to delete abandoned jobs too. Neither touches open jobs or `docs/features/`. The factory's placeholders are listed under [Factory Placeholders](#factory-placeholders).
673
+
588
674
  ## AI Agent Skills
589
675
 
590
676
  Enhance your AI agents with specialized capabilities using the **Agent Skills** open format. Skills are folders of instructions, scripts, and resources that agents can discover and use to perform tasks more accurately and efficiently.
@@ -662,7 +748,7 @@ Skills are installed to `{.cursor|.agents|.github}/skills/` and automatically av
662
748
 
663
749
  ### Bundled Universal Skills
664
750
 
665
- This toolkit includes **32 pre-installed universal skills** in the `.agents/skills/` folder. These skills are copied automatically when you set up your project and require **no additional installation**.
751
+ This toolkit includes **33 pre-installed universal skills** in the `.agents/skills/` folder. These skills are copied automatically when you set up your project and require **no additional installation**.
666
752
 
667
753
  | Skill | Description |
668
754
  | ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
@@ -671,6 +757,7 @@ This toolkit includes **32 pre-installed universal skills** in the `.agents/skil
671
757
  | **verification-before-completion** | Quality checks before claiming work is done. Evidence before assertions, always. |
672
758
  | **writing-plans** | Creating clear implementation plans before coding. |
673
759
  | **executing-plans** | Following through on implementation plans systematically. |
760
+ | **factory** | Opt-in spec-to-merge feature pipeline with review lenses and an approval gate. |
674
761
  | **requesting-code-review** | Best practices for requesting code reviews from teammates. |
675
762
  | **receiving-code-review** | Responding constructively to review feedback. |
676
763
  | **brainstorming** | Structured ideation and problem-solving sessions. |
@@ -701,7 +788,7 @@ This toolkit includes **32 pre-installed universal skills** in the `.agents/skil
701
788
 
702
789
  **These skills are ready to use immediately after copying the templates.** No `npx -y skills add -a <agent> <owner/repo> --skill '*' --agent github-copilot cursor` commands needed.
703
790
 
704
- The **ponytail** skill bundle is an integrated optional skill set from [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail) that adds YAGNI/stdlib/native/one-line/minimum discipline to your AI agents. It includes 6 skills, 6 commands, an OpenCode plugin, 7 activation hooks, and ambient agent rules — all pre-configured and ready to use.
791
+ The **ponytail** skill bundle is an integrated optional skill set from [DietrichGebert/ponytail](https://github.com/DietrichGebert/ponytail) that adds YAGNI/stdlib/native/one-line/minimum discipline to your AI agents. It includes 6 skills, 6 commands, 7 activation hooks, and ambient agent rules. OpenCode uses the author's upstream `@dietrichgebert/ponytail@4.12.0` package, not a bundled local plugin. See [native setup](#native-opencode-v2-toolkit-400); scaffolding alone does not establish activation.
705
792
 
706
793
  ### Additional Recommended Skills
707
794
 
@@ -862,7 +949,7 @@ After copying templates and installing additional skills, your project will have
862
949
  │ └── SKILL.md
863
950
  ├── verification-before-completion/
864
951
  │ └── SKILL.md
865
- └── ... (32 skills total)
952
+ └── ... (33 skills total)
866
953
 
867
954
  .cursor/ # or .github/ for GitHub Copilot
868
955
  ├── rules/ # IDE behavior rules (from this toolkit)
package/bin/cli.js CHANGED
@@ -9,6 +9,9 @@
9
9
  const fs = require('fs');
10
10
  const path = require('path');
11
11
  const { execSync, spawnSync } = require('child_process');
12
+ const factory = require('./factory-watch.js');
13
+ const factoryArchive = require('./factory-archive.js');
14
+ const factoryPrune = require('./factory-prune.js');
12
15
 
13
16
  const PKG_ROOT = path.join(__dirname, '..');
14
17
  const SRC_DIR = path.join(PKG_ROOT, 'src');
@@ -16,10 +19,13 @@ const SRC_DIR = path.join(PKG_ROOT, 'src');
16
19
  // Platform config dirs per harness. Claude and Codex share `.agents` (universal skills).
17
20
  const ENV_DIRS = {
18
21
  claude: ['.claude', '.agents'],
19
- codex: ['.codex', '.agents', path.join('.github', 'instructions'), path.join('.claude', 'rules-snippets')],
22
+ codex: ['.codex', '.agents', path.join('.github', 'instructions')],
20
23
  copilot: ['.github', '.vscode'],
21
24
  cursor: ['.cursor'],
22
- opencode: ['.opencode'],
25
+ // Shared reference sources, not other harnesses' active config/agent wrappers.
26
+ opencode: ['.opencode', path.join('.agents', 'skills'),
27
+ path.join('.claude', 'agents-snippets'), path.join('.claude', 'prompt-snippets'),
28
+ path.join('.github', 'instructions')],
23
29
  };
24
30
  const ENV_INFO = {
25
31
  claude: { name: 'Claude Code', command: '/bootstrap (Claude Code command in .claude/commands/)' },
@@ -30,8 +36,17 @@ const ENV_INFO = {
30
36
  };
31
37
  // Files/dirs copied for every environment (shared across harnesses).
32
38
  const SHARED_ROOT = ['AGENTS.md', 'README.md', 'CLAUDE.md', '.mcp.json'];
33
- const SHARED_DIRS = ['docs', 'templates', 'hooks'];
34
-
39
+ // `factory` is the job queue every harness reads and writes, so it is shared.
40
+ // Agent/skill bodies and prompt snippets are referenced by wrappers in every harness
41
+ // (e.g. `.claude/agents-snippets/*` from Cursor and Copilot agents), so they are shared too.
42
+ // `.claude/rules-snippets` is linked the same way from every harness's rules and instructions.
43
+ // Only `.agents/skills` is shared: `.agents/rules` is Claude/Codex ambient config.
44
+ const SHARED_DIRS = ['docs', 'templates', 'hooks', 'factory', path.join('.agents', 'skills'),
45
+ path.join('.claude', 'agents-snippets'), path.join('.claude', 'prompt-snippets'),
46
+ path.join('.claude', 'rules-snippets')];
47
+ // npm drops `.gitignore` files from packages, so templates ship them as `_gitignore`
48
+ // and install them under the real name.
49
+ const TEMPLATE_RENAMES = { '_gitignore': '.gitignore' };
35
50
  const C = {
36
51
  reset: '\x1b[0m',
37
52
  cyan: '\x1b[36m',
@@ -82,6 +97,12 @@ function templateFiles(type) {
82
97
  return files;
83
98
  }
84
99
 
100
+ // Path a template file is installed to (see TEMPLATE_RENAMES).
101
+ function installName(rel) {
102
+ const renamed = TEMPLATE_RENAMES[path.basename(rel)];
103
+ return renamed ? path.join(path.dirname(rel), renamed) : rel;
104
+ }
105
+
85
106
  // Restrict the file set to the chosen environment (shared + that env's dirs).
86
107
  function scopeFiles(files, env) {
87
108
  if (!env || !ENV_DIRS[env]) return files;
@@ -102,7 +123,8 @@ function copyMissing(type, cwd, force, env) {
102
123
  let skipped = 0;
103
124
  for (const rel of files) {
104
125
  const from = path.join(src, rel);
105
- const to = path.join(cwd, rel);
126
+ const dest = installName(rel);
127
+ const to = path.join(cwd, dest);
106
128
  if (fs.existsSync(to) && !force) {
107
129
  skipped++;
108
130
  continue;
@@ -127,7 +149,8 @@ function conflicts(type, cwd, env) {
127
149
  const files = scopeFiles(templateFiles(type), env);
128
150
  const out = [];
129
151
  for (const rel of files) {
130
- if (fs.existsSync(path.join(cwd, rel))) out.push(rel);
152
+ const dest = installName(rel);
153
+ if (fs.existsSync(path.join(cwd, dest))) out.push(dest);
131
154
  }
132
155
  return out;
133
156
  }
@@ -216,14 +239,25 @@ function cmdPatch(cwd, flags) {
216
239
  return { copied, skipped };
217
240
  }
218
241
 
242
+ // Placeholders the agent fills per task or feature at runtime, so they are not bootstrap leftovers.
243
+ const RUNTIME_PLACEHOLDERS = new Set(['ISSUE_ID', 'ISSUE_KEY', 'FEATURE_NAME', 'NAME']);
244
+ // `templates/` ships blank forms whose placeholders are meant to stay until a feature is written.
245
+ const PLACEHOLDER_EXEMPT_DIR = 'templates';
246
+
247
+ function hasUnresolvedPlaceholder(content) {
248
+ const names = content.match(/\{\{[A-Z][A-Z0-9_]*\}\}/g) || [];
249
+ return names.some((name) => !RUNTIME_PLACEHOLDERS.has(name.slice(2, -2)));
250
+ }
251
+
219
252
  function scanPlaceholders(cwd, relFiles) {
220
253
  const found = [];
221
254
  for (const rel of relFiles) {
255
+ if (path.normalize(rel).split(path.sep)[0] === PLACEHOLDER_EXEMPT_DIR) continue;
222
256
  const p = path.join(cwd, rel);
223
257
  if (!fs.existsSync(p)) continue;
224
258
  try {
225
259
  const content = fs.readFileSync(p, 'utf8');
226
- if (/\{\{[A-Z_]+\}\}/.test(content)) found.push(rel);
260
+ if (hasUnresolvedPlaceholder(content)) found.push(rel);
227
261
  } catch (_) { /* ignore */ }
228
262
  }
229
263
  return found;
@@ -231,7 +265,7 @@ function scanPlaceholders(cwd, relFiles) {
231
265
 
232
266
  function cmdDoctor(cwd, flags) {
233
267
  const type = detectType(cwd, flags);
234
- const files = scopeFiles(templateFiles(type), flags.env);
268
+ const files = scopeFiles(templateFiles(type), flags.env).map(installName);
235
269
  const missingFiles = [];
236
270
  let present = 0;
237
271
  for (const rel of files) {
@@ -258,6 +292,16 @@ function cmdDoctor(cwd, flags) {
258
292
  return result;
259
293
  }
260
294
 
295
+ // Upgrade runs the latest package in patch mode: missing files are added and existing
296
+ // (possibly customised) files are kept. Only an explicit --force reaches `install --force`.
297
+ function upgradeArgs(flags) {
298
+ const force = Boolean(flags && flags.force);
299
+ const args = ['--yes', 'nitm-ai-dev-toolkit@latest', force ? 'install' : 'patch'];
300
+ if (force) args.push('--force');
301
+ if (flags && flags.env) args.push('--env', flags.env);
302
+ return args;
303
+ }
304
+
261
305
  function cmdUpgrade(cwd, flags) {
262
306
  let latest;
263
307
  try {
@@ -272,17 +316,17 @@ function cmdUpgrade(cwd, flags) {
272
316
  } catch (_) {
273
317
  cur = 'unknown';
274
318
  }
275
- if (latest === cur) {
276
- ok(`Already on latest version (${cur}).`);
277
- return;
319
+ if (latest === cur) info(`Already on latest version (${cur}); refreshing missing files and the handoff.`);
320
+ else info(`New version available: ${latest} (current ${cur}).`);
321
+ if (flags && flags.force) {
322
+ warn('--force: existing files will be overwritten with the latest templates. Back up your customisations first.');
323
+ } else {
324
+ info('Adding missing files only; your existing files are kept. Pass --force to overwrite them.');
278
325
  }
279
- info(`New version available: ${latest} (current ${cur}).`);
280
326
  info('Upgrading via latest package...');
281
- const args = ['--yes', 'nitm-ai-dev-toolkit@latest', 'install', '--force'];
282
- if (flags && flags.env) args.push('--env', flags.env);
283
- const r = spawnSync('npx', args, { cwd, stdio: 'inherit', timeout: 120000 });
327
+ const r = spawnSync('npx', upgradeArgs(flags), { cwd, stdio: 'inherit', timeout: 120000 });
284
328
  if (r.error) { err('Upgrade failed: ' + r.error.message); return; }
285
- if (r.status === 0) ok('Upgrade complete.');
329
+ if (r.status === 0) ok('Upgrade complete. Hand off to your AI agent with .nitm/BOOTSTRAP.md to finish setup.');
286
330
  else err('Upgrade failed.');
287
331
  }
288
332
 
@@ -303,6 +347,38 @@ function cmdOmoStarter(cwd, flags) {
303
347
  }
304
348
  }
305
349
 
350
+ const FACTORY_USAGE = 'Usage: nitm-ai-dev-toolkit factory <status|watch|archive|prune> [--dir <path>] [--all] [--abandoned] [--interval <ms>] [--dry-run] [--yes]';
351
+ const FACTORY_SUB_USAGE = {
352
+ archive: 'Usage: nitm-ai-dev-toolkit factory archive [--dir <path>] [--all] [--dry-run]',
353
+ prune: 'Usage: nitm-ai-dev-toolkit factory prune [--dir <path>] [--all] [--abandoned] [--dry-run] [--yes]',
354
+ };
355
+
356
+ function cmdFactory(cwd, flags) {
357
+ const sub = flags._[1];
358
+ // --abandoned changes what prune deletes; elsewhere it would be a silent no-op, so refuse it.
359
+ if (flags.abandoned && sub !== 'prune') {
360
+ err('--abandoned is only for factory prune');
361
+ process.exitCode = 1;
362
+ return;
363
+ }
364
+ if (sub === 'status') return factory.cmdFactoryStatus(cwd, flags);
365
+ if (sub === 'watch') return factory.cmdFactoryWatch(cwd, flags);
366
+ if (sub === 'archive' || sub === 'prune') {
367
+ const extra = flags._.slice(2);
368
+ if (extra.length) {
369
+ err(`Unknown option for factory ${sub}: ${extra[0]}`);
370
+ err(FACTORY_SUB_USAGE[sub]);
371
+ process.exitCode = 1;
372
+ return;
373
+ }
374
+ return sub === 'archive'
375
+ ? factoryArchive.cmdFactoryArchive(cwd, flags)
376
+ : factoryPrune.cmdFactoryPrune(cwd, flags);
377
+ }
378
+ err(FACTORY_USAGE);
379
+ process.exitCode = 1;
380
+ }
381
+
306
382
  function parseFlags(argv) {
307
383
  const flags = { _: [] };
308
384
  for (let i = 0; i < argv.length; i++) {
@@ -310,7 +386,21 @@ function parseFlags(argv) {
310
386
  if (a === '--monorepo') flags.monorepo = true;
311
387
  else if (a === '--repo') flags.repo = true;
312
388
  else if (a === '--force' || a === '-f') flags.force = true;
389
+ else if (a === '--all') flags.all = true;
390
+ else if (a === '--dry-run') flags.dryRun = true;
391
+ else if (a === '--yes') flags.yes = true;
392
+ else if (a === '--abandoned') flags.abandoned = true;
313
393
  else if (a === '--help' || a === '-h') flags.help = true;
394
+ else if (a === '--dir' || a === '--interval') {
395
+ const v = argv[++i];
396
+ if (v === undefined || v.startsWith('--')) throw new Error(`${a} requires a value`);
397
+ flags[a.slice(2)] = v;
398
+ } else if (a.startsWith('--dir=') || a.startsWith('--interval=')) {
399
+ const eq = a.indexOf('=');
400
+ const v = a.slice(eq + 1);
401
+ if (!v) throw new Error(`${a.slice(0, eq)}= requires a value`);
402
+ flags[a.slice(2, eq)] = v;
403
+ }
314
404
  else if (a === '--env') {
315
405
  const v = argv[++i];
316
406
  if (v === undefined || v.startsWith('--')) throw new Error('--env requires a value (e.g. --env claude)');
@@ -338,8 +428,10 @@ Usage:
338
428
  Add only missing template files (never overwrites existing ones), then
339
429
  re-emit the bootstrap handoff so your AI agent can finish configuration.
340
430
 
341
- npx nitm-ai-dev-toolkit upgrade
342
- Upgrade to the latest published version (re-scaffolds via @latest).
431
+ npx nitm-ai-dev-toolkit upgrade [--env <env>] [--force]
432
+ Upgrade to the latest published version. Like \`patch\`, adds missing files and
433
+ keeps your existing ones, then re-emits the bootstrap handoff. --force also
434
+ overwrites existing files with the latest templates (back up customisations first).
343
435
 
344
436
  npx nitm-ai-dev-toolkit doctor [--monorepo|--repo] [--env <env>]
345
437
  Inspect the current install: missing files, unresolved {{PLACEHOLDER}}
@@ -349,6 +441,31 @@ Usage:
349
441
  Run \`npx nitm-opencode-starter install\` to scaffold the oh-my-opencode-slim
350
442
  starter (published to npm). Falls back to printing the command if unavailable.
351
443
 
444
+ npx nitm-ai-dev-toolkit factory status [--dir <path>] [--all]
445
+ Print one table of factory jobs (factory/jobs/<NNN-slug>/job.json), then exit.
446
+ Closed jobs (merged, abandoned) are hidden and counted; --all shows them,
447
+ plus archived jobs from factory/archive/ (marked [archived]).
448
+ Read-only. Run /factory <feature> in your agent to create jobs.
449
+
450
+ npx nitm-ai-dev-toolkit factory watch [--dir <path>] [--interval <ms>] [--all]
451
+ Live terminal view of factory jobs. ↑/↓ or j/k select, Enter opens a job's
452
+ markdown files, q quits. Closed jobs are hidden unless --all.
453
+ Not a TTY? Behaves like \`factory status\`.
454
+
455
+ npx nitm-ai-dev-toolkit factory archive [--dir <path>] [--all] [--dry-run]
456
+ Move closed jobs (merged, abandoned) from factory/jobs/ to factory/archive/.
457
+ Default: only jobs closed more than 7 days ago. --all: every closed job.
458
+ --dry-run: list what would move, move nothing. Open jobs are never touched.
459
+ Does not touch git or branches (/factory archive and /factory prune handle those).
460
+
461
+ npx nitm-ai-dev-toolkit factory prune [--dir <path>] [--all] [--abandoned] [--dry-run] [--yes]
462
+ PERMANENTLY delete closed jobs from factory/jobs/ and factory/archive/.
463
+ Default: only merged jobs closed more than 7 days ago. --all: every merged job
464
+ (no age limit). --abandoned: also delete abandoned jobs (same age rule).
465
+ Without --yes it only lists what would be deleted. --dry-run always only
466
+ lists, even with --yes. Open jobs are never touched. Deletes job folders only;
467
+ git, docs and branches are not touched.
468
+
352
469
  Options:
353
470
  --env <env> Optional. Limit the scaffold to ONE harness:
354
471
  claude | codex | copilot | cursor | opencode
@@ -358,26 +475,36 @@ Options:
358
475
  file directly (no native /bootstrap command is provided).
359
476
  --monorepo Force monorepo template
360
477
  --repo Force single-repo template
361
- --force Overwrite existing files
478
+ --force Overwrite existing files (install, upgrade). patch never overwrites
479
+ --dir <path> factory: jobs directory (default: ./factory/jobs)
480
+ --interval <ms>
481
+ factory watch: refresh interval (default: 2000, minimum: 100)
482
+ --all factory status/watch: include closed and archived jobs
483
+ factory archive: include every closed job, not just those older than 7 days
484
+ factory prune: include every merged job, not just those older than 7 days
485
+ --abandoned factory prune: also delete abandoned jobs (default: merged only)
486
+ --dry-run factory archive/prune: list only, change nothing
487
+ --yes factory prune: actually delete (without it, prune only lists)
362
488
  -h, --help Show this help
363
489
  `);
364
490
  }
365
491
 
366
492
  function main() {
367
- const flags = parseFlags(process.argv.slice(2));
368
- if (flags.help) {
369
- usage();
370
- return;
371
- }
372
- const cmd = flags._[0];
373
493
  const cwd = process.cwd();
374
494
  try {
495
+ const flags = parseFlags(process.argv.slice(2));
496
+ if (flags.help) {
497
+ usage();
498
+ return;
499
+ }
500
+ const cmd = flags._[0];
375
501
  switch (cmd) {
376
502
  case 'install': return cmdInstall(cwd, flags);
377
503
  case 'patch': return cmdPatch(cwd, flags);
378
504
  case 'upgrade': return cmdUpgrade(cwd, flags);
379
505
  case 'doctor': return cmdDoctor(cwd, flags);
380
506
  case 'omo-slim-starter': return cmdOmoStarter(cwd, flags);
507
+ case 'factory': return cmdFactory(cwd, flags);
381
508
  default:
382
509
  if (cmd) err(`Unknown command: ${cmd}`);
383
510
  usage();
@@ -394,6 +521,7 @@ module.exports = {
394
521
  detectType,
395
522
  scopeFiles,
396
523
  templateFiles,
524
+ installName,
397
525
  copyMissing,
398
526
  scanPlaceholders,
399
527
  envHeader,
@@ -403,7 +531,9 @@ module.exports = {
403
531
  cmdPatch,
404
532
  cmdDoctor,
405
533
  cmdUpgrade,
534
+ upgradeArgs,
406
535
  cmdOmoStarter,
536
+ cmdFactory,
407
537
  ENV_DIRS,
408
538
  SHARED_ROOT,
409
539
  SHARED_DIRS,