@agfpd/iapeer-memory 0.1.3 → 0.1.4
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/package.json +2 -2
- package/src/commands/init.ts +44 -14
- package/src/templates/index.ts +32 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agfpd/iapeer-memory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "iapeer-memory — peer memory for the iapeer ecosystem: vault, memoryd (index/search/MCP-http), layer-5 context fragments, role doctrines. The package IS the system; the claude/codex plugins are thin session sockets (docs/10-distribution.md, ADR-009).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@agfpd/iapeer-memory-core": "0.1.
|
|
30
|
+
"@agfpd/iapeer-memory-core": "0.1.4"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/bun": "^1.2.0",
|
package/src/commands/init.ts
CHANGED
|
@@ -35,8 +35,10 @@ import { provisionVault, writeDefaultConfig } from "../provision.js";
|
|
|
35
35
|
import { writeRolesManifest, type RoleEntry } from "../roles.js";
|
|
36
36
|
import { writeSlot } from "../slot.js";
|
|
37
37
|
import {
|
|
38
|
+
doctrineOwnership,
|
|
38
39
|
guideText,
|
|
39
40
|
materialiseTemplates,
|
|
41
|
+
rolePersonality,
|
|
40
42
|
roleTemplatePath,
|
|
41
43
|
ROLE_NAMES,
|
|
42
44
|
} from "../templates/index.js";
|
|
@@ -61,6 +63,10 @@ type InitFlags = {
|
|
|
61
63
|
skipDeps: boolean;
|
|
62
64
|
skipEcosystem: boolean;
|
|
63
65
|
skipBinary: boolean;
|
|
66
|
+
/** Skip the host-wide guide fragment (staged fleet rollout — cutover 10.06:
|
|
67
|
+
* the fleet still carries the MergeMind guide; ours lands by a separate
|
|
68
|
+
* decision after the plugin swap). */
|
|
69
|
+
skipGuide: boolean;
|
|
64
70
|
iapeerBin: string;
|
|
65
71
|
};
|
|
66
72
|
|
|
@@ -70,6 +76,7 @@ function parseFlags(argv: string[]): InitFlags | null {
|
|
|
70
76
|
skipDeps: false,
|
|
71
77
|
skipEcosystem: false,
|
|
72
78
|
skipBinary: false,
|
|
79
|
+
skipGuide: false,
|
|
73
80
|
iapeerBin: "iapeer",
|
|
74
81
|
};
|
|
75
82
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -85,6 +92,7 @@ function parseFlags(argv: string[]): InitFlags | null {
|
|
|
85
92
|
case "--skip-deps": f.skipDeps = true; break;
|
|
86
93
|
case "--skip-ecosystem": f.skipEcosystem = true; break;
|
|
87
94
|
case "--skip-binary": f.skipBinary = true; break;
|
|
95
|
+
case "--skip-guide": f.skipGuide = true; break;
|
|
88
96
|
case "--iapeer-bin": f.iapeerBin = take() ?? "iapeer"; break;
|
|
89
97
|
default:
|
|
90
98
|
console.error(`iapeer-memory init: unknown flag: ${a}`);
|
|
@@ -263,7 +271,9 @@ export async function cmdInit(argv: string[]): Promise<number> {
|
|
|
263
271
|
const tmpl = materialiseTemplates({ templatesDir: paths.templatesDir, locale });
|
|
264
272
|
step("templates", `${paths.templatesDir} (${tmpl.written.length} written, ${tmpl.identical.length} identical)`);
|
|
265
273
|
|
|
266
|
-
// 6. role peers + doctrines + manifest
|
|
274
|
+
// 6. role peers + doctrines + manifest. Personalities are namespaced
|
|
275
|
+
// memory-<role> (collision-proof by design); the manifest keeps the
|
|
276
|
+
// CONCEPTUAL role keys.
|
|
267
277
|
if (flags.skipEcosystem) {
|
|
268
278
|
step("roles", "skipped (--skip-ecosystem)");
|
|
269
279
|
} else {
|
|
@@ -271,26 +281,42 @@ export async function cmdInit(argv: string[]): Promise<number> {
|
|
|
271
281
|
let rolesOk = true;
|
|
272
282
|
let createdAny = false;
|
|
273
283
|
for (const role of ROLE_NAMES) {
|
|
274
|
-
const
|
|
284
|
+
const personality = rolePersonality(role);
|
|
285
|
+
const exists = (peers ?? []).some((p) => p.personality === personality);
|
|
275
286
|
if (!exists) {
|
|
276
|
-
const created = run([flags.iapeerBin, "create",
|
|
287
|
+
const created = run([flags.iapeerBin, "create", personality]);
|
|
277
288
|
if (created.exitCode !== 0) {
|
|
278
289
|
rolesOk = false;
|
|
279
|
-
console.log(` roles create ${
|
|
290
|
+
console.log(` roles create ${personality} failed: ${created.stderr.trim()}`);
|
|
280
291
|
continue;
|
|
281
292
|
}
|
|
282
293
|
createdAny = true;
|
|
283
294
|
}
|
|
284
295
|
}
|
|
285
296
|
// peerCwd: the registry FACT when the core exposes it (`cwd` in
|
|
286
|
-
// `iapeer list --json` — iapeer
|
|
287
|
-
//
|
|
288
|
-
// IAPEER_ROOT-aware).
|
|
289
|
-
// automatically once the deployed core ships the field.
|
|
297
|
+
// `iapeer list --json` — iapeer 0.2.14), otherwise the core's
|
|
298
|
+
// DOCUMENTED create default (no --path — требование Артура;
|
|
299
|
+
// IAPEER_ROOT-aware).
|
|
290
300
|
const freshPeers = createdAny ? listPeers(flags.iapeerBin) : peers;
|
|
291
301
|
for (const role of ROLE_NAMES) {
|
|
292
|
-
const
|
|
293
|
-
const
|
|
302
|
+
const personality = rolePersonality(role);
|
|
303
|
+
const registryCwd = (freshPeers ?? []).find((p) => p.personality === personality)?.cwd;
|
|
304
|
+
const peerCwd = registryCwd || path.join(iapeerDir, "peers", personality);
|
|
305
|
+
// COLLISION GUARD (прецедент 10.06: «index» был занят живым
|
|
306
|
+
// MergeMind-Индексом; бренд-имена ролей — решение Артура, защита
|
|
307
|
+
// целиком здесь): a pre-existing peer whose doctrine is NOT ours is
|
|
308
|
+
// somebody else's — rendering over it would hijack a live peer.
|
|
309
|
+
// FAIL loud with a recipe, never render.
|
|
310
|
+
if (doctrineOwnership(peerCwd) === "foreign") {
|
|
311
|
+
rolesOk = false;
|
|
312
|
+
console.log(
|
|
313
|
+
` roles COLLISION: peer "${personality}" exists with a foreign doctrine ` +
|
|
314
|
+
`(${path.join(peerCwd, ".iapeer", "IAPEER.md")}) — not touching it. Recipe: ` +
|
|
315
|
+
`rename/remove that peer (iapeer stop ${personality} && iapeer remove ${personality}) ` +
|
|
316
|
+
`or move its cwd, then re-run init`,
|
|
317
|
+
);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
294
320
|
const template = roleTemplatePath(paths.templatesDir, locale, role);
|
|
295
321
|
const rendered = renderDoctrine({ templatePath: template, peerCwd, version });
|
|
296
322
|
if (rendered.action === "missing-template") {
|
|
@@ -308,8 +334,8 @@ export async function cmdInit(argv: string[]): Promise<number> {
|
|
|
308
334
|
const wakePolicy = cwEntry ? patchWakePolicyEphemeral(cwEntry.peerCwd) : "missing-profile";
|
|
309
335
|
step(
|
|
310
336
|
"roles",
|
|
311
|
-
`${roleEntries.map((r) => r.role).join(", ")}
|
|
312
|
-
`copywriter wake_policy: ${wakePolicy}, manifest ${paths.rolesManifestPath})`,
|
|
337
|
+
`${roleEntries.map((r) => rolePersonality(r.role as (typeof ROLE_NAMES)[number])).join(", ")} ` +
|
|
338
|
+
`(doctrines v${version}, copywriter wake_policy: ${wakePolicy}, manifest ${paths.rolesManifestPath})`,
|
|
313
339
|
rolesOk && roleEntries.length === ROLE_NAMES.length && wakePolicy !== "missing-profile",
|
|
314
340
|
);
|
|
315
341
|
}
|
|
@@ -398,8 +424,12 @@ export async function cmdInit(argv: string[]): Promise<number> {
|
|
|
398
424
|
}
|
|
399
425
|
|
|
400
426
|
// 10. host-wide guide fragment (layer 5 — reaches every peer on next wakes)
|
|
401
|
-
|
|
402
|
-
|
|
427
|
+
if (flags.skipGuide) {
|
|
428
|
+
step("guide", "skipped (--skip-guide) — roll out by a separate decision after the fleet plugin swap");
|
|
429
|
+
} else {
|
|
430
|
+
const guidePath = writeHostWideGuideFragment(iapeerDir, guideText(locale));
|
|
431
|
+
step("guide", guidePath);
|
|
432
|
+
}
|
|
403
433
|
|
|
404
434
|
console.log(
|
|
405
435
|
failures
|
package/src/templates/index.ts
CHANGED
|
@@ -30,6 +30,38 @@ import {
|
|
|
30
30
|
export const ROLE_NAMES = ["index", "copywriter", "dreamweaver"] as const;
|
|
31
31
|
export type RoleName = (typeof ROLE_NAMES)[number];
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Peer PERSONALITY of a role — the BRAND names, identical to the conceptual
|
|
35
|
+
* role names (решение Артура 10.06: «index должен остаться с таким же
|
|
36
|
+
* именем — это бренд, не memory-index»; the `memory-*` namespace variant
|
|
37
|
+
* was built and rolled back the same day). Collision safety therefore rests
|
|
38
|
+
* ENTIRELY on the init roles-step GUARD: a pre-existing peer with a foreign
|
|
39
|
+
* doctrine fails loud with a recipe, never gets rendered over (прецедент:
|
|
40
|
+
* «index» был занят живым MergeMind-Индексом до cutover'а). The single
|
|
41
|
+
* mapping point is kept so a future namespace decision is one line.
|
|
42
|
+
*/
|
|
43
|
+
export function rolePersonality(role: RoleName): string {
|
|
44
|
+
return role;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Who owns a peer's rendered doctrine — the init COLLISION GUARD's eye.
|
|
49
|
+
* "ours" = carries the ADR-010 marker; "foreign" = somebody else's live
|
|
50
|
+
* doctrine (rendering over it would hijack the peer — FAIL loud);
|
|
51
|
+
* "none" = bare peer, ours to render.
|
|
52
|
+
*/
|
|
53
|
+
export function doctrineOwnership(peerCwd: string): "ours" | "foreign" | "none" {
|
|
54
|
+
try {
|
|
55
|
+
const current = fs.readFileSync(
|
|
56
|
+
path.join(peerCwd, ".iapeer", "IAPEER.md"),
|
|
57
|
+
"utf-8",
|
|
58
|
+
);
|
|
59
|
+
return current.includes("<!-- iapeer-memory doctrine") ? "ours" : "foreign";
|
|
60
|
+
} catch {
|
|
61
|
+
return "none";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
33
65
|
const ROLES: Record<LocaleId, Record<RoleName, string>> = {
|
|
34
66
|
en: {
|
|
35
67
|
index: INDEX_DOCTRINE_EN,
|