@njeybe/nativ 2.8.0 → 2.9.1
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 +41 -0
- package/README.md +62 -6
- package/bin/cli.js +1 -1
- package/dist/commands/check.d.ts +3 -0
- package/dist/commands/check.js +36 -0
- package/dist/commands/doctor.js +7 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +1 -1
- package/dist/commands/milestone.d.ts +10 -0
- package/dist/commands/milestone.js +84 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +39 -8
- package/dist/commands/task/task-add.js +62 -9
- package/dist/commands/task/task-common.d.ts +6 -0
- package/dist/commands/task/task-lifecycle.d.ts +3 -1
- package/dist/commands/task/task-lifecycle.js +48 -4
- package/dist/commands/task/task-query.js +4 -2
- package/dist/commands/task/task-show.d.ts +9 -0
- package/dist/commands/task/task-show.js +74 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +1 -1
- package/dist/commands/validate.js +24 -0
- package/dist/commands/verify.d.ts +7 -0
- package/dist/commands/verify.js +26 -2
- package/dist/core/checks/docs-check.d.ts +18 -0
- package/dist/core/checks/docs-check.js +186 -0
- package/dist/core/code-shape.d.ts +7 -2
- package/dist/core/code-shape.js +46 -16
- package/dist/core/env-reads.d.ts +15 -0
- package/dist/core/env-reads.js +126 -0
- package/dist/core/human-gate.js +6 -3
- package/dist/core/milestone-archive.d.ts +40 -0
- package/dist/core/milestone-archive.js +299 -0
- package/dist/core/profiles.d.ts +8 -0
- package/dist/core/profiles.js +43 -2
- package/dist/core/root-resolver.d.ts +4 -0
- package/dist/core/root-resolver.js +32 -0
- package/dist/core/setup/gitignore-block.d.ts +17 -0
- package/dist/core/setup/gitignore-block.js +182 -0
- package/dist/core/setup/pm-playbook.d.ts +1 -1
- package/dist/core/setup/pm-playbook.js +7 -1
- package/dist/core/setup/session-context.d.ts +9 -1
- package/dist/core/setup/session-context.js +24 -2
- package/dist/core/setup/setup-planner.js +36 -3
- package/dist/core/setup/types.d.ts +6 -0
- package/dist/core/verifier/clean-tree.d.ts +14 -0
- package/dist/core/verifier/clean-tree.js +151 -0
- package/dist/core/verifier/executor.d.ts +3 -1
- package/dist/core/verifier/executor.js +54 -31
- package/dist/core/verifier/isolation.d.ts +25 -0
- package/dist/core/verifier/isolation.js +77 -0
- package/dist/core/verifier/portability-lint.d.ts +2 -0
- package/dist/core/verifier/portability-lint.js +64 -0
- package/dist/core/verifier/skip-policy.d.ts +8 -0
- package/dist/core/verifier/skip-policy.js +13 -0
- package/dist/core/verifier/task-verifier.d.ts +6 -8
- package/dist/core/verifier/task-verifier.js +95 -41
- package/dist/core/verifier/types.d.ts +4 -0
- package/dist/core/verifier/verify-env.d.ts +2 -0
- package/dist/core/verifier/verify-env.js +22 -0
- package/dist/index.js +47 -7
- package/dist/mcp/server.js +27 -2
- package/dist/runner/agent-supervisor.js +4 -1
- package/dist/scanner/types.d.ts +18 -0
- package/dist/scanner/types.js +11 -0
- package/dist/server/routes/task-routes.d.ts +2 -0
- package/dist/server/routes/task-routes.js +13 -5
- package/dist/server/studio-ui/shell/model.js +16 -9
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/agents/architect.md +14 -7
- package/plugin/agents/verifier.md +16 -14
- package/plugin/agents/worker.md +3 -3
- package/plugin/hooks/hooks.json +1 -1
- package/plugin/skills/nativ/SKILL.md +13 -9
- package/templates/AGENTS.md +13 -9
- package/templates/claude-agents/architect.md +14 -7
- package/templates/claude-agents/verifier.md +16 -14
- package/templates/claude-agents/worker.md +3 -3
- package/templates/dot-ai/subagents/qa-tester.md +14 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.9.1
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Plan compaction.** `nativ milestone archive <ids> --summary "..."` moves a finished milestone into `.ai/archive/` once the regression suite passes, and `nativ milestone restore <id>` brings it back. `nativ task show <id>` reads one task, archived or not. MCP tools `nativ_milestone_archive` and `nativ_task_show` cover archive and show; restore stays CLI only.
|
|
8
|
+
- **Archived milestones count everywhere.** `nativ status` and the Studio keep archived tasks in their totals, dependencies on archived tasks count as met, `nativ validate` fails when an archive file is missing or does not match, and `verify --all` skips archived tasks.
|
|
9
|
+
- **`regressionCommand` setting.** The test suite `nativ milestone archive` runs, saved by `--check`.
|
|
10
|
+
- **Project manager guide.** After the verifier passes a milestone: archive it, tell the human, suggest `/clear`.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Smaller JSON by default.** `nativ status --json` returns full tasks only for the active milestone, and `nativ task list --json` (and `nativ tasks --json`) returns a short summary per task. Pass `--full` for the old output. Scripts that read these may need to change.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`--json` on `task list`.** The help promised a summary, but the CLI always printed every field. It now matches the MCP tool.
|
|
19
|
+
- **Checks ran twice.** The worker guide told workers to run a task's check before `nativ task complete`, which runs it anyway.
|
|
20
|
+
- **Startup instructions loaded twice.** With nativ installed as both a plugin and in the project, the plugin's hook now stays silent.
|
|
21
|
+
- **Git Bash found by clean checks on Windows.** Clean check environments now pass `ProgramFiles` and `ProgramFiles(x86)` on Windows, so native runner checks find Git Bash without a `verifyEnv` workaround.
|
|
22
|
+
|
|
23
|
+
## 2.9.0
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **Clean checks.** A task's check runs in a throwaway copy of the repo with only the files git ships and a minimal environment. Defaults: prototype off, solo on, enterprise on. Use `--isolation none|clean` (or `--clean`) on `nativ verify` and `nativ task complete`. Under enterprise, `--isolation none` on `task complete` needs an interactive terminal. Config keys: `verifyIsolation`, `verifyIsolationFailure`, `verifyEnv`, `verifyLinks`.
|
|
28
|
+
- **Portability warnings.** When a check passes only on your machine, a failure in the clean copy warns "Passes only on this machine". nativ names the test lines that read gitignored files or spawn `nativ`.
|
|
29
|
+
- **Check types.** Tasks take a `verificationKind` (unit, integration, static-config, docs, probe, manual) through `nativ task add --kind`, plus `--notes`. `--kind docs` defaults its check to `nativ check docs`. A manual task needs notes.
|
|
30
|
+
- **Skipped-check policy.** `skippedCheck` allows a skip in prototype, warns in solo and blocks in enterprise. A skip passes quietly only for a manual task with notes. Tasks with no kind only warn.
|
|
31
|
+
- **`nativ check docs <files>`.** Checks backticked file paths, `<PLACEHOLDER>` tokens against the file the paragraph names, setting names used in assignments or as variable references, and nativ subcommands and flags. Supports `--json`. Skip lines with `<!-- nativ-check-ignore -->`, or wrap them in the `-start` and `-end` pair.
|
|
32
|
+
- **Fail-loud config rule.** Code-shape reports "config" findings for reads of settings that silently fall back to empty, such as `?? ''`, `|| ''`, `?? null`, Python `os.getenv('X', '')` and unchecked Go `os.Getenv`. Setting: `codeStyle.configFallback` (off, warn or block; default warn). Opt out per line with `nativ-allow-fallback`.
|
|
33
|
+
- **nativ stays out of git.** `nativ init`, `setup` and `update` add a managed `# >>> nativ (managed) >>>` block to the project's `.gitignore`, listing only files nativ generated. `--git-visibility hidden|shared` sets it (default hidden); shared keeps only secret and machine files ignored. `nativ doctor` warns when the block is missing (`--fix` adds it) and prints the `git rm --cached` command for nativ files already committed. It never runs that command.
|
|
34
|
+
- **`nativ task add --new-milestone <name>`.** Starts the next milestone when the others are done. `--force` works only from the CLI.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- **Verification environment.** Checks get an allowlisted environment, and a timed-out check stops its whole process tree. `CI` inside a check no longer bypasses human-only steps. A check command may not set `NATIV_CI_OVERRIDE` or `NATIV_HEADLESS_OVERRIDE`; `nativ validate` and `task add` reject it.
|
|
39
|
+
- **`--accept` keeps each value whole.** A `|` no longer splits a criterion.
|
|
40
|
+
- **Guides.** AGENTS.md says required settings fail loudly and tests read only tracked files. The verifier has a "Weak verification" step and a config-fallback checklist item. The architect says what a real check looks like for each kind.
|
|
41
|
+
|
|
42
|
+
<!-- nativ-check-ignore-start -->
|
|
3
43
|
## 2.8.0
|
|
4
44
|
|
|
5
45
|
### Changed
|
|
@@ -262,3 +302,4 @@ nativ is now role-based and Claude-first. The roles (architect, project manager,
|
|
|
262
302
|
## 1.0.0
|
|
263
303
|
|
|
264
304
|
Initial release.
|
|
305
|
+
<!-- nativ-check-ignore-end -->
|
package/README.md
CHANGED
|
@@ -128,6 +128,7 @@ concrete result to the next.
|
|
|
128
128
|
| 5. Check | let the worker complete the task | The gatekeeper runs your verify phases, then the task's command, then a code-style check. |
|
|
129
129
|
| 6. Review | ask for a **verifier** pass | An independent read of the diff against the contracts. |
|
|
130
130
|
| 7. Improve | approve lessons agents proposed | `nativ learn list --status proposed`, then `approve` or `reject`. |
|
|
131
|
+
| 8. Compact | archive the finished milestone, then `/clear` | `nativ milestone archive` shrinks the plan to a one-line summary. The plan holds the state, so a fresh session loses nothing and costs fewer tokens. |
|
|
131
132
|
|
|
132
133
|
---
|
|
133
134
|
|
|
@@ -152,6 +153,7 @@ Every command takes an optional `[targetDir]` (default: the current directory) a
|
|
|
152
153
|
| [`hook status`](#nativ-hook) | Enforcement mode, role and active task scope | You, Agent |
|
|
153
154
|
| [`task next`](#nativ-task-next) | The next executable task with its context | Agent |
|
|
154
155
|
| [`task list`](#nativ-task-list) / `tasks` | List tasks with filters | You, Agent |
|
|
156
|
+
| [`task show`](#nativ-task-show) | One task from the plan or its archive | You, Agent |
|
|
155
157
|
| [`task add`](#nativ-task-add) | Add a task | Architect |
|
|
156
158
|
| [`task start`](#nativ-task-start) | Mark a task in progress | Agent |
|
|
157
159
|
| [`task reclaim`](#nativ-task-reclaim) | Free tasks left in progress by a stopped agent | You, Agent |
|
|
@@ -160,6 +162,7 @@ Every command takes an optional `[targetDir]` (default: the current directory) a
|
|
|
160
162
|
| [`task escalate`](#nativ-task-escalate) | Escalate a contract gap to the architect | Agent |
|
|
161
163
|
| [`task propose-patch`](#nativ-task-propose-patch) | Propose a governed contract change | Agent |
|
|
162
164
|
| [`task unlock`](#nativ-task-unlock) | Lift a task's file scope in an emergency | *You only* |
|
|
165
|
+
| [`milestone archive` / `restore`](#nativ-milestone-archive--restore) | Move a finished milestone out of the plan, or bring it back | You, Agent (`restore` by CLI) |
|
|
163
166
|
| [`verify`](#nativ-verify) | Run verification for a task, a milestone or all | You, Agent |
|
|
164
167
|
| [`learn propose`](#nativ-learn-propose) | Propose a lesson for later workers | Agent |
|
|
165
168
|
| [`learn list`](#nativ-learn-list) | List lessons | You, Agent |
|
|
@@ -193,6 +196,7 @@ nativ init [targetDir] [-f, --force]
|
|
|
193
196
|
| :--- | :--- |
|
|
194
197
|
| `-f, --force` | Overwrite existing specification and directive files |
|
|
195
198
|
| `--design <taste-v1|taste-v2>` | Set the design profile in `.ai/ui_specs.md` and install the taste-skill bundle |
|
|
199
|
+
| `--git-visibility <mode>` | `hidden` (default) adds a managed `.gitignore` block listing only nativ's generated files; `shared` ignores only secret and machine files |
|
|
196
200
|
|
|
197
201
|
#### `nativ design check`
|
|
198
202
|
|
|
@@ -260,6 +264,8 @@ nativ doctor [targetDir] [options]
|
|
|
260
264
|
| `--no-deep` | Skip asking Claude Code (`claude mcp list`) whether the server connects |
|
|
261
265
|
| `--json` | Output the report as JSON |
|
|
262
266
|
|
|
267
|
+
It also warns when the managed `.gitignore` block is missing (`--fix` adds it) and prints the `git rm --cached` command for nativ files already committed. It never runs that command.
|
|
268
|
+
|
|
263
269
|
Run it after updating Claude Code or nativ.
|
|
264
270
|
|
|
265
271
|
#### `nativ update`
|
|
@@ -287,9 +293,11 @@ nativ validate [targetDir]
|
|
|
287
293
|
Progress per milestone; with `--telemetry`, durations, token and cost estimates, pass rates and role violations.
|
|
288
294
|
|
|
289
295
|
```bash
|
|
290
|
-
nativ status [targetDir] [-t, --telemetry] [--json]
|
|
296
|
+
nativ status [targetDir] [-t, --telemetry] [--json] [--full]
|
|
291
297
|
```
|
|
292
298
|
|
|
299
|
+
`--json` returns full tasks only for the active milestone, one line for each other milestone and the telemetry summary. Add `--full` for every task and per-task telemetry. Archived milestones count toward the totals.
|
|
300
|
+
|
|
293
301
|
#### `nativ hook`
|
|
294
302
|
|
|
295
303
|
```bash
|
|
@@ -345,7 +353,8 @@ nativ task list [targetDir] [options] # also available as: nativ tasks
|
|
|
345
353
|
| `-s, --status <status>` | `pending`, `in_progress`, `completed` or `blocked` |
|
|
346
354
|
| `-m, --milestone <id>` | Milestone ID or name |
|
|
347
355
|
| `--fast-path` | Only fast-path tasks |
|
|
348
|
-
| `--json` | Output as JSON |
|
|
356
|
+
| `--json` | Output a short summary per task as JSON |
|
|
357
|
+
| `--full` | With `--json`, include every task field |
|
|
349
358
|
|
|
350
359
|
#### `nativ task add`
|
|
351
360
|
|
|
@@ -365,8 +374,11 @@ nativ task add <title> [targetDir] [options]
|
|
|
365
374
|
| `--deps <ids>` | Comma-separated dependency task IDs |
|
|
366
375
|
| `--spec-refs <refs>` | Contract anchors, e.g. `ui_specs.md#appointment-list` or `api_contracts.json#GET /appointments` |
|
|
367
376
|
| `--complexity <level>` | `simple`, `standard` or `complex` (routes the worker's model) |
|
|
368
|
-
| `--accept <text>` | "Done when" line; repeatable
|
|
377
|
+
| `--accept <text>` | "Done when" line; repeatable, each value kept whole |
|
|
369
378
|
| `--fast-path` | Route to the fast-path milestone for quick fixes |
|
|
379
|
+
| `--kind <kind>` | Verification kind: `unit`, `integration`, `static-config`, `docs`, `probe` or `manual`. `docs` defaults the check to `nativ check docs` |
|
|
380
|
+
| `--notes <text>` | Task notes; required with `--kind manual` |
|
|
381
|
+
| `--new-milestone <name>` | Create the next milestone with this name and add the task to it. Refused while another planned milestone is open, unless `--force` |
|
|
370
382
|
| `--json` | Output the created task as JSON |
|
|
371
383
|
|
|
372
384
|
```bash
|
|
@@ -384,6 +396,26 @@ nativ task start <taskId> [targetDir] [--agent <id>] [--force]
|
|
|
384
396
|
|
|
385
397
|
Claims the task, marks it `in_progress` and records the baseline the test-integrity guard compares against. The task must be pending and its dependencies completed; a blocked task goes through `nativ triage` first, and only a person can override with `--force`. The claim records `claimedBy` (`--agent` or `$NATIV_AGENT_ID`) and `claimedAt`, so a parallel agent cannot take a task another agent holds. Running `task start` again on your own task refreshes the claim; a claim older than 4 hours counts as abandoned.
|
|
386
398
|
|
|
399
|
+
#### `nativ task show`
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
nativ task show <taskId> [targetDir] [--json]
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Prints one task from the plan, or from its milestone archive when the milestone is archived. Use it for an old task instead of opening the plan.
|
|
406
|
+
|
|
407
|
+
#### `nativ milestone archive` / `restore`
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
nativ milestone archive <ids> --summary "Studio redesign: shell, routing, pages"
|
|
411
|
+
nativ milestone archive m1,m2 --summaries summaries.json [--check "npm test"] [--timeout 600000]
|
|
412
|
+
nativ milestone restore <id>
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Archives finished milestones after the regression suite passes. Every task must be completed, and the working tree must be clean. The archive is written to `.ai/archive/<id>.json` and read back before the plan changes. The suite is the `regressionCommand` in `.nativ/config.json`; `--check` overrides and saves it. Default timeout is 10 minutes. Archived task ids still count for numbering and dependencies. `restore` is CLI only.
|
|
416
|
+
|
|
417
|
+
A summaries file maps each milestone id to its one-line summary, and the suite runs once for the whole batch. `.ai/` is usually not in git, so keep archive files: they are the only full copy of those tasks.
|
|
418
|
+
|
|
387
419
|
#### `nativ task reclaim`
|
|
388
420
|
|
|
389
421
|
```bash
|
|
@@ -404,6 +436,8 @@ nativ task complete <taskId> [targetDir] [options]
|
|
|
404
436
|
| :--- | :--- |
|
|
405
437
|
| `-n, --notes <notes>` | Completion notes |
|
|
406
438
|
| `--timeout <ms>` | Verification timeout (default 120000) |
|
|
439
|
+
| `--isolation <mode>` | Run the check in place (`none`) or in a clean copy of the repo (`clean`). Accepts `none` or `clean`. See [Verification](#verification) |
|
|
440
|
+
| `--clean` | Short for `--isolation clean` |
|
|
407
441
|
| `--no-verify` | Skip verification. *You only*: needs an interactive terminal, is denied to agents, and the MCP tool refuses it |
|
|
408
442
|
|
|
409
443
|
#### `nativ task block`
|
|
@@ -462,7 +496,7 @@ nativ task unlock <taskId> --revoke
|
|
|
462
496
|
|
|
463
497
|
#### `nativ verify`
|
|
464
498
|
|
|
465
|
-
Re-run verification for one task, a milestone or every completed task. Also available as `nativ task verify`.
|
|
499
|
+
Re-run verification for one task, a milestone or every completed task. Also available as `nativ task verify`. `--all` skips archived tasks, because their old checks may no longer apply; naming an archived task or milestone explains how to restore it.
|
|
466
500
|
|
|
467
501
|
```bash
|
|
468
502
|
nativ verify task-01
|
|
@@ -475,6 +509,8 @@ nativ verify --all --json
|
|
|
475
509
|
| `-a, --all` | Every completed task |
|
|
476
510
|
| `-m, --milestone <id>` | Every task in a milestone |
|
|
477
511
|
| `--timeout <ms>` | Per command (default 120000) |
|
|
512
|
+
| `--isolation <mode>` | In place, or in a clean copy with only the files git ships and a minimal environment |
|
|
513
|
+
| `--clean` | Short for `--isolation clean` |
|
|
478
514
|
| `--json` | Output as JSON |
|
|
479
515
|
|
|
480
516
|
**What runs, in order** (the same for `verify`, the `task complete` gatekeeper and Studio dispatch):
|
|
@@ -492,6 +528,16 @@ nativ verify --all --json
|
|
|
492
528
|
|
|
493
529
|
With no phases configured, verification is just the task's command and the style check. The settings always come from the main checkout, never a task worktree's copy. If `.nativ/config.json` cannot be read, or a phase has no `name` or `run`, verification fails with a clear message instead of skipping the checks.
|
|
494
530
|
|
|
531
|
+
#### `nativ check docs`
|
|
532
|
+
|
|
533
|
+
Checks markdown files for drift: backticked file paths, `<PLACEHOLDER>` tokens against the file the paragraph names, setting names used in assignments or as variable references, and nativ subcommands and flags.
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
nativ check docs README.md CHANGELOG.md [--json]
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Skip a line with `<!-- nativ-check-ignore -->`, or wrap a block in `<!-- nativ-check-ignore-start -->` and `<!-- nativ-check-ignore-end -->`.
|
|
540
|
+
|
|
495
541
|
[Back to contents](#contents)
|
|
496
542
|
|
|
497
543
|
---
|
|
@@ -722,6 +768,12 @@ Written by `setup`; every key is optional.
|
|
|
722
768
|
| `codeStyle.mode` | `warn` | `warn`, `block` or `off` for the line-length and comment check |
|
|
723
769
|
| `codeStyle.maxLineLength` | `120` | Longest allowed line |
|
|
724
770
|
| `codeStyle.maxCommentLines` | `2` | Longest allowed comment block |
|
|
771
|
+
| `codeStyle.configFallback` | `warn` | `off`, `warn` or `block` for reads of settings that silently fall back to empty. Opt out per line with `nativ-allow-fallback` |
|
|
772
|
+
| `verifyIsolation` | `none` (`clean` in solo and enterprise) | Run checks in a clean copy of the repo. See [`nativ verify`](#verification) |
|
|
773
|
+
| `verifyIsolationFailure` | `warn` | `warn` or `block` when a check fails in the clean copy but passes in place |
|
|
774
|
+
| `verifyEnv` | none | Extra environment variable names a check may read |
|
|
775
|
+
| `verifyLinks` | `node_modules`, `.venv`, `venv` | Files linked into the clean copy, for example dependency folders |
|
|
776
|
+
| `regressionCommand` | none | The test suite `nativ milestone archive` runs before archiving; `--check` sets it |
|
|
725
777
|
| `providers` | see [Providers](#providers) | Provider order per role: `architect`, `pm`, `worker`, `verifier`, `triage` |
|
|
726
778
|
| `models` | provider default | Model per provider |
|
|
727
779
|
| `notify` | off | `webhook`, `command` and `events` for human notifications. See [`nativ notify test`](#nativ-notify-test) |
|
|
@@ -821,6 +873,7 @@ A deterministic firewall between agents and the contracts:
|
|
|
821
873
|
| `.ai/db_schema.json`, `.ai/api_contracts.json`, `.ai/ui_specs.md` | architect | The contracts |
|
|
822
874
|
| `.ai/design/style-tile.html` | architect | The chosen visual direction |
|
|
823
875
|
| `.ai/master_plan.json` | `nativ task` commands | Milestones and tasks |
|
|
876
|
+
| `.ai/archive/<id>.json` | `nativ milestone archive` | Full copy of an archived milestone; the plan keeps a one-line summary |
|
|
824
877
|
| `.ai/subagents/*.md` | init, update | One guide per worker role |
|
|
825
878
|
| `.ai/escalation.json` | `task escalate`, triage | Escalations and how they were settled |
|
|
826
879
|
| `.ai/learnings.json` | `nativ learn` | Proposed, approved and rejected lessons |
|
|
@@ -838,7 +891,8 @@ Files nativ wrote carry a marker, so `update` and `doctor` can tell untouched fi
|
|
|
838
891
|
|
|
839
892
|
| Tools | Purpose |
|
|
840
893
|
| :--- | :--- |
|
|
841
|
-
| `nativ_task_next`, `nativ_task_list`, `nativ_task_add` | Find and add work |
|
|
894
|
+
| `nativ_task_next`, `nativ_task_list`, `nativ_task_show`, `nativ_task_add` | Find and add work (`task_show` also reads archived tasks) |
|
|
895
|
+
| `nativ_milestone_archive` | Archive finished milestones after the regression suite passes. A slow suite can outlast a client's tool timeout; use the CLI for those |
|
|
842
896
|
| `nativ_task_start`, `nativ_task_complete`, `nativ_task_block`, `nativ_task_reclaim` | Move a task through its lifecycle (complete always runs the gatekeeper; reclaim frees stale claims) |
|
|
843
897
|
| `nativ_validate` | Check the `.ai/` contracts, plan and role guides; returns only problems and a verdict |
|
|
844
898
|
| `nativ_triage` | Self-heal: evaluate pending escalations, apply sandbox-proven safe fixes, return decision cards for the rest |
|
|
@@ -853,7 +907,7 @@ Files nativ wrote carry a marker, so `update` and `doctor` can tell untouched fi
|
|
|
853
907
|
| :--- |
|
|
854
908
|
| `nativ://context`, `nativ://master-plan`, `nativ://db-schema`, `nativ://api-contracts`, `nativ://escalation`, `nativ://telemetry` |
|
|
855
909
|
|
|
856
|
-
There is deliberately no MCP tool to unlock a task, approve a lesson, write a contract, sync a database schema or run setup.
|
|
910
|
+
There is deliberately no MCP tool to unlock a task, approve a lesson, write a contract, sync a database schema, restore an archived milestone or run setup.
|
|
857
911
|
|
|
858
912
|
**Manual registration:**
|
|
859
913
|
|
|
@@ -941,6 +995,8 @@ npm test
|
|
|
941
995
|
npm run sync-plugin # after changing a template or the setup generator; a test fails if plugin/ is stale
|
|
942
996
|
```
|
|
943
997
|
|
|
998
|
+
Some suites need a temp folder outside any git repository. If your home folder is a git repository and temp sits inside it, `learnings`, `verify-gatekeeper` and `clean-tree` fail; point `TEMP`, `TMP` and `TMPDIR` elsewhere when you run `npm test`.
|
|
999
|
+
|
|
944
1000
|
---
|
|
945
1001
|
|
|
946
1002
|
## License
|
package/bin/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ if (group === 'hook' && (subcommand === 'check' || subcommand === 'context')) {
|
|
|
14
14
|
await runHookCheck(targetDir);
|
|
15
15
|
} else {
|
|
16
16
|
const { runSessionContext } = await import('../dist/core/setup-assets.js');
|
|
17
|
-
runSessionContext(targetDir);
|
|
17
|
+
runSessionContext(targetDir, undefined, { fromPlugin: rest.includes('--from-plugin') });
|
|
18
18
|
}
|
|
19
19
|
} catch (err) {
|
|
20
20
|
// A hook must never fail the tool call it guards.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolveProjectRoot } from '../core/root-resolver.js';
|
|
4
|
+
import { listProjectEnvNames } from '../core/env-reads.js';
|
|
5
|
+
import { checkDocText } from '../core/checks/docs-check.js';
|
|
6
|
+
function specOf(cmd) {
|
|
7
|
+
const flags = cmd.options.flatMap((o) => [o.long, o.short].filter((f) => !!f));
|
|
8
|
+
const subs = {};
|
|
9
|
+
for (const sub of cmd.commands)
|
|
10
|
+
subs[sub.name()] = specOf(sub);
|
|
11
|
+
return { flags, subs };
|
|
12
|
+
}
|
|
13
|
+
export async function runCheckDocs(files, options) {
|
|
14
|
+
const root = resolveProjectRoot(process.cwd());
|
|
15
|
+
const { createProgram } = await import('../index.js');
|
|
16
|
+
const ctx = { root, envNames: new Set(listProjectEnvNames(root)), program: specOf(createProgram()) };
|
|
17
|
+
const findings = [];
|
|
18
|
+
for (const f of files) {
|
|
19
|
+
const full = path.resolve(process.cwd(), f);
|
|
20
|
+
if (!fs.existsSync(full)) {
|
|
21
|
+
console.error(`Cannot read ${f}`);
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
findings.push(...checkDocText(fs.readFileSync(full, 'utf8'), path.relative(root, full), ctx));
|
|
26
|
+
}
|
|
27
|
+
if (options.json)
|
|
28
|
+
console.log(JSON.stringify({ ok: findings.length === 0, findings }, null, 2));
|
|
29
|
+
else if (findings.length === 0)
|
|
30
|
+
console.log('Docs check: no problems found.');
|
|
31
|
+
else
|
|
32
|
+
for (const x of findings)
|
|
33
|
+
console.log(`${x.file}:${x.line} ${x.kind} ${x.message}`);
|
|
34
|
+
if (findings.length > 0)
|
|
35
|
+
process.exitCode = 1;
|
|
36
|
+
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -100,7 +100,11 @@ export function collectChecks(root, options = {}) {
|
|
|
100
100
|
add('cli-runs', version.ok ? 'ok' : 'fail', version.ok ? `\`${cli.join(' ')}\` runs (${version.output})` : `\`${cli.join(' ')}\` does not run: ${version.output || 'no output'}. The hooks and MCP server would fail.`);
|
|
101
101
|
// Generated assets: whatever setup would still change is drift
|
|
102
102
|
for (const change of plan.changes) {
|
|
103
|
-
if (change.
|
|
103
|
+
if (change.path === '.gitignore' && change.action !== 'unchanged') {
|
|
104
|
+
const what = change.action === 'created' ? 'has no' : 'has an outdated';
|
|
105
|
+
add('gitignore', 'warn', `.gitignore ${what} nativ block, so generated files may be committed by accident`, true);
|
|
106
|
+
}
|
|
107
|
+
else if (change.action === 'created')
|
|
104
108
|
add(`asset:${change.path}`, 'fail', `${change.path} is missing`, true);
|
|
105
109
|
else if (change.action === 'updated')
|
|
106
110
|
add(`asset:${change.path}`, 'warn', `${change.path} is out of date${change.detail ? ` (${change.detail})` : ''}`, true);
|
|
@@ -109,6 +113,8 @@ export function collectChecks(root, options = {}) {
|
|
|
109
113
|
else if (change.detail)
|
|
110
114
|
add(`asset:${change.path}`, 'info', `${change.path}: ${change.detail}`);
|
|
111
115
|
}
|
|
116
|
+
if (plan.gitRmCommand)
|
|
117
|
+
add('gitignore-tracked', 'info', `nativ files are already tracked by git. To stop tracking them, run: ${plan.gitRmCommand}`);
|
|
112
118
|
if (!plan.changes.some((c) => c.action === 'created' || c.action === 'updated'))
|
|
113
119
|
add('assets', 'ok', 'MCP server, hooks, permissions, agents and directives are in place');
|
|
114
120
|
// Claude Code's own view (opt-in: it connects to every configured MCP server)
|
package/dist/commands/init.d.ts
CHANGED
package/dist/commands/init.js
CHANGED
|
@@ -175,7 +175,7 @@ export async function runInit(targetDirArg, options = {}) {
|
|
|
175
175
|
}
|
|
176
176
|
// 10. Claude Code configuration: MCP server, hooks, permissions, agents, AGENTS.md. Merges; never overwrites.
|
|
177
177
|
console.log(pc.yellow('Configuring Claude Code (MCP server, hooks, agents)...'));
|
|
178
|
-
const setup = await runSetup(targetDir, { quiet: true });
|
|
178
|
+
const setup = await runSetup(targetDir, { quiet: true, gitVisibility: options.gitVisibility });
|
|
179
179
|
if (setup) {
|
|
180
180
|
printSetupResult(setup);
|
|
181
181
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
export interface MilestoneArchiveOptions {
|
|
3
|
+
summary?: string;
|
|
4
|
+
summaries?: string;
|
|
5
|
+
check?: string;
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function runMilestoneArchive(idList: string, targetDirArg: string | undefined, options: MilestoneArchiveOptions): Promise<void>;
|
|
9
|
+
export declare function runMilestoneRestore(id: string, targetDirArg?: string): Promise<void>;
|
|
10
|
+
export declare function registerMilestoneCommands(program: Command): void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { archiveMilestones, ArchiveRefusal, restoreMilestone, DEFAULT_SUITE_TIMEOUT_MS, } from '../core/milestone-archive.js';
|
|
5
|
+
function readSummaries(file) {
|
|
6
|
+
let parsed;
|
|
7
|
+
try {
|
|
8
|
+
parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
throw new ArchiveRefusal(`--summaries file cannot be read (${err?.message}).`);
|
|
12
|
+
}
|
|
13
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
14
|
+
throw new ArchiveRefusal('--summaries file must be a JSON object of milestone id to summary.');
|
|
15
|
+
}
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
function collectSummaries(ids, options) {
|
|
19
|
+
if (options.summary !== undefined && options.summaries) {
|
|
20
|
+
throw new ArchiveRefusal('Use --summary or --summaries, not both.');
|
|
21
|
+
}
|
|
22
|
+
if (options.summaries)
|
|
23
|
+
return readSummaries(options.summaries);
|
|
24
|
+
if (options.summary === undefined)
|
|
25
|
+
return {};
|
|
26
|
+
if (ids.length > 1)
|
|
27
|
+
throw new ArchiveRefusal('Several milestones need --summaries <file.json>.');
|
|
28
|
+
return { [ids[0]]: options.summary };
|
|
29
|
+
}
|
|
30
|
+
async function report(work, onDone) {
|
|
31
|
+
try {
|
|
32
|
+
onDone(await work());
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (!(err instanceof ArchiveRefusal))
|
|
36
|
+
throw err;
|
|
37
|
+
console.error(pc.red(`\n✖ ${err.message}\n`));
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function runMilestoneArchive(idList, targetDirArg, options) {
|
|
42
|
+
const targetDir = path.resolve(targetDirArg || process.cwd());
|
|
43
|
+
const ids = idList.split(',').map((id) => id.trim()).filter(Boolean);
|
|
44
|
+
await report(async () => archiveMilestones(targetDir, {
|
|
45
|
+
ids,
|
|
46
|
+
summaries: collectSummaries(ids, options),
|
|
47
|
+
check: options.check,
|
|
48
|
+
timeout: options.timeout,
|
|
49
|
+
}), (result) => {
|
|
50
|
+
for (const m of result.archived) {
|
|
51
|
+
console.log(pc.green(`\n✔ Archived [${m.id}]: ${m.taskCount} tasks moved to ${m.file}`));
|
|
52
|
+
}
|
|
53
|
+
console.log(pc.dim(` Active milestone: ${result.activeMilestoneId}\n`));
|
|
54
|
+
result.warnings.forEach((w) => console.log(pc.yellow(`⚠ ${w}`)));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export async function runMilestoneRestore(id, targetDirArg) {
|
|
58
|
+
const targetDir = path.resolve(targetDirArg || process.cwd());
|
|
59
|
+
await report(() => restoreMilestone(targetDir, id), (result) => {
|
|
60
|
+
console.log(pc.green(`\n✔ Restored [${result.id}]: ${result.taskCount} tasks are back\n`));
|
|
61
|
+
result.warnings.forEach((w) => console.log(pc.yellow(`⚠ ${w}`)));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function registerMilestoneCommands(program) {
|
|
65
|
+
const milestone = program
|
|
66
|
+
.command('milestone')
|
|
67
|
+
.description('Move finished milestones out of .ai/master_plan.json and back');
|
|
68
|
+
milestone
|
|
69
|
+
.command('archive <ids> [targetDir]')
|
|
70
|
+
.description('Archive finished milestones (comma-separated ids) after the test suite passes')
|
|
71
|
+
.option('--summary <text>', 'What the milestone delivered (required for one milestone)')
|
|
72
|
+
.option('--summaries <file>', 'JSON file of milestone id to summary, for several milestones')
|
|
73
|
+
.option('--check <command>', 'Suite to run; saved as regressionCommand in .nativ/config.json')
|
|
74
|
+
.option('--timeout <ms>', `Suite timeout in ms (default ${DEFAULT_SUITE_TIMEOUT_MS})`, parseInt)
|
|
75
|
+
.action(async (ids, targetDir, options) => {
|
|
76
|
+
await runMilestoneArchive(ids, targetDir, options);
|
|
77
|
+
});
|
|
78
|
+
milestone
|
|
79
|
+
.command('restore <id> [targetDir]')
|
|
80
|
+
.description('Put an archived milestone and its tasks back into the plan')
|
|
81
|
+
.action(async (id, targetDir) => {
|
|
82
|
+
await runMilestoneRestore(id, targetDir);
|
|
83
|
+
});
|
|
84
|
+
}
|
package/dist/commands/setup.d.ts
CHANGED
package/dist/commands/setup.js
CHANGED
|
@@ -32,6 +32,7 @@ export async function runSetup(targetDirArg, options = {}) {
|
|
|
32
32
|
command: options.command,
|
|
33
33
|
enforcement: options.enforcement,
|
|
34
34
|
force: options.force,
|
|
35
|
+
gitVisibility: options.gitVisibility,
|
|
35
36
|
dryRun: options.dryRun,
|
|
36
37
|
};
|
|
37
38
|
const result = applySetup(targetDir, setupOptions);
|
package/dist/commands/status.js
CHANGED
|
@@ -17,14 +17,7 @@ export async function runStatus(targetDirArg, options = {}) {
|
|
|
17
17
|
const plan = JSON.parse(raw);
|
|
18
18
|
const telemetry = loadTelemetry(telemetryPath, plan.projectName);
|
|
19
19
|
if (options.json) {
|
|
20
|
-
console.log(JSON.stringify(
|
|
21
|
-
projectName: plan.projectName,
|
|
22
|
-
overallStatus: plan.overallStatus,
|
|
23
|
-
activeMilestoneId: plan.activeMilestoneId,
|
|
24
|
-
milestones: plan.milestones,
|
|
25
|
-
telemetry: telemetry.summary,
|
|
26
|
-
tasksTelemetry: telemetry.tasks,
|
|
27
|
-
}, null, 2));
|
|
20
|
+
console.log(JSON.stringify(buildStatusJson(plan, telemetry, !!options.full), null, 2));
|
|
28
21
|
return;
|
|
29
22
|
}
|
|
30
23
|
console.log(pc.bold(pc.cyan(`\n📊 AI Agent Workflow Status: ${plan.projectName}`)));
|
|
@@ -34,6 +27,14 @@ export async function runStatus(targetDirArg, options = {}) {
|
|
|
34
27
|
let inProgressTasks = 0;
|
|
35
28
|
let blockedTasks = 0;
|
|
36
29
|
for (const m of plan.milestones) {
|
|
30
|
+
if (m.archived) {
|
|
31
|
+
// Archived work still counts, so progress does not drop after archiving.
|
|
32
|
+
totalTasks += m.archived.taskCount;
|
|
33
|
+
completedTasks += m.archived.taskCount;
|
|
34
|
+
const line = `🚩 Milestone [${m.id}]: ${m.name} (archived, ${m.archived.taskCount} tasks)`;
|
|
35
|
+
console.log(pc.bold(`\n${line}`) + pc.dim(` - ${m.archived.summary}`));
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
37
38
|
console.log(pc.bold(`\n🚩 Milestone [${m.id}]: ${m.name} (${m.status})`));
|
|
38
39
|
for (const t of m.tasks) {
|
|
39
40
|
totalTasks++;
|
|
@@ -73,3 +74,33 @@ export async function runStatus(targetDirArg, options = {}) {
|
|
|
73
74
|
process.exitCode = 1;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
77
|
+
function buildStatusJson(plan, telemetry, full) {
|
|
78
|
+
const base = {
|
|
79
|
+
projectName: plan.projectName,
|
|
80
|
+
overallStatus: plan.overallStatus,
|
|
81
|
+
activeMilestoneId: plan.activeMilestoneId,
|
|
82
|
+
};
|
|
83
|
+
if (full) {
|
|
84
|
+
return { ...base, milestones: plan.milestones, telemetry: telemetry.summary, tasksTelemetry: telemetry.tasks };
|
|
85
|
+
}
|
|
86
|
+
const milestones = plan.milestones.map((m) => m.archived
|
|
87
|
+
? {
|
|
88
|
+
id: m.id,
|
|
89
|
+
name: m.name,
|
|
90
|
+
status: m.status,
|
|
91
|
+
taskCount: m.archived.taskCount,
|
|
92
|
+
completedCount: m.archived.taskCount,
|
|
93
|
+
archived: true,
|
|
94
|
+
summary: m.archived.summary,
|
|
95
|
+
}
|
|
96
|
+
: m.id === plan.activeMilestoneId
|
|
97
|
+
? m
|
|
98
|
+
: {
|
|
99
|
+
id: m.id,
|
|
100
|
+
name: m.name,
|
|
101
|
+
status: m.status,
|
|
102
|
+
taskCount: m.tasks.length,
|
|
103
|
+
completedCount: m.tasks.filter((t) => t.status === 'completed').length,
|
|
104
|
+
});
|
|
105
|
+
return { ...base, milestones, telemetry: telemetry.summary };
|
|
106
|
+
}
|