nitm-ai-dev-toolkit 4.0.0 → 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.
- package/CHANGELOG.md +37 -1
- package/README.md +60 -4
- package/bin/cli.js +152 -25
- package/bin/factory-archive.js +71 -0
- package/bin/factory-prune.js +79 -0
- package/bin/factory-watch.js +509 -0
- package/package.json +1 -1
- package/src/monorepo/.agents/skills/factory/SKILL.md +593 -0
- package/src/monorepo/.claude/agents/admin-portal.agent.md +1 -1
- package/src/monorepo/.claude/agents/api-specialist.agent.md +1 -1
- package/src/monorepo/.claude/agents/backend-architect.agent.md +1 -1
- package/src/monorepo/.claude/agents/documenter.agent.md +1 -1
- package/src/monorepo/.claude/agents/factory-approver.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-builder.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-code.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-security.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-ui.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-reviewer-ux.agent.md +12 -0
- package/src/monorepo/.claude/agents/factory-spec-writer.agent.md +12 -0
- package/src/monorepo/.claude/agents/feature-builder.agent.md +1 -1
- package/src/monorepo/.claude/agents/frontend-developer.agent.md +1 -1
- package/src/monorepo/.claude/agents/green.agent.md +1 -1
- package/src/monorepo/.claude/agents/implementer.agent.md +1 -1
- package/src/monorepo/.claude/agents/planner.agent.md +1 -1
- package/src/monorepo/.claude/agents/red.agent.md +1 -1
- package/src/monorepo/.claude/agents/refactor.agent.md +1 -1
- package/src/monorepo/.claude/agents/researcher.agent.md +1 -1
- package/src/monorepo/.claude/agents/reviewer.agent.md +1 -1
- package/src/monorepo/.claude/agents/tdd.agent.md +1 -1
- package/src/monorepo/.claude/agents-snippets/factory-approver.md +72 -0
- package/src/monorepo/.claude/agents-snippets/factory-builder.md +101 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-code.md +57 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-security.md +48 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-ui.md +64 -0
- package/src/monorepo/.claude/agents-snippets/factory-reviewer-ux.md +64 -0
- package/src/monorepo/.claude/agents-snippets/factory-spec-writer.md +119 -0
- package/src/monorepo/.claude/agents-snippets/feature-builder.md +1 -1
- package/src/monorepo/.claude/commands/factory.md +24 -0
- package/src/monorepo/.claude/prompt-snippets/assign-tasks.md +6 -2
- package/src/monorepo/.claude/prompt-snippets/bootstrap.md +22 -13
- package/src/monorepo/.claude/prompt-snippets/factory.md +20 -0
- package/src/monorepo/.claude/prompt-snippets/implement-feature.md +2 -2
- package/src/monorepo/.claude/prompt-snippets/tasks.md +49 -87
- package/src/monorepo/.claude/rules-snippets/documentation.md +8 -6
- package/src/monorepo/.cursor/agents/factory-approver.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-builder.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-code.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-security.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/monorepo/.cursor/agents/factory-spec-writer.agent.md +11 -0
- package/src/monorepo/.cursor/commands/factory.md +24 -0
- package/src/monorepo/.github/agents/factory-approver.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-builder.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-code.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-security.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/monorepo/.github/agents/factory-spec-writer.agent.md +11 -0
- package/src/monorepo/.github/prompts/factory.prompt.md +25 -0
- package/src/monorepo/.opencode/agents/factory-approver.md +20 -0
- package/src/monorepo/.opencode/agents/factory-builder.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-code.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-security.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-ui.md +20 -0
- package/src/monorepo/.opencode/agents/factory-reviewer-ux.md +20 -0
- package/src/monorepo/.opencode/agents/factory-spec-writer.md +20 -0
- package/src/monorepo/.opencode/commands/factory.md +29 -0
- package/src/monorepo/AGENTS.md +22 -1
- package/src/monorepo/README.md +6 -0
- package/src/monorepo/factory/README.md +194 -0
- package/src/monorepo/factory/_gitignore +5 -0
- package/src/repo/.agents/skills/factory/SKILL.md +593 -0
- package/src/repo/.claude/agents/admin-portal.agent.md +1 -1
- package/src/repo/.claude/agents/api-specialist.agent.md +1 -1
- package/src/repo/.claude/agents/backend-architect.agent.md +1 -1
- package/src/repo/.claude/agents/documenter.agent.md +1 -1
- package/src/repo/.claude/agents/factory-approver.agent.md +12 -0
- package/src/repo/.claude/agents/factory-builder.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-code.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-security.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-ui.agent.md +12 -0
- package/src/repo/.claude/agents/factory-reviewer-ux.agent.md +12 -0
- package/src/repo/.claude/agents/factory-spec-writer.agent.md +12 -0
- package/src/repo/.claude/agents/feature-builder.agent.md +1 -1
- package/src/repo/.claude/agents/frontend-developer.agent.md +1 -1
- package/src/repo/.claude/agents/green.agent.md +1 -1
- package/src/repo/.claude/agents/implementer.agent.md +1 -1
- package/src/repo/.claude/agents/planner.agent.md +1 -1
- package/src/repo/.claude/agents/red.agent.md +1 -1
- package/src/repo/.claude/agents/refactor.agent.md +1 -1
- package/src/repo/.claude/agents/researcher.agent.md +1 -1
- package/src/repo/.claude/agents/reviewer.agent.md +1 -1
- package/src/repo/.claude/agents/tdd.agent.md +1 -1
- package/src/repo/.claude/agents-snippets/factory-approver.md +72 -0
- package/src/repo/.claude/agents-snippets/factory-builder.md +100 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-code.md +57 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-security.md +48 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-ui.md +64 -0
- package/src/repo/.claude/agents-snippets/factory-reviewer-ux.md +64 -0
- package/src/repo/.claude/agents-snippets/factory-spec-writer.md +117 -0
- package/src/repo/.claude/agents-snippets/feature-builder.md +1 -1
- package/src/repo/.claude/commands/factory.md +24 -0
- package/src/repo/.claude/prompt-snippets/assign-tasks.md +6 -2
- package/src/repo/.claude/prompt-snippets/bootstrap.md +30 -20
- package/src/repo/.claude/prompt-snippets/factory.md +20 -0
- package/src/repo/.claude/prompt-snippets/implement-feature.md +2 -2
- package/src/repo/.claude/prompt-snippets/tasks.md +49 -112
- package/src/repo/.cursor/agents/factory-approver.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-builder.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-code.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-security.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/repo/.cursor/agents/factory-spec-writer.agent.md +11 -0
- package/src/repo/.cursor/commands/factory.md +24 -0
- package/src/repo/.github/agents/factory-approver.agent.md +11 -0
- package/src/repo/.github/agents/factory-builder.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-code.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-security.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-ui.agent.md +11 -0
- package/src/repo/.github/agents/factory-reviewer-ux.agent.md +11 -0
- package/src/repo/.github/agents/factory-spec-writer.agent.md +11 -0
- package/src/repo/.github/prompts/factory.prompt.md +25 -0
- package/src/repo/.opencode/agents/factory-approver.md +20 -0
- package/src/repo/.opencode/agents/factory-builder.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-code.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-security.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-ui.md +20 -0
- package/src/repo/.opencode/agents/factory-reviewer-ux.md +20 -0
- package/src/repo/.opencode/agents/factory-spec-writer.md +20 -0
- package/src/repo/.opencode/commands/factory.md +29 -0
- package/src/repo/AGENTS.md +19 -1
- package/src/repo/README.md +6 -0
- package/src/repo/factory/README.md +194 -0
- package/src/repo/factory/_gitignore +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ 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
|
+
|
|
10
45
|
### Migration
|
|
11
46
|
|
|
12
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.
|
|
@@ -118,5 +153,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
118
153
|
- Documentation structure templates (`docs/features/`, `docs/fixes/`)
|
|
119
154
|
- Monthly fix log template
|
|
120
155
|
|
|
121
|
-
[Unreleased]: https://github.com/ninjasitm/ai-assisted-dev-toolkit/compare/
|
|
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
|
|
122
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,6 +60,7 @@ 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
|
│
|
|
@@ -172,9 +174,12 @@ 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
|
```
|
|
@@ -214,7 +219,7 @@ Follow official V2 [migration](https://opencode.ai/v2/docs/migrate-v1), [config]
|
|
|
214
219
|
|
|
215
220
|
### Native Codex
|
|
216
221
|
|
|
217
|
-
- `--env codex` includes `.codex/`, `.agents/`, and shared context, plus `.github/instructions/`
|
|
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.
|
|
218
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.
|
|
219
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.
|
|
220
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.
|
|
@@ -238,6 +243,7 @@ Templates use `{{PLACEHOLDER}}` syntax for values you need to customize. The boo
|
|
|
238
243
|
| `{{PACKAGE_MANAGER}}` | Package manager | `npm`, `composer`, `dotnet`, `pip`, `bundler`, `go`, `cargo`, `maven` |
|
|
239
244
|
| `{{FILE_EXTENSION}}` | File extension | `ts`, `php`, `cs`, `py`, `rb`, `go`, `rs`, `java` |
|
|
240
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` |
|
|
241
247
|
| `{{DEPLOY_PLATFORM}}` | Deployment target | `Vercel`, `AWS`, `Azure`, `Heroku` |
|
|
242
248
|
| `{{DATABASE}}` | Database/ORM | `Prisma`, `Eloquent`, `EF Core`, `SQLAlchemy`, `ActiveRecord` |
|
|
243
249
|
| `{{TEST_FRAMEWORK}}` | Testing framework | `Vitest`, `PHPUnit`, `xUnit`, `pytest`, `RSpec`, `JUnit` |
|
|
@@ -260,6 +266,7 @@ Templates use `{{PLACEHOLDER}}` syntax for values you need to customize. The boo
|
|
|
260
266
|
| Placeholder | Description | Example |
|
|
261
267
|
| --------------------- | -------------------- | --------------------------------------------------- |
|
|
262
268
|
| `{{INSTALL_COMMAND}}` | Install dependencies | `npm install`, `composer install`, `dotnet restore` |
|
|
269
|
+
| `{{SETUP_COMMAND}}` | Install command | `npm install`, `composer install`, `dotnet restore` |
|
|
263
270
|
| `{{DEV_COMMAND}}` | Run dev server | `npm run dev`, `php artisan serve`, `dotnet watch` |
|
|
264
271
|
| `{{BUILD_COMMAND}}` | Build for production | `npm run build`, `dotnet publish` |
|
|
265
272
|
| `{{TEST_COMMAND}}` | Run tests | `npm test`, `./vendor/bin/phpunit`, `dotnet test` |
|
|
@@ -291,6 +298,17 @@ Templates use `{{PLACEHOLDER}}` syntax for values you need to customize. The boo
|
|
|
291
298
|
| `{{APP_3}}` | Third app name | `web`, `marketing`, `landing` |
|
|
292
299
|
| `{{PROJECT_STRUCTURE}}` | Full project structure | (Generated tree diagram) |
|
|
293
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
|
+
|
|
294
312
|
## Supported Ecosystems
|
|
295
313
|
|
|
296
314
|
The bootstrap command supports the following language ecosystems:
|
|
@@ -616,6 +634,43 @@ The bundled skills complement custom agents:
|
|
|
616
634
|
- **`writing-plans`** — Creating the plans that Feature Builder and TDD agents execute
|
|
617
635
|
- **`test-driven-development`** — The TDD methodology that Red/Green/Refactor agents implement
|
|
618
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
|
+
|
|
619
674
|
## AI Agent Skills
|
|
620
675
|
|
|
621
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.
|
|
@@ -693,7 +748,7 @@ Skills are installed to `{.cursor|.agents|.github}/skills/` and automatically av
|
|
|
693
748
|
|
|
694
749
|
### Bundled Universal Skills
|
|
695
750
|
|
|
696
|
-
This toolkit includes **
|
|
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**.
|
|
697
752
|
|
|
698
753
|
| Skill | Description |
|
|
699
754
|
| ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
@@ -702,6 +757,7 @@ This toolkit includes **32 pre-installed universal skills** in the `.agents/skil
|
|
|
702
757
|
| **verification-before-completion** | Quality checks before claiming work is done. Evidence before assertions, always. |
|
|
703
758
|
| **writing-plans** | Creating clear implementation plans before coding. |
|
|
704
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. |
|
|
705
761
|
| **requesting-code-review** | Best practices for requesting code reviews from teammates. |
|
|
706
762
|
| **receiving-code-review** | Responding constructively to review feedback. |
|
|
707
763
|
| **brainstorming** | Structured ideation and problem-solving sessions. |
|
|
@@ -893,7 +949,7 @@ After copying templates and installing additional skills, your project will have
|
|
|
893
949
|
│ └── SKILL.md
|
|
894
950
|
├── verification-before-completion/
|
|
895
951
|
│ └── SKILL.md
|
|
896
|
-
└── ... (
|
|
952
|
+
└── ... (33 skills total)
|
|
897
953
|
|
|
898
954
|
.cursor/ # or .github/ for GitHub Copilot
|
|
899
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,13 +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')
|
|
22
|
+
codex: ['.codex', '.agents', path.join('.github', 'instructions')],
|
|
20
23
|
copilot: ['.github', '.vscode'],
|
|
21
24
|
cursor: ['.cursor'],
|
|
22
25
|
// Shared reference sources, not other harnesses' active config/agent wrappers.
|
|
23
26
|
opencode: ['.opencode', path.join('.agents', 'skills'),
|
|
24
27
|
path.join('.claude', 'agents-snippets'), path.join('.claude', 'prompt-snippets'),
|
|
25
|
-
path.join('.
|
|
28
|
+
path.join('.github', 'instructions')],
|
|
26
29
|
};
|
|
27
30
|
const ENV_INFO = {
|
|
28
31
|
claude: { name: 'Claude Code', command: '/bootstrap (Claude Code command in .claude/commands/)' },
|
|
@@ -33,8 +36,17 @@ const ENV_INFO = {
|
|
|
33
36
|
};
|
|
34
37
|
// Files/dirs copied for every environment (shared across harnesses).
|
|
35
38
|
const SHARED_ROOT = ['AGENTS.md', 'README.md', 'CLAUDE.md', '.mcp.json'];
|
|
36
|
-
|
|
37
|
-
|
|
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' };
|
|
38
50
|
const C = {
|
|
39
51
|
reset: '\x1b[0m',
|
|
40
52
|
cyan: '\x1b[36m',
|
|
@@ -85,6 +97,12 @@ function templateFiles(type) {
|
|
|
85
97
|
return files;
|
|
86
98
|
}
|
|
87
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
|
+
|
|
88
106
|
// Restrict the file set to the chosen environment (shared + that env's dirs).
|
|
89
107
|
function scopeFiles(files, env) {
|
|
90
108
|
if (!env || !ENV_DIRS[env]) return files;
|
|
@@ -105,7 +123,8 @@ function copyMissing(type, cwd, force, env) {
|
|
|
105
123
|
let skipped = 0;
|
|
106
124
|
for (const rel of files) {
|
|
107
125
|
const from = path.join(src, rel);
|
|
108
|
-
const
|
|
126
|
+
const dest = installName(rel);
|
|
127
|
+
const to = path.join(cwd, dest);
|
|
109
128
|
if (fs.existsSync(to) && !force) {
|
|
110
129
|
skipped++;
|
|
111
130
|
continue;
|
|
@@ -130,7 +149,8 @@ function conflicts(type, cwd, env) {
|
|
|
130
149
|
const files = scopeFiles(templateFiles(type), env);
|
|
131
150
|
const out = [];
|
|
132
151
|
for (const rel of files) {
|
|
133
|
-
|
|
152
|
+
const dest = installName(rel);
|
|
153
|
+
if (fs.existsSync(path.join(cwd, dest))) out.push(dest);
|
|
134
154
|
}
|
|
135
155
|
return out;
|
|
136
156
|
}
|
|
@@ -219,14 +239,25 @@ function cmdPatch(cwd, flags) {
|
|
|
219
239
|
return { copied, skipped };
|
|
220
240
|
}
|
|
221
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
|
+
|
|
222
252
|
function scanPlaceholders(cwd, relFiles) {
|
|
223
253
|
const found = [];
|
|
224
254
|
for (const rel of relFiles) {
|
|
255
|
+
if (path.normalize(rel).split(path.sep)[0] === PLACEHOLDER_EXEMPT_DIR) continue;
|
|
225
256
|
const p = path.join(cwd, rel);
|
|
226
257
|
if (!fs.existsSync(p)) continue;
|
|
227
258
|
try {
|
|
228
259
|
const content = fs.readFileSync(p, 'utf8');
|
|
229
|
-
if (
|
|
260
|
+
if (hasUnresolvedPlaceholder(content)) found.push(rel);
|
|
230
261
|
} catch (_) { /* ignore */ }
|
|
231
262
|
}
|
|
232
263
|
return found;
|
|
@@ -234,7 +265,7 @@ function scanPlaceholders(cwd, relFiles) {
|
|
|
234
265
|
|
|
235
266
|
function cmdDoctor(cwd, flags) {
|
|
236
267
|
const type = detectType(cwd, flags);
|
|
237
|
-
const files = scopeFiles(templateFiles(type), flags.env);
|
|
268
|
+
const files = scopeFiles(templateFiles(type), flags.env).map(installName);
|
|
238
269
|
const missingFiles = [];
|
|
239
270
|
let present = 0;
|
|
240
271
|
for (const rel of files) {
|
|
@@ -261,6 +292,16 @@ function cmdDoctor(cwd, flags) {
|
|
|
261
292
|
return result;
|
|
262
293
|
}
|
|
263
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
|
+
|
|
264
305
|
function cmdUpgrade(cwd, flags) {
|
|
265
306
|
let latest;
|
|
266
307
|
try {
|
|
@@ -275,17 +316,17 @@ function cmdUpgrade(cwd, flags) {
|
|
|
275
316
|
} catch (_) {
|
|
276
317
|
cur = 'unknown';
|
|
277
318
|
}
|
|
278
|
-
if (latest === cur) {
|
|
279
|
-
|
|
280
|
-
|
|
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.');
|
|
281
325
|
}
|
|
282
|
-
info(`New version available: ${latest} (current ${cur}).`);
|
|
283
326
|
info('Upgrading via latest package...');
|
|
284
|
-
const
|
|
285
|
-
if (flags && flags.env) args.push('--env', flags.env);
|
|
286
|
-
const r = spawnSync('npx', args, { cwd, stdio: 'inherit', timeout: 120000 });
|
|
327
|
+
const r = spawnSync('npx', upgradeArgs(flags), { cwd, stdio: 'inherit', timeout: 120000 });
|
|
287
328
|
if (r.error) { err('Upgrade failed: ' + r.error.message); return; }
|
|
288
|
-
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.');
|
|
289
330
|
else err('Upgrade failed.');
|
|
290
331
|
}
|
|
291
332
|
|
|
@@ -306,6 +347,38 @@ function cmdOmoStarter(cwd, flags) {
|
|
|
306
347
|
}
|
|
307
348
|
}
|
|
308
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
|
+
|
|
309
382
|
function parseFlags(argv) {
|
|
310
383
|
const flags = { _: [] };
|
|
311
384
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -313,7 +386,21 @@ function parseFlags(argv) {
|
|
|
313
386
|
if (a === '--monorepo') flags.monorepo = true;
|
|
314
387
|
else if (a === '--repo') flags.repo = true;
|
|
315
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;
|
|
316
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
|
+
}
|
|
317
404
|
else if (a === '--env') {
|
|
318
405
|
const v = argv[++i];
|
|
319
406
|
if (v === undefined || v.startsWith('--')) throw new Error('--env requires a value (e.g. --env claude)');
|
|
@@ -341,8 +428,10 @@ Usage:
|
|
|
341
428
|
Add only missing template files (never overwrites existing ones), then
|
|
342
429
|
re-emit the bootstrap handoff so your AI agent can finish configuration.
|
|
343
430
|
|
|
344
|
-
npx nitm-ai-dev-toolkit upgrade
|
|
345
|
-
Upgrade to the latest published version
|
|
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).
|
|
346
435
|
|
|
347
436
|
npx nitm-ai-dev-toolkit doctor [--monorepo|--repo] [--env <env>]
|
|
348
437
|
Inspect the current install: missing files, unresolved {{PLACEHOLDER}}
|
|
@@ -352,6 +441,31 @@ Usage:
|
|
|
352
441
|
Run \`npx nitm-opencode-starter install\` to scaffold the oh-my-opencode-slim
|
|
353
442
|
starter (published to npm). Falls back to printing the command if unavailable.
|
|
354
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
|
+
|
|
355
469
|
Options:
|
|
356
470
|
--env <env> Optional. Limit the scaffold to ONE harness:
|
|
357
471
|
claude | codex | copilot | cursor | opencode
|
|
@@ -361,26 +475,36 @@ Options:
|
|
|
361
475
|
file directly (no native /bootstrap command is provided).
|
|
362
476
|
--monorepo Force monorepo template
|
|
363
477
|
--repo Force single-repo template
|
|
364
|
-
--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)
|
|
365
488
|
-h, --help Show this help
|
|
366
489
|
`);
|
|
367
490
|
}
|
|
368
491
|
|
|
369
492
|
function main() {
|
|
370
|
-
const flags = parseFlags(process.argv.slice(2));
|
|
371
|
-
if (flags.help) {
|
|
372
|
-
usage();
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
const cmd = flags._[0];
|
|
376
493
|
const cwd = process.cwd();
|
|
377
494
|
try {
|
|
495
|
+
const flags = parseFlags(process.argv.slice(2));
|
|
496
|
+
if (flags.help) {
|
|
497
|
+
usage();
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const cmd = flags._[0];
|
|
378
501
|
switch (cmd) {
|
|
379
502
|
case 'install': return cmdInstall(cwd, flags);
|
|
380
503
|
case 'patch': return cmdPatch(cwd, flags);
|
|
381
504
|
case 'upgrade': return cmdUpgrade(cwd, flags);
|
|
382
505
|
case 'doctor': return cmdDoctor(cwd, flags);
|
|
383
506
|
case 'omo-slim-starter': return cmdOmoStarter(cwd, flags);
|
|
507
|
+
case 'factory': return cmdFactory(cwd, flags);
|
|
384
508
|
default:
|
|
385
509
|
if (cmd) err(`Unknown command: ${cmd}`);
|
|
386
510
|
usage();
|
|
@@ -397,6 +521,7 @@ module.exports = {
|
|
|
397
521
|
detectType,
|
|
398
522
|
scopeFiles,
|
|
399
523
|
templateFiles,
|
|
524
|
+
installName,
|
|
400
525
|
copyMissing,
|
|
401
526
|
scanPlaceholders,
|
|
402
527
|
envHeader,
|
|
@@ -406,7 +531,9 @@ module.exports = {
|
|
|
406
531
|
cmdPatch,
|
|
407
532
|
cmdDoctor,
|
|
408
533
|
cmdUpgrade,
|
|
534
|
+
upgradeArgs,
|
|
409
535
|
cmdOmoStarter,
|
|
536
|
+
cmdFactory,
|
|
410
537
|
ENV_DIRS,
|
|
411
538
|
SHARED_ROOT,
|
|
412
539
|
SHARED_DIRS,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// `factory archive`: moves closed jobs (merged, abandoned) from factory/jobs/<id>/
|
|
4
|
+
// to factory/archive/<id>/. Default only archives jobs closed more than 7 days ago;
|
|
5
|
+
// --all archives every closed job. Open jobs are never touched. Stdlib only.
|
|
6
|
+
// Selection is shared with `factory prune` (factory.selectClosedJobs).
|
|
7
|
+
// Never touches git or branches: those are left to /factory archive and /factory prune.
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const factory = require('./factory-watch.js');
|
|
12
|
+
|
|
13
|
+
function cmdFactoryArchive(cwd, flags = {}, opts = {}) {
|
|
14
|
+
const stdout = opts.stdout || process.stdout;
|
|
15
|
+
const now = opts.now === undefined ? Date.now() : opts.now;
|
|
16
|
+
const all = Boolean(flags.all);
|
|
17
|
+
const dryRun = Boolean(flags.dryRun);
|
|
18
|
+
const jobsDir = factory.resolveDir(cwd, flags);
|
|
19
|
+
const archiveDir = factory.archiveDirOf(jobsDir);
|
|
20
|
+
const line = (text) => stdout.write(text + '\n');
|
|
21
|
+
const result = {
|
|
22
|
+
dir: jobsDir, archiveDir, dryRun, exists: true,
|
|
23
|
+
moved: [], skippedOpen: 0, kept: 0, skippedInvalid: [], skippedArchived: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const sel = factory.selectClosedJobs(jobsDir, { all, now });
|
|
27
|
+
if (!sel.exists) {
|
|
28
|
+
result.exists = false;
|
|
29
|
+
line(`• No jobs directory at ${jobsDir} — nothing to archive.`);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
result.skippedOpen = sel.skippedOpen;
|
|
33
|
+
result.kept = sel.kept;
|
|
34
|
+
result.skippedInvalid = sel.skippedInvalid;
|
|
35
|
+
|
|
36
|
+
for (const c of sel.candidates) {
|
|
37
|
+
const target = path.join(archiveDir, c.id);
|
|
38
|
+
if (fs.existsSync(target)) {
|
|
39
|
+
result.skippedArchived.push(c.id);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!dryRun) {
|
|
43
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
44
|
+
fs.renameSync(c.dir, target);
|
|
45
|
+
}
|
|
46
|
+
result.moved.push({ id: c.id, stage: c.stage });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const verb = dryRun ? 'Would move' : 'Moved';
|
|
50
|
+
if (result.moved.length) {
|
|
51
|
+
line(`${verb} ${result.moved.length} closed job(s) to ${archiveDir}:`);
|
|
52
|
+
for (const { id, stage } of result.moved) line(` ${id} ${stage}`);
|
|
53
|
+
} else {
|
|
54
|
+
line('No closed jobs to move.');
|
|
55
|
+
}
|
|
56
|
+
line(`Skipped ${result.skippedOpen} open job(s).`);
|
|
57
|
+
if (result.skippedInvalid.length) {
|
|
58
|
+
line(`Skipped ${result.skippedInvalid.length} invalid (not moved):`);
|
|
59
|
+
for (const { id, reason } of result.skippedInvalid) line(` ${id} ${reason}`);
|
|
60
|
+
}
|
|
61
|
+
if (result.skippedArchived.length) {
|
|
62
|
+
line(`Skipped ${result.skippedArchived.length} already in archive (not overwritten):`);
|
|
63
|
+
for (const id of result.skippedArchived) line(` ${id}`);
|
|
64
|
+
}
|
|
65
|
+
if (!all && result.kept) {
|
|
66
|
+
line(`Kept ${result.kept} closed job(s) newer than ${factory.DEFAULT_AGE_DAYS} days (--all moves them too).`);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { cmdFactoryArchive };
|