godpowers 3.0.2 → 3.13.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.
@@ -149,6 +149,395 @@ function runExtensionScaffoldCommand(opts) {
149
149
  }
150
150
  }
151
151
 
152
+ function verifyFailure(opts, id, reason) {
153
+ return {
154
+ command: 'verify',
155
+ verdict: 'fail',
156
+ project: opts.project,
157
+ kind: null,
158
+ verified: false,
159
+ checks: [{ id, status: 'fail', artifact: '.godpowers/ledger/verifications.jsonl', reason }],
160
+ findings: [{ id, severity: 'error', artifact: '.godpowers/ledger/verifications.jsonl', reason }]
161
+ };
162
+ }
163
+
164
+ function relLedger(opts, absPath) {
165
+ const path = require('path');
166
+ return path.relative(opts.project, absPath);
167
+ }
168
+
169
+ function renderVerify(result) {
170
+ const lines = [];
171
+ lines.push('Godpowers Verify');
172
+ lines.push('');
173
+ if (result.verdict === 'fail' && result.findings && result.findings.length > 0 && !result.record) {
174
+ lines.push('Verdict: fail');
175
+ for (const finding of result.findings) {
176
+ lines.push(`Error: ${finding.reason}`);
177
+ }
178
+ return lines.join('\n');
179
+ }
180
+ const verdictLabel = result.kind === 'attested'
181
+ ? 'attested (self-reported, not machine-checked)'
182
+ : result.verdict;
183
+ lines.push(`Verdict: ${verdictLabel}`);
184
+ lines.push(`Command: ${result.commandText ? '`' + result.commandText + '`' : '(none)'}`);
185
+ if (result.substep) lines.push(`Substep: ${result.substep}`);
186
+ if (result.kind === 'executed') {
187
+ lines.push(`Exit: ${result.exitCode} (${result.durationMs} ms)`);
188
+ }
189
+ if (result.claim) lines.push(`Claim: ${result.claim}`);
190
+ lines.push(`Ledger: ${result.ledger}`);
191
+ if (result.rollup) {
192
+ lines.push(result.rollup.applied
193
+ ? `Rollup: applied ${result.rollup.substep} verification.commands (${result.rollup.status})`
194
+ : `Rollup: skipped (${result.rollup.reason})`);
195
+ }
196
+ if (result.event) {
197
+ lines.push(result.event.emitted
198
+ ? `Event: ${result.event.name} -> runs/${result.event.runId}/events.jsonl`
199
+ : `Event: not emitted (${result.event.error})`);
200
+ }
201
+ if (result.kind === 'executed' && !result.verified && result.diagnostics) {
202
+ lines.push('');
203
+ lines.push('Diagnostics:');
204
+ lines.push(result.diagnostics);
205
+ }
206
+ return lines.join('\n');
207
+ }
208
+
209
+ function runVerifyCommand(opts) {
210
+ const evidence = require('./evidence');
211
+
212
+ if (opts.attest) {
213
+ if (!opts.claim) {
214
+ const result = verifyFailure(opts, 'claim-required', 'verify --attest requires --claim="<claim>"');
215
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
216
+ else console.log(renderVerify(result));
217
+ process.exitCode = 1;
218
+ return;
219
+ }
220
+ const outcome = evidence.verifyClaim(opts.claim, opts.evidence, {
221
+ substep: opts.substep || undefined,
222
+ projectRoot: opts.project
223
+ });
224
+ const result = {
225
+ command: 'verify',
226
+ kind: 'attested',
227
+ verdict: 'attested',
228
+ verified: null,
229
+ project: opts.project,
230
+ commandText: null,
231
+ substep: outcome.record.substep,
232
+ claim: outcome.record.claim,
233
+ ledger: relLedger(opts, outcome.ledger),
234
+ rollup: null,
235
+ event: null,
236
+ record: outcome.record
237
+ };
238
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
239
+ else console.log(renderVerify(result));
240
+ return;
241
+ }
242
+
243
+ if (!opts.verifyCommand) {
244
+ const result = verifyFailure(opts, 'command-required', 'verify requires a command, for example: npx godpowers verify "npm test" --substep tier-2.build');
245
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
246
+ else console.log(renderVerify(result));
247
+ process.exitCode = 1;
248
+ return;
249
+ }
250
+ if (!opts.substep) {
251
+ const result = verifyFailure(opts, 'substep-required', 'verify requires --substep=<id>, for example --substep=tier-2.build');
252
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
253
+ else console.log(renderVerify(result));
254
+ process.exitCode = 1;
255
+ return;
256
+ }
257
+
258
+ const outcome = evidence.verify(opts.verifyCommand, {
259
+ substep: opts.substep,
260
+ claim: opts.claim || undefined,
261
+ timeout: opts.timeout || undefined,
262
+ projectRoot: opts.project
263
+ });
264
+ const result = {
265
+ command: 'verify',
266
+ kind: 'executed',
267
+ verdict: outcome.verified ? 'pass' : 'fail',
268
+ verified: outcome.verified,
269
+ project: opts.project,
270
+ commandText: outcome.record.command,
271
+ substep: outcome.record.substep,
272
+ claim: outcome.record.claim,
273
+ exitCode: outcome.record.exit_code,
274
+ durationMs: Math.max(0, Math.round(outcome.record.duration_seconds * 1000)),
275
+ diagnostics: outcome.rollup && outcome.rollup.applied
276
+ ? undefined
277
+ : (outcome.record.stderr_tail || outcome.record.stdout_tail || ''),
278
+ ledger: relLedger(opts, outcome.ledger),
279
+ rollup: outcome.rollup,
280
+ event: outcome.event,
281
+ record: outcome.record
282
+ };
283
+ // Surface diagnostics on failure regardless of rollup state.
284
+ if (!outcome.verified) {
285
+ result.diagnostics = outcome.record.stderr_tail || outcome.record.stdout_tail || `exit ${outcome.record.exit_code}`;
286
+ }
287
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
288
+ else console.log(renderVerify(result));
289
+ if (!outcome.verified) process.exitCode = 1;
290
+ }
291
+
292
+ function renderCanClose(result) {
293
+ const lines = [];
294
+ lines.push('Godpowers Can-Close');
295
+ lines.push('');
296
+ lines.push(`Substep: ${result.substep || '(none)'}`);
297
+ lines.push(`Can close: ${result.canClose ? 'yes' : 'no'}`);
298
+ lines.push(`Reason: ${result.reason}`);
299
+ if (result.strategy) lines.push(`Strategy: ${result.strategy}`);
300
+ if (result.wentInFlightAt) lines.push(`In-flight since: ${result.wentInFlightAt}`);
301
+ return lines.join('\n');
302
+ }
303
+
304
+ function runCanCloseCommand(opts) {
305
+ const evidence = require('./evidence');
306
+ if (!opts.substep) {
307
+ const result = {
308
+ command: 'can-close',
309
+ project: opts.project,
310
+ canClose: false,
311
+ reason: 'substep-required',
312
+ strategy: null,
313
+ substep: null
314
+ };
315
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
316
+ else console.log(`${renderCanClose(result)}\n\nError: can-close requires --substep=<id>, for example --substep=tier-2.build`);
317
+ process.exitCode = 1;
318
+ return;
319
+ }
320
+ const verdict = evidence.canClose(opts.substep, { projectRoot: opts.project });
321
+ const result = { command: 'can-close', project: opts.project, ...verdict };
322
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
323
+ else console.log(renderCanClose(result));
324
+ if (!verdict.canClose) process.exitCode = 1;
325
+ }
326
+
327
+ function renderRoute(result) {
328
+ const lines = [];
329
+ lines.push('Godpowers Route');
330
+ lines.push('');
331
+ lines.push(`Route: ${result.route}`);
332
+ lines.push(`Reason: ${result.reason}`);
333
+ lines.push(`Next command: ${result.nextCommand || '(answer inline)'}`);
334
+ lines.push(`Ceremony: ${result.ceremony}`);
335
+ lines.push(`Verification: ${result.verificationStrategy}`);
336
+ lines.push(`Chat policy: ${result.chatPolicy}`);
337
+ const ev = result.evidence || {};
338
+ lines.push(`Evidence: classification=${ev.classification}, latest verdict=${ev.latestVerdict}, open findings=${ev.openFindings}`);
339
+ return lines.join('\n');
340
+ }
341
+
342
+ function runRouteCommand(opts) {
343
+ const quarterback = require('./quarterback');
344
+ const result = quarterback.route(opts.routePrompt || '', { projectRoot: opts.project });
345
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
346
+ else console.log(renderRoute(result));
347
+ }
348
+
349
+ function runReportCommand(opts) {
350
+ const workReport = require('./work-report');
351
+ const result = workReport.report({
352
+ since: opts.since || 'last',
353
+ peek: opts.peek,
354
+ projectRoot: opts.project
355
+ });
356
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
357
+ else console.log(workReport.render(result));
358
+ }
359
+
360
+ function runReflectCommand(opts) {
361
+ const evidence = require('./evidence');
362
+ if (!opts.reflectAction) {
363
+ const result = {
364
+ command: 'reflect',
365
+ verdict: 'fail',
366
+ project: opts.project,
367
+ findings: [{ id: 'action-required', severity: 'error', artifact: '.godpowers/ledger/reflections.jsonl', reason: 'reflect requires --action="<what was attempted>"' }]
368
+ };
369
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
370
+ else console.log('Godpowers Reflect\n\nError: reflect requires --action="<what was attempted>"');
371
+ process.exitCode = 1;
372
+ return;
373
+ }
374
+ const outcome = evidence.reflect({
375
+ action: opts.reflectAction,
376
+ outcome: opts.outcome || undefined,
377
+ observation: opts.observation || undefined,
378
+ rootCause: opts.rootCause || undefined,
379
+ next: opts.nextAction || undefined,
380
+ lesson: opts.lesson || undefined
381
+ }, { substep: opts.substep || undefined, projectRoot: opts.project });
382
+ if (opts.json) {
383
+ console.log(JSON.stringify(outcome, null, 2));
384
+ } else {
385
+ const r = outcome.record;
386
+ const lines = ['Godpowers Reflect', '', `Outcome: ${r.outcome}`, `Action: ${r.action}`];
387
+ if (r.substep) lines.push(`Substep: ${r.substep}`);
388
+ if (r.next) lines.push(`Next: ${r.next}`);
389
+ if (r.lesson) lines.push(`Lesson: ${r.lesson}`);
390
+ lines.push('Recorded to .godpowers/ledger/reflections.jsonl');
391
+ console.log(lines.join('\n'));
392
+ }
393
+ }
394
+
395
+ function memoryError(opts, reason) {
396
+ if (opts.json) {
397
+ console.log(JSON.stringify({ command: 'memory', verdict: 'fail', project: opts.project, reason }, null, 2));
398
+ } else {
399
+ console.log(`Godpowers Memory\n\nError: ${reason}`);
400
+ }
401
+ process.exitCode = 1;
402
+ }
403
+
404
+ function runMemoryCommand(opts) {
405
+ const evidence = require('./evidence');
406
+ const action = opts.memoryAction;
407
+ if (!action || !['set', 'get', 'list', 'clear'].includes(action)) {
408
+ return memoryError(opts, 'memory requires an action: set, get, list, or clear');
409
+ }
410
+ const projectRoot = opts.project;
411
+ let result;
412
+ if (action === 'set') {
413
+ if (!opts.memoryKey || opts.memoryValue === null) {
414
+ return memoryError(opts, 'memory set requires a key and value, for example: memory set decision "use postgres"');
415
+ }
416
+ result = evidence.memory.set(opts.memoryKey, opts.memoryValue, { category: opts.category || undefined, projectRoot });
417
+ } else if (action === 'get') {
418
+ if (!opts.memoryKey) return memoryError(opts, 'memory get requires a key');
419
+ result = evidence.memory.get(opts.memoryKey, { projectRoot });
420
+ } else if (action === 'list') {
421
+ result = evidence.memory.list({ category: opts.category || undefined, projectRoot });
422
+ } else {
423
+ result = evidence.memory.clear(opts.memoryKey || undefined, { projectRoot });
424
+ }
425
+ if (opts.json) {
426
+ console.log(JSON.stringify({ command: 'memory', action, result }, null, 2));
427
+ } else {
428
+ const lines = ['Godpowers Memory', '', `Action: ${action}`];
429
+ if (action === 'list') {
430
+ const entries = result || [];
431
+ lines.push(`Entries: ${entries.length}`);
432
+ for (const e of entries) lines.push(` [${e.category}] ${e.key} = ${e.value}`);
433
+ } else if (action === 'get') {
434
+ lines.push(result ? `[${result.category}] ${result.key} = ${result.value}` : '(not found)');
435
+ } else if (action === 'set') {
436
+ lines.push(`Set [${result.category}] ${result.key} = ${result.value}`);
437
+ } else {
438
+ lines.push(`Removed ${result.removed} entr${result.removed === 1 ? 'y' : 'ies'}`);
439
+ }
440
+ console.log(lines.join('\n'));
441
+ }
442
+ if (action === 'get' && !result) process.exitCode = 1;
443
+ }
444
+
445
+ function runLessonCommand(opts) {
446
+ const evidence = require('./evidence');
447
+ const action = opts.lessonAction;
448
+ if (!action || !['add', 'list'].includes(action)) {
449
+ if (opts.json) console.log(JSON.stringify({ command: 'lesson', verdict: 'fail', reason: 'lesson requires an action: add or list' }, null, 2));
450
+ else console.log('Godpowers Lesson\n\nError: lesson requires an action: add or list');
451
+ process.exitCode = 1;
452
+ return;
453
+ }
454
+ const projectRoot = opts.project;
455
+ if (action === 'add') {
456
+ if (!opts.lessonText) {
457
+ if (opts.json) console.log(JSON.stringify({ command: 'lesson', verdict: 'fail', reason: 'lesson add requires the lesson text' }, null, 2));
458
+ else console.log('Godpowers Lesson\n\nError: lesson add requires the lesson text, for example: lesson add "guard inputs before parsing"');
459
+ process.exitCode = 1;
460
+ return;
461
+ }
462
+ const tags = opts.tags ? String(opts.tags).split(',').map((t) => t.trim()).filter(Boolean) : [];
463
+ const record = evidence.lesson.add(opts.lessonText, { tags, scope: opts.scope || undefined, projectRoot });
464
+ if (opts.json) console.log(JSON.stringify({ command: 'lesson', action, result: record }, null, 2));
465
+ else console.log(`Godpowers Lesson\n\nAdded [${record.scope}]${record.tags.length ? ` (${record.tags.join(',')})` : ''}: ${record.lesson}`);
466
+ return;
467
+ }
468
+ const records = evidence.lesson.list({ scope: opts.scope || undefined, projectRoot });
469
+ if (opts.json) {
470
+ console.log(JSON.stringify({ command: 'lesson', action, result: records }, null, 2));
471
+ } else {
472
+ const lines = ['Godpowers Lesson', '', `Lessons: ${records.length}`];
473
+ for (const r of records) lines.push(` [${r.scope}]${r.tags && r.tags.length ? ` (${r.tags.join(',')})` : ''} ${r.lesson}`);
474
+ console.log(lines.join('\n'));
475
+ }
476
+ }
477
+
478
+ function outcomeError(opts, reason) {
479
+ if (opts.json) console.log(JSON.stringify({ command: 'outcome', verdict: 'fail', reason }, null, 2));
480
+ else console.log(`Godpowers Outcome\n\nError: ${reason}`);
481
+ process.exitCode = 1;
482
+ }
483
+
484
+ function runOutcomeCommand(opts) {
485
+ const evidence = require('./evidence');
486
+ const action = opts.outcomeAction;
487
+ if (!action || !['start', 'check', 'stop', 'status'].includes(action)) {
488
+ return outcomeError(opts, 'outcome requires an action: start, check, stop, or status');
489
+ }
490
+ if (!opts.outcomeSlug) {
491
+ return outcomeError(opts, `outcome ${action} requires a name, for example: outcome ${action} green-build`);
492
+ }
493
+ const projectRoot = opts.project;
494
+ let payload;
495
+ if (action === 'start') {
496
+ payload = evidence.outcome.start(opts.outcomeSlug, {
497
+ goal: opts.outcomeGoal || undefined,
498
+ verifier: opts.outcomeVerify || undefined,
499
+ budget: opts.budget || undefined,
500
+ substep: opts.substep || undefined,
501
+ projectRoot
502
+ });
503
+ } else if (action === 'check') {
504
+ payload = evidence.outcome.check(opts.outcomeSlug, { projectRoot });
505
+ } else if (action === 'stop') {
506
+ payload = evidence.outcome.stop(opts.outcomeSlug, opts.reason || undefined, { projectRoot });
507
+ } else {
508
+ payload = evidence.outcome.status(opts.outcomeSlug, { projectRoot });
509
+ }
510
+ if (opts.json) {
511
+ console.log(JSON.stringify({ command: 'outcome', action, result: payload }, null, 2));
512
+ } else {
513
+ const lines = ['Godpowers Outcome', '', `Action: ${action}`];
514
+ const goal = payload && (payload.goal || (payload.slug ? payload : null));
515
+ if (goal && goal.slug) {
516
+ lines.push(`Outcome: ${goal.slug}`);
517
+ lines.push(`Status: ${goal.status}`);
518
+ lines.push(`Iterations: ${goal.iterations}/${goal.budget}`);
519
+ }
520
+ if (payload && payload.verified !== undefined) lines.push(`This check: ${payload.verified ? 'verified' : 'unverified'}`);
521
+ if (payload && payload.reason) lines.push(`Note: ${payload.reason}`);
522
+ if (!payload) lines.push('(outcome not found)');
523
+ console.log(lines.join('\n'));
524
+ }
525
+ // A check that did not verify, or a missing outcome, exits non-zero.
526
+ if (action === 'check' && payload && payload.verified === false) process.exitCode = 1;
527
+ if ((action === 'status') && !payload) process.exitCode = 1;
528
+ }
529
+
530
+ function runImportLedgerCommand(opts) {
531
+ const importer = require('./evidence-import');
532
+ const result = importer.importMythify({
533
+ source: opts.importFrom || undefined,
534
+ projectRoot: opts.project
535
+ });
536
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
537
+ else console.log(importer.render(result));
538
+ if (!result.found) process.exitCode = 1;
539
+ }
540
+
152
541
  function runGateCommand(opts) {
153
542
  if (!opts.tier) {
154
543
  const result = {
@@ -223,7 +612,16 @@ const COMMAND_RUNNERS = {
223
612
  demo: runDemoCommand,
224
613
  surface: runSurfaceCommand,
225
614
  'extension-scaffold': runExtensionScaffoldCommand,
226
- gate: runGateCommand
615
+ gate: runGateCommand,
616
+ verify: runVerifyCommand,
617
+ 'can-close': runCanCloseCommand,
618
+ route: runRouteCommand,
619
+ report: runReportCommand,
620
+ reflect: runReflectCommand,
621
+ memory: runMemoryCommand,
622
+ lesson: runLessonCommand,
623
+ outcome: runOutcomeCommand,
624
+ 'import-ledger': runImportLedgerCommand
227
625
  };
228
626
 
229
627
  function runCommand(opts) {
@@ -247,5 +645,14 @@ module.exports = {
247
645
  runMcpInfoCommand,
248
646
  runExtensionScaffoldCommand,
249
647
  runGateCommand,
250
- runStateCommand
648
+ runStateCommand,
649
+ runVerifyCommand,
650
+ runCanCloseCommand,
651
+ runRouteCommand,
652
+ runReportCommand,
653
+ runReflectCommand,
654
+ runMemoryCommand,
655
+ runLessonCommand,
656
+ runOutcomeCommand,
657
+ runImportLedgerCommand
251
658
  };
@@ -0,0 +1,45 @@
1
+ {
2
+ "source": "mythify-mcp",
3
+ "upstreamRepo": "mythify",
4
+ "upstreamPath": "mcp-server/src/index.js",
5
+ "engine": "verify_run + verify_claim (verification tools)",
6
+ "version": "3.6.3",
7
+ "commit": "7cbd601f9c4d5328a54921a051d450703e44c514",
8
+ "syncedAt": "2026-06-15",
9
+ "adaptations": [
10
+ "verificationStepContext (plan/step_id/step_title/step_status) rebound to substepContext (arc/substep/substep_status)",
11
+ "state dir .mythify/ rebound to .godpowers/ledger/",
12
+ "appendJsonl (fs.appendFileSync) rebound to an atomic read-modify-write append via lib/atomic-write.js",
13
+ "executed verdict additionally rolled up into state.json verification.commands[] via lib/state.js in the Godpowers gate shape",
14
+ "executed verdict additionally emitted as gate.pass / gate.fail to .godpowers/runs/<id>/events.jsonl via lib/events.js",
15
+ "verify_claim exposed as evidence.verifyClaim: attested, verified:null, never rolled up, never emitted"
16
+ ],
17
+ "upstreamRecordShape": {
18
+ "executed": [
19
+ "kind",
20
+ "claim",
21
+ "command",
22
+ "exit_code",
23
+ "duration_seconds",
24
+ "stdout_tail",
25
+ "stderr_tail",
26
+ "verified",
27
+ "timestamp",
28
+ "plan",
29
+ "step_id",
30
+ "step_title",
31
+ "step_status"
32
+ ],
33
+ "attested": [
34
+ "kind",
35
+ "claim",
36
+ "evidence",
37
+ "verified",
38
+ "timestamp",
39
+ "plan",
40
+ "step_id",
41
+ "step_title",
42
+ "step_status"
43
+ ]
44
+ }
45
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * One-time importer for existing Mythify .mythify/ ledgers.
3
+ *
4
+ * Optional, best-effort, additive: it copies an existing Mythify state dir into
5
+ * the Godpowers ledger, rebinding the Mythify plan/step context to Godpowers'
6
+ * arc/substep on each record. It appends to the ledger and does NOT roll up into
7
+ * state.json or emit gate events; this is a historical import, not a re-run.
8
+ *
9
+ * See docs/FUSION-ARCHITECTURE.md (Phase 3, optional importer).
10
+ */
11
+
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+
15
+ const atomic = require('./atomic-write');
16
+ const evidence = require('./evidence');
17
+
18
+ function readJson(file) {
19
+ try {
20
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
21
+ } catch (_) {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Rebind a Mythify record's plan/step context to Godpowers' arc/substep.
28
+ * Records that already carry arc/substep pass through unchanged.
29
+ */
30
+ function rebindRecord(record) {
31
+ if (!record || typeof record !== 'object') return record;
32
+ const hasMythifyContext = 'plan' in record || 'step_id' in record
33
+ || 'step_title' in record || 'step_status' in record;
34
+ if (!hasMythifyContext) return { ...record };
35
+ const out = { ...record };
36
+ out.arc = record.arc !== undefined ? record.arc : (record.plan !== undefined ? record.plan : null);
37
+ out.substep = record.substep !== undefined ? record.substep : (record.step_id !== undefined ? record.step_id : null);
38
+ out.substep_status = record.substep_status !== undefined
39
+ ? record.substep_status
40
+ : (record.step_status !== undefined ? record.step_status : null);
41
+ delete out.plan;
42
+ delete out.step_id;
43
+ delete out.step_title;
44
+ delete out.step_status;
45
+ return out;
46
+ }
47
+
48
+ /**
49
+ * Import an existing .mythify/ ledger into the Godpowers ledger.
50
+ *
51
+ * @param {{ source?: string, projectRoot?: string }} [opts]
52
+ * @returns {{ source: string, projectRoot: string, found: boolean,
53
+ * imported: { verifications: number, reflections: number, memory: number, lessons: number, outcomes: number },
54
+ * error?: string }}
55
+ */
56
+ function importMythify(opts = {}) {
57
+ const projectRoot = path.resolve(opts.projectRoot || process.cwd());
58
+ const source = path.resolve(opts.source || path.join(projectRoot, '.mythify'));
59
+ const result = {
60
+ source,
61
+ projectRoot,
62
+ found: false,
63
+ imported: { verifications: 0, reflections: 0, memory: 0, lessons: 0, outcomes: 0 }
64
+ };
65
+ if (!fs.existsSync(source)) {
66
+ result.error = 'source-not-found';
67
+ return result;
68
+ }
69
+ result.found = true;
70
+
71
+ // Verifications (rebind plan/step -> arc/substep).
72
+ for (const record of evidence.readJsonl(path.join(source, 'verifications.jsonl'))) {
73
+ evidence.appendJsonlAtomic(evidence.verificationsPath(projectRoot), rebindRecord(record));
74
+ result.imported.verifications += 1;
75
+ }
76
+
77
+ // Reflections (rebind for any bound context; mostly pass-through).
78
+ for (const record of evidence.readJsonl(path.join(source, 'reflections.jsonl'))) {
79
+ evidence.appendJsonlAtomic(evidence.reflectionsPath(projectRoot), rebindRecord(record));
80
+ result.imported.reflections += 1;
81
+ }
82
+
83
+ // Memory (merge entries by key).
84
+ const sourceMemory = readJson(path.join(source, 'memory.json'));
85
+ if (sourceMemory && Array.isArray(sourceMemory.entries) && sourceMemory.entries.length > 0) {
86
+ const dest = readJson(evidence.memoryPath(projectRoot)) || { entries: [], metadata: {} };
87
+ if (!Array.isArray(dest.entries)) dest.entries = [];
88
+ for (const entry of sourceMemory.entries) {
89
+ if (!entry || typeof entry.key !== 'string') continue;
90
+ const idx = dest.entries.findIndex((existing) => existing && existing.key === entry.key);
91
+ if (idx >= 0) dest.entries[idx] = entry;
92
+ else dest.entries.push(entry);
93
+ result.imported.memory += 1;
94
+ }
95
+ dest.metadata = dest.metadata || {};
96
+ dest.metadata.total_entries = dest.entries.length;
97
+ fs.mkdirSync(evidence.ledgerDir(projectRoot), { recursive: true });
98
+ atomic.writeJsonAtomic(evidence.memoryPath(projectRoot), dest);
99
+ }
100
+
101
+ // Lessons (Mythify lessons/*.json -> our lessons.jsonl).
102
+ const lessonsDir = path.join(source, 'lessons');
103
+ if (fs.existsSync(lessonsDir)) {
104
+ for (const name of fs.readdirSync(lessonsDir).filter((n) => n.endsWith('.json'))) {
105
+ const lesson = readJson(path.join(lessonsDir, name));
106
+ if (lesson) {
107
+ evidence.appendJsonlAtomic(evidence.lessonsPath(projectRoot, 'project'), lesson);
108
+ result.imported.lessons += 1;
109
+ }
110
+ }
111
+ }
112
+
113
+ // Outcomes (copy goal.json + iterations.jsonl per slug).
114
+ const outcomesDir = path.join(source, 'outcomes');
115
+ if (fs.existsSync(outcomesDir)) {
116
+ for (const slug of fs.readdirSync(outcomesDir)) {
117
+ const srcGoal = path.join(outcomesDir, slug, 'goal.json');
118
+ if (!fs.existsSync(srcGoal)) continue;
119
+ const destDir = path.join(evidence.ledgerDir(projectRoot), 'outcomes', slug);
120
+ fs.mkdirSync(destDir, { recursive: true });
121
+ atomic.writeFileAtomic(path.join(destDir, 'goal.json'), fs.readFileSync(srcGoal, 'utf8'));
122
+ const srcIters = path.join(outcomesDir, slug, 'iterations.jsonl');
123
+ if (fs.existsSync(srcIters)) {
124
+ atomic.writeFileAtomic(path.join(destDir, 'iterations.jsonl'), fs.readFileSync(srcIters, 'utf8'));
125
+ }
126
+ result.imported.outcomes += 1;
127
+ }
128
+ }
129
+
130
+ return result;
131
+ }
132
+
133
+ function render(result) {
134
+ const lines = ['Godpowers Ledger Import', ''];
135
+ lines.push(`Source: ${result.source}`);
136
+ if (!result.found) {
137
+ lines.push('');
138
+ lines.push('No .mythify/ ledger found at the source. Pass --from <path>.');
139
+ return lines.join('\n');
140
+ }
141
+ const i = result.imported;
142
+ lines.push(`Imported: ${i.verifications} verification(s), ${i.reflections} reflection(s), ${i.memory} memory entr(ies), ${i.lessons} lesson(s), ${i.outcomes} outcome(s)`);
143
+ lines.push('Records were appended to the Godpowers ledger (no state rollup, no gate events).');
144
+ return lines.join('\n');
145
+ }
146
+
147
+ module.exports = { importMythify, rebindRecord, render };