@njeybe/nativ 2.0.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 +40 -0
  2. package/LICENSE +21 -0
  3. package/README.md +334 -0
  4. package/bin/cli.js +31 -0
  5. package/dist/commands/bench.d.ts +8 -0
  6. package/dist/commands/bench.js +52 -0
  7. package/dist/commands/db.d.ts +22 -0
  8. package/dist/commands/db.js +415 -0
  9. package/dist/commands/doctor.d.ts +38 -0
  10. package/dist/commands/doctor.js +169 -0
  11. package/dist/commands/hook.d.ts +31 -0
  12. package/dist/commands/hook.js +105 -0
  13. package/dist/commands/init.d.ts +3 -0
  14. package/dist/commands/init.js +144 -0
  15. package/dist/commands/mcp.d.ts +5 -0
  16. package/dist/commands/mcp.js +16 -0
  17. package/dist/commands/setup.d.ts +17 -0
  18. package/dist/commands/setup.js +51 -0
  19. package/dist/commands/status.d.ts +5 -0
  20. package/dist/commands/status.js +75 -0
  21. package/dist/commands/task.d.ts +83 -0
  22. package/dist/commands/task.js +807 -0
  23. package/dist/commands/test-gen.d.ts +14 -0
  24. package/dist/commands/test-gen.js +52 -0
  25. package/dist/commands/triage.d.ts +70 -0
  26. package/dist/commands/triage.js +343 -0
  27. package/dist/commands/update.d.ts +14 -0
  28. package/dist/commands/update.js +112 -0
  29. package/dist/commands/validate.d.ts +1 -0
  30. package/dist/commands/validate.js +89 -0
  31. package/dist/commands/verify.d.ts +8 -0
  32. package/dist/commands/verify.js +65 -0
  33. package/dist/commands/worktree.d.ts +47 -0
  34. package/dist/commands/worktree.js +396 -0
  35. package/dist/core/benchmark.d.ts +57 -0
  36. package/dist/core/benchmark.js +772 -0
  37. package/dist/core/enforcement.d.ts +80 -0
  38. package/dist/core/enforcement.js +271 -0
  39. package/dist/core/legacy-templates.d.ts +24 -0
  40. package/dist/core/legacy-templates.js +60 -0
  41. package/dist/core/lock-manager.d.ts +54 -0
  42. package/dist/core/lock-manager.js +260 -0
  43. package/dist/core/root-resolver.d.ts +32 -0
  44. package/dist/core/root-resolver.js +149 -0
  45. package/dist/core/setup-assets.d.ts +123 -0
  46. package/dist/core/setup-assets.js +438 -0
  47. package/dist/core/telemetry.d.ts +156 -0
  48. package/dist/core/telemetry.js +416 -0
  49. package/dist/core/test-generator-types.d.ts +227 -0
  50. package/dist/core/test-generator-types.js +179 -0
  51. package/dist/core/test-generator.d.ts +34 -0
  52. package/dist/core/test-generator.js +1690 -0
  53. package/dist/core/tier1-liaison.d.ts +139 -0
  54. package/dist/core/tier1-liaison.js +320 -0
  55. package/dist/core/verifier.d.ts +52 -0
  56. package/dist/core/verifier.js +249 -0
  57. package/dist/core/version.d.ts +5 -0
  58. package/dist/core/version.js +14 -0
  59. package/dist/core/worktree-merge.d.ts +30 -0
  60. package/dist/core/worktree-merge.js +98 -0
  61. package/dist/db/data-engine.d.ts +46 -0
  62. package/dist/db/data-engine.js +802 -0
  63. package/dist/db/diff.d.ts +39 -0
  64. package/dist/db/diff.js +420 -0
  65. package/dist/db/env-parser.d.ts +86 -0
  66. package/dist/db/env-parser.js +549 -0
  67. package/dist/db/introspector.d.ts +21 -0
  68. package/dist/db/introspector.js +835 -0
  69. package/dist/db/types.d.ts +217 -0
  70. package/dist/db/types.js +9 -0
  71. package/dist/governor/circuit-breaker.d.ts +100 -0
  72. package/dist/governor/circuit-breaker.js +231 -0
  73. package/dist/governor/evaluator.d.ts +67 -0
  74. package/dist/governor/evaluator.js +851 -0
  75. package/dist/governor/index.d.ts +6 -0
  76. package/dist/governor/index.js +6 -0
  77. package/dist/governor/rules/api-rules.d.ts +2 -0
  78. package/dist/governor/rules/api-rules.js +140 -0
  79. package/dist/governor/rules/db-rules.d.ts +2 -0
  80. package/dist/governor/rules/db-rules.js +180 -0
  81. package/dist/governor/store.d.ts +33 -0
  82. package/dist/governor/store.js +84 -0
  83. package/dist/governor/types.d.ts +64 -0
  84. package/dist/governor/types.js +1 -0
  85. package/dist/index.d.ts +3 -0
  86. package/dist/index.js +358 -0
  87. package/dist/mcp/server.d.ts +3 -0
  88. package/dist/mcp/server.js +309 -0
  89. package/dist/providers/claude-api.d.ts +5 -0
  90. package/dist/providers/claude-api.js +75 -0
  91. package/dist/providers/claude-cli.d.ts +13 -0
  92. package/dist/providers/claude-cli.js +134 -0
  93. package/dist/providers/gemini.d.ts +8 -0
  94. package/dist/providers/gemini.js +78 -0
  95. package/dist/providers/index.d.ts +5 -0
  96. package/dist/providers/index.js +5 -0
  97. package/dist/providers/registry.d.ts +38 -0
  98. package/dist/providers/registry.js +121 -0
  99. package/dist/providers/types.d.ts +74 -0
  100. package/dist/providers/types.js +25 -0
  101. package/dist/runner/agent-supervisor.d.ts +363 -0
  102. package/dist/runner/agent-supervisor.js +1672 -0
  103. package/dist/scanner/context-builder.d.ts +2 -0
  104. package/dist/scanner/context-builder.js +29 -0
  105. package/dist/scanner/detector.d.ts +2 -0
  106. package/dist/scanner/detector.js +760 -0
  107. package/dist/scanner/types.d.ts +80 -0
  108. package/dist/scanner/types.js +50 -0
  109. package/dist/server/studio-server.d.ts +39 -0
  110. package/dist/server/studio-server.js +1931 -0
  111. package/dist/server/studio-ui.d.ts +15 -0
  112. package/dist/server/studio-ui.js +5321 -0
  113. package/package.json +70 -0
  114. package/plugin/.claude-plugin/plugin.json +18 -0
  115. package/plugin/.mcp.json +10 -0
  116. package/plugin/agents/architect.md +28 -0
  117. package/plugin/agents/verifier.md +28 -0
  118. package/plugin/agents/worker.md +26 -0
  119. package/plugin/hooks/hooks.json +28 -0
  120. package/plugin/skills/nativ/SKILL.md +75 -0
  121. package/templates/.claudeignore +45 -0
  122. package/templates/AGENTS.md +67 -0
  123. package/templates/CLAUDE.md +32 -0
  124. package/templates/GEMINI.md +66 -0
  125. package/templates/claude-agents/architect.md +28 -0
  126. package/templates/claude-agents/verifier.md +28 -0
  127. package/templates/claude-agents/worker.md +26 -0
  128. package/templates/dot-ai/api_contracts.json +66 -0
  129. package/templates/dot-ai/context.md +38 -0
  130. package/templates/dot-ai/db_schema.json +39 -0
  131. package/templates/dot-ai/master_plan.json +28 -0
  132. package/templates/dot-ai/subagents/backend.md +11 -0
  133. package/templates/dot-ai/subagents/database.md +10 -0
  134. package/templates/dot-ai/subagents/db-migration.md +11 -0
  135. package/templates/dot-ai/subagents/devops-agent.md +11 -0
  136. package/templates/dot-ai/subagents/flutter-developer.md +11 -0
  137. package/templates/dot-ai/subagents/frontend.md +126 -0
  138. package/templates/dot-ai/subagents/qa-tester.md +13 -0
  139. package/templates/dot-ai/subagents/security-auditor.md +11 -0
  140. package/templates/dot-ai/ui_specs.md +78 -0
@@ -0,0 +1,105 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import pc from 'picocolors';
4
+ import { ARCHITECT_ROLE, ROLE_ENV, evaluateHookPayload, isTaskUnlocked, loadEnforcementMode, parseJsonLoose, } from '../core/enforcement.js';
5
+ import { resolveProjectRoot } from '../core/root-resolver.js';
6
+ import { recordRoleViolation } from '../core/telemetry.js';
7
+ /** Payloads are a few hundred bytes; anything past this is not a hook call. */
8
+ const MAX_STDIN_BYTES = 1_000_000;
9
+ function readStdin(stream) {
10
+ return new Promise((resolve) => {
11
+ let data = '';
12
+ let settled = false;
13
+ const done = () => {
14
+ if (settled)
15
+ return;
16
+ settled = true;
17
+ resolve(data);
18
+ };
19
+ stream.setEncoding('utf8');
20
+ stream.on('data', (chunk) => {
21
+ data += chunk;
22
+ if (data.length > MAX_STDIN_BYTES)
23
+ done();
24
+ });
25
+ stream.on('end', done);
26
+ stream.on('error', done);
27
+ });
28
+ }
29
+ /**
30
+ * `nativ hook check`: the PreToolUse hook entry point. Reads Claude Code's JSON from stdin and prints the
31
+ * hook decision to stdout. It always exits 0 and fails open: a broken payload, unreadable state or a bug
32
+ * here must never stop an agent's tool call. Only `block` mode ever denies, and only on a real violation.
33
+ */
34
+ export async function runHookCheck(targetDirArg, options = {}) {
35
+ const write = options.output ?? ((text) => process.stdout.write(text));
36
+ try {
37
+ const raw = await readStdin(options.input ?? process.stdin);
38
+ let payload;
39
+ try {
40
+ payload = raw.trim() ? JSON.parse(raw) : null;
41
+ }
42
+ catch {
43
+ process.stderr.write('nativ hook check: stdin was not JSON; allowing.\n');
44
+ return;
45
+ }
46
+ const fallbackCwd = targetDirArg ? path.resolve(targetDirArg) : process.env.CLAUDE_PROJECT_DIR || process.cwd();
47
+ const { output, result } = evaluateHookPayload(payload, { mode: options.mode, role: options.role, fallbackCwd });
48
+ if (result && result.decision !== 'allow' && result.rule && result.relPath && fs.existsSync(path.join(result.root, '.ai'))) {
49
+ const toolName = payload.tool_name ?? 'unknown';
50
+ await recordRoleViolation(result.root, {
51
+ rule: result.rule,
52
+ mode: result.mode === 'block' ? 'block' : 'warn',
53
+ tool: toolName,
54
+ path: result.relPath,
55
+ taskIds: result.taskIds,
56
+ });
57
+ }
58
+ if (output)
59
+ write(JSON.stringify(output));
60
+ }
61
+ catch (err) {
62
+ process.stderr.write(`nativ hook check: ${err instanceof Error ? err.message : String(err)}; allowing.\n`);
63
+ }
64
+ }
65
+ /** `nativ hook status`: what the hook would enforce right now. Read-only. */
66
+ export function getHookStatus(targetDirArg) {
67
+ const root = resolveProjectRoot(targetDirArg);
68
+ const activeTasks = [];
69
+ try {
70
+ const plan = parseJsonLoose(fs.readFileSync(path.join(root, '.ai', 'master_plan.json'), 'utf8'));
71
+ for (const milestone of plan.milestones ?? []) {
72
+ for (const task of milestone.tasks ?? []) {
73
+ if (task.status === 'in_progress' && typeof task.id === 'string') {
74
+ activeTasks.push({ id: task.id, targetFiles: task.targetFiles ?? [], unlocked: isTaskUnlocked(root, task.id) });
75
+ }
76
+ }
77
+ }
78
+ }
79
+ catch {
80
+ // No plan: nothing is in progress.
81
+ }
82
+ return { ok: true, root, mode: loadEnforcementMode(root), role: (process.env[ROLE_ENV] ?? '').toLowerCase() || 'worker', activeTasks };
83
+ }
84
+ export function runHookStatus(targetDirArg, options = {}) {
85
+ const status = getHookStatus(targetDirArg);
86
+ if (options.json) {
87
+ console.log(JSON.stringify(status, null, 2));
88
+ return status;
89
+ }
90
+ const modeColor = status.mode === 'block' ? pc.red : status.mode === 'warn' ? pc.yellow : pc.dim;
91
+ console.log(pc.bold('\nnativ role enforcement'));
92
+ console.log(` Mode: ${modeColor(status.mode)}`);
93
+ console.log(` Role: ${status.role}${status.role === ARCHITECT_ROLE ? pc.dim(' (may edit .ai/ contracts)') : ''}`);
94
+ if (!status.activeTasks.length) {
95
+ console.log(pc.dim(' No task is in progress: only contract and secret paths are protected.\n'));
96
+ }
97
+ else {
98
+ for (const task of status.activeTasks) {
99
+ console.log(` Task: ${pc.cyan(task.id)}${task.unlocked ? pc.yellow(' [unlocked]') : ''}`);
100
+ console.log(pc.dim(` ${task.targetFiles.join(', ') || 'no target files'}`));
101
+ }
102
+ console.log('');
103
+ }
104
+ return status;
105
+ }
@@ -0,0 +1,3 @@
1
+ export declare function runInit(targetDirArg?: string, options?: {
2
+ force?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,144 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import pc from 'picocolors';
5
+ import { detectProject } from '../scanner/detector.js';
6
+ import { buildContextContent } from '../scanner/context-builder.js';
7
+ import { runSetup, printSetupResult } from './setup.js';
8
+ import { stampManaged } from '../core/setup-assets.js';
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ function getTemplatesDir() {
12
+ // In dist/commands/init.js -> ../../templates
13
+ const candidate = path.resolve(__dirname, '../../templates');
14
+ if (fs.existsSync(candidate))
15
+ return candidate;
16
+ // Fallback for direct development: src/../templates
17
+ return path.resolve(__dirname, '../templates');
18
+ }
19
+ export async function runInit(targetDirArg, options = {}) {
20
+ const targetDir = path.resolve(targetDirArg || process.cwd());
21
+ const templatesDir = getTemplatesDir();
22
+ console.log(pc.bold(pc.cyan('\nNativ: Initializing Multi-Tier Pipeline...')));
23
+ console.log(pc.dim(`Target directory: ${targetDir}`));
24
+ // 1. Run scanner
25
+ console.log(pc.yellow('Scanning repository structure & tech stack...'));
26
+ const projectInfo = detectProject(targetDir);
27
+ console.log(pc.green(`āœ” Identified: ${pc.bold(projectInfo.projectName)} (${pc.cyan(projectInfo.repositoryType)})`));
28
+ console.log(pc.dim(' ā”œā”€ Workspace Topology: ') + pc.white(projectInfo.repositoryType));
29
+ if (projectInfo.monorepoWorkspaces.length > 0) {
30
+ console.log(pc.dim(' │ └─ Workspaces: ') + pc.white(projectInfo.monorepoWorkspaces.join(', ')));
31
+ }
32
+ console.log(pc.dim(' ā”œā”€ Runtime & Language: ') + pc.white(projectInfo.runtime));
33
+ console.log(pc.dim(' ā”œā”€ Framework: ') + pc.white(projectInfo.framework));
34
+ const ormDisplay = projectInfo.detectedOrmConfig
35
+ ? `${projectInfo.databaseOrm} (${projectInfo.detectedOrmConfig})`
36
+ : projectInfo.databaseOrm;
37
+ console.log(pc.dim(' ā”œā”€ Database & ORM: ') + pc.white(ormDisplay));
38
+ if (projectInfo.ecosystemManifests.length > 0) {
39
+ console.log(pc.dim(' ā”œā”€ Manifests Scanned: ') + pc.dim(projectInfo.ecosystemManifests.join(', ')));
40
+ }
41
+ console.log(pc.dim(' └─ Verification Gate: ') + pc.cyan(projectInfo.verificationCommand));
42
+ console.log();
43
+ // 2. Prepare directories
44
+ const aiDir = path.join(targetDir, '.ai');
45
+ const subagentsDir = path.join(aiDir, 'subagents');
46
+ fs.mkdirSync(subagentsDir, { recursive: true });
47
+ const timestamp = new Date().toISOString();
48
+ // 3. Write .ai/context.md
49
+ const contextTemplate = fs.readFileSync(path.join(templatesDir, 'dot-ai/context.md'), 'utf8');
50
+ const contextContent = buildContextContent(contextTemplate, projectInfo);
51
+ const contextPath = path.join(aiDir, 'context.md');
52
+ if (!fs.existsSync(contextPath) || options.force) {
53
+ fs.writeFileSync(contextPath, contextContent, 'utf8');
54
+ console.log(pc.green('āœ” Created .ai/context.md'));
55
+ }
56
+ else {
57
+ console.log(pc.dim('- Skipped existing .ai/context.md'));
58
+ }
59
+ // 4. Write .ai/db_schema.json
60
+ const dbTemplate = fs.readFileSync(path.join(templatesDir, 'dot-ai/db_schema.json'), 'utf8');
61
+ const dbContent = dbTemplate
62
+ .replace(/\{\{PROJECT_NAME\}\}/g, projectInfo.projectName)
63
+ .replace(/\{\{TIMESTAMP\}\}/g, timestamp)
64
+ .replace(/\{\{DETECTED_DATABASE_ENGINE\}\}/g, projectInfo.databaseOrm !== 'None detected' ? projectInfo.databaseOrm : 'PostgreSQL');
65
+ const dbPath = path.join(aiDir, 'db_schema.json');
66
+ if (!fs.existsSync(dbPath) || options.force) {
67
+ fs.writeFileSync(dbPath, dbContent, 'utf8');
68
+ console.log(pc.green('āœ” Created .ai/db_schema.json'));
69
+ }
70
+ // 5. Write .ai/api_contracts.json
71
+ const apiTemplate = fs.readFileSync(path.join(templatesDir, 'dot-ai/api_contracts.json'), 'utf8');
72
+ const apiContent = apiTemplate
73
+ .replace(/\{\{PROJECT_NAME\}\}/g, projectInfo.projectName)
74
+ .replace(/\{\{TIMESTAMP\}\}/g, timestamp);
75
+ const apiPath = path.join(aiDir, 'api_contracts.json');
76
+ if (!fs.existsSync(apiPath) || options.force) {
77
+ fs.writeFileSync(apiPath, apiContent, 'utf8');
78
+ console.log(pc.green('āœ” Created .ai/api_contracts.json'));
79
+ }
80
+ // 6. Write .ai/ui_specs.md
81
+ const uiTemplate = fs.readFileSync(path.join(templatesDir, 'dot-ai/ui_specs.md'), 'utf8');
82
+ const uiPath = path.join(aiDir, 'ui_specs.md');
83
+ if (!fs.existsSync(uiPath) || options.force) {
84
+ fs.writeFileSync(uiPath, uiTemplate, 'utf8');
85
+ console.log(pc.green('āœ” Created .ai/ui_specs.md'));
86
+ }
87
+ // 7. Write .ai/master_plan.json
88
+ const planTemplate = fs.readFileSync(path.join(templatesDir, 'dot-ai/master_plan.json'), 'utf8');
89
+ const planContent = planTemplate
90
+ .replace(/\{\{PROJECT_NAME\}\}/g, projectInfo.projectName)
91
+ .replace(/\{\{TIMESTAMP\}\}/g, timestamp)
92
+ .replace(/\{\{VERIFICATION_COMMAND\}\}/g, projectInfo.verificationCommand);
93
+ const planPath = path.join(aiDir, 'master_plan.json');
94
+ if (!fs.existsSync(planPath) || options.force) {
95
+ fs.writeFileSync(planPath, planContent, 'utf8');
96
+ console.log(pc.green('āœ” Created .ai/master_plan.json'));
97
+ }
98
+ // 7. Write Sub-agent specifications
99
+ const subagentFiles = [
100
+ 'backend.md',
101
+ 'frontend.md',
102
+ 'database.md',
103
+ 'qa-tester.md',
104
+ 'flutter-developer.md',
105
+ 'devops-agent.md',
106
+ 'security-auditor.md',
107
+ 'db-migration.md',
108
+ ];
109
+ for (const file of subagentFiles) {
110
+ const src = path.join(templatesDir, 'dot-ai/subagents', file);
111
+ const dest = path.join(subagentsDir, file);
112
+ if (fs.existsSync(src) && (!fs.existsSync(dest) || options.force)) {
113
+ // Stamped so a later `nativ update` can tell an untouched guide from one a person edited.
114
+ fs.writeFileSync(dest, stampManaged(fs.readFileSync(src, 'utf8')), 'utf8');
115
+ console.log(pc.green(`āœ” Created .ai/subagents/${file}`));
116
+ }
117
+ }
118
+ // 8. Write root CLAUDE.md
119
+ const claudeSrc = path.join(templatesDir, 'CLAUDE.md');
120
+ const claudeDest = path.join(targetDir, 'CLAUDE.md');
121
+ if (fs.existsSync(claudeSrc) && (!fs.existsSync(claudeDest) || options.force)) {
122
+ fs.writeFileSync(claudeDest, stampManaged(fs.readFileSync(claudeSrc, 'utf8')), 'utf8');
123
+ console.log(pc.green('āœ” Created CLAUDE.md (Project Manager Directive)'));
124
+ }
125
+ // 9. Write root GEMINI.md
126
+ const geminiSrc = path.join(templatesDir, 'GEMINI.md');
127
+ const geminiDest = path.join(targetDir, 'GEMINI.md');
128
+ if (fs.existsSync(geminiSrc) && (!fs.existsSync(geminiDest) || options.force)) {
129
+ fs.writeFileSync(geminiDest, stampManaged(fs.readFileSync(geminiSrc, 'utf8')), 'utf8');
130
+ console.log(pc.green('āœ” Created GEMINI.md (optional Gemini/Antigravity architect adapter)'));
131
+ }
132
+ // 10. Claude Code configuration: MCP server, hooks, permissions, agents, AGENTS.md. Merges; never overwrites.
133
+ console.log(pc.yellow('Configuring Claude Code (MCP server, hooks, agents)...'));
134
+ const setup = await runSetup(targetDir, { quiet: true });
135
+ if (setup) {
136
+ printSetupResult(setup);
137
+ }
138
+ console.log(pc.bold(pc.cyan('\n✨ Pipeline Scaffolding Completed Successfully!\n')));
139
+ console.log(pc.bold('Next Steps:'));
140
+ console.log(pc.white(` 1. Run ${pc.green('claude')} in this directory and ask the ${pc.magenta('architect')} agent to design your contracts (database, API, UI). You approve each step.`));
141
+ console.log(pc.white(` 2. The architect writes ${pc.cyan('.ai/db_schema.json')}, ${pc.cyan('.ai/api_contracts.json')}, ${pc.cyan('.ai/ui_specs.md')}, and ${pc.cyan('.ai/master_plan.json')}.`));
142
+ console.log(pc.white(` 3. Ask Claude to run ${pc.green('nativ task next')} and delegate each task to a ${pc.magenta('worker')} agent; use ${pc.magenta('verifier')} for an independent check.`));
143
+ console.log(pc.dim(` Run ${pc.white('nativ doctor')} any time, and after updating Claude Code, to confirm the integration is intact.\n`));
144
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * `nativ mcp` — runs the native MCP server over stdio.
3
+ * Nothing may be written to stdout here: it carries the JSON-RPC stream. Diagnostics go to stderr.
4
+ */
5
+ export declare function runMcp(targetDirArg?: string): Promise<void>;
@@ -0,0 +1,16 @@
1
+ import path from 'node:path';
2
+ import { startMcpServer } from '../mcp/server.js';
3
+ /**
4
+ * `nativ mcp` — runs the native MCP server over stdio.
5
+ * Nothing may be written to stdout here: it carries the JSON-RPC stream. Diagnostics go to stderr.
6
+ */
7
+ export async function runMcp(targetDirArg) {
8
+ const targetDir = path.resolve(targetDirArg || process.cwd());
9
+ try {
10
+ await startMcpServer(targetDir);
11
+ }
12
+ catch (err) {
13
+ process.stderr.write(`āœ– Failed to start Nativ MCP server: ${err instanceof Error ? err.message : String(err)}\n`);
14
+ process.exitCode = 1;
15
+ }
16
+ }
@@ -0,0 +1,17 @@
1
+ import { type SetupResult } from '../core/setup-assets.js';
2
+ export interface SetupCommandOptions {
3
+ command?: string;
4
+ enforcement?: string;
5
+ dryRun?: boolean;
6
+ force?: boolean;
7
+ json?: boolean;
8
+ /** Suppresses the report; `nativ init` prints its own. */
9
+ quiet?: boolean;
10
+ }
11
+ export declare function printSetupResult(result: SetupResult): void;
12
+ /**
13
+ * `nativ setup`: writes the Claude Code configuration (.mcp.json, .claude/settings.json, .claude/agents/,
14
+ * AGENTS.md, .nativ/config.json), merging into what exists and never overwriting user-authored content.
15
+ * Safe to run any number of times.
16
+ */
17
+ export declare function runSetup(targetDirArg?: string, options?: SetupCommandOptions): Promise<SetupResult | null>;
@@ -0,0 +1,51 @@
1
+ import path from 'node:path';
2
+ import pc from 'picocolors';
3
+ import { applySetup } from '../core/setup-assets.js';
4
+ const MODES = ['off', 'warn', 'block'];
5
+ const ICON = { created: '+', updated: '~', unchanged: '=', skipped: '!' };
6
+ export function printSetupResult(result) {
7
+ const paint = (action, text) => action === 'created' ? pc.green(text) : action === 'updated' ? pc.cyan(text) : action === 'skipped' ? pc.yellow(text) : pc.dim(text);
8
+ for (const change of result.changes) {
9
+ const verb = result.dryRun && (change.action === 'created' || change.action === 'updated') ? `would be ${change.action}` : change.action;
10
+ console.log(paint(change.action, ` ${ICON[change.action]} ${change.path}`) + pc.dim(` ${verb}${change.detail ? `: ${change.detail}` : ''}`));
11
+ }
12
+ for (const warning of result.warnings)
13
+ console.log(pc.yellow(`\n⚠ ${warning}`));
14
+ }
15
+ /**
16
+ * `nativ setup`: writes the Claude Code configuration (.mcp.json, .claude/settings.json, .claude/agents/,
17
+ * AGENTS.md, .nativ/config.json), merging into what exists and never overwriting user-authored content.
18
+ * Safe to run any number of times.
19
+ */
20
+ export async function runSetup(targetDirArg, options = {}) {
21
+ const targetDir = path.resolve(targetDirArg || process.cwd());
22
+ if (options.enforcement !== undefined && !MODES.includes(options.enforcement)) {
23
+ const message = `--enforcement must be one of: ${MODES.join(', ')}`;
24
+ if (options.json)
25
+ console.log(JSON.stringify({ ok: false, error: message }, null, 2));
26
+ else
27
+ console.error(pc.red(`\nāœ– ${message}\n`));
28
+ process.exitCode = 1;
29
+ return null;
30
+ }
31
+ const setupOptions = {
32
+ command: options.command,
33
+ enforcement: options.enforcement,
34
+ force: options.force,
35
+ dryRun: options.dryRun,
36
+ };
37
+ const result = applySetup(targetDir, setupOptions);
38
+ if (options.json) {
39
+ console.log(JSON.stringify({ ok: true, root: result.root, dryRun: result.dryRun, cli: result.invocation, changes: result.changes, warnings: result.warnings }, null, 2));
40
+ return result;
41
+ }
42
+ if (options.quiet)
43
+ return result;
44
+ console.log(pc.bold(pc.cyan(`\nnativ setup${result.dryRun ? ' (dry run)' : ''}`)) + pc.dim(` ${result.root}`));
45
+ printSetupResult(result);
46
+ const changed = result.changes.filter((c) => c.action === 'created' || c.action === 'updated').length;
47
+ console.log(changed
48
+ ? pc.green(`\nāœ” ${result.dryRun ? `${changed} change(s) pending` : `${changed} file(s) written`}.`) + pc.dim(' Restart Claude Code (or run /hooks) to load the hooks.\n')
49
+ : pc.green('\nāœ” Everything is already set up. Nothing changed.\n'));
50
+ return result;
51
+ }
@@ -0,0 +1,5 @@
1
+ export interface StatusOptions {
2
+ telemetry?: boolean;
3
+ json?: boolean;
4
+ }
5
+ export declare function runStatus(targetDirArg?: string, options?: StatusOptions): Promise<void>;
@@ -0,0 +1,75 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import pc from 'picocolors';
4
+ import { loadTelemetry, formatTelemetrySummary, recomputeTelemetrySummary } from '../core/telemetry.js';
5
+ export async function runStatus(targetDirArg, options = {}) {
6
+ const targetDir = path.resolve(targetDirArg || process.cwd());
7
+ const planPath = path.join(targetDir, '.ai', 'master_plan.json');
8
+ const telemetryPath = path.join(targetDir, '.ai', 'telemetry.json');
9
+ if (!fs.existsSync(planPath)) {
10
+ console.error(pc.red(`\nāœ– No .ai/master_plan.json found in: ${targetDir}`));
11
+ console.log(pc.yellow('Run `nativ init` first to scaffold the environment.\n'));
12
+ process.exitCode = 1;
13
+ return;
14
+ }
15
+ try {
16
+ const raw = fs.readFileSync(planPath, 'utf8');
17
+ const plan = JSON.parse(raw);
18
+ const telemetry = loadTelemetry(telemetryPath, plan.projectName);
19
+ if (options.json) {
20
+ console.log(JSON.stringify({
21
+ projectName: plan.projectName,
22
+ overallStatus: plan.overallStatus,
23
+ activeMilestoneId: plan.activeMilestoneId,
24
+ milestones: plan.milestones,
25
+ telemetry: telemetry.summary,
26
+ tasksTelemetry: telemetry.tasks,
27
+ }, null, 2));
28
+ return;
29
+ }
30
+ console.log(pc.bold(pc.cyan(`\nšŸ“Š AI Agent Workflow Status: ${plan.projectName}`)));
31
+ console.log(pc.dim(`Overall Status: ${plan.overallStatus.toUpperCase()} | Active Milestone: ${plan.activeMilestoneId}`));
32
+ let totalTasks = 0;
33
+ let completedTasks = 0;
34
+ let inProgressTasks = 0;
35
+ let blockedTasks = 0;
36
+ for (const m of plan.milestones) {
37
+ console.log(pc.bold(`\n🚩 Milestone [${m.id}]: ${m.name} (${m.status})`));
38
+ for (const t of m.tasks) {
39
+ totalTasks++;
40
+ let statusBadge = pc.gray('ā—‹ pending');
41
+ if (t.status === 'completed') {
42
+ completedTasks++;
43
+ statusBadge = pc.green('āœ” completed');
44
+ }
45
+ else if (t.status === 'in_progress') {
46
+ inProgressTasks++;
47
+ statusBadge = pc.yellow('ā–¶ in_progress');
48
+ }
49
+ else if (t.status === 'blocked') {
50
+ blockedTasks++;
51
+ statusBadge = pc.red('āœ– blocked');
52
+ }
53
+ console.log(` ${statusBadge} ${pc.bold(t.id)}: ${t.title}`);
54
+ console.log(pc.dim(` Agent: [${t.assignedSubagent}] | Verify: \`${t.verificationCommand || 'none'}\``));
55
+ if (t.notes) {
56
+ console.log(pc.red(` Note: ${t.notes}`));
57
+ }
58
+ }
59
+ }
60
+ const pct = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
61
+ console.log(pc.bold(pc.cyan(`\nšŸ“ˆ Completion: ${completedTasks}/${totalTasks} tasks (${pct}%)`)));
62
+ if (options.telemetry || telemetry.summary.totalTasksCompleted > 0) {
63
+ // Re-derive the summary in memory so it reflects current rates even before the next write.
64
+ recomputeTelemetrySummary(telemetry);
65
+ console.log(formatTelemetrySummary(telemetry, { completedTasks }) + '\n');
66
+ }
67
+ else {
68
+ console.log(pc.dim('\nTip: Run `nativ status --telemetry` for full token, duration, and cost metrics.\n'));
69
+ }
70
+ }
71
+ catch (err) {
72
+ console.error(pc.red(`āœ– Failed to read or parse .ai/master_plan.json: ${err.message}`));
73
+ process.exitCode = 1;
74
+ }
75
+ }
@@ -0,0 +1,83 @@
1
+ import { MasterPlan, MasterPlanTask, SubagentType } from '../scanner/types.js';
2
+ import { loadPlan, savePlan, withPlanLock } from '../core/lock-manager.js';
3
+ export { loadPlan, savePlan, withPlanLock };
4
+ export declare function getRoleGuide(assignedSubagent: SubagentType): string;
5
+ export declare function getRecommendedContractSlice(assignedSubagent: SubagentType): string;
6
+ export declare function runTaskList(targetDirArg?: string, options?: {
7
+ available?: boolean;
8
+ status?: string;
9
+ milestone?: string;
10
+ fastPath?: boolean;
11
+ json?: boolean;
12
+ }): Promise<void>;
13
+ export declare function runTaskNext(targetDirArg?: string, options?: {
14
+ json?: boolean;
15
+ }): Promise<void>;
16
+ export declare function runTaskStart(taskId: string, targetDirArg?: string): Promise<void | null>;
17
+ export interface TaskUnlockOptions {
18
+ /** Why scope enforcement is being lifted; kept in .nativ/unlocks.json. */
19
+ reason?: string;
20
+ /** Re-locks the task instead. */
21
+ revoke?: boolean;
22
+ }
23
+ /**
24
+ * Lifts role enforcement's target-file scope for one task (emergency escape hatch), or restores it with
25
+ * --revoke. Contract and secret paths stay protected either way. Deliberately CLI-only and not an MCP
26
+ * tool: an agent must not be able to remove its own guardrail.
27
+ */
28
+ export declare function runTaskUnlock(taskId: string, targetDirArg?: string, options?: TaskUnlockOptions): Promise<void>;
29
+ export interface TaskCompleteOptions {
30
+ notes?: string;
31
+ skipVerify?: boolean;
32
+ timeout?: number;
33
+ }
34
+ export declare function runTaskComplete(taskId: string, targetDirArg?: string, options?: TaskCompleteOptions): Promise<void>;
35
+ export declare function runTaskBlock(taskId: string, reason: string, targetDirArg?: string): Promise<void | null>;
36
+ export declare function runTaskEscalate(taskId: string, targetDirArg?: string, options?: {
37
+ type?: string;
38
+ details?: string;
39
+ affected?: string;
40
+ }): Promise<void | null>;
41
+ export declare const FAST_PATH_MILESTONE_NAME = "Fast-Path Tasks";
42
+ export declare function generateNextTaskId(plan: MasterPlan): string;
43
+ export declare function generateNextMilestoneId(plan: MasterPlan): string;
44
+ export interface TaskAddOptions {
45
+ /** Milestone ID or name; defaults to the active milestone (or the fast-path milestone with fastPath). */
46
+ milestone?: string;
47
+ /** Assigned sub-agent (default backend). */
48
+ agent?: string;
49
+ /** Verification command run by the `task complete` gatekeeper. */
50
+ verify?: string;
51
+ /** Target files, comma-separated or as an array. */
52
+ files?: string | string[];
53
+ description?: string;
54
+ fastPath?: boolean;
55
+ /** Dependency task IDs, comma-separated or as an array. */
56
+ deps?: string | string[];
57
+ notes?: string;
58
+ json?: boolean;
59
+ }
60
+ export interface TaskAddResult {
61
+ task: MasterPlanTask;
62
+ milestoneId: string;
63
+ milestoneName: string;
64
+ createdMilestone: boolean;
65
+ reopenedMilestone: boolean;
66
+ }
67
+ /**
68
+ * Appends a task to .ai/master_plan.json with an auto-incremented ID.
69
+ * Routing: --milestone wins; otherwise fast-path tasks go to the fast-path milestone (created on
70
+ * first use) and planned tasks to the active milestone. Completed milestones are reopened.
71
+ */
72
+ export declare function runTaskAdd(title: string, targetDirArg?: string, options?: TaskAddOptions): Promise<TaskAddResult | null>;
73
+ export interface ProposePatchOptions {
74
+ taskId: string;
75
+ target: 'db_schema' | 'api_contracts';
76
+ operation: 'ADD' | 'ALTER' | 'DROP' | 'RENAME';
77
+ path: string;
78
+ value?: any;
79
+ reason: string;
80
+ baseHash?: string;
81
+ json?: boolean;
82
+ }
83
+ export declare function runTaskProposePatch(targetDirArg: string | undefined, options: ProposePatchOptions): Promise<import("../governor/types.js").GovernorVerdict>;