@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,47 @@
1
+ /**
2
+ * pop brain remove-lesson — mark an existing lesson as soft-deleted.
3
+ *
4
+ * Sets a tombstone on the lesson object:
5
+ *
6
+ * {
7
+ * removed: true,
8
+ * removedAt: <unix-seconds>,
9
+ * removedBy: <signing wallet address>,
10
+ * removedReason: <optional --reason string>
11
+ * }
12
+ *
13
+ * The lesson stays in doc.lessons — the projection layer filters
14
+ * tombstoned entries out of rendered markdown. This is the CRDT-safe
15
+ * shape: hard delete from an Automerge list can lose concurrent edits,
16
+ * and two concurrent soft-deletes converge via last-write-wins at the
17
+ * field level (adding `removed: true` twice is idempotent).
18
+ *
19
+ * No --undo / --restore in this ship. If we need undelete later, the
20
+ * shape is another field like `restoredAt` that supersedes removedAt.
21
+ */
22
+ import type { Argv, ArgumentsCamelCase } from 'yargs';
23
+ interface RemoveArgs {
24
+ doc: string;
25
+ lessonId: string;
26
+ reason?: string;
27
+ allowInvalidShape?: boolean;
28
+ 'idempotency-key'?: string;
29
+ 'no-idempotency'?: boolean;
30
+ }
31
+ export declare const removeLessonHandler: {
32
+ builder: (yargs: Argv) => Argv<{
33
+ doc: string;
34
+ } & {
35
+ "lesson-id": string;
36
+ } & {
37
+ reason: string | undefined;
38
+ } & {
39
+ "allow-invalid-shape": boolean;
40
+ } & {
41
+ "idempotency-key": string | undefined;
42
+ } & {
43
+ "no-idempotency": boolean;
44
+ }>;
45
+ handler: (argv: ArgumentsCamelCase<RemoveArgs>) => Promise<void>;
46
+ };
47
+ export {};
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ /**
3
+ * pop brain remove-lesson — mark an existing lesson as soft-deleted.
4
+ *
5
+ * Sets a tombstone on the lesson object:
6
+ *
7
+ * {
8
+ * removed: true,
9
+ * removedAt: <unix-seconds>,
10
+ * removedBy: <signing wallet address>,
11
+ * removedReason: <optional --reason string>
12
+ * }
13
+ *
14
+ * The lesson stays in doc.lessons — the projection layer filters
15
+ * tombstoned entries out of rendered markdown. This is the CRDT-safe
16
+ * shape: hard delete from an Automerge list can lose concurrent edits,
17
+ * and two concurrent soft-deletes converge via last-write-wins at the
18
+ * field level (adding `removed: true` twice is idempotent).
19
+ *
20
+ * No --undo / --restore in this ship. If we need undelete later, the
21
+ * shape is another field like `restoredAt` that supersedes removedAt.
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ exports.removeLessonHandler = void 0;
58
+ const ethers_1 = require("ethers");
59
+ const brain_1 = require("../../lib/brain");
60
+ const brain_ops_1 = require("../../lib/brain-ops");
61
+ const idempotency_1 = require("@poa-box/cli/lib/idempotency");
62
+ const output = __importStar(require("@poa-box/cli/lib/output"));
63
+ exports.removeLessonHandler = {
64
+ builder: (yargs) => yargs
65
+ .option('doc', {
66
+ describe: 'Brain document ID (e.g. pop.brain.shared)',
67
+ type: 'string',
68
+ demandOption: true,
69
+ })
70
+ .option('lesson-id', {
71
+ describe: 'The lesson.id to tombstone',
72
+ type: 'string',
73
+ demandOption: true,
74
+ })
75
+ .option('reason', {
76
+ describe: 'Optional human-readable reason recorded on the tombstone',
77
+ type: 'string',
78
+ })
79
+ .option('allow-invalid-shape', {
80
+ describe: 'Bypass write-time schema validation (Task #346).',
81
+ type: 'boolean',
82
+ default: false,
83
+ })
84
+ .option('idempotency-key', {
85
+ type: 'string',
86
+ describe: 'Task #374 (HB#215): explicit idempotency key. Agent-scoped.',
87
+ })
88
+ .option('no-idempotency', {
89
+ type: 'boolean',
90
+ default: false,
91
+ describe: 'Bypass the idempotency cache.',
92
+ }),
93
+ handler: async (argv) => {
94
+ try {
95
+ // Pre-flight peek: locate the lesson, fail fast with candidate
96
+ // ids if not found. Same pattern as edit-lesson.
97
+ const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
98
+ const lessons = Array.isArray(currentDoc?.lessons) ? currentDoc.lessons : [];
99
+ const target = lessons.find((l) => l?.id === argv.lessonId);
100
+ if (!target) {
101
+ const candidates = lessons
102
+ .map((l) => l?.id)
103
+ .filter((id) => typeof id === 'string')
104
+ .slice(0, 8);
105
+ output.error(`Lesson "${argv.lessonId}" not found in ${argv.doc}. ` +
106
+ `Available ids (first 8): ${candidates.join(', ') || '(none)'}`);
107
+ process.exitCode = 1;
108
+ return;
109
+ }
110
+ // Already tombstoned — short-circuit.
111
+ if (target.removed === true) {
112
+ if (output.isJsonMode()) {
113
+ output.json({
114
+ status: 'already-removed',
115
+ docId: argv.doc,
116
+ lessonId: argv.lessonId,
117
+ removedAt: target.removedAt,
118
+ removedBy: target.removedBy,
119
+ });
120
+ }
121
+ else {
122
+ console.log(`Lesson "${argv.lessonId}" is already tombstoned ` +
123
+ `(removedAt=${target.removedAt}, removedBy=${target.removedBy}). No-op.`);
124
+ }
125
+ return;
126
+ }
127
+ // Derive the remover address from POP_PRIVATE_KEY so the
128
+ // tombstone's removedBy matches the signing envelope's author.
129
+ const key = process.env.POP_PRIVATE_KEY;
130
+ if (!key) {
131
+ output.error('POP_PRIVATE_KEY not set — cannot record removedBy. Set the env var and retry.');
132
+ process.exitCode = 1;
133
+ return;
134
+ }
135
+ const removerAddress = new ethers_1.ethers.Wallet(key).address.toLowerCase();
136
+ const now = Math.floor(Date.now() / 1000);
137
+ // Task #374: idempotency check (agent-scoped, uses removerAddress)
138
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
139
+ if (!argv.noIdempotency) {
140
+ const cached = (0, idempotency_1.checkIdempotencyCache)(removerAddress, 'brain.removeLesson', idempKey);
141
+ if (cached) {
142
+ if (output.isJsonMode()) {
143
+ output.json({ status: 'ok', cached: true, ...cached });
144
+ }
145
+ else {
146
+ console.log('');
147
+ console.log(` Lesson "${argv.lessonId}" already removed (idempotency cache hit)`);
148
+ console.log(` head: ${cached.headCid}`);
149
+ console.log('');
150
+ }
151
+ return;
152
+ }
153
+ }
154
+ // Route through the unified dispatcher (HB#324 ship-2).
155
+ const result = await (0, brain_ops_1.routedDispatch)({
156
+ type: 'removeLesson',
157
+ docId: argv.doc,
158
+ lessonId: argv.lessonId,
159
+ removedBy: removerAddress,
160
+ removedAt: now,
161
+ removedReason: argv.reason,
162
+ allowInvalidShape: argv.allowInvalidShape,
163
+ });
164
+ if (!argv.noIdempotency) {
165
+ (0, idempotency_1.recordIdempotentResult)(removerAddress, 'brain.removeLesson', idempKey, {
166
+ docId: argv.doc,
167
+ lessonId: argv.lessonId,
168
+ headCid: result.headCid,
169
+ });
170
+ }
171
+ if (output.isJsonMode()) {
172
+ output.json({
173
+ status: 'ok',
174
+ docId: argv.doc,
175
+ lessonId: argv.lessonId,
176
+ headCid: result.headCid,
177
+ routedViaDaemon: result.routedViaDaemon,
178
+ removedAt: now,
179
+ removedBy: removerAddress,
180
+ removedReason: argv.reason ?? null,
181
+ });
182
+ }
183
+ else {
184
+ console.log('');
185
+ console.log(` Lesson "${argv.lessonId}" tombstoned in ${argv.doc}`);
186
+ console.log(` removedBy: ${removerAddress}`);
187
+ console.log(` removedAt: ${new Date(now * 1000).toISOString()}`);
188
+ if (argv.reason)
189
+ console.log(` reason: ${argv.reason}`);
190
+ console.log(` new head: ${result.headCid}`);
191
+ console.log(` routed: ${result.routedViaDaemon ? 'via brain daemon' : 'in-process (no daemon)'}`);
192
+ console.log('');
193
+ console.log(` The lesson is still in the Automerge doc (pop brain read still shows it). ` +
194
+ `The next pop brain snapshot will filter it out of the rendered markdown.`);
195
+ console.log('');
196
+ }
197
+ }
198
+ catch (err) {
199
+ output.error(err.message);
200
+ process.exitCode = 1;
201
+ }
202
+ finally {
203
+ await (0, brain_1.stopBrainNode)();
204
+ }
205
+ },
206
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * pop brain remove-project — soft-delete a project entry in pop.brain.projects.
3
+ *
4
+ * Mirror of remove-lesson (#304). Sets {removed: true, removedAt,
5
+ * removedBy, removedReason?} on the project object. The raw Automerge
6
+ * state is unchanged; projectProjects filters tombstoned projects
7
+ * out of the main Projects section and surfaces a compact '## Removed
8
+ * projects' summary at the bottom.
9
+ *
10
+ * Soft delete stays CRDT-safe: concurrent tombstones are idempotent
11
+ * at the field-level LWW, and concurrent edits to a project that's
12
+ * tombstoned concurrently still converge cleanly.
13
+ */
14
+ import type { Argv, ArgumentsCamelCase } from 'yargs';
15
+ interface RemoveProjectArgs {
16
+ doc: string;
17
+ projectId: string;
18
+ reason?: string;
19
+ 'idempotency-key'?: string;
20
+ 'no-idempotency'?: boolean;
21
+ }
22
+ export declare const removeProjectHandler: {
23
+ builder: (yargs: Argv) => Argv<{
24
+ doc: string;
25
+ } & {
26
+ "project-id": string;
27
+ } & {
28
+ reason: string | undefined;
29
+ } & {
30
+ "idempotency-key": string | undefined;
31
+ } & {
32
+ "no-idempotency": boolean;
33
+ }>;
34
+ handler: (argv: ArgumentsCamelCase<RemoveProjectArgs>) => Promise<void>;
35
+ };
36
+ export {};
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ /**
3
+ * pop brain remove-project — soft-delete a project entry in pop.brain.projects.
4
+ *
5
+ * Mirror of remove-lesson (#304). Sets {removed: true, removedAt,
6
+ * removedBy, removedReason?} on the project object. The raw Automerge
7
+ * state is unchanged; projectProjects filters tombstoned projects
8
+ * out of the main Projects section and surfaces a compact '## Removed
9
+ * projects' summary at the bottom.
10
+ *
11
+ * Soft delete stays CRDT-safe: concurrent tombstones are idempotent
12
+ * at the field-level LWW, and concurrent edits to a project that's
13
+ * tombstoned concurrently still converge cleanly.
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.removeProjectHandler = void 0;
50
+ const ethers_1 = require("ethers");
51
+ const brain_1 = require("../../lib/brain");
52
+ const brain_ops_1 = require("../../lib/brain-ops");
53
+ const idempotency_1 = require("@poa-box/cli/lib/idempotency");
54
+ const output = __importStar(require("@poa-box/cli/lib/output"));
55
+ exports.removeProjectHandler = {
56
+ builder: (yargs) => yargs
57
+ .option('doc', {
58
+ describe: 'Brain document ID (typically pop.brain.projects)',
59
+ type: 'string',
60
+ demandOption: true,
61
+ })
62
+ .option('project-id', {
63
+ describe: 'The project.id to tombstone',
64
+ type: 'string',
65
+ demandOption: true,
66
+ })
67
+ .option('reason', {
68
+ describe: 'Optional human-readable reason recorded on the tombstone',
69
+ type: 'string',
70
+ })
71
+ .option('idempotency-key', { type: 'string', describe: 'Task #375 (HB#217) idempotency cache.' })
72
+ .option('no-idempotency', { type: 'boolean', default: false, describe: 'Bypass the idempotency cache.' }),
73
+ handler: async (argv) => {
74
+ try {
75
+ const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
76
+ const projects = Array.isArray(currentDoc?.projects) ? currentDoc.projects : [];
77
+ const target = projects.find((p) => p?.id === argv.projectId);
78
+ if (!target) {
79
+ const candidates = projects
80
+ .map((p) => p?.id)
81
+ .filter((id) => typeof id === 'string')
82
+ .slice(0, 8);
83
+ output.error(`Project "${argv.projectId}" not found in ${argv.doc}. ` +
84
+ `Available ids (first 8): ${candidates.join(', ') || '(none)'}`);
85
+ process.exitCode = 1;
86
+ return;
87
+ }
88
+ if (target.removed === true) {
89
+ if (output.isJsonMode()) {
90
+ output.json({
91
+ status: 'already-removed',
92
+ docId: argv.doc,
93
+ projectId: argv.projectId,
94
+ removedAt: target.removedAt,
95
+ removedBy: target.removedBy,
96
+ });
97
+ }
98
+ else {
99
+ console.log(`Project "${argv.projectId}" is already tombstoned ` +
100
+ `(removedAt=${target.removedAt}, removedBy=${target.removedBy}). No-op.`);
101
+ }
102
+ return;
103
+ }
104
+ const key = process.env.POP_PRIVATE_KEY;
105
+ if (!key) {
106
+ output.error('POP_PRIVATE_KEY not set — cannot record removedBy. Set the env var and retry.');
107
+ process.exitCode = 1;
108
+ return;
109
+ }
110
+ const removerAddress = new ethers_1.ethers.Wallet(key).address.toLowerCase();
111
+ const now = Math.floor(Date.now() / 1000);
112
+ // Task #375 (HB#217): agent-scoped idempotency cache
113
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
114
+ if (!argv.noIdempotency) {
115
+ const cached = (0, idempotency_1.checkIdempotencyCache)(removerAddress, 'brain.removeProject', idempKey);
116
+ if (cached) {
117
+ if (output.isJsonMode()) {
118
+ output.json({ status: 'ok', cached: true, ...cached });
119
+ }
120
+ else {
121
+ console.log(` Project "${argv.projectId}" already removed (idempotency cache hit). head: ${cached.headCid}`);
122
+ }
123
+ return;
124
+ }
125
+ }
126
+ // Route through the unified dispatcher (HB#324 ship-2).
127
+ const result = await (0, brain_ops_1.routedDispatch)({
128
+ type: 'removeProject',
129
+ docId: argv.doc,
130
+ projectId: argv.projectId,
131
+ removedBy: removerAddress,
132
+ removedAt: now,
133
+ removedReason: argv.reason,
134
+ });
135
+ if (!argv.noIdempotency) {
136
+ (0, idempotency_1.recordIdempotentResult)(removerAddress, 'brain.removeProject', idempKey, {
137
+ docId: argv.doc,
138
+ projectId: argv.projectId,
139
+ headCid: result.headCid,
140
+ });
141
+ }
142
+ if (output.isJsonMode()) {
143
+ output.json({
144
+ status: 'ok',
145
+ docId: argv.doc,
146
+ projectId: argv.projectId,
147
+ headCid: result.headCid,
148
+ routedViaDaemon: result.routedViaDaemon,
149
+ removedAt: now,
150
+ removedBy: removerAddress,
151
+ removedReason: argv.reason ?? null,
152
+ });
153
+ }
154
+ else {
155
+ console.log('');
156
+ console.log(` Project "${argv.projectId}" tombstoned in ${argv.doc}`);
157
+ console.log(` removedBy: ${removerAddress}`);
158
+ console.log(` removedAt: ${new Date(now * 1000).toISOString()}`);
159
+ if (argv.reason)
160
+ console.log(` reason: ${argv.reason}`);
161
+ console.log(` new head: ${result.headCid}`);
162
+ console.log(` routed: ${result.routedViaDaemon ? 'via brain daemon' : 'in-process (no daemon)'}`);
163
+ console.log('');
164
+ console.log(` The project is still in the Automerge doc (pop brain read still shows it). ` +
165
+ `The next pop brain snapshot will filter it out of the rendered markdown.`);
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,84 @@
1
+ /**
2
+ * pop brain retro file-tasks — convert agreed retro changes into on-chain tasks.
3
+ *
4
+ * Usage:
5
+ *
6
+ * pop brain retro file-tasks --retro retro-327-1776...
7
+ * pop brain retro file-tasks --retro retro-327-1776... --dry-run
8
+ * pop brain retro file-tasks --retro retro-327-1776... --project "CLI Infrastructure"
9
+ *
10
+ * Flow:
11
+ *
12
+ * 1. Load the retro from pop.brain.retros.
13
+ * 2. Find every proposed change whose status is 'agreed'. Changes at any
14
+ * other status are skipped — 'proposed' needs more discussion first,
15
+ * 'rejected' was explicitly rejected, 'modified' needs a second round,
16
+ * and 'filed' is already done (this is the idempotency guarantee:
17
+ * re-running the command is safe because filed changes are no-ops).
18
+ * 3. For each agreed change:
19
+ * a. Call `pop task create` with a structured description derived
20
+ * from the change summary + details + retro window.
21
+ * b. Capture the returned task id.
22
+ * c. Run `updateChangeStatus` to flip the change to 'filed' with
23
+ * filedTaskId set.
24
+ * 4. When every change is filed or rejected, the updateChangeStatus op
25
+ * auto-advances the retro status to 'shipped' with a closedAt
26
+ * timestamp.
27
+ *
28
+ * ## Idempotency
29
+ *
30
+ * This command is safe to run multiple times. A change with status='filed'
31
+ * is skipped. A change that was already converted won't be re-filed. The
32
+ * idempotency guarantee requires the `agreed → filed` transition to be
33
+ * the only way a change gets converted. Operators who want to re-file a
34
+ * change after filing it should run `pop brain retro respond --vote
35
+ * change-X=agree` again and then call file-tasks — but the previous
36
+ * filed task id is never overwritten, so the old task sticks around
37
+ * unless manually cancelled. That's a deliberate safety design: no
38
+ * accidental re-filing of the same work.
39
+ *
40
+ * ## Dry-run
41
+ *
42
+ * `--dry-run` prints the plan (which changes would be filed, what task
43
+ * description would be produced) without actually creating any tasks or
44
+ * mutating the retro. Use this to preview before committing.
45
+ *
46
+ * ## Project selection
47
+ *
48
+ * Defaults to "CLI Infrastructure" — the historical catch-all for brain
49
+ * layer + CLI work. Override with --project if the retro's proposed
50
+ * changes belong to a different project (e.g. "DeFi Research",
51
+ * "Agent Protocol").
52
+ */
53
+ import type { Argv, ArgumentsCamelCase } from 'yargs';
54
+ interface RetroFileTasksArgs {
55
+ doc: string;
56
+ retro: string;
57
+ project?: string;
58
+ payout?: number;
59
+ difficulty?: 'easy' | 'medium' | 'hard';
60
+ estHours?: number;
61
+ dryRun?: boolean;
62
+ yes?: boolean;
63
+ }
64
+ export declare const retroFileTasksHandler: {
65
+ builder: (yargs: Argv) => Argv<{
66
+ doc: string;
67
+ } & {
68
+ retro: string;
69
+ } & {
70
+ project: string;
71
+ } & {
72
+ payout: number;
73
+ } & {
74
+ difficulty: "medium" | "easy" | "hard";
75
+ } & {
76
+ "est-hours": number;
77
+ } & {
78
+ "dry-run": boolean;
79
+ } & {
80
+ yes: boolean;
81
+ }>;
82
+ handler: (argv: ArgumentsCamelCase<RetroFileTasksArgs>) => Promise<void>;
83
+ };
84
+ export {};