@neurcode-ai/cli 0.14.0 → 0.15.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 (188) hide show
  1. package/README.md +60 -8
  2. package/dist/api-client.d.ts +284 -0
  3. package/dist/api-client.d.ts.map +1 -1
  4. package/dist/api-client.js +111 -0
  5. package/dist/api-client.js.map +1 -1
  6. package/dist/commands/activate.d.ts +82 -0
  7. package/dist/commands/activate.d.ts.map +1 -0
  8. package/dist/commands/activate.js +551 -0
  9. package/dist/commands/activate.js.map +1 -0
  10. package/dist/commands/admission.d.ts +67 -0
  11. package/dist/commands/admission.d.ts.map +1 -0
  12. package/dist/commands/admission.js +350 -0
  13. package/dist/commands/admission.js.map +1 -0
  14. package/dist/commands/agent.d.ts +3 -0
  15. package/dist/commands/agent.d.ts.map +1 -0
  16. package/dist/commands/agent.js +2045 -0
  17. package/dist/commands/agent.js.map +1 -0
  18. package/dist/commands/demo.d.ts +3 -0
  19. package/dist/commands/demo.d.ts.map +1 -0
  20. package/dist/commands/demo.js +102 -0
  21. package/dist/commands/demo.js.map +1 -0
  22. package/dist/commands/init.d.ts.map +1 -1
  23. package/dist/commands/init.js +58 -44
  24. package/dist/commands/init.js.map +1 -1
  25. package/dist/commands/login.d.ts +1 -1
  26. package/dist/commands/login.d.ts.map +1 -1
  27. package/dist/commands/login.js +44 -22
  28. package/dist/commands/login.js.map +1 -1
  29. package/dist/commands/profile.d.ts +14 -0
  30. package/dist/commands/profile.d.ts.map +1 -0
  31. package/dist/commands/profile.js +118 -0
  32. package/dist/commands/profile.js.map +1 -0
  33. package/dist/commands/quickstart.d.ts +2 -2
  34. package/dist/commands/quickstart.d.ts.map +1 -1
  35. package/dist/commands/quickstart.js +31 -30
  36. package/dist/commands/quickstart.js.map +1 -1
  37. package/dist/commands/remediate-export.d.ts +6 -1
  38. package/dist/commands/remediate-export.d.ts.map +1 -1
  39. package/dist/commands/remediate-export.js +359 -7
  40. package/dist/commands/remediate-export.js.map +1 -1
  41. package/dist/commands/replay.d.ts.map +1 -1
  42. package/dist/commands/replay.js +84 -0
  43. package/dist/commands/replay.js.map +1 -1
  44. package/dist/commands/run.d.ts +3 -0
  45. package/dist/commands/run.d.ts.map +1 -0
  46. package/dist/commands/run.js +98 -0
  47. package/dist/commands/run.js.map +1 -0
  48. package/dist/commands/runtime-adapter.d.ts +8 -0
  49. package/dist/commands/runtime-adapter.d.ts.map +1 -0
  50. package/dist/commands/runtime-adapter.js +375 -0
  51. package/dist/commands/runtime-adapter.js.map +1 -0
  52. package/dist/commands/runtime-doctor.d.ts +6 -0
  53. package/dist/commands/runtime-doctor.d.ts.map +1 -0
  54. package/dist/commands/runtime-doctor.js +478 -0
  55. package/dist/commands/runtime-doctor.js.map +1 -0
  56. package/dist/commands/runtime-report.d.ts +13 -0
  57. package/dist/commands/runtime-report.d.ts.map +1 -0
  58. package/dist/commands/runtime-report.js +81 -0
  59. package/dist/commands/runtime-report.js.map +1 -0
  60. package/dist/commands/runtime-sync.d.ts +17 -0
  61. package/dist/commands/runtime-sync.d.ts.map +1 -0
  62. package/dist/commands/runtime-sync.js +656 -0
  63. package/dist/commands/runtime-sync.js.map +1 -0
  64. package/dist/commands/runtime.d.ts +16 -0
  65. package/dist/commands/runtime.d.ts.map +1 -0
  66. package/dist/commands/runtime.js +380 -0
  67. package/dist/commands/runtime.js.map +1 -0
  68. package/dist/commands/session-hook.d.ts +35 -0
  69. package/dist/commands/session-hook.d.ts.map +1 -0
  70. package/dist/commands/session-hook.js +1297 -0
  71. package/dist/commands/session-hook.js.map +1 -0
  72. package/dist/commands/session.d.ts +91 -0
  73. package/dist/commands/session.d.ts.map +1 -1
  74. package/dist/commands/session.js +1226 -0
  75. package/dist/commands/session.js.map +1 -1
  76. package/dist/commands/whoami.d.ts +7 -4
  77. package/dist/commands/whoami.d.ts.map +1 -1
  78. package/dist/commands/whoami.js +59 -34
  79. package/dist/commands/whoami.js.map +1 -1
  80. package/dist/config.d.ts.map +1 -1
  81. package/dist/config.js +24 -5
  82. package/dist/config.js.map +1 -1
  83. package/dist/daemon/routes.d.ts.map +1 -1
  84. package/dist/daemon/routes.js +8 -0
  85. package/dist/daemon/routes.js.map +1 -1
  86. package/dist/daemon/server.d.ts.map +1 -1
  87. package/dist/daemon/server.js +88 -0
  88. package/dist/daemon/server.js.map +1 -1
  89. package/dist/governance/impact-analysis.d.ts +27 -0
  90. package/dist/governance/impact-analysis.d.ts.map +1 -0
  91. package/dist/governance/impact-analysis.js +274 -0
  92. package/dist/governance/impact-analysis.js.map +1 -0
  93. package/dist/index.js +472 -29
  94. package/dist/index.js.map +1 -1
  95. package/dist/intent-engine/matcher.d.ts.map +1 -1
  96. package/dist/intent-engine/matcher.js +3 -12
  97. package/dist/intent-engine/matcher.js.map +1 -1
  98. package/dist/utils/admission-artifact.d.ts +59 -0
  99. package/dist/utils/admission-artifact.d.ts.map +1 -0
  100. package/dist/utils/admission-artifact.js +410 -0
  101. package/dist/utils/admission-artifact.js.map +1 -0
  102. package/dist/utils/agent-adapter-setup.d.ts +80 -0
  103. package/dist/utils/agent-adapter-setup.d.ts.map +1 -0
  104. package/dist/utils/agent-adapter-setup.js +577 -0
  105. package/dist/utils/agent-adapter-setup.js.map +1 -0
  106. package/dist/utils/agent-guard-supervisor.d.ts +75 -0
  107. package/dist/utils/agent-guard-supervisor.d.ts.map +1 -0
  108. package/dist/utils/agent-guard-supervisor.js +388 -0
  109. package/dist/utils/agent-guard-supervisor.js.map +1 -0
  110. package/dist/utils/agent-guard.d.ts +92 -0
  111. package/dist/utils/agent-guard.d.ts.map +1 -0
  112. package/dist/utils/agent-guard.js +326 -0
  113. package/dist/utils/agent-guard.js.map +1 -0
  114. package/dist/utils/agent-session-launcher.d.ts +89 -0
  115. package/dist/utils/agent-session-launcher.d.ts.map +1 -0
  116. package/dist/utils/agent-session-launcher.js +308 -0
  117. package/dist/utils/agent-session-launcher.js.map +1 -0
  118. package/dist/utils/bash-command-analysis.d.ts +19 -0
  119. package/dist/utils/bash-command-analysis.d.ts.map +1 -0
  120. package/dist/utils/bash-command-analysis.js +295 -0
  121. package/dist/utils/bash-command-analysis.js.map +1 -0
  122. package/dist/utils/consequence-nudges.d.ts +30 -0
  123. package/dist/utils/consequence-nudges.d.ts.map +1 -0
  124. package/dist/utils/consequence-nudges.js +313 -0
  125. package/dist/utils/consequence-nudges.js.map +1 -0
  126. package/dist/utils/drift-intelligence.d.ts.map +1 -1
  127. package/dist/utils/drift-intelligence.js +29 -7
  128. package/dist/utils/drift-intelligence.js.map +1 -1
  129. package/dist/utils/git-coverage.d.ts +57 -0
  130. package/dist/utils/git-coverage.d.ts.map +1 -0
  131. package/dist/utils/git-coverage.js +302 -0
  132. package/dist/utils/git-coverage.js.map +1 -0
  133. package/dist/utils/gitignore.d.ts.map +1 -1
  134. package/dist/utils/gitignore.js +2 -1
  135. package/dist/utils/gitignore.js.map +1 -1
  136. package/dist/utils/governed-intent.d.ts +10 -0
  137. package/dist/utils/governed-intent.d.ts.map +1 -0
  138. package/dist/utils/governed-intent.js +108 -0
  139. package/dist/utils/governed-intent.js.map +1 -0
  140. package/dist/utils/hook-heartbeat.d.ts +55 -0
  141. package/dist/utils/hook-heartbeat.d.ts.map +1 -0
  142. package/dist/utils/hook-heartbeat.js +116 -0
  143. package/dist/utils/hook-heartbeat.js.map +1 -0
  144. package/dist/utils/intent-continuity.d.ts +21 -0
  145. package/dist/utils/intent-continuity.d.ts.map +1 -0
  146. package/dist/utils/intent-continuity.js +192 -0
  147. package/dist/utils/intent-continuity.js.map +1 -0
  148. package/dist/utils/messages.d.ts +1 -1
  149. package/dist/utils/messages.d.ts.map +1 -1
  150. package/dist/utils/messages.js +24 -21
  151. package/dist/utils/messages.js.map +1 -1
  152. package/dist/utils/runtime-companion.d.ts +137 -0
  153. package/dist/utils/runtime-companion.d.ts.map +1 -0
  154. package/dist/utils/runtime-companion.js +231 -0
  155. package/dist/utils/runtime-companion.js.map +1 -0
  156. package/dist/utils/runtime-connection.d.ts +46 -0
  157. package/dist/utils/runtime-connection.d.ts.map +1 -0
  158. package/dist/utils/runtime-connection.js +148 -0
  159. package/dist/utils/runtime-connection.js.map +1 -0
  160. package/dist/utils/runtime-evidence.d.ts +68 -0
  161. package/dist/utils/runtime-evidence.d.ts.map +1 -0
  162. package/dist/utils/runtime-evidence.js +248 -0
  163. package/dist/utils/runtime-evidence.js.map +1 -0
  164. package/dist/utils/runtime-live.d.ts +33 -0
  165. package/dist/utils/runtime-live.d.ts.map +1 -0
  166. package/dist/utils/runtime-live.js +361 -0
  167. package/dist/utils/runtime-live.js.map +1 -0
  168. package/dist/utils/runtime-outbox.d.ts +76 -0
  169. package/dist/utils/runtime-outbox.d.ts.map +1 -0
  170. package/dist/utils/runtime-outbox.js +410 -0
  171. package/dist/utils/runtime-outbox.js.map +1 -0
  172. package/dist/utils/runtime-receipt.d.ts +50 -0
  173. package/dist/utils/runtime-receipt.d.ts.map +1 -0
  174. package/dist/utils/runtime-receipt.js +223 -0
  175. package/dist/utils/runtime-receipt.js.map +1 -0
  176. package/dist/utils/state.d.ts +21 -0
  177. package/dist/utils/state.d.ts.map +1 -1
  178. package/dist/utils/state.js +30 -0
  179. package/dist/utils/state.js.map +1 -1
  180. package/dist/utils/structural-understanding.d.ts +334 -0
  181. package/dist/utils/structural-understanding.d.ts.map +1 -0
  182. package/dist/utils/structural-understanding.js +2316 -0
  183. package/dist/utils/structural-understanding.js.map +1 -0
  184. package/dist/utils/v0-governance.d.ts +197 -0
  185. package/dist/utils/v0-governance.d.ts.map +1 -0
  186. package/dist/utils/v0-governance.js +904 -0
  187. package/dist/utils/v0-governance.js.map +1 -0
  188. package/package.json +5 -5
@@ -0,0 +1,2045 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentCommand = agentCommand;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_child_process_1 = require("node:child_process");
6
+ const node_crypto_1 = require("node:crypto");
7
+ const governance_runtime_1 = require("@neurcode-ai/governance-runtime");
8
+ const agent_session_launcher_1 = require("../utils/agent-session-launcher");
9
+ const v0_governance_1 = require("../utils/v0-governance");
10
+ const agent_adapter_setup_1 = require("../utils/agent-adapter-setup");
11
+ const agent_guard_1 = require("../utils/agent-guard");
12
+ const admission_artifact_1 = require("../utils/admission-artifact");
13
+ const agent_guard_supervisor_1 = require("../utils/agent-guard-supervisor");
14
+ const runtime_live_1 = require("../utils/runtime-live");
15
+ const runtime_evidence_1 = require("../utils/runtime-evidence");
16
+ const runtime_connection_1 = require("../utils/runtime-connection");
17
+ const runtime_adapter_1 = require("./runtime-adapter");
18
+ const session_1 = require("./session");
19
+ let chalk;
20
+ try {
21
+ chalk = require('chalk');
22
+ }
23
+ catch {
24
+ chalk = {
25
+ green: (s) => s,
26
+ yellow: (s) => s,
27
+ red: (s) => s,
28
+ bold: (s) => s,
29
+ dim: (s) => s,
30
+ cyan: (s) => s,
31
+ white: (s) => s,
32
+ };
33
+ }
34
+ const AGENT_TO_ADAPTER = {
35
+ claude: 'claude-code-hooks',
36
+ 'claude-code': 'claude-code-hooks',
37
+ copilot: 'copilot-hooks',
38
+ 'github-copilot': 'copilot-hooks',
39
+ 'copilot-hooks': 'copilot-hooks',
40
+ codex: 'codex-mcp',
41
+ cursor: 'cursor-mcp',
42
+ gemini: 'generic-mcp',
43
+ generic: 'generic-mcp',
44
+ mcp: 'generic-mcp',
45
+ 'generic-mcp': 'generic-mcp',
46
+ vscode: 'vscode-extension',
47
+ 'vs-code': 'vscode-extension',
48
+ 'vscode-extension': 'vscode-extension',
49
+ };
50
+ function compact(values, max = 5) {
51
+ if (values.length === 0)
52
+ return 'none';
53
+ const shown = values.slice(0, max).join(', ');
54
+ return values.length > max ? `${shown} +${values.length - max} more` : shown;
55
+ }
56
+ function collect(value, previous = []) {
57
+ return [...previous, value];
58
+ }
59
+ function readPlan(options) {
60
+ if (options.planFile)
61
+ return (0, node_fs_1.readFileSync)(options.planFile, 'utf8');
62
+ return options.plan;
63
+ }
64
+ function normalizeAdapter(value) {
65
+ if (!value)
66
+ return null;
67
+ const key = value.trim().toLowerCase();
68
+ return AGENT_TO_ADAPTER[key] ?? ((0, governance_runtime_1.listAgentRuntimeAdapterCapabilities)().some((capability) => capability.adapter === key)
69
+ ? key
70
+ : null);
71
+ }
72
+ function adapterFromOptions(options, fallbackAgent) {
73
+ const direct = normalizeAdapter(options.adapter);
74
+ if (direct)
75
+ return direct;
76
+ const agent = normalizeAdapter(options.agent || fallbackAgent);
77
+ if (agent)
78
+ return agent;
79
+ throw new Error(`Unsupported agent/adapter. Use one of: claude, copilot, codex, cursor, generic-mcp, vscode.`);
80
+ }
81
+ function repoRootFrom(options) {
82
+ return (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
83
+ }
84
+ function dashboardPairingSummary(repoRoot) {
85
+ const connection = (0, runtime_connection_1.loadRuntimeConnection)(repoRoot);
86
+ if (!connection) {
87
+ return {
88
+ status: 'local_only',
89
+ autoSyncEnabled: false,
90
+ message: 'Local-only: this repo is not paired with Runtime Control Plane, so runs will not appear in the dashboard yet.',
91
+ nextAction: 'Open Runtime Control Plane, choose Connect repo, then run the short-lived pairing command from this repository before the pilot.',
92
+ };
93
+ }
94
+ return {
95
+ status: 'connected',
96
+ repoName: connection.repo.name,
97
+ apiUrl: connection.apiUrl,
98
+ autoSyncEnabled: Boolean(connection.autoSync?.enabled),
99
+ lastStatus: connection.autoSync?.lastStatus ?? null,
100
+ message: `Dashboard-connected: runtime evidence can sync to ${connection.repo.name}.`,
101
+ nextAction: connection.autoSync?.enabled
102
+ ? undefined
103
+ : 'Enable runtime auto-sync or run `neurcode sync --runtime` after the governed session.',
104
+ };
105
+ }
106
+ function renderDashboardPairing(pairing) {
107
+ const label = pairing.status === 'connected' ? chalk.green('connected') : chalk.yellow('local-only');
108
+ console.log(`Dashboard: ${label} ${chalk.dim(pairing.message)}`);
109
+ if (pairing.status === 'connected') {
110
+ console.log(`Sync: ${pairing.autoSyncEnabled ? chalk.green('enabled') : chalk.yellow('manual')} ${chalk.dim(pairing.lastStatus ? `last: ${pairing.lastStatus}` : 'no prior sync status')}`);
111
+ }
112
+ if (pairing.nextAction) {
113
+ console.log(chalk.dim(`Next: ${pairing.nextAction}`));
114
+ }
115
+ }
116
+ async function submitAgentEvent(input) {
117
+ const repoRoot = repoRootFrom({ dir: input.dir });
118
+ const normalized = (0, governance_runtime_1.normalizeAgentRuntimeEvent)({
119
+ schemaVersion: governance_runtime_1.AGENT_RUNTIME_ADAPTER_SCHEMA_VERSION,
120
+ adapter: input.adapter,
121
+ eventType: input.eventType,
122
+ cwd: repoRoot,
123
+ payload: input.payload ?? {},
124
+ });
125
+ return (0, runtime_adapter_1.submitAgentRuntimeEvent)({ ...normalized, cwd: repoRoot });
126
+ }
127
+ function emitJson(value) {
128
+ console.log(JSON.stringify(value, null, 2));
129
+ }
130
+ function emitJsonLine(value) {
131
+ console.log(JSON.stringify(value));
132
+ }
133
+ function emitError(error, json) {
134
+ const message = error instanceof Error ? error.message : String(error);
135
+ if (json)
136
+ emitJson({ ok: false, error: message });
137
+ else
138
+ console.error(chalk.red(`Neurcode agent command failed: ${message}`));
139
+ process.exitCode = 1;
140
+ }
141
+ function renderLaunch(result) {
142
+ console.log('');
143
+ console.log(chalk.bold('Neurcode universal agent session'));
144
+ console.log(chalk.dim('-'.repeat(72)));
145
+ console.log(`Repo: ${chalk.white(result.repoRoot)}`);
146
+ console.log(`Session: ${chalk.cyan(result.session.sessionId)}`);
147
+ console.log(`Agent: ${result.agent.normalized} -> ${result.agent.adapter} ${chalk.dim(`(${result.agent.compatibilityMode.replace(/_/g, ' ')})`)}`);
148
+ console.log(`Goal: ${result.session.goal}`);
149
+ console.log(`Scope: ${result.session.scopeMode} · ${compact(result.session.allowedGlobs)}`);
150
+ console.log(`Gates: ${compact(result.session.approvalRequiredGlobs)}`);
151
+ console.log(`Enforce: ${compact(result.agent.enforceable, 3)}`);
152
+ console.log(`Advisory: ${compact(result.agent.advisoryOnly, 3)}`);
153
+ console.log(`Next: ${result.handshake.nextEvent ?? 'status'} · ${result.handshake.status}`);
154
+ console.log('');
155
+ console.log(chalk.bold('Starter prompt'));
156
+ console.log(result.handshake.starterPrompt);
157
+ console.log('');
158
+ console.log(chalk.bold('Agent commands'));
159
+ console.log(chalk.dim(` neurcode agent handshake --adapter ${result.agent.adapter} --session-id ${result.session.sessionId}`));
160
+ console.log(chalk.dim(` neurcode agent plan --adapter ${result.agent.adapter} --plan "<source-free plan>" --session-id ${result.session.sessionId}`));
161
+ console.log(chalk.dim(` neurcode agent check <repo-relative-path> --adapter ${result.agent.adapter} --session-id ${result.session.sessionId}`));
162
+ console.log('');
163
+ }
164
+ function renderDecision(result, target) {
165
+ const color = result.decision === 'deny'
166
+ ? chalk.red
167
+ : result.decision === 'warn'
168
+ ? chalk.yellow
169
+ : chalk.green;
170
+ console.log(color(`${result.decision.toUpperCase()}: ${result.message}`));
171
+ if (target)
172
+ console.log(chalk.dim(`Path: ${target}`));
173
+ console.log(chalk.dim(`Adapter: ${result.adapter} · ${result.enforcementLevel} · ${result.eventType}`));
174
+ }
175
+ function capabilityFor(adapter) {
176
+ return (0, governance_runtime_1.listAgentRuntimeAdapterCapabilities)().find((capability) => capability.adapter === adapter);
177
+ }
178
+ function controlLevelLabel(level) {
179
+ if (level === 'hard_block_capable')
180
+ return 'hard-block capable';
181
+ if (level === 'supervised_advisory_capable')
182
+ return 'supervised/advisory capable';
183
+ if (level === 'evidence_only_capable')
184
+ return 'evidence-only capable';
185
+ return 'unsupported/unknown';
186
+ }
187
+ function firstRunCommands(input) {
188
+ const session = input.sessionIdPlaceholder || '<session-id>';
189
+ if (input.target === 'claude') {
190
+ return {
191
+ activate: 'neurcode activate claude',
192
+ start: `neurcode run claude --goal "${input.goal}"`,
193
+ status: 'neurcode status',
194
+ approve: 'neurcode session approve --path <exact-path> --reason "<reason>"',
195
+ };
196
+ }
197
+ if (input.target === 'copilot') {
198
+ return {
199
+ activate: 'neurcode activate copilot',
200
+ start: `neurcode agent guard start copilot --goal "${input.goal}" --plan "<source-free plan>"`,
201
+ status: 'neurcode status',
202
+ approve: 'Approve the exact suggested path in Runtime Control Plane',
203
+ finish: `neurcode agent guard finish --session-id ${session} --fail-on-unverified`,
204
+ report: `neurcode agent report copilot --session-id ${session}`,
205
+ };
206
+ }
207
+ return {
208
+ bootstrap: `neurcode agent bootstrap ${input.target}`,
209
+ start: `neurcode agent guard start ${input.target} --goal "${input.goal}" --plan "<source-free plan>"`,
210
+ handshake: `neurcode agent handshake --adapter ${input.adapter} --session-id ${session}`,
211
+ plan: `neurcode agent plan --adapter ${input.adapter} --session-id ${session} --plan "<source-free plan>"`,
212
+ check: `neurcode agent check <repo-relative-path> --adapter ${input.adapter} --session-id ${session}`,
213
+ approve: `neurcode agent approve <exact-path> --adapter ${input.adapter} --session-id ${session} --reason "<reason>"`,
214
+ finish: `neurcode agent guard finish --session-id ${session} --fail-on-unverified`,
215
+ report: `neurcode agent report ${input.target} --session-id ${session}`,
216
+ };
217
+ }
218
+ function buildSetupPayload(agentArg, options) {
219
+ const repoRoot = repoRootFrom({ dir: options.dir });
220
+ const target = (0, agent_adapter_setup_1.normalizeAgentSetupTarget)(agentArg);
221
+ const dashboardPairing = dashboardPairingSummary(repoRoot);
222
+ const snippet = (0, agent_adapter_setup_1.buildAgentSetupSnippet)({
223
+ target,
224
+ repoRoot,
225
+ global: options.global === true,
226
+ });
227
+ const instructionArtifact = (0, agent_adapter_setup_1.buildAgentInstructionArtifact)({ target, repoRoot });
228
+ const capability = capabilityFor(snippet.adapter);
229
+ const profile = (0, v0_governance_1.ensureFreshGovernanceProfile)(repoRoot, { force: options.forceProfile === true });
230
+ const before = (0, agent_adapter_setup_1.inspectAgentSetup)({
231
+ target,
232
+ repoRoot,
233
+ global: options.global === true,
234
+ });
235
+ const instructionsBefore = (0, agent_adapter_setup_1.inspectAgentInstructions)({ target, repoRoot });
236
+ const write = options.write
237
+ ? (0, agent_adapter_setup_1.writeAgentSetup)({
238
+ target,
239
+ repoRoot,
240
+ global: options.global === true,
241
+ })
242
+ : {
243
+ status: 'not_requested',
244
+ configPath: snippet.configPath,
245
+ message: 'Run with --write to update the known adapter config automatically.',
246
+ };
247
+ const instructionWrite = options.writeInstructions
248
+ ? (0, agent_adapter_setup_1.writeAgentInstructions)({ target, repoRoot })
249
+ : {
250
+ status: 'not_requested',
251
+ filePath: instructionArtifact.filePath,
252
+ message: 'Run with --write-instructions to add repo-native agent runtime instructions.',
253
+ };
254
+ const after = (0, agent_adapter_setup_1.inspectAgentSetup)({
255
+ target,
256
+ repoRoot,
257
+ global: options.global === true,
258
+ });
259
+ const instructionsAfter = (0, agent_adapter_setup_1.inspectAgentInstructions)({ target, repoRoot });
260
+ const goal = options.goal || 'modify one named safe file and keep protected boundaries untouched';
261
+ return {
262
+ schemaVersion: agent_adapter_setup_1.AGENT_ADAPTER_SETUP_SCHEMA_VERSION,
263
+ ok: true,
264
+ generatedAt: new Date().toISOString(),
265
+ repoRoot,
266
+ privacy: {
267
+ metadataOnly: true,
268
+ sourceUploaded: false,
269
+ sourceIncluded: false,
270
+ },
271
+ target,
272
+ adapter: snippet.adapter,
273
+ dashboardPairing,
274
+ enforcement: {
275
+ level: capability?.enforcementLevel ?? 'cooperative',
276
+ controlLevel: capability?.controlLevel ?? 'unsupported_unknown',
277
+ automatic: capability?.automatic ?? false,
278
+ description: capability?.description ?? 'Portable MCP runtime adapter.',
279
+ },
280
+ profile: {
281
+ status: profile.status,
282
+ refreshed: profile.refreshed,
283
+ profileHash: profile.profile.profileHash,
284
+ topologyHash: profile.profile.topology.hash,
285
+ trackedFileCount: profile.profile.topology.trackedFileCount,
286
+ reasons: [...profile.reasons],
287
+ },
288
+ mcp: {
289
+ before,
290
+ after,
291
+ snippet,
292
+ write,
293
+ },
294
+ instructions: {
295
+ before: instructionsBefore,
296
+ after: instructionsAfter,
297
+ artifact: instructionArtifact,
298
+ write: instructionWrite,
299
+ },
300
+ firstRun: {
301
+ goal,
302
+ commands: firstRunCommands({ target, adapter: snippet.adapter, goal }),
303
+ expectedFlow: [
304
+ dashboardPairing.status === 'connected'
305
+ ? 'dashboard receives source-free live session/evidence updates'
306
+ : 'local-only until the repo is paired from Runtime Control Plane',
307
+ 'start governed session',
308
+ 'agent handshakes into session',
309
+ 'agent captures source-free plan',
310
+ 'agent calls check before each write',
311
+ 'human approves exact path only when needed',
312
+ 'session finishes with replayable evidence',
313
+ ],
314
+ },
315
+ };
316
+ }
317
+ function statusLabel(status) {
318
+ if (status === 'pass')
319
+ return chalk.green('PASS');
320
+ if (status === 'warn')
321
+ return chalk.yellow('WARN');
322
+ if (status === 'fail')
323
+ return chalk.red('FAIL');
324
+ return chalk.dim('SKIP');
325
+ }
326
+ function renderSetup(payload) {
327
+ console.log('');
328
+ console.log(chalk.bold(`Neurcode agent setup - ${payload.target}`));
329
+ console.log(chalk.dim('-'.repeat(72)));
330
+ console.log(`Repo: ${chalk.white(payload.repoRoot)}`);
331
+ console.log(`Adapter: ${payload.adapter} ${chalk.dim(`(${payload.enforcement.level}${payload.enforcement.automatic ? ', automatic' : ', explicit'})`)}`);
332
+ console.log(`Control: ${controlLevelLabel(payload.enforcement.controlLevel)}`);
333
+ console.log(`Profile: ${payload.profile.refreshed ? chalk.green('refreshed') : chalk.green(payload.profile.status)} ${chalk.dim(payload.profile.profileHash)}`);
334
+ renderDashboardPairing(payload.dashboardPairing);
335
+ console.log(`MCP: ${payload.mcp.after.configured === true ? chalk.green('configured') : payload.mcp.after.configured === false ? chalk.yellow('not configured') : chalk.dim('manual')}`);
336
+ console.log(`Rules: ${payload.instructions.after.installed === true ? chalk.green('installed') : payload.instructions.after.installed === false ? chalk.yellow('missing') : chalk.dim('optional')}`);
337
+ if (payload.mcp.snippet.configPath) {
338
+ console.log(`Config: ${chalk.white(payload.mcp.snippet.configPath)}`);
339
+ }
340
+ console.log(`Write: ${payload.mcp.write.status} ${chalk.dim(payload.mcp.write.message)}`);
341
+ console.log(`Instr: ${payload.instructions.write.status} ${chalk.dim(payload.instructions.write.message)}`);
342
+ console.log(chalk.dim('-'.repeat(72)));
343
+ console.log(chalk.bold('MCP config'));
344
+ console.log(chalk.dim(`# Destination: ${payload.mcp.snippet.destination}`));
345
+ console.log(chalk.dim(`# Action: ${payload.mcp.snippet.instruction}`));
346
+ console.log(payload.mcp.snippet.body.trimEnd());
347
+ console.log('');
348
+ console.log(chalk.bold('Agent instructions'));
349
+ console.log(chalk.dim(`# Destination: ${payload.instructions.artifact.destination}`));
350
+ console.log(chalk.dim(`# Action: ${payload.instructions.artifact.instruction}`));
351
+ console.log(payload.instructions.artifact.body.trimEnd());
352
+ console.log('');
353
+ console.log(chalk.bold('First governed session'));
354
+ for (const [name, command] of Object.entries(payload.firstRun.commands)) {
355
+ console.log(chalk.dim(` ${name.padEnd(10)} ${command}`));
356
+ }
357
+ console.log('');
358
+ }
359
+ function buildWalkthroughPayload(agentArg, options) {
360
+ const repoRoot = repoRootFrom({ dir: options.dir });
361
+ const target = (0, agent_adapter_setup_1.normalizeAgentSetupTarget)(agentArg);
362
+ const adapter = AGENT_TO_ADAPTER[target] ?? 'generic-mcp';
363
+ const dashboardPairing = dashboardPairingSummary(repoRoot);
364
+ const goal = options.goal || 'Modify one named file and keep protected boundaries untouched';
365
+ const safePath = options.safePath || '<safe-path-inside-task-scope>';
366
+ const blockedPath = options.blockedPath || '<approval-required-path>';
367
+ const session = '<session-id>';
368
+ const steps = [
369
+ {
370
+ id: 'connect',
371
+ title: 'Connect repo',
372
+ outcome: dashboardPairing.status === 'connected'
373
+ ? `Already paired to Runtime Control Plane as ${dashboardPairing.repoName}; source-free runtime sync is available.`
374
+ : 'Pair this checkout before the pilot if you want live sessions, approval requests, and evidence to appear in Runtime Control Plane.',
375
+ command: dashboardPairing.status === 'connected'
376
+ ? 'Already connected. Continue to bootstrap.'
377
+ : 'Open Runtime Control Plane -> Connect repo, then run the copied short-lived pairing command from this repository.',
378
+ },
379
+ {
380
+ id: 'bootstrap',
381
+ title: 'Bootstrap agent',
382
+ outcome: 'MCP config, repo-native agent instructions, and governance profile are ready.',
383
+ command: `neurcode agent bootstrap ${target}`,
384
+ },
385
+ {
386
+ id: 'start',
387
+ title: 'Start guarded task',
388
+ outcome: 'Intent, plan, allowed scope, guard baseline, and live runtime transport are created.',
389
+ command: `neurcode agent guard start ${target} --goal "${goal}" --plan "<source-free plan>"`,
390
+ },
391
+ {
392
+ id: 'safe-check',
393
+ title: 'Check safe path',
394
+ outcome: 'The intended file is allowed before write and later counted as verified.',
395
+ command: `neurcode agent check ${safePath} --adapter ${adapter} --session-id ${session}`,
396
+ },
397
+ {
398
+ id: 'boundary-block',
399
+ title: 'See boundary block',
400
+ outcome: dashboardPairing.status === 'connected'
401
+ ? 'A protected path is denied and appears in Runtime Control Plane with owners and exact approval path.'
402
+ : 'A protected path is denied locally. It will not appear in Runtime Control Plane until this repo is paired.',
403
+ command: `neurcode agent check ${blockedPath} --adapter ${adapter} --session-id ${session}`,
404
+ },
405
+ {
406
+ id: 'approve',
407
+ title: 'Approve exact path',
408
+ outcome: 'Only the suggested file is granted for this session; sibling files stay blocked.',
409
+ command: `Approve ${blockedPath} in Runtime Control Plane, or run neurcode agent approve ${blockedPath} --session-id ${session}`,
410
+ },
411
+ {
412
+ id: 'finish',
413
+ title: 'Finish and report',
414
+ outcome: 'The guard closes, replay integrity is minted, and the post-run report says whether the run is pilot-ready.',
415
+ command: `neurcode agent guard finish --session-id ${session} --fail-on-unverified && neurcode agent report ${target} --session-id ${session}`,
416
+ },
417
+ {
418
+ id: 'evidence',
419
+ title: 'Open evidence record',
420
+ outcome: dashboardPairing.status === 'connected'
421
+ ? 'The dashboard shows source-free intent, plan, changed-path facts, approvals, contained denials, structural consequences, and receipt hashes.'
422
+ : 'Local AI Change Record JSON is written; pair and sync the repo before expecting dashboard evidence.',
423
+ command: dashboardPairing.status === 'connected'
424
+ ? 'Open Runtime Evidence from the Control Plane, or export the AI Change Record JSON.'
425
+ : 'Run the dashboard pairing command, then `neurcode sync --runtime` for finished local evidence.',
426
+ },
427
+ ];
428
+ return {
429
+ schemaVersion: 'neurcode.agent-walkthrough.v1',
430
+ ok: true,
431
+ generatedAt: new Date().toISOString(),
432
+ repoRoot,
433
+ target,
434
+ adapter,
435
+ dashboardPairing,
436
+ goal,
437
+ guarantee: {
438
+ mode: readinessMode(capabilityFor(adapter)?.enforcementLevel),
439
+ controlLabel: controlLevelLabel(capabilityFor(adapter)?.controlLevel),
440
+ },
441
+ steps,
442
+ acceptance: [
443
+ dashboardPairing.status === 'connected'
444
+ ? 'Runtime Control Plane shows the live session and evidence'
445
+ : 'local-only status is explicit before the run starts',
446
+ 'safe path is allowed before write',
447
+ 'approval-required path is denied before or during write',
448
+ 'exact approval does not grant directory or sibling files',
449
+ 'agent guard finish reports zero unverified writes',
450
+ 'agent report shows no open blocks and includes replay/record hashes',
451
+ 'dashboard evidence remains source-free',
452
+ ],
453
+ privacy: {
454
+ metadataOnly: true,
455
+ sourceUploaded: false,
456
+ sourceIncluded: false,
457
+ },
458
+ };
459
+ }
460
+ function renderWalkthrough(payload) {
461
+ console.log('');
462
+ console.log(chalk.bold(`Neurcode self-serve pilot walkthrough - ${payload.target}`));
463
+ console.log(chalk.dim('-'.repeat(72)));
464
+ console.log(`Repo: ${chalk.white(payload.repoRoot)}`);
465
+ console.log(`Adapter: ${payload.adapter}`);
466
+ console.log(`Guarantee: ${payload.guarantee.controlLabel} · ${payload.guarantee.mode}`);
467
+ renderDashboardPairing(payload.dashboardPairing);
468
+ console.log('');
469
+ payload.steps.forEach((step, index) => {
470
+ console.log(chalk.bold(`${index + 1}. ${step.title}`));
471
+ console.log(chalk.dim(` Outcome: ${step.outcome}`));
472
+ console.log(chalk.dim(` ${step.command}`));
473
+ });
474
+ console.log('');
475
+ console.log(chalk.bold('Acceptance'));
476
+ payload.acceptance.forEach((item) => console.log(chalk.dim(` - ${item}`)));
477
+ console.log('');
478
+ }
479
+ function npxCheck() {
480
+ const result = (0, node_child_process_1.spawnSync)('npx', ['--version'], { encoding: 'utf8' });
481
+ if (result.status === 0) {
482
+ return {
483
+ id: 'npx',
484
+ label: 'npx',
485
+ status: 'pass',
486
+ message: `npx ${String(result.stdout || '').trim()} is available for zero-install MCP startup.`,
487
+ };
488
+ }
489
+ return {
490
+ id: 'npx',
491
+ label: 'npx',
492
+ status: 'fail',
493
+ message: 'npx is not available, so agent MCP clients cannot start @neurcode-ai/mcp-server with the generated config.',
494
+ recommendation: 'Install Node.js >=18 or configure your MCP client to use a locally installed neurcode-mcp binary.',
495
+ };
496
+ }
497
+ function configCheck(inspection) {
498
+ if (inspection.configured === true) {
499
+ return {
500
+ id: 'mcp_config',
501
+ label: 'MCP config',
502
+ status: 'pass',
503
+ message: inspection.message,
504
+ };
505
+ }
506
+ if (inspection.configured === false) {
507
+ return {
508
+ id: 'mcp_config',
509
+ label: 'MCP config',
510
+ status: 'warn',
511
+ message: inspection.message,
512
+ recommendation: `Run neurcode agent bootstrap ${inspection.target}, or paste the emitted snippet manually.`,
513
+ };
514
+ }
515
+ return {
516
+ id: 'mcp_config',
517
+ label: 'MCP config',
518
+ status: 'skip',
519
+ message: inspection.message,
520
+ };
521
+ }
522
+ function instructionCheck(inspection) {
523
+ if (inspection.installed === true) {
524
+ return {
525
+ id: 'agent_instructions',
526
+ label: 'Agent instructions',
527
+ status: 'pass',
528
+ message: inspection.message,
529
+ };
530
+ }
531
+ if (inspection.installed === false) {
532
+ return {
533
+ id: 'agent_instructions',
534
+ label: 'Agent instructions',
535
+ status: 'warn',
536
+ message: inspection.message,
537
+ recommendation: `Run neurcode agent bootstrap ${inspection.target}, or paste the emitted instruction block manually.`,
538
+ };
539
+ }
540
+ return {
541
+ id: 'agent_instructions',
542
+ label: 'Agent instructions',
543
+ status: 'skip',
544
+ message: inspection.message,
545
+ };
546
+ }
547
+ function buildDoctorPayload(agentArg, options) {
548
+ const repoRoot = repoRootFrom({ dir: options.dir });
549
+ const target = (0, agent_adapter_setup_1.normalizeAgentSetupTarget)(agentArg);
550
+ const inspection = (0, agent_adapter_setup_1.inspectAgentSetup)({
551
+ target,
552
+ repoRoot,
553
+ global: options.global === true,
554
+ });
555
+ const instructionInspection = (0, agent_adapter_setup_1.inspectAgentInstructions)({ target, repoRoot });
556
+ const staleness = (0, v0_governance_1.getProfileStaleness)(repoRoot);
557
+ const activeSession = (0, governance_runtime_1.loadActiveSession)(repoRoot);
558
+ const capability = capabilityFor(inspection.adapter);
559
+ const needsNpx = target === 'codex' || target === 'cursor' || target === 'generic-mcp';
560
+ const checks = [
561
+ {
562
+ id: 'adapter',
563
+ label: 'Adapter capability',
564
+ status: 'pass',
565
+ message: `${inspection.adapter} reports ${controlLevelLabel(capability?.controlLevel)} (${capability?.enforcementLevel ?? 'cooperative'}, ${capability?.automatic ? 'automatic' : 'explicit agent calls'}).`,
566
+ },
567
+ configCheck(inspection),
568
+ instructionCheck(instructionInspection),
569
+ ...(needsNpx ? [npxCheck()] : []),
570
+ {
571
+ id: 'profile',
572
+ label: 'Governance profile',
573
+ status: staleness.status === 'fresh' ? 'pass' : 'warn',
574
+ message: staleness.status === 'fresh'
575
+ ? `Fresh profile at ${staleness.profilePath} (${staleness.currentProfile.topology.trackedFileCount} tracked files).`
576
+ : `${staleness.status}: ${staleness.reasons.join('; ') || 'profile needs refresh'}.`,
577
+ recommendation: staleness.status === 'fresh' ? undefined : `Run neurcode agent bootstrap ${target}.`,
578
+ },
579
+ {
580
+ id: 'active_session',
581
+ label: 'Active session',
582
+ status: activeSession?.status === 'active' ? 'pass' : 'skip',
583
+ message: activeSession?.status === 'active'
584
+ ? `Session ${activeSession.sessionId} is active (${activeSession.contract.scopeMode} scope).`
585
+ : 'No active governed session. Start one with neurcode agent start.',
586
+ },
587
+ ];
588
+ const summary = {
589
+ pass: checks.filter((item) => item.status === 'pass').length,
590
+ warn: checks.filter((item) => item.status === 'warn').length,
591
+ fail: checks.filter((item) => item.status === 'fail').length,
592
+ skip: checks.filter((item) => item.status === 'skip').length,
593
+ };
594
+ return {
595
+ schemaVersion: agent_adapter_setup_1.AGENT_ADAPTER_DOCTOR_SCHEMA_VERSION,
596
+ ok: summary.fail === 0,
597
+ generatedAt: new Date().toISOString(),
598
+ repoRoot,
599
+ target,
600
+ adapter: inspection.adapter,
601
+ controlLevel: capability?.controlLevel ?? 'unsupported_unknown',
602
+ controlLabel: controlLevelLabel(capability?.controlLevel),
603
+ enforcement: {
604
+ level: capability?.enforcementLevel ?? 'unknown',
605
+ automatic: capability?.automatic ?? false,
606
+ compatibilityMode: capability?.compatibilityMode ?? 'unknown',
607
+ enforceable: capability?.enforceable ?? [],
608
+ advisoryOnly: capability?.advisoryOnly ?? [],
609
+ },
610
+ checks,
611
+ summary,
612
+ next: summary.fail > 0
613
+ ? 'Resolve failed checks before relying on agent MCP calls.'
614
+ : inspection.configured === false
615
+ ? `Run neurcode agent bootstrap ${target} or paste the MCP snippet.`
616
+ : instructionInspection.installed === false
617
+ ? `Run neurcode agent bootstrap ${target} or paste the agent instruction block.`
618
+ : `Run neurcode agent start ${target} --goal "<task>".`,
619
+ };
620
+ }
621
+ function renderDoctor(payload) {
622
+ console.log('');
623
+ console.log(chalk.bold(`Neurcode agent doctor - ${payload.target}`));
624
+ console.log(chalk.dim('-'.repeat(72)));
625
+ console.log(`Repo: ${chalk.white(payload.repoRoot)}`);
626
+ console.log(`Adapter: ${payload.adapter}`);
627
+ console.log(`Control: ${payload.controlLabel}`);
628
+ console.log(`Mode: ${payload.enforcement.compatibilityMode.replace(/_/g, ' ')} · ${payload.enforcement.automatic ? 'automatic' : 'explicit/cooperative'}`);
629
+ console.log('');
630
+ console.log(chalk.bold('Enforced / recorded'));
631
+ for (const item of payload.enforcement.enforceable) {
632
+ console.log(chalk.dim(` - ${item}`));
633
+ }
634
+ if (payload.enforcement.enforceable.length === 0)
635
+ console.log(chalk.dim(' - none reported by this adapter'));
636
+ console.log(chalk.bold('Advisory / not claimed'));
637
+ for (const item of payload.enforcement.advisoryOnly) {
638
+ console.log(chalk.dim(` - ${item}`));
639
+ }
640
+ if (payload.enforcement.advisoryOnly.length === 0)
641
+ console.log(chalk.dim(' - none reported by this adapter'));
642
+ console.log('');
643
+ for (const check of payload.checks) {
644
+ console.log(`${statusLabel(check.status)} ${chalk.bold(check.label)}`);
645
+ console.log(chalk.dim(` ${check.message}`));
646
+ if (check.recommendation)
647
+ console.log(chalk.dim(` Next: ${check.recommendation}`));
648
+ console.log('');
649
+ }
650
+ console.log(chalk.dim('-'.repeat(72)));
651
+ console.log(`Pass ${payload.summary.pass} | Warn ${payload.summary.warn} | Fail ${payload.summary.fail} | Skip ${payload.summary.skip}`);
652
+ console.log(chalk.dim(`Next: ${payload.next}`));
653
+ console.log('');
654
+ }
655
+ function readinessCheck(id, label, status, message, recommendation) {
656
+ return { id, label, status, message, ...(recommendation ? { recommendation } : {}) };
657
+ }
658
+ function readinessMode(enforcementLevel) {
659
+ if (enforcementLevel === 'hard_deny')
660
+ return 'hard pre-write deny';
661
+ if (enforcementLevel === 'cooperative')
662
+ return 'cooperative pre-write checks + local guard supervision';
663
+ if (enforcementLevel === 'observe_only')
664
+ return 'observe-only companion visibility';
665
+ if (enforcementLevel === 'post_change_backstop')
666
+ return 'post-change backstop';
667
+ return 'unknown runtime guarantee';
668
+ }
669
+ function buildReadinessPayload(agentArg, options) {
670
+ const doctor = buildDoctorPayload(agentArg, options);
671
+ const repoRoot = doctor.repoRoot;
672
+ const target = doctor.target;
673
+ const adapter = doctor.adapter;
674
+ const capability = capabilityFor(adapter);
675
+ const session = options.sessionId
676
+ ? (0, governance_runtime_1.loadSession)(repoRoot, options.sessionId)
677
+ : (0, governance_runtime_1.loadActiveSession)(repoRoot);
678
+ const invocation = session ? (0, governance_runtime_1.buildAgentInvocationSummary)(session) : null;
679
+ const guardPosture = session ? (0, governance_runtime_1.buildAgentGuardPostureSummary)(session) : null;
680
+ const supervisor = session ? (0, agent_guard_supervisor_1.inspectAgentGuardSupervisor)(repoRoot, session.sessionId) : null;
681
+ const isCooperative = capability?.enforcementLevel === 'cooperative';
682
+ const isObserveOnly = capability?.enforcementLevel === 'observe_only';
683
+ const isHardDeny = capability?.enforcementLevel === 'hard_deny';
684
+ const checks = [
685
+ ...doctor.checks.map((check) => ({ ...check })),
686
+ readinessCheck('active_governed_session', 'Active governed session', session?.status === 'active' ? 'pass' : 'warn', session
687
+ ? `Session ${session.sessionId} is ${session.status} (${session.contract.scopeMode} scope).`
688
+ : 'No governed session is active in this repository.', `Run neurcode agent guard start ${target} --goal "<task>" for supervised agent work.`),
689
+ ];
690
+ if (session && invocation) {
691
+ checks.push(readinessCheck('agent_launch_binding', 'Agent launch binding', invocation.launched && invocation.adapter === adapter
692
+ ? 'pass'
693
+ : invocation.launched
694
+ ? 'warn'
695
+ : 'warn', invocation.launched
696
+ ? `Session is bound to ${invocation.adapter ?? 'unknown adapter'} (${invocation.enforcementLevel ?? 'unknown'}).`
697
+ : 'Session has no agent launch marker.', invocation.launched && invocation.adapter === adapter
698
+ ? undefined
699
+ : `Start this workflow with neurcode agent guard start ${target} --goal "<task>".`));
700
+ checks.push(readinessCheck('agent_protocol', 'Agent protocol', invocation.status === 'following_contract' || invocation.status === 'finished' || invocation.status === 'observe_only'
701
+ ? 'pass'
702
+ : invocation.status === 'attention_needed'
703
+ ? 'fail'
704
+ : 'warn', `${invocation.status.replace(/_/g, ' ')} · ${invocation.nextAction}`, invocation.nextAction));
705
+ }
706
+ else {
707
+ checks.push(readinessCheck('agent_launch_binding', 'Agent launch binding', 'warn', 'No active session exists to prove agent launch binding.', `Run neurcode agent guard start ${target} --goal "<task>".`));
708
+ checks.push(readinessCheck('agent_protocol', 'Agent protocol', 'skip', 'Protocol checks require an active governed session.'));
709
+ }
710
+ if (isCooperative) {
711
+ const guardStatus = guardPosture?.status ?? 'not_started';
712
+ checks.push(readinessCheck('guard_posture', 'Local guard posture', guardStatus === 'following_contract' || guardStatus === 'finished_clean'
713
+ ? 'pass'
714
+ : guardStatus === 'attention_required' || guardStatus === 'finished_attention'
715
+ ? 'fail'
716
+ : 'warn', guardPosture
717
+ ? `${guardStatus.replace(/_/g, ' ')} · ${guardPosture.nextAction}`
718
+ : 'No local guard posture was found.', guardStatus === 'following_contract' || guardStatus === 'finished_clean'
719
+ ? undefined
720
+ : `Use neurcode agent guard start ${target} --goal "<task>" and keep the supervisor running during agent work.`));
721
+ checks.push(readinessCheck('guard_supervisor', 'Guard supervisor', supervisor?.effectiveStatus === 'running'
722
+ ? 'pass'
723
+ : supervisor?.effectiveStatus === 'stale' || supervisor?.effectiveStatus === 'failed'
724
+ ? 'fail'
725
+ : 'warn', supervisor?.state
726
+ ? `Supervisor is ${supervisor.effectiveStatus} for session ${supervisor.state.sessionId}.`
727
+ : 'No detached supervisor state exists for this session.', supervisor?.effectiveStatus === 'running'
728
+ ? undefined
729
+ : 'Start it with neurcode agent guard supervise start, or launch through neurcode agent guard start.'));
730
+ }
731
+ else if (isObserveOnly) {
732
+ checks.push(readinessCheck('guard_posture', 'Local guard posture', guardPosture?.status === 'attention_required' || guardPosture?.status === 'finished_attention'
733
+ ? 'fail'
734
+ : 'warn', 'VS Code is observe-only; pair it with Codex/Cursor/MCP guard supervision for write accountability.', 'Use VS Code as the companion surface, then launch the real agent workflow from the Runtime Companion or CLI.'));
735
+ }
736
+ else if (isHardDeny) {
737
+ checks.push(readinessCheck('guard_posture', 'Local guard posture', 'skip', 'Claude Code hooks provide the hard-deny layer; guard supervision is optional as a bypass audit.'));
738
+ }
739
+ checks.push(readinessCheck('source_free_contract', 'Source-free contract', 'pass', 'Readiness uses paths, config presence, runtime metadata, guard posture, and integrity hashes only.'));
740
+ const summary = {
741
+ pass: checks.filter((item) => item.status === 'pass').length,
742
+ warn: checks.filter((item) => item.status === 'warn').length,
743
+ fail: checks.filter((item) => item.status === 'fail').length,
744
+ skip: checks.filter((item) => item.status === 'skip').length,
745
+ };
746
+ const nextActions = checks
747
+ .filter((item) => item.status === 'fail' || item.status === 'warn')
748
+ .map((item) => item.recommendation || item.message)
749
+ .filter(Boolean)
750
+ .slice(0, 8);
751
+ const readiness = summary.fail > 0
752
+ ? 'not_ready'
753
+ : summary.warn > 0
754
+ ? 'needs_attention'
755
+ : 'ready';
756
+ return {
757
+ schemaVersion: 'neurcode.agent-readiness.v1',
758
+ ok: summary.fail === 0 && (options.strict !== true || summary.warn === 0),
759
+ generatedAt: new Date().toISOString(),
760
+ repoRoot,
761
+ target,
762
+ adapter,
763
+ readiness,
764
+ guarantee: {
765
+ enforcementLevel: capability?.enforcementLevel ?? 'unknown',
766
+ controlLevel: capability?.controlLevel ?? 'unsupported_unknown',
767
+ controlLabel: controlLevelLabel(capability?.controlLevel),
768
+ automatic: capability?.automatic ?? false,
769
+ mode: readinessMode(capability?.enforcementLevel),
770
+ truthfulClaim: isCooperative
771
+ ? 'Agent writes are governed when the agent calls MCP/CLI pre-write checks; guard supervision detects bypassed writes.'
772
+ : isObserveOnly
773
+ ? 'VS Code provides operator visibility and approval UX, not editor-level hard deny.'
774
+ : isHardDeny
775
+ ? 'Claude Code hooks can deny supported write tools before the write lands.'
776
+ : capability?.description ?? 'Runtime guarantee is unknown.',
777
+ },
778
+ pilotReady: readiness === 'ready' || (readiness === 'needs_attention' && summary.fail === 0 && !isCooperative),
779
+ session: session
780
+ ? {
781
+ sessionId: session.sessionId,
782
+ status: session.status,
783
+ scopeMode: session.contract.scopeMode,
784
+ allowedGlobs: session.contract.allowedGlobs,
785
+ approvalRequiredGlobs: session.contract.approvalRequiredGlobs,
786
+ replayHash: session.replayHash,
787
+ }
788
+ : null,
789
+ invocation,
790
+ guardPosture,
791
+ supervisor: supervisor
792
+ ? {
793
+ exists: supervisor.exists,
794
+ alive: supervisor.alive,
795
+ effectiveStatus: supervisor.effectiveStatus,
796
+ statePath: supervisor.statePath,
797
+ state: supervisor.state,
798
+ error: supervisor.error,
799
+ }
800
+ : null,
801
+ checks,
802
+ summary,
803
+ nextActions,
804
+ privacy: {
805
+ metadataOnly: true,
806
+ sourceUploaded: false,
807
+ sourceIncluded: false,
808
+ localContentDigestsOnly: true,
809
+ },
810
+ };
811
+ }
812
+ function renderReadiness(payload) {
813
+ console.log('');
814
+ console.log(chalk.bold(`Neurcode agent readiness - ${payload.target}`));
815
+ console.log(chalk.dim('-'.repeat(72)));
816
+ console.log(`Repo: ${chalk.white(payload.repoRoot)}`);
817
+ console.log(`Adapter: ${payload.adapter}`);
818
+ console.log(`Guarantee: ${payload.guarantee.controlLabel} · ${payload.guarantee.mode}`);
819
+ console.log(`Readiness: ${payload.readiness === 'ready' ? chalk.green(payload.readiness) : payload.readiness === 'not_ready' ? chalk.red(payload.readiness) : chalk.yellow(payload.readiness)}`);
820
+ console.log(`Pilot: ${payload.pilotReady ? chalk.green('ready enough to test') : chalk.yellow('needs attention')}`);
821
+ if (payload.session)
822
+ console.log(`Session: ${chalk.cyan(payload.session.sessionId)} (${payload.session.status})`);
823
+ console.log('');
824
+ for (const check of payload.checks) {
825
+ console.log(`${statusLabel(check.status)} ${chalk.bold(check.label)}`);
826
+ console.log(chalk.dim(` ${check.message}`));
827
+ if (check.recommendation)
828
+ console.log(chalk.dim(` Next: ${check.recommendation}`));
829
+ }
830
+ if (payload.nextActions.length > 0) {
831
+ console.log('');
832
+ console.log(chalk.bold('Next actions'));
833
+ payload.nextActions.forEach((action, index) => console.log(chalk.dim(` ${index + 1}. ${action}`)));
834
+ }
835
+ console.log('');
836
+ }
837
+ function latestLocalSession(repoRoot) {
838
+ return (0, runtime_evidence_1.listRuntimeSessions)(repoRoot)[0]?.session ?? null;
839
+ }
840
+ function resolveReportSession(repoRoot, options) {
841
+ if (options.sessionId)
842
+ return (0, governance_runtime_1.loadSession)(repoRoot, options.sessionId);
843
+ if (options.latest)
844
+ return latestLocalSession(repoRoot);
845
+ return (0, governance_runtime_1.loadActiveSession)(repoRoot) ?? latestLocalSession(repoRoot);
846
+ }
847
+ function recordAppliedApprovalPaths(record) {
848
+ return new Set(record.approvals
849
+ .filter((approval) => approval.status === 'active')
850
+ .map((approval) => approval.path));
851
+ }
852
+ function containedDenialPaths(record) {
853
+ if (record.session.status !== 'finished')
854
+ return [];
855
+ const approved = recordAppliedApprovalPaths(record);
856
+ return record.trajectory
857
+ .filter((entry) => entry.verdicts.includes('block') &&
858
+ !entry.verdicts.some((verdict) => verdict === 'ok' || verdict === 'warn'))
859
+ .map((entry) => entry.suggestedApprovalPath || entry.filePath)
860
+ .filter((path, index, paths) => !approved.has(path) && paths.indexOf(path) === index);
861
+ }
862
+ function unresolvedBriefBlockCount(record) {
863
+ const section = record.reviewBrief.sections.find((item) => item.id === 'governance_events');
864
+ const fact = section?.facts.find((item) => item.includes('without active approval') || item.includes('without applied approval'));
865
+ if (!fact || fact.startsWith('no '))
866
+ return 0;
867
+ const match = fact.match(/^(\d+)/);
868
+ return match ? Number(match[1]) : 1;
869
+ }
870
+ function isGovernedReviewReady(input) {
871
+ return input.record.session.status === 'finished'
872
+ && Boolean(input.record.integrity.replayHash)
873
+ && input.unresolvedBlockCount === 0
874
+ && input.record.reviewBrief.verdict === 'needs_human_inspection'
875
+ && (input.containedDenials.length > 0
876
+ || input.record.approvals.length > 0
877
+ || input.record.session.counts.warn > 0
878
+ || !input.guardClean);
879
+ }
880
+ function buildAgentReportPayload(agentArg, options) {
881
+ const repoRoot = repoRootFrom({ dir: options.dir });
882
+ const target = (0, agent_adapter_setup_1.normalizeAgentSetupTarget)(agentArg);
883
+ const adapter = AGENT_TO_ADAPTER[target] ?? 'generic-mcp';
884
+ const capability = capabilityFor(adapter);
885
+ const dashboardPairing = dashboardPairingSummary(repoRoot);
886
+ const session = resolveReportSession(repoRoot, options);
887
+ if (!session) {
888
+ return {
889
+ schemaVersion: 'neurcode.agent-report.v1',
890
+ ok: false,
891
+ generatedAt: new Date().toISOString(),
892
+ repoRoot,
893
+ target,
894
+ adapter,
895
+ dashboardPairing,
896
+ reportStatus: 'no_session',
897
+ pilotReady: false,
898
+ message: 'No active or completed governed session was found in this repository.',
899
+ nextActions: [
900
+ dashboardPairing.status === 'connected'
901
+ ? `Run neurcode agent guard start ${target} --goal "<task>".`
902
+ : 'Pair this repo from Runtime Control Plane if you want the pilot to appear in the dashboard.',
903
+ `Run neurcode agent guard start ${target} --goal "<task>".`,
904
+ ],
905
+ privacy: {
906
+ metadataOnly: true,
907
+ sourceUploaded: false,
908
+ sourceIncluded: false,
909
+ },
910
+ };
911
+ }
912
+ const { record, path } = options.writeRecord === false
913
+ ? { record: (0, governance_runtime_1.buildAIChangeRecord)(session), path: '<not-written>' }
914
+ : (0, governance_runtime_1.writeAIChangeRecord)(repoRoot, session);
915
+ const invocation = (0, governance_runtime_1.buildAgentInvocationSummary)(session);
916
+ const guardPosture = (0, governance_runtime_1.buildAgentGuardPostureSummary)(session);
917
+ const supervisor = (0, agent_guard_supervisor_1.inspectAgentGuardSupervisor)(repoRoot, session.sessionId);
918
+ const contained = containedDenialPaths(record);
919
+ const unresolvedBlockCount = unresolvedBriefBlockCount(record);
920
+ const guardClean = guardPosture.status === 'finished_clean' || guardPosture.status === 'following_contract';
921
+ const governedReviewReady = isGovernedReviewReady({
922
+ record,
923
+ containedDenials: contained,
924
+ unresolvedBlockCount,
925
+ guardClean,
926
+ });
927
+ const reportStatus = record.reviewBrief.verdict === 'blocked_unresolved' || unresolvedBlockCount > 0 || guardPosture.status === 'attention_required' || guardPosture.status === 'finished_attention'
928
+ ? 'attention_needed'
929
+ : !record.integrity.replayHash
930
+ ? 'in_progress'
931
+ : guardClean && record.session.status === 'finished'
932
+ ? 'pilot_ready'
933
+ : governedReviewReady
934
+ ? 'governed_review_ready'
935
+ : 'review_ready';
936
+ const nextActions = reportStatus === 'pilot_ready'
937
+ ? [
938
+ dashboardPairing.status === 'connected'
939
+ ? 'Open Runtime Evidence for human review, or export the AI Change Record.'
940
+ : 'Local pilot is clean; pair this repo from Runtime Control Plane and run `neurcode sync --runtime` before expecting dashboard evidence.',
941
+ ]
942
+ : reportStatus === 'governed_review_ready'
943
+ ? [
944
+ 'Open Runtime Evidence and review the contained approval / denial trail.',
945
+ guardClean
946
+ ? 'This governed run is complete; keep it as review evidence, not a failed run.'
947
+ : `For a green cooperative-agent pilot report, start the next run with neurcode agent guard start ${target} --goal "<task>" and finish with --fail-on-unverified.`,
948
+ ]
949
+ : reportStatus === 'attention_needed'
950
+ ? ['Resolve open blocked paths or unverified writes before presenting this run as clean pilot evidence.']
951
+ : record.session.status === 'active'
952
+ ? [`Finish with neurcode agent guard finish --session-id ${session.sessionId} --fail-on-unverified.`]
953
+ : ['Open the Runtime Evidence record and review remaining warnings.'];
954
+ return {
955
+ schemaVersion: 'neurcode.agent-report.v1',
956
+ ok: reportStatus !== 'attention_needed',
957
+ generatedAt: new Date().toISOString(),
958
+ repoRoot,
959
+ target,
960
+ adapter,
961
+ dashboardPairing,
962
+ reportStatus,
963
+ pilotReady: reportStatus === 'pilot_ready',
964
+ governedRunComplete: reportStatus === 'pilot_ready' || reportStatus === 'governed_review_ready',
965
+ humanReviewRequired: reportStatus === 'governed_review_ready'
966
+ || record.reviewBrief.verdict === 'needs_human_inspection',
967
+ guarantee: {
968
+ enforcementLevel: capability?.enforcementLevel ?? 'unknown',
969
+ controlLevel: capability?.controlLevel ?? 'unsupported_unknown',
970
+ controlLabel: controlLevelLabel(capability?.controlLevel),
971
+ mode: readinessMode(capability?.enforcementLevel),
972
+ },
973
+ session: {
974
+ sessionId: session.sessionId,
975
+ status: session.status,
976
+ goal: session.contract.goal,
977
+ scopeMode: session.contract.scopeMode,
978
+ startedAt: session.events[0]?.ts ?? null,
979
+ finishedAt: session.finishedAt ?? null,
980
+ },
981
+ reviewBrief: record.reviewBrief,
982
+ counts: {
983
+ ok: record.session.counts.ok,
984
+ warn: record.session.counts.warn,
985
+ block: record.session.counts.block,
986
+ events: record.session.counts.events,
987
+ approvals: record.approvals.length,
988
+ activeApprovals: record.approvals.filter((approval) => approval.status === 'active').length,
989
+ containedBoundaryDenials: contained.length,
990
+ unresolvedBlocks: unresolvedBlockCount,
991
+ },
992
+ invocation,
993
+ guardPosture,
994
+ supervisor: {
995
+ exists: supervisor.exists,
996
+ alive: supervisor.alive,
997
+ effectiveStatus: supervisor.effectiveStatus,
998
+ },
999
+ integrity: {
1000
+ replayHash: record.integrity.replayHash,
1001
+ recordHash: record.integrity.recordHash,
1002
+ recordPath: path === '<not-written>' ? path : path.replace(`${repoRoot}/`, ''),
1003
+ },
1004
+ dashboardEvidence: {
1005
+ status: dashboardPairing.status === 'connected' ? 'sync_available' : 'local_only',
1006
+ autoSyncEnabled: dashboardPairing.autoSyncEnabled,
1007
+ lastStatus: dashboardPairing.lastStatus ?? null,
1008
+ message: dashboardPairing.status === 'connected'
1009
+ ? 'Runtime Evidence should receive this source-free record through auto-sync or manual sync.'
1010
+ : 'This record is local-only until the repository is paired with Runtime Control Plane.',
1011
+ },
1012
+ containedBoundaryDenials: contained,
1013
+ nextActions,
1014
+ privacy: {
1015
+ metadataOnly: true,
1016
+ sourceUploaded: false,
1017
+ sourceIncluded: false,
1018
+ localContentDigestsOnly: true,
1019
+ },
1020
+ };
1021
+ }
1022
+ function renderAgentReport(payload) {
1023
+ console.log('');
1024
+ console.log(chalk.bold(`Neurcode agent report - ${payload.target}`));
1025
+ console.log(chalk.dim('-'.repeat(72)));
1026
+ console.log(`Repo: ${chalk.white(payload.repoRoot)}`);
1027
+ renderDashboardPairing(payload.dashboardPairing);
1028
+ const renderedStatus = payload.reportStatus === 'pilot_ready'
1029
+ ? chalk.green(payload.reportStatus)
1030
+ : payload.reportStatus === 'governed_review_ready'
1031
+ ? chalk.cyan(payload.reportStatus)
1032
+ : payload.reportStatus === 'attention_needed' || payload.reportStatus === 'no_session'
1033
+ ? chalk.red(payload.reportStatus)
1034
+ : chalk.yellow(payload.reportStatus);
1035
+ console.log(`Status: ${renderedStatus}`);
1036
+ if (payload.reportStatus === 'no_session') {
1037
+ console.log(payload.message);
1038
+ }
1039
+ else {
1040
+ const full = payload;
1041
+ console.log(`Session: ${chalk.cyan(full.session.sessionId)} (${full.session.status})`);
1042
+ if (full.reportStatus === 'governed_review_ready') {
1043
+ console.log(`Outcome: ${chalk.cyan('governed approval flow complete; human review recommended')}`);
1044
+ }
1045
+ console.log(`Verdict: ${full.reviewBrief.verdict}`);
1046
+ console.log(`Summary: ${full.reviewBrief.headline}`);
1047
+ console.log(`Counts: ok ${full.counts.ok} · warn ${full.counts.warn} · block ${full.counts.block} · approvals ${full.counts.approvals}`);
1048
+ console.log(`Control: contained ${full.counts.containedBoundaryDenials} · open blocks ${full.counts.unresolvedBlocks}`);
1049
+ console.log(`Guard: ${full.guardPosture.status.replace(/_/g, ' ')} · ${full.guardPosture.nextAction}`);
1050
+ console.log(`Record: ${full.integrity.recordPath}`);
1051
+ console.log(`Evidence:${full.dashboardEvidence.status === 'sync_available' ? chalk.green(' dashboard sync available') : chalk.yellow(' local-only')} ${chalk.dim(full.dashboardEvidence.message)}`);
1052
+ if (full.integrity.replayHash)
1053
+ console.log(`Replay: ${full.integrity.replayHash}`);
1054
+ }
1055
+ if (payload.nextActions.length > 0) {
1056
+ console.log('');
1057
+ console.log(chalk.bold('Next'));
1058
+ payload.nextActions.forEach((action, index) => console.log(chalk.dim(` ${index + 1}. ${action}`)));
1059
+ }
1060
+ console.log('');
1061
+ }
1062
+ function loadGuardSession(repoRoot, sessionId) {
1063
+ return sessionId ? (0, governance_runtime_1.loadSession)(repoRoot, sessionId) : (0, governance_runtime_1.loadActiveSession)(repoRoot);
1064
+ }
1065
+ function renderGuardStart(input) {
1066
+ console.log('');
1067
+ console.log(chalk.bold('Neurcode agent guard started'));
1068
+ console.log(chalk.dim('-'.repeat(72)));
1069
+ console.log(`Repo: ${chalk.white(input.launch.repoRoot)}`);
1070
+ console.log(`Session: ${chalk.cyan(input.launch.session.sessionId)}`);
1071
+ console.log(`Agent: ${input.launch.agent.normalized} -> ${input.launch.agent.adapter}`);
1072
+ console.log(`Guard: ${chalk.white(input.guardPath)}`);
1073
+ console.log(`Baseline: ${input.baselineFileCount} files ${chalk.dim(input.baselineTreeHash.slice(0, 12))}`);
1074
+ console.log(`Watch: ${input.supervisor?.enabled
1075
+ ? chalk.green(input.supervisor.started ? `running (pid ${input.supervisor.pid ?? 'pending'})` : input.supervisor.alreadyRunning ? `already running (pid ${input.supervisor.pid ?? 'unknown'})` : 'enabled')
1076
+ : chalk.dim('disabled')}`);
1077
+ console.log('');
1078
+ console.log(chalk.bold('Next'));
1079
+ console.log(chalk.dim(` neurcode agent handshake --adapter ${input.launch.agent.adapter} --session-id ${input.launch.session.sessionId}`));
1080
+ console.log(chalk.dim(` neurcode agent plan --adapter ${input.launch.agent.adapter} --session-id ${input.launch.session.sessionId} --plan "<source-free plan>"`));
1081
+ console.log(chalk.dim(` neurcode agent check <repo-relative-path> --adapter ${input.launch.agent.adapter} --session-id ${input.launch.session.sessionId}`));
1082
+ console.log(chalk.dim(` neurcode agent guard supervise status --session-id ${input.launch.session.sessionId}`));
1083
+ console.log('');
1084
+ console.log(chalk.dim('Guard truth: this catches bypassed or denied writes by comparing repo changes to runtime events. It is not hard-deny unless the agent host supports pre-write hooks.'));
1085
+ console.log('');
1086
+ }
1087
+ function classificationLabel(classification) {
1088
+ if (classification === 'verified_prewrite')
1089
+ return chalk.green('verified');
1090
+ if (classification === 'denied_but_changed')
1091
+ return chalk.red('denied-changed');
1092
+ if (classification === 'observed_after_only')
1093
+ return chalk.yellow('after-only');
1094
+ if (classification === 'prewrite_call_without_verdict')
1095
+ return chalk.yellow('no-verdict');
1096
+ return chalk.red('unverified');
1097
+ }
1098
+ function repoRelativeArtifactPath(repoRoot, artifactPath) {
1099
+ const root = repoRoot.replace(/\\/g, '/').replace(/\/+$/, '');
1100
+ const normalized = artifactPath.replace(/\\/g, '/');
1101
+ return normalized.startsWith(`${root}/`)
1102
+ ? normalized.slice(root.length + 1)
1103
+ : '<external-agent-guard-artifact>';
1104
+ }
1105
+ function guardEvaluationFingerprint(evaluation) {
1106
+ return (0, node_crypto_1.createHash)('sha256')
1107
+ .update(JSON.stringify({
1108
+ pass: evaluation.pass,
1109
+ status: evaluation.status,
1110
+ summary: evaluation.summary,
1111
+ changedFiles: evaluation.changedFiles.map((file) => ({
1112
+ path: file.path,
1113
+ changeType: file.changeType,
1114
+ classification: file.classification,
1115
+ evidence: file.evidence,
1116
+ })),
1117
+ }))
1118
+ .digest('hex')
1119
+ .slice(0, 24);
1120
+ }
1121
+ function guardEvaluationDetail(repoRoot, artifactPath, evaluation) {
1122
+ return {
1123
+ schemaVersion: 'neurcode.agent-guard-status.v1',
1124
+ guardId: evaluation.guardId,
1125
+ artifactPath: repoRelativeArtifactPath(repoRoot, artifactPath),
1126
+ reportFingerprint: guardEvaluationFingerprint(evaluation),
1127
+ pass: evaluation.pass,
1128
+ status: evaluation.status,
1129
+ summary: evaluation.summary,
1130
+ changedFiles: evaluation.changedFiles.slice(0, 100).map((file) => ({
1131
+ path: file.path,
1132
+ changeType: file.changeType,
1133
+ classification: file.classification,
1134
+ evidence: file.evidence,
1135
+ })),
1136
+ privacy: evaluation.privacy,
1137
+ };
1138
+ }
1139
+ function renderGuardEvaluation(evaluation, artifactPath) {
1140
+ console.log('');
1141
+ console.log(chalk.bold('Neurcode agent guard status'));
1142
+ console.log(chalk.dim('-'.repeat(72)));
1143
+ console.log(`Session: ${chalk.white(evaluation.sessionId)}`);
1144
+ console.log(`Agent: ${evaluation.agent} -> ${evaluation.adapter}`);
1145
+ console.log(`Guard: ${chalk.white(artifactPath)}`);
1146
+ console.log(`Status: ${evaluation.pass ? chalk.green('following contract') : chalk.red('attention required')}`);
1147
+ console.log('');
1148
+ console.log(`Changed ${evaluation.summary.changedFiles} · ` +
1149
+ `verified ${chalk.green(String(evaluation.summary.verifiedPrewrite))} · ` +
1150
+ `unverified ${evaluation.summary.unverifiedWrites > 0 ? chalk.red(String(evaluation.summary.unverifiedWrites)) : '0'} · ` +
1151
+ `denied-changed ${evaluation.summary.deniedButChanged > 0 ? chalk.red(String(evaluation.summary.deniedButChanged)) : '0'}`);
1152
+ if (evaluation.changedFiles.length > 0) {
1153
+ console.log('');
1154
+ for (const file of evaluation.changedFiles.slice(0, 12)) {
1155
+ console.log(`${classificationLabel(file.classification)} ${file.changeType.padEnd(8)} ${file.path} ` +
1156
+ chalk.dim(`(prewrite ${file.evidence.allowedPreWriteCheckCount}, denied ${file.evidence.deniedPreWriteCheckCount}, after ${file.evidence.postWriteObservationCount})`));
1157
+ }
1158
+ if (evaluation.changedFiles.length > 12) {
1159
+ console.log(chalk.dim(`... +${evaluation.changedFiles.length - 12} more`));
1160
+ }
1161
+ }
1162
+ console.log('');
1163
+ console.log(chalk.dim(`Next: ${evaluation.nextAction}`));
1164
+ console.log('');
1165
+ }
1166
+ async function publishGuardEvent(input) {
1167
+ const updated = (0, governance_runtime_1.appendEvent)(input.repoRoot, input.sessionId, input.event);
1168
+ if (updated) {
1169
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(input.repoRoot, updated);
1170
+ }
1171
+ }
1172
+ async function publishGuardEvaluation(input) {
1173
+ const session = (0, governance_runtime_1.loadSession)(input.repoRoot, input.evaluation.sessionId);
1174
+ if (!session)
1175
+ return;
1176
+ const detail = guardEvaluationDetail(input.repoRoot, input.artifactPath, input.evaluation);
1177
+ const latestStatus = [...session.events]
1178
+ .reverse()
1179
+ .find((event) => event.type === input.eventType);
1180
+ if (latestStatus?.detail?.reportFingerprint === detail.reportFingerprint) {
1181
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(input.repoRoot, session);
1182
+ return;
1183
+ }
1184
+ await publishGuardEvent({
1185
+ repoRoot: input.repoRoot,
1186
+ sessionId: input.evaluation.sessionId,
1187
+ event: {
1188
+ type: input.eventType,
1189
+ ts: input.evaluation.generatedAt,
1190
+ message: input.message,
1191
+ detail,
1192
+ },
1193
+ });
1194
+ }
1195
+ function requireGuardContext(input) {
1196
+ const repoRoot = repoRootFrom({ dir: input.dir });
1197
+ const guardRead = (0, agent_guard_1.readAgentGuardArtifact)({
1198
+ repoRoot,
1199
+ sessionId: input.sessionId,
1200
+ artifactPath: input.guardPath,
1201
+ });
1202
+ if (!guardRead.artifact) {
1203
+ throw new Error(guardRead.error || `Agent guard not found (${guardRead.path}).`);
1204
+ }
1205
+ const artifact = guardRead.artifact;
1206
+ const session = loadGuardSession(repoRoot, input.sessionId || artifact.sessionId);
1207
+ if (!session) {
1208
+ throw new Error(`Local governance session ${input.sessionId || artifact.sessionId} was not found.`);
1209
+ }
1210
+ return { repoRoot, guardRead, artifact, session };
1211
+ }
1212
+ async function evaluateAndPublishGuardStatus(input) {
1213
+ const { repoRoot, guardRead, artifact, session } = requireGuardContext(input);
1214
+ const evaluation = (0, agent_guard_1.evaluateAgentGuard)(repoRoot, artifact, session);
1215
+ await publishGuardEvaluation({
1216
+ repoRoot,
1217
+ artifactPath: guardRead.path,
1218
+ evaluation,
1219
+ eventType: 'agent_guard_status',
1220
+ message: evaluation.pass
1221
+ ? 'Agent guard status: changed files have allowed pre-write evidence.'
1222
+ : 'Agent guard status: attention required for unverified or denied writes.',
1223
+ });
1224
+ return { repoRoot, artifactPath: guardRead.path, evaluation };
1225
+ }
1226
+ async function recordSupervisorEvent(input) {
1227
+ await publishGuardEvent({
1228
+ repoRoot: input.repoRoot,
1229
+ sessionId: input.sessionId,
1230
+ event: {
1231
+ type: input.type,
1232
+ ts: new Date().toISOString(),
1233
+ message: input.message,
1234
+ detail: {
1235
+ schemaVersion: 'neurcode.agent-guard-supervisor.v1',
1236
+ ...input.detail,
1237
+ privacy: {
1238
+ metadataOnly: true,
1239
+ sourceUploaded: false,
1240
+ sourceIncluded: false,
1241
+ watchesPathsOnly: true,
1242
+ },
1243
+ },
1244
+ },
1245
+ });
1246
+ }
1247
+ function renderSupervisorInspection(inspection) {
1248
+ console.log('');
1249
+ console.log(chalk.bold('Neurcode agent guard supervisor'));
1250
+ console.log(chalk.dim('-'.repeat(72)));
1251
+ console.log(`State: ${chalk.white(inspection.statePath)}`);
1252
+ console.log(`Status: ${inspection.effectiveStatus}`);
1253
+ console.log(`Alive: ${inspection.alive ? chalk.green('yes') : chalk.dim('no')}`);
1254
+ if (inspection.state) {
1255
+ console.log(`Session: ${chalk.white(inspection.state.sessionId)}`);
1256
+ console.log(`PID: ${inspection.state.pid ?? 'none'}`);
1257
+ console.log(`Checks: ${inspection.state.evaluationCount}`);
1258
+ console.log(`Changed: ${inspection.state.lastChangedFiles}`);
1259
+ console.log(`Last: ${inspection.state.lastEvaluatedAt || 'not evaluated yet'}`);
1260
+ if (inspection.state.lastError)
1261
+ console.log(`Error: ${chalk.red(inspection.state.lastError)}`);
1262
+ }
1263
+ if (inspection.error)
1264
+ console.log(`Error: ${chalk.red(inspection.error)}`);
1265
+ console.log('');
1266
+ }
1267
+ function agentCommand(program) {
1268
+ const cmd = program
1269
+ .command('agent')
1270
+ .description('Universal local runtime ingress for AI coding agents');
1271
+ cmd
1272
+ .command('capabilities')
1273
+ .description('List agent adapters and their enforcement guarantees')
1274
+ .option('--json', 'Output machine-readable JSON')
1275
+ .action((options) => {
1276
+ const capabilities = (0, governance_runtime_1.listAgentRuntimeAdapterCapabilities)();
1277
+ if (options.json) {
1278
+ emitJson({ ok: true, capabilities });
1279
+ return;
1280
+ }
1281
+ for (const capability of capabilities) {
1282
+ console.log(`${capability.adapter.padEnd(18)} ${controlLevelLabel(capability.controlLevel).padEnd(28)} ${capability.enforcementLevel.padEnd(20)} ` +
1283
+ `${capability.automatic ? 'automatic' : 'explicit'} · ${capability.description}`);
1284
+ }
1285
+ });
1286
+ cmd
1287
+ .command('bootstrap [agent]')
1288
+ .description('One-command self-serve setup: refresh profile, write MCP config, and install agent instructions')
1289
+ .option('--dir <path>', 'Repository root (default: current directory)')
1290
+ .option('--goal <goal>', 'Example first governed task for generated commands')
1291
+ .option('--global', 'For Cursor, write/check ~/.cursor/mcp.json instead of repo-local .cursor/mcp.json')
1292
+ .option('--json', 'Output machine-readable JSON')
1293
+ .action((agent, options) => {
1294
+ try {
1295
+ const payload = buildSetupPayload(agent, {
1296
+ ...options,
1297
+ write: true,
1298
+ writeInstructions: true,
1299
+ forceProfile: true,
1300
+ });
1301
+ if (options.json)
1302
+ emitJson(payload);
1303
+ else
1304
+ renderSetup(payload);
1305
+ }
1306
+ catch (error) {
1307
+ emitError(error, options.json);
1308
+ }
1309
+ });
1310
+ cmd
1311
+ .command('walkthrough [agent]')
1312
+ .description('Print the self-serve pilot loop from repo pairing to AI Change Record')
1313
+ .option('--dir <path>', 'Repository root (default: current directory)')
1314
+ .option('--goal <goal>', 'Task goal to render in the guided commands')
1315
+ .option('--safe-path <path>', 'Example intended path that should be allowed')
1316
+ .option('--blocked-path <path>', 'Example approval-required path that should be blocked')
1317
+ .option('--json', 'Output machine-readable JSON')
1318
+ .action((agent, options) => {
1319
+ try {
1320
+ const payload = buildWalkthroughPayload(agent, options);
1321
+ if (options.json)
1322
+ emitJson(payload);
1323
+ else
1324
+ renderWalkthrough(payload);
1325
+ }
1326
+ catch (error) {
1327
+ emitError(error, options.json);
1328
+ }
1329
+ });
1330
+ cmd
1331
+ .command('setup [agent]')
1332
+ .description('Prepare MCP config and first-run commands for Codex, Cursor, Claude, or generic MCP')
1333
+ .option('--dir <path>', 'Repository root (default: current directory)')
1334
+ .option('--goal <goal>', 'Example first governed task for generated commands')
1335
+ .option('--write', 'Write known MCP config entries for Codex/Cursor')
1336
+ .option('--write-instructions', 'Write repo-native agent instructions (AGENTS.md, Cursor rules, or NEURCODE_AGENT.md)')
1337
+ .option('--global', 'For Cursor, write/check ~/.cursor/mcp.json instead of repo-local .cursor/mcp.json')
1338
+ .option('--force-profile', 'Force refresh the repo governance profile')
1339
+ .option('--json', 'Output machine-readable JSON')
1340
+ .action((agent, options) => {
1341
+ try {
1342
+ const payload = buildSetupPayload(agent, options);
1343
+ if (options.json)
1344
+ emitJson(payload);
1345
+ else
1346
+ renderSetup(payload);
1347
+ }
1348
+ catch (error) {
1349
+ emitError(error, options.json);
1350
+ }
1351
+ });
1352
+ cmd
1353
+ .command('doctor [agent]')
1354
+ .description('Check whether an agent adapter is ready to call the Neurcode runtime')
1355
+ .option('--dir <path>', 'Repository root (default: current directory)')
1356
+ .option('--global', 'For Cursor, check ~/.cursor/mcp.json instead of repo-local .cursor/mcp.json')
1357
+ .option('--json', 'Output machine-readable JSON')
1358
+ .action((agent, options) => {
1359
+ try {
1360
+ const payload = buildDoctorPayload(agent, options);
1361
+ if (options.json)
1362
+ emitJson(payload);
1363
+ else
1364
+ renderDoctor(payload);
1365
+ if (!payload.ok)
1366
+ process.exitCode = 1;
1367
+ }
1368
+ catch (error) {
1369
+ emitError(error, options.json);
1370
+ }
1371
+ });
1372
+ cmd
1373
+ .command('readiness [agent]')
1374
+ .description('Assess enterprise readiness for an agent workflow, including setup, session protocol, guard posture, and truthful enforcement level')
1375
+ .option('--dir <path>', 'Repository root (default: current directory)')
1376
+ .option('--session-id <id>', 'Local governance session ID (default: active session)')
1377
+ .option('--global', 'For Cursor, check ~/.cursor/mcp.json instead of repo-local .cursor/mcp.json')
1378
+ .option('--strict', 'Exit non-zero when any readiness warning remains')
1379
+ .option('--json', 'Output machine-readable JSON')
1380
+ .action((agent, options) => {
1381
+ try {
1382
+ const payload = buildReadinessPayload(agent, options);
1383
+ if (options.json)
1384
+ emitJson(payload);
1385
+ else
1386
+ renderReadiness(payload);
1387
+ if (!payload.ok)
1388
+ process.exitCode = payload.summary.fail > 0 ? 2 : 1;
1389
+ }
1390
+ catch (error) {
1391
+ emitError(error, options.json);
1392
+ }
1393
+ });
1394
+ cmd
1395
+ .command('report [agent]')
1396
+ .description('Summarize the latest or selected governed agent run after it finishes')
1397
+ .option('--dir <path>', 'Repository root (default: current directory)')
1398
+ .option('--session-id <id>', 'Local governance session ID (default: active session, then latest local session)')
1399
+ .option('--latest', 'Prefer the latest local session even when another session is active')
1400
+ .option('--global', 'For Cursor, check ~/.cursor/mcp.json instead of repo-local .cursor/mcp.json')
1401
+ .option('--no-write-record', 'Do not refresh the local AI Change Record sidecar')
1402
+ .option('--json', 'Output machine-readable JSON')
1403
+ .action((agent, options) => {
1404
+ try {
1405
+ const payload = buildAgentReportPayload(agent, options);
1406
+ if (options.json)
1407
+ emitJson(payload);
1408
+ else
1409
+ renderAgentReport(payload);
1410
+ if (!payload.ok)
1411
+ process.exitCode = payload.reportStatus === 'no_session' ? 1 : 2;
1412
+ }
1413
+ catch (error) {
1414
+ emitError(error, options.json);
1415
+ }
1416
+ });
1417
+ cmd
1418
+ .command('start [agent]')
1419
+ .description('Start a governed AI coding session for Claude, Codex, Cursor, VS Code, or generic MCP')
1420
+ .requiredOption('--goal <goal>', 'Task goal for the governed AI session')
1421
+ .option('--dir <path>', 'Repository root (default: current directory)')
1422
+ .option('--plan <text>', 'Optional initial source-free plan')
1423
+ .option('--plan-file <path>', 'Read initial source-free plan from a file')
1424
+ .option('--no-activate', 'Do not install/refresh Claude Code hooks when launching Claude')
1425
+ .option('--force-profile', 'Force refresh the repo governance profile before launch')
1426
+ .option('--json', 'Output machine-readable JSON')
1427
+ .action(async (agent, options) => {
1428
+ try {
1429
+ const result = await (0, agent_session_launcher_1.launchAgentSession)({
1430
+ agent,
1431
+ goal: options.goal || '',
1432
+ dir: options.dir,
1433
+ plan: readPlan(options),
1434
+ activate: options.activate !== false,
1435
+ forceProfile: options.forceProfile === true,
1436
+ actor: 'local_cli',
1437
+ });
1438
+ if (options.json)
1439
+ emitJson(result);
1440
+ else
1441
+ renderLaunch(result);
1442
+ }
1443
+ catch (error) {
1444
+ emitError(error, options.json);
1445
+ }
1446
+ });
1447
+ cmd
1448
+ .command('status')
1449
+ .description('Show the active governed AI coding session')
1450
+ .option('--session-id <id>', 'Local governance session ID (default: active session)')
1451
+ .option('--dir <path>', 'Repository root (default: current directory)')
1452
+ .option('--json', 'Output machine-readable JSON')
1453
+ .action((options) => {
1454
+ (0, session_1.localGovernanceStatusCommand)({
1455
+ sessionId: options.sessionId,
1456
+ dir: options.dir,
1457
+ json: options.json === true,
1458
+ });
1459
+ });
1460
+ cmd
1461
+ .command('handshake')
1462
+ .description('Handshake a cooperative agent into the active governed session')
1463
+ .option('--adapter <adapter>', 'Runtime adapter, e.g. codex-mcp, cursor-mcp, generic-mcp')
1464
+ .option('--agent <agent>', 'Agent alias, e.g. codex, cursor, gemini')
1465
+ .option('--session-id <id>', 'Session ID (default: active session)')
1466
+ .option('--actor <name>', 'Optional caller identity')
1467
+ .option('--dir <path>', 'Repository root (default: current directory)')
1468
+ .option('--json', 'Output machine-readable JSON')
1469
+ .action(async (options) => {
1470
+ try {
1471
+ const adapter = adapterFromOptions(options, 'generic-mcp');
1472
+ const result = await submitAgentEvent({
1473
+ adapter,
1474
+ eventType: 'session.handshake',
1475
+ dir: options.dir,
1476
+ payload: {
1477
+ ...(options.sessionId ? { sessionId: options.sessionId } : {}),
1478
+ ...(options.actor ? { actor: options.actor } : {}),
1479
+ },
1480
+ });
1481
+ if (options.json)
1482
+ emitJson(result);
1483
+ else
1484
+ renderDecision(result);
1485
+ }
1486
+ catch (error) {
1487
+ emitError(error, options.json);
1488
+ }
1489
+ });
1490
+ cmd
1491
+ .command('plan')
1492
+ .description('Capture the agent source-free implementation plan before edits')
1493
+ .option('--plan <text>', 'Source-free implementation plan')
1494
+ .option('--plan-file <path>', 'Read source-free plan from a file')
1495
+ .option('--adapter <adapter>', 'Runtime adapter, e.g. codex-mcp, cursor-mcp, generic-mcp')
1496
+ .option('--agent <agent>', 'Agent alias, e.g. codex, cursor, gemini')
1497
+ .option('--session-id <id>', 'Session ID (default: active session)')
1498
+ .option('--dir <path>', 'Repository root (default: current directory)')
1499
+ .option('--json', 'Output machine-readable JSON')
1500
+ .action(async (options) => {
1501
+ try {
1502
+ const adapter = adapterFromOptions(options, 'generic-mcp');
1503
+ const plan = readPlan(options);
1504
+ const result = await submitAgentEvent({
1505
+ adapter,
1506
+ eventType: 'plan.capture',
1507
+ dir: options.dir,
1508
+ payload: {
1509
+ plan,
1510
+ ...(options.sessionId ? { sessionId: options.sessionId } : {}),
1511
+ },
1512
+ });
1513
+ if (options.json)
1514
+ emitJson(result);
1515
+ else
1516
+ renderDecision(result);
1517
+ }
1518
+ catch (error) {
1519
+ emitError(error, options.json);
1520
+ }
1521
+ });
1522
+ cmd
1523
+ .command('amend')
1524
+ .description('Amend or re-plan the active source-free implementation plan')
1525
+ .option('--plan <text>', 'Replacement source-free implementation plan')
1526
+ .option('--plan-file <path>', 'Read replacement source-free plan from a file')
1527
+ .option('--summary <text>', 'Plan summary patch')
1528
+ .option('--scope <glob>', 'Scope glob to add to the plan; repeatable', collect, [])
1529
+ .option('--reason <text>', 'Reason for the plan change')
1530
+ .option('--adapter <adapter>', 'Runtime adapter, e.g. codex-mcp, cursor-mcp, generic-mcp')
1531
+ .option('--agent <agent>', 'Agent alias, e.g. codex, cursor, gemini')
1532
+ .option('--session-id <id>', 'Session ID (default: active session)')
1533
+ .option('--dir <path>', 'Repository root (default: current directory)')
1534
+ .option('--json', 'Output machine-readable JSON')
1535
+ .action(async (options) => {
1536
+ try {
1537
+ const adapter = adapterFromOptions(options, 'generic-mcp');
1538
+ const scope = Array.isArray(options.scope) ? options.scope : [];
1539
+ const plan = readPlan(options);
1540
+ const result = await submitAgentEvent({
1541
+ adapter,
1542
+ eventType: 'plan.amend',
1543
+ dir: options.dir,
1544
+ payload: {
1545
+ ...(plan ? { plan } : {}),
1546
+ ...(options.summary ? { summary: options.summary } : {}),
1547
+ ...(scope.length > 0 ? { scope } : {}),
1548
+ ...(options.reason ? { reason: options.reason } : {}),
1549
+ ...(options.sessionId ? { sessionId: options.sessionId } : {}),
1550
+ },
1551
+ });
1552
+ if (options.json)
1553
+ emitJson(result);
1554
+ else
1555
+ renderDecision(result);
1556
+ }
1557
+ catch (error) {
1558
+ emitError(error, options.json);
1559
+ }
1560
+ });
1561
+ cmd
1562
+ .command('check <filePath>')
1563
+ .description('Check a proposed agent write before it lands')
1564
+ .option('--adapter <adapter>', 'Runtime adapter, e.g. codex-mcp, cursor-mcp, generic-mcp')
1565
+ .option('--agent <agent>', 'Agent alias, e.g. codex, cursor, gemini')
1566
+ .option('--tool-name <name>', 'Agent write tool name', 'Write')
1567
+ .option('--after', 'Record as post-change observation instead of pre-write enforcement')
1568
+ .option('--session-id <id>', 'Session ID (default: active session)')
1569
+ .option('--dir <path>', 'Repository root (default: current directory)')
1570
+ .option('--json', 'Output machine-readable JSON')
1571
+ .action(async (filePath, options) => {
1572
+ try {
1573
+ const adapter = adapterFromOptions(options, 'generic-mcp');
1574
+ const result = await submitAgentEvent({
1575
+ adapter,
1576
+ eventType: options.after ? 'edit.after' : 'edit.before',
1577
+ dir: options.dir,
1578
+ payload: {
1579
+ filePath,
1580
+ toolName: options.toolName || 'Write',
1581
+ ...(options.sessionId ? { sessionId: options.sessionId } : {}),
1582
+ },
1583
+ });
1584
+ if (options.json)
1585
+ emitJson(result);
1586
+ else
1587
+ renderDecision(result, filePath);
1588
+ }
1589
+ catch (error) {
1590
+ emitError(error, options.json);
1591
+ }
1592
+ });
1593
+ cmd
1594
+ .command('approve <path>')
1595
+ .description('Apply an exact-path approval for the active governed session')
1596
+ .option('--reason <text>', 'Human-readable approval reason')
1597
+ .option('--adapter <adapter>', 'Runtime adapter, e.g. codex-mcp, cursor-mcp, generic-mcp')
1598
+ .option('--agent <agent>', 'Agent alias, e.g. codex, cursor, gemini')
1599
+ .option('--session-id <id>', 'Session ID (default: active session)')
1600
+ .option('--dir <path>', 'Repository root (default: current directory)')
1601
+ .option('--json', 'Output machine-readable JSON')
1602
+ .action(async (path, options) => {
1603
+ try {
1604
+ const adapter = adapterFromOptions(options, 'generic-mcp');
1605
+ const result = await submitAgentEvent({
1606
+ adapter,
1607
+ eventType: 'approval.apply',
1608
+ dir: options.dir,
1609
+ payload: {
1610
+ path,
1611
+ ...(options.reason ? { reason: options.reason } : {}),
1612
+ ...(options.sessionId ? { sessionId: options.sessionId } : {}),
1613
+ },
1614
+ });
1615
+ if (options.json)
1616
+ emitJson(result);
1617
+ else
1618
+ renderDecision(result, path);
1619
+ }
1620
+ catch (error) {
1621
+ emitError(error, options.json);
1622
+ }
1623
+ });
1624
+ cmd
1625
+ .command('finish')
1626
+ .description('Finish the active governed agent session')
1627
+ .option('--adapter <adapter>', 'Runtime adapter, e.g. codex-mcp, cursor-mcp, generic-mcp')
1628
+ .option('--agent <agent>', 'Agent alias, e.g. codex, cursor, gemini')
1629
+ .option('--session-id <id>', 'Session ID (default: active session)')
1630
+ .option('--dir <path>', 'Repository root (default: current directory)')
1631
+ .option('--json', 'Output machine-readable JSON')
1632
+ .action(async (options) => {
1633
+ try {
1634
+ const adapter = adapterFromOptions(options, 'generic-mcp');
1635
+ const result = await submitAgentEvent({
1636
+ adapter,
1637
+ eventType: 'session.finish',
1638
+ dir: options.dir,
1639
+ payload: {
1640
+ ...(options.sessionId ? { sessionId: options.sessionId } : {}),
1641
+ },
1642
+ });
1643
+ if (options.json)
1644
+ emitJson(result);
1645
+ else
1646
+ renderDecision(result);
1647
+ }
1648
+ catch (error) {
1649
+ emitError(error, options.json);
1650
+ }
1651
+ });
1652
+ const guard = cmd
1653
+ .command('guard')
1654
+ .description('Detect agent writes that bypassed Neurcode runtime checks');
1655
+ guard
1656
+ .command('start [agent]')
1657
+ .description('Start a governed agent session with local bypass detection')
1658
+ .requiredOption('--goal <goal>', 'Task goal for the guarded AI session')
1659
+ .option('--dir <path>', 'Repository root (default: current directory)')
1660
+ .option('--plan <text>', 'Optional initial source-free plan')
1661
+ .option('--plan-file <path>', 'Read initial source-free plan from a file')
1662
+ .option('--guard-path <path>', 'Guard artifact path (default: .neurcode/agent-guard/<session>.json)')
1663
+ .option('--no-supervise', 'Do not start the detached local guard supervisor')
1664
+ .option('--debounce-ms <ms>', 'Supervisor file-change debounce in milliseconds', (value) => Number.parseInt(value, 10))
1665
+ .option('--no-activate', 'Do not install/refresh Claude Code hooks when launching Claude')
1666
+ .option('--force-profile', 'Force refresh the repo governance profile before launch')
1667
+ .option('--json', 'Output machine-readable JSON')
1668
+ .action(async (agent, options) => {
1669
+ try {
1670
+ const launch = await (0, agent_session_launcher_1.launchAgentSession)({
1671
+ agent,
1672
+ goal: options.goal || '',
1673
+ dir: options.dir,
1674
+ plan: readPlan(options),
1675
+ activate: options.activate !== false,
1676
+ forceProfile: options.forceProfile === true,
1677
+ actor: 'local_cli_agent_guard',
1678
+ });
1679
+ const artifact = (0, agent_guard_1.createAgentGuardArtifact)({
1680
+ repoRoot: launch.repoRoot,
1681
+ sessionId: launch.session.sessionId,
1682
+ agent: launch.agent.normalized,
1683
+ adapter: launch.agent.adapter,
1684
+ startedAt: launch.generatedAt,
1685
+ });
1686
+ const guardPath = (0, agent_guard_1.writeAgentGuardArtifact)(launch.repoRoot, artifact, options.guardPath);
1687
+ await publishGuardEvent({
1688
+ repoRoot: launch.repoRoot,
1689
+ sessionId: launch.session.sessionId,
1690
+ event: {
1691
+ type: 'agent_guard_started',
1692
+ ts: artifact.startedAt,
1693
+ message: `Agent guard started for ${artifact.agent} (${artifact.adapter}).`,
1694
+ detail: {
1695
+ schemaVersion: artifact.schemaVersion,
1696
+ guardId: artifact.guardId,
1697
+ artifactPath: repoRelativeArtifactPath(launch.repoRoot, guardPath),
1698
+ baselineFileCount: artifact.baseline.fileCount,
1699
+ baselineTreeHash: artifact.baseline.treeHash,
1700
+ privacy: artifact.privacy,
1701
+ },
1702
+ },
1703
+ });
1704
+ const cliEntry = process.argv[1];
1705
+ const supervisor = options.supervise !== false && cliEntry
1706
+ ? {
1707
+ enabled: true,
1708
+ ...(0, agent_guard_supervisor_1.startAgentGuardSupervisorDetached)({
1709
+ repoRoot: launch.repoRoot,
1710
+ sessionId: launch.session.sessionId,
1711
+ guardPath,
1712
+ cliEntry,
1713
+ debounceMs: options.debounceMs,
1714
+ }),
1715
+ }
1716
+ : { enabled: false };
1717
+ if (supervisor.enabled) {
1718
+ await recordSupervisorEvent({
1719
+ repoRoot: launch.repoRoot,
1720
+ sessionId: launch.session.sessionId,
1721
+ type: 'agent_guard_supervisor_started',
1722
+ message: supervisor.alreadyRunning
1723
+ ? 'Agent guard supervisor already running.'
1724
+ : 'Agent guard supervisor started.',
1725
+ detail: {
1726
+ guardId: artifact.guardId,
1727
+ pid: supervisor.pid ?? null,
1728
+ status: supervisor.state.status,
1729
+ debounceMs: supervisor.state.debounceMs,
1730
+ alreadyRunning: supervisor.alreadyRunning,
1731
+ },
1732
+ });
1733
+ }
1734
+ const payload = {
1735
+ ok: true,
1736
+ schemaVersion: artifact.schemaVersion,
1737
+ launch,
1738
+ guard: {
1739
+ guardId: artifact.guardId,
1740
+ sessionId: artifact.sessionId,
1741
+ artifactPath: guardPath,
1742
+ active: artifact.active,
1743
+ baselineFileCount: artifact.baseline.fileCount,
1744
+ baselineTreeHash: artifact.baseline.treeHash,
1745
+ privacy: artifact.privacy,
1746
+ },
1747
+ supervisor,
1748
+ };
1749
+ if (options.json)
1750
+ emitJson(payload);
1751
+ else
1752
+ renderGuardStart({
1753
+ launch,
1754
+ guardPath,
1755
+ baselineFileCount: artifact.baseline.fileCount,
1756
+ baselineTreeHash: artifact.baseline.treeHash,
1757
+ supervisor,
1758
+ });
1759
+ }
1760
+ catch (error) {
1761
+ emitError(error, options.json);
1762
+ }
1763
+ });
1764
+ guard
1765
+ .command('status')
1766
+ .description('Compare current repo writes against the governed agent session')
1767
+ .option('--dir <path>', 'Repository root (default: current directory)')
1768
+ .option('--session-id <id>', 'Session ID (default: active session or active guard pointer)')
1769
+ .option('--guard-path <path>', 'Guard artifact path')
1770
+ .option('--fail-on-unverified', 'Exit non-zero when writes lack allowed pre-write evidence')
1771
+ .option('--json', 'Output machine-readable JSON')
1772
+ .action(async (options) => {
1773
+ try {
1774
+ const { artifactPath, evaluation } = await evaluateAndPublishGuardStatus(options);
1775
+ if (options.json)
1776
+ emitJson({ ...evaluation, artifactPath });
1777
+ else
1778
+ renderGuardEvaluation(evaluation, artifactPath);
1779
+ if (options.failOnUnverified && !evaluation.pass)
1780
+ process.exitCode = 2;
1781
+ }
1782
+ catch (error) {
1783
+ emitError(error, options.json);
1784
+ }
1785
+ });
1786
+ guard
1787
+ .command('finish')
1788
+ .description('Finish the agent guard and optionally close the governed session')
1789
+ .option('--dir <path>', 'Repository root (default: current directory)')
1790
+ .option('--session-id <id>', 'Session ID (default: active session or active guard pointer)')
1791
+ .option('--guard-path <path>', 'Guard artifact path')
1792
+ .option('--no-finish-session', 'Archive the guard without closing the governed session')
1793
+ .option('--fail-on-unverified', 'Exit non-zero when writes lack allowed pre-write evidence')
1794
+ .option('--json', 'Output machine-readable JSON')
1795
+ .action(async (options) => {
1796
+ try {
1797
+ const repoRoot = repoRootFrom({ dir: options.dir });
1798
+ const guardRead = (0, agent_guard_1.readAgentGuardArtifact)({
1799
+ repoRoot,
1800
+ sessionId: options.sessionId,
1801
+ artifactPath: options.guardPath,
1802
+ });
1803
+ if (!guardRead.artifact) {
1804
+ const message = guardRead.error || `Agent guard not found (${guardRead.path}).`;
1805
+ if (options.json)
1806
+ emitJson({ ok: false, path: guardRead.path, error: message });
1807
+ else
1808
+ console.error(chalk.red(`Neurcode agent guard failed: ${message}`));
1809
+ process.exitCode = 1;
1810
+ return;
1811
+ }
1812
+ const session = loadGuardSession(repoRoot, options.sessionId || guardRead.artifact.sessionId);
1813
+ if (!session) {
1814
+ const message = `Local governance session ${options.sessionId || guardRead.artifact.sessionId} was not found.`;
1815
+ if (options.json)
1816
+ emitJson({ ok: false, path: guardRead.path, error: message });
1817
+ else
1818
+ console.error(chalk.red(`Neurcode agent guard failed: ${message}`));
1819
+ process.exitCode = 1;
1820
+ return;
1821
+ }
1822
+ const evaluation = (0, agent_guard_1.evaluateAgentGuard)(repoRoot, guardRead.artifact, session);
1823
+ const finishedArtifact = (0, agent_guard_1.markAgentGuardFinished)(guardRead.artifact, evaluation.generatedAt);
1824
+ const artifactPath = (0, agent_guard_1.writeAgentGuardArtifact)(repoRoot, finishedArtifact, guardRead.path);
1825
+ await publishGuardEvaluation({
1826
+ repoRoot,
1827
+ artifactPath,
1828
+ evaluation,
1829
+ eventType: 'agent_guard_finished',
1830
+ message: evaluation.pass
1831
+ ? 'Agent guard finished: all changed files had allowed pre-write evidence.'
1832
+ : 'Agent guard finished: attention required for unverified or denied writes.',
1833
+ });
1834
+ const supervisorStop = (0, agent_guard_supervisor_1.stopAgentGuardSupervisor)(repoRoot, session.sessionId);
1835
+ if (supervisorStop.state) {
1836
+ await recordSupervisorEvent({
1837
+ repoRoot,
1838
+ sessionId: session.sessionId,
1839
+ type: 'agent_guard_supervisor_stopped',
1840
+ message: supervisorStop.signaled
1841
+ ? 'Agent guard supervisor stop requested.'
1842
+ : 'Agent guard supervisor stopped.',
1843
+ detail: {
1844
+ guardId: finishedArtifact.guardId,
1845
+ pid: supervisorStop.state.pid,
1846
+ status: supervisorStop.effectiveStatus,
1847
+ signaled: supervisorStop.signaled,
1848
+ },
1849
+ });
1850
+ }
1851
+ const closedSession = options.finishSession === false
1852
+ ? null
1853
+ : (0, governance_runtime_1.finishSession)(repoRoot, session.sessionId);
1854
+ if (closedSession) {
1855
+ // Phase A: emit the self-attested, source-free admission artifact.
1856
+ // Best-effort — never disrupts guard finish.
1857
+ (0, admission_artifact_1.tryEmitSelfAttestedAdmissionRecord)({ repoRoot, session: closedSession });
1858
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, closedSession);
1859
+ }
1860
+ if (options.json) {
1861
+ emitJson({
1862
+ ...evaluation,
1863
+ artifactPath,
1864
+ guardActive: finishedArtifact.active,
1865
+ sessionFinished: Boolean(closedSession),
1866
+ supervisor: supervisorStop,
1867
+ });
1868
+ }
1869
+ else {
1870
+ renderGuardEvaluation(evaluation, artifactPath);
1871
+ console.log(chalk.dim(closedSession
1872
+ ? `Session finished with replayHash ${closedSession.replayHash}.`
1873
+ : 'Guard archived; governed session left active.'));
1874
+ if (closedSession) {
1875
+ console.log(chalk.dim(`Next: neurcode agent report --session-id ${closedSession.sessionId}`));
1876
+ }
1877
+ console.log('');
1878
+ }
1879
+ if (options.failOnUnverified && !evaluation.pass)
1880
+ process.exitCode = 2;
1881
+ }
1882
+ catch (error) {
1883
+ emitError(error, options.json);
1884
+ }
1885
+ });
1886
+ const supervise = guard
1887
+ .command('supervise')
1888
+ .description('Continuously evaluate and publish guard posture while files change');
1889
+ supervise
1890
+ .command('start')
1891
+ .description('Start the detached local agent guard supervisor')
1892
+ .option('--dir <path>', 'Repository root (default: current directory)')
1893
+ .option('--session-id <id>', 'Session ID (default: active session or active guard pointer)')
1894
+ .option('--guard-path <path>', 'Guard artifact path')
1895
+ .option('--debounce-ms <ms>', 'File-change debounce in milliseconds', (value) => Number.parseInt(value, 10))
1896
+ .option('--json', 'Output machine-readable JSON')
1897
+ .action(async (options) => {
1898
+ try {
1899
+ const { repoRoot, guardRead, artifact, session } = requireGuardContext(options);
1900
+ if (!process.argv[1])
1901
+ throw new Error('Unable to resolve the active Neurcode CLI entrypoint.');
1902
+ const supervisor = (0, agent_guard_supervisor_1.startAgentGuardSupervisorDetached)({
1903
+ repoRoot,
1904
+ sessionId: session.sessionId,
1905
+ guardPath: guardRead.path,
1906
+ cliEntry: process.argv[1],
1907
+ debounceMs: options.debounceMs,
1908
+ });
1909
+ await recordSupervisorEvent({
1910
+ repoRoot,
1911
+ sessionId: session.sessionId,
1912
+ type: 'agent_guard_supervisor_started',
1913
+ message: supervisor.alreadyRunning
1914
+ ? 'Agent guard supervisor already running.'
1915
+ : 'Agent guard supervisor started.',
1916
+ detail: {
1917
+ guardId: artifact.guardId,
1918
+ pid: supervisor.pid,
1919
+ status: supervisor.state.status,
1920
+ debounceMs: supervisor.state.debounceMs,
1921
+ alreadyRunning: supervisor.alreadyRunning,
1922
+ },
1923
+ });
1924
+ if (options.json)
1925
+ emitJson({ ok: true, ...supervisor });
1926
+ else
1927
+ renderSupervisorInspection((0, agent_guard_supervisor_1.inspectAgentGuardSupervisor)(repoRoot, session.sessionId));
1928
+ }
1929
+ catch (error) {
1930
+ emitError(error, options.json);
1931
+ }
1932
+ });
1933
+ supervise
1934
+ .command('status')
1935
+ .description('Show the detached supervisor process and heartbeat state')
1936
+ .option('--dir <path>', 'Repository root (default: current directory)')
1937
+ .option('--session-id <id>', 'Session ID (default: active session or active guard pointer)')
1938
+ .option('--guard-path <path>', 'Guard artifact path')
1939
+ .option('--json', 'Output machine-readable JSON')
1940
+ .action((options) => {
1941
+ try {
1942
+ const { repoRoot, artifact } = requireGuardContext(options);
1943
+ const inspection = (0, agent_guard_supervisor_1.inspectAgentGuardSupervisor)(repoRoot, options.sessionId || artifact.sessionId);
1944
+ if (options.json)
1945
+ emitJson({ ok: inspection.exists && inspection.state !== null, ...inspection });
1946
+ else
1947
+ renderSupervisorInspection(inspection);
1948
+ if (!inspection.exists || !inspection.state)
1949
+ process.exitCode = 1;
1950
+ }
1951
+ catch (error) {
1952
+ emitError(error, options.json);
1953
+ }
1954
+ });
1955
+ supervise
1956
+ .command('stop')
1957
+ .description('Stop the detached supervisor without finishing the governed session')
1958
+ .option('--dir <path>', 'Repository root (default: current directory)')
1959
+ .option('--session-id <id>', 'Session ID (default: active session or active guard pointer)')
1960
+ .option('--guard-path <path>', 'Guard artifact path')
1961
+ .option('--json', 'Output machine-readable JSON')
1962
+ .action(async (options) => {
1963
+ try {
1964
+ const { repoRoot, artifact, session } = requireGuardContext(options);
1965
+ const stopped = (0, agent_guard_supervisor_1.stopAgentGuardSupervisor)(repoRoot, session.sessionId);
1966
+ if (stopped.state) {
1967
+ await recordSupervisorEvent({
1968
+ repoRoot,
1969
+ sessionId: session.sessionId,
1970
+ type: 'agent_guard_supervisor_stopped',
1971
+ message: stopped.signaled
1972
+ ? 'Agent guard supervisor stop requested.'
1973
+ : 'Agent guard supervisor stopped.',
1974
+ detail: {
1975
+ guardId: artifact.guardId,
1976
+ pid: stopped.state.pid,
1977
+ status: stopped.effectiveStatus,
1978
+ signaled: stopped.signaled,
1979
+ },
1980
+ });
1981
+ }
1982
+ if (options.json)
1983
+ emitJson({ ok: stopped.state !== null, ...stopped });
1984
+ else
1985
+ renderSupervisorInspection((0, agent_guard_supervisor_1.inspectAgentGuardSupervisor)(repoRoot, session.sessionId));
1986
+ }
1987
+ catch (error) {
1988
+ emitError(error, options.json);
1989
+ }
1990
+ });
1991
+ supervise
1992
+ .command('run')
1993
+ .description('Run the guard supervisor in the foreground (internal/process-manager mode)')
1994
+ .option('--dir <path>', 'Repository root (default: current directory)')
1995
+ .option('--session-id <id>', 'Session ID (default: active session or active guard pointer)')
1996
+ .option('--guard-path <path>', 'Guard artifact path')
1997
+ .option('--debounce-ms <ms>', 'File-change debounce in milliseconds', (value) => Number.parseInt(value, 10))
1998
+ .option('--heartbeat-ms <ms>', 'Supervisor heartbeat interval in milliseconds', (value) => Number.parseInt(value, 10))
1999
+ .option('--exit-after-ms <ms>', 'Stop automatically after a duration (test/process-manager support)', (value) => Number.parseInt(value, 10))
2000
+ .option('--no-initial-evaluation', 'Wait for the first file change before evaluating')
2001
+ .option('--json-lines', 'Emit source-free JSON line updates')
2002
+ .action(async (options) => {
2003
+ try {
2004
+ const { repoRoot, guardRead, session } = requireGuardContext(options);
2005
+ const finalState = await (0, agent_guard_supervisor_1.runAgentGuardSupervisor)({
2006
+ repoRoot,
2007
+ sessionId: session.sessionId,
2008
+ guardPath: guardRead.path,
2009
+ debounceMs: options.debounceMs,
2010
+ heartbeatMs: options.heartbeatMs,
2011
+ exitAfterMs: options.exitAfterMs,
2012
+ evaluateImmediately: options.initialEvaluation !== false,
2013
+ onEvaluate: async () => {
2014
+ const { evaluation } = await evaluateAndPublishGuardStatus({
2015
+ dir: repoRoot,
2016
+ sessionId: session.sessionId,
2017
+ guardPath: guardRead.path,
2018
+ });
2019
+ if (options.jsonLines)
2020
+ emitJsonLine({ type: 'evaluation', evaluation });
2021
+ return {
2022
+ pass: evaluation.pass,
2023
+ changedFiles: evaluation.summary.changedFiles,
2024
+ evaluatedAt: evaluation.generatedAt,
2025
+ };
2026
+ },
2027
+ onState: options.jsonLines
2028
+ ? (state) => emitJsonLine({ type: 'supervisor_state', state })
2029
+ : undefined,
2030
+ });
2031
+ if (options.jsonLines)
2032
+ emitJsonLine({ type: 'supervisor_stopped', state: finalState });
2033
+ }
2034
+ catch (error) {
2035
+ if (options.jsonLines) {
2036
+ emitJsonLine({ ok: false, error: error instanceof Error ? error.message : String(error) });
2037
+ process.exitCode = 1;
2038
+ }
2039
+ else {
2040
+ emitError(error);
2041
+ }
2042
+ }
2043
+ });
2044
+ }
2045
+ //# sourceMappingURL=agent.js.map