@nathapp/nax 0.38.0 → 0.38.2
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/dist/nax.js +3294 -2907
- package/package.json +2 -2
- package/src/agents/claude-complete.ts +72 -0
- package/src/agents/claude-execution.ts +189 -0
- package/src/agents/claude-interactive.ts +77 -0
- package/src/agents/claude-plan.ts +23 -8
- package/src/agents/claude.ts +64 -349
- package/src/analyze/classifier.ts +2 -1
- package/src/cli/config-descriptions.ts +206 -0
- package/src/cli/config-diff.ts +103 -0
- package/src/cli/config-display.ts +285 -0
- package/src/cli/config-get.ts +55 -0
- package/src/cli/config.ts +7 -618
- package/src/cli/plugins.ts +15 -4
- package/src/cli/prompts-export.ts +58 -0
- package/src/cli/prompts-init.ts +200 -0
- package/src/cli/prompts-main.ts +237 -0
- package/src/cli/prompts-tdd.ts +78 -0
- package/src/cli/prompts.ts +10 -541
- package/src/commands/logs-formatter.ts +201 -0
- package/src/commands/logs-reader.ts +171 -0
- package/src/commands/logs.ts +11 -362
- package/src/config/loader.ts +4 -15
- package/src/config/runtime-types.ts +451 -0
- package/src/config/schema-types.ts +53 -0
- package/src/config/schemas.ts +2 -0
- package/src/config/types.ts +49 -486
- package/src/context/auto-detect.ts +2 -1
- package/src/context/builder.ts +3 -2
- package/src/execution/crash-heartbeat.ts +77 -0
- package/src/execution/crash-recovery.ts +23 -365
- package/src/execution/crash-signals.ts +149 -0
- package/src/execution/crash-writer.ts +154 -0
- package/src/execution/lifecycle/run-setup.ts +7 -1
- package/src/execution/parallel-coordinator.ts +278 -0
- package/src/execution/parallel-executor-rectification-pass.ts +117 -0
- package/src/execution/parallel-executor-rectify.ts +135 -0
- package/src/execution/parallel-executor.ts +19 -211
- package/src/execution/parallel-worker.ts +148 -0
- package/src/execution/parallel.ts +5 -404
- package/src/execution/pid-registry.ts +3 -8
- package/src/execution/runner-completion.ts +160 -0
- package/src/execution/runner-execution.ts +221 -0
- package/src/execution/runner-setup.ts +82 -0
- package/src/execution/runner.ts +53 -202
- package/src/execution/timeout-handler.ts +100 -0
- package/src/hooks/runner.ts +11 -21
- package/src/metrics/tracker.ts +7 -30
- package/src/pipeline/runner.ts +2 -1
- package/src/pipeline/stages/completion.ts +0 -1
- package/src/pipeline/stages/context.ts +2 -1
- package/src/plugins/extensions.ts +225 -0
- package/src/plugins/loader.ts +40 -4
- package/src/plugins/types.ts +18 -221
- package/src/prd/index.ts +2 -1
- package/src/prd/validate.ts +41 -0
- package/src/precheck/checks-blockers.ts +15 -419
- package/src/precheck/checks-cli.ts +68 -0
- package/src/precheck/checks-config.ts +102 -0
- package/src/precheck/checks-git.ts +87 -0
- package/src/precheck/checks-system.ts +163 -0
- package/src/review/orchestrator.ts +19 -6
- package/src/review/runner.ts +17 -5
- package/src/routing/chain.ts +2 -1
- package/src/routing/loader.ts +2 -5
- package/src/tdd/orchestrator.ts +2 -1
- package/src/tdd/verdict-reader.ts +266 -0
- package/src/tdd/verdict.ts +6 -271
- package/src/utils/errors.ts +12 -0
- package/src/utils/git.ts +12 -5
- package/src/utils/json-file.ts +72 -0
- package/src/verification/executor.ts +2 -1
- package/src/verification/smart-runner.ts +23 -3
- package/src/worktree/manager.ts +9 -3
- package/src/worktree/merge.ts +3 -2
package/src/plugins/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plugin System Types
|
|
2
|
+
* Plugin System Types — Hub file
|
|
3
3
|
*
|
|
4
4
|
* Defines the plugin interface and extension point types for the nax plugin system.
|
|
5
5
|
* Plugins export a NaxPlugin object with extension implementations.
|
|
@@ -7,8 +7,21 @@
|
|
|
7
7
|
|
|
8
8
|
import type { AgentAdapter } from "../agents/types";
|
|
9
9
|
import type { IPromptOptimizer } from "../optimizer/types";
|
|
10
|
-
import type { UserStory } from "../prd/types";
|
|
11
10
|
import type { RoutingStrategy } from "../routing/strategy";
|
|
11
|
+
import type { IContextProvider, IReporter, IReviewPlugin } from "./extensions";
|
|
12
|
+
|
|
13
|
+
// Re-export extension types
|
|
14
|
+
export type {
|
|
15
|
+
ContextProviderResult,
|
|
16
|
+
IContextProvider,
|
|
17
|
+
IReporter,
|
|
18
|
+
IReviewPlugin,
|
|
19
|
+
ReviewCheckResult,
|
|
20
|
+
ReviewFinding,
|
|
21
|
+
RunEndEvent,
|
|
22
|
+
RunStartEvent,
|
|
23
|
+
StoryCompleteEvent,
|
|
24
|
+
} from "./extensions";
|
|
12
25
|
|
|
13
26
|
/**
|
|
14
27
|
* Extension point types that plugins can provide.
|
|
@@ -111,228 +124,10 @@ export interface PluginExtensions {
|
|
|
111
124
|
* Plugin optimizers use the same interface as built-in optimizers.
|
|
112
125
|
*/
|
|
113
126
|
export type {
|
|
127
|
+
IPromptOptimizer,
|
|
114
128
|
PromptOptimizerInput,
|
|
115
129
|
PromptOptimizerResult,
|
|
116
130
|
} from "../optimizer/types";
|
|
117
|
-
export type { IPromptOptimizer } from "../optimizer/types";
|
|
118
|
-
|
|
119
|
-
// ============================================================================
|
|
120
|
-
// Review Extension
|
|
121
|
-
// ============================================================================
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* A single structured finding from a review check.
|
|
125
|
-
*
|
|
126
|
-
* Designed to be service-agnostic — works with Semgrep, ESLint, SonarQube,
|
|
127
|
-
* Snyk, CodeQL, and other SAST/DAST/linting tools.
|
|
128
|
-
*/
|
|
129
|
-
export interface ReviewFinding {
|
|
130
|
-
/** Rule or check ID (e.g., "detect-non-literal-regexp", "no-unused-vars") */
|
|
131
|
-
ruleId: string;
|
|
132
|
-
/** Severity level (tool-agnostic scale) */
|
|
133
|
-
severity: "critical" | "error" | "warning" | "info" | "low";
|
|
134
|
-
/** File path (relative to workdir) */
|
|
135
|
-
file: string;
|
|
136
|
-
/** Line number (1-indexed) */
|
|
137
|
-
line: number;
|
|
138
|
-
/** Column number (1-indexed, optional) */
|
|
139
|
-
column?: number;
|
|
140
|
-
/** End line number (optional, for multi-line findings) */
|
|
141
|
-
endLine?: number;
|
|
142
|
-
/** End column number (optional) */
|
|
143
|
-
endColumn?: number;
|
|
144
|
-
/** Human-readable message */
|
|
145
|
-
message: string;
|
|
146
|
-
/** Optional URL for rule documentation or details */
|
|
147
|
-
url?: string;
|
|
148
|
-
/** Source tool that produced this finding (e.g., "semgrep", "eslint", "snyk") */
|
|
149
|
-
source?: string;
|
|
150
|
-
/** Finding category (e.g., "security", "performance", "style", "bug") */
|
|
151
|
-
category?: string;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Result from a review check.
|
|
156
|
-
*/
|
|
157
|
-
export interface ReviewCheckResult {
|
|
158
|
-
/** Whether the review check passed */
|
|
159
|
-
passed: boolean;
|
|
160
|
-
/** Human-readable output or error messages */
|
|
161
|
-
output: string;
|
|
162
|
-
/** Exit code from the check process (if applicable) */
|
|
163
|
-
exitCode?: number;
|
|
164
|
-
/** Structured findings (optional — plugins can provide machine-readable results) */
|
|
165
|
-
findings?: ReviewFinding[];
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Review plugin interface.
|
|
170
|
-
*
|
|
171
|
-
* Review plugins run custom checks after agent execution (e.g., security scans,
|
|
172
|
-
* license checks, performance tests). Failures trigger retry/escalation.
|
|
173
|
-
*
|
|
174
|
-
* @example
|
|
175
|
-
* ```ts
|
|
176
|
-
* const reviewer: IReviewPlugin = {
|
|
177
|
-
* name: "security-scan",
|
|
178
|
-
* description: "Scans for security vulnerabilities",
|
|
179
|
-
* async check(workdir, changedFiles) {
|
|
180
|
-
* const result = await securityScanner.scan(workdir, changedFiles);
|
|
181
|
-
* return {
|
|
182
|
-
* passed: result.vulnerabilities.length === 0,
|
|
183
|
-
* output: result.report
|
|
184
|
-
* };
|
|
185
|
-
* }
|
|
186
|
-
* };
|
|
187
|
-
* ```
|
|
188
|
-
*/
|
|
189
|
-
export interface IReviewPlugin {
|
|
190
|
-
/** Check name (e.g., "security-scan", "license-check") */
|
|
191
|
-
name: string;
|
|
192
|
-
|
|
193
|
-
/** Human-readable description */
|
|
194
|
-
description: string;
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Run the review check against the working directory.
|
|
198
|
-
*
|
|
199
|
-
* @param workdir - Project root directory
|
|
200
|
-
* @param changedFiles - Files modified by the agent in this story
|
|
201
|
-
* @returns Review check result
|
|
202
|
-
*/
|
|
203
|
-
check(workdir: string, changedFiles: string[]): Promise<ReviewCheckResult>;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// ============================================================================
|
|
207
|
-
// Context Provider Extension
|
|
208
|
-
// ============================================================================
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Result from a context provider.
|
|
212
|
-
*/
|
|
213
|
-
export interface ContextProviderResult {
|
|
214
|
-
/** Markdown content to inject */
|
|
215
|
-
content: string;
|
|
216
|
-
/** Token estimate for budget tracking */
|
|
217
|
-
estimatedTokens: number;
|
|
218
|
-
/** Section label in the prompt (e.g., "Jira Context") */
|
|
219
|
-
label: string;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Context provider interface.
|
|
224
|
-
*
|
|
225
|
-
* Context providers fetch external data (Jira tickets, Confluence docs,
|
|
226
|
-
* Linear issues, etc.) and inject it into agent prompts.
|
|
227
|
-
*
|
|
228
|
-
* @example
|
|
229
|
-
* ```ts
|
|
230
|
-
* const provider: IContextProvider = {
|
|
231
|
-
* name: "jira",
|
|
232
|
-
* async getContext(story) {
|
|
233
|
-
* const ticket = await jiraApi.getTicket(story.tags[0]);
|
|
234
|
-
* return {
|
|
235
|
-
* content: `## ${ticket.key}\n\n${ticket.description}`,
|
|
236
|
-
* estimatedTokens: estimateTokens(ticket.description),
|
|
237
|
-
* label: "Jira Context"
|
|
238
|
-
* };
|
|
239
|
-
* }
|
|
240
|
-
* };
|
|
241
|
-
* ```
|
|
242
|
-
*/
|
|
243
|
-
export interface IContextProvider {
|
|
244
|
-
/** Provider name (e.g., "jira", "linear", "confluence") */
|
|
245
|
-
name: string;
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Fetch external context relevant to a story.
|
|
249
|
-
*
|
|
250
|
-
* @param story - The user story being executed
|
|
251
|
-
* @returns Markdown content to inject into the agent prompt
|
|
252
|
-
*/
|
|
253
|
-
getContext(story: UserStory): Promise<ContextProviderResult>;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// ============================================================================
|
|
257
|
-
// Reporter Extension
|
|
258
|
-
// ============================================================================
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Event emitted when a run starts.
|
|
262
|
-
*/
|
|
263
|
-
export interface RunStartEvent {
|
|
264
|
-
runId: string;
|
|
265
|
-
feature: string;
|
|
266
|
-
totalStories: number;
|
|
267
|
-
startTime: string;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Event emitted when a story completes.
|
|
272
|
-
*/
|
|
273
|
-
export interface StoryCompleteEvent {
|
|
274
|
-
runId: string;
|
|
275
|
-
storyId: string;
|
|
276
|
-
status: "completed" | "failed" | "skipped" | "paused";
|
|
277
|
-
runElapsedMs: number;
|
|
278
|
-
cost: number;
|
|
279
|
-
tier: string;
|
|
280
|
-
testStrategy: string;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Event emitted when a run ends.
|
|
285
|
-
*/
|
|
286
|
-
export interface RunEndEvent {
|
|
287
|
-
runId: string;
|
|
288
|
-
totalDurationMs: number;
|
|
289
|
-
totalCost: number;
|
|
290
|
-
storySummary: {
|
|
291
|
-
completed: number;
|
|
292
|
-
failed: number;
|
|
293
|
-
skipped: number;
|
|
294
|
-
paused: number;
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Reporter interface.
|
|
300
|
-
*
|
|
301
|
-
* Reporters receive run lifecycle events and can emit them to external
|
|
302
|
-
* systems (dashboards, Slack, CI, databases, etc.).
|
|
303
|
-
*
|
|
304
|
-
* All reporter methods are fire-and-forget — failures are logged but
|
|
305
|
-
* never block the pipeline.
|
|
306
|
-
*
|
|
307
|
-
* @example
|
|
308
|
-
* ```ts
|
|
309
|
-
* const reporter: IReporter = {
|
|
310
|
-
* name: "telegram",
|
|
311
|
-
* async onRunStart(event) {
|
|
312
|
-
* await telegram.send(`Started ${event.feature}`);
|
|
313
|
-
* },
|
|
314
|
-
* async onStoryComplete(event) {
|
|
315
|
-
* await telegram.send(`${event.storyId} ${event.status}`);
|
|
316
|
-
* },
|
|
317
|
-
* async onRunEnd(event) {
|
|
318
|
-
* await telegram.send(`Completed ${event.storySummary.completed} stories`);
|
|
319
|
-
* }
|
|
320
|
-
* };
|
|
321
|
-
* ```
|
|
322
|
-
*/
|
|
323
|
-
export interface IReporter {
|
|
324
|
-
/** Reporter name */
|
|
325
|
-
name: string;
|
|
326
|
-
|
|
327
|
-
/** Called when a run starts */
|
|
328
|
-
onRunStart?(event: RunStartEvent): Promise<void>;
|
|
329
|
-
|
|
330
|
-
/** Called when a story completes (success or failure) */
|
|
331
|
-
onStoryComplete?(event: StoryCompleteEvent): Promise<void>;
|
|
332
|
-
|
|
333
|
-
/** Called when a run ends */
|
|
334
|
-
onRunEnd?(event: RunEndEvent): Promise<void>;
|
|
335
|
-
}
|
|
336
131
|
|
|
337
132
|
// ============================================================================
|
|
338
133
|
// Plugin Logger
|
|
@@ -406,4 +201,6 @@ export interface PluginConfigEntry {
|
|
|
406
201
|
module: string;
|
|
407
202
|
/** Plugin-specific configuration */
|
|
408
203
|
config?: Record<string, unknown>;
|
|
204
|
+
/** Whether this plugin is enabled (default: true) */
|
|
205
|
+
enabled?: boolean;
|
|
409
206
|
}
|
package/src/prd/index.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { existsSync, statSync } from "node:fs";
|
|
6
6
|
import type { FailureCategory } from "../tdd/types";
|
|
7
|
+
import { saveJsonFile } from "../utils/json-file";
|
|
7
8
|
import type { PRD, UserStory } from "./types";
|
|
8
9
|
|
|
9
10
|
export type {
|
|
@@ -64,7 +65,7 @@ export async function loadPRD(path: string): Promise<PRD> {
|
|
|
64
65
|
/** Save PRD to file */
|
|
65
66
|
export async function savePRD(prd: PRD, path: string): Promise<void> {
|
|
66
67
|
prd.updatedAt = new Date().toISOString();
|
|
67
|
-
await
|
|
68
|
+
await saveJsonFile(path, prd, "prd");
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
/**
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story ID validation
|
|
3
|
+
*
|
|
4
|
+
* Validates story IDs before they're used in git operations (branch names, worktree paths).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validates a story ID for use in git operations.
|
|
9
|
+
*
|
|
10
|
+
* Rejects:
|
|
11
|
+
* - Empty strings
|
|
12
|
+
* - Path traversal attempts (../)
|
|
13
|
+
* - Git flags starting with --
|
|
14
|
+
* - Invalid characters (only allow alphanumeric, dots, hyphens, underscores)
|
|
15
|
+
*
|
|
16
|
+
* Valid pattern: /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/
|
|
17
|
+
* - Starts with alphanumeric
|
|
18
|
+
* - Contains only alphanumeric, dot, underscore, hyphen
|
|
19
|
+
* - Max 64 characters
|
|
20
|
+
*/
|
|
21
|
+
export function validateStoryId(id: string): void {
|
|
22
|
+
if (!id || id.length === 0) {
|
|
23
|
+
throw new Error("Story ID cannot be empty");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Reject path traversal
|
|
27
|
+
if (id.includes("..")) {
|
|
28
|
+
throw new Error("Story ID cannot contain path traversal (..)");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Reject git flags
|
|
32
|
+
if (id.startsWith("--")) {
|
|
33
|
+
throw new Error("Story ID cannot start with git flags (--)");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Reject invalid characters - must match pattern
|
|
37
|
+
const validPattern = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
|
|
38
|
+
if (!validPattern.test(id)) {
|
|
39
|
+
throw new Error(`Story ID must match pattern [a-zA-Z0-9][a-zA-Z0-9._-]{0,63}. Got: ${id}`);
|
|
40
|
+
}
|
|
41
|
+
}
|