@nathapp/nax 0.32.2 → 0.34.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 (48) hide show
  1. package/README.md +191 -6
  2. package/dist/nax.js +1150 -382
  3. package/package.json +1 -1
  4. package/src/cli/analyze.ts +145 -0
  5. package/src/cli/config.ts +9 -0
  6. package/src/config/defaults.ts +8 -0
  7. package/src/config/schema.ts +1 -0
  8. package/src/config/schemas.ts +10 -0
  9. package/src/config/types.ts +18 -0
  10. package/src/context/elements.ts +13 -0
  11. package/src/context/greenfield.ts +1 -1
  12. package/src/decompose/apply.ts +44 -0
  13. package/src/decompose/builder.ts +181 -0
  14. package/src/decompose/index.ts +8 -0
  15. package/src/decompose/sections/codebase.ts +26 -0
  16. package/src/decompose/sections/constraints.ts +32 -0
  17. package/src/decompose/sections/index.ts +4 -0
  18. package/src/decompose/sections/sibling-stories.ts +25 -0
  19. package/src/decompose/sections/target-story.ts +31 -0
  20. package/src/decompose/types.ts +55 -0
  21. package/src/decompose/validators/complexity.ts +45 -0
  22. package/src/decompose/validators/coverage.ts +134 -0
  23. package/src/decompose/validators/dependency.ts +91 -0
  24. package/src/decompose/validators/index.ts +35 -0
  25. package/src/decompose/validators/overlap.ts +128 -0
  26. package/src/execution/crash-recovery.ts +8 -0
  27. package/src/execution/escalation/tier-escalation.ts +9 -2
  28. package/src/execution/iteration-runner.ts +2 -0
  29. package/src/execution/lifecycle/run-completion.ts +100 -15
  30. package/src/execution/parallel-executor.ts +20 -1
  31. package/src/execution/pipeline-result-handler.ts +5 -1
  32. package/src/execution/runner.ts +20 -0
  33. package/src/execution/sequential-executor.ts +2 -11
  34. package/src/hooks/types.ts +20 -10
  35. package/src/interaction/index.ts +1 -0
  36. package/src/interaction/triggers.ts +21 -0
  37. package/src/interaction/types.ts +7 -0
  38. package/src/metrics/tracker.ts +7 -0
  39. package/src/metrics/types.ts +2 -0
  40. package/src/pipeline/stages/review.ts +6 -0
  41. package/src/pipeline/stages/routing.ts +89 -0
  42. package/src/pipeline/types.ts +2 -0
  43. package/src/plugins/types.ts +33 -0
  44. package/src/prd/index.ts +7 -2
  45. package/src/prd/types.ts +17 -2
  46. package/src/review/orchestrator.ts +1 -0
  47. package/src/review/types.ts +2 -0
  48. package/src/tdd/isolation.ts +1 -1
package/src/prd/index.ts CHANGED
@@ -107,6 +107,7 @@ export function getNextStory(prd: PRD, currentStoryId?: string | null, maxRetrie
107
107
  s.status !== "blocked" &&
108
108
  s.status !== "failed" &&
109
109
  s.status !== "paused" &&
110
+ s.status !== "decomposed" &&
110
111
  s.dependencies.every((dep) => completedIds.has(dep)),
111
112
  ) ?? null
112
113
  );
@@ -132,11 +133,12 @@ export function countStories(prd: PRD): {
132
133
  skipped: number;
133
134
  blocked: number;
134
135
  paused: number;
136
+ decomposed: number;
135
137
  } {
136
138
  return {
137
139
  total: prd.userStories.length,
138
140
  passed: prd.userStories.filter((s) => s.passes || s.status === "passed").length,
139
- failed: prd.userStories.filter((s) => s.status === "failed").length,
141
+ failed: prd.userStories.filter((s) => s.status === "failed" || s.status === "regression-failed").length,
140
142
  pending: prd.userStories.filter(
141
143
  (s) =>
142
144
  !s.passes &&
@@ -144,11 +146,14 @@ export function countStories(prd: PRD): {
144
146
  s.status !== "failed" &&
145
147
  s.status !== "skipped" &&
146
148
  s.status !== "blocked" &&
147
- s.status !== "paused",
149
+ s.status !== "paused" &&
150
+ s.status !== "regression-failed" &&
151
+ s.status !== "decomposed",
148
152
  ).length,
149
153
  skipped: prd.userStories.filter((s) => s.status === "skipped").length,
150
154
  blocked: prd.userStories.filter((s) => s.status === "blocked").length,
151
155
  paused: prd.userStories.filter((s) => s.status === "paused").length,
156
+ decomposed: prd.userStories.filter((s) => s.status === "decomposed").length,
152
157
  };
153
158
  }
154
159
 
package/src/prd/types.ts CHANGED
@@ -9,7 +9,16 @@ import type { ModelTier } from "../config";
9
9
  import type { FailureCategory } from "../tdd/types";
10
10
 
11
11
  /** User story status */
12
- export type StoryStatus = "pending" | "in-progress" | "passed" | "failed" | "skipped" | "blocked" | "paused";
12
+ export type StoryStatus =
13
+ | "pending"
14
+ | "in-progress"
15
+ | "passed"
16
+ | "failed"
17
+ | "skipped"
18
+ | "blocked"
19
+ | "paused"
20
+ | "regression-failed"
21
+ | "decomposed";
13
22
 
14
23
  /** Verification stage where failure occurred */
15
24
  export type VerificationStage = "verify" | "review" | "regression" | "rectification" | "agent-session" | "escalation";
@@ -38,6 +47,8 @@ export interface StructuredFailure {
38
47
  summary: string;
39
48
  /** Parsed test failures (if applicable) */
40
49
  testFailures?: TestFailureContext[];
50
+ /** Structured review findings from plugin reviewers (e.g., semgrep, eslint) */
51
+ reviewFindings?: import("../plugins/types").ReviewFinding[];
41
52
  /** ISO timestamp when failure was recorded */
42
53
  timestamp: string;
43
54
  }
@@ -150,7 +161,10 @@ export function isStalled(prd: PRD): boolean {
150
161
 
151
162
  const blockedIds = new Set(
152
163
  prd.userStories
153
- .filter((s) => s.status === "blocked" || s.status === "failed" || s.status === "paused")
164
+ .filter(
165
+ (s) =>
166
+ s.status === "blocked" || s.status === "failed" || s.status === "paused" || s.status === "regression-failed",
167
+ )
154
168
  .map((s) => s.id),
155
169
  );
156
170
 
@@ -159,6 +173,7 @@ export function isStalled(prd: PRD): boolean {
159
173
  s.status === "blocked" ||
160
174
  s.status === "failed" ||
161
175
  s.status === "paused" ||
176
+ s.status === "regression-failed" ||
162
177
  s.dependencies.some((dep) => blockedIds.has(dep)),
163
178
  );
164
179
  }
@@ -72,6 +72,7 @@ export class ReviewOrchestrator {
72
72
  passed: result.passed,
73
73
  output: result.output,
74
74
  exitCode: result.exitCode,
75
+ findings: result.findings,
75
76
  });
76
77
  if (!result.passed) {
77
78
  builtIn.pluginReviewers = pluginResults;
@@ -35,6 +35,8 @@ export interface PluginReviewerResult {
35
35
  exitCode?: number;
36
36
  /** Error message if reviewer threw an exception */
37
37
  error?: string;
38
+ /** Structured findings from the reviewer (optional) */
39
+ findings?: import("../plugins/types").ReviewFinding[];
38
40
  }
39
41
 
40
42
  /** Review phase result */
@@ -55,7 +55,7 @@ function matchesAllowedPath(filePath: string, allowedPaths: string[]): boolean {
55
55
  return allowedPaths.some((pattern) => {
56
56
  // Simple glob matching: ** = any directory, * = any filename segment
57
57
  const regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\//g, "\\/");
58
- const regex = new RegExp(`^${regexPattern}$`);
58
+ const regex = new RegExp(`^${regexPattern}$`); // nosemgrep: detect-non-literal-regexp — pattern from PRD scope config, not user input
59
59
  return regex.test(filePath);
60
60
  });
61
61
  }