nubos-pilot 1.1.0 → 1.1.3
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/README.md +7 -5
- package/agents/np-architect.md +1 -1
- package/agents/np-build-fixer.md +4 -4
- package/agents/np-executor.md +1 -1
- package/agents/np-plan-checker.md +5 -5
- package/agents/np-planner.md +2 -2
- package/agents/np-researcher.md +2 -2
- package/bin/check-workflows.cjs +1 -8
- package/bin/install.js +9 -23
- package/bin/np-tools/_args.cjs +8 -0
- package/bin/np-tools/_commands.cjs +4 -8
- package/bin/np-tools/_commands.test.cjs +31 -0
- package/bin/np-tools/askuser.cjs +2 -8
- package/bin/np-tools/commit-task.cjs +1 -3
- package/bin/np-tools/commit.cjs +2 -8
- package/bin/np-tools/config.cjs +2 -8
- package/bin/np-tools/discuss-project.cjs +0 -27
- package/bin/np-tools/doctor.cjs +3 -14
- package/bin/np-tools/help.cjs +5 -2
- package/bin/np-tools/knowledge-search.cjs +10 -1
- package/bin/np-tools/lang-directive.cjs +2 -8
- package/bin/np-tools/learning-log.cjs +5 -9
- package/bin/np-tools/loop-preflight.cjs +4 -8
- package/bin/np-tools/park.cjs +1 -1
- package/bin/np-tools/plan-lint.cjs +1 -1
- package/bin/np-tools/plan-milestone.cjs +1 -1
- package/bin/np-tools/research-phase.cjs +1 -4
- package/bin/np-tools/research-phase.test.cjs +2 -1
- package/bin/np-tools/reset-slice.cjs +2 -2
- package/bin/np-tools/resume-work.cjs +0 -2
- package/bin/np-tools/scan-codebase.test.cjs +1 -1
- package/bin/np-tools/skip.cjs +1 -1
- package/bin/np-tools/state.cjs +3 -3
- package/bin/np-tools/state.test.cjs +2 -2
- package/bin/np-tools/stats.cjs +3 -9
- package/bin/np-tools/template-path.cjs +2 -8
- package/bin/np-tools/text-mode.cjs +2 -8
- package/bin/np-tools/undo-task.cjs +1 -1
- package/bin/np-tools/undo.cjs +2 -2
- package/bin/np-tools/unpark.cjs +1 -1
- package/bin/np-tools/update-docs.test.cjs +1 -1
- package/bin/researcher-merge.cjs +7 -1
- package/lib/agents.cjs +2 -0
- package/lib/archive.cjs +3 -3
- package/lib/archive.test.cjs +32 -0
- package/lib/checkpoint.cjs +0 -4
- package/lib/codebase-docs.cjs +7 -9
- package/lib/codebase-docs.test.cjs +2 -2
- package/lib/codebase-manifest.cjs +2 -0
- package/lib/commit-policy.cjs +4 -29
- package/lib/config-defaults.cjs +0 -1
- package/lib/config.cjs +32 -1
- package/lib/core.cjs +10 -0
- package/lib/frontmatter.cjs +7 -18
- package/lib/git.cjs +5 -34
- package/lib/handoff.cjs +3 -6
- package/lib/install/backup.cjs +19 -14
- package/lib/install/codex-toml.cjs +15 -66
- package/lib/install/runtime-assets.cjs +4 -3
- package/lib/install/runtime-detect.cjs +1 -1
- package/lib/install/runtimes-registry.cjs +0 -5
- package/lib/knowledge-adapter.cjs +1 -8
- package/lib/knowledge-adapter.test.cjs +0 -20
- package/lib/knowledge.cjs +21 -5
- package/lib/language.cjs +3 -19
- package/lib/learnings.cjs +8 -8
- package/lib/memory.cjs +9 -1
- package/lib/metrics-aggregate.cjs +3 -2
- package/lib/metrics.cjs +3 -1
- package/lib/model-profiles.cjs +2 -0
- package/lib/nubosloop-audit.cjs +344 -0
- package/lib/nubosloop.cjs +50 -279
- package/lib/nubosloop.test.cjs +52 -21
- package/lib/output-lint.cjs +1 -1
- package/lib/plan-lint.cjs +1 -1
- package/lib/researcher-reconciler.cjs +5 -10
- package/lib/researcher-swarm.cjs +82 -68
- package/lib/roadmap-render.cjs +1 -1
- package/lib/roadmap.cjs +6 -15
- package/lib/runtime/_factory.cjs +19 -0
- package/lib/runtime/antigravity.cjs +4 -12
- package/lib/runtime/augment.cjs +4 -12
- package/lib/runtime/claude.cjs +2 -0
- package/lib/runtime/cline.cjs +4 -12
- package/lib/runtime/codebuddy.cjs +4 -12
- package/lib/runtime/codex.cjs +4 -12
- package/lib/runtime/copilot.cjs +4 -12
- package/lib/runtime/cursor.cjs +4 -12
- package/lib/runtime/gemini.cjs +4 -12
- package/lib/runtime/index.cjs +3 -1
- package/lib/runtime/index.test.cjs +5 -0
- package/lib/runtime/kilo.cjs +4 -12
- package/lib/runtime/opencode.cjs +4 -12
- package/lib/runtime/qwen.cjs +4 -12
- package/lib/runtime/trae.cjs +4 -12
- package/lib/runtime/windsurf.cjs +4 -12
- package/lib/state.cjs +8 -3
- package/lib/state.test.cjs +19 -0
- package/lib/template.cjs +0 -1
- package/lib/text-mode.cjs +4 -30
- package/lib/verify.cjs +3 -13
- package/lib/workspace-scan.cjs +2 -0
- package/lib/worktree.cjs +3 -9
- package/np-tools.cjs +15 -28
- package/package.json +3 -5
- package/templates/COMPLETENESS.md +3 -3
- package/workflows/add-tests.md +1 -1
- package/workflows/architect-phase.md +1 -1
- package/workflows/doctor.md +7 -5
- package/workflows/execute-phase.md +13 -9
- package/workflows/plan-phase.md +3 -3
- package/workflows/research-phase.md +1 -1
- package/bin/np-tools/research-merge.cjs +0 -105
- package/bin/np-tools/research-merge.test.cjs +0 -166
- package/docs/adr/0001-no-daemon-invariant.md +0 -82
- package/docs/adr/0002-zero-runtime-dependencies.md +0 -91
- package/docs/adr/0003-max-six-unit-types.md +0 -85
- package/docs/adr/0004-atomic-commit-per-unit.md +0 -102
- package/docs/adr/0005-three-orthogonal-file-trees.md +0 -98
- package/docs/adr/0006-yaml-dependency-amendment.md +0 -60
- package/docs/adr/0007-codebase-docs-layer.md +0 -273
- package/docs/adr/0008-worktree-isolation-per-slice.md +0 -140
- package/docs/adr/0009-tui-framework-for-dashboard.md +0 -95
- package/docs/adr/0010-nubosloop.md +0 -244
- package/docs/adr/0011-researcher-swarm-consensus.md +0 -84
- package/docs/adr/0012-completeness-doctrine.md +0 -85
- package/docs/adr/0013-learnings-store-schema-evolution.md +0 -128
- package/docs/adr/0013-plan-trust-layer.md +0 -95
- package/docs/adr/0014-vector-memory-layer.md +0 -175
- package/docs/adr/0015-named-agent-messaging.md +0 -162
- package/docs/adr/README.md +0 -37
- package/docs/agent-frontmatter-schema.md +0 -105
- package/docs/phase-artifact-schemas.md +0 -292
- package/docs/phase-directory-layout.md +0 -82
- package/lib/install/mcp-writer.cjs +0 -127
- package/mcp-configs/README.md +0 -41
- package/mcp-configs/claude-code.example.json +0 -27
- package/mcp-configs/codex.example.toml +0 -17
- package/mcp-configs/nubos-knowledge.notes.md +0 -42
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ AI-driven planning and execution tool for code projects. Installs into 14 host C
|
|
|
5
5
|
- **No daemon.** Every command runs as a short-lived `node` invocation.
|
|
6
6
|
- **Markdown-first.** Workflows and agents are plain `.md` files — the host reads them directly.
|
|
7
7
|
- **Atomic per-task commits.** One `task(M<NNN>-S<NNN>-T<NNNN>): …` commit per unit of work. `/np:undo-task` and `/np:undo` are mechanical reverts.
|
|
8
|
-
- **Multi-runtime.** One source tree, one install payload,
|
|
8
|
+
- **Multi-runtime.** One source tree, one install payload, fourteen supported host CLIs.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -95,16 +95,18 @@ task(M001-S001-T0002): wire login handler
|
|
|
95
95
|
|
|
96
96
|
## Agents
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
Thirteen spawnable subagents are installed into the host's agent directory (alongside three `np-critic-*` audit modules consumed by `np-critic`):
|
|
99
99
|
|
|
100
100
|
- `np-planner` (opus) — breaks a milestone into slices + tasks
|
|
101
101
|
- `np-plan-checker` (opus) — adversarial goal-backward review before execution
|
|
102
102
|
- `np-architect` (sonnet) — optional ADR-style decisions before planning
|
|
103
103
|
- `np-researcher` (sonnet) — milestone-level stack + pitfalls research
|
|
104
|
+
- `np-researcher-reconciler` (sonnet) — reconciles disagreements across researcher-swarm outputs
|
|
104
105
|
- `np-sc-extractor` (haiku) — derives observable Success Criteria from goal + CONTEXT
|
|
105
106
|
- `np-codebase-documenter` (sonnet) — maintains `.nubos-pilot/codebase/` module docs
|
|
106
107
|
- `np-executor` (sonnet) — one task per spawn, one commit per task
|
|
107
108
|
- `np-build-fixer` (sonnet) — recovery patcher for executor verify failures (manual spawn)
|
|
109
|
+
- `np-critic` (sonnet) — Nubosloop critic; audits executor output across style, tests and acceptance
|
|
108
110
|
- `np-verifier` (sonnet) — post-execution Pass/Fail/Defer per success_criterion
|
|
109
111
|
- `np-nyquist-auditor` (haiku) — requirement test-coverage audit
|
|
110
112
|
- `np-security-reviewer` (sonnet) — OWASP-aligned read-only audit (manual spawn)
|
|
@@ -113,7 +115,7 @@ Every spawn runs with an **explicit tier** (`haiku` / `sonnet` / `opus`) resolve
|
|
|
113
115
|
|
|
114
116
|
## Model profile
|
|
115
117
|
|
|
116
|
-
Five profiles (`frontier`, `quality`, `balanced`, `budget`, `inherit`) map each tier (`haiku` / `sonnet` / `opus`) to a concrete model. Set at install time (`Model-Profile?` prompt) or in `.nubos-pilot/config.json`.
|
|
118
|
+
Five profiles (`frontier`, `quality`, `balanced`, `budget`, `inherit`) map each tier (`haiku` / `sonnet` / `opus`) to a concrete model. Set at install time (`Model-Profile?` prompt) or in `.nubos-pilot/config.json`.
|
|
117
119
|
|
|
118
120
|
## Requirements
|
|
119
121
|
|
|
@@ -131,11 +133,11 @@ node np-tools.cjs help # JSON: { commands: [ { name, category, descrip
|
|
|
131
133
|
## Doctor
|
|
132
134
|
|
|
133
135
|
```bash
|
|
134
|
-
npx nubos-pilot doctor #
|
|
136
|
+
npx nubos-pilot doctor # 12-check integrity scan
|
|
135
137
|
npx nubos-pilot doctor --fix # auto-fix what's safely fixable
|
|
136
138
|
```
|
|
137
139
|
|
|
138
|
-
Checks: payload manifest integrity, version mismatch, hooks presence, codex-toml sanity, askuser runtime availability, codebase docs freshness, milestone/slice directory layout.
|
|
140
|
+
Checks: payload manifest integrity, version mismatch, hooks presence, codex-toml sanity, askuser runtime availability, codebase docs freshness, milestone/slice directory layout, the three Nubosloop checks (critics present, knowledge store, config), orphan temp files, and output schemas.
|
|
139
141
|
|
|
140
142
|
## Development
|
|
141
143
|
|
package/agents/np-architect.md
CHANGED
|
@@ -70,7 +70,7 @@ If the project already documents a module/pattern that fits, extend it instead o
|
|
|
70
70
|
|
|
71
71
|
## Output Contract
|
|
72
72
|
|
|
73
|
-
**Granularity (ADR-
|
|
73
|
+
**Granularity (ADR-0019).** Architecture decisions are intent-level: which library, which boundary, which protocol. They do NOT prescribe implementation — no schema DDL, no exact framework-generated filenames, no code-style edicts. Those are executor-territory and downstream `np-planner` will refuse plans that bake them in (Plan-side Trust Layer, ADR-0019). If you find yourself describing how a controller method should be structured, stop — that's not architecture.
|
|
74
74
|
|
|
75
75
|
```markdown
|
|
76
76
|
# M<NNN> — <milestone name> — Architecture
|
package/agents/np-build-fixer.md
CHANGED
|
@@ -58,9 +58,9 @@ The orchestrator provides these in your prompt context. Read every path it hands
|
|
|
58
58
|
- `infra` (missing tool, network, env var) → STOP and emit `## INFRA BLOCKER` block; do not edit source.
|
|
59
59
|
1a. **MANDATORY knowledge lookup (Rule 9 — non-optional, runs before any Edit).** Pick the failing symbol or error class from Step 1 and run:
|
|
60
60
|
```bash
|
|
61
|
-
node .nubos-pilot/bin/np-tools.cjs knowledge-search "<failing-symbol-or-error-class>" --limit 5
|
|
61
|
+
node .nubos-pilot/bin/np-tools.cjs knowledge-search "<failing-symbol-or-error-class>" --task <task-id> --limit 5
|
|
62
62
|
```
|
|
63
|
-
If a hit lives in `.nubos-pilot/codebase/<module>.md`, `Read` that doc before patching. Skipping this step stamps `rule-9-violation` in the Layer-C audit log and the loop routes back to the researcher swarm next round — it is **not** an opt-out.
|
|
63
|
+
The `--task <task-id>` flag is required: it records the Rule 9 evidence the tool-use audit cross-checks. A `knowledge-search` run without it leaves no ledger entry, so the audit treats the spawn as if it never searched (`rule-9-search-tool-unverified`). If a hit lives in `.nubos-pilot/codebase/<module>.md`, `Read` that doc before patching. Skipping this step stamps `rule-9-violation` in the Layer-C audit log and the loop routes back to the researcher swarm next round — it is **not** an opt-out.
|
|
64
64
|
2. **Locate the failure surface** strictly inside `files_modified`. If the failure points outside that set, emit `## SCOPE EXPANSION REQUEST` and stop — do NOT edit out-of-scope files.
|
|
65
65
|
3. **Propose the smallest patch** that addresses the root cause:
|
|
66
66
|
- For `compile` / `lint`: edit the offending file directly.
|
|
@@ -72,10 +72,10 @@ The orchestrator provides these in your prompt context. Read every path it hands
|
|
|
72
72
|
|
|
73
73
|
## Mandatory Knowledge Lookup (Rule 9)
|
|
74
74
|
|
|
75
|
-
**This is non-optional, not advisory.** Workflow Step 1a runs the lookup before any Edit. Skipping it stamps `rule-9-violation` in the audit log and forces a re-route to the researcher swarm.
|
|
75
|
+
**This is non-optional, not advisory.** Workflow Step 1a runs the lookup before any Edit. Skipping it — or running it without `--task` — stamps `rule-9-violation` in the audit log and forces a re-route to the researcher swarm.
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
node .nubos-pilot/bin/np-tools.cjs knowledge-search "<failing-symbol>" --limit 5
|
|
78
|
+
node .nubos-pilot/bin/np-tools.cjs knowledge-search "<failing-symbol>" --task <task-id> --limit 5
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
If a hit lives in `.nubos-pilot/codebase/<module>.md`, `Read` that doc before patching. Cross-task context belongs in `RULES.md` and `M<NNN>-CONTEXT.md`.
|
package/agents/np-executor.md
CHANGED
|
@@ -31,7 +31,7 @@ This agent operates under [`templates/COMPLETENESS.md`](../templates/COMPLETENES
|
|
|
31
31
|
- **Rule 3 — Do it with tests.** Every commit ships tests for the production code it adds or changes. No "trivial enough to skip tests" exceptions.
|
|
32
32
|
- **Rule 4 — Do it with documentation.** Update `.nubos-pilot/codebase/<module>.md` after every commit (`update-docs` is mandatory, not optional).
|
|
33
33
|
- **Rule 7 — Never leave a dangling thread.** Dead imports, unused symbols, half-renamed identifiers — clean them up in the same commit that introduces the change.
|
|
34
|
-
- **Rule 9 — Search before building.**
|
|
34
|
+
- **Rule 9 — Search before building.** Before writing any new symbol, run `node np-tools.cjs knowledge-search "<symbol>" --task <task-id>` via Bash. The `--task <task-id>` flag is mandatory — it records the evidence the Rule 9 tool-use audit cross-checks; a lookup without it counts as no search. Reuse beats reinvention.
|
|
35
35
|
- **Rule 10 — Test before shipping.** Verify must be green before you call `commit-task`. Manual "I ran it once" is not proof of work.
|
|
36
36
|
|
|
37
37
|
Refusal of any rule is a hard-stop. Surface the violation to the orchestrator verbatim and abort the spawn.
|
|
@@ -28,7 +28,7 @@ Refusal of any rule is a hard-stop. Surface the violation to the orchestrator ve
|
|
|
28
28
|
|
|
29
29
|
## Role
|
|
30
30
|
|
|
31
|
-
Adversarial reader of milestone plans. You assume the planner made mistakes and look for them systematically. You enforce the canonical finding-category taxonomy
|
|
31
|
+
Adversarial reader of milestone plans. You assume the planner made mistakes and look for them systematically. You enforce the canonical finding-category taxonomy defined below — every issue you emit MUST use one of those codes verbatim.
|
|
32
32
|
|
|
33
33
|
You are NOT the executor (`/np:execute-phase`) and NOT the post-execution verifier (`/np:validate-phase`). You verify plans WILL work before execution; the verifier confirms code DID work after execution. Same goal-backward methodology, different timing.
|
|
34
34
|
|
|
@@ -53,7 +53,7 @@ Additional context the orchestrator may inline in the prompt:
|
|
|
53
53
|
|
|
54
54
|
## Review Dimensions
|
|
55
55
|
|
|
56
|
-
Each dimension maps to one or more canonical finding categories
|
|
56
|
+
Each dimension maps to one or more canonical finding categories. The 14 canonical codes are:
|
|
57
57
|
|
|
58
58
|
- `missing-success-criterion` — a ROADMAP SC-X is not mapped to any task.
|
|
59
59
|
- `non-atomic-task` — a task bundles multiple distinct deliverables that should be split.
|
|
@@ -66,9 +66,9 @@ Each dimension maps to one or more canonical finding categories from `docs/agent
|
|
|
66
66
|
- `hook-field-present` — agent frontmatter contains `hooks:` (D-10).
|
|
67
67
|
- `forbidden-agent-field` — agent frontmatter contains `model:` or `model_profile:` (D-10).
|
|
68
68
|
- `unverified-assumption` — a slice plan's `<reality_check>` block is missing, empty, or contains an `<assumption>` without a non-empty `verified_by` attribute, OR a `<files_read>` path does not exist in the repo (Reality-Check rule, see Dimension 12).
|
|
69
|
-
- `verify-command-unknown` — a `<verify>` block invokes a command that is not a known np-tools verb, declared composer/npm script, vendor binary, or POSIX baseline tool (Plan-side Trust Layer, ADR-
|
|
70
|
-
- `parallel-task-implicit-dependency` — tasks marked `depends_on: []` in the same slice but one of them runs a working-tree-reading verify (`update-docs`, `phpstan analyse`, `git diff`, etc.) against files another sibling modifies. Implicit ordering must be made explicit (Plan-side Trust Layer, ADR-
|
|
71
|
-
- `plan-over-specifies-implementation` — PLAN.md body contains schema DDL, framework-controlled timestamped filenames, or large inline code snippets. Plans specify intent + boundary + acceptance, not implementation. Severity is `major` (advisory) — not a hard block, but you flag it so the planner course-corrects (Plan-side Granularity Doctrine, ADR-
|
|
69
|
+
- `verify-command-unknown` — a `<verify>` block invokes a command that is not a known np-tools verb, declared composer/npm script, vendor binary, or POSIX baseline tool (Plan-side Trust Layer, ADR-0019). Mechanically detected by `np-tools.cjs plan-lint`; you mirror the verdict into your findings array so the loop handler treats it uniformly with semantic findings.
|
|
70
|
+
- `parallel-task-implicit-dependency` — tasks marked `depends_on: []` in the same slice but one of them runs a working-tree-reading verify (`update-docs`, `phpstan analyse`, `git diff`, etc.) against files another sibling modifies. Implicit ordering must be made explicit (Plan-side Trust Layer, ADR-0019).
|
|
71
|
+
- `plan-over-specifies-implementation` — PLAN.md body contains schema DDL, framework-controlled timestamped filenames, or large inline code snippets. Plans specify intent + boundary + acceptance, not implementation. Severity is `major` (advisory) — not a hard block, but you flag it so the planner course-corrects (Plan-side Granularity Doctrine, ADR-0019).
|
|
72
72
|
|
|
73
73
|
Note on the Nubosloop critic: as of 2026-05-05 a single `np-critic` agent covers style + tests + acceptance in one spawn (ADR-0010 §Single-Critic Revision). The legacy three-critic schwarm (`np-critic-style`/`np-critic-tests`/`np-critic-acceptance`) is removed. References in older plans should be updated.
|
|
74
74
|
|
package/agents/np-planner.md
CHANGED
|
@@ -277,7 +277,7 @@ Every PLAN.md you write will be consumed by an executor agent that:
|
|
|
277
277
|
**Implications for your writing style:**
|
|
278
278
|
|
|
279
279
|
- **Name the library, not the category.** "Use `jose` for JWT" > "use a JWT library".
|
|
280
|
-
- **Name the file, not the area** — for *deterministic edits the planner can know up-front*. "Modify `src/api/auth/login.ts`" > "update the auth layer". For *scaffolding tasks where a framework generates files at install/publish time*, use a glob (`database/migrations/*_cashier_*.php`) or leave `files_modified` empty — the executor resolves the real paths from the actual publish output and `commit-task` falls back to `checkpoint.files_touched` (D-04, ADR-
|
|
280
|
+
- **Name the file, not the area** — for *deterministic edits the planner can know up-front*. "Modify `src/api/auth/login.ts`" > "update the auth layer". For *scaffolding tasks where a framework generates files at install/publish time*, use a glob (`database/migrations/*_cashier_*.php`) or leave `files_modified` empty — the executor resolves the real paths from the actual publish output and `commit-task` falls back to `checkpoint.files_touched` (D-04, ADR-0019 Layer-D Granularity).
|
|
281
281
|
- **Name the command, not the intent.** "Run `npm test -- --filter=auth`" > "run the tests".
|
|
282
282
|
- **Cite existing interfaces verbatim.** If `lib/core.cjs` exports `NubosPilotError(code, message, details)` — quote that signature in the task context so the executor doesn't mis-remember.
|
|
283
283
|
- **Document deviations from canonical advice.** If you deviate from CONTEXT.md's stack choice, say so explicitly and note why.
|
|
@@ -286,7 +286,7 @@ If the executor has to stop and read three more files to figure out what you mea
|
|
|
286
286
|
</downstream_awareness>
|
|
287
287
|
|
|
288
288
|
<plan_granularity>
|
|
289
|
-
## Plan Granularity Doctrine — Intent + Boundary + Acceptance, NOT Implementation (ADR-
|
|
289
|
+
## Plan Granularity Doctrine — Intent + Boundary + Acceptance, NOT Implementation (ADR-0019)
|
|
290
290
|
|
|
291
291
|
A PLAN.md is a contract. It specifies **what** must be true at the end (intent), **where** the work is allowed to touch (boundary), and **how** success is measured (acceptance). It does NOT specify HOW the implementation looks. That's the executor's territory; you don't have ground-truth on it and pretending you do is the bug class that produced the M004 plan-bugs.
|
|
292
292
|
|
package/agents/np-researcher.md
CHANGED
|
@@ -24,7 +24,7 @@ Your output is prescriptive, not exploratory: "Use library X at version Y" beats
|
|
|
24
24
|
This agent operates under [`templates/COMPLETENESS.md`](../templates/COMPLETENESS.md). The rules that bind this role:
|
|
25
25
|
|
|
26
26
|
- **Rule 5 — Aim to genuinely impress.** Prescriptive beats exploratory. "Use `jose@6.0.10`" beats "consider a JWT library". Vague research produces vague plans, vague plans produce vague software.
|
|
27
|
-
- **Rule 9 — Search before building.** This is your core job. Before any new claim, search the local knowledge index
|
|
27
|
+
- **Rule 9 — Search before building.** This is your core job. Before any new claim, search the local knowledge index via `node np-tools.cjs knowledge-search "<query>"` (pass `--task <task-id>` when spawned inside the execute-loop so the Rule 9 audit ledger records the call), the codebase docs (`.nubos-pilot/codebase/`), and Context7 / WebFetch. Reuse prior learnings.
|
|
28
28
|
- **Rule 11 — Ship the complete thing.** RESEARCH.md is a deliverable, not a draft. Every claim has provenance, every assumption is tagged `[ASSUMED]`, every gap is listed in `Open Questions`. No half-research.
|
|
29
29
|
|
|
30
30
|
Refusal of any rule is a hard-stop. Surface the violation to the orchestrator verbatim and abort the spawn.
|
|
@@ -37,7 +37,7 @@ Your per-spawn output MUST conform to the **`researcher-output`** schema. The or
|
|
|
37
37
|
|
|
38
38
|
Hard rules from the schema:
|
|
39
39
|
|
|
40
|
-
- Frontmatter must include `schema_version`, `agent: np-researcher`, `spawn_index`, `seed_delta`, `task_query_hash`, plus count fields (`decision_count`, `risk_count`, etc.).
|
|
40
|
+
- Frontmatter must include `schema_version`, `agent: np-researcher`, `spawn_index`, `seed_delta`, `task_query_hash`, plus count fields (`decision_count`, `risk_count`, etc.). `spawn_index` and `seed_delta` are **integers** — copy them verbatim from the `index` / `seed_delta` fields of your spawn spec. The prose perspective nudge arrives on the spawn spec's separate `seed_nudge` field; it shapes HOW you investigate and never goes into frontmatter.
|
|
41
41
|
- Five body sections are pflichtig (use `_None._` if empty): `## Decisions`, `## Risks`, `## Patterns`, `## Open Questions`, `## Sources`.
|
|
42
42
|
- Every Decision / Risk / Pattern / Open Question / Source uses heading style `### <PREFIX>-N: <text>` where PREFIX ∈ {D, R, P, Q, S}.
|
|
43
43
|
- **Every entry has a `**Reasoning:**` field** (mandatory). The Reasoning field documents what you weighed, what you discarded, and why this conclusion. The reconciler compares `Reasoning` traces across spawns to detect groupthink (identical reasoning → low independent evidence) vs orthogonal evidence (different reasoning paths to same conclusion → strong signal).
|
package/bin/check-workflows.cjs
CHANGED
|
@@ -66,8 +66,6 @@ function _scanMetricsCoverage(files) {
|
|
|
66
66
|
const raw = fs.readFileSync(file, 'utf-8');
|
|
67
67
|
const lines = raw.split(/\r?\n/);
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
69
|
let inAnyFence = false;
|
|
72
70
|
let inBashFence = false;
|
|
73
71
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -102,10 +100,6 @@ function _scanMetricsCoverage(files) {
|
|
|
102
100
|
return warnings;
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
function _scan(dir) {
|
|
106
|
-
return _scanFiles(_walk(dir, [], { ext: ['.md'] }));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
103
|
function _scanInstallerSurface(cwd) {
|
|
110
104
|
const root = cwd || process.cwd();
|
|
111
105
|
const files = [];
|
|
@@ -131,7 +125,7 @@ function checkWorkflows(dir) {
|
|
|
131
125
|
violations.push(..._scanFiles(workflowFiles));
|
|
132
126
|
warnings.push(..._scanMetricsCoverage(workflowFiles));
|
|
133
127
|
}
|
|
134
|
-
} catch {
|
|
128
|
+
} catch {}
|
|
135
129
|
violations.push(..._scanInstallerSurface());
|
|
136
130
|
return { violations, warnings, exitCode: violations.length ? 1 : 0 };
|
|
137
131
|
}
|
|
@@ -158,7 +152,6 @@ if (require.main === module) main();
|
|
|
158
152
|
|
|
159
153
|
module.exports = {
|
|
160
154
|
checkWorkflows,
|
|
161
|
-
_scan,
|
|
162
155
|
_scanFiles,
|
|
163
156
|
_scanInstallerSurface,
|
|
164
157
|
_scanMetricsCoverage,
|
package/bin/install.js
CHANGED
|
@@ -87,7 +87,7 @@ function _parseAgentsFlag(value) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function parseInstallFlags(args) {
|
|
90
|
-
const flags = { agent: null, agents: null, scope: null,
|
|
90
|
+
const flags = { agent: null, agents: null, scope: null, yes: false };
|
|
91
91
|
const rest = [];
|
|
92
92
|
for (let i = 0; i < args.length; i++) {
|
|
93
93
|
const a = args[i];
|
|
@@ -98,7 +98,6 @@ function parseInstallFlags(args) {
|
|
|
98
98
|
if (a === '--all') { flags.agents = VALID_AGENTS.slice(); continue; }
|
|
99
99
|
if (a === '--scope' || a === '-s') { flags.scope = args[++i] || null; continue; }
|
|
100
100
|
if (a.startsWith('--scope=')) { flags.scope = a.slice('--scope='.length); continue; }
|
|
101
|
-
if (a === '--mcp') { flags.mcp = true; continue; }
|
|
102
101
|
if (a === '--yes' || a === '-y') { flags.yes = true; continue; }
|
|
103
102
|
rest.push(a);
|
|
104
103
|
}
|
|
@@ -229,7 +228,7 @@ function _copyTree(src, dst) {
|
|
|
229
228
|
try {
|
|
230
229
|
entries = fs.readdirSync(src, { withFileTypes: true });
|
|
231
230
|
} catch (err) {
|
|
232
|
-
if (err && err.code === 'ENOENT') return;
|
|
231
|
+
if (err && err.code === 'ENOENT') return;
|
|
233
232
|
throw err;
|
|
234
233
|
}
|
|
235
234
|
fs.mkdirSync(dst, { recursive: true });
|
|
@@ -241,7 +240,6 @@ function _copyTree(src, dst) {
|
|
|
241
240
|
} else if (e.isFile()) {
|
|
242
241
|
fs.copyFileSync(from, to);
|
|
243
242
|
}
|
|
244
|
-
|
|
245
243
|
}
|
|
246
244
|
}
|
|
247
245
|
|
|
@@ -280,7 +278,7 @@ async function _runInitQuestions(detectedRuntime, askUser, flags) {
|
|
|
280
278
|
options: ['frontier', 'quality', 'balanced', 'budget', 'inherit'], default: 'frontier' })).value;
|
|
281
279
|
const response_language = (await askUser({ type: 'input', question: 'Response language (ISO-639 code)?', default: 'en' })).value;
|
|
282
280
|
return configDefaults.buildInstallConfig({
|
|
283
|
-
runtime, runtimes, scope,
|
|
281
|
+
runtime, runtimes, scope,
|
|
284
282
|
model_profile,
|
|
285
283
|
response_language,
|
|
286
284
|
});
|
|
@@ -330,7 +328,7 @@ function _rewriteManagedMarkdown(projectRoot, runtimes, responseLanguage) {
|
|
|
330
328
|
|
|
331
329
|
const base = fs.existsSync(targetPath)
|
|
332
330
|
? fs.readFileSync(targetPath, 'utf-8')
|
|
333
|
-
: agentsMdMod.generateAgentsMd(claudeRendered, id
|
|
331
|
+
: agentsMdMod.generateAgentsMd(claudeRendered, id);
|
|
334
332
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
335
333
|
atomicWriteFileSync(targetPath, managedBlockMod.rewriteBlock(base, innerMd));
|
|
336
334
|
}
|
|
@@ -462,7 +460,7 @@ async function _runInstallLocked(ctx) {
|
|
|
462
460
|
scope: resolvedScope,
|
|
463
461
|
wouldWrite: Object.keys(newManifest.files).length,
|
|
464
462
|
wouldBackup: backupLog.length, wouldDelete: diff.stale.length,
|
|
465
|
-
wouldWriteGemini:
|
|
463
|
+
wouldWriteGemini: selectedRuntimesEarly.includes('gemini'),
|
|
466
464
|
wouldWriteOpencodeJson: opencodeSelected && !fs.existsSync(path.join(projectRoot, 'opencode.json')),
|
|
467
465
|
stale: diff.stale, changed: diff.changed, added: diff.added };
|
|
468
466
|
process.stdout.write(JSON.stringify(summary, null, 2) + '\n');
|
|
@@ -541,20 +539,6 @@ async function _runInstallLocked(ctx) {
|
|
|
541
539
|
console.error(yellow + ' [shim] np-tools shim skipped: ' + (err && err.message) + reset);
|
|
542
540
|
}
|
|
543
541
|
|
|
544
|
-
if (initConfig && initConfig.mcp && !dryRun) {
|
|
545
|
-
try {
|
|
546
|
-
const mcpWriter = require('../lib/install/mcp-writer.cjs');
|
|
547
|
-
const result = mcpWriter.writeMcpConfig({
|
|
548
|
-
runtime: initConfig.runtime,
|
|
549
|
-
scope: initConfig.scope,
|
|
550
|
-
projectRoot,
|
|
551
|
-
});
|
|
552
|
-
console.error(green + ' [mcp] nubos MCP configured → ' + result.path + reset);
|
|
553
|
-
} catch (err) {
|
|
554
|
-
console.error(yellow + ' [mcp] skipped: ' + (err && err.message) + reset);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
542
|
if (opencodeSelected) {
|
|
559
543
|
const projectOpencodeJson = path.join(projectRoot, 'opencode.json');
|
|
560
544
|
if (!fs.existsSync(projectOpencodeJson) && fs.existsSync(OPENCODE_JSON_TEMPLATE)) {
|
|
@@ -625,7 +609,9 @@ function _runUninstallLocked(projectRoot) {
|
|
|
625
609
|
const assetDirs = new Set();
|
|
626
610
|
for (const rel of Object.keys(manifest.files)) {
|
|
627
611
|
const isAsset = runtimeAssetsMod.isAssetManifestKey(rel);
|
|
628
|
-
const abs =
|
|
612
|
+
const abs = rel.startsWith('~/')
|
|
613
|
+
? path.join(os.homedir(), rel.slice(2))
|
|
614
|
+
: isAsset ? path.join(payloadBase, rel) : path.join(payloadDir, rel);
|
|
629
615
|
try {
|
|
630
616
|
fs.unlinkSync(abs);
|
|
631
617
|
removed++;
|
|
@@ -651,7 +637,7 @@ function _runUninstallLocked(projectRoot) {
|
|
|
651
637
|
|
|
652
638
|
try { fs.unlinkSync(path.join(payloadDir, '.manifest.json')); } catch {}
|
|
653
639
|
|
|
654
|
-
try { fs.rmdirSync(payloadDir); } catch {
|
|
640
|
+
try { fs.rmdirSync(payloadDir); } catch {}
|
|
655
641
|
|
|
656
642
|
const cfgPath = path.join(_stateDirFor(projectRoot), 'config.json');
|
|
657
643
|
let installedRuntimes = [];
|
package/bin/np-tools/_args.cjs
CHANGED
|
@@ -54,10 +54,18 @@ function assertOptionalMatch(value, re, code, label) {
|
|
|
54
54
|
assertMatch(value, re, code, label);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
function emitErrorEnvelope(err, stderr, fallbackCode) {
|
|
58
|
+
const code = err && err.name === 'NubosPilotError' ? err.code : fallbackCode;
|
|
59
|
+
const message = (err && err.message) || String(err);
|
|
60
|
+
const details = (err && err.details) || null;
|
|
61
|
+
stderr.write(JSON.stringify({ code, message, details }) + '\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
57
64
|
module.exports = {
|
|
58
65
|
getFlag,
|
|
59
66
|
getJsonFlag,
|
|
60
67
|
optionalJsonFlag,
|
|
61
68
|
assertMatch,
|
|
62
69
|
assertOptionalMatch,
|
|
70
|
+
emitErrorEnvelope,
|
|
63
71
|
};
|
|
@@ -7,7 +7,7 @@ const COMMANDS = [
|
|
|
7
7
|
{ name: 'discuss-phase', category: 'Planning', description: 'Adaptive milestone-context interview (writes M<NNN>-CONTEXT.md)', description_de: 'Adaptives Milestone-Kontext-Interview (schreibt M<NNN>-CONTEXT.md)' },
|
|
8
8
|
{ name: 'research-phase', category: 'Planning', description: 'Milestone-level research (WebFetch + MCP; offline fallback)', description_de: 'Milestone-Recherche (WebFetch + MCP; Offline-Fallback)' },
|
|
9
9
|
{ name: 'plan-milestone', category: 'Planning', description: 'Plan a milestone: scaffolds slices + tasks', description_de: 'Plant einen Milestone: erzeugt Slices + Tasks' },
|
|
10
|
-
{ name: 'plan-lint', category: 'Planning', description: 'Mechanical Trust-Layer linter for PLAN.md (verify-command + parallel-race + over-specification). ADR-
|
|
10
|
+
{ name: 'plan-lint', category: 'Planning', description: 'Mechanical Trust-Layer linter for PLAN.md (verify-command + parallel-race + over-specification). ADR-0019', description_de: 'Mechanischer Trust-Layer-Linter für PLAN.md (verify-command + parallel-race + Über-Spezifikation). ADR-0019' },
|
|
11
11
|
{ name: 'output-lint', category: 'Review', description: 'Mechanical output-artifact linter (frontmatter + body + cross-field invariants). Verbs: check | prompt | list. Schemas in lib/schemas/. Hard-gates verify-work, validate-phase. ADR-0017', description_de: 'Mechanischer Output-Artefakt-Linter (Frontmatter + Body + Cross-Field-Invarianten). Verben: check | prompt | list. Schemas in lib/schemas/. Hard-Gate für verify-work, validate-phase. ADR-0017' },
|
|
12
12
|
{ name: 'researcher-reconcile', category: 'Planning', description: 'Researcher-swarm reconciliation (ADR-0018). Verbs: parse-spawn --file | prepare <N> | gate <N>. Reads per-spawn outputs, applies reasoning-trace classification, surfaces contested decisions, hard-gates on agreement_score / contested_count.', description_de: 'Researcher-Schwarm-Reconciliation (ADR-0018). Verben: parse-spawn --file | prepare <N> | gate <N>. Liest Per-Spawn-Outputs, klassifiziert Reasoning-Trace, hebt Contested Decisions hervor, Hard-Gate auf agreement_score / contested_count.' },
|
|
13
13
|
{ name: 'new-project', category: 'Planning', description: 'Greenfield project init (PROJECT.md + REQUIREMENTS.md + M001 milestone)', description_de: 'Greenfield-Projekt-Init (PROJECT.md + REQUIREMENTS.md + M001-Milestone)' },
|
|
@@ -33,17 +33,14 @@ const COMMANDS = [
|
|
|
33
33
|
{ name: 'undo-task', category: 'Execution', description: 'Revert a single task commit and reset task status to pending', description_de: 'Revertiert einen einzelnen Task-Commit und setzt Task-Status auf pending zurück' },
|
|
34
34
|
{ name: 'reset-slice', category: 'Execution', description: 'Discard in-flight task: restore working tree from HEAD, drop checkpoint, clear STATE.current_task', description_de: 'Verwirft laufenden Task: stellt Working-Tree von HEAD wieder her, löscht Checkpoint, leert STATE.current_task' },
|
|
35
35
|
|
|
36
|
-
{ name: 'doctor', category: 'Install', description: '
|
|
36
|
+
{ name: 'doctor', category: 'Install', description: '12-check install-integrity scan (--fix for auto-safe fixes)', description_de: '12-Check-Install-Integritäts-Scan (--fix für auto-sichere Fixes)' },
|
|
37
37
|
{ name: 'scan-codebase', category: 'Install', description: 'Initial deep codebase inventory → .nubos-pilot/codebase/ skill docs', description_de: 'Initiale tiefe Codebase-Inventur → .nubos-pilot/codebase/ Skill-Docs' },
|
|
38
38
|
{ name: 'update-docs', category: 'Install', description: 'Refresh stale module docs after code changes', description_de: 'Aktualisiert veraltete Modul-Docs nach Code-Änderungen' },
|
|
39
39
|
|
|
40
40
|
{ name: 'resolve-model', category: 'Utility', description: 'Resolve agent/tier to model alias or id (Tier×Profile matrix)', description_de: 'Löst Agent/Tier zu Model-Alias oder -ID auf (Tier×Profile-Matrix)' },
|
|
41
41
|
{ name: 'metrics', category: 'Utility', description: 'Record JSONL metrics entry (record | now | start-timestamp | end-timestamp)', description_de: 'Schreibt JSONL-Metrics-Eintrag (record | now | start-timestamp | end-timestamp)' },
|
|
42
42
|
|
|
43
|
-
{ name: 'validate-phase', category: 'Review', description: 'Nyquist validation gap-fill via np-nyquist-auditor', description_de: 'Nyquist-Validierungs-Gap-Fill über np-nyquist-auditor' },
|
|
44
|
-
|
|
45
43
|
{ name: 'add-todo', category: 'Capture', description: 'Capture a pending todo to .nubos-pilot/todos/pending/ + increment STATE count', description_de: 'Erfasst pending Todo nach .nubos-pilot/todos/pending/ + erhöht STATE-Counter' },
|
|
46
|
-
{ name: 'note', category: 'Capture', description: 'Capture a free-form note (project default, --global writes to ~/.nubos-pilot/notes/)', description_de: 'Erfasst freiformige Notiz (Projekt-Default, --global schreibt nach ~/.nubos-pilot/notes/)' },
|
|
47
44
|
|
|
48
45
|
{ name: 'askuser', category: 'Utility', description: 'Capability-layer prompt wrapper (reads spec JSON, returns chosen label)', description_de: 'Capability-Layer-Prompt-Wrapper (liest Spec-JSON, gibt gewähltes Label zurück)' },
|
|
49
46
|
{ name: 'commit', category: 'Utility', description: 'Atomic git commit wrapper with gitignore-guard', description_de: 'Atomarer Git-Commit-Wrapper mit Gitignore-Guard' },
|
|
@@ -64,7 +61,7 @@ const COMMANDS = [
|
|
|
64
61
|
{ name: 'handoff-list', category: 'Capture', description: 'List handoffs (JSON array); filter with --for AGENT, --milestone M<NNN>, --status STATUS, --global', description_de: 'Listet Handoffs (JSON-Array); filtert mit --for AGENT, --milestone M<NNN>, --status STATUS, --global' },
|
|
65
62
|
{ name: 'handoff-status', category: 'Capture', description: 'Update a handoff status (open|read|acted|archived)', description_de: 'Aktualisiert Handoff-Status (open|read|acted|archived)' },
|
|
66
63
|
{ name: 'messages-send', category: 'Capture', description: 'Send addressed inter-agent message (request|response|notify) to .nubos-pilot/messages/inbox/<to>/. ADR-0015', description_de: 'Sendet adressierte Inter-Agent-Nachricht (request|response|notify) an .nubos-pilot/messages/inbox/<to>/. ADR-0015' },
|
|
67
|
-
{ name: 'messages-inbox', category: 'Capture', description: 'List
|
|
64
|
+
{ name: 'messages-inbox', category: 'Capture', description: 'List unread messages addressed to an agent (filterable by --kind, --since, --task)', description_de: 'Listet ungelesene Messages für einen Agent (filterbar via --kind, --since, --task)' },
|
|
68
65
|
{ name: 'messages-archive', category: 'Capture', description: 'Move an inbox message to archive/; refuses request+expects_reply without prior response', description_de: 'Verschiebt Inbox-Message nach archive/; weigert sich bei request+expects_reply ohne Reply' },
|
|
69
66
|
{ name: 'messages-thread', category: 'Capture', description: 'Print full reply-chain for a message id (causal order)', description_de: 'Gibt vollständige Reply-Chain für eine Message-ID aus (kausale Reihenfolge)' },
|
|
70
67
|
{ name: 'memory-index', category: 'Capture', description: 'Bulk-index records into vector memory (--records JSON or --records-file JSONL). Opt-in via memory.enabled=true. ADR-0014', description_de: 'Bulk-Index für Records ins Vector-Memory (--records JSON oder --records-file JSONL). Opt-in via memory.enabled=true. ADR-0014' },
|
|
@@ -80,13 +77,12 @@ const COMMANDS = [
|
|
|
80
77
|
{ name: 'thread-resume', category: 'Utility', description: 'Bump a thread markdown on resume (status OPEN→IN_PROGRESS, refresh last_resumed) via atomic write', description_de: 'Bumpt Thread-Markdown beim Resume (Status OPEN→IN_PROGRESS, aktualisiert last_resumed) via atomic write' },
|
|
81
78
|
{ name: 'state-incr', category: 'Capture', description: 'Increment a whitelisted STATE.md counter (e.g. pending_todos) under withFileLock', description_de: 'Erhöht whitelisteten STATE.md-Counter (z.B. pending_todos) unter withFileLock' },
|
|
82
79
|
|
|
83
|
-
{ name: 'thread', category: 'Utility', description: 'Cross-session thread CRUD (create/resume under .nubos-pilot/threads/)', description_de: 'Cross-Session-Thread-CRUD (create/resume unter .nubos-pilot/threads/)' },
|
|
84
80
|
{ name: 'session-aggregate', category: 'Utility', description: 'Aggregate session metrics under withFileLock; reads pointer .last-session unless --since overrides', description_de: 'Aggregiert Session-Metriken unter withFileLock; liest Pointer .last-session, außer --since überschreibt' },
|
|
85
81
|
{ name: 'session-pointer-write', category: 'Utility', description: 'Atomic write of .nubos-pilot/reports/.last-session under withFileLock (ISO-8601 UTC)', description_de: 'Atomares Schreiben von .nubos-pilot/reports/.last-session unter withFileLock (ISO-8601 UTC)' },
|
|
86
82
|
{ name: 'workspace-scan', category: 'Install', description: 'Scan a workspace and emit inventory JSON (full result or --summary shape for /np:new-project)', description_de: 'Scannt einen Workspace und liefert Inventar-JSON (volles Ergebnis oder --summary-Shape für /np:new-project)' },
|
|
87
83
|
|
|
88
84
|
{ name: 'knowledge-index', category: 'Utility', description: 'Build BM25-light index over .nubos-pilot/**/*.md → .nubos-pilot/state/knowledge-index.json', description_de: 'Baut BM25-Light-Index über .nubos-pilot/**/*.md → .nubos-pilot/state/knowledge-index.json' },
|
|
89
|
-
{ name: 'knowledge-search', category: 'Utility', description: 'Query the knowledge index; returns top-N JSON hits (rel_path + lines + score + preview)', description_de: 'Sucht im Knowledge-Index; liefert Top-N-JSON-Treffer (rel_path + Zeilen + Score + Preview)' },
|
|
85
|
+
{ name: 'knowledge-search', category: 'Utility', description: 'Query the knowledge index; returns top-N JSON hits (rel_path + lines + score + preview). Pass --task <id> inside a Nubosloop task to record Rule 9 audit evidence', description_de: 'Sucht im Knowledge-Index; liefert Top-N-JSON-Treffer (rel_path + Zeilen + Score + Preview). --task <id> innerhalb eines Nubosloop-Tasks schreibt den Rule-9-Audit-Nachweis' },
|
|
90
86
|
{ name: 'knowledge-stats', category: 'Utility', description: 'Print knowledge-index size + grouping (auto-builds if missing)', description_de: 'Gibt Knowledge-Index-Größe + Gruppierung aus (baut auto bei Fehlen)' },
|
|
91
87
|
{ name: 'context-stats', category: 'Utility', description: 'Aggregated context-budget stats (file counts + bytes per group, knowledge-index size)', description_de: 'Aggregierte Context-Budget-Stats (Dateien/Bytes pro Gruppe, Knowledge-Index-Größe)' },
|
|
92
88
|
{ name: 'session-snapshot-write', category: 'Utility', description: 'Capture session snapshot (current_task + recent commits + open handoffs) for resume', description_de: 'Erfasst Session-Snapshot (current_task + letzte Commits + offene Handoffs) für Resume' },
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
|
|
4
|
+
const { COMMANDS } = require('./_commands.cjs');
|
|
5
|
+
const nt = require('../../np-tools.cjs');
|
|
6
|
+
|
|
7
|
+
const HARD_CASED = ['init', 'state', 'help'];
|
|
8
|
+
|
|
9
|
+
function dispatchNames() {
|
|
10
|
+
return new Set([
|
|
11
|
+
...HARD_CASED,
|
|
12
|
+
...Object.keys(nt.initWorkflows),
|
|
13
|
+
...Object.keys(nt.topLevelCommands),
|
|
14
|
+
]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function catalogNames() {
|
|
18
|
+
return new Set(COMMANDS.map((c) => c.name));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test('CAT-DISPATCH-1: every catalog command has a dispatch entry', () => {
|
|
22
|
+
const dispatch = dispatchNames();
|
|
23
|
+
const orphans = [...catalogNames()].filter((n) => !dispatch.has(n));
|
|
24
|
+
assert.deepEqual(orphans, [], 'catalog commands without a dispatch entry: ' + orphans.join(', '));
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('CAT-DISPATCH-2: every dispatch entry has a catalog command', () => {
|
|
28
|
+
const catalog = catalogNames();
|
|
29
|
+
const orphans = [...dispatchNames()].filter((n) => !catalog.has(n));
|
|
30
|
+
assert.deepEqual(orphans, [], 'dispatch entries without a catalog command: ' + orphans.join(', '));
|
|
31
|
+
});
|
package/bin/np-tools/askuser.cjs
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
const { askUser } = require('../../lib/askuser.cjs');
|
|
2
|
+
const { emitErrorEnvelope } = require('./_args.cjs');
|
|
2
3
|
|
|
3
4
|
function _usage() {
|
|
4
5
|
return 'Usage:\n np-tools.cjs askuser --json \'{...spec...}\'';
|
|
5
6
|
}
|
|
6
7
|
|
|
7
|
-
function _emitError(err, stderr) {
|
|
8
|
-
const code = err && err.name === 'NubosPilotError' ? err.code : 'askuser-internal-error';
|
|
9
|
-
const message = (err && err.message) || String(err);
|
|
10
|
-
const details = (err && err.details) || null;
|
|
11
|
-
stderr.write(JSON.stringify({ code, message, details }) + '\n');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
8
|
async function run(argv, ctx) {
|
|
15
9
|
const context = ctx || {};
|
|
16
10
|
const stdout = context.stdout || process.stdout;
|
|
@@ -38,7 +32,7 @@ async function run(argv, ctx) {
|
|
|
38
32
|
stdout.write(out + '\n');
|
|
39
33
|
return 0;
|
|
40
34
|
} catch (err) {
|
|
41
|
-
|
|
35
|
+
emitErrorEnvelope(err, stderr, 'askuser-internal-error');
|
|
42
36
|
return 1;
|
|
43
37
|
}
|
|
44
38
|
}
|
|
@@ -85,8 +85,6 @@ function _resolveTaskFile(taskId, cwd) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function _resolveSafe(root, p) {
|
|
88
|
-
|
|
89
|
-
|
|
90
88
|
const abs = path.resolve(root, p);
|
|
91
89
|
const rel = path.relative(root, abs);
|
|
92
90
|
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
@@ -203,7 +201,7 @@ function run(args, ctx) {
|
|
|
203
201
|
|
|
204
202
|
const sha = findCommitByTaskId(taskId);
|
|
205
203
|
|
|
206
|
-
try { deleteCheckpoint(taskId, cwd); } catch {
|
|
204
|
+
try { deleteCheckpoint(taskId, cwd); } catch {}
|
|
207
205
|
try { setTaskStatus(taskId, 'done', cwd); } catch (err) {
|
|
208
206
|
process.stderr.write('[nubos-pilot warn] setTaskStatus failed for ' + taskId + ': ' + (err && err.message) + '\n');
|
|
209
207
|
}
|
package/bin/np-tools/commit.cjs
CHANGED
|
@@ -4,6 +4,7 @@ const path = require('node:path');
|
|
|
4
4
|
const { NubosPilotError, findProjectRoot } = require('../../lib/core.cjs');
|
|
5
5
|
const { assertCommittablePaths } = require('../../lib/git.cjs');
|
|
6
6
|
const { resolveCommitArtifacts } = require('../../lib/commit-policy.cjs');
|
|
7
|
+
const { emitErrorEnvelope } = require('./_args.cjs');
|
|
7
8
|
|
|
8
9
|
const MAX_MSG = 2000;
|
|
9
10
|
|
|
@@ -11,13 +12,6 @@ function _usage() {
|
|
|
11
12
|
return 'Usage:\n np-tools.cjs commit "message" --files f1 f2 ...';
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
function _emitError(err, stderr) {
|
|
15
|
-
const code = err && err.name === 'NubosPilotError' ? err.code : 'commit-internal-error';
|
|
16
|
-
const message = (err && err.message) || String(err);
|
|
17
|
-
const details = (err && err.details) || null;
|
|
18
|
-
stderr.write(JSON.stringify({ code, message, details }) + '\n');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
15
|
function _parseArgs(argv) {
|
|
22
16
|
const args = Array.isArray(argv) ? argv.slice() : [];
|
|
23
17
|
let msg = null;
|
|
@@ -123,7 +117,7 @@ function run(argv, ctx) {
|
|
|
123
117
|
stdout.write(JSON.stringify({ committed: true, sha, files: committable }) + '\n');
|
|
124
118
|
return 0;
|
|
125
119
|
} catch (err) {
|
|
126
|
-
|
|
120
|
+
emitErrorEnvelope(err, stderr, 'commit-internal-error');
|
|
127
121
|
return 1;
|
|
128
122
|
}
|
|
129
123
|
}
|
package/bin/np-tools/config.cjs
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('node:fs');
|
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
const { findProjectRoot, NubosPilotError } = require('../../lib/core.cjs');
|
|
4
4
|
const { DEFAULT_CONFIG_TREE } = require('../../lib/config-defaults.cjs');
|
|
5
|
+
const { emitErrorEnvelope } = require('./_args.cjs');
|
|
5
6
|
|
|
6
7
|
const SEGMENT_RE = /^[a-zA-Z0-9_-]+$/;
|
|
7
8
|
const BLOCKED_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
@@ -10,13 +11,6 @@ function _usage() {
|
|
|
10
11
|
return 'Usage:\n np-tools.cjs config-get <dotted.key> [--raw]';
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
function _emitError(err, stderr) {
|
|
14
|
-
const code = err && err.name === 'NubosPilotError' ? err.code : 'config-get-internal-error';
|
|
15
|
-
const message = (err && err.message) || String(err);
|
|
16
|
-
const details = (err && err.details) || null;
|
|
17
|
-
stderr.write(JSON.stringify({ code, message, details }) + '\n');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
14
|
function _readConfig(cwd) {
|
|
21
15
|
let root;
|
|
22
16
|
try {
|
|
@@ -89,7 +83,7 @@ function run(argv, ctx) {
|
|
|
89
83
|
else stdout.write(out + '\n');
|
|
90
84
|
return 0;
|
|
91
85
|
} catch (err) {
|
|
92
|
-
|
|
86
|
+
emitErrorEnvelope(err, stderr, 'config-get-internal-error');
|
|
93
87
|
return 1;
|
|
94
88
|
}
|
|
95
89
|
}
|
|
@@ -6,9 +6,6 @@ const { scan } = require('../../lib/workspace-scan.cjs');
|
|
|
6
6
|
const { workspaceGitInfo } = require('../../lib/git.cjs');
|
|
7
7
|
const textMode = require('../../lib/text-mode.cjs');
|
|
8
8
|
|
|
9
|
-
const TEMPLATES_DIR = path.join(__dirname, '..', '..', 'templates');
|
|
10
|
-
const PLACEHOLDER_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
|
|
11
|
-
|
|
12
9
|
const REQUIRED_FIELDS = Object.freeze([
|
|
13
10
|
'project_description',
|
|
14
11
|
'domain_text',
|
|
@@ -18,19 +15,6 @@ const REQUIRED_FIELDS = Object.freeze([
|
|
|
18
15
|
'strategic_decisions_text',
|
|
19
16
|
]);
|
|
20
17
|
|
|
21
|
-
function _render(raw, vars, name) {
|
|
22
|
-
return raw.replace(PLACEHOLDER_RE, (_m, key) => {
|
|
23
|
-
if (!(key in vars)) {
|
|
24
|
-
throw new NubosPilotError(
|
|
25
|
-
'template-unresolved-var',
|
|
26
|
-
`Undefined placeholder {{${key}}} in template "${name}"`,
|
|
27
|
-
{ template: name, variable: key, available: Object.keys(vars) },
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
return String(vars[key]);
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
18
|
function _parseArgs(args) {
|
|
35
19
|
const flags = {
|
|
36
20
|
cwd: null,
|
|
@@ -140,17 +124,6 @@ function _readExistingProjectMd(projectMd) {
|
|
|
140
124
|
}
|
|
141
125
|
}
|
|
142
126
|
|
|
143
|
-
function _extractExistingField(md, heading) {
|
|
144
|
-
const re = new RegExp('^##\\s+' + heading.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + '\\s*$', 'm');
|
|
145
|
-
const match = md.match(re);
|
|
146
|
-
if (!match) return null;
|
|
147
|
-
const start = match.index + match[0].length;
|
|
148
|
-
const rest = md.slice(start);
|
|
149
|
-
const nextHeading = rest.match(/\n##\s+/);
|
|
150
|
-
const body = nextHeading ? rest.slice(0, nextHeading.index) : rest;
|
|
151
|
-
return body.trim();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
127
|
function _replaceSectionBody(md, heading, newBody) {
|
|
155
128
|
const lines = md.split('\n');
|
|
156
129
|
const headingRe = new RegExp('^##\\s+' + heading.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + '\\s*$');
|
package/bin/np-tools/doctor.cjs
CHANGED
|
@@ -18,7 +18,6 @@ const PAYLOAD_SUBPATH = path.join('.claude', 'nubos-pilot');
|
|
|
18
18
|
const STATE_SUBPATH = '.nubos-pilot';
|
|
19
19
|
const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
|
|
20
20
|
const OPENCODE_LOCAL_PREFIX = '.opencode/nubos-pilot/';
|
|
21
|
-
const OPENCODE_GLOBAL_PREFIX = '~/.config/opencode/nubos-pilot/';
|
|
22
21
|
|
|
23
22
|
function _readScope(projectRoot) {
|
|
24
23
|
const cfgPath = path.join(projectRoot, STATE_SUBPATH, 'config.json');
|
|
@@ -433,25 +432,15 @@ function _checkNubosloopConfig(projectRoot) {
|
|
|
433
432
|
try { cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8')); } catch { return issues; }
|
|
434
433
|
const swarm = cfg && cfg.swarm;
|
|
435
434
|
const adapter = swarm && swarm.knowledge_adapter;
|
|
436
|
-
if (adapter && adapter !== 'local'
|
|
435
|
+
if (adapter && adapter !== 'local') {
|
|
437
436
|
issues.push({
|
|
438
437
|
id: 'nubosloop-knowledge-adapter-invalid',
|
|
439
438
|
severity: 'warn',
|
|
440
439
|
fixable: 'manual',
|
|
441
440
|
details: {
|
|
442
441
|
value: adapter,
|
|
443
|
-
supported: ['local'
|
|
444
|
-
hint: 'set swarm.knowledge_adapter to "local"
|
|
445
|
-
},
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
if (adapter === 'mcp') {
|
|
449
|
-
issues.push({
|
|
450
|
-
id: 'nubosloop-knowledge-adapter-mcp-pending',
|
|
451
|
-
severity: 'info',
|
|
452
|
-
fixable: 'manual',
|
|
453
|
-
details: {
|
|
454
|
-
hint: 'mcp adapter is configured but the transport ships in Phase 7/8; matchExistingLearning will throw mcp-adapter-not-implemented until then.',
|
|
442
|
+
supported: ['local'],
|
|
443
|
+
hint: 'set swarm.knowledge_adapter to "local" — falls back to "local" silently otherwise.',
|
|
455
444
|
},
|
|
456
445
|
});
|
|
457
446
|
}
|