@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,306 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain migrate — one-shot import of a hand-written brain knowledge
|
|
4
|
+
* file into a CRDT Automerge doc. Step 8 of the brain plan.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* pop brain migrate --from agent/brain/Knowledge/shared.md \
|
|
8
|
+
* --doc pop.brain.shared
|
|
9
|
+
*
|
|
10
|
+
* Flow:
|
|
11
|
+
* 1. Read the markdown file from disk.
|
|
12
|
+
* 2. parseSharedMarkdown → SharedBrainDoc (rules + lessons).
|
|
13
|
+
* 3. --dry-run: print the parsed structure and exit.
|
|
14
|
+
* 4. Otherwise: check that the target doc is empty (or --force),
|
|
15
|
+
* then apply the parsed content via applyBrainChange. This
|
|
16
|
+
* auto-signs + auto-publishes via the step 5 gossipsub layer
|
|
17
|
+
* so any live subscriber picks up the migration immediately.
|
|
18
|
+
* 5. Write a DEPRECATED banner to the top of the source file
|
|
19
|
+
* pointing agents at <docId>.generated.md as the new source
|
|
20
|
+
* of truth. Original content is preserved below the banner
|
|
21
|
+
* for historical reference — the banner is opt-in.
|
|
22
|
+
*
|
|
23
|
+
* Idempotence: running the command twice on a doc that already has
|
|
24
|
+
* content errors out unless --force is supplied. This protects
|
|
25
|
+
* against accidental clobbering during a fumbled migration.
|
|
26
|
+
*/
|
|
27
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
30
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
31
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
32
|
+
}
|
|
33
|
+
Object.defineProperty(o, k2, desc);
|
|
34
|
+
}) : (function(o, m, k, k2) {
|
|
35
|
+
if (k2 === undefined) k2 = k;
|
|
36
|
+
o[k2] = m[k];
|
|
37
|
+
}));
|
|
38
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
39
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
40
|
+
}) : function(o, v) {
|
|
41
|
+
o["default"] = v;
|
|
42
|
+
});
|
|
43
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
44
|
+
var ownKeys = function(o) {
|
|
45
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
46
|
+
var ar = [];
|
|
47
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
48
|
+
return ar;
|
|
49
|
+
};
|
|
50
|
+
return ownKeys(o);
|
|
51
|
+
};
|
|
52
|
+
return function (mod) {
|
|
53
|
+
if (mod && mod.__esModule) return mod;
|
|
54
|
+
var result = {};
|
|
55
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
56
|
+
__setModuleDefault(result, mod);
|
|
57
|
+
return result;
|
|
58
|
+
};
|
|
59
|
+
})();
|
|
60
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
61
|
+
exports.migrateHandler = void 0;
|
|
62
|
+
exports.computeMergeDelta = computeMergeDelta;
|
|
63
|
+
const fs_1 = require("fs");
|
|
64
|
+
const path_1 = require("path");
|
|
65
|
+
const brain_1 = require("../../lib/brain");
|
|
66
|
+
const brain_migrate_1 = require("../../lib/brain-migrate");
|
|
67
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
68
|
+
/**
|
|
69
|
+
* Banner injected at the top of the source hand-written file after a
|
|
70
|
+
* successful migration. Keeps the original content below so git
|
|
71
|
+
* history + human review are preserved; future hand-edits still
|
|
72
|
+
* survive but are flagged as out-of-sync with the CRDT.
|
|
73
|
+
*/
|
|
74
|
+
const DEPRECATED_BANNER = (docId, headCid) => `<!-- DEPRECATED: migrated to ${docId} at CID ${headCid} -->
|
|
75
|
+
<!-- Source of truth is now agent/brain/Knowledge/${docId}.generated.md -->
|
|
76
|
+
<!-- regenerated by \`pop brain snapshot --doc ${docId}\` at end of every heartbeat. -->
|
|
77
|
+
<!-- Edit via \`pop brain\` CLI; hand-edits here will NOT propagate. -->
|
|
78
|
+
|
|
79
|
+
`;
|
|
80
|
+
/**
|
|
81
|
+
* Pure helper: compute which parsed entries should be appended to an
|
|
82
|
+
* existing list vs skipped because an entry with the same `id` already
|
|
83
|
+
* exists. Extracted so the --merge dedup logic is unit-testable
|
|
84
|
+
* independent of the brain daemon / Automerge doc layer.
|
|
85
|
+
*
|
|
86
|
+
* Entries without an `id` are ALWAYS added (we can't dedup what has no
|
|
87
|
+
* key). Callers should ensure parsed entries have ids if they want
|
|
88
|
+
* dedup semantics — parseSharedMarkdown always assigns an id via
|
|
89
|
+
* slugify fallback, so in practice every parsed lesson has an id.
|
|
90
|
+
*/
|
|
91
|
+
function computeMergeDelta(existing, parsed) {
|
|
92
|
+
const existingIds = new Set(existing
|
|
93
|
+
.map((e) => e?.id)
|
|
94
|
+
.filter((id) => typeof id === 'string'));
|
|
95
|
+
const toAdd = [];
|
|
96
|
+
let skippedCount = 0;
|
|
97
|
+
for (const entry of parsed) {
|
|
98
|
+
if (entry.id && existingIds.has(entry.id)) {
|
|
99
|
+
skippedCount += 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
toAdd.push(entry);
|
|
103
|
+
}
|
|
104
|
+
return { toAdd, skippedCount };
|
|
105
|
+
}
|
|
106
|
+
exports.migrateHandler = {
|
|
107
|
+
builder: (yargs) => yargs
|
|
108
|
+
.option('from', {
|
|
109
|
+
describe: 'Path to the hand-written markdown file to migrate',
|
|
110
|
+
type: 'string',
|
|
111
|
+
demandOption: true,
|
|
112
|
+
})
|
|
113
|
+
.option('doc', {
|
|
114
|
+
describe: 'Target brain document ID (e.g. pop.brain.shared)',
|
|
115
|
+
type: 'string',
|
|
116
|
+
demandOption: true,
|
|
117
|
+
})
|
|
118
|
+
.option('force', {
|
|
119
|
+
describe: 'Re-migrate even if the target doc already has content (DESTRUCTIVE: clears existing lessons/rules before re-seeding)',
|
|
120
|
+
type: 'boolean',
|
|
121
|
+
default: false,
|
|
122
|
+
})
|
|
123
|
+
.option('merge', {
|
|
124
|
+
describe: 'Non-destructive append: migrate new entries into an existing doc, deduping by id (mutually exclusive with --force)',
|
|
125
|
+
type: 'boolean',
|
|
126
|
+
default: false,
|
|
127
|
+
})
|
|
128
|
+
.option('dry-run', {
|
|
129
|
+
describe: 'Parse and print the structured output without persisting',
|
|
130
|
+
type: 'boolean',
|
|
131
|
+
default: false,
|
|
132
|
+
})
|
|
133
|
+
.option('current-hb', {
|
|
134
|
+
describe: 'Current heartbeat number for HB#→timestamp estimation (default 270)',
|
|
135
|
+
type: 'number',
|
|
136
|
+
default: 270,
|
|
137
|
+
})
|
|
138
|
+
.option('author', {
|
|
139
|
+
describe: 'Default author for sections without explicit attribution',
|
|
140
|
+
type: 'string',
|
|
141
|
+
default: 'migration',
|
|
142
|
+
})
|
|
143
|
+
.option('no-banner', {
|
|
144
|
+
describe: 'Do NOT write a DEPRECATED banner to the source file after migration',
|
|
145
|
+
type: 'boolean',
|
|
146
|
+
default: false,
|
|
147
|
+
}),
|
|
148
|
+
handler: async (argv) => {
|
|
149
|
+
const srcPath = (0, path_1.resolve)(argv.from);
|
|
150
|
+
const docId = argv.doc;
|
|
151
|
+
try {
|
|
152
|
+
if (argv.force && argv.merge) {
|
|
153
|
+
output.error('--force and --merge are mutually exclusive. --force clears and re-seeds; ' +
|
|
154
|
+
'--merge appends dedup-by-id into existing content. Pick one.');
|
|
155
|
+
process.exitCode = 1;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!(0, fs_1.existsSync)(srcPath)) {
|
|
159
|
+
output.error(`Source file not found: ${srcPath}`);
|
|
160
|
+
process.exitCode = 1;
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const raw = (0, fs_1.readFileSync)(srcPath, 'utf8');
|
|
164
|
+
const now = Math.floor(Date.now() / 1000);
|
|
165
|
+
const currentHB = argv.currentHb ?? 270;
|
|
166
|
+
const ctx = {
|
|
167
|
+
defaultAuthor: argv.author ?? 'migration',
|
|
168
|
+
defaultTimestamp: now,
|
|
169
|
+
timestampForHB: (hb) => now - (currentHB - hb) * 15 * 60,
|
|
170
|
+
};
|
|
171
|
+
const parsed = (0, brain_migrate_1.parseSharedMarkdown)(raw, ctx);
|
|
172
|
+
if (argv.dryRun) {
|
|
173
|
+
if (output.isJsonMode()) {
|
|
174
|
+
output.json({ status: 'dry-run', docId, parsed });
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.log('');
|
|
178
|
+
console.log(` Dry-run migration: ${srcPath}`);
|
|
179
|
+
console.log(` Target doc: ${docId}`);
|
|
180
|
+
console.log(' ' + '─'.repeat(60));
|
|
181
|
+
console.log(` Rules: ${parsed.rules.length}`);
|
|
182
|
+
console.log(` Lessons: ${parsed.lessons.length}`);
|
|
183
|
+
console.log('');
|
|
184
|
+
console.log(' First 3 rules:');
|
|
185
|
+
for (const r of parsed.rules.slice(0, 3)) {
|
|
186
|
+
console.log(` - ${r.id}: ${(r.text ?? '').slice(0, 80).replace(/\n/g, ' ')}...`);
|
|
187
|
+
}
|
|
188
|
+
console.log(' First 3 lessons:');
|
|
189
|
+
for (const l of parsed.lessons.slice(0, 3)) {
|
|
190
|
+
console.log(` - ${l.id}: ${(l.title ?? '').slice(0, 80)}`);
|
|
191
|
+
}
|
|
192
|
+
console.log('');
|
|
193
|
+
console.log(' No changes persisted (--dry-run).');
|
|
194
|
+
console.log('');
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// Refuse to overwrite an existing doc unless --force or --merge.
|
|
199
|
+
const existing = await (0, brain_1.openBrainDoc)(docId);
|
|
200
|
+
const alreadyHasContent = existing.headCid !== null &&
|
|
201
|
+
existing.doc &&
|
|
202
|
+
(Object.keys(existing.doc).length > 0);
|
|
203
|
+
if (alreadyHasContent && !argv.force && !argv.merge) {
|
|
204
|
+
output.error(`Target doc "${docId}" already has content at head ${existing.headCid}. ` +
|
|
205
|
+
'Re-run with --merge to append new entries non-destructively (dedup by id), ' +
|
|
206
|
+
'or --force to clear-and-reseed (DESTRUCTIVE).');
|
|
207
|
+
process.exitCode = 1;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Merge-mode stats: set in the change callback, read after the apply.
|
|
211
|
+
let lessonsAdded = 0;
|
|
212
|
+
let lessonsSkipped = 0;
|
|
213
|
+
let rulesAdded = 0;
|
|
214
|
+
let rulesSkipped = 0;
|
|
215
|
+
// Apply the parsed state in a single Automerge change so the
|
|
216
|
+
// entire migration lands as one content-addressed snapshot.
|
|
217
|
+
const result = await (0, brain_1.applyBrainChange)(docId, (doc) => {
|
|
218
|
+
if (!doc.rules)
|
|
219
|
+
doc.rules = [];
|
|
220
|
+
if (!doc.lessons)
|
|
221
|
+
doc.lessons = [];
|
|
222
|
+
if (argv.force) {
|
|
223
|
+
// Force re-migration: clear existing and re-seed.
|
|
224
|
+
doc.rules.splice(0, doc.rules.length);
|
|
225
|
+
doc.lessons.splice(0, doc.lessons.length);
|
|
226
|
+
}
|
|
227
|
+
if (argv.merge) {
|
|
228
|
+
// Non-destructive append: dedup by id against existing entries.
|
|
229
|
+
const rulesDelta = computeMergeDelta(doc.rules, parsed.rules);
|
|
230
|
+
const lessonsDelta = computeMergeDelta(doc.lessons, parsed.lessons);
|
|
231
|
+
for (const r of rulesDelta.toAdd)
|
|
232
|
+
doc.rules.push({ ...r });
|
|
233
|
+
for (const l of lessonsDelta.toAdd)
|
|
234
|
+
doc.lessons.push({ ...l });
|
|
235
|
+
rulesAdded = rulesDelta.toAdd.length;
|
|
236
|
+
rulesSkipped = rulesDelta.skippedCount;
|
|
237
|
+
lessonsAdded = lessonsDelta.toAdd.length;
|
|
238
|
+
lessonsSkipped = lessonsDelta.skippedCount;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
// Legacy / --force path: append everything unconditionally.
|
|
242
|
+
for (const r of parsed.rules) {
|
|
243
|
+
doc.rules.push({ ...r });
|
|
244
|
+
rulesAdded += 1;
|
|
245
|
+
}
|
|
246
|
+
for (const l of parsed.lessons) {
|
|
247
|
+
doc.lessons.push({ ...l });
|
|
248
|
+
lessonsAdded += 1;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
// Write the deprecated banner to the source file unless
|
|
252
|
+
// explicitly suppressed. Keep the original content below.
|
|
253
|
+
if (!argv.noBanner) {
|
|
254
|
+
const banner = DEPRECATED_BANNER(docId, result.headCid);
|
|
255
|
+
if (!raw.includes('DEPRECATED: migrated to')) {
|
|
256
|
+
(0, fs_1.writeFileSync)(srcPath, banner + raw);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (output.isJsonMode()) {
|
|
260
|
+
output.json({
|
|
261
|
+
status: 'ok',
|
|
262
|
+
docId,
|
|
263
|
+
headCid: result.headCid,
|
|
264
|
+
mode: argv.merge ? 'merge' : argv.force ? 'force' : 'fresh',
|
|
265
|
+
rulesAdded,
|
|
266
|
+
rulesSkipped,
|
|
267
|
+
lessonsAdded,
|
|
268
|
+
lessonsSkipped,
|
|
269
|
+
// Back-compat keys — total parsed, not added.
|
|
270
|
+
rulesMigrated: parsed.rules.length,
|
|
271
|
+
lessonsMigrated: parsed.lessons.length,
|
|
272
|
+
sourceFile: srcPath,
|
|
273
|
+
bannerWritten: !argv.noBanner,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
console.log('');
|
|
278
|
+
console.log(` Migration complete: ${srcPath} → ${docId}`);
|
|
279
|
+
console.log(` Mode: ${argv.merge ? 'merge' : argv.force ? 'force' : 'fresh'}`);
|
|
280
|
+
console.log(` New head CID: ${result.headCid}`);
|
|
281
|
+
if (argv.merge) {
|
|
282
|
+
console.log(` Rules: +${rulesAdded} added, ${rulesSkipped} skipped (dup id)`);
|
|
283
|
+
console.log(` Lessons: +${lessonsAdded} added, ${lessonsSkipped} skipped (dup id)`);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
console.log(` Rules: ${rulesAdded}`);
|
|
287
|
+
console.log(` Lessons: ${lessonsAdded}`);
|
|
288
|
+
}
|
|
289
|
+
if (!argv.noBanner) {
|
|
290
|
+
console.log(` Banner written to source file.`);
|
|
291
|
+
}
|
|
292
|
+
console.log('');
|
|
293
|
+
console.log(` Run \`pop brain read --doc ${docId}\` to inspect.`);
|
|
294
|
+
console.log(` Run \`pop brain snapshot --doc ${docId}\` to produce the .generated.md.`);
|
|
295
|
+
console.log('');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
output.error(err.message);
|
|
300
|
+
process.exitCode = 1;
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
await (0, brain_1.stopBrainNode)();
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain new-project — create a project entry in pop.brain.projects.
|
|
3
|
+
*
|
|
4
|
+
* First write command for the pop.brain.projects doc shape introduced
|
|
5
|
+
* in #306. Thin wrapper over applyBrainChange that pushes a new
|
|
6
|
+
* BrainProject onto doc.projects and auto-publishes via the existing
|
|
7
|
+
* gossipsub hook. Validates stage against the ProjectStage union so
|
|
8
|
+
* typos get caught at the CLI boundary.
|
|
9
|
+
*
|
|
10
|
+
* Out of scope for this command (each is a separate follow-up):
|
|
11
|
+
* - advance-stage (advance a project to the next lifecycle stage)
|
|
12
|
+
* - post-discussion (append a discussion entry)
|
|
13
|
+
* - add-task-plan (attach a task plan block)
|
|
14
|
+
* - stage-transition validation (currently any stage is accepted as
|
|
15
|
+
* the starting stage; the PROPOSE → DISCUSS → PLAN order is a soft
|
|
16
|
+
* convention, not a hard constraint)
|
|
17
|
+
*/
|
|
18
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
19
|
+
import type { ProjectStage } from '../../lib/brain-projections';
|
|
20
|
+
interface NewProjectArgs {
|
|
21
|
+
doc: string;
|
|
22
|
+
name: string;
|
|
23
|
+
stage?: ProjectStage;
|
|
24
|
+
proposedBy?: string;
|
|
25
|
+
brief?: string;
|
|
26
|
+
briefFile?: string;
|
|
27
|
+
id?: string;
|
|
28
|
+
'idempotency-key'?: string;
|
|
29
|
+
'no-idempotency'?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare const newProjectHandler: {
|
|
32
|
+
builder: (yargs: Argv) => Argv<{
|
|
33
|
+
doc: string;
|
|
34
|
+
} & {
|
|
35
|
+
name: string;
|
|
36
|
+
} & {
|
|
37
|
+
stage: ProjectStage;
|
|
38
|
+
} & {
|
|
39
|
+
"proposed-by": string | undefined;
|
|
40
|
+
} & {
|
|
41
|
+
brief: string | undefined;
|
|
42
|
+
} & {
|
|
43
|
+
"brief-file": string | undefined;
|
|
44
|
+
} & {
|
|
45
|
+
id: string | undefined;
|
|
46
|
+
} & {
|
|
47
|
+
"idempotency-key": string | undefined;
|
|
48
|
+
} & {
|
|
49
|
+
"no-idempotency": boolean;
|
|
50
|
+
}>;
|
|
51
|
+
handler: (argv: ArgumentsCamelCase<NewProjectArgs>) => Promise<void>;
|
|
52
|
+
};
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain new-project — create a project entry in pop.brain.projects.
|
|
4
|
+
*
|
|
5
|
+
* First write command for the pop.brain.projects doc shape introduced
|
|
6
|
+
* in #306. Thin wrapper over applyBrainChange that pushes a new
|
|
7
|
+
* BrainProject onto doc.projects and auto-publishes via the existing
|
|
8
|
+
* gossipsub hook. Validates stage against the ProjectStage union so
|
|
9
|
+
* typos get caught at the CLI boundary.
|
|
10
|
+
*
|
|
11
|
+
* Out of scope for this command (each is a separate follow-up):
|
|
12
|
+
* - advance-stage (advance a project to the next lifecycle stage)
|
|
13
|
+
* - post-discussion (append a discussion entry)
|
|
14
|
+
* - add-task-plan (attach a task plan block)
|
|
15
|
+
* - stage-transition validation (currently any stage is accepted as
|
|
16
|
+
* the starting stage; the PROPOSE → DISCUSS → PLAN order is a soft
|
|
17
|
+
* convention, not a hard constraint)
|
|
18
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.newProjectHandler = void 0;
|
|
54
|
+
const fs_1 = require("fs");
|
|
55
|
+
const path_1 = require("path");
|
|
56
|
+
const ethers_1 = require("ethers");
|
|
57
|
+
const brain_1 = require("../../lib/brain");
|
|
58
|
+
const brain_ops_1 = require("../../lib/brain-ops");
|
|
59
|
+
const idempotency_1 = require("@poa-box/cli/lib/idempotency");
|
|
60
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
61
|
+
const VALID_STAGES = [
|
|
62
|
+
'propose', 'discuss', 'plan', 'vote', 'execute', 'review', 'ship',
|
|
63
|
+
];
|
|
64
|
+
function slugify(s) {
|
|
65
|
+
return s
|
|
66
|
+
.toLowerCase()
|
|
67
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
68
|
+
.replace(/^-+|-+$/g, '')
|
|
69
|
+
.slice(0, 60);
|
|
70
|
+
}
|
|
71
|
+
exports.newProjectHandler = {
|
|
72
|
+
builder: (yargs) => yargs
|
|
73
|
+
.option('doc', {
|
|
74
|
+
describe: 'Target brain document ID (typically pop.brain.projects)',
|
|
75
|
+
type: 'string',
|
|
76
|
+
demandOption: true,
|
|
77
|
+
})
|
|
78
|
+
.option('name', {
|
|
79
|
+
describe: 'Project name (used as the markdown header + default id base)',
|
|
80
|
+
type: 'string',
|
|
81
|
+
demandOption: true,
|
|
82
|
+
})
|
|
83
|
+
.option('stage', {
|
|
84
|
+
describe: 'Starting lifecycle stage',
|
|
85
|
+
type: 'string',
|
|
86
|
+
choices: VALID_STAGES,
|
|
87
|
+
default: 'propose',
|
|
88
|
+
})
|
|
89
|
+
.option('proposed-by', {
|
|
90
|
+
describe: 'Override the proposer label (default: signing wallet address)',
|
|
91
|
+
type: 'string',
|
|
92
|
+
})
|
|
93
|
+
.option('brief', {
|
|
94
|
+
describe: 'Inline project brief (short form)',
|
|
95
|
+
type: 'string',
|
|
96
|
+
})
|
|
97
|
+
.option('brief-file', {
|
|
98
|
+
describe: 'Path to a file whose contents are used as the brief',
|
|
99
|
+
type: 'string',
|
|
100
|
+
})
|
|
101
|
+
.option('id', {
|
|
102
|
+
describe: 'Override the auto-generated project id',
|
|
103
|
+
type: 'string',
|
|
104
|
+
})
|
|
105
|
+
.option('idempotency-key', {
|
|
106
|
+
type: 'string',
|
|
107
|
+
describe: 'Task #375 (HB#217) idempotency cache. Agent-scoped.',
|
|
108
|
+
})
|
|
109
|
+
.option('no-idempotency', {
|
|
110
|
+
type: 'boolean',
|
|
111
|
+
default: false,
|
|
112
|
+
describe: 'Bypass the idempotency cache.',
|
|
113
|
+
}),
|
|
114
|
+
handler: async (argv) => {
|
|
115
|
+
try {
|
|
116
|
+
// Resolve brief content.
|
|
117
|
+
let brief;
|
|
118
|
+
if (argv.briefFile) {
|
|
119
|
+
const p = (0, path_1.resolve)(argv.briefFile);
|
|
120
|
+
if (!(0, fs_1.existsSync)(p)) {
|
|
121
|
+
output.error(`--brief-file not found: ${p}`);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
brief = (0, fs_1.readFileSync)(p, 'utf8').replace(/\s+$/, '');
|
|
126
|
+
}
|
|
127
|
+
else if (argv.brief !== undefined) {
|
|
128
|
+
brief = argv.brief.trim();
|
|
129
|
+
}
|
|
130
|
+
// Resolve proposedBy label up front — matches the append-lesson
|
|
131
|
+
// pattern. Default: lowercased wallet address from POP_PRIVATE_KEY.
|
|
132
|
+
let proposedBy;
|
|
133
|
+
if (argv.proposedBy) {
|
|
134
|
+
proposedBy = argv.proposedBy;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const key = process.env.POP_PRIVATE_KEY;
|
|
138
|
+
if (!key) {
|
|
139
|
+
output.error('POP_PRIVATE_KEY not set — cannot derive default proposedBy. Pass --proposed-by explicitly.');
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
proposedBy = new ethers_1.ethers.Wallet(key).address.toLowerCase();
|
|
144
|
+
}
|
|
145
|
+
// Compute the id. Default is slugify(name); on collision with
|
|
146
|
+
// an existing project, append a unix-seconds suffix so a second
|
|
147
|
+
// caller with the same --name still succeeds.
|
|
148
|
+
const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
|
|
149
|
+
const existing = Array.isArray(currentDoc?.projects) ? currentDoc.projects : [];
|
|
150
|
+
const baseId = argv.id ?? (slugify(argv.name) || 'project');
|
|
151
|
+
let id = baseId;
|
|
152
|
+
if (existing.some(p => p?.id === id)) {
|
|
153
|
+
id = `${baseId}-${Math.floor(Date.now() / 1000)}`;
|
|
154
|
+
}
|
|
155
|
+
const now = Math.floor(Date.now() / 1000);
|
|
156
|
+
const stage = (argv.stage ?? 'propose');
|
|
157
|
+
// Task #375 (HB#217): idempotency check, agent-scoped
|
|
158
|
+
const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
|
|
159
|
+
if (!argv.noIdempotency) {
|
|
160
|
+
const cached = (0, idempotency_1.checkIdempotencyCache)(proposedBy, 'brain.newProject', idempKey);
|
|
161
|
+
if (cached) {
|
|
162
|
+
if (output.isJsonMode()) {
|
|
163
|
+
output.json({ status: 'ok', cached: true, ...cached });
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log(` Project already created (idempotency cache hit)`);
|
|
168
|
+
console.log(` id: ${cached.projectId}`);
|
|
169
|
+
console.log(` head: ${cached.headCid}`);
|
|
170
|
+
console.log('');
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Route through the unified dispatcher (HB#324 ship-2).
|
|
176
|
+
const result = await (0, brain_ops_1.routedDispatch)({
|
|
177
|
+
type: 'newProject',
|
|
178
|
+
docId: argv.doc,
|
|
179
|
+
projectId: id,
|
|
180
|
+
name: argv.name,
|
|
181
|
+
brief,
|
|
182
|
+
stage,
|
|
183
|
+
proposedBy,
|
|
184
|
+
proposedAt: now,
|
|
185
|
+
});
|
|
186
|
+
if (!argv.noIdempotency) {
|
|
187
|
+
(0, idempotency_1.recordIdempotentResult)(proposedBy, 'brain.newProject', idempKey, {
|
|
188
|
+
docId: argv.doc,
|
|
189
|
+
projectId: id,
|
|
190
|
+
headCid: result.headCid,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (output.isJsonMode()) {
|
|
194
|
+
output.json({
|
|
195
|
+
status: 'ok',
|
|
196
|
+
docId: argv.doc,
|
|
197
|
+
projectId: id,
|
|
198
|
+
name: argv.name,
|
|
199
|
+
stage,
|
|
200
|
+
proposedBy,
|
|
201
|
+
headCid: result.headCid,
|
|
202
|
+
envelopeAuthor: result.envelopeAuthor,
|
|
203
|
+
routedViaDaemon: result.routedViaDaemon,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log(` Project created in ${argv.doc}`);
|
|
209
|
+
console.log(` id: ${id}`);
|
|
210
|
+
console.log(` name: ${argv.name}`);
|
|
211
|
+
console.log(` stage: ${stage}`);
|
|
212
|
+
console.log(` proposedBy: ${proposedBy}`);
|
|
213
|
+
console.log(` head: ${result.headCid}`);
|
|
214
|
+
console.log(` routed: ${result.routedViaDaemon ? 'via brain daemon' : 'in-process (no daemon)'}`);
|
|
215
|
+
console.log('');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
output.error(err.message);
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
await (0, brain_1.stopBrainNode)();
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain read — project a brain doc to JSON (or human-readable text).
|
|
3
|
+
*
|
|
4
|
+
* This is the read side of the brain CRDT substrate. Loads the Automerge
|
|
5
|
+
* doc identified by the current head CID in the manifest, verifies the
|
|
6
|
+
* signed envelope, and prints the current state. Useful for:
|
|
7
|
+
*
|
|
8
|
+
* - Verifying that a subscriber successfully synced a remote write
|
|
9
|
+
* (step 6 acceptance)
|
|
10
|
+
* - Inspecting the state of any local brain doc from the shell
|
|
11
|
+
* - Feeding the projection layer (step 7) when it replaces the
|
|
12
|
+
* hand-written shared.md / projects.md files
|
|
13
|
+
*/
|
|
14
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
15
|
+
interface ReadArgs {
|
|
16
|
+
doc: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const readHandler: {
|
|
19
|
+
builder: (yargs: Argv) => Argv<{
|
|
20
|
+
doc: string;
|
|
21
|
+
}>;
|
|
22
|
+
handler: (argv: ArgumentsCamelCase<ReadArgs>) => Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain read — project a brain doc to JSON (or human-readable text).
|
|
4
|
+
*
|
|
5
|
+
* This is the read side of the brain CRDT substrate. Loads the Automerge
|
|
6
|
+
* doc identified by the current head CID in the manifest, verifies the
|
|
7
|
+
* signed envelope, and prints the current state. Useful for:
|
|
8
|
+
*
|
|
9
|
+
* - Verifying that a subscriber successfully synced a remote write
|
|
10
|
+
* (step 6 acceptance)
|
|
11
|
+
* - Inspecting the state of any local brain doc from the shell
|
|
12
|
+
* - Feeding the projection layer (step 7) when it replaces the
|
|
13
|
+
* hand-written shared.md / projects.md files
|
|
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.readHandler = void 0;
|
|
50
|
+
const brain_1 = require("../../lib/brain");
|
|
51
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
52
|
+
exports.readHandler = {
|
|
53
|
+
builder: (yargs) => yargs.option('doc', {
|
|
54
|
+
describe: 'Brain document ID (e.g. pop.brain.shared)',
|
|
55
|
+
type: 'string',
|
|
56
|
+
demandOption: true,
|
|
57
|
+
}),
|
|
58
|
+
handler: async (argv) => {
|
|
59
|
+
try {
|
|
60
|
+
const { doc, headCid } = await (0, brain_1.readBrainDoc)(argv.doc);
|
|
61
|
+
if (output.isJsonMode()) {
|
|
62
|
+
output.json({ docId: argv.doc, headCid, doc });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(` Brain doc: ${argv.doc}`);
|
|
67
|
+
console.log(` Head CID: ${headCid ?? '(none — empty doc)'}`);
|
|
68
|
+
console.log(' ' + '─'.repeat(60));
|
|
69
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
70
|
+
console.log('');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
output.error(err.message);
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
await (0, brain_1.stopBrainNode)();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|