@poa-box/agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.env.agent.template +20 -0
  2. package/README.md +46 -0
  3. package/brain/Config/agent-config.json +14 -0
  4. package/brain/Config/brain-allowlist.json +20 -0
  5. package/brain/Identity/goals.template.md +23 -0
  6. package/brain/Identity/how-i-think.md +406 -0
  7. package/brain/Identity/who-i-am.template.md +34 -0
  8. package/brain/Knowledge/BOOTSTRAP.md +66 -0
  9. package/brain/Knowledge/audit-corpus-index.json +406 -0
  10. package/brain/Knowledge/discussions.json +245 -0
  11. package/brain/Knowledge/pop.brain.brainstorms.generated.md +48 -0
  12. package/brain/Knowledge/pop.brain.brainstorms.genesis.bin +0 -0
  13. package/brain/Knowledge/pop.brain.heuristics.snapshot.bin +0 -0
  14. package/brain/Knowledge/pop.brain.projects.generated.md +16 -0
  15. package/brain/Knowledge/pop.brain.projects.genesis.bin +0 -0
  16. package/brain/Knowledge/pop.brain.retros.generated.md +91 -0
  17. package/brain/Knowledge/pop.brain.retros.genesis.bin +0 -0
  18. package/brain/Knowledge/pop.brain.shared.generated.md +3811 -0
  19. package/brain/Knowledge/pop.brain.shared.genesis.bin +0 -0
  20. package/brain/Knowledge/projects.md +181 -0
  21. package/brain/Knowledge/risk-framework.md +90 -0
  22. package/brain/Knowledge/shared.md +416 -0
  23. package/brain/Knowledge/sprint-priorities.md +439 -0
  24. package/brain/Memory/.gitkeep +0 -0
  25. package/dist/commands/agent/daily-digest.d.ts +24 -0
  26. package/dist/commands/agent/daily-digest.js +336 -0
  27. package/dist/commands/agent/delegate.d.ts +12 -0
  28. package/dist/commands/agent/delegate.js +91 -0
  29. package/dist/commands/agent/deploy-to-org.d.ts +20 -0
  30. package/dist/commands/agent/deploy-to-org.js +154 -0
  31. package/dist/commands/agent/index.d.ts +2 -0
  32. package/dist/commands/agent/index.js +27 -0
  33. package/dist/commands/agent/init.d.ts +19 -0
  34. package/dist/commands/agent/init.js +303 -0
  35. package/dist/commands/agent/onboard.d.ts +22 -0
  36. package/dist/commands/agent/onboard.js +192 -0
  37. package/dist/commands/agent/paymaster-status.d.ts +14 -0
  38. package/dist/commands/agent/paymaster-status.js +130 -0
  39. package/dist/commands/agent/register.d.ts +21 -0
  40. package/dist/commands/agent/register.js +116 -0
  41. package/dist/commands/agent/setup-sponsorship.d.ts +22 -0
  42. package/dist/commands/agent/setup-sponsorship.js +154 -0
  43. package/dist/commands/agent/status.d.ts +12 -0
  44. package/dist/commands/agent/status.js +171 -0
  45. package/dist/commands/agent/triage.d.ts +12 -0
  46. package/dist/commands/agent/triage.js +503 -0
  47. package/dist/commands/brain/advance-stage.d.ts +42 -0
  48. package/dist/commands/brain/advance-stage.js +206 -0
  49. package/dist/commands/brain/allowlist.d.ts +30 -0
  50. package/dist/commands/brain/allowlist.js +274 -0
  51. package/dist/commands/brain/append-lesson.d.ts +55 -0
  52. package/dist/commands/brain/append-lesson.js +245 -0
  53. package/dist/commands/brain/brainstorm.d.ts +154 -0
  54. package/dist/commands/brain/brainstorm.js +573 -0
  55. package/dist/commands/brain/daemon.d.ts +31 -0
  56. package/dist/commands/brain/daemon.js +348 -0
  57. package/dist/commands/brain/doctor.d.ts +27 -0
  58. package/dist/commands/brain/doctor.js +497 -0
  59. package/dist/commands/brain/edit-lesson.d.ts +51 -0
  60. package/dist/commands/brain/edit-lesson.js +248 -0
  61. package/dist/commands/brain/import-snapshot.d.ts +68 -0
  62. package/dist/commands/brain/import-snapshot.js +177 -0
  63. package/dist/commands/brain/index.d.ts +2 -0
  64. package/dist/commands/brain/index.js +67 -0
  65. package/dist/commands/brain/list.d.ts +21 -0
  66. package/dist/commands/brain/list.js +83 -0
  67. package/dist/commands/brain/migrate-projects.d.ts +44 -0
  68. package/dist/commands/brain/migrate-projects.js +209 -0
  69. package/dist/commands/brain/migrate.d.ts +74 -0
  70. package/dist/commands/brain/migrate.js +306 -0
  71. package/dist/commands/brain/new-project.d.ts +53 -0
  72. package/dist/commands/brain/new-project.js +226 -0
  73. package/dist/commands/brain/read.d.ts +24 -0
  74. package/dist/commands/brain/read.js +81 -0
  75. package/dist/commands/brain/remove-lesson.d.ts +47 -0
  76. package/dist/commands/brain/remove-lesson.js +206 -0
  77. package/dist/commands/brain/remove-project.d.ts +36 -0
  78. package/dist/commands/brain/remove-project.js +177 -0
  79. package/dist/commands/brain/retro-file-tasks.d.ts +84 -0
  80. package/dist/commands/brain/retro-file-tasks.js +372 -0
  81. package/dist/commands/brain/retro-list.d.ts +28 -0
  82. package/dist/commands/brain/retro-list.js +125 -0
  83. package/dist/commands/brain/retro-mark-change.d.ts +58 -0
  84. package/dist/commands/brain/retro-mark-change.js +176 -0
  85. package/dist/commands/brain/retro-remove.d.ts +36 -0
  86. package/dist/commands/brain/retro-remove.js +142 -0
  87. package/dist/commands/brain/retro-respond.d.ts +56 -0
  88. package/dist/commands/brain/retro-respond.js +250 -0
  89. package/dist/commands/brain/retro-show.d.ts +23 -0
  90. package/dist/commands/brain/retro-show.js +100 -0
  91. package/dist/commands/brain/retro-start.d.ts +55 -0
  92. package/dist/commands/brain/retro-start.js +311 -0
  93. package/dist/commands/brain/search.d.ts +48 -0
  94. package/dist/commands/brain/search.js +190 -0
  95. package/dist/commands/brain/snapshot.d.ts +32 -0
  96. package/dist/commands/brain/snapshot.js +243 -0
  97. package/dist/commands/brain/status.d.ts +15 -0
  98. package/dist/commands/brain/status.js +166 -0
  99. package/dist/commands/brain/subscribe.d.ts +28 -0
  100. package/dist/commands/brain/subscribe.js +90 -0
  101. package/dist/commands/brain/tag.d.ts +46 -0
  102. package/dist/commands/brain/tag.js +192 -0
  103. package/dist/index.d.ts +17 -0
  104. package/dist/index.js +22 -0
  105. package/dist/lib/brain-daemon.d.ts +126 -0
  106. package/dist/lib/brain-daemon.js +811 -0
  107. package/dist/lib/brain-membership.d.ts +58 -0
  108. package/dist/lib/brain-membership.js +115 -0
  109. package/dist/lib/brain-migrate-projects.d.ts +43 -0
  110. package/dist/lib/brain-migrate-projects.js +247 -0
  111. package/dist/lib/brain-migrate.d.ts +77 -0
  112. package/dist/lib/brain-migrate.js +328 -0
  113. package/dist/lib/brain-ops.d.ts +271 -0
  114. package/dist/lib/brain-ops.js +571 -0
  115. package/dist/lib/brain-paths.d.ts +15 -0
  116. package/dist/lib/brain-paths.js +33 -0
  117. package/dist/lib/brain-projections.d.ts +216 -0
  118. package/dist/lib/brain-projections.js +829 -0
  119. package/dist/lib/brain-schemas.d.ts +36 -0
  120. package/dist/lib/brain-schemas.js +316 -0
  121. package/dist/lib/brain-signing.d.ts +103 -0
  122. package/dist/lib/brain-signing.js +256 -0
  123. package/dist/lib/brain.d.ts +198 -0
  124. package/dist/lib/brain.js +1057 -0
  125. package/dist/pop-agent.d.ts +1 -0
  126. package/dist/pop-agent.js +18 -0
  127. package/docs/agent.md +126 -0
  128. package/docs/agents/brain-anti-entropy.md +127 -0
  129. package/docs/agents/brain-cross-device-onboarding.md +210 -0
  130. package/docs/agents/brain-cross-machine-smoke.md +241 -0
  131. package/docs/agents/brain-layer-setup.md +725 -0
  132. package/docs/agents/offboarding-protocol.md +188 -0
  133. package/docs/agents/onboarding-protocol.md +243 -0
  134. package/docs/agents/running-an-agent.md +200 -0
  135. package/docs/brain.md +560 -0
  136. package/package.json +61 -0
  137. package/scripts/apply.sh +140 -0
  138. package/scripts/onboard.sh +205 -0
  139. package/scripts/setup-agent.ts +272 -0
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ /**
3
+ * pop brain edit-lesson — update fields on an existing brain lesson.
4
+ *
5
+ * Finds a lesson by its id inside a brain doc, mutates the provided
6
+ * fields in-place, and persists the result via applyBrainChange so
7
+ * the edit goes through the full sign → persist → gossipsub publish
8
+ * pipeline. Edits are idempotent from the user's perspective: running
9
+ * the same edit twice produces the same final state (the second run
10
+ * is a no-op if no field actually changes).
11
+ *
12
+ * Scope: edit in place only. No remove, no re-id, no ordering mutation.
13
+ * Remove requires CRDT tombstone semantics which are a separate ship.
14
+ */
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.editLessonHandler = void 0;
50
+ const fs_1 = require("fs");
51
+ const path_1 = require("path");
52
+ const brain_1 = require("../../lib/brain");
53
+ const brain_ops_1 = require("../../lib/brain-ops");
54
+ const ethers_1 = require("ethers");
55
+ const idempotency_1 = require("@poa-box/cli/lib/idempotency");
56
+ const output = __importStar(require("@poa-box/cli/lib/output"));
57
+ exports.editLessonHandler = {
58
+ builder: (yargs) => yargs
59
+ .option('doc', {
60
+ describe: 'Brain document ID (e.g. pop.brain.shared)',
61
+ type: 'string',
62
+ demandOption: true,
63
+ })
64
+ .option('lesson-id', {
65
+ describe: 'The lesson.id of the entry to update',
66
+ type: 'string',
67
+ demandOption: true,
68
+ })
69
+ .option('title', { describe: 'New title', type: 'string' })
70
+ .option('body', { describe: 'New body (inline)', type: 'string' })
71
+ .option('body-file', { describe: 'Path to a file whose contents replace the body', type: 'string' })
72
+ .option('author', { describe: 'Override the author label', type: 'string' })
73
+ .option('touch', {
74
+ describe: 'Bump the lesson.timestamp to now on edit (default: preserve original timestamp)',
75
+ type: 'boolean',
76
+ default: false,
77
+ })
78
+ .option('allow-invalid-shape', {
79
+ describe: 'Bypass write-time schema validation (Task #346).',
80
+ type: 'boolean',
81
+ default: false,
82
+ })
83
+ .option('idempotency-key', {
84
+ type: 'string',
85
+ describe: 'Task #374 (HB#215): explicit idempotency key. Agent-scoped.',
86
+ })
87
+ .option('no-idempotency', {
88
+ type: 'boolean',
89
+ default: false,
90
+ describe: 'Bypass the idempotency cache.',
91
+ })
92
+ .check((argv) => {
93
+ if (argv.title === undefined &&
94
+ argv.body === undefined &&
95
+ argv['body-file'] === undefined &&
96
+ argv.author === undefined) {
97
+ throw new Error('Must supply at least one of --title / --body / --body-file / --author');
98
+ }
99
+ return true;
100
+ }),
101
+ handler: async (argv) => {
102
+ try {
103
+ // Pre-flight: open the doc and verify the target lesson exists.
104
+ // We do a read-only peek first so we can fail fast with a
105
+ // useful error (list candidate ids) instead of touching the
106
+ // blockstore for a no-op change.
107
+ const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
108
+ const lessons = Array.isArray(currentDoc?.lessons) ? currentDoc.lessons : [];
109
+ const target = lessons.find((l) => l?.id === argv.lessonId);
110
+ if (!target) {
111
+ const candidates = lessons
112
+ .map((l) => l?.id)
113
+ .filter((id) => typeof id === 'string')
114
+ .slice(0, 8);
115
+ output.error(`Lesson "${argv.lessonId}" not found in ${argv.doc}. ` +
116
+ `Available ids (first 8): ${candidates.join(', ') || '(none)'}`);
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ // Resolve body replacement content if --body-file was given.
121
+ let bodyReplacement;
122
+ if (argv.bodyFile !== undefined) {
123
+ const p = (0, path_1.resolve)(argv.bodyFile);
124
+ if (!(0, fs_1.existsSync)(p)) {
125
+ output.error(`--body-file not found: ${p}`);
126
+ process.exitCode = 1;
127
+ return;
128
+ }
129
+ bodyReplacement = (0, fs_1.readFileSync)(p, 'utf8').replace(/\s+$/, '');
130
+ }
131
+ else if (argv.body !== undefined) {
132
+ bodyReplacement = argv.body.trim();
133
+ }
134
+ // Capture "before" state for the diff report so the operator
135
+ // can see what actually changed.
136
+ const before = {
137
+ title: target.title,
138
+ body: target.body,
139
+ author: target.author,
140
+ timestamp: target.timestamp,
141
+ };
142
+ const after = { ...before };
143
+ if (argv.title !== undefined)
144
+ after.title = argv.title;
145
+ if (bodyReplacement !== undefined)
146
+ after.body = bodyReplacement;
147
+ if (argv.author !== undefined)
148
+ after.author = argv.author;
149
+ if (argv.touch)
150
+ after.timestamp = Math.floor(Date.now() / 1000);
151
+ // If nothing actually changed, short-circuit — don't burn a new
152
+ // CID on a no-op.
153
+ const changedKeys = Object.keys(after).filter(k => after[k] !== before[k]);
154
+ if (changedKeys.length === 0) {
155
+ if (output.isJsonMode()) {
156
+ output.json({ status: 'noop', docId: argv.doc, lessonId: argv.lessonId });
157
+ }
158
+ else {
159
+ console.log(`No changes to apply — all requested fields already match. No new head produced.`);
160
+ }
161
+ return;
162
+ }
163
+ // Task #374: idempotency check. Brain writes are agent-scoped, so
164
+ // the scope component is the signing wallet address.
165
+ const signerKey = process.env.POP_PRIVATE_KEY;
166
+ const authorScope = signerKey ? new ethers_1.ethers.Wallet(signerKey).address.toLowerCase() : 'anonymous';
167
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
168
+ if (!argv.noIdempotency) {
169
+ const cached = (0, idempotency_1.checkIdempotencyCache)(authorScope, 'brain.editLesson', idempKey);
170
+ if (cached) {
171
+ if (output.isJsonMode()) {
172
+ output.json({ status: 'ok', cached: true, ...cached });
173
+ }
174
+ else {
175
+ console.log('');
176
+ console.log(` Lesson "${argv.lessonId}" edit cached (idempotency hit)`);
177
+ console.log(` head: ${cached.headCid}`);
178
+ console.log('');
179
+ }
180
+ return;
181
+ }
182
+ }
183
+ // Route through the unified dispatcher. When the brain daemon is
184
+ // running, this serializes an `editLesson` op and sends it via IPC
185
+ // so the write lands in the daemon's long-lived libp2p context.
186
+ // When no daemon, dispatchOp runs in-process (same applyBrainChange
187
+ // call path as before).
188
+ const fields = {};
189
+ if (argv.title !== undefined)
190
+ fields.title = argv.title;
191
+ if (bodyReplacement !== undefined)
192
+ fields.body = bodyReplacement;
193
+ if (argv.author !== undefined)
194
+ fields.author = argv.author;
195
+ const result = await (0, brain_ops_1.routedDispatch)({
196
+ type: 'editLesson',
197
+ docId: argv.doc,
198
+ lessonId: argv.lessonId,
199
+ fields,
200
+ touch: argv.touch === true,
201
+ allowInvalidShape: argv.allowInvalidShape,
202
+ });
203
+ if (!argv.noIdempotency) {
204
+ (0, idempotency_1.recordIdempotentResult)(authorScope, 'brain.editLesson', idempKey, {
205
+ docId: argv.doc,
206
+ lessonId: argv.lessonId,
207
+ headCid: result.headCid,
208
+ });
209
+ }
210
+ if (output.isJsonMode()) {
211
+ output.json({
212
+ status: 'ok',
213
+ docId: argv.doc,
214
+ lessonId: argv.lessonId,
215
+ headCid: result.headCid,
216
+ envelopeAuthor: result.envelopeAuthor,
217
+ routedViaDaemon: result.routedViaDaemon,
218
+ changedKeys,
219
+ before,
220
+ after,
221
+ });
222
+ }
223
+ else {
224
+ console.log('');
225
+ console.log(` Lesson "${argv.lessonId}" updated in ${argv.doc}`);
226
+ console.log(` changed: ${changedKeys.join(', ')}`);
227
+ console.log(` new head: ${result.headCid}`);
228
+ console.log(` routed: ${result.routedViaDaemon ? 'via brain daemon' : 'in-process (no daemon)'}`);
229
+ console.log('');
230
+ for (const k of changedKeys) {
231
+ const b = String(before[k] ?? '(unset)').slice(0, 100);
232
+ const a = String(after[k] ?? '(unset)').slice(0, 100);
233
+ console.log(` ${k}:`);
234
+ console.log(` - ${b}`);
235
+ console.log(` + ${a}`);
236
+ }
237
+ console.log('');
238
+ }
239
+ }
240
+ catch (err) {
241
+ output.error(err.message);
242
+ process.exitCode = 1;
243
+ }
244
+ finally {
245
+ await (0, brain_1.stopBrainNode)();
246
+ }
247
+ },
248
+ };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * pop brain import-snapshot — load a raw Automerge snapshot as the new local
3
+ * head for a brain doc. Task #353 (HB#348) migration tool for converging
4
+ * disjoint brain state across existing agents.
5
+ *
6
+ * Typical use:
7
+ *
8
+ * # Operator on vigil_01's machine, after fetching argus's baseline from
9
+ * # IPFS (see HB#341 brain lesson `argus-baseline-exported-for-353-migration`
10
+ * # for the pinned CIDs):
11
+ *
12
+ * curl https://ipfs.io/ipfs/QmPk6tiY2AHZyXVCFpPeRyAUY2WviCkDq6iAheokEzRbd7 > /tmp/shared.json
13
+ * node -e "
14
+ * const j = require('/tmp/shared.json');
15
+ * require('fs').writeFileSync('/tmp/shared.bin', Buffer.from(j.base64, 'base64'));
16
+ * "
17
+ *
18
+ * pop brain daemon stop # safety — no writes during migration
19
+ * pop brain read --doc pop.brain.shared --json > /tmp/local-backup.json # backup current state
20
+ * pop brain import-snapshot --doc pop.brain.shared \
21
+ * --file /tmp/shared.bin \
22
+ * --force # required if local head exists
23
+ * pop brain daemon start # restart daemon with new head
24
+ *
25
+ * ## Safety
26
+ *
27
+ * - `--force` is REQUIRED when the local brain home already has a manifest
28
+ * entry for the target doc. Without `--force`, the command refuses and
29
+ * tells the operator to back up first.
30
+ * - Operators are responsible for preserving local-only content BEFORE
31
+ * running this command. Use `pop brain read --doc <id> --json` to snapshot
32
+ * current state, replay any local-only lessons via `pop brain append-lesson`
33
+ * AFTER the import lands.
34
+ * - The import runs write-time schema validation (#346) by default. Pass
35
+ * `--allow-invalid-shape` only when you know the source bytes deliberately
36
+ * contain a non-canonical shape.
37
+ *
38
+ * ## Why this command exists
39
+ *
40
+ * HB#333-335 discovered that Automerge.merge silently drops content across
41
+ * disjoint histories. HB#337 task #352 shipped shared-genesis bootstrap so
42
+ * NEW agents joining post-PR-#10 share a common root. But the 3 existing
43
+ * Argus agents (argus_prime / vigil_01 / sentinel_01) each independently
44
+ * initialized their pop.brain.shared BEFORE #352 landed, so they remain
45
+ * mutually disjoint. Task #353 handles the one-time migration from disjoint
46
+ * back to shared-root, using one agent's current state as the canonical
47
+ * baseline that the other two import.
48
+ */
49
+ import type { Argv, ArgumentsCamelCase } from 'yargs';
50
+ interface ImportSnapshotArgs {
51
+ doc: string;
52
+ file: string;
53
+ force?: boolean;
54
+ allowInvalidShape?: boolean;
55
+ }
56
+ export declare const importSnapshotHandler: {
57
+ builder: (yargs: Argv) => Argv<{
58
+ doc: string;
59
+ } & {
60
+ file: string;
61
+ } & {
62
+ force: boolean;
63
+ } & {
64
+ "allow-invalid-shape": boolean;
65
+ }>;
66
+ handler: (argv: ArgumentsCamelCase<ImportSnapshotArgs>) => Promise<void>;
67
+ };
68
+ export {};
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /**
3
+ * pop brain import-snapshot — load a raw Automerge snapshot as the new local
4
+ * head for a brain doc. Task #353 (HB#348) migration tool for converging
5
+ * disjoint brain state across existing agents.
6
+ *
7
+ * Typical use:
8
+ *
9
+ * # Operator on vigil_01's machine, after fetching argus's baseline from
10
+ * # IPFS (see HB#341 brain lesson `argus-baseline-exported-for-353-migration`
11
+ * # for the pinned CIDs):
12
+ *
13
+ * curl https://ipfs.io/ipfs/QmPk6tiY2AHZyXVCFpPeRyAUY2WviCkDq6iAheokEzRbd7 > /tmp/shared.json
14
+ * node -e "
15
+ * const j = require('/tmp/shared.json');
16
+ * require('fs').writeFileSync('/tmp/shared.bin', Buffer.from(j.base64, 'base64'));
17
+ * "
18
+ *
19
+ * pop brain daemon stop # safety — no writes during migration
20
+ * pop brain read --doc pop.brain.shared --json > /tmp/local-backup.json # backup current state
21
+ * pop brain import-snapshot --doc pop.brain.shared \
22
+ * --file /tmp/shared.bin \
23
+ * --force # required if local head exists
24
+ * pop brain daemon start # restart daemon with new head
25
+ *
26
+ * ## Safety
27
+ *
28
+ * - `--force` is REQUIRED when the local brain home already has a manifest
29
+ * entry for the target doc. Without `--force`, the command refuses and
30
+ * tells the operator to back up first.
31
+ * - Operators are responsible for preserving local-only content BEFORE
32
+ * running this command. Use `pop brain read --doc <id> --json` to snapshot
33
+ * current state, replay any local-only lessons via `pop brain append-lesson`
34
+ * AFTER the import lands.
35
+ * - The import runs write-time schema validation (#346) by default. Pass
36
+ * `--allow-invalid-shape` only when you know the source bytes deliberately
37
+ * contain a non-canonical shape.
38
+ *
39
+ * ## Why this command exists
40
+ *
41
+ * HB#333-335 discovered that Automerge.merge silently drops content across
42
+ * disjoint histories. HB#337 task #352 shipped shared-genesis bootstrap so
43
+ * NEW agents joining post-PR-#10 share a common root. But the 3 existing
44
+ * Argus agents (argus_prime / vigil_01 / sentinel_01) each independently
45
+ * initialized their pop.brain.shared BEFORE #352 landed, so they remain
46
+ * mutually disjoint. Task #353 handles the one-time migration from disjoint
47
+ * back to shared-root, using one agent's current state as the canonical
48
+ * baseline that the other two import.
49
+ */
50
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
51
+ if (k2 === undefined) k2 = k;
52
+ var desc = Object.getOwnPropertyDescriptor(m, k);
53
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
54
+ desc = { enumerable: true, get: function() { return m[k]; } };
55
+ }
56
+ Object.defineProperty(o, k2, desc);
57
+ }) : (function(o, m, k, k2) {
58
+ if (k2 === undefined) k2 = k;
59
+ o[k2] = m[k];
60
+ }));
61
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
62
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
63
+ }) : function(o, v) {
64
+ o["default"] = v;
65
+ });
66
+ var __importStar = (this && this.__importStar) || (function () {
67
+ var ownKeys = function(o) {
68
+ ownKeys = Object.getOwnPropertyNames || function (o) {
69
+ var ar = [];
70
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
71
+ return ar;
72
+ };
73
+ return ownKeys(o);
74
+ };
75
+ return function (mod) {
76
+ if (mod && mod.__esModule) return mod;
77
+ var result = {};
78
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
79
+ __setModuleDefault(result, mod);
80
+ return result;
81
+ };
82
+ })();
83
+ Object.defineProperty(exports, "__esModule", { value: true });
84
+ exports.importSnapshotHandler = void 0;
85
+ const fs_1 = require("fs");
86
+ const path_1 = require("path");
87
+ const brain_1 = require("../../lib/brain");
88
+ const output = __importStar(require("@poa-box/cli/lib/output"));
89
+ exports.importSnapshotHandler = {
90
+ builder: (yargs) => yargs
91
+ .option('doc', {
92
+ describe: 'Target brain document ID (e.g. pop.brain.shared)',
93
+ type: 'string',
94
+ demandOption: true,
95
+ })
96
+ .option('file', {
97
+ describe: 'Path to the raw Automerge snapshot bytes to import (.bin file produced by Automerge.save())',
98
+ type: 'string',
99
+ demandOption: true,
100
+ })
101
+ .option('force', {
102
+ describe: 'Required when a local head already exists for this doc. The existing head becomes orphaned (old envelope stays in blockstore but manifest no longer points at it). Back up local-only content via `pop brain read --doc <id> --json` BEFORE using this flag.',
103
+ type: 'boolean',
104
+ default: false,
105
+ })
106
+ .option('allow-invalid-shape', {
107
+ describe: 'Bypass write-time schema validation (#346). Use only when the source bytes deliberately contain a non-canonical shape.',
108
+ type: 'boolean',
109
+ default: false,
110
+ }),
111
+ handler: async (argv) => {
112
+ try {
113
+ // Resolve + validate input file.
114
+ const filePath = (0, path_1.resolve)(argv.file);
115
+ if (!(0, fs_1.existsSync)(filePath)) {
116
+ output.error(`--file not found: ${filePath}`);
117
+ process.exitCode = 1;
118
+ return;
119
+ }
120
+ const bytes = (0, fs_1.readFileSync)(filePath);
121
+ if (bytes.length === 0) {
122
+ output.error(`--file is empty: ${filePath}`);
123
+ process.exitCode = 1;
124
+ return;
125
+ }
126
+ // Safety gate: refuse to clobber an existing head unless --force.
127
+ const existing = (0, brain_1.listBrainDocs)().find(d => d.docId === argv.doc);
128
+ if (existing && !argv.force) {
129
+ output.error(`Local brain home already has a head for "${argv.doc}" ` +
130
+ `(${existing.headCid}). Importing would orphan the existing state. ` +
131
+ `Back up local-only content first via \`pop brain read --doc ${argv.doc} --json\`, ` +
132
+ `then re-run this command with --force to confirm.`);
133
+ process.exitCode = 1;
134
+ return;
135
+ }
136
+ // Import. importBrainDoc validates via Automerge.load() + schema check,
137
+ // signs new envelope, writes block, updates manifest, publishes head.
138
+ const result = await (0, brain_1.importBrainDoc)(argv.doc, new Uint8Array(bytes), { allowInvalidShape: argv.allowInvalidShape === true });
139
+ if (output.isJsonMode()) {
140
+ output.json({
141
+ status: 'ok',
142
+ docId: argv.doc,
143
+ sourceFile: filePath,
144
+ sourceBytes: bytes.length,
145
+ newHeadCid: result.headCid,
146
+ envelopeAuthor: result.author,
147
+ replacedExistingHead: existing?.headCid ?? null,
148
+ });
149
+ }
150
+ else {
151
+ console.log('');
152
+ console.log(` Snapshot imported as new head for ${argv.doc}`);
153
+ console.log(` source file: ${filePath} (${bytes.length} bytes)`);
154
+ console.log(` new head: ${result.headCid}`);
155
+ console.log(` envelope author: ${result.author}`);
156
+ if (existing) {
157
+ console.log(` replaced head: ${existing.headCid} (orphaned in blockstore)`);
158
+ }
159
+ else {
160
+ console.log(` previous head: (none — this is a fresh import)`);
161
+ }
162
+ console.log('');
163
+ console.log(` Next steps: replay any local-only content (e.g. lessons this agent wrote ` +
164
+ `but that weren't in the source snapshot) via pop brain append-lesson. ` +
165
+ `Then restart the brain daemon if it was stopped for the migration.`);
166
+ console.log('');
167
+ }
168
+ }
169
+ catch (err) {
170
+ output.error(err.message);
171
+ process.exitCode = 1;
172
+ }
173
+ finally {
174
+ await (0, brain_1.stopBrainNode)();
175
+ }
176
+ },
177
+ };
@@ -0,0 +1,2 @@
1
+ import type { Argv } from 'yargs';
2
+ export declare function registerBrainCommands(yargs: Argv): Argv<{}>;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerBrainCommands = registerBrainCommands;
4
+ const status_1 = require("./status");
5
+ const subscribe_1 = require("./subscribe");
6
+ const read_1 = require("./read");
7
+ const list_1 = require("./list");
8
+ const snapshot_1 = require("./snapshot");
9
+ const migrate_1 = require("./migrate");
10
+ const append_lesson_1 = require("./append-lesson");
11
+ const edit_lesson_1 = require("./edit-lesson");
12
+ const remove_lesson_1 = require("./remove-lesson");
13
+ const search_1 = require("./search");
14
+ const tag_1 = require("./tag");
15
+ const brainstorm_1 = require("./brainstorm");
16
+ const new_project_1 = require("./new-project");
17
+ const advance_stage_1 = require("./advance-stage");
18
+ const remove_project_1 = require("./remove-project");
19
+ const allowlist_1 = require("./allowlist");
20
+ const migrate_projects_1 = require("./migrate-projects");
21
+ const doctor_1 = require("./doctor");
22
+ const import_snapshot_1 = require("./import-snapshot");
23
+ const daemon_1 = require("./daemon");
24
+ const retro_start_1 = require("./retro-start");
25
+ const retro_list_1 = require("./retro-list");
26
+ const retro_show_1 = require("./retro-show");
27
+ const retro_respond_1 = require("./retro-respond");
28
+ const retro_file_tasks_1 = require("./retro-file-tasks");
29
+ const retro_mark_change_1 = require("./retro-mark-change");
30
+ const retro_remove_1 = require("./retro-remove");
31
+ function registerBrainCommands(yargs) {
32
+ return yargs
33
+ .command('status', 'Show local brain layer state (Helia + CRDT sync)', status_1.statusHandler.builder, status_1.statusHandler.handler)
34
+ .command('subscribe', 'Subscribe to a doc topic; fetch + merge announced blocks (or --log-only)', subscribe_1.subscribeHandler.builder, subscribe_1.subscribeHandler.handler)
35
+ .command('read', 'Load a brain doc from local state and print its contents', read_1.readHandler.builder, read_1.readHandler.handler)
36
+ .command('list', 'List all known brain docs with their current head CIDs', list_1.listHandler.builder, list_1.listHandler.handler)
37
+ .command('snapshot', 'Project a brain doc to markdown on disk (step 7)', snapshot_1.snapshotHandler.builder, snapshot_1.snapshotHandler.handler)
38
+ .command('migrate', 'Import a hand-written markdown file into a brain doc (step 8)', migrate_1.migrateHandler.builder, migrate_1.migrateHandler.handler)
39
+ .command('append-lesson', 'Append a lesson to a brain doc (signed + gossipsub-published)', append_lesson_1.appendLessonHandler.builder, append_lesson_1.appendLessonHandler.handler)
40
+ .command('edit-lesson', 'Update fields on an existing brain lesson (in-place)', edit_lesson_1.editLessonHandler.builder, edit_lesson_1.editLessonHandler.handler)
41
+ .command('remove-lesson', 'Soft-delete a brain lesson (tombstone; filtered from snapshot output)', remove_lesson_1.removeLessonHandler.builder, remove_lesson_1.removeLessonHandler.handler)
42
+ .command('search', 'Filter lessons in a brain doc by query / tag / author / timestamp', search_1.searchHandler.builder, search_1.searchHandler.handler)
43
+ .command('tag', 'Add or remove tags on an existing brain lesson', tag_1.tagHandler.builder, tag_1.tagHandler.handler)
44
+ .command('brainstorm-start', 'Open a new cross-agent brainstorm (task #354 — forward-looking ideation surface)', brainstorm_1.brainstormStartHandler.builder, brainstorm_1.brainstormStartHandler.handler)
45
+ .command('brainstorm-respond', 'Post a message, add an idea, or cast votes on an existing brainstorm', brainstorm_1.brainstormRespondHandler.builder, brainstorm_1.brainstormRespondHandler.handler)
46
+ .command('brainstorm-promote', 'Promote a brainstorm idea to a pop.brain.projects entry (link via promotedToProjectIds)', brainstorm_1.brainstormPromoteHandler.builder, brainstorm_1.brainstormPromoteHandler.handler)
47
+ .command('brainstorm-close', 'Close a brainstorm without promoting any idea', brainstorm_1.brainstormCloseHandler.builder, brainstorm_1.brainstormCloseHandler.handler)
48
+ .command('brainstorm-remove', 'Soft-delete a brainstorm (tombstone; filtered from projection output)', brainstorm_1.brainstormRemoveHandler.builder, brainstorm_1.brainstormRemoveHandler.handler)
49
+ .command('new-project', 'Create a project entry in pop.brain.projects (signed + gossipsub-published)', new_project_1.newProjectHandler.builder, new_project_1.newProjectHandler.handler)
50
+ .command('advance-stage', 'Move a project forward in the lifecycle (propose → discuss → ... → ship)', advance_stage_1.advanceStageHandler.builder, advance_stage_1.advanceStageHandler.handler)
51
+ .command('remove-project', 'Soft-delete a project entry (tombstone; filtered from snapshot output)', remove_project_1.removeProjectHandler.builder, remove_project_1.removeProjectHandler.handler)
52
+ .command('allowlist <action>', 'Manage the brain allowlist (list/add/remove)', allowlist_1.allowlistHandler.builder, allowlist_1.allowlistHandler.handler)
53
+ .command('migrate-projects', 'Import projects.md into a pop.brain.projects doc (sprint-3 follow-up to step 8)', migrate_projects_1.migrateProjectsHandler.builder, migrate_projects_1.migrateProjectsHandler.handler)
54
+ .command('doctor', 'Health check for brain layer setup (env, keys, libp2p init, allowlist, manifest)', doctor_1.doctorHandler.builder, doctor_1.doctorHandler.handler)
55
+ .command('import-snapshot', 'Load a raw Automerge snapshot file as the new local head for a brain doc (#353 migration tool for converging disjoint agents onto a shared baseline)', import_snapshot_1.importSnapshotHandler.builder, import_snapshot_1.importSnapshotHandler.handler)
56
+ .command('daemon <action>', 'Manage the persistent brain daemon (start/stop/status/logs) — keeps libp2p alive so gossipsub announcements actually propagate', daemon_1.daemonHandler.builder, daemon_1.daemonHandler.handler)
57
+ .command('retro <action>', 'Manage session retros in pop.brain.retros (start/list/show) — recurring self-reflection cycles with proposed changes and cross-agent discussion', (yargs) => yargs
58
+ .command('start', 'Start a new retro with observations + proposed changes', retro_start_1.retroStartHandler.builder, retro_start_1.retroStartHandler.handler)
59
+ .command('list', 'List retros in pop.brain.retros (optionally filter by --status)', retro_list_1.retroListHandler.builder, retro_list_1.retroListHandler.handler)
60
+ .command('show <retro-id>', 'Render a single retro as markdown', retro_show_1.retroShowHandler.builder, retro_show_1.retroShowHandler.handler)
61
+ .command('respond', 'Append a discussion entry (optionally with per-change votes) to an open retro', retro_respond_1.retroRespondHandler.builder, retro_respond_1.retroRespondHandler.handler)
62
+ .command('file-tasks', 'Convert agreed retro changes into on-chain tasks (idempotent)', retro_file_tasks_1.retroFileTasksHandler.builder, retro_file_tasks_1.retroFileTasksHandler.handler)
63
+ .command('mark-change <retro-id> <change-id>', 'Manually set a proposed-change status (e.g. agreed / rejected / modified) before running file-tasks', retro_mark_change_1.retroMarkChangeHandler.builder, retro_mark_change_1.retroMarkChangeHandler.handler)
64
+ .command('remove <retro-id>', 'Soft-delete a retro (tombstone; filtered from projection) — useful for test retros and retros started in error', retro_remove_1.retroRemoveHandler.builder, retro_remove_1.retroRemoveHandler.handler)
65
+ .demandCommand(1, 'Please specify a retro action: start, list, show, respond, file-tasks, mark-change, remove'), () => { })
66
+ .demandCommand(1, 'Please specify a brain action');
67
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * pop brain list — enumerate all known brain docs with their current head CIDs.
3
+ *
4
+ * Thin wrapper over listBrainDocs() which reads the manifest file directly
5
+ * (no Helia process required). Useful for:
6
+ *
7
+ * - Discovering what docs an agent has locally without knowing the ID
8
+ * - Sanity-checking that a subscribe session actually merged a remote head
9
+ * - Feeding `pop brain read --doc <id>` for any doc returned here
10
+ *
11
+ * Implementation intentionally avoids spinning up a Helia node — listing
12
+ * is a pure manifest read.
13
+ */
14
+ import type { Argv, ArgumentsCamelCase } from 'yargs';
15
+ interface ListArgs {
16
+ }
17
+ export declare const listHandler: {
18
+ builder: (yargs: Argv) => Argv<{}>;
19
+ handler: (_argv: ArgumentsCamelCase<ListArgs>) => Promise<void>;
20
+ };
21
+ export {};