@njeybe/nativ 2.4.0 → 2.5.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 (140) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +26 -0
  3. package/dist/commands/db/db-formatter.d.ts +5 -0
  4. package/dist/commands/db/db-formatter.js +116 -0
  5. package/dist/commands/db/db-introspect.d.ts +11 -0
  6. package/dist/commands/db/db-introspect.js +84 -0
  7. package/dist/commands/db.js +4 -206
  8. package/dist/commands/profile.d.ts +3 -0
  9. package/dist/commands/profile.js +39 -0
  10. package/dist/commands/task/task-add.d.ts +11 -0
  11. package/dist/commands/task/task-add.js +169 -0
  12. package/dist/commands/task/task-common.d.ts +57 -0
  13. package/dist/commands/task/task-common.js +45 -0
  14. package/dist/commands/task/task-escalate.d.ts +7 -0
  15. package/dist/commands/task/task-escalate.js +119 -0
  16. package/dist/commands/task/task-lifecycle.d.ts +5 -0
  17. package/dist/commands/task/task-lifecycle.js +252 -0
  18. package/dist/commands/task/task-query.d.ts +10 -0
  19. package/dist/commands/task/task-query.js +269 -0
  20. package/dist/commands/task.d.ts +9 -87
  21. package/dist/commands/task.js +8 -850
  22. package/dist/commands/triage/triage-store.d.ts +56 -0
  23. package/dist/commands/triage/triage-store.js +108 -0
  24. package/dist/commands/triage/triage-ui.d.ts +14 -0
  25. package/dist/commands/triage/triage-ui.js +104 -0
  26. package/dist/commands/triage.d.ts +3 -52
  27. package/dist/commands/triage.js +8 -216
  28. package/dist/core/enforcement.d.ts +1 -0
  29. package/dist/core/enforcement.js +19 -0
  30. package/dist/core/human-gate.d.ts +2 -1
  31. package/dist/core/human-gate.js +9 -2
  32. package/dist/core/profiles.d.ts +14 -0
  33. package/dist/core/profiles.js +82 -0
  34. package/dist/core/setup/config-merger.d.ts +59 -0
  35. package/dist/core/setup/config-merger.js +107 -0
  36. package/dist/core/setup/session-context.d.ts +4 -0
  37. package/dist/core/setup/session-context.js +65 -0
  38. package/dist/core/setup/setup-planner.d.ts +14 -0
  39. package/dist/core/setup/setup-planner.js +180 -0
  40. package/dist/core/setup/template-planner.d.ts +8 -0
  41. package/dist/core/setup/template-planner.js +32 -0
  42. package/dist/core/setup/types.d.ts +65 -0
  43. package/dist/core/setup/types.js +55 -0
  44. package/dist/core/setup-assets.d.ts +8 -119
  45. package/dist/core/setup-assets.js +7 -437
  46. package/dist/core/spec-slices.js +119 -7
  47. package/dist/core/tier1-liaison.d.ts +2 -0
  48. package/dist/core/tier1-liaison.js +64 -3
  49. package/dist/core/verifier/executor.d.ts +5 -0
  50. package/dist/core/verifier/executor.js +62 -0
  51. package/dist/core/verifier/phases.d.ts +5 -0
  52. package/dist/core/verifier/phases.js +71 -0
  53. package/dist/core/verifier/task-verifier.d.ts +37 -0
  54. package/dist/core/verifier/task-verifier.js +233 -0
  55. package/dist/core/verifier/types.d.ts +51 -0
  56. package/dist/core/verifier/types.js +1 -0
  57. package/dist/core/verifier.d.ts +5 -93
  58. package/dist/core/verifier.js +5 -359
  59. package/dist/db/introspector.js +59 -59
  60. package/dist/governor/evaluator.d.ts +4 -64
  61. package/dist/governor/evaluator.js +21 -686
  62. package/dist/governor/patch-applier.d.ts +8 -0
  63. package/dist/governor/patch-applier.js +121 -0
  64. package/dist/governor/self-healing.d.ts +24 -0
  65. package/dist/governor/self-healing.js +351 -0
  66. package/dist/governor/test-integrity.d.ts +41 -0
  67. package/dist/governor/test-integrity.js +237 -0
  68. package/dist/index.js +8 -0
  69. package/dist/mcp/capture.d.ts +4 -0
  70. package/dist/mcp/capture.js +33 -0
  71. package/dist/mcp/resources.d.ts +39 -0
  72. package/dist/mcp/resources.js +44 -0
  73. package/dist/mcp/server.d.ts +11 -0
  74. package/dist/mcp/server.js +14 -89
  75. package/dist/mcp/tools.d.ts +1 -0
  76. package/dist/mcp/tools.js +1 -0
  77. package/dist/runner/agent-supervisor.d.ts +8 -302
  78. package/dist/runner/agent-supervisor.js +41 -1030
  79. package/dist/runner/cli-config.d.ts +84 -0
  80. package/dist/runner/cli-config.js +215 -0
  81. package/dist/runner/native-driver.d.ts +18 -0
  82. package/dist/runner/native-driver.js +208 -0
  83. package/dist/runner/native-engine.d.ts +27 -0
  84. package/dist/runner/native-engine.js +184 -0
  85. package/dist/runner/native-tools.d.ts +31 -0
  86. package/dist/runner/native-tools.js +241 -0
  87. package/dist/runner/process-utils.d.ts +12 -0
  88. package/dist/runner/process-utils.js +96 -0
  89. package/dist/runner/run-lifecycle.d.ts +6 -0
  90. package/dist/runner/run-lifecycle.js +42 -0
  91. package/dist/runner/types.d.ts +215 -0
  92. package/dist/runner/types.js +41 -0
  93. package/dist/scanner/detector.js +42 -663
  94. package/dist/scanner/detectors/ecosystem-detector.d.ts +16 -0
  95. package/dist/scanner/detectors/ecosystem-detector.js +354 -0
  96. package/dist/scanner/detectors/orm-detector.d.ts +5 -0
  97. package/dist/scanner/detectors/orm-detector.js +59 -0
  98. package/dist/scanner/detectors/workspace-detector.d.ts +19 -0
  99. package/dist/scanner/detectors/workspace-detector.js +266 -0
  100. package/dist/server/cli-runner.d.ts +15 -0
  101. package/dist/server/cli-runner.js +43 -0
  102. package/dist/server/event-hub.d.ts +42 -0
  103. package/dist/server/event-hub.js +169 -0
  104. package/dist/server/http-utils.d.ts +15 -0
  105. package/dist/server/http-utils.js +56 -0
  106. package/dist/server/plan-utils.d.ts +9 -0
  107. package/dist/server/plan-utils.js +31 -0
  108. package/dist/server/routes/db-routes.d.ts +57 -0
  109. package/dist/server/routes/db-routes.js +484 -0
  110. package/dist/server/routes/task-routes.d.ts +266 -0
  111. package/dist/server/routes/task-routes.js +285 -0
  112. package/dist/server/routes/telemetry-routes.d.ts +128 -0
  113. package/dist/server/routes/telemetry-routes.js +179 -0
  114. package/dist/server/routes/triage-routes.d.ts +81 -0
  115. package/dist/server/routes/triage-routes.js +244 -0
  116. package/dist/server/routes/worktree-routes.d.ts +31 -0
  117. package/dist/server/routes/worktree-routes.js +144 -0
  118. package/dist/server/studio-server.d.ts +4 -11
  119. package/dist/server/studio-server.js +34 -1578
  120. package/dist/server/studio-ui/shell/pipeline-state.js +2 -0
  121. package/dist/server/studio-ui/shell/triage.js +10 -0
  122. package/package.json +2 -2
  123. package/plugin/.claude-plugin/plugin.json +1 -1
  124. package/plugin/agents/architect.md +4 -2
  125. package/plugin/agents/verifier.md +1 -1
  126. package/plugin/agents/worker.md +7 -4
  127. package/plugin/skills/nativ/SKILL.md +1 -0
  128. package/templates/.claudeignore +62 -45
  129. package/templates/AGENTS.md +1 -0
  130. package/templates/CLAUDE.md +13 -0
  131. package/templates/claude-agents/architect.md +4 -2
  132. package/templates/claude-agents/verifier.md +1 -1
  133. package/templates/claude-agents/worker.md +7 -4
  134. package/templates/dot-ai/api_contracts.json +66 -66
  135. package/templates/dot-ai/context.md +38 -38
  136. package/templates/dot-ai/db_schema.json +39 -39
  137. package/dist/commands/session.d.ts +0 -22
  138. package/dist/commands/session.js +0 -106
  139. package/dist/core/session-memory.d.ts +0 -42
  140. package/dist/core/session-memory.js +0 -121
@@ -0,0 +1,144 @@
1
+ import { execFile } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import { CircuitBreaker } from '../../governor/index.js';
4
+ import { runWorktreeMerge, runWorktreeRemove } from '../../commands/worktree.js';
5
+ import { HttpError } from '../http-utils.js';
6
+ import { parseTaskId, planMilestones, readPlan } from '../plan-utils.js';
7
+ import { jsonErrorMessage, runCaptured, truncateOutput } from '../cli-runner.js';
8
+ export function git(cwd, args) {
9
+ return new Promise((resolve, reject) => {
10
+ execFile('git', args, { cwd, timeout: 10_000, windowsHide: true, maxBuffer: 1024 * 1024 }, (err, stdout) => err ? reject(err) : resolve(stdout));
11
+ });
12
+ }
13
+ /** `git diff --no-index` exits 1 when the inputs differ, so stdout is also read from that "error". */
14
+ export function gitNoIndexDiff(cwd, file) {
15
+ return new Promise((resolve) => {
16
+ execFile('git', ['diff', '--no-index', '--', '/dev/null', file], { cwd, timeout: 10_000, windowsHide: true, maxBuffer: 1024 * 1024 }, (err, stdout) => resolve(!err || err.code === 1 ? String(stdout) : ''));
17
+ });
18
+ }
19
+ /** Async twin of `nativ worktree list --json` (which blocks on execSync and prints to the console). */
20
+ export async function listGitWorktrees(root) {
21
+ let raw;
22
+ try {
23
+ raw = await git(root, ['worktree', 'list', '--porcelain']);
24
+ }
25
+ catch {
26
+ return []; // Not a git repository, or git is unavailable.
27
+ }
28
+ const worktrees = [];
29
+ for (const block of raw.trim().split(/\r?\n\r?\n/)) {
30
+ let wtPath = '';
31
+ let head = '';
32
+ let branch = '';
33
+ for (const line of block.split(/\r?\n/)) {
34
+ if (line.startsWith('worktree '))
35
+ wtPath = line.slice(9).trim();
36
+ else if (line.startsWith('HEAD '))
37
+ head = line.slice(5).trim();
38
+ else if (line.startsWith('branch '))
39
+ branch = line.slice(7).trim().replace(/^refs\/heads\//, '');
40
+ }
41
+ if (!wtPath)
42
+ continue;
43
+ worktrees.push({
44
+ path: wtPath,
45
+ head,
46
+ branch: branch || 'detached',
47
+ isAgentWorktree: wtPath.includes('.worktrees') || branch.startsWith('agent/task-'),
48
+ taskId: /^agent\/task-(.+)$/.exec(branch)?.[1],
49
+ });
50
+ }
51
+ return worktrees;
52
+ }
53
+ /**
54
+ * GET /api/pipeline/worktrees. Merge eligibility mirrors the `nativ worktree merge` gatekeeper (task completed,
55
+ * circuit breaker not tripped), and additionally requires the task to be declared in the plan.
56
+ */
57
+ export async function describeWorktrees(root) {
58
+ const statusById = new Map(planMilestones(readPlan(root)).flatMap((m) => m.tasks.map((t) => [t.id, t.status])));
59
+ return (await listGitWorktrees(root)).map((wt) => {
60
+ const taskStatus = (wt.taskId && statusById.get(wt.taskId)) || null;
61
+ let mergeBlockedReason = null;
62
+ if (!wt.isAgentWorktree || !wt.taskId)
63
+ mergeBlockedReason = 'Not an agent task worktree';
64
+ else if (!taskStatus)
65
+ mergeBlockedReason = `Task "${wt.taskId}" is not declared in .ai/master_plan.json`;
66
+ else if (taskStatus !== 'completed')
67
+ mergeBlockedReason = `Task status is '${taskStatus}'; only completed tasks may be merged`;
68
+ else if (CircuitBreaker.getStatus(root, wt.taskId).tripped)
69
+ mergeBlockedReason = 'Circuit breaker is tripped; resolve the escalation before merging';
70
+ return { ...wt, taskStatus, mergeEligible: mergeBlockedReason === null, mergeBlockedReason };
71
+ });
72
+ }
73
+ /** POST /api/pipeline/worktrees/action: `nativ worktree merge|remove`, never forced past the merge gatekeeper. */
74
+ export async function handleWorktreeAction(root, body) {
75
+ const { action } = body;
76
+ if (action !== 'merge' && action !== 'remove')
77
+ throw new HttpError(400, 'VALIDATION_ERROR', '"action" must be one of: merge, remove');
78
+ const taskId = parseTaskId(body.taskId);
79
+ const worktree = (await describeWorktrees(root)).find((wt) => wt.isAgentWorktree && wt.taskId === taskId);
80
+ if (!worktree)
81
+ throw new HttpError(400, 'WORKTREE_NOT_FOUND', `No agent worktree found for task "${taskId}"`);
82
+ if (action === 'merge' && !worktree.mergeEligible) {
83
+ throw new HttpError(400, 'MERGE_REJECTED', worktree.mergeBlockedReason ?? 'Merge rejected by the gatekeeper');
84
+ }
85
+ const { result, failed, output } = await runCaptured(() => action === 'merge' ? runWorktreeMerge(taskId, root, { json: true }) : runWorktreeRemove(taskId, root, { json: true }));
86
+ if (failed || !result) {
87
+ throw new HttpError(400, 'WORKTREE_ACTION_FAILED', jsonErrorMessage(output) ?? truncateOutput(output || `Worktree ${action} failed`));
88
+ }
89
+ return { ok: true, message: result.message };
90
+ }
91
+ /** GET /api/pipeline/worktrees/diff: uncommitted changes and unified diff for an agent worktree. */
92
+ export async function handleWorktreeDiff(root, searchParams) {
93
+ const rawTaskId = searchParams.get('taskId');
94
+ if (!rawTaskId)
95
+ throw new HttpError(400, 'VALIDATION_ERROR', '"taskId" query parameter is required');
96
+ const taskId = parseTaskId(rawTaskId);
97
+ const worktree = (await describeWorktrees(root)).find((wt) => wt.isAgentWorktree && wt.taskId === taskId);
98
+ if (!worktree || !fs.existsSync(worktree.path)) {
99
+ throw new HttpError(400, 'WORKTREE_NOT_FOUND', `No agent worktree found for task "${taskId}"`);
100
+ }
101
+ let statusOut;
102
+ try {
103
+ // -z keeps paths unquoted and unambiguous; -uall lists the files inside untracked directories.
104
+ statusOut = await git(worktree.path, ['status', '--porcelain', '-z', '--untracked-files=all']);
105
+ }
106
+ catch {
107
+ throw new HttpError(400, 'WORKTREE_INVALID', `Failed to read git status in the worktree for task "${taskId}"`);
108
+ }
109
+ const filesChanged = [];
110
+ const untracked = [];
111
+ const entries = statusOut.split('\0');
112
+ for (let i = 0; i < entries.length; i++) {
113
+ const entry = entries[i];
114
+ if (entry.length < 4)
115
+ continue;
116
+ const xy = entry.slice(0, 2);
117
+ const file = entry.slice(3);
118
+ filesChanged.push(file);
119
+ if (xy === '??')
120
+ untracked.push(file);
121
+ // Renames and copies carry the original path in the next field.
122
+ if (xy[0] === 'R' || xy[0] === 'C')
123
+ i++;
124
+ }
125
+ let diff = '';
126
+ try {
127
+ diff = await git(worktree.path, ['diff', 'HEAD']);
128
+ }
129
+ catch {
130
+ diff = await git(worktree.path, ['diff']).catch(() => '');
131
+ }
132
+ // `git diff HEAD` skips untracked files; render them as additions without touching the index.
133
+ for (const file of untracked) {
134
+ diff += (await gitNoIndexDiff(worktree.path, file)) || `+++ b/${file} (untracked)\n`;
135
+ }
136
+ return {
137
+ ok: true,
138
+ taskId,
139
+ branch: worktree.branch,
140
+ hasChanges: filesChanged.length > 0,
141
+ filesChanged,
142
+ diff: diff.trim(),
143
+ };
144
+ }
@@ -1,15 +1,11 @@
1
1
  import http from 'node:http';
2
- import type { DatabaseEnv, DatabaseStatus, TableSchema } from '../db/types.js';
2
+ import type { DatabaseEnv } from '../db/types.js';
3
3
  import { type Tier1LiaisonOptions } from '../core/tier1-liaison.js';
4
4
  import { type AgentSupervisorOptions } from '../runner/agent-supervisor.js';
5
- /**
6
- * Embedded Studio HTTP server (native node:http).
7
- * Serves the REST endpoints from .ai/api_contracts.json plus the dashboard page.
8
- *
9
- * Air-gap rules: binds to loopback only, keeps raw connection URLs in this process's
10
- * memory, and only ever returns masked URLs in responses.
11
- */
12
5
  export declare const DEFAULT_STUDIO_PORT = 4983;
6
+ export { DEFAULT_HEARTBEAT_MS, PipelineEventHub } from './event-hub.js';
7
+ export { writeContractSchema } from './routes/db-routes.js';
8
+ export { HttpError } from './http-utils.js';
13
9
  export interface StudioServerOptions {
14
10
  port?: number;
15
11
  host?: string;
@@ -31,9 +27,6 @@ export interface StudioServerHandle {
31
27
  port: number;
32
28
  close(): Promise<void>;
33
29
  }
34
- /** Writes structure-only table metadata into .ai/db_schema.json, preserving other contract fields. */
35
- export declare function writeContractSchema(cwd: string, status: DatabaseStatus, tables: TableSchema[]): string;
36
- export declare const DEFAULT_HEARTBEAT_MS = 15000;
37
30
  export declare function createStudioServer(options?: StudioServerOptions): http.Server;
38
31
  /** Starts the studio on loopback, trying up to 10 consecutive ports if the preferred one is busy. */
39
32
  export declare function startStudioServer(options?: StudioServerOptions): Promise<StudioServerHandle>;