nubos-pilot 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/agents/np-ai-researcher.md +140 -0
  2. package/agents/np-code-fixer.md +363 -0
  3. package/agents/np-code-reviewer.md +351 -0
  4. package/agents/np-domain-researcher.md +136 -0
  5. package/agents/np-eval-auditor.md +167 -0
  6. package/agents/np-eval-planner.md +153 -0
  7. package/agents/np-executor.md +72 -0
  8. package/agents/np-framework-selector.md +171 -0
  9. package/agents/np-nyquist-auditor.md +185 -0
  10. package/agents/np-plan-checker.md +165 -0
  11. package/agents/np-planner.md +199 -0
  12. package/agents/np-researcher.md +150 -0
  13. package/agents/np-security-auditor.md +206 -0
  14. package/agents/np-ui-auditor.md +369 -0
  15. package/agents/np-ui-checker.md +192 -0
  16. package/agents/np-ui-researcher.md +324 -0
  17. package/agents/np-verifier.md +79 -0
  18. package/bin/check-coverage.cjs +40 -0
  19. package/bin/check-workflows.cjs +171 -0
  20. package/bin/check-workflows.test.cjs +208 -0
  21. package/bin/install.js +500 -0
  22. package/bin/np-tools/_commands.cjs +70 -0
  23. package/bin/np-tools/add-tests.cjs +171 -0
  24. package/bin/np-tools/add-tests.test.cjs +122 -0
  25. package/bin/np-tools/add-todo.cjs +108 -0
  26. package/bin/np-tools/add-todo.test.cjs +112 -0
  27. package/bin/np-tools/agent-skills.cjs +14 -0
  28. package/bin/np-tools/agent-skills.test.cjs +42 -0
  29. package/bin/np-tools/ai-integration-phase.cjs +109 -0
  30. package/bin/np-tools/ai-integration-phase.test.cjs +123 -0
  31. package/bin/np-tools/askuser.cjs +53 -0
  32. package/bin/np-tools/askuser.test.cjs +49 -0
  33. package/bin/np-tools/autonomous.cjs +69 -0
  34. package/bin/np-tools/autonomous.test.cjs +74 -0
  35. package/bin/np-tools/checkpoint.cjs +101 -0
  36. package/bin/np-tools/checkpoint.test.cjs +119 -0
  37. package/bin/np-tools/code-review.cjs +133 -0
  38. package/bin/np-tools/code-review.test.cjs +96 -0
  39. package/bin/np-tools/commit-task.cjs +120 -0
  40. package/bin/np-tools/commit-task.test.cjs +160 -0
  41. package/bin/np-tools/commit.cjs +103 -0
  42. package/bin/np-tools/commit.test.cjs +93 -0
  43. package/bin/np-tools/config.cjs +101 -0
  44. package/bin/np-tools/config.test.cjs +71 -0
  45. package/bin/np-tools/discuss-phase-power.cjs +265 -0
  46. package/bin/np-tools/discuss-phase-power.test.cjs +242 -0
  47. package/bin/np-tools/discuss-phase.cjs +132 -0
  48. package/bin/np-tools/discuss-phase.test.cjs +148 -0
  49. package/bin/np-tools/dispatch.cjs +116 -0
  50. package/bin/np-tools/doctor.cjs +242 -0
  51. package/bin/np-tools/eval-review.cjs +116 -0
  52. package/bin/np-tools/eval-review.test.cjs +123 -0
  53. package/bin/np-tools/execute-phase.cjs +182 -0
  54. package/bin/np-tools/execute-phase.test.cjs +116 -0
  55. package/bin/np-tools/execute-plan.cjs +124 -0
  56. package/bin/np-tools/execute-plan.test.cjs +82 -0
  57. package/bin/np-tools/help.cjs +28 -0
  58. package/bin/np-tools/help.test.cjs +29 -0
  59. package/bin/np-tools/init-dispatch.test.cjs +91 -0
  60. package/bin/np-tools/metrics.cjs +97 -0
  61. package/bin/np-tools/metrics.test.cjs +188 -0
  62. package/bin/np-tools/new-milestone.cjs +288 -0
  63. package/bin/np-tools/new-milestone.test.cjs +166 -0
  64. package/bin/np-tools/new-project.cjs +284 -0
  65. package/bin/np-tools/new-project.test.cjs +165 -0
  66. package/bin/np-tools/next.cjs +7 -0
  67. package/bin/np-tools/next.test.cjs +30 -0
  68. package/bin/np-tools/park.cjs +48 -0
  69. package/bin/np-tools/park.test.cjs +50 -0
  70. package/bin/np-tools/pause-work.cjs +24 -0
  71. package/bin/np-tools/pause-work.test.cjs +74 -0
  72. package/bin/np-tools/phase.cjs +71 -0
  73. package/bin/np-tools/phase.test.cjs +81 -0
  74. package/bin/np-tools/plan-diff.cjs +57 -0
  75. package/bin/np-tools/plan-diff.test.cjs +134 -0
  76. package/bin/np-tools/plan-milestone-gaps.cjs +115 -0
  77. package/bin/np-tools/plan-milestone-gaps.test.cjs +122 -0
  78. package/bin/np-tools/plan-phase.cjs +350 -0
  79. package/bin/np-tools/plan-phase.test.cjs +263 -0
  80. package/bin/np-tools/progress.cjs +7 -0
  81. package/bin/np-tools/progress.test.cjs +44 -0
  82. package/bin/np-tools/queue.cjs +213 -0
  83. package/bin/np-tools/research-phase.cjs +144 -0
  84. package/bin/np-tools/research-phase.test.cjs +154 -0
  85. package/bin/np-tools/reset-slice.cjs +17 -0
  86. package/bin/np-tools/reset-slice.test.cjs +96 -0
  87. package/bin/np-tools/resolve-model.cjs +110 -0
  88. package/bin/np-tools/resolve-model.test.cjs +200 -0
  89. package/bin/np-tools/resume-work.cjs +76 -0
  90. package/bin/np-tools/resume-work.test.cjs +91 -0
  91. package/bin/np-tools/skip.cjs +48 -0
  92. package/bin/np-tools/skip.test.cjs +66 -0
  93. package/bin/np-tools/slug.cjs +34 -0
  94. package/bin/np-tools/slug.test.cjs +46 -0
  95. package/bin/np-tools/state.cjs +16 -0
  96. package/bin/np-tools/state.test.cjs +40 -0
  97. package/bin/np-tools/stats.cjs +151 -0
  98. package/bin/np-tools/stats.test.cjs +118 -0
  99. package/bin/np-tools/triage.cjs +128 -0
  100. package/bin/np-tools/ui-phase.cjs +108 -0
  101. package/bin/np-tools/ui-phase.test.cjs +121 -0
  102. package/bin/np-tools/ui-review.cjs +108 -0
  103. package/bin/np-tools/ui-review.test.cjs +120 -0
  104. package/bin/np-tools/undo-task.cjs +31 -0
  105. package/bin/np-tools/undo-task.test.cjs +117 -0
  106. package/bin/np-tools/undo.cjs +43 -0
  107. package/bin/np-tools/undo.test.cjs +120 -0
  108. package/bin/np-tools/unpark.cjs +48 -0
  109. package/bin/np-tools/unpark.test.cjs +50 -0
  110. package/bin/np-tools/verify-work.cjs +186 -0
  111. package/bin/np-tools/verify-work.test.cjs +97 -0
  112. package/docs/adr/0001-no-daemon-invariant.md +82 -0
  113. package/docs/adr/0002-zero-runtime-dependencies.md +90 -0
  114. package/docs/adr/0003-max-six-unit-types.md +85 -0
  115. package/docs/adr/0004-atomic-commit-per-unit.md +102 -0
  116. package/docs/adr/0005-three-orthogonal-file-trees.md +98 -0
  117. package/docs/adr/0006-yaml-dependency-amendment.md +60 -0
  118. package/docs/adr/README.md +27 -0
  119. package/docs/agent-frontmatter-schema.md +84 -0
  120. package/docs/phase-artifact-schemas.md +292 -0
  121. package/docs/phase-directory-layout.md +82 -0
  122. package/lib/__tests__/README.md +1 -0
  123. package/lib/agents.cjs +98 -0
  124. package/lib/agents.test.cjs +286 -0
  125. package/lib/askuser.cjs +36 -0
  126. package/lib/askuser.test.cjs +310 -0
  127. package/lib/checkpoint.cjs +135 -0
  128. package/lib/checkpoint.test.cjs +184 -0
  129. package/lib/core.cjs +165 -0
  130. package/lib/core.test.cjs +405 -0
  131. package/lib/fixtures/README.md +1 -0
  132. package/lib/fixtures/phase-tree/README.md +1 -0
  133. package/lib/fixtures/plans/cycle/PLAN.md +16 -0
  134. package/lib/fixtures/plans/cycle/tasks/T-01.md +20 -0
  135. package/lib/fixtures/plans/cycle/tasks/T-02.md +20 -0
  136. package/lib/fixtures/plans/cycle/tasks/T-03.md +20 -0
  137. package/lib/fixtures/plans/linear/PLAN.md +16 -0
  138. package/lib/fixtures/plans/linear/tasks/T-01.md +20 -0
  139. package/lib/fixtures/plans/linear/tasks/T-02.md +20 -0
  140. package/lib/fixtures/plans/linear/tasks/T-03.md +20 -0
  141. package/lib/fixtures/plans/parallel/PLAN.md +16 -0
  142. package/lib/fixtures/plans/parallel/tasks/T-01.md +20 -0
  143. package/lib/fixtures/plans/parallel/tasks/T-02.md +20 -0
  144. package/lib/fixtures/plans/parallel/tasks/T-03.md +20 -0
  145. package/lib/fixtures/plans/wave-conflict/PLAN.md +16 -0
  146. package/lib/fixtures/plans/wave-conflict/tasks/T-01.md +20 -0
  147. package/lib/fixtures/plans/wave-conflict/tasks/T-02.md +20 -0
  148. package/lib/fixtures/roadmap/ROADMAP-malformed.md +3 -0
  149. package/lib/fixtures/roadmap/ROADMAP-minimal.md +51 -0
  150. package/lib/fixtures/roadmap/roadmap-malformed.yaml +7 -0
  151. package/lib/fixtures/roadmap/roadmap-minimal.yaml +40 -0
  152. package/lib/fixtures/roadmap/roadmap-ten-phases.yaml +101 -0
  153. package/lib/fixtures/templates/phase-context.md +6 -0
  154. package/lib/fixtures/templates/plan-skeleton.md +6 -0
  155. package/lib/frontmatter.cjs +251 -0
  156. package/lib/frontmatter.test.cjs +177 -0
  157. package/lib/gaps.cjs +197 -0
  158. package/lib/gaps.test.cjs +200 -0
  159. package/lib/git.cjs +207 -0
  160. package/lib/git.test.cjs +305 -0
  161. package/lib/install/agents-md.cjs +77 -0
  162. package/lib/install/backup.cjs +70 -0
  163. package/lib/install/codex-toml.cjs +440 -0
  164. package/lib/install/managed-block.cjs +30 -0
  165. package/lib/install/manifest.cjs +148 -0
  166. package/lib/install/mcp-writer.cjs +127 -0
  167. package/lib/install/runtime-detect.cjs +44 -0
  168. package/lib/install/staging.cjs +149 -0
  169. package/lib/metrics-aggregate.cjs +229 -0
  170. package/lib/metrics-aggregate.test.cjs +192 -0
  171. package/lib/metrics.cjs +120 -0
  172. package/lib/metrics.test.cjs +182 -0
  173. package/lib/model-aliases.regression.test.cjs +16 -0
  174. package/lib/model-profiles.cjs +42 -0
  175. package/lib/model-profiles.test.cjs +61 -0
  176. package/lib/next.cjs +236 -0
  177. package/lib/next.test.cjs +194 -0
  178. package/lib/phase.cjs +95 -0
  179. package/lib/phase.test.cjs +189 -0
  180. package/lib/plan-checker-contract.test.cjs +72 -0
  181. package/lib/plan-diff.cjs +173 -0
  182. package/lib/plan-diff.test.cjs +217 -0
  183. package/lib/plan.cjs +85 -0
  184. package/lib/plan.test.cjs +263 -0
  185. package/lib/progress.cjs +95 -0
  186. package/lib/progress.test.cjs +116 -0
  187. package/lib/researcher-contract.test.cjs +61 -0
  188. package/lib/roadmap-render.cjs +206 -0
  189. package/lib/roadmap-render.test.cjs +121 -0
  190. package/lib/roadmap.cjs +416 -0
  191. package/lib/roadmap.test.cjs +371 -0
  192. package/lib/runtime/_contract.test.cjs +61 -0
  193. package/lib/runtime/_readline.cjs +119 -0
  194. package/lib/runtime/_readline.test.cjs +126 -0
  195. package/lib/runtime/claude.cjs +48 -0
  196. package/lib/runtime/claude.test.cjs +101 -0
  197. package/lib/runtime/codex.cjs +35 -0
  198. package/lib/runtime/codex.test.cjs +114 -0
  199. package/lib/runtime/gemini.cjs +35 -0
  200. package/lib/runtime/gemini.test.cjs +109 -0
  201. package/lib/runtime/index.cjs +49 -0
  202. package/lib/runtime/index.test.cjs +181 -0
  203. package/lib/runtime/opencode.cjs +35 -0
  204. package/lib/runtime/opencode.test.cjs +124 -0
  205. package/lib/state.cjs +205 -0
  206. package/lib/state.test.cjs +264 -0
  207. package/lib/surface-audit.test.cjs +46 -0
  208. package/lib/tasks.cjs +327 -0
  209. package/lib/tasks.test.cjs +389 -0
  210. package/lib/template.cjs +66 -0
  211. package/lib/template.test.cjs +159 -0
  212. package/lib/undo.cjs +179 -0
  213. package/lib/undo.test.cjs +261 -0
  214. package/lib/verify.cjs +116 -0
  215. package/lib/verify.test.cjs +187 -0
  216. package/np-tools.cjs +303 -0
  217. package/package.json +39 -0
  218. package/templates/AI-SPEC.md +90 -0
  219. package/templates/CONTEXT.md +32 -0
  220. package/templates/PLAN.md +69 -0
  221. package/templates/PROJECT.md +60 -0
  222. package/templates/REQUIREMENTS.md +38 -0
  223. package/templates/SECURITY.md +61 -0
  224. package/templates/UI-SPEC.md +64 -0
  225. package/templates/VALIDATION.md +76 -0
  226. package/templates/claude/payload/README.md +11 -0
  227. package/templates/opencode/opencode.json +6 -0
  228. package/templates/opencode/payload/AGENTS.md +9 -0
  229. package/workflows/add-backlog.md +212 -0
  230. package/workflows/add-tests.md +69 -0
  231. package/workflows/add-todo.md +222 -0
  232. package/workflows/ai-integration-phase.md +230 -0
  233. package/workflows/autonomous.md +94 -0
  234. package/workflows/cleanup.md +325 -0
  235. package/workflows/code-review-fix.md +435 -0
  236. package/workflows/code-review.md +447 -0
  237. package/workflows/discuss-phase-assumptions.md +269 -0
  238. package/workflows/discuss-phase-power.md +139 -0
  239. package/workflows/discuss-phase.md +386 -0
  240. package/workflows/dispatch.md +9 -0
  241. package/workflows/doctor.md +10 -0
  242. package/workflows/eval-review.md +243 -0
  243. package/workflows/execute-phase.md +142 -0
  244. package/workflows/execute-plan.md +82 -0
  245. package/workflows/help.md +8 -0
  246. package/workflows/new-milestone.md +166 -0
  247. package/workflows/new-project.md +213 -0
  248. package/workflows/next.md +8 -0
  249. package/workflows/note.md +244 -0
  250. package/workflows/park.md +29 -0
  251. package/workflows/pause-work.md +34 -0
  252. package/workflows/plan-milestone-gaps.md +233 -0
  253. package/workflows/plan-phase.md +351 -0
  254. package/workflows/progress.md +8 -0
  255. package/workflows/queue.md +9 -0
  256. package/workflows/research-phase.md +327 -0
  257. package/workflows/reset-slice.md +39 -0
  258. package/workflows/resume-work.md +79 -0
  259. package/workflows/review.md +489 -0
  260. package/workflows/secure-phase.md +209 -0
  261. package/workflows/session-report.md +243 -0
  262. package/workflows/skip.md +29 -0
  263. package/workflows/state.md +7 -0
  264. package/workflows/stats.md +170 -0
  265. package/workflows/thread.md +214 -0
  266. package/workflows/triage.md +9 -0
  267. package/workflows/ui-phase.md +246 -0
  268. package/workflows/ui-review.md +222 -0
  269. package/workflows/undo-task.md +42 -0
  270. package/workflows/undo.md +55 -0
  271. package/workflows/unpark.md +29 -0
  272. package/workflows/validate-phase.md +231 -0
  273. package/workflows/verify-work.md +83 -0
@@ -0,0 +1,242 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ const { NubosPilotError, atomicWriteFileSync } = require('../../lib/core.cjs');
8
+ const manifestMod = require('../../lib/install/manifest.cjs');
9
+ const codexTomlMod = require('../../lib/install/codex-toml.cjs');
10
+ const askuserMod = require('../../lib/askuser.cjs');
11
+
12
+ const PAYLOAD_SUBPATH = path.join('.claude', 'nubos-pilot');
13
+ const CODEX_CONFIG_PATH = path.join(os.homedir(), '.codex', 'config.toml');
14
+
15
+ function _payloadDirFor(projectRoot) {
16
+ return path.join(projectRoot, PAYLOAD_SUBPATH);
17
+ }
18
+
19
+ function _pkgVersion() {
20
+ try {
21
+ return require('../../package.json').version || '0.0.0';
22
+ } catch {
23
+ return '0.0.0';
24
+ }
25
+ }
26
+
27
+ function _checkManifestIntegrity(payloadDir) {
28
+ const issues = [];
29
+ let manifest = null;
30
+ try {
31
+ manifest = manifestMod.readManifest(payloadDir);
32
+ } catch (err) {
33
+ issues.push({
34
+ id: 'missing-manifest',
35
+ severity: 'error',
36
+ fixable: 'reinstall',
37
+ details: { reason: 'parse-failed', cause: err && err.message },
38
+ });
39
+ return { manifest: null, issues };
40
+ }
41
+ if (!manifest) {
42
+ issues.push({
43
+ id: 'missing-manifest',
44
+ severity: 'error',
45
+ fixable: 'reinstall',
46
+ details: { payloadDir },
47
+ });
48
+ return { manifest: null, issues };
49
+ }
50
+ const files = (manifest.files && typeof manifest.files === 'object') ? manifest.files : {};
51
+ for (const rel of Object.keys(files)) {
52
+ const full = path.join(payloadDir, rel);
53
+ if (!fs.existsSync(full)) {
54
+ issues.push({
55
+ id: 'payload-missing',
56
+ file: rel,
57
+ severity: 'error',
58
+ fixable: 'reinstall',
59
+ });
60
+ continue;
61
+ }
62
+ let hash;
63
+ try { hash = manifestMod.fileHashSync(full); } catch { hash = null; }
64
+ if (hash && hash !== files[rel]) {
65
+ issues.push({
66
+ id: 'payload-modified',
67
+ file: rel,
68
+ severity: 'warn',
69
+ fixable: 'reinstall',
70
+ });
71
+ }
72
+ }
73
+ return { manifest, issues };
74
+ }
75
+
76
+ function _checkVersionMismatch(manifest) {
77
+ if (!manifest) return [];
78
+ const installed = String(manifest.version == null ? '' : manifest.version);
79
+ const pkg = String(_pkgVersion());
80
+ if (installed && installed !== pkg) {
81
+ return [{
82
+ id: 'version-mismatch',
83
+ severity: 'warn',
84
+ fixable: 'reinstall',
85
+ details: { installed, expected: pkg },
86
+ }];
87
+ }
88
+ return [];
89
+ }
90
+
91
+ function _checkHooksMissing(manifest, payloadDir) {
92
+ if (!manifest) return [];
93
+ const files = (manifest.files && typeof manifest.files === 'object') ? manifest.files : {};
94
+ const hasHooksEntries = Object.keys(files).some((rel) => rel.startsWith('hooks/'));
95
+ if (!hasHooksEntries) return [];
96
+ const hooksDir = path.join(payloadDir, 'hooks');
97
+ if (fs.existsSync(hooksDir)) return [];
98
+ return [{
99
+ id: 'hooks-missing',
100
+ severity: 'error',
101
+ fixable: 'reinstall',
102
+ details: { hooksDir },
103
+ }];
104
+ }
105
+
106
+ function _checkCodexTrappedFeatures() {
107
+ if (!fs.existsSync(CODEX_CONFIG_PATH)) return { issues: [], content: null };
108
+ let content;
109
+ try {
110
+ content = fs.readFileSync(CODEX_CONFIG_PATH, 'utf-8');
111
+ } catch (err) {
112
+ return {
113
+ issues: [{
114
+ id: 'codex-trapped-features',
115
+ severity: 'warn',
116
+ fixable: 'reinstall',
117
+ details: { reason: 'read-failed', cause: err && err.message },
118
+ }],
119
+ content: null,
120
+ };
121
+ }
122
+ if (codexTomlMod.hasTrappedFeatures(content)) {
123
+ return {
124
+ issues: [{
125
+ id: 'codex-trapped-features',
126
+ severity: 'warn',
127
+ fixable: 'auto',
128
+ details: { path: CODEX_CONFIG_PATH },
129
+ }],
130
+ content,
131
+ };
132
+ }
133
+ return { issues: [], content };
134
+ }
135
+
136
+ function _checkAskUserBroken() {
137
+ try {
138
+ askuserMod.getRuntime();
139
+ return [];
140
+ } catch (err) {
141
+ return [{
142
+ id: 'askuser-broken',
143
+ severity: 'warn',
144
+ fixable: 'prompt',
145
+ details: { cause: err && err.message },
146
+ }];
147
+ }
148
+ }
149
+
150
+ function _audit(projectRoot) {
151
+ const payloadDir = _payloadDirFor(projectRoot);
152
+ const issues = [];
153
+ const { manifest, issues: manifestIssues } = _checkManifestIntegrity(payloadDir);
154
+ issues.push(...manifestIssues);
155
+ issues.push(..._checkVersionMismatch(manifest));
156
+ issues.push(..._checkHooksMissing(manifest, payloadDir));
157
+ const codex = _checkCodexTrappedFeatures();
158
+ issues.push(...codex.issues);
159
+ issues.push(..._checkAskUserBroken());
160
+ return { issues, _codexContent: codex.content };
161
+ }
162
+
163
+ function _fixCodexTrappedFeatures(content) {
164
+ const repaired = codexTomlMod.repairTrappedFeatures(content);
165
+ if (repaired === content) return false;
166
+ atomicWriteFileSync(CODEX_CONFIG_PATH, repaired);
167
+ return true;
168
+ }
169
+
170
+ async function _applyFixes(issues, codexContent, askUser, stderr) {
171
+ const applied = [];
172
+ const skipped = [];
173
+ for (const issue of issues) {
174
+ if (issue.fixable === 'auto') {
175
+ if (issue.id === 'codex-trapped-features' && codexContent != null) {
176
+ try {
177
+ const ok = _fixCodexTrappedFeatures(codexContent);
178
+ if (ok) applied.push({ id: issue.id, fix: 'codex-trapped-features-repaired' });
179
+ else skipped.push({ id: issue.id, reason: 'no-change' });
180
+ } catch (err) {
181
+ skipped.push({ id: issue.id, reason: 'fix-failed', cause: err && err.message });
182
+ }
183
+ } else {
184
+ skipped.push({ id: issue.id, reason: 'no-auto-handler' });
185
+ }
186
+ continue;
187
+ }
188
+ if (issue.fixable === 'prompt') {
189
+ const answer = await askUser({
190
+ type: 'confirm',
191
+ question: `Issue ${issue.id} gefunden — reparieren?`,
192
+ default: true,
193
+ });
194
+ if (answer && answer.value) {
195
+ applied.push({ id: issue.id, fix: 'user-confirmed' });
196
+ } else {
197
+ skipped.push({ id: issue.id, reason: 'user-declined' });
198
+ }
199
+ continue;
200
+ }
201
+ if (issue.fixable === 'reinstall') {
202
+ try { stderr.write(`[doctor] ${issue.id}: Run \`npx nubos-pilot\` to reinstall.\n`); } catch {}
203
+ skipped.push({ id: issue.id, reason: 'requires-reinstall' });
204
+ continue;
205
+ }
206
+ skipped.push({ id: issue.id, reason: 'not-fixable' });
207
+ }
208
+ return { applied, skipped };
209
+ }
210
+
211
+ async function run(args, ctx) {
212
+ const context = ctx || {};
213
+ const cwd = context.cwd || process.cwd();
214
+ const stdout = context.stdout || process.stdout;
215
+ const stderr = context.stderr || process.stderr;
216
+ const askUser = typeof context.askUser === 'function'
217
+ ? context.askUser
218
+ : askuserMod.askUser;
219
+ const list = Array.isArray(args) ? args : [];
220
+ const doFix = list.includes('--fix');
221
+
222
+ const audit = _audit(cwd);
223
+ const payload = { issues: audit.issues };
224
+
225
+ if (doFix && audit.issues.length > 0) {
226
+ const { applied, skipped } = await _applyFixes(
227
+ audit.issues,
228
+ audit._codexContent,
229
+ askUser,
230
+ stderr,
231
+ );
232
+ payload.applied = applied;
233
+ payload.skipped = skipped;
234
+ }
235
+
236
+ try { stdout.write(JSON.stringify(payload)); } catch (err) {
237
+ throw new NubosPilotError('doctor-emit-failed', err && err.message, {});
238
+ }
239
+ return payload;
240
+ }
241
+
242
+ module.exports = { run };
@@ -0,0 +1,116 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+
4
+ const { NubosPilotError } = require('../../lib/core.cjs');
5
+ const { getPhase } = require('../../lib/roadmap.cjs');
6
+ const { paddedPhase, phaseSlug, findPhaseDir } = require('../../lib/phase.cjs');
7
+ const { detect } = require('../../lib/runtime/index.cjs');
8
+
9
+ function _validatePhaseArg(raw) {
10
+ if (raw == null || raw === '') {
11
+ throw new NubosPilotError(
12
+ 'eval-review-invalid-arg',
13
+ 'eval-review requires a phase number argument',
14
+ { value: raw == null ? '' : String(raw) },
15
+ );
16
+ }
17
+ const s = String(raw);
18
+ if (!/^\d+(\.\d+)?$/.test(s)) {
19
+ throw new NubosPilotError(
20
+ 'eval-review-invalid-arg',
21
+ 'Invalid phase number: ' + s,
22
+ { value: s },
23
+ );
24
+ }
25
+ return s;
26
+ }
27
+
28
+ function _resolvePhaseDir(phaseArg, cwd, slug) {
29
+ const hit = findPhaseDir(phaseArg, cwd);
30
+ if (hit) return hit;
31
+ const padded = paddedPhase(phaseArg);
32
+ return path.join(path.resolve(cwd), '.nubos-pilot', 'phases', padded + '-' + slug);
33
+ }
34
+
35
+ function _computeState(hasAiSpec, summaryPresent) {
36
+ if (hasAiSpec && summaryPresent) return 'A';
37
+ if (summaryPresent) return 'B';
38
+ return 'C';
39
+ }
40
+
41
+ function _buildPayload(phaseArg, cwd) {
42
+ let phase;
43
+ try {
44
+ phase = getPhase(phaseArg, cwd);
45
+ } catch (err) {
46
+ if (err && err.code === 'phase-not-found') {
47
+ throw new NubosPilotError(
48
+ 'eval-review-not-found',
49
+ 'Phase ' + phaseArg + ' not found in roadmap',
50
+ { number: phaseArg },
51
+ );
52
+ }
53
+ throw err;
54
+ }
55
+
56
+ const padded = paddedPhase(phaseArg);
57
+ const slug = phase.slug || phaseSlug(phase.name);
58
+ const phase_dir = _resolvePhaseDir(phaseArg, cwd, slug);
59
+ const eval_review_path = path.join(phase_dir, padded + '-EVAL-REVIEW.md');
60
+ const summary_path = path.join(phase_dir, padded + '-SUMMARY.md');
61
+ const ai_spec_path = path.join(phase_dir, padded + '-AI-SPEC.md');
62
+ const summary_present = fs.existsSync(summary_path);
63
+ const has_ai_spec = fs.existsSync(ai_spec_path);
64
+ const state = _computeState(has_ai_spec, summary_present);
65
+ const { runtime } = detect({ cwd });
66
+
67
+ return {
68
+ _workflow: 'eval-review',
69
+ phase: phaseArg,
70
+ padded,
71
+ phase_dir,
72
+ eval_review_path,
73
+ summary_present,
74
+ summary_path,
75
+ ai_spec_path,
76
+ has_ai_spec,
77
+ state,
78
+ agents: { eval_auditor: 'np-eval-auditor' },
79
+ runtime,
80
+ };
81
+ }
82
+
83
+ function _emitError(err, stderr) {
84
+ if (err && err.name === 'NubosPilotError') {
85
+ stderr.write(
86
+ JSON.stringify({ code: err.code, message: err.message, details: err.details }) + '\n',
87
+ );
88
+ } else {
89
+ stderr.write(String((err && err.stack) || err) + '\n');
90
+ }
91
+ }
92
+
93
+ function run(args, ctx) {
94
+ const context = ctx || {};
95
+ const cwd = context.cwd || process.cwd();
96
+ const stdout = context.stdout || process.stdout;
97
+ const stderr = context.stderr || process.stderr;
98
+ const list = Array.isArray(args) ? args : [];
99
+
100
+ if (list[0] == null || list[0] === '') {
101
+ stderr.write('Usage: np-tools.cjs init eval-review <phase>\n');
102
+ return 1;
103
+ }
104
+
105
+ try {
106
+ const phaseArg = _validatePhaseArg(list[0]);
107
+ const payload = _buildPayload(phaseArg, cwd);
108
+ stdout.write(JSON.stringify(payload, null, 2));
109
+ return 0;
110
+ } catch (err) {
111
+ _emitError(err, stderr);
112
+ return 1;
113
+ }
114
+ }
115
+
116
+ module.exports = { run, _buildPayload, _computeState };
@@ -0,0 +1,123 @@
1
+ const { test, afterEach } = require('node:test');
2
+ const assert = require('node:assert/strict');
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+
6
+ const { makeSandbox, seedRoadmapYaml, seedPhaseDir, cleanupAll } =
7
+ require('../../tests/helpers/fixture.cjs');
8
+ const subcmd = require('./eval-review.cjs');
9
+
10
+ function _baseRoadmap() {
11
+ return {
12
+ schema_version: 1,
13
+ milestones: [
14
+ {
15
+ id: 'v1.0',
16
+ name: 'first',
17
+ phases: [
18
+ {
19
+ number: 9,
20
+ name: 'Feature Set',
21
+ slug: 'feature-set',
22
+ goal: 'Ship advanced workflows',
23
+ depends_on: [],
24
+ requirements: ['R-04'],
25
+ success_criteria: ['Eval review produced'],
26
+ status: 'pending',
27
+ plans: [],
28
+ },
29
+ ],
30
+ },
31
+ ],
32
+ };
33
+ }
34
+
35
+ function _capture() {
36
+ let buf = '';
37
+ return { stub: { write: (s) => { buf += s; return true; } }, get: () => buf };
38
+ }
39
+
40
+ afterEach(cleanupAll);
41
+
42
+ test('EVR-1: State A — both AI-SPEC and SUMMARY present', () => {
43
+ const sandbox = makeSandbox();
44
+ seedRoadmapYaml(sandbox, _baseRoadmap());
45
+ seedPhaseDir(sandbox, 9, 'feature-set', {
46
+ '09-AI-SPEC.md': '# ai\n',
47
+ '09-SUMMARY.md': '# sum\n',
48
+ });
49
+ const cap = _capture();
50
+ subcmd.run(['9'], { cwd: sandbox, stdout: cap.stub });
51
+ const payload = JSON.parse(cap.get().trim());
52
+ assert.equal(payload.state, 'A');
53
+ assert.equal(payload.has_ai_spec, true);
54
+ assert.equal(payload.summary_present, true);
55
+ });
56
+
57
+ test('EVR-2: State B — only SUMMARY present', () => {
58
+ const sandbox = makeSandbox();
59
+ seedRoadmapYaml(sandbox, _baseRoadmap());
60
+ seedPhaseDir(sandbox, 9, 'feature-set', {
61
+ '09-SUMMARY.md': '# sum\n',
62
+ });
63
+ const cap = _capture();
64
+ subcmd.run(['9'], { cwd: sandbox, stdout: cap.stub });
65
+ const payload = JSON.parse(cap.get().trim());
66
+ assert.equal(payload.state, 'B');
67
+ assert.equal(payload.has_ai_spec, false);
68
+ assert.equal(payload.summary_present, true);
69
+ });
70
+
71
+ test('EVR-3: State C — neither file present', () => {
72
+ const sandbox = makeSandbox();
73
+ seedRoadmapYaml(sandbox, _baseRoadmap());
74
+ seedPhaseDir(sandbox, 9, 'feature-set', {});
75
+ const cap = _capture();
76
+ subcmd.run(['9'], { cwd: sandbox, stdout: cap.stub });
77
+ const payload = JSON.parse(cap.get().trim());
78
+ assert.equal(payload.state, 'C');
79
+ assert.equal(payload.has_ai_spec, false);
80
+ assert.equal(payload.summary_present, false);
81
+ });
82
+
83
+ test('EVR-4: payload declares eval_auditor agent + standard fields', () => {
84
+ const sandbox = makeSandbox();
85
+ seedRoadmapYaml(sandbox, _baseRoadmap());
86
+ const dir = seedPhaseDir(sandbox, 9, 'feature-set', {});
87
+ const cap = _capture();
88
+ subcmd.run(['9'], { cwd: sandbox, stdout: cap.stub });
89
+ const payload = JSON.parse(cap.get().trim());
90
+ assert.deepEqual(payload.agents, { eval_auditor: 'np-eval-auditor' });
91
+ assert.equal(payload.eval_review_path, path.join(dir, '09-EVAL-REVIEW.md'));
92
+ assert.equal(payload.summary_path, path.join(dir, '09-SUMMARY.md'));
93
+ assert.equal(payload.ai_spec_path, path.join(dir, '09-AI-SPEC.md'));
94
+ });
95
+
96
+ test('EVR-5: runtime populated via detect()', () => {
97
+ const sandbox = makeSandbox();
98
+ seedRoadmapYaml(sandbox, _baseRoadmap());
99
+ seedPhaseDir(sandbox, 9, 'feature-set', {});
100
+ fs.writeFileSync(
101
+ path.join(sandbox, '.nubos-pilot', 'config.json'),
102
+ JSON.stringify({ runtime: 'claude' }),
103
+ 'utf-8',
104
+ );
105
+ const cap = _capture();
106
+ subcmd.run(['9'], { cwd: sandbox, stdout: cap.stub });
107
+ const payload = JSON.parse(cap.get().trim());
108
+ assert.equal(payload.runtime, 'claude');
109
+ });
110
+
111
+ test('EVR-6: unknown phase → eval-review-not-found', () => {
112
+ const sandbox = makeSandbox();
113
+ seedRoadmapYaml(sandbox, _baseRoadmap());
114
+ const cap = _capture();
115
+ let errBuf = '';
116
+ const code = subcmd.run(['99'], {
117
+ cwd: sandbox, stdout: cap.stub,
118
+ stderr: { write: (s) => { errBuf += s; return true; } },
119
+ });
120
+ assert.equal(code, 1);
121
+ const parsed = JSON.parse(errBuf.trim());
122
+ assert.equal(parsed.code, 'eval-review-not-found');
123
+ });
@@ -0,0 +1,182 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const os = require('node:os');
4
+ const crypto = require('node:crypto');
5
+
6
+ const {
7
+ NubosPilotError,
8
+ projectStateDir,
9
+ } = require('../../lib/core.cjs');
10
+ const { getPhase } = require('../../lib/roadmap.cjs');
11
+ const { paddedPhase, phaseSlug, findPhaseDir } = require('../../lib/phase.cjs');
12
+ const { listPlans, parsePlan } = require('../../lib/plan.cjs');
13
+ const { loadTaskGraph, TASK_ID_RE } = require('../../lib/tasks.cjs');
14
+ const { extractFrontmatter } = require('../../lib/frontmatter.cjs');
15
+ const { getAgentSkills } = require('../../lib/agents.cjs');
16
+
17
+ const INLINE_THRESHOLD_BYTES = 16 * 1024;
18
+
19
+ function _validatePhaseArg(raw) {
20
+ if (raw == null || raw === '') {
21
+ throw new NubosPilotError(
22
+ 'execute-phase-invalid-phase-arg',
23
+ 'execute-phase requires a phase number',
24
+ { value: raw == null ? '' : String(raw) },
25
+ );
26
+ }
27
+ const s = String(raw);
28
+ if (!/^\d+(\.\d+)?$/.test(s)) {
29
+ throw new NubosPilotError(
30
+ 'execute-phase-invalid-phase-arg',
31
+ 'Invalid phase number: ' + s,
32
+ { value: s },
33
+ );
34
+ }
35
+ return s;
36
+ }
37
+
38
+ function _resolvePhaseDir(n, cwd, slug) {
39
+ const hit = findPhaseDir(n, cwd);
40
+ if (hit) return hit;
41
+ const padded = paddedPhase(n);
42
+ let stateDir;
43
+ try { stateDir = projectStateDir(cwd); } catch { stateDir = path.join(path.resolve(cwd), '.nubos-pilot'); }
44
+ return path.join(stateDir, 'phases', padded + '-' + slug);
45
+ }
46
+
47
+ function _safeSkills(name, cwd) {
48
+ try { return getAgentSkills(name, cwd); } catch { return []; }
49
+ }
50
+
51
+ function _emit(payload, stdout, cwd) {
52
+ const json = JSON.stringify(payload, null, 2);
53
+ if (Buffer.byteLength(json, 'utf-8') <= INLINE_THRESHOLD_BYTES) {
54
+ stdout.write(json);
55
+ return;
56
+ }
57
+ let tmpDir;
58
+ try {
59
+ tmpDir = path.join(projectStateDir(cwd), '.tmp');
60
+ fs.mkdirSync(tmpDir, { recursive: true });
61
+ } catch { tmpDir = os.tmpdir(); }
62
+ const suffix = process.pid + '-' + crypto.randomBytes(4).toString('hex');
63
+ const tmpPath = path.join(tmpDir, 'init-execute-phase-' + suffix + '.json');
64
+ fs.writeFileSync(tmpPath, json, 'utf-8');
65
+ stdout.write('@file:' + tmpPath);
66
+ }
67
+
68
+ function _initPayload(phaseArg, cwd) {
69
+ const phase = getPhase(phaseArg, cwd);
70
+ const padded = paddedPhase(phaseArg);
71
+ const slug = phase.slug || phaseSlug(phase.name);
72
+ const phase_dir = _resolvePhaseDir(phaseArg, cwd, slug);
73
+ const plans = [];
74
+ let planPaths = [];
75
+ try { planPaths = listPlans(phase_dir); } catch { planPaths = []; }
76
+ for (const planPath of planPaths) {
77
+ const parsed = parsePlan(planPath);
78
+ const planDir = path.dirname(planPath);
79
+ const tg = loadTaskGraph(planDir);
80
+ plans.push({
81
+ plan_path: planPath,
82
+ plan_frontmatter: parsed.frontmatter,
83
+ tasks_dir: path.join(planDir, 'tasks'),
84
+ task_count: tg.tasks.length,
85
+ waves: tg.waves,
86
+ warnings: tg.warnings,
87
+ });
88
+ }
89
+ return {
90
+ _workflow: 'execute-phase',
91
+ phase: phaseArg,
92
+ padded,
93
+ phase_dir,
94
+ phase_name: phase.name,
95
+ phase_slug: slug,
96
+ goal: phase.goal || '',
97
+ requirements: Array.isArray(phase.requirements) ? phase.requirements : [],
98
+ success_criteria: Array.isArray(phase.success_criteria) ? phase.success_criteria : [],
99
+ plans,
100
+ executor_tier: 'sonnet',
101
+ agent_skills: { executor: _safeSkills('np-executor', cwd) },
102
+ };
103
+ }
104
+
105
+ function _findTaskPayload(phaseArg, taskId, cwd) {
106
+ if (!TASK_ID_RE.test(taskId)) {
107
+ throw new NubosPilotError(
108
+ 'execute-phase-invalid-task-id',
109
+ 'Invalid task id: ' + taskId,
110
+ { taskId },
111
+ );
112
+ }
113
+ const phase = getPhase(phaseArg, cwd);
114
+ const slug = phase.slug || phaseSlug(phase.name);
115
+ const phase_dir = _resolvePhaseDir(phaseArg, cwd, slug);
116
+ const planPaths = listPlans(phase_dir);
117
+ for (const planPath of planPaths) {
118
+ const planDir = path.dirname(planPath);
119
+ const taskFile = path.join(planDir, 'tasks', taskId + '.md');
120
+ if (!fs.existsSync(taskFile)) continue;
121
+ const { frontmatter, body } = extractFrontmatter(fs.readFileSync(taskFile, 'utf-8'));
122
+ return {
123
+ _workflow: 'execute-phase',
124
+ verb: 'execute-task',
125
+ phase: phaseArg,
126
+ task_id: taskId,
127
+ task_file: taskFile,
128
+ plan_path: planPath,
129
+ files_modified: Array.isArray(frontmatter.files_modified) ? frontmatter.files_modified : [],
130
+ task_name: frontmatter.name || (String(body || '').match(/^#\s+(?:Task:\s*)?(.+?)\s*$/m) || [null, taskId])[1],
131
+ wave: frontmatter.wave,
132
+ tier: frontmatter.tier || 'sonnet',
133
+ depends_on: Array.isArray(frontmatter.depends_on) ? frontmatter.depends_on : [],
134
+ executor_tier: 'sonnet',
135
+ agent_skills: { executor: _safeSkills('np-executor', cwd) },
136
+ };
137
+ }
138
+ throw new NubosPilotError(
139
+ 'execute-phase-task-not-found',
140
+ 'Task ' + taskId + ' not found under phase ' + phaseArg,
141
+ { taskId, phase: phaseArg },
142
+ );
143
+ }
144
+
145
+ function run(args, ctx) {
146
+ const context = ctx || {};
147
+ const cwd = context.cwd || process.cwd();
148
+ const stdout = context.stdout || process.stdout;
149
+ const list = Array.isArray(args) ? args : [];
150
+ const verb = list[0];
151
+
152
+ switch (verb) {
153
+ case 'init': {
154
+ const phaseArg = _validatePhaseArg(list[1]);
155
+ const payload = _initPayload(phaseArg, cwd);
156
+ _emit(payload, stdout, cwd);
157
+ return payload;
158
+ }
159
+ case 'execute-task': {
160
+ const phaseArg = _validatePhaseArg(list[1]);
161
+ const taskId = list[2];
162
+ if (!taskId) {
163
+ throw new NubosPilotError(
164
+ 'execute-phase-missing-task-id',
165
+ 'execute-task requires <task-id>',
166
+ {},
167
+ );
168
+ }
169
+ const payload = _findTaskPayload(phaseArg, taskId, cwd);
170
+ _emit(payload, stdout, cwd);
171
+ return payload;
172
+ }
173
+ default:
174
+ throw new NubosPilotError(
175
+ 'execute-phase-unknown-verb',
176
+ 'execute-phase: unknown verb: ' + String(verb),
177
+ { verb },
178
+ );
179
+ }
180
+ }
181
+
182
+ module.exports = { run, INLINE_THRESHOLD_BYTES };