@neurcode-ai/cli 0.14.0 → 0.15.1

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 (189) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +60 -8
  3. package/dist/api-client.d.ts +284 -0
  4. package/dist/api-client.d.ts.map +1 -1
  5. package/dist/api-client.js +111 -0
  6. package/dist/api-client.js.map +1 -1
  7. package/dist/commands/activate.d.ts +82 -0
  8. package/dist/commands/activate.d.ts.map +1 -0
  9. package/dist/commands/activate.js +551 -0
  10. package/dist/commands/activate.js.map +1 -0
  11. package/dist/commands/admission.d.ts +67 -0
  12. package/dist/commands/admission.d.ts.map +1 -0
  13. package/dist/commands/admission.js +350 -0
  14. package/dist/commands/admission.js.map +1 -0
  15. package/dist/commands/agent.d.ts +3 -0
  16. package/dist/commands/agent.d.ts.map +1 -0
  17. package/dist/commands/agent.js +2045 -0
  18. package/dist/commands/agent.js.map +1 -0
  19. package/dist/commands/demo.d.ts +3 -0
  20. package/dist/commands/demo.d.ts.map +1 -0
  21. package/dist/commands/demo.js +102 -0
  22. package/dist/commands/demo.js.map +1 -0
  23. package/dist/commands/init.d.ts.map +1 -1
  24. package/dist/commands/init.js +58 -44
  25. package/dist/commands/init.js.map +1 -1
  26. package/dist/commands/login.d.ts +1 -1
  27. package/dist/commands/login.d.ts.map +1 -1
  28. package/dist/commands/login.js +44 -22
  29. package/dist/commands/login.js.map +1 -1
  30. package/dist/commands/profile.d.ts +14 -0
  31. package/dist/commands/profile.d.ts.map +1 -0
  32. package/dist/commands/profile.js +118 -0
  33. package/dist/commands/profile.js.map +1 -0
  34. package/dist/commands/quickstart.d.ts +2 -2
  35. package/dist/commands/quickstart.d.ts.map +1 -1
  36. package/dist/commands/quickstart.js +31 -30
  37. package/dist/commands/quickstart.js.map +1 -1
  38. package/dist/commands/remediate-export.d.ts +6 -1
  39. package/dist/commands/remediate-export.d.ts.map +1 -1
  40. package/dist/commands/remediate-export.js +359 -7
  41. package/dist/commands/remediate-export.js.map +1 -1
  42. package/dist/commands/replay.d.ts.map +1 -1
  43. package/dist/commands/replay.js +84 -0
  44. package/dist/commands/replay.js.map +1 -1
  45. package/dist/commands/run.d.ts +3 -0
  46. package/dist/commands/run.d.ts.map +1 -0
  47. package/dist/commands/run.js +98 -0
  48. package/dist/commands/run.js.map +1 -0
  49. package/dist/commands/runtime-adapter.d.ts +8 -0
  50. package/dist/commands/runtime-adapter.d.ts.map +1 -0
  51. package/dist/commands/runtime-adapter.js +375 -0
  52. package/dist/commands/runtime-adapter.js.map +1 -0
  53. package/dist/commands/runtime-doctor.d.ts +6 -0
  54. package/dist/commands/runtime-doctor.d.ts.map +1 -0
  55. package/dist/commands/runtime-doctor.js +478 -0
  56. package/dist/commands/runtime-doctor.js.map +1 -0
  57. package/dist/commands/runtime-report.d.ts +13 -0
  58. package/dist/commands/runtime-report.d.ts.map +1 -0
  59. package/dist/commands/runtime-report.js +81 -0
  60. package/dist/commands/runtime-report.js.map +1 -0
  61. package/dist/commands/runtime-sync.d.ts +17 -0
  62. package/dist/commands/runtime-sync.d.ts.map +1 -0
  63. package/dist/commands/runtime-sync.js +656 -0
  64. package/dist/commands/runtime-sync.js.map +1 -0
  65. package/dist/commands/runtime.d.ts +16 -0
  66. package/dist/commands/runtime.d.ts.map +1 -0
  67. package/dist/commands/runtime.js +380 -0
  68. package/dist/commands/runtime.js.map +1 -0
  69. package/dist/commands/session-hook.d.ts +35 -0
  70. package/dist/commands/session-hook.d.ts.map +1 -0
  71. package/dist/commands/session-hook.js +1297 -0
  72. package/dist/commands/session-hook.js.map +1 -0
  73. package/dist/commands/session.d.ts +91 -0
  74. package/dist/commands/session.d.ts.map +1 -1
  75. package/dist/commands/session.js +1226 -0
  76. package/dist/commands/session.js.map +1 -1
  77. package/dist/commands/whoami.d.ts +7 -4
  78. package/dist/commands/whoami.d.ts.map +1 -1
  79. package/dist/commands/whoami.js +59 -34
  80. package/dist/commands/whoami.js.map +1 -1
  81. package/dist/config.d.ts.map +1 -1
  82. package/dist/config.js +24 -5
  83. package/dist/config.js.map +1 -1
  84. package/dist/daemon/routes.d.ts.map +1 -1
  85. package/dist/daemon/routes.js +8 -0
  86. package/dist/daemon/routes.js.map +1 -1
  87. package/dist/daemon/server.d.ts.map +1 -1
  88. package/dist/daemon/server.js +88 -0
  89. package/dist/daemon/server.js.map +1 -1
  90. package/dist/governance/impact-analysis.d.ts +27 -0
  91. package/dist/governance/impact-analysis.d.ts.map +1 -0
  92. package/dist/governance/impact-analysis.js +274 -0
  93. package/dist/governance/impact-analysis.js.map +1 -0
  94. package/dist/index.js +472 -29
  95. package/dist/index.js.map +1 -1
  96. package/dist/intent-engine/matcher.d.ts.map +1 -1
  97. package/dist/intent-engine/matcher.js +3 -12
  98. package/dist/intent-engine/matcher.js.map +1 -1
  99. package/dist/utils/admission-artifact.d.ts +59 -0
  100. package/dist/utils/admission-artifact.d.ts.map +1 -0
  101. package/dist/utils/admission-artifact.js +410 -0
  102. package/dist/utils/admission-artifact.js.map +1 -0
  103. package/dist/utils/agent-adapter-setup.d.ts +80 -0
  104. package/dist/utils/agent-adapter-setup.d.ts.map +1 -0
  105. package/dist/utils/agent-adapter-setup.js +577 -0
  106. package/dist/utils/agent-adapter-setup.js.map +1 -0
  107. package/dist/utils/agent-guard-supervisor.d.ts +75 -0
  108. package/dist/utils/agent-guard-supervisor.d.ts.map +1 -0
  109. package/dist/utils/agent-guard-supervisor.js +388 -0
  110. package/dist/utils/agent-guard-supervisor.js.map +1 -0
  111. package/dist/utils/agent-guard.d.ts +92 -0
  112. package/dist/utils/agent-guard.d.ts.map +1 -0
  113. package/dist/utils/agent-guard.js +326 -0
  114. package/dist/utils/agent-guard.js.map +1 -0
  115. package/dist/utils/agent-session-launcher.d.ts +89 -0
  116. package/dist/utils/agent-session-launcher.d.ts.map +1 -0
  117. package/dist/utils/agent-session-launcher.js +308 -0
  118. package/dist/utils/agent-session-launcher.js.map +1 -0
  119. package/dist/utils/bash-command-analysis.d.ts +19 -0
  120. package/dist/utils/bash-command-analysis.d.ts.map +1 -0
  121. package/dist/utils/bash-command-analysis.js +295 -0
  122. package/dist/utils/bash-command-analysis.js.map +1 -0
  123. package/dist/utils/consequence-nudges.d.ts +30 -0
  124. package/dist/utils/consequence-nudges.d.ts.map +1 -0
  125. package/dist/utils/consequence-nudges.js +313 -0
  126. package/dist/utils/consequence-nudges.js.map +1 -0
  127. package/dist/utils/drift-intelligence.d.ts.map +1 -1
  128. package/dist/utils/drift-intelligence.js +29 -7
  129. package/dist/utils/drift-intelligence.js.map +1 -1
  130. package/dist/utils/git-coverage.d.ts +57 -0
  131. package/dist/utils/git-coverage.d.ts.map +1 -0
  132. package/dist/utils/git-coverage.js +302 -0
  133. package/dist/utils/git-coverage.js.map +1 -0
  134. package/dist/utils/gitignore.d.ts.map +1 -1
  135. package/dist/utils/gitignore.js +2 -1
  136. package/dist/utils/gitignore.js.map +1 -1
  137. package/dist/utils/governed-intent.d.ts +10 -0
  138. package/dist/utils/governed-intent.d.ts.map +1 -0
  139. package/dist/utils/governed-intent.js +108 -0
  140. package/dist/utils/governed-intent.js.map +1 -0
  141. package/dist/utils/hook-heartbeat.d.ts +55 -0
  142. package/dist/utils/hook-heartbeat.d.ts.map +1 -0
  143. package/dist/utils/hook-heartbeat.js +116 -0
  144. package/dist/utils/hook-heartbeat.js.map +1 -0
  145. package/dist/utils/intent-continuity.d.ts +21 -0
  146. package/dist/utils/intent-continuity.d.ts.map +1 -0
  147. package/dist/utils/intent-continuity.js +192 -0
  148. package/dist/utils/intent-continuity.js.map +1 -0
  149. package/dist/utils/messages.d.ts +1 -1
  150. package/dist/utils/messages.d.ts.map +1 -1
  151. package/dist/utils/messages.js +24 -21
  152. package/dist/utils/messages.js.map +1 -1
  153. package/dist/utils/runtime-companion.d.ts +137 -0
  154. package/dist/utils/runtime-companion.d.ts.map +1 -0
  155. package/dist/utils/runtime-companion.js +231 -0
  156. package/dist/utils/runtime-companion.js.map +1 -0
  157. package/dist/utils/runtime-connection.d.ts +46 -0
  158. package/dist/utils/runtime-connection.d.ts.map +1 -0
  159. package/dist/utils/runtime-connection.js +148 -0
  160. package/dist/utils/runtime-connection.js.map +1 -0
  161. package/dist/utils/runtime-evidence.d.ts +68 -0
  162. package/dist/utils/runtime-evidence.d.ts.map +1 -0
  163. package/dist/utils/runtime-evidence.js +248 -0
  164. package/dist/utils/runtime-evidence.js.map +1 -0
  165. package/dist/utils/runtime-live.d.ts +33 -0
  166. package/dist/utils/runtime-live.d.ts.map +1 -0
  167. package/dist/utils/runtime-live.js +361 -0
  168. package/dist/utils/runtime-live.js.map +1 -0
  169. package/dist/utils/runtime-outbox.d.ts +76 -0
  170. package/dist/utils/runtime-outbox.d.ts.map +1 -0
  171. package/dist/utils/runtime-outbox.js +410 -0
  172. package/dist/utils/runtime-outbox.js.map +1 -0
  173. package/dist/utils/runtime-receipt.d.ts +50 -0
  174. package/dist/utils/runtime-receipt.d.ts.map +1 -0
  175. package/dist/utils/runtime-receipt.js +223 -0
  176. package/dist/utils/runtime-receipt.js.map +1 -0
  177. package/dist/utils/state.d.ts +21 -0
  178. package/dist/utils/state.d.ts.map +1 -1
  179. package/dist/utils/state.js +30 -0
  180. package/dist/utils/state.js.map +1 -1
  181. package/dist/utils/structural-understanding.d.ts +334 -0
  182. package/dist/utils/structural-understanding.d.ts.map +1 -0
  183. package/dist/utils/structural-understanding.js +2316 -0
  184. package/dist/utils/structural-understanding.js.map +1 -0
  185. package/dist/utils/v0-governance.d.ts +197 -0
  186. package/dist/utils/v0-governance.d.ts.map +1 -0
  187. package/dist/utils/v0-governance.js +904 -0
  188. package/dist/utils/v0-governance.js.map +1 -0
  189. package/package.json +11 -12
@@ -43,6 +43,18 @@ var __importStar = (this && this.__importStar) || (function () {
43
43
  };
44
44
  })();
45
45
  Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.buildLocalGovernanceStatus = buildLocalGovernanceStatus;
47
+ exports.localGovernanceStatusCommand = localGovernanceStatusCommand;
48
+ exports.resetStaleGovernanceSessionCommand = resetStaleGovernanceSessionCommand;
49
+ exports.replanGovernanceSessionCommand = replanGovernanceSessionCommand;
50
+ exports.decideGovernanceReplanCommand = decideGovernanceReplanCommand;
51
+ exports.approveGovernanceSessionCommand = approveGovernanceSessionCommand;
52
+ exports.showGovernanceObligationsCommand = showGovernanceObligationsCommand;
53
+ exports.waiveGovernanceObligationCommand = waiveGovernanceObligationCommand;
54
+ exports.listRuntimeSessionsCommand = listRuntimeSessionsCommand;
55
+ exports.showRuntimeSessionCommand = showRuntimeSessionCommand;
56
+ exports.aiChangeRecordCommand = aiChangeRecordCommand;
57
+ exports.structuralUnderstandingCommand = structuralUnderstandingCommand;
46
58
  exports.listSessionsCommand = listSessionsCommand;
47
59
  exports.endSessionCommand = endSessionCommand;
48
60
  exports.sessionStatusCommand = sessionStatusCommand;
@@ -53,9 +65,21 @@ exports.compareLocalSessionsCommand = compareLocalSessionsCommand;
53
65
  const config_1 = require("../config");
54
66
  const api_client_1 = require("../api-client");
55
67
  const state_1 = require("../utils/state");
68
+ const governance_runtime_1 = require("@neurcode-ai/governance-runtime");
56
69
  const messages_1 = require("../utils/messages");
57
70
  const project_root_1 = require("../utils/project-root");
71
+ const diff_parser_1 = require("@neurcode-ai/diff-parser");
58
72
  const session_continuity_1 = require("../utils/session-continuity");
73
+ const runtime_evidence_1 = require("../utils/runtime-evidence");
74
+ const v0_governance_1 = require("../utils/v0-governance");
75
+ const runtime_connection_1 = require("../utils/runtime-connection");
76
+ const runtime_live_1 = require("../utils/runtime-live");
77
+ const runtime_outbox_1 = require("../utils/runtime-outbox");
78
+ const structural_understanding_1 = require("../utils/structural-understanding");
79
+ const agent_guard_supervisor_1 = require("../utils/agent-guard-supervisor");
80
+ const node_child_process_1 = require("node:child_process");
81
+ const node_fs_1 = require("node:fs");
82
+ const node_path_1 = require("node:path");
59
83
  const readline = __importStar(require("readline"));
60
84
  // Import chalk with fallback
61
85
  let chalk;
@@ -89,11 +113,1207 @@ function promptUser(question) {
89
113
  });
90
114
  });
91
115
  }
116
+ function truncate(value, max = 96) {
117
+ return value.length > max ? `${value.slice(0, max - 3)}...` : value;
118
+ }
119
+ function compactList(values, max = 6) {
120
+ if (values.length === 0)
121
+ return 'none';
122
+ const shown = values.slice(0, max).join(', ');
123
+ return values.length > max ? `${shown} +${values.length - max} more` : shown;
124
+ }
125
+ function eventLabel(event) {
126
+ if (event.type === 'check_ok')
127
+ return 'OK';
128
+ if (event.type === 'check_warn')
129
+ return 'WARN';
130
+ if (event.type === 'check_block')
131
+ return 'BLOCK';
132
+ if (event.type === 'approval_decision')
133
+ return 'APPROVE';
134
+ if (event.type === 'session_start')
135
+ return 'START';
136
+ if (event.type === 'plan_captured')
137
+ return 'PLAN';
138
+ if (event.type === 'plan_amended')
139
+ return 'REPLAN';
140
+ if (event.type === 'obligation_waiver_decision')
141
+ return 'WAIVE';
142
+ if (event.type === 'obligation_state_changed')
143
+ return 'OBLIG';
144
+ if (event.type === 'structural_understanding')
145
+ return 'STRUCT';
146
+ if (event.type === 'session_finish')
147
+ return 'FINISH';
148
+ return event.type.toUpperCase();
149
+ }
150
+ function approvalContextFrom(event) {
151
+ const detail = event?.detail;
152
+ const raw = detail && typeof detail === 'object'
153
+ ? detail['approvalContext']
154
+ : null;
155
+ if (!raw || typeof raw !== 'object')
156
+ return null;
157
+ const context = raw;
158
+ const owners = Array.isArray(context['owners'])
159
+ ? context['owners'].filter((owner) => typeof owner === 'string')
160
+ : [];
161
+ return {
162
+ blockedPath: typeof context['blockedPath'] === 'string' ? context['blockedPath'] : event?.filePath,
163
+ owners,
164
+ suggestedApprovalPath: typeof context['suggestedApprovalPath'] === 'string'
165
+ ? context['suggestedApprovalPath']
166
+ : event?.filePath,
167
+ };
168
+ }
169
+ function resolveUnderstandingDiff(repoRoot, options) {
170
+ const args = ['diff'];
171
+ if (options.staged) {
172
+ args.push('--cached');
173
+ }
174
+ else if (options.base && options.base.trim()) {
175
+ args.push(options.base.trim());
176
+ }
177
+ else {
178
+ args.push('HEAD');
179
+ }
180
+ return (0, node_child_process_1.execFileSync)('git', args, {
181
+ cwd: repoRoot,
182
+ encoding: 'utf8',
183
+ maxBuffer: 64 * 1024 * 1024,
184
+ });
185
+ }
186
+ function loadLocalGovernanceSession(repoRoot, sessionId) {
187
+ return sessionId ? (0, governance_runtime_1.loadSession)(repoRoot, sessionId) : (0, governance_runtime_1.loadActiveSession)(repoRoot);
188
+ }
189
+ function latestEventTimestamp(session) {
190
+ return [...session.events]
191
+ .reverse()
192
+ .find((event) => typeof event.ts === 'string' && event.ts.trim())?.ts ||
193
+ new Date(0).toISOString();
194
+ }
195
+ function sessionAgeMinutes(session, now = new Date()) {
196
+ const started = Date.parse(latestEventTimestamp(session));
197
+ if (!Number.isFinite(started))
198
+ return Number.POSITIVE_INFINITY;
199
+ return Math.max(0, (now.getTime() - started) / 60_000);
200
+ }
201
+ function approvedPathMatches(filePath, approvedPath) {
202
+ const normalizedFile = filePath.replace(/\\/g, '/');
203
+ const normalizedApproved = approvedPath.replace(/\\/g, '/');
204
+ if (normalizedFile === normalizedApproved)
205
+ return true;
206
+ if (normalizedApproved.endsWith('/**')) {
207
+ const prefix = normalizedApproved.slice(0, -3);
208
+ return normalizedFile === prefix || normalizedFile.startsWith(`${prefix}/`);
209
+ }
210
+ if (normalizedApproved.includes('*')) {
211
+ // Keep reset-stale conservative for uncommon glob approvals: do not assume
212
+ // a block is resolved unless it is an exact path or directory-scope grant.
213
+ return false;
214
+ }
215
+ return false;
216
+ }
217
+ function pendingApprovalBlock(session, now = new Date()) {
218
+ const activeApprovals = (0, governance_runtime_1.activeApprovalPaths)(session.contract, now.toISOString());
219
+ for (const event of [...session.events].reverse()) {
220
+ if (event.type !== 'check_block')
221
+ continue;
222
+ const context = approvalContextFrom(event);
223
+ const suggestedApprovalPath = context?.suggestedApprovalPath || event.filePath || null;
224
+ if (!suggestedApprovalPath)
225
+ continue;
226
+ if (activeApprovals.some((approved) => approvedPathMatches(suggestedApprovalPath, approved))) {
227
+ continue;
228
+ }
229
+ return {
230
+ filePath: event.filePath || context?.blockedPath || null,
231
+ suggestedApprovalPath,
232
+ owners: context?.owners ?? [],
233
+ message: event.message,
234
+ };
235
+ }
236
+ return null;
237
+ }
238
+ function buildLocalGovernanceStatus(options = {}) {
239
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
240
+ const session = loadLocalGovernanceSession(repoRoot, options.sessionId);
241
+ const connection = (0, runtime_connection_1.loadRuntimeConnection)(repoRoot);
242
+ if (!session) {
243
+ return {
244
+ ok: false,
245
+ repoRoot,
246
+ active: false,
247
+ message: options.sessionId
248
+ ? `Local governance session ${options.sessionId} was not found.`
249
+ : 'No active in-flow governance session found.',
250
+ connection,
251
+ };
252
+ }
253
+ const recentEvents = session.events.slice(-10);
254
+ const latestBlock = [...session.events].reverse().find((event) => event.type === 'check_block');
255
+ const latestApprovalContext = approvalContextFrom(latestBlock);
256
+ const suggestedApprovalPath = latestApprovalContext?.suggestedApprovalPath ||
257
+ latestBlock?.filePath ||
258
+ null;
259
+ return {
260
+ ok: true,
261
+ repoRoot,
262
+ active: session.status === 'active',
263
+ sessionId: session.sessionId,
264
+ status: session.status,
265
+ goal: session.contract.goal,
266
+ profileHash: session.profileHash,
267
+ scopeMode: session.contract.scopeMode,
268
+ planCoherenceMode: session.contract.planCoherenceMode ?? 'warn',
269
+ agentPlan: session.contract.agentPlan ?? null,
270
+ agentPlanRevision: typeof session.contract.agentPlanRevision === 'number'
271
+ ? session.contract.agentPlanRevision
272
+ : session.contract.agentPlan
273
+ ? 1
274
+ : null,
275
+ pendingPlanAmendments: (session.contract.planAmendmentProposals ?? [])
276
+ .filter((proposal) => proposal.status === 'pending'),
277
+ architectureObligations: session.contract.architectureObligations ?? [],
278
+ allowedGlobs: session.contract.allowedGlobs,
279
+ sensitiveGlobs: session.contract.sensitiveGlobs,
280
+ approvalRequiredGlobs: session.contract.approvalRequiredGlobs,
281
+ approvedPaths: session.contract.approvedPaths,
282
+ recentEvents,
283
+ agentInvocation: (0, governance_runtime_1.buildAgentInvocationSummary)(session),
284
+ agentGuard: (0, governance_runtime_1.buildAgentGuardPostureSummary)(session),
285
+ agentSupervisor: (0, agent_guard_supervisor_1.inspectAgentGuardSupervisor)(repoRoot, session.sessionId),
286
+ latestBlock: latestBlock
287
+ ? {
288
+ filePath: latestBlock.filePath,
289
+ message: latestBlock.message,
290
+ owners: latestApprovalContext?.owners ?? [],
291
+ suggestedApprovalPath,
292
+ approveCommand: suggestedApprovalPath
293
+ ? `neurcode session approve --path ${suggestedApprovalPath}`
294
+ : null,
295
+ }
296
+ : null,
297
+ recordPath: `.neurcode/sessions/${session.sessionId}.json`,
298
+ connection,
299
+ };
300
+ }
301
+ function localGovernanceStatusCommand(options = {}) {
302
+ const status = buildLocalGovernanceStatus(options);
303
+ if (options.json) {
304
+ console.log(JSON.stringify(status, null, 2));
305
+ if (!status.ok)
306
+ process.exitCode = 1;
307
+ return;
308
+ }
309
+ console.log('');
310
+ console.log(chalk.bold('Neurcode in-flow session'));
311
+ console.log(chalk.dim('-'.repeat(72)));
312
+ console.log(`Repo: ${chalk.white(status.repoRoot)}`);
313
+ if (!status.ok) {
314
+ console.log(chalk.yellow(status.message));
315
+ if (status.connection) {
316
+ const sync = status.connection.autoSync;
317
+ console.log(chalk.dim(`Cloud: connected to ${status.connection.repo.name} · auto-sync ${sync.enabled ? 'on' : 'off'} · ${sync.lastStatus || 'never'}`));
318
+ }
319
+ console.log(chalk.dim('Next: run `neurcode activate claude`, then prompt Claude Code in this repo.'));
320
+ console.log('');
321
+ process.exitCode = 1;
322
+ return;
323
+ }
324
+ const activeStatus = status;
325
+ console.log(`Session: ${chalk.white(activeStatus.sessionId)} ${activeStatus.active ? chalk.green('active') : chalk.dim(activeStatus.status)}`);
326
+ console.log(`Goal: ${chalk.white(truncate(activeStatus.goal))}`);
327
+ console.log(`Scope: ${chalk.white(activeStatus.scopeMode)}`);
328
+ console.log(`Plan: ${chalk.white(activeStatus.planCoherenceMode)}${activeStatus.agentPlanRevision ? chalk.dim(` · rev ${activeStatus.agentPlanRevision}`) : ''}`);
329
+ console.log(`Agent: ${chalk.white(activeStatus.agentInvocation.status.replace(/_/g, ' '))}` +
330
+ chalk.dim(` · score ${activeStatus.agentInvocation.score}`) +
331
+ chalk.dim(` · checks ${activeStatus.agentInvocation.preWriteCheckCount}`));
332
+ if (activeStatus.agentGuard.status !== 'not_started') {
333
+ console.log(`Guard: ${chalk.white(activeStatus.agentGuard.status.replace(/_/g, ' '))}` +
334
+ chalk.dim(` · changed ${activeStatus.agentGuard.summary.changedFiles}`) +
335
+ chalk.dim(` · unverified ${activeStatus.agentGuard.summary.unverifiedWrites}`) +
336
+ chalk.dim(` · denied-changed ${activeStatus.agentGuard.summary.deniedButChanged}`));
337
+ }
338
+ if (activeStatus.agentSupervisor.effectiveStatus !== 'missing') {
339
+ console.log(`Watch: ${chalk.white(activeStatus.agentSupervisor.effectiveStatus.replace(/_/g, ' '))}` +
340
+ chalk.dim(` · ${activeStatus.agentSupervisor.alive ? 'alive' : 'not running'}`) +
341
+ chalk.dim(` · checks ${activeStatus.agentSupervisor.state?.evaluationCount ?? 0}`));
342
+ }
343
+ if (activeStatus.agentPlan?.summary) {
344
+ console.log(`Plan: ${chalk.white(truncate(activeStatus.agentPlan.summary))}`);
345
+ }
346
+ if (activeStatus.agentInvocation.gaps[0]) {
347
+ console.log(`Next: ${chalk.yellow(activeStatus.agentInvocation.nextAction)}`);
348
+ }
349
+ if (activeStatus.pendingPlanAmendments.length > 0) {
350
+ const proposal = activeStatus.pendingPlanAmendments[0];
351
+ console.log(`Re-plan: ${chalk.yellow(`${proposal.proposalId} pending human decision · ${proposal.risk.level} risk`)}`);
352
+ }
353
+ const obligationSummary = (0, governance_runtime_1.summarizeArchitectureObligations)(activeStatus.architectureObligations);
354
+ console.log(`Obligations: ${chalk.white(`${obligationSummary.satisfied}/${obligationSummary.total} satisfied`)}${obligationSummary.criticalPending ? chalk.yellow(` · ${obligationSummary.criticalPending} critical pending`) : ''}`);
355
+ for (const obligation of activeStatus.architectureObligations.filter((item) => item.status === 'pending').slice(0, 3)) {
356
+ console.log(chalk.dim(` pending ${obligation.severity.padEnd(8)} ${obligation.title}`));
357
+ }
358
+ console.log(`Allowed: ${chalk.dim(compactList(activeStatus.allowedGlobs))}`);
359
+ console.log(`Gates: ${chalk.dim(compactList(activeStatus.approvalRequiredGlobs))}`);
360
+ console.log(`Approved:${chalk.dim(' ' + compactList(activeStatus.approvedPaths))}`);
361
+ console.log('');
362
+ console.log(chalk.bold('Recent events'));
363
+ if (activeStatus.recentEvents.length === 0) {
364
+ console.log(chalk.dim(' none'));
365
+ }
366
+ else {
367
+ for (const event of activeStatus.recentEvents) {
368
+ const target = event.filePath || event.decision || '';
369
+ console.log(chalk.dim(` ${eventLabel(event).padEnd(7)} ${target}`));
370
+ }
371
+ }
372
+ console.log('');
373
+ if (activeStatus.latestBlock?.suggestedApprovalPath) {
374
+ console.log(chalk.bold('Latest block'));
375
+ console.log(` Path: ${chalk.white(activeStatus.latestBlock.filePath || activeStatus.latestBlock.suggestedApprovalPath)}`);
376
+ if (activeStatus.latestBlock.owners.length > 0) {
377
+ console.log(` Owner: ${chalk.white(activeStatus.latestBlock.owners.join(', '))}`);
378
+ }
379
+ console.log(` UI: ${chalk.cyan(`Approve exactly ${activeStatus.latestBlock.suggestedApprovalPath} in Runtime Control Plane`)}`);
380
+ console.log(` CLI: ${chalk.cyan(activeStatus.latestBlock.approveCommand)}`);
381
+ console.log(chalk.dim(` MCP: neurcode_session_approve({ path: "${activeStatus.latestBlock.suggestedApprovalPath}" })`));
382
+ console.log('');
383
+ }
384
+ console.log(chalk.dim(`Record: ${activeStatus.recordPath}`));
385
+ if (activeStatus.connection) {
386
+ const sync = activeStatus.connection.autoSync;
387
+ const transport = (0, runtime_outbox_1.inspectRuntimeOutbox)(activeStatus.repoRoot);
388
+ console.log(chalk.dim(`Cloud: connected to ${activeStatus.connection.repo.name} · auto-sync ${sync.enabled ? 'on' : 'off'} · ${sync.lastStatus || 'never'}`));
389
+ console.log(chalk.dim(`Live: ${transport.health} · ${transport.pendingEvents} queued · ${transport.retryingEvents} retrying · ${transport.deadLetterEvents} dead-lettered ` +
390
+ `${transport.lastError ? `· last error ${transport.lastError}` : ''}`));
391
+ }
392
+ console.log('');
393
+ }
394
+ async function resetStaleGovernanceSessionCommand(options = {}) {
395
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
396
+ const now = new Date();
397
+ const maxAgeMinutes = Number.isFinite(options.maxAgeMinutes)
398
+ ? Math.max(0, Number(options.maxAgeMinutes))
399
+ : 120;
400
+ const active = (0, governance_runtime_1.loadActiveSession)(repoRoot);
401
+ const output = (payload, statusCode = 0) => {
402
+ if (options.json) {
403
+ console.log(JSON.stringify(payload, null, 2));
404
+ }
405
+ else if (payload.ok === true) {
406
+ console.log(chalk.green(String(payload.message || 'Stale session reset complete.')));
407
+ if (payload.sessionId)
408
+ console.log(chalk.dim(`Session: ${payload.sessionId}`));
409
+ if (payload.replayHash)
410
+ console.log(chalk.dim(`replayHash: ${payload.replayHash}`));
411
+ }
412
+ else {
413
+ console.error(chalk.yellow(String(payload.message || payload.error || 'No reset performed.')));
414
+ if (payload.suggestedApprovalPath) {
415
+ console.error(chalk.dim(`Pending approval: ${payload.suggestedApprovalPath}`));
416
+ }
417
+ }
418
+ process.exitCode = statusCode;
419
+ };
420
+ if (!active || active.status !== 'active') {
421
+ output({
422
+ ok: true,
423
+ reset: false,
424
+ repoRoot,
425
+ reason: 'no_active_session',
426
+ message: 'No active in-flow governance session found.',
427
+ });
428
+ return;
429
+ }
430
+ const ageMinutes = sessionAgeMinutes(active, now);
431
+ const pending = pendingApprovalBlock(active, now);
432
+ const stale = ageMinutes >= maxAgeMinutes;
433
+ if (!stale && options.force !== true) {
434
+ output({
435
+ ok: true,
436
+ reset: false,
437
+ repoRoot,
438
+ sessionId: active.sessionId,
439
+ reason: 'session_not_stale',
440
+ ageMinutes: Number(ageMinutes.toFixed(2)),
441
+ maxAgeMinutes,
442
+ pendingApproval: pending,
443
+ message: `Active session ${active.sessionId} is not stale yet (${ageMinutes.toFixed(1)}m < ${maxAgeMinutes}m).`,
444
+ });
445
+ return;
446
+ }
447
+ if (pending && options.force !== true) {
448
+ output({
449
+ ok: false,
450
+ reset: false,
451
+ repoRoot,
452
+ sessionId: active.sessionId,
453
+ reason: 'pending_approval',
454
+ ageMinutes: Number(ageMinutes.toFixed(2)),
455
+ maxAgeMinutes,
456
+ filePath: pending.filePath,
457
+ owners: pending.owners,
458
+ suggestedApprovalPath: pending.suggestedApprovalPath,
459
+ message: 'Active session is waiting on an unresolved approval; refusing to reset without --force.',
460
+ next: [
461
+ `Approve exactly ${pending.suggestedApprovalPath} from the dashboard or MCP.`,
462
+ 'Or run `neurcode session reset-stale --force` if this is abandoned rehearsal state.',
463
+ ],
464
+ }, 2);
465
+ return;
466
+ }
467
+ (0, governance_runtime_1.appendEvent)(repoRoot, active.sessionId, {
468
+ type: 'user_decision',
469
+ ts: now.toISOString(),
470
+ decision: 'reset_stale_session',
471
+ detail: {
472
+ source: 'local_cli',
473
+ force: options.force === true,
474
+ maxAgeMinutes,
475
+ ageMinutes: Number(ageMinutes.toFixed(2)),
476
+ pendingApproval: pending,
477
+ },
478
+ });
479
+ const finished = (0, governance_runtime_1.finishSession)(repoRoot, active.sessionId);
480
+ if (!finished) {
481
+ output({
482
+ ok: false,
483
+ reset: false,
484
+ repoRoot,
485
+ sessionId: active.sessionId,
486
+ reason: 'finish_failed',
487
+ message: `Could not finish active session ${active.sessionId}.`,
488
+ }, 1);
489
+ return;
490
+ }
491
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, finished);
492
+ output({
493
+ ok: true,
494
+ reset: true,
495
+ repoRoot,
496
+ sessionId: finished.sessionId,
497
+ previousGoal: finished.contract.goal,
498
+ status: finished.status,
499
+ replayHash: finished.replayHash,
500
+ ageMinutes: Number(ageMinutes.toFixed(2)),
501
+ maxAgeMinutes,
502
+ forced: options.force === true,
503
+ recordPath: `.neurcode/sessions/${finished.sessionId}.json`,
504
+ message: `Finished active session ${finished.sessionId} and cleared the active pointer.`,
505
+ });
506
+ }
507
+ async function replanGovernanceSessionCommand(options = {}) {
508
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
509
+ let planText = options.plan;
510
+ if (!planText && options.planFile) {
511
+ const planPath = (0, node_path_1.isAbsolute)(options.planFile)
512
+ ? options.planFile
513
+ : (0, node_path_1.resolve)(repoRoot, options.planFile);
514
+ planText = (0, node_fs_1.readFileSync)(planPath, 'utf8');
515
+ }
516
+ // `amend-plan --scope <glob>` is sugar for adding expected globs to the plan;
517
+ // merge it with any explicit --add-glob values.
518
+ const addExpectedGlobs = [...(options.addGlob || []), ...(options.scope || [])];
519
+ try {
520
+ const result = (0, governance_runtime_1.amendAgentPlan)(repoRoot, {
521
+ sessionId: options.sessionId,
522
+ planText,
523
+ summary: options.summary,
524
+ addSteps: options.addStep,
525
+ removeSteps: options.removeStep,
526
+ addExpectedFiles: options.addFile,
527
+ removeExpectedFiles: options.removeFile,
528
+ addExpectedGlobs,
529
+ removeExpectedGlobs: options.removeGlob,
530
+ addConstraints: options.addConstraint,
531
+ removeConstraints: options.removeConstraint,
532
+ addRisks: options.addRisk,
533
+ removeRisks: options.removeRisk,
534
+ reason: options.reason,
535
+ source: 'manual',
536
+ proposedBy: options.proposedBy || 'human',
537
+ decidedBy: options.decidedBy,
538
+ });
539
+ const session = (0, governance_runtime_1.loadSession)(repoRoot, result.sessionId);
540
+ if (session)
541
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session);
542
+ if (options.json) {
543
+ console.log(JSON.stringify({ ok: true, repoRoot, ...result }, null, 2));
544
+ return;
545
+ }
546
+ console.log('');
547
+ if (result.status === 'pending') {
548
+ console.log(chalk.yellow(`Plan amendment pending human decision: ${result.proposal?.proposalId || result.eventId}`));
549
+ console.log(chalk.dim(`Risk: ${result.risk.level}`));
550
+ console.log(chalk.dim(`Reasons: ${compactList(result.risk.reasons, 6)}`));
551
+ console.log(chalk.dim(`Accept: neurcode session replan-decide --proposal-id ${result.proposal?.proposalId || result.eventId} --decision accept --reason "<why>"`));
552
+ console.log('');
553
+ return;
554
+ }
555
+ console.log(chalk.green(`Plan updated: revision ${result.previousRevision} -> ${result.revision}`));
556
+ console.log(chalk.dim(`Session: ${result.sessionId}`));
557
+ console.log(chalk.dim(`Action: ${result.action}`));
558
+ console.log(chalk.dim(`Reason: ${result.reason}`));
559
+ const activePlan = result.activePlan;
560
+ if (activePlan?.summary) {
561
+ console.log(`Plan: ${chalk.white(truncate(activePlan.summary))}`);
562
+ }
563
+ if (activePlan?.expectedFiles.length) {
564
+ console.log(chalk.dim(`Files: ${compactList(activePlan.expectedFiles, 12)}`));
565
+ }
566
+ console.log('');
567
+ }
568
+ catch (error) {
569
+ const message = error instanceof Error ? error.message : String(error);
570
+ if (options.json) {
571
+ console.log(JSON.stringify({ ok: false, repoRoot, error: message }, null, 2));
572
+ }
573
+ else {
574
+ (0, messages_1.printError)('Re-plan Failed', message, [
575
+ 'Use --plan "<new plan>" for a full replacement, or --add-step / --add-file for a patch.',
576
+ ]);
577
+ }
578
+ process.exitCode = 1;
579
+ }
580
+ }
581
+ async function decideGovernanceReplanCommand(options = {}) {
582
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
583
+ if (!options.proposalId || !options.decision) {
584
+ (0, messages_1.printError)('Missing Re-plan Decision', undefined, [
585
+ 'Usage: neurcode session replan-decide --proposal-id <id> --decision <accept|reject> --reason "<why>"',
586
+ ]);
587
+ process.exitCode = 2;
588
+ return;
589
+ }
590
+ try {
591
+ const result = (0, governance_runtime_1.decideAgentPlanAmendment)(repoRoot, {
592
+ sessionId: options.sessionId,
593
+ proposalId: options.proposalId,
594
+ decision: options.decision,
595
+ reason: options.reason,
596
+ decidedBy: options.decidedBy,
597
+ source: 'manual',
598
+ });
599
+ const session = (0, governance_runtime_1.loadSession)(repoRoot, result.sessionId);
600
+ if (session)
601
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session);
602
+ if (options.json) {
603
+ console.log(JSON.stringify({ ok: true, repoRoot, ...result }, null, 2));
604
+ return;
605
+ }
606
+ console.log('');
607
+ console.log(result.decision === 'accept'
608
+ ? chalk.green(`Plan amendment accepted: ${result.proposalId}`)
609
+ : chalk.yellow(`Plan amendment rejected: ${result.proposalId}`));
610
+ console.log(chalk.dim(`Session: ${result.sessionId}`));
611
+ console.log(chalk.dim(`Status: ${result.status}`));
612
+ if (result.revision)
613
+ console.log(chalk.dim(`Revision: ${result.previousRevision} -> ${result.revision}`));
614
+ console.log('');
615
+ }
616
+ catch (error) {
617
+ const message = error instanceof Error ? error.message : String(error);
618
+ if (options.json) {
619
+ console.log(JSON.stringify({ ok: false, repoRoot, error: message }, null, 2));
620
+ }
621
+ else {
622
+ (0, messages_1.printError)('Re-plan Decision Failed', message);
623
+ }
624
+ process.exitCode = 1;
625
+ }
626
+ }
627
+ async function approveGovernanceSessionCommand(options = {}) {
628
+ const path = options.path;
629
+ if (!path) {
630
+ (0, messages_1.printError)('Missing Approval Path', undefined, ['Usage: neurcode session approve --path <file-or-glob>']);
631
+ process.exitCode = 2;
632
+ return;
633
+ }
634
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
635
+ try {
636
+ const result = (0, governance_runtime_1.approveSession)(repoRoot, path, options.reason, options.sessionId);
637
+ const session = (0, governance_runtime_1.loadSession)(repoRoot, result.sessionId);
638
+ if (session)
639
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session);
640
+ if (options.json) {
641
+ console.log(JSON.stringify({ ok: true, repoRoot, ...result }, null, 2));
642
+ return;
643
+ }
644
+ console.log('');
645
+ console.log(chalk.green(`Approved: ${result.approvedPath}`));
646
+ console.log(chalk.dim(`Session: ${result.sessionId}`));
647
+ console.log(chalk.dim(`Approved paths: ${compactList(result.approvedPaths, 12)}`));
648
+ console.log('');
649
+ }
650
+ catch (error) {
651
+ const message = error instanceof Error ? error.message : String(error);
652
+ if (options.json) {
653
+ console.log(JSON.stringify({ ok: false, repoRoot, error: message }, null, 2));
654
+ }
655
+ else {
656
+ (0, messages_1.printError)('Approval Failed', message);
657
+ }
658
+ process.exitCode = 1;
659
+ }
660
+ }
661
+ function showGovernanceObligationsCommand(options = {}) {
662
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
663
+ const session = loadLocalGovernanceSession(repoRoot, options.sessionId);
664
+ if (!session) {
665
+ const message = options.sessionId
666
+ ? `Local governance session ${options.sessionId} was not found.`
667
+ : 'No active in-flow governance session found.';
668
+ if (options.json)
669
+ console.log(JSON.stringify({ ok: false, repoRoot, error: message }, null, 2));
670
+ else
671
+ (0, messages_1.printError)('Architecture Obligations Unavailable', message);
672
+ process.exitCode = 1;
673
+ return;
674
+ }
675
+ const obligations = session.contract.architectureObligations ?? [];
676
+ const summary = (0, governance_runtime_1.summarizeArchitectureObligations)(obligations);
677
+ if (options.json) {
678
+ console.log(JSON.stringify({
679
+ ok: true,
680
+ repoRoot,
681
+ sessionId: session.sessionId,
682
+ summary,
683
+ obligations,
684
+ waivers: session.contract.architectureObligationWaivers ?? [],
685
+ policy: session.contract.architectureObligationPolicy ?? { mode: 'warn', ruleModes: {} },
686
+ }, null, 2));
687
+ return;
688
+ }
689
+ console.log('');
690
+ console.log(chalk.bold(`Architecture obligations · ${session.sessionId}`));
691
+ console.log(chalk.dim('-'.repeat(72)));
692
+ const policy = session.contract.architectureObligationPolicy ?? { mode: 'warn', ruleModes: {} };
693
+ console.log(`Policy: ${chalk.white(policy.mode)}${Object.keys(policy.ruleModes).length ? chalk.dim(` · ${Object.keys(policy.ruleModes).length} rule override(s)`) : ''}`);
694
+ console.log(`Summary: ${chalk.white(`${summary.satisfied}/${summary.total} satisfied`)}${summary.waived ? chalk.yellow(` · ${summary.waived} waived`) : ''}${summary.blockingPending ? chalk.red(` · ${summary.blockingPending} blocking pending`) : summary.criticalPending ? chalk.yellow(` · ${summary.criticalPending} critical pending`) : ''}`);
695
+ console.log('');
696
+ if (obligations.length === 0) {
697
+ console.log(chalk.dim('No deterministic architecture obligations derived for this session.'));
698
+ }
699
+ for (const obligation of obligations) {
700
+ const status = obligation.status === 'satisfied'
701
+ ? chalk.green('satisfied')
702
+ : obligation.status === 'waived'
703
+ ? chalk.yellow('waived')
704
+ : chalk.yellow('pending');
705
+ console.log(`${status.padEnd(18)} ${chalk.white(obligation.title)} ${chalk.dim(`[${obligation.effectiveMode ?? 'warn'}]`)}`);
706
+ console.log(chalk.dim(` ${obligation.requiredEvidence[0]}`));
707
+ if (obligation.observedEvidence[0])
708
+ console.log(chalk.dim(` evidence: ${obligation.observedEvidence[0].summary}`));
709
+ if (obligation.status === 'pending')
710
+ console.log(chalk.dim(` waive: neurcode session waive-obligation --id ${obligation.id} --reason "<why>"`));
711
+ }
712
+ console.log('');
713
+ }
714
+ async function waiveGovernanceObligationCommand(options = {}) {
715
+ if (!options.obligationId) {
716
+ (0, messages_1.printError)('Missing Obligation ID', undefined, [
717
+ 'Usage: neurcode session waive-obligation --id <obligation-id> --reason "<why>"',
718
+ ]);
719
+ process.exitCode = 2;
720
+ return;
721
+ }
722
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
723
+ try {
724
+ const result = (0, governance_runtime_1.waiveArchitectureObligation)(repoRoot, options.obligationId, {
725
+ reason: options.reason,
726
+ sessionId: options.sessionId,
727
+ expiresAt: options.expiresAt,
728
+ ttlMs: typeof options.ttlMinutes === 'number' && Number.isFinite(options.ttlMinutes)
729
+ ? Math.max(0, Math.floor(options.ttlMinutes * 60 * 1000))
730
+ : undefined,
731
+ waivedBy: options.waivedBy,
732
+ source: options.waiverSource || 'local_cli',
733
+ });
734
+ const session = (0, governance_runtime_1.loadSession)(repoRoot, result.sessionId);
735
+ if (session)
736
+ await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session);
737
+ if (options.json) {
738
+ console.log(JSON.stringify({ ok: true, repoRoot, ...result }, null, 2));
739
+ return;
740
+ }
741
+ console.log('');
742
+ console.log(chalk.yellow(`Waived obligation: ${result.obligationId}`));
743
+ console.log(chalk.dim(`Session: ${result.sessionId}`));
744
+ console.log(chalk.dim(`Expires: ${result.expiresAt || 'never'}`));
745
+ console.log(chalk.dim(`Reason: ${result.waiver.reason}`));
746
+ console.log('');
747
+ }
748
+ catch (error) {
749
+ const message = error instanceof Error ? error.message : String(error);
750
+ if (options.json) {
751
+ console.log(JSON.stringify({ ok: false, repoRoot, error: message }, null, 2));
752
+ }
753
+ else {
754
+ (0, messages_1.printError)('Obligation Waiver Failed', message);
755
+ }
756
+ process.exitCode = 1;
757
+ }
758
+ }
759
+ function listRuntimeSessionsCommand(options = {}) {
760
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
761
+ const records = (0, runtime_evidence_1.listRuntimeSessions)(repoRoot);
762
+ if (options.json) {
763
+ console.log(JSON.stringify({
764
+ ok: true,
765
+ repoRoot,
766
+ count: records.length,
767
+ sessions: records.map((record) => ({
768
+ sessionId: record.session.sessionId,
769
+ status: record.session.status,
770
+ goal: record.session.contract.goal,
771
+ scopeMode: record.session.contract.scopeMode,
772
+ blockCount: record.blockCount,
773
+ warnCount: record.warnCount,
774
+ okCount: record.okCount,
775
+ approvalCount: record.approvalCount,
776
+ approvedPaths: record.session.contract.approvedPaths,
777
+ replayHash: record.session.replayHash,
778
+ recordPath: record.path,
779
+ })),
780
+ }, null, 2));
781
+ return;
782
+ }
783
+ console.log('');
784
+ console.log(chalk.bold('Neurcode in-flow sessions'));
785
+ console.log(chalk.dim('-'.repeat(96)));
786
+ console.log(`Repo: ${chalk.white(repoRoot)}`);
787
+ console.log('');
788
+ if (records.length === 0) {
789
+ console.log(chalk.dim('No local governance sessions found.'));
790
+ console.log(chalk.dim('Next: run `neurcode activate claude`, then prompt Claude Code in this repo.'));
791
+ console.log('');
792
+ return;
793
+ }
794
+ const rows = [['Session', 'Status', 'Scope', 'Blocks', 'Warns', 'Approvals', 'Goal']];
795
+ for (const record of records) {
796
+ rows.push([
797
+ record.session.sessionId,
798
+ record.session.status,
799
+ record.session.contract.scopeMode,
800
+ String(record.blockCount),
801
+ String(record.warnCount),
802
+ String(record.approvalCount),
803
+ truncate(record.session.contract.goal, 44),
804
+ ]);
805
+ }
806
+ (0, messages_1.printTable)(rows);
807
+ console.log(chalk.dim('Show details: neurcode session show <session-id>'));
808
+ console.log('');
809
+ }
810
+ function showRuntimeSessionCommand(sessionId, options = {}) {
811
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
812
+ const session = (0, governance_runtime_1.loadSession)(repoRoot, sessionId);
813
+ if (!session) {
814
+ if (options.json) {
815
+ console.log(JSON.stringify({ ok: false, repoRoot, error: `Session not found: ${sessionId}` }, null, 2));
816
+ }
817
+ else {
818
+ (0, messages_1.printError)('Session Not Found', `No local governance session found for ${sessionId}`);
819
+ }
820
+ process.exitCode = 1;
821
+ return;
822
+ }
823
+ const records = (0, runtime_evidence_1.listRuntimeSessions)(repoRoot);
824
+ const record = records.find((candidate) => candidate.session.sessionId === sessionId);
825
+ const blockCount = record?.blockCount ?? session.events.filter((event) => event.type === 'check_block').length;
826
+ const warnCount = record?.warnCount ?? session.events.filter((event) => event.type === 'check_warn').length;
827
+ const okCount = record?.okCount ?? session.events.filter((event) => event.type === 'check_ok').length;
828
+ const approvalCount = record?.approvalCount ?? session.events.filter((event) => event.type === 'approval_decision').length;
829
+ const payload = {
830
+ ok: true,
831
+ repoRoot,
832
+ sessionId: session.sessionId,
833
+ status: session.status,
834
+ goal: session.contract.goal,
835
+ scopeMode: session.contract.scopeMode,
836
+ agentPlan: session.contract.agentPlan ?? null,
837
+ agentPlanRevision: session.contract.agentPlanRevision ?? (session.contract.agentPlan ? 1 : null),
838
+ agentPlanRevisions: session.contract.agentPlanRevisions ?? [],
839
+ architectureObligations: session.contract.architectureObligations ?? [],
840
+ allowedGlobs: session.contract.allowedGlobs,
841
+ approvalRequiredGlobs: session.contract.approvalRequiredGlobs,
842
+ approvedPaths: session.contract.approvedPaths,
843
+ blockCount,
844
+ warnCount,
845
+ okCount,
846
+ approvalCount,
847
+ replayHash: session.replayHash,
848
+ recordPath: record?.path ?? `.neurcode/sessions/${session.sessionId}.json`,
849
+ events: session.events,
850
+ };
851
+ if (options.json) {
852
+ console.log(JSON.stringify(payload, null, 2));
853
+ return;
854
+ }
855
+ console.log('');
856
+ console.log(chalk.bold(`Neurcode session ${session.sessionId}`));
857
+ console.log(chalk.dim('-'.repeat(72)));
858
+ console.log(`Status: ${chalk.white(session.status)}`);
859
+ console.log(`Goal: ${chalk.white(session.contract.goal)}`);
860
+ console.log(`Scope: ${chalk.white(session.contract.scopeMode)}`);
861
+ console.log(`Plan: ${chalk.white(session.contract.planCoherenceMode ?? 'warn')}${session.contract.agentPlanRevision ? chalk.dim(` · rev ${session.contract.agentPlanRevision}`) : ''}`);
862
+ if (session.contract.agentPlan?.summary) {
863
+ console.log(`Agent: ${chalk.white(truncate(session.contract.agentPlan.summary))}`);
864
+ }
865
+ const obligationSummary = (0, governance_runtime_1.summarizeArchitectureObligations)(session.contract.architectureObligations ?? []);
866
+ console.log(`Obligations: ${chalk.white(`${obligationSummary.satisfied}/${obligationSummary.total} satisfied`)}${obligationSummary.criticalPending ? chalk.yellow(` · ${obligationSummary.criticalPending} critical pending`) : ''}`);
867
+ console.log(`Allowed: ${chalk.dim(compactList(session.contract.allowedGlobs))}`);
868
+ console.log(`Gates: ${chalk.dim(compactList(session.contract.approvalRequiredGlobs))}`);
869
+ console.log(`Approved: ${chalk.dim(compactList(session.contract.approvedPaths))}`);
870
+ console.log(`Events: ok=${okCount} warn=${warnCount} block=${blockCount} approvals=${approvalCount}`);
871
+ console.log(`Replay: ${chalk.dim(session.replayHash ?? 'n/a')}`);
872
+ console.log('');
873
+ console.log(chalk.bold('Timeline'));
874
+ for (const event of session.events) {
875
+ const target = event.filePath || event.decision || event.message || '';
876
+ console.log(chalk.dim(` ${event.ts} ${eventLabel(event).padEnd(7)} ${target}`));
877
+ }
878
+ console.log('');
879
+ }
880
+ function resolveAIChangeRecordSession(repoRoot, options) {
881
+ if (options.sessionId)
882
+ return (0, governance_runtime_1.loadSession)(repoRoot, options.sessionId);
883
+ const active = (0, governance_runtime_1.loadActiveSession)(repoRoot);
884
+ if (active && !options.latest)
885
+ return active;
886
+ const [latest] = (0, runtime_evidence_1.listRuntimeSessions)(repoRoot);
887
+ return latest?.session ?? active ?? null;
888
+ }
889
+ function renderAIChangeRecord(record, recordPath) {
890
+ const counts = record.session.counts;
891
+ const blocked = record.trajectory.filter((path) => path.verdicts.includes('block'));
892
+ const warned = record.trajectory.filter((path) => path.verdicts.includes('warn'));
893
+ const activeApprovals = record.approvals.filter((approval) => approval.status === 'active');
894
+ const pendingAmendments = record.plan.pendingAmendments.length;
895
+ const consequenceImpacts = topConsequenceImpactsFromRecord(record.understanding.latest?.consequenceUnderstanding);
896
+ const consequenceFindings = topConsequenceFindingsFromRecord(record.understanding.latest?.consequenceUnderstanding);
897
+ console.log('');
898
+ console.log(chalk.bold(`AI Change Record ${record.session.sessionId}`));
899
+ console.log(chalk.dim('-'.repeat(76)));
900
+ console.log(`Repo: ${chalk.white(record.session.repoName)}`);
901
+ console.log(`Status: ${chalk.white(record.session.status)}${record.integrity.replayHashStatus === 'present' ? '' : chalk.yellow(' · replay pending')}`);
902
+ console.log(`Goal: ${chalk.white(truncate(record.intent.contract?.summary || record.session.goal, 120))}`);
903
+ console.log(`Scope: ${chalk.white(record.session.scopeMode)} · ${chalk.dim(compactList(record.scope.allowedGlobs, 8))}`);
904
+ console.log(`Plan: ${chalk.white(record.plan.activeSummary ? truncate(record.plan.activeSummary, 120) : 'not captured')}${record.plan.activeRevision ? chalk.dim(` · rev ${record.plan.activeRevision}`) : ''}`);
905
+ if (pendingAmendments > 0) {
906
+ console.log(`Re-plan: ${chalk.yellow(`${pendingAmendments} pending amendment${pendingAmendments === 1 ? '' : 's'}`)}`);
907
+ }
908
+ console.log(`Checks: ok=${counts.ok} warn=${counts.warn} block=${counts.block} approvals=${counts.approval}`);
909
+ console.log(`Oblig: ${record.architecture.summary.satisfied}/${record.architecture.summary.total} satisfied${record.architecture.summary.blockingPending ? chalk.yellow(` · ${record.architecture.summary.blockingPending} blocking`) : ''}`);
910
+ console.log(`Approvals: ${activeApprovals.length} active · ${record.approvals.length} lifecycle entr${record.approvals.length === 1 ? 'y' : 'ies'}`);
911
+ if (record.understanding.latest) {
912
+ const understanding = record.understanding.latest;
913
+ console.log(`Understand: ${understanding.changedSymbolCount} changed symbols · ` +
914
+ `${understanding.referenceCount} references · ${understanding.testReferenceCount} test refs`);
915
+ const digest = understanding.digest;
916
+ const topConsequences = digest?.topConsequences ?? [];
917
+ if (topConsequences.length > 0) {
918
+ const hidden = digest?.hidden && typeof digest.hidden === 'object'
919
+ ? digest.hidden
920
+ : {};
921
+ const hiddenRefs = typeof hidden.references === 'number' ? hidden.references : 0;
922
+ const hiddenTests = typeof hidden.testReferences === 'number' ? hidden.testReferences : 0;
923
+ console.log(`Digest: ${topConsequences.length} consequences · hidden ${hiddenRefs} refs / ${hiddenTests} tests`);
924
+ }
925
+ if (consequenceImpacts.length > 0) {
926
+ const headline = consequenceHeadlineFromRecord(record.understanding.latest?.consequenceUnderstanding);
927
+ if (headline)
928
+ console.log(`Reach: ${headline}`);
929
+ console.log(`Impacts: ${consequenceImpacts.length} grouped impact${consequenceImpacts.length === 1 ? '' : 's'}`);
930
+ for (const impact of consequenceImpacts.slice(0, 5)) {
931
+ const consumers = impact.productionConsumerCount > 0 || impact.testConsumerCount > 0
932
+ ? ` · ${impact.productionConsumerCount} prod file(s), ${impact.testConsumerCount} test file(s)`
933
+ : '';
934
+ const flags = [
935
+ impact.highFanout ? 'high-fanout' : null,
936
+ impact.architectureRelevant ? 'architecture-relevant' : null,
937
+ ].filter(Boolean).join(', ');
938
+ console.log(chalk.dim(` ${impact.rank}. ${truncate(impact.summary, 140)}${consumers}${flags ? ` · ${flags}` : ''}`));
939
+ }
940
+ }
941
+ else if (consequenceFindings.length > 0) {
942
+ const headline = consequenceHeadlineFromRecord(record.understanding.latest?.consequenceUnderstanding);
943
+ if (headline)
944
+ console.log(`Reach: ${headline}`);
945
+ console.log(`Consequences: ${consequenceFindings.length} ranked finding${consequenceFindings.length === 1 ? '' : 's'}`);
946
+ for (const finding of consequenceFindings.slice(0, 5)) {
947
+ const consumers = finding.consumerCount > 0
948
+ ? ` · ${finding.nonTestConsumerCount} non-test consumer(s), ${finding.testConsumerCount} test`
949
+ : '';
950
+ const reasons = finding.reasonCodes.slice(0, 3).join(', ');
951
+ console.log(chalk.dim(` ${finding.rank}. ${truncate(finding.summary, 140)}${consumers}${reasons ? ` · ${reasons}` : ''}`));
952
+ }
953
+ }
954
+ }
955
+ if (blocked.length > 0) {
956
+ console.log('');
957
+ console.log(chalk.bold('Blocked paths'));
958
+ for (const item of blocked.slice(0, 8)) {
959
+ const owners = item.owners.length ? ` · ${item.owners.join(', ')}` : '';
960
+ const approval = item.suggestedApprovalPath ? ` · approve ${item.suggestedApprovalPath}` : '';
961
+ console.log(chalk.dim(` ${item.filePath}${owners}${approval}`));
962
+ }
963
+ if (blocked.length > 8)
964
+ console.log(chalk.dim(` +${blocked.length - 8} more`));
965
+ }
966
+ if (warned.length > 0) {
967
+ console.log('');
968
+ console.log(chalk.bold('Warned paths'));
969
+ for (const item of warned.slice(0, 5)) {
970
+ console.log(chalk.dim(` ${item.filePath}`));
971
+ }
972
+ if (warned.length > 5)
973
+ console.log(chalk.dim(` +${warned.length - 5} more`));
974
+ }
975
+ console.log('');
976
+ console.log(`Record: ${chalk.dim(recordPath)}`);
977
+ console.log(`Hash: ${chalk.dim(record.integrity.recordHash)}`);
978
+ console.log(`Replay: ${chalk.dim(record.integrity.replayHash ?? 'pending-session-finish')}`);
979
+ console.log(chalk.dim('Privacy: source-free; no source code, diff hunks, patches, or shell command bodies.'));
980
+ console.log('');
981
+ }
982
+ function consequenceHeadlineFromRecord(value) {
983
+ if (!value || typeof value !== 'object' || Array.isArray(value))
984
+ return null;
985
+ const summary = value.summary;
986
+ if (!summary || typeof summary !== 'object' || Array.isArray(summary))
987
+ return null;
988
+ const headline = summary.headline;
989
+ return typeof headline === 'string' && headline.trim() ? headline.trim() : null;
990
+ }
991
+ function topConsequenceImpactsFromRecord(value) {
992
+ if (!value || typeof value !== 'object' || Array.isArray(value))
993
+ return [];
994
+ const record = value;
995
+ if (!Array.isArray(record.topImpacts))
996
+ return [];
997
+ return record.topImpacts.flatMap((item) => {
998
+ if (!item || typeof item !== 'object' || Array.isArray(item))
999
+ return [];
1000
+ const row = item;
1001
+ const summary = typeof row.summary === 'string' ? row.summary.trim() : '';
1002
+ if (!summary)
1003
+ return [];
1004
+ const rank = typeof row.rank === 'number' && Number.isFinite(row.rank) ? row.rank : 0;
1005
+ const productionConsumerCount = typeof row.productionConsumerCount === 'number' && Number.isFinite(row.productionConsumerCount) ? row.productionConsumerCount : 0;
1006
+ const testConsumerCount = typeof row.testConsumerCount === 'number' && Number.isFinite(row.testConsumerCount) ? row.testConsumerCount : 0;
1007
+ const highFanout = row.highFanout === true;
1008
+ const architectureRelevant = row.architectureRelevant === true;
1009
+ const reasonCodes = Array.isArray(row.reasonCodes)
1010
+ ? row.reasonCodes.filter((reason) => typeof reason === 'string').slice(0, 6)
1011
+ : [];
1012
+ return [{ rank, summary, productionConsumerCount, testConsumerCount, highFanout, architectureRelevant, reasonCodes }];
1013
+ }).sort((a, b) => a.rank - b.rank || a.summary.localeCompare(b.summary));
1014
+ }
1015
+ function topConsequenceFindingsFromRecord(value) {
1016
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1017
+ return [];
1018
+ const record = value;
1019
+ if (!Array.isArray(record.topFindings))
1020
+ return [];
1021
+ return record.topFindings.flatMap((item) => {
1022
+ if (!item || typeof item !== 'object' || Array.isArray(item))
1023
+ return [];
1024
+ const row = item;
1025
+ const summary = typeof row.summary === 'string' ? row.summary.trim() : '';
1026
+ if (!summary)
1027
+ return [];
1028
+ const rank = typeof row.rank === 'number' && Number.isFinite(row.rank) ? row.rank : 0;
1029
+ const consumerCount = typeof row.consumerCount === 'number' && Number.isFinite(row.consumerCount) ? row.consumerCount : 0;
1030
+ const nonTestConsumerCount = typeof row.nonTestConsumerCount === 'number' && Number.isFinite(row.nonTestConsumerCount) ? row.nonTestConsumerCount : 0;
1031
+ const testConsumerCount = typeof row.testConsumerCount === 'number' && Number.isFinite(row.testConsumerCount) ? row.testConsumerCount : 0;
1032
+ const reasonCodes = Array.isArray(row.reasonCodes)
1033
+ ? row.reasonCodes.filter((reason) => typeof reason === 'string').slice(0, 6)
1034
+ : [];
1035
+ return [{ rank, summary, consumerCount, nonTestConsumerCount, testConsumerCount, reasonCodes }];
1036
+ }).sort((a, b) => a.rank - b.rank || a.summary.localeCompare(b.summary));
1037
+ }
1038
+ function aiChangeRecordCommand(options = {}) {
1039
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
1040
+ const session = resolveAIChangeRecordSession(repoRoot, options);
1041
+ if (!session) {
1042
+ if (options.json) {
1043
+ console.log(JSON.stringify({
1044
+ ok: false,
1045
+ repoRoot,
1046
+ error: options.sessionId
1047
+ ? `Session not found: ${options.sessionId}`
1048
+ : 'No local governance sessions found.',
1049
+ }, null, 2));
1050
+ }
1051
+ else {
1052
+ (0, messages_1.printError)('AI Change Record Not Found', options.sessionId
1053
+ ? `No local governance session found for ${options.sessionId}`
1054
+ : 'No local governance sessions found.');
1055
+ }
1056
+ process.exitCode = 1;
1057
+ return;
1058
+ }
1059
+ const { record, path } = (0, governance_runtime_1.writeAIChangeRecord)(repoRoot, session);
1060
+ if (options.json) {
1061
+ console.log(JSON.stringify({
1062
+ ok: true,
1063
+ repoRoot,
1064
+ recordPath: path,
1065
+ record,
1066
+ }, null, 2));
1067
+ return;
1068
+ }
1069
+ renderAIChangeRecord(record, path.replace(`${repoRoot}/`, ''));
1070
+ }
1071
+ function referenceLabel(ref) {
1072
+ const owner = ref.referencingSymbol
1073
+ ? `${ref.referencingFile}#${ref.referencingSymbol}:${ref.line}`
1074
+ : `${ref.referencingFile}:${ref.line}`;
1075
+ const test = ref.isTestFile ? ' test' : '';
1076
+ return `${ref.targetFile}#${ref.targetSymbol} <- ${owner}${test}`;
1077
+ }
1078
+ function structuralEventDetail(artifact, artifactPath, repoRoot) {
1079
+ return {
1080
+ schemaVersion: artifact.schemaVersion,
1081
+ artifactHash: artifact.artifactHash,
1082
+ artifactPath: artifactPath.replace(`${repoRoot}/`, ''),
1083
+ analysis: artifact.analysis,
1084
+ changedSymbols: artifact.changedSymbols.map((symbol) => ({
1085
+ file: symbol.file,
1086
+ name: symbol.name,
1087
+ kind: symbol.kind,
1088
+ action: symbol.action,
1089
+ })),
1090
+ topReferences: artifact.references.slice(0, 25).map((ref) => ({
1091
+ targetFile: ref.targetFile,
1092
+ targetSymbol: ref.targetSymbol,
1093
+ referencingFile: ref.referencingFile,
1094
+ referencingSymbol: ref.referencingSymbol,
1095
+ line: ref.line,
1096
+ isTestFile: ref.isTestFile,
1097
+ })),
1098
+ suppressedArtifacts: artifact.suppressedArtifacts,
1099
+ digest: artifact.digest,
1100
+ consequenceUnderstanding: artifact.consequenceUnderstanding,
1101
+ planAlignment: artifact.planAlignment,
1102
+ boundaryImpact: artifact.boundaryImpact,
1103
+ privacy: artifact.privacy,
1104
+ };
1105
+ }
1106
+ function renderStructuralUnderstanding(artifact, artifactPath, repoRoot) {
1107
+ const analysis = artifact.analysis;
1108
+ console.log('');
1109
+ console.log(chalk.bold('Local Structural Understanding'));
1110
+ console.log(chalk.dim('-'.repeat(76)));
1111
+ console.log(`Mode: ${analysis.confidence}${analysis.reason ? chalk.yellow(` · ${analysis.reason}`) : ''}`);
1112
+ console.log(`Files: ${analysis.changedFileCount} changed · ${analysis.filesAnalyzed} analyzed`);
1113
+ console.log(`Symbols: ${analysis.changedSymbolCount} changed`);
1114
+ console.log(`Edges: ${analysis.referenceCount} references · ${analysis.testReferenceCount} test references`);
1115
+ if (artifact.suppressedArtifacts?.length > 0) {
1116
+ const preview = artifact.suppressedArtifacts
1117
+ .slice(0, 3)
1118
+ .map((item) => `${item.path} (${item.reasonCode})`)
1119
+ .join(', ');
1120
+ const suffix = artifact.suppressedArtifacts.length > 3
1121
+ ? `, +${artifact.suppressedArtifacts.length - 3} more`
1122
+ : '';
1123
+ console.log(chalk.yellow(`Suppressed: ${artifact.suppressedArtifacts.length} generated artifact(s): ${preview}${suffix}`));
1124
+ }
1125
+ const consequence = artifact.consequenceUnderstanding;
1126
+ if (consequence?.analyzed &&
1127
+ (consequence.topImpacts.length > 0 ||
1128
+ consequence.topFindings.length > 0 ||
1129
+ consequence.effectDeltas.length > 0 ||
1130
+ consequence.contractDeltas.length > 0 ||
1131
+ consequence.inheritorProjections.length > 0)) {
1132
+ console.log('');
1133
+ console.log(chalk.bold('Consequence understanding'));
1134
+ if (consequence.summary.headline) {
1135
+ console.log(chalk.dim(` ${consequence.summary.headline}`));
1136
+ }
1137
+ if (consequence.topImpacts.length > 0) {
1138
+ for (const impact of consequence.topImpacts.slice(0, 6)) {
1139
+ const reachable = impact.reachableProductionConsumerCount ?? impact.productionConsumerCount;
1140
+ const external = impact.externalProductionConsumerCount ?? 0;
1141
+ const consumers = reachable > 0 || impact.testConsumerCount > 0
1142
+ ? ` · ${reachable} reachable prod file(s), ${external} external, ${impact.testConsumerCount} test file(s)`
1143
+ : '';
1144
+ const flags = [
1145
+ impact.highFanout ? 'high-fanout' : null,
1146
+ impact.architectureRelevant ? 'architecture-relevant' : null,
1147
+ ].filter(Boolean).join(', ');
1148
+ const reasons = impact.reasonCodes.slice(0, 3).join(', ');
1149
+ console.log(chalk.dim(` ${impact.rank}. ${impact.summary}${consumers}${flags ? ` · ${flags}` : ''}${reasons ? ` · ${reasons}` : ''}`));
1150
+ }
1151
+ if (consequence.topFindings.length > consequence.topImpacts.length) {
1152
+ console.log(chalk.dim(` Raw findings: ${consequence.topFindings.length} deterministic finding(s) collapsed into ${consequence.topImpacts.length} impact(s).`));
1153
+ }
1154
+ }
1155
+ else if (consequence.topFindings.length > 0) {
1156
+ for (const finding of consequence.topFindings.slice(0, 8)) {
1157
+ const consumers = finding.consumerCount > 0
1158
+ ? ` · ${finding.nonTestConsumerCount} non-test consumer(s), ${finding.testConsumerCount} test`
1159
+ : '';
1160
+ const reasons = finding.reasonCodes.slice(0, 3).join(', ');
1161
+ console.log(chalk.dim(` ${finding.rank}. ${finding.summary}${consumers} · ${reasons}`));
1162
+ }
1163
+ }
1164
+ else {
1165
+ for (const effect of consequence.effectDeltas.slice(0, 5)) {
1166
+ console.log(chalk.dim(` ${effect.file}#${effect.symbol} ${effect.direction} ${effect.effectCategory}` +
1167
+ ` (${effect.calleeName}${effect.line ? ` @ line ${effect.line}` : ''})`));
1168
+ }
1169
+ }
1170
+ }
1171
+ if (artifact.digest.topReferences.length > 0 || artifact.digest.topSymbols.length > 0) {
1172
+ console.log('');
1173
+ console.log(chalk.bold('Structural digest'));
1174
+ for (const symbol of artifact.digest.topSymbols.slice(0, 3)) {
1175
+ console.log(chalk.dim(` ${symbol.file}#${symbol.name} · ${symbol.referenceCount} refs` +
1176
+ ` · ${symbol.crossPackageReferenceCount} cross-package` +
1177
+ ` · ${symbol.testReferenceCount} tests`));
1178
+ }
1179
+ if (artifact.digest.topConsequences.length > 0) {
1180
+ console.log(chalk.dim(' Top consequences:'));
1181
+ for (const item of artifact.digest.topConsequences.slice(0, 5)) {
1182
+ const reasons = item.reasonCodes.slice(0, 3).join(', ');
1183
+ const lines = item.representativeLines.length ? ` lines ${item.representativeLines.join(',')}` : '';
1184
+ console.log(chalk.dim(` ${item.rank}. ${item.targetFile}#${item.targetSymbol} <- ${item.referencingFile}` +
1185
+ ` · ${item.referenceCount} refs (${item.nonTestReferenceCount} non-test, ${item.testReferenceCount} tests)${lines} · ${reasons}`));
1186
+ }
1187
+ }
1188
+ else if (artifact.digest.topReferences.length > 0) {
1189
+ console.log(chalk.dim(' Most relevant references:'));
1190
+ for (const ref of artifact.digest.topReferences.slice(0, 5)) {
1191
+ const reasons = ref.reasonCodes.slice(0, 3).join(', ');
1192
+ console.log(chalk.dim(` ${ref.rank}. ${referenceLabel(ref)} · ${reasons}`));
1193
+ }
1194
+ }
1195
+ console.log(chalk.dim(` Hidden: ${artifact.digest.hidden.references} refs, ` +
1196
+ `${artifact.digest.hidden.testReferences} test refs, ` +
1197
+ `${artifact.digest.hidden.lowSignalReferences} low-signal refs.`));
1198
+ }
1199
+ if (artifact.changedSymbols.length > 0) {
1200
+ console.log('');
1201
+ console.log(chalk.bold('Changed symbols'));
1202
+ for (const symbol of artifact.changedSymbols.slice(0, 10)) {
1203
+ const refs = artifact.references.filter((ref) => ref.targetFile === symbol.file &&
1204
+ ref.targetSymbol === symbol.name &&
1205
+ ref.targetKind === symbol.kind);
1206
+ const tests = refs.filter((ref) => ref.isTestFile);
1207
+ console.log(chalk.dim(` ${symbol.file}#${symbol.name} (${symbol.kind}) · ${refs.length} refs · ${tests.length} tests`));
1208
+ }
1209
+ if (artifact.changedSymbols.length > 10) {
1210
+ console.log(chalk.dim(` +${artifact.changedSymbols.length - 10} more`));
1211
+ }
1212
+ }
1213
+ if (artifact.references.length > 0) {
1214
+ console.log('');
1215
+ console.log(chalk.bold('Relational facts'));
1216
+ for (const ref of artifact.references.slice(0, 12)) {
1217
+ console.log(chalk.dim(` ${referenceLabel(ref)}`));
1218
+ }
1219
+ if (artifact.references.length > 12) {
1220
+ console.log(chalk.dim(` +${artifact.references.length - 12} more references in artifact`));
1221
+ }
1222
+ }
1223
+ if (artifact.planAlignment) {
1224
+ console.log('');
1225
+ console.log(chalk.bold('Plan vs actual'));
1226
+ console.log(chalk.dim(` planned touched: ${compactList(artifact.planAlignment.plannedFilesTouched, 6)}`));
1227
+ console.log(chalk.dim(` unplanned touched: ${compactList(artifact.planAlignment.unplannedFilesTouched, 6)}`));
1228
+ console.log(chalk.dim(` symbols named: ${compactList(artifact.planAlignment.changedSymbolsMentionedInPlan, 6)}`));
1229
+ }
1230
+ if (artifact.boundaryImpact.length > 0) {
1231
+ console.log('');
1232
+ console.log(chalk.bold('Boundary impact'));
1233
+ for (const item of artifact.boundaryImpact.slice(0, 8)) {
1234
+ const approval = item.approvalRequired ? chalk.yellow('approval-required') : 'owned';
1235
+ console.log(chalk.dim(` ${item.file} · ${approval} · ${compactList(item.owners, 4)}`));
1236
+ }
1237
+ }
1238
+ console.log('');
1239
+ console.log(`Artifact: ${chalk.dim(artifactPath.replace(`${repoRoot}/`, ''))}`);
1240
+ console.log(`Hash: ${chalk.dim(artifact.artifactHash)}`);
1241
+ console.log(chalk.dim('Privacy: facts-only; no source code, diff hunks, patch text, or model judgments.'));
1242
+ console.log('');
1243
+ }
1244
+ function structuralUnderstandingCommand(options = {}) {
1245
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
1246
+ const session = resolveAIChangeRecordSession(repoRoot, options);
1247
+ if (!session) {
1248
+ if (options.json) {
1249
+ console.log(JSON.stringify({
1250
+ ok: false,
1251
+ repoRoot,
1252
+ error: 'No local governance session found. Start a governed session before building structural understanding.',
1253
+ }, null, 2));
1254
+ }
1255
+ else {
1256
+ (0, messages_1.printError)('No Governed Session', 'Start a governed session first, then run `neurcode session understanding` while the agent change is in progress.');
1257
+ }
1258
+ process.exitCode = 1;
1259
+ return;
1260
+ }
1261
+ let diffText = '';
1262
+ try {
1263
+ diffText = resolveUnderstandingDiff(repoRoot, options);
1264
+ }
1265
+ catch (error) {
1266
+ const message = error instanceof Error ? error.message : String(error);
1267
+ if (options.json) {
1268
+ console.log(JSON.stringify({ ok: false, repoRoot, sessionId: session.sessionId, error: message }, null, 2));
1269
+ }
1270
+ else {
1271
+ (0, messages_1.printError)('Unable to Read Diff', message);
1272
+ }
1273
+ process.exitCode = 1;
1274
+ return;
1275
+ }
1276
+ const diffFiles = diffText.trim() ? (0, diff_parser_1.parseDiff)(diffText) : [];
1277
+ const profile = (0, v0_governance_1.getProfileStaleness)(repoRoot).currentProfile;
1278
+ const artifact = (0, structural_understanding_1.buildStructuralUnderstanding)(repoRoot, diffFiles, {
1279
+ session,
1280
+ profile,
1281
+ maxProgramFiles: options.maxProgramFiles,
1282
+ timeBudgetMs: options.timeBudgetMs,
1283
+ });
1284
+ const artifactPath = (0, structural_understanding_1.writeStructuralUnderstanding)(repoRoot, session.sessionId, artifact);
1285
+ const message = artifact.analysis.analyzed
1286
+ ? `Structural understanding: ${artifact.analysis.changedSymbolCount} changed symbols, ${artifact.analysis.referenceCount} references, ${artifact.analysis.testReferenceCount} test references.`
1287
+ : `Structural understanding not analyzed: ${artifact.analysis.reason ?? 'unknown reason'}.`;
1288
+ const eventSession = (0, governance_runtime_1.appendEvent)(repoRoot, session.sessionId, {
1289
+ type: 'structural_understanding',
1290
+ ts: artifact.generatedAt,
1291
+ message,
1292
+ detail: structuralEventDetail(artifact, artifactPath, repoRoot),
1293
+ });
1294
+ if (eventSession)
1295
+ (0, governance_runtime_1.writeAIChangeRecord)(repoRoot, eventSession);
1296
+ if (options.json) {
1297
+ console.log(JSON.stringify({
1298
+ ok: true,
1299
+ repoRoot,
1300
+ sessionId: session.sessionId,
1301
+ artifactPath,
1302
+ artifact,
1303
+ }, null, 2));
1304
+ return;
1305
+ }
1306
+ renderStructuralUnderstanding(artifact, artifactPath, repoRoot);
1307
+ }
92
1308
  /**
93
1309
  * List all sessions
94
1310
  */
95
1311
  async function listSessionsCommand(options) {
96
1312
  try {
1313
+ if (options.local) {
1314
+ listRuntimeSessionsCommand(options);
1315
+ return;
1316
+ }
97
1317
  const config = (0, config_1.loadConfig)();
98
1318
  if (!config.apiKey) {
99
1319
  config.apiKey = (0, config_1.requireApiKey)();
@@ -328,6 +1548,12 @@ async function endSessionCommand(options) {
328
1548
  */
329
1549
  async function sessionStatusCommand(options) {
330
1550
  try {
1551
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
1552
+ const localSession = loadLocalGovernanceSession(repoRoot, options.sessionId);
1553
+ if (localSession || options.local || (options.json && !options.projectId)) {
1554
+ localGovernanceStatusCommand(options);
1555
+ return;
1556
+ }
331
1557
  const config = (0, config_1.loadConfig)();
332
1558
  if (!config.apiKey) {
333
1559
  config.apiKey = (0, config_1.requireApiKey)();