@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.
- package/.env.agent.template +20 -0
- package/README.md +46 -0
- package/brain/Config/agent-config.json +14 -0
- package/brain/Config/brain-allowlist.json +20 -0
- package/brain/Identity/goals.template.md +23 -0
- package/brain/Identity/how-i-think.md +406 -0
- package/brain/Identity/who-i-am.template.md +34 -0
- package/brain/Knowledge/BOOTSTRAP.md +66 -0
- package/brain/Knowledge/audit-corpus-index.json +406 -0
- package/brain/Knowledge/discussions.json +245 -0
- package/brain/Knowledge/pop.brain.brainstorms.generated.md +48 -0
- package/brain/Knowledge/pop.brain.brainstorms.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.heuristics.snapshot.bin +0 -0
- package/brain/Knowledge/pop.brain.projects.generated.md +16 -0
- package/brain/Knowledge/pop.brain.projects.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.retros.generated.md +91 -0
- package/brain/Knowledge/pop.brain.retros.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.shared.generated.md +3811 -0
- package/brain/Knowledge/pop.brain.shared.genesis.bin +0 -0
- package/brain/Knowledge/projects.md +181 -0
- package/brain/Knowledge/risk-framework.md +90 -0
- package/brain/Knowledge/shared.md +416 -0
- package/brain/Knowledge/sprint-priorities.md +439 -0
- package/brain/Memory/.gitkeep +0 -0
- package/dist/commands/agent/daily-digest.d.ts +24 -0
- package/dist/commands/agent/daily-digest.js +336 -0
- package/dist/commands/agent/delegate.d.ts +12 -0
- package/dist/commands/agent/delegate.js +91 -0
- package/dist/commands/agent/deploy-to-org.d.ts +20 -0
- package/dist/commands/agent/deploy-to-org.js +154 -0
- package/dist/commands/agent/index.d.ts +2 -0
- package/dist/commands/agent/index.js +27 -0
- package/dist/commands/agent/init.d.ts +19 -0
- package/dist/commands/agent/init.js +303 -0
- package/dist/commands/agent/onboard.d.ts +22 -0
- package/dist/commands/agent/onboard.js +192 -0
- package/dist/commands/agent/paymaster-status.d.ts +14 -0
- package/dist/commands/agent/paymaster-status.js +130 -0
- package/dist/commands/agent/register.d.ts +21 -0
- package/dist/commands/agent/register.js +116 -0
- package/dist/commands/agent/setup-sponsorship.d.ts +22 -0
- package/dist/commands/agent/setup-sponsorship.js +154 -0
- package/dist/commands/agent/status.d.ts +12 -0
- package/dist/commands/agent/status.js +171 -0
- package/dist/commands/agent/triage.d.ts +12 -0
- package/dist/commands/agent/triage.js +503 -0
- package/dist/commands/brain/advance-stage.d.ts +42 -0
- package/dist/commands/brain/advance-stage.js +206 -0
- package/dist/commands/brain/allowlist.d.ts +30 -0
- package/dist/commands/brain/allowlist.js +274 -0
- package/dist/commands/brain/append-lesson.d.ts +55 -0
- package/dist/commands/brain/append-lesson.js +245 -0
- package/dist/commands/brain/brainstorm.d.ts +154 -0
- package/dist/commands/brain/brainstorm.js +573 -0
- package/dist/commands/brain/daemon.d.ts +31 -0
- package/dist/commands/brain/daemon.js +348 -0
- package/dist/commands/brain/doctor.d.ts +27 -0
- package/dist/commands/brain/doctor.js +497 -0
- package/dist/commands/brain/edit-lesson.d.ts +51 -0
- package/dist/commands/brain/edit-lesson.js +248 -0
- package/dist/commands/brain/import-snapshot.d.ts +68 -0
- package/dist/commands/brain/import-snapshot.js +177 -0
- package/dist/commands/brain/index.d.ts +2 -0
- package/dist/commands/brain/index.js +67 -0
- package/dist/commands/brain/list.d.ts +21 -0
- package/dist/commands/brain/list.js +83 -0
- package/dist/commands/brain/migrate-projects.d.ts +44 -0
- package/dist/commands/brain/migrate-projects.js +209 -0
- package/dist/commands/brain/migrate.d.ts +74 -0
- package/dist/commands/brain/migrate.js +306 -0
- package/dist/commands/brain/new-project.d.ts +53 -0
- package/dist/commands/brain/new-project.js +226 -0
- package/dist/commands/brain/read.d.ts +24 -0
- package/dist/commands/brain/read.js +81 -0
- package/dist/commands/brain/remove-lesson.d.ts +47 -0
- package/dist/commands/brain/remove-lesson.js +206 -0
- package/dist/commands/brain/remove-project.d.ts +36 -0
- package/dist/commands/brain/remove-project.js +177 -0
- package/dist/commands/brain/retro-file-tasks.d.ts +84 -0
- package/dist/commands/brain/retro-file-tasks.js +372 -0
- package/dist/commands/brain/retro-list.d.ts +28 -0
- package/dist/commands/brain/retro-list.js +125 -0
- package/dist/commands/brain/retro-mark-change.d.ts +58 -0
- package/dist/commands/brain/retro-mark-change.js +176 -0
- package/dist/commands/brain/retro-remove.d.ts +36 -0
- package/dist/commands/brain/retro-remove.js +142 -0
- package/dist/commands/brain/retro-respond.d.ts +56 -0
- package/dist/commands/brain/retro-respond.js +250 -0
- package/dist/commands/brain/retro-show.d.ts +23 -0
- package/dist/commands/brain/retro-show.js +100 -0
- package/dist/commands/brain/retro-start.d.ts +55 -0
- package/dist/commands/brain/retro-start.js +311 -0
- package/dist/commands/brain/search.d.ts +48 -0
- package/dist/commands/brain/search.js +190 -0
- package/dist/commands/brain/snapshot.d.ts +32 -0
- package/dist/commands/brain/snapshot.js +243 -0
- package/dist/commands/brain/status.d.ts +15 -0
- package/dist/commands/brain/status.js +166 -0
- package/dist/commands/brain/subscribe.d.ts +28 -0
- package/dist/commands/brain/subscribe.js +90 -0
- package/dist/commands/brain/tag.d.ts +46 -0
- package/dist/commands/brain/tag.js +192 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +22 -0
- package/dist/lib/brain-daemon.d.ts +126 -0
- package/dist/lib/brain-daemon.js +811 -0
- package/dist/lib/brain-membership.d.ts +58 -0
- package/dist/lib/brain-membership.js +115 -0
- package/dist/lib/brain-migrate-projects.d.ts +43 -0
- package/dist/lib/brain-migrate-projects.js +247 -0
- package/dist/lib/brain-migrate.d.ts +77 -0
- package/dist/lib/brain-migrate.js +328 -0
- package/dist/lib/brain-ops.d.ts +271 -0
- package/dist/lib/brain-ops.js +571 -0
- package/dist/lib/brain-paths.d.ts +15 -0
- package/dist/lib/brain-paths.js +33 -0
- package/dist/lib/brain-projections.d.ts +216 -0
- package/dist/lib/brain-projections.js +829 -0
- package/dist/lib/brain-schemas.d.ts +36 -0
- package/dist/lib/brain-schemas.js +316 -0
- package/dist/lib/brain-signing.d.ts +103 -0
- package/dist/lib/brain-signing.js +256 -0
- package/dist/lib/brain.d.ts +198 -0
- package/dist/lib/brain.js +1057 -0
- package/dist/pop-agent.d.ts +1 -0
- package/dist/pop-agent.js +18 -0
- package/docs/agent.md +126 -0
- package/docs/agents/brain-anti-entropy.md +127 -0
- package/docs/agents/brain-cross-device-onboarding.md +210 -0
- package/docs/agents/brain-cross-machine-smoke.md +241 -0
- package/docs/agents/brain-layer-setup.md +725 -0
- package/docs/agents/offboarding-protocol.md +188 -0
- package/docs/agents/onboarding-protocol.md +243 -0
- package/docs/agents/running-an-agent.md +200 -0
- package/docs/brain.md +560 -0
- package/package.json +61 -0
- package/scripts/apply.sh +140 -0
- package/scripts/onboard.sh +205 -0
- 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 {};
|