@xrkseek/harness 0.6.7 → 0.6.9

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 (98) hide show
  1. package/node_modules/@xrkseek/preset-harness/dist/preset.d.ts.map +1 -1
  2. package/node_modules/@xrkseek/preset-harness/dist/preset.js +10 -2
  3. package/node_modules/@xrkseek/preset-harness/dist/preset.js.map +1 -1
  4. package/node_modules/@xrkseek/preset-harness/dist/tsconfig.tsbuildinfo +1 -1
  5. package/node_modules/@xrkseek/preset-minimal/dist/tsconfig.tsbuildinfo +1 -1
  6. package/node_modules/@xrkseek/protocol/dist/content.d.ts.map +1 -1
  7. package/node_modules/@xrkseek/protocol/dist/content.js +5 -1
  8. package/node_modules/@xrkseek/protocol/dist/content.js.map +1 -1
  9. package/node_modules/@xrkseek/server-face/dist/agent-member-card.d.ts.map +1 -1
  10. package/node_modules/@xrkseek/server-face/dist/agent-member-card.js +14 -1
  11. package/node_modules/@xrkseek/server-face/dist/agent-member-card.js.map +1 -1
  12. package/node_modules/@xrkseek/server-face/dist/agent-member-pack.d.ts +160 -0
  13. package/node_modules/@xrkseek/server-face/dist/agent-member-pack.d.ts.map +1 -0
  14. package/node_modules/@xrkseek/server-face/dist/agent-member-pack.js +684 -0
  15. package/node_modules/@xrkseek/server-face/dist/agent-member-pack.js.map +1 -0
  16. package/node_modules/@xrkseek/server-face/dist/agent-roster-store.d.ts +44 -2
  17. package/node_modules/@xrkseek/server-face/dist/agent-roster-store.d.ts.map +1 -1
  18. package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js +122 -2
  19. package/node_modules/@xrkseek/server-face/dist/agent-roster-store.js.map +1 -1
  20. package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.d.ts +7 -5
  21. package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.d.ts.map +1 -1
  22. package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.js +831 -64
  23. package/node_modules/@xrkseek/server-face/dist/agent-roster-tools.js.map +1 -1
  24. package/node_modules/@xrkseek/server-face/dist/agent-team-roles.d.ts +9 -0
  25. package/node_modules/@xrkseek/server-face/dist/agent-team-roles.d.ts.map +1 -1
  26. package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js +25 -2
  27. package/node_modules/@xrkseek/server-face/dist/agent-team-roles.js.map +1 -1
  28. package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.d.ts +4 -0
  29. package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.d.ts.map +1 -1
  30. package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.js +28 -7
  31. package/node_modules/@xrkseek/server-face/dist/capture-member-playbook.js.map +1 -1
  32. package/node_modules/@xrkseek/server-face/dist/dispatch.d.ts.map +1 -1
  33. package/node_modules/@xrkseek/server-face/dist/dispatch.js +5 -1
  34. package/node_modules/@xrkseek/server-face/dist/dispatch.js.map +1 -1
  35. package/node_modules/@xrkseek/server-face/dist/handlers/team.d.ts +4 -0
  36. package/node_modules/@xrkseek/server-face/dist/handlers/team.d.ts.map +1 -1
  37. package/node_modules/@xrkseek/server-face/dist/handlers/team.js +223 -3
  38. package/node_modules/@xrkseek/server-face/dist/handlers/team.js.map +1 -1
  39. package/node_modules/@xrkseek/server-face/dist/index.d.ts +2 -2
  40. package/node_modules/@xrkseek/server-face/dist/index.d.ts.map +1 -1
  41. package/node_modules/@xrkseek/server-face/dist/index.js +2 -2
  42. package/node_modules/@xrkseek/server-face/dist/index.js.map +1 -1
  43. package/node_modules/@xrkseek/server-face/dist/runtime.d.ts.map +1 -1
  44. package/node_modules/@xrkseek/server-face/dist/runtime.js +7 -2
  45. package/node_modules/@xrkseek/server-face/dist/runtime.js.map +1 -1
  46. package/node_modules/@xrkseek/server-face/dist/session-capability.d.ts.map +1 -1
  47. package/node_modules/@xrkseek/server-face/dist/session-capability.js +30 -2
  48. package/node_modules/@xrkseek/server-face/dist/session-capability.js.map +1 -1
  49. package/node_modules/@xrkseek/server-face/dist/session-posture.d.ts.map +1 -1
  50. package/node_modules/@xrkseek/server-face/dist/session-posture.js +3 -1
  51. package/node_modules/@xrkseek/server-face/dist/session-posture.js.map +1 -1
  52. package/node_modules/@xrkseek/server-face/dist/session-tool-copy.d.ts +9 -0
  53. package/node_modules/@xrkseek/server-face/dist/session-tool-copy.d.ts.map +1 -1
  54. package/node_modules/@xrkseek/server-face/dist/session-tool-copy.js +18 -2
  55. package/node_modules/@xrkseek/server-face/dist/session-tool-copy.js.map +1 -1
  56. package/node_modules/@xrkseek/server-face/dist/session-tools.d.ts.map +1 -1
  57. package/node_modules/@xrkseek/server-face/dist/session-tools.js +5 -0
  58. package/node_modules/@xrkseek/server-face/dist/session-tools.js.map +1 -1
  59. package/node_modules/@xrkseek/server-face/dist/subagent-tools.d.ts.map +1 -1
  60. package/node_modules/@xrkseek/server-face/dist/subagent-tools.js +46 -3
  61. package/node_modules/@xrkseek/server-face/dist/subagent-tools.js.map +1 -1
  62. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.d.ts +1 -1
  63. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.d.ts.map +1 -1
  64. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.js +25 -7
  65. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/demand-translate.js.map +1 -1
  66. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.d.ts +1 -1
  67. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.d.ts.map +1 -1
  68. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.js +18 -5
  69. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/home-config.js.map +1 -1
  70. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/index.d.ts +1 -1
  71. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/index.js +1 -1
  72. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.d.ts +1 -1
  73. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.d.ts.map +1 -1
  74. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.js +12 -7
  75. package/node_modules/@xrkseek/server-face/dist/xiangyang-skill-bodies/skill-merge.js.map +1 -1
  76. package/node_modules/@xrkseek/server-face/dist/zip-store.d.ts +5 -3
  77. package/node_modules/@xrkseek/server-face/dist/zip-store.d.ts.map +1 -1
  78. package/node_modules/@xrkseek/server-face/dist/zip-store.js +60 -1
  79. package/node_modules/@xrkseek/server-face/dist/zip-store.js.map +1 -1
  80. package/node_modules/@xrkseek/server-host/dist/index.d.ts.map +1 -1
  81. package/node_modules/@xrkseek/server-host/dist/index.js +31 -24
  82. package/node_modules/@xrkseek/server-host/dist/index.js.map +1 -1
  83. package/node_modules/@xrkseek/workspace/dist/index.d.ts +8 -0
  84. package/node_modules/@xrkseek/workspace/dist/index.d.ts.map +1 -1
  85. package/node_modules/@xrkseek/workspace/dist/index.js +17 -5
  86. package/node_modules/@xrkseek/workspace/dist/index.js.map +1 -1
  87. package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.d.ts +1 -0
  88. package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.d.ts.map +1 -1
  89. package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.js +4 -1
  90. package/node_modules/@xrkseek/workspace/dist/inject-fingerprint.js.map +1 -1
  91. package/node_modules/@xrkseek/workspace/dist/skill-dirs.d.ts +2 -0
  92. package/node_modules/@xrkseek/workspace/dist/skill-dirs.d.ts.map +1 -1
  93. package/node_modules/@xrkseek/workspace/dist/skill-dirs.js +24 -21
  94. package/node_modules/@xrkseek/workspace/dist/skill-dirs.js.map +1 -1
  95. package/node_modules/@xrkseek/workspace/dist/skills.d.ts +2 -0
  96. package/node_modules/@xrkseek/workspace/dist/skills.d.ts.map +1 -1
  97. package/node_modules/@xrkseek/workspace/dist/skills.js.map +1 -1
  98. package/package.json +1 -1
@@ -0,0 +1,684 @@
1
+ /**
2
+ * Portable advanced 干员 pack (公文包): playbook + optional skills + env prep +
3
+ * workspace materials. Thin cards stay on `xrk-agent-member` JSON in the roster.
4
+ *
5
+ * Layout: see {@link AGENT_MEMBER_PACK_LAYOUT}. Import rejects unknown root
6
+ * names, path traversal, and skills/<id>/ without SKILL.md.
7
+ *
8
+ * Session cwd stays the parent workspace; pack_home is injected as 公文包.
9
+ * Never pack curated memory (`scope=agent`) or appearance — those stay local.
10
+ */
11
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
12
+ import path from "node:path";
13
+ import { AGENT_MEMBER_CARD_KIND, parseAgentMemberCard, } from "./agent-member-card.js";
14
+ import { MEMBER_PLAYBOOK_MAX, memberPackDir, memberPackWorkspaceDir, } from "./agent-roster-store.js";
15
+ import { buildStoredZip, readZipEntries, zipEntryName } from "./zip-store.js";
16
+ export const AGENT_MEMBER_PACK_KIND = "xrk-agent-pack";
17
+ export const AGENT_MEMBER_PACK_VERSION = 1;
18
+ /** Required root marker — zip/dir without it is rejected. */
19
+ export const AGENT_MEMBER_PACK_MARKER = ".xrk-agent-pack";
20
+ /** Model-facing bilingual name for the advanced pack home (not session cwd). */
21
+ export const MEMBER_PACK_BRIEFCASE = "公文包 (briefcase)";
22
+ /** One-line layout contract (tools, errors, skills). */
23
+ export const AGENT_MEMBER_PACK_LAYOUT = `Required: .xrk-agent-pack · manifest.json (kind xrk-agent-pack) · playbook.md. ` +
24
+ `Optional: skills/<id>/ (+SKILL.md, references/…) · workspace/ (${MEMBER_PACK_BRIEFCASE} materials) · env/SETUP.md. ` +
25
+ `Session cwd = parent workspace; ${MEMBER_PACK_BRIEFCASE} paths are absolute at runtime — share zip with relative paths only.`;
26
+ /** Allowed single-segment root file names (besides dirs below). */
27
+ const PACK_ROOT_FILES = new Set([
28
+ AGENT_MEMBER_PACK_MARKER,
29
+ "manifest.json",
30
+ "member.json",
31
+ "card.json",
32
+ "playbook.md",
33
+ "PLAYBOOK.md",
34
+ "SETUP.md",
35
+ ]);
36
+ /** Allowed top-level directories. Anything else at root → illegal pack. */
37
+ const PACK_ROOT_DIRS = new Set(["skills", "workspace", "env"]);
38
+ function asRecord(value) {
39
+ if (!value || typeof value !== "object" || Array.isArray(value))
40
+ return undefined;
41
+ return value;
42
+ }
43
+ function textOf(files, rel) {
44
+ const buf = files.get(zipEntryName(rel));
45
+ if (!buf)
46
+ return undefined;
47
+ return buf.toString("utf8");
48
+ }
49
+ function listSkillDirs(files) {
50
+ const names = new Set();
51
+ for (const key of files.keys()) {
52
+ const m = /^skills\/([^/]+)\/SKILL\.md$/i.exec(key);
53
+ if (m?.[1])
54
+ names.add(m[1]);
55
+ }
56
+ return [...names].sort();
57
+ }
58
+ function markerPayload() {
59
+ return `${JSON.stringify({
60
+ kind: AGENT_MEMBER_PACK_KIND,
61
+ version: AGENT_MEMBER_PACK_VERSION,
62
+ })}\n`;
63
+ }
64
+ function hasPackMarker(files) {
65
+ const raw = textOf(files, AGENT_MEMBER_PACK_MARKER)?.trim();
66
+ if (!raw)
67
+ return false;
68
+ try {
69
+ const row = asRecord(JSON.parse(raw));
70
+ return row?.kind === AGENT_MEMBER_PACK_KIND;
71
+ }
72
+ catch {
73
+ // Allow a bare token file for hand-authored packs.
74
+ return raw === AGENT_MEMBER_PACK_KIND || raw.includes(AGENT_MEMBER_PACK_KIND);
75
+ }
76
+ }
77
+ /** Reject path traversal, absolute entries, and non-standard root names. */
78
+ export function validatePackLayout(files) {
79
+ const skillIds = new Set();
80
+ const skillHasMd = new Set();
81
+ for (const key of files.keys()) {
82
+ const name = zipEntryName(key);
83
+ if (!name)
84
+ continue;
85
+ if (name.includes("..") ||
86
+ name.startsWith("/") ||
87
+ name.startsWith("\\") ||
88
+ /^[a-zA-Z]:/.test(name)) {
89
+ return `illegal pack path: ${name}`;
90
+ }
91
+ const parts = name.split("/").filter(Boolean);
92
+ if (parts.length === 0)
93
+ continue;
94
+ const top = parts[0];
95
+ if (parts.length === 1) {
96
+ if (!PACK_ROOT_FILES.has(top)) {
97
+ return `illegal pack root file "${top}" — ${AGENT_MEMBER_PACK_LAYOUT}`;
98
+ }
99
+ continue;
100
+ }
101
+ if (!PACK_ROOT_DIRS.has(top)) {
102
+ return `illegal pack root "${top}/" — allowed: skills/ workspace/ env/`;
103
+ }
104
+ if (top === "skills") {
105
+ const id = parts[1];
106
+ if (!id || id === "." || id === "..") {
107
+ return `illegal skills path: ${name}`;
108
+ }
109
+ skillIds.add(id);
110
+ if (parts.length === 3 && parts[2].toLowerCase() === "skill.md") {
111
+ skillHasMd.add(id);
112
+ }
113
+ }
114
+ else if (top === "env") {
115
+ const leaf = parts.slice(1).join("/");
116
+ if (leaf.toLowerCase() !== "setup.md") {
117
+ return `illegal env file "${name}" — only env/SETUP.md`;
118
+ }
119
+ }
120
+ }
121
+ for (const id of skillIds) {
122
+ if (!skillHasMd.has(id)) {
123
+ return `skills/${id}/ missing SKILL.md`;
124
+ }
125
+ }
126
+ return undefined;
127
+ }
128
+ /** Parse a pack from an in-memory path→bytes map (zip or walked directory). */
129
+ export function parseAgentMemberPackFiles(files) {
130
+ const normalized = new Map();
131
+ for (const [k, v] of files) {
132
+ const name = zipEntryName(k);
133
+ if (name && !name.endsWith("/"))
134
+ normalized.set(name, v);
135
+ }
136
+ if (!hasPackMarker(normalized)) {
137
+ return {
138
+ ok: false,
139
+ error: `illegal pack: missing ${AGENT_MEMBER_PACK_MARKER} marker`,
140
+ };
141
+ }
142
+ const layoutProblem = validatePackLayout(normalized);
143
+ if (layoutProblem) {
144
+ return { ok: false, error: layoutProblem };
145
+ }
146
+ const playbookRaw = textOf(normalized, "playbook.md") ?? textOf(normalized, "PLAYBOOK.md");
147
+ const playbook = playbookRaw?.trim() ?? "";
148
+ if (!playbook) {
149
+ return { ok: false, error: "pack needs playbook.md" };
150
+ }
151
+ if (playbook.length > MEMBER_PLAYBOOK_MAX) {
152
+ return {
153
+ ok: false,
154
+ error: `playbook exceeds ${MEMBER_PLAYBOOK_MAX} characters`,
155
+ };
156
+ }
157
+ const manifestRaw = textOf(normalized, "manifest.json") ??
158
+ textOf(normalized, "member.json") ??
159
+ textOf(normalized, "card.json");
160
+ if (!manifestRaw) {
161
+ return { ok: false, error: "pack needs manifest.json (or member.json)" };
162
+ }
163
+ let manifestValue;
164
+ try {
165
+ manifestValue = JSON.parse(manifestRaw);
166
+ }
167
+ catch {
168
+ return { ok: false, error: "manifest.json is not JSON" };
169
+ }
170
+ const row = asRecord(manifestValue);
171
+ if (!row)
172
+ return { ok: false, error: "manifest must be an object" };
173
+ const kind = typeof row.kind === "string" ? row.kind.trim() : "";
174
+ if (kind !== AGENT_MEMBER_PACK_KIND && kind !== AGENT_MEMBER_CARD_KIND) {
175
+ return {
176
+ ok: false,
177
+ error: `manifest kind must be ${AGENT_MEMBER_PACK_KIND} or ${AGENT_MEMBER_CARD_KIND}`,
178
+ };
179
+ }
180
+ const cardPayload = {
181
+ ...row,
182
+ kind: AGENT_MEMBER_CARD_KIND,
183
+ playbook,
184
+ };
185
+ const parsed = parseAgentMemberCard(cardPayload);
186
+ if (!parsed.ok)
187
+ return { ok: false, error: parsed.error };
188
+ const envPrep = textOf(normalized, "env/SETUP.md")?.trim() ||
189
+ textOf(normalized, "env/setup.md")?.trim() ||
190
+ textOf(normalized, "SETUP.md")?.trim() ||
191
+ undefined;
192
+ return {
193
+ ok: true,
194
+ pack: {
195
+ card: parsed.card,
196
+ skillDirs: listSkillDirs(normalized),
197
+ ...(envPrep ? { envPrep } : {}),
198
+ files: normalized,
199
+ },
200
+ };
201
+ }
202
+ function walkDirFiles(root, prefix = "") {
203
+ const out = new Map();
204
+ let entries;
205
+ try {
206
+ entries = readdirSync(root);
207
+ }
208
+ catch {
209
+ return out;
210
+ }
211
+ for (const name of entries) {
212
+ if (name === "." || name === ".." || name === ".DS_Store")
213
+ continue;
214
+ const abs = path.join(root, name);
215
+ let st;
216
+ try {
217
+ st = statSync(abs);
218
+ }
219
+ catch {
220
+ continue;
221
+ }
222
+ const rel = prefix ? `${prefix}/${name}` : name;
223
+ if (st.isDirectory()) {
224
+ for (const [k, v] of walkDirFiles(abs, rel))
225
+ out.set(k, v);
226
+ }
227
+ else if (st.isFile()) {
228
+ out.set(zipEntryName(rel), readFileSync(abs));
229
+ }
230
+ }
231
+ return out;
232
+ }
233
+ function stripSingleTopFolder(entries) {
234
+ const keys = [...entries.keys()];
235
+ const tops = new Set(keys.map((k) => k.split("/")[0]).filter((t) => t.length > 0));
236
+ if (tops.size !== 1)
237
+ return entries;
238
+ const top = [...tops][0];
239
+ const hasManifest = entries.has(`${top}/manifest.json`) ||
240
+ entries.has(`${top}/member.json`) ||
241
+ entries.has("manifest.json");
242
+ const hasMarker = entries.has(`${top}/${AGENT_MEMBER_PACK_MARKER}`) ||
243
+ entries.has(AGENT_MEMBER_PACK_MARKER);
244
+ if (hasManifest &&
245
+ hasMarker &&
246
+ !entries.has("manifest.json") &&
247
+ !entries.has(AGENT_MEMBER_PACK_MARKER)) {
248
+ const stripped = new Map();
249
+ const prefix = `${top}/`;
250
+ for (const [k, v] of entries) {
251
+ if (k.startsWith(prefix))
252
+ stripped.set(k.slice(prefix.length), v);
253
+ else
254
+ stripped.set(k, v);
255
+ }
256
+ return stripped;
257
+ }
258
+ return entries;
259
+ }
260
+ /** Load a pack from a `.zip` file or an unpacked directory. */
261
+ export function loadAgentMemberPack(sourcePath) {
262
+ const abs = path.resolve(sourcePath);
263
+ if (!existsSync(abs)) {
264
+ return { ok: false, error: `pack path not found: ${abs}` };
265
+ }
266
+ let st;
267
+ try {
268
+ st = statSync(abs);
269
+ }
270
+ catch {
271
+ return { ok: false, error: `cannot stat pack path: ${abs}` };
272
+ }
273
+ if (st.isDirectory()) {
274
+ return parseAgentMemberPackFiles(walkDirFiles(abs));
275
+ }
276
+ if (!st.isFile()) {
277
+ return { ok: false, error: "pack path must be a .zip or directory" };
278
+ }
279
+ const buf = readFileSync(abs);
280
+ const entries = readZipEntries(buf);
281
+ if (!entries) {
282
+ return { ok: false, error: "not a readable zip (store or deflate)" };
283
+ }
284
+ return parseAgentMemberPackFiles(stripSingleTopFolder(entries));
285
+ }
286
+ /** Load pack bytes (e.g. UI upload). */
287
+ export function loadAgentMemberPackBytes(buf) {
288
+ const entries = readZipEntries(buf);
289
+ if (!entries) {
290
+ return { ok: false, error: "not a readable zip (store or deflate)" };
291
+ }
292
+ return parseAgentMemberPackFiles(stripSingleTopFolder(entries));
293
+ }
294
+ /** Read env/SETUP.md from an already-materialized pack dir (parent prep text). */
295
+ export function readMemberPackEnvPrep(packDir) {
296
+ for (const rel of ["env/SETUP.md", "env/setup.md", "SETUP.md"]) {
297
+ const abs = path.join(packDir, rel);
298
+ if (!existsSync(abs))
299
+ continue;
300
+ try {
301
+ const text = readFileSync(abs, "utf8").trim();
302
+ if (text)
303
+ return text;
304
+ }
305
+ catch {
306
+ continue;
307
+ }
308
+ }
309
+ return undefined;
310
+ }
311
+ /** Hints for advanced members that are not ready yet (team_list / Face list / UI). */
312
+ export function notReadyPackHints(input) {
313
+ const packDir = memberPackDir(input.rosterWorkspaceId, input.memberId, input.productHome);
314
+ const envPrep = readMemberPackEnvPrep(packDir);
315
+ return {
316
+ packDir,
317
+ ...(envPrep ? { envPrep } : {}),
318
+ envHint: envPrep
319
+ ? "Follow envPrep in this parent session, then team_ready. Do not auto-run untrusted scripts."
320
+ : "No env/SETUP.md — call team_ready when the member is configured.",
321
+ next: "team_ready → subagent member_id",
322
+ };
323
+ }
324
+ /**
325
+ * Write pack files under `agent-rosters/packs/{scope}/{id}/`.
326
+ * Skills stay in the pack — never installed into workspace `.agents/skills`.
327
+ */
328
+ export function materializeAgentMemberPack(pack, options) {
329
+ const packDir = memberPackDir(options.rosterWorkspaceId, options.memberId, options.productHome);
330
+ if (existsSync(packDir)) {
331
+ rmSync(packDir, { recursive: true, force: true });
332
+ }
333
+ mkdirSync(packDir, { recursive: true });
334
+ const files = new Map(pack.files);
335
+ if (!files.has(AGENT_MEMBER_PACK_MARKER)) {
336
+ files.set(AGENT_MEMBER_PACK_MARKER, Buffer.from(markerPayload()));
337
+ }
338
+ for (const [rel, data] of files) {
339
+ const name = zipEntryName(rel);
340
+ if (!name || name.includes(".."))
341
+ continue;
342
+ const dest = path.join(packDir, name);
343
+ mkdirSync(path.dirname(dest), { recursive: true });
344
+ writeFileSync(dest, data);
345
+ }
346
+ return {
347
+ card: pack.card,
348
+ packDir,
349
+ skillNames: pack.skillDirs,
350
+ ...(pack.envPrep ? { envPrep: pack.envPrep } : {}),
351
+ };
352
+ }
353
+ /** Keep on-disk pack `playbook.md` aligned after a roster playbook revise. */
354
+ export function syncMemberPackPlaybook(input) {
355
+ const packDir = memberPackDir(input.rosterWorkspaceId, input.memberId, input.productHome);
356
+ if (!existsSync(packDir) || !statSync(packDir).isDirectory())
357
+ return false;
358
+ const text = input.playbook.endsWith("\n") ? input.playbook : `${input.playbook}\n`;
359
+ try {
360
+ writeFileSync(path.join(packDir, "playbook.md"), text, "utf8");
361
+ return true;
362
+ }
363
+ catch {
364
+ return false;
365
+ }
366
+ }
367
+ /**
368
+ * Bundle local skill directories into pack `skills/<id>/…` files.
369
+ * Each path must be a directory containing SKILL.md (or a SKILL.md file's parent).
370
+ * Walks the full tree — includes `references/`, scripts, and other assets.
371
+ */
372
+ export function collectPackSkillFiles(skillPaths) {
373
+ const files = new Map();
374
+ for (const raw of skillPaths) {
375
+ const trimmed = raw.trim();
376
+ if (!trimmed)
377
+ continue;
378
+ let abs = path.resolve(trimmed);
379
+ if (!existsSync(abs)) {
380
+ return { ok: false, error: `skill path not found: ${trimmed}` };
381
+ }
382
+ let st;
383
+ try {
384
+ st = statSync(abs);
385
+ }
386
+ catch {
387
+ return { ok: false, error: `cannot stat skill path: ${trimmed}` };
388
+ }
389
+ if (st.isFile()) {
390
+ if (path.basename(abs).toLowerCase() !== "skill.md") {
391
+ return { ok: false, error: `skill path must be a dir or SKILL.md: ${trimmed}` };
392
+ }
393
+ abs = path.dirname(abs);
394
+ }
395
+ else if (!st.isDirectory()) {
396
+ return { ok: false, error: `skill path must be a directory: ${trimmed}` };
397
+ }
398
+ const skillMd = path.join(abs, "SKILL.md");
399
+ if (!existsSync(skillMd)) {
400
+ return { ok: false, error: `skill dir missing SKILL.md: ${trimmed}` };
401
+ }
402
+ const id = path.basename(abs).trim();
403
+ if (!id || id === "." || id === "..") {
404
+ return { ok: false, error: `invalid skill id from path: ${trimmed}` };
405
+ }
406
+ for (const [rel, data] of walkDirFiles(abs)) {
407
+ files.set(zipEntryName(`skills/${id}/${rel}`), data);
408
+ }
409
+ }
410
+ return { ok: true, files };
411
+ }
412
+ /**
413
+ * Bundle dirs/files into pack `workspace/…` (公文包/briefcase materials, not session cwd).
414
+ * A directory keeps its basename; a file lands as `workspace/<basename>`.
415
+ */
416
+ export function collectPackWorkspaceFiles(filePaths) {
417
+ const files = new Map();
418
+ for (const raw of filePaths) {
419
+ const trimmed = raw.trim();
420
+ if (!trimmed)
421
+ continue;
422
+ const abs = path.resolve(trimmed);
423
+ if (!existsSync(abs)) {
424
+ return { ok: false, error: `workspace path not found: ${trimmed}` };
425
+ }
426
+ let st;
427
+ try {
428
+ st = statSync(abs);
429
+ }
430
+ catch {
431
+ return { ok: false, error: `cannot stat workspace path: ${trimmed}` };
432
+ }
433
+ if (st.isFile()) {
434
+ const base = path.basename(abs);
435
+ if (!base || base === "." || base === "..") {
436
+ return { ok: false, error: `invalid workspace file: ${trimmed}` };
437
+ }
438
+ files.set(zipEntryName(`workspace/${base}`), readFileSync(abs));
439
+ continue;
440
+ }
441
+ if (!st.isDirectory()) {
442
+ return { ok: false, error: `workspace path must be a file or directory: ${trimmed}` };
443
+ }
444
+ const id = path.basename(abs).trim();
445
+ if (!id || id === "." || id === "..") {
446
+ return { ok: false, error: `invalid workspace dir from path: ${trimmed}` };
447
+ }
448
+ for (const [rel, data] of walkDirFiles(abs)) {
449
+ files.set(zipEntryName(`workspace/${id}/${rel}`), data);
450
+ }
451
+ }
452
+ return { ok: true, files };
453
+ }
454
+ /**
455
+ * Advanced member 公文包 (briefcase) root — not the session workspace cwd.
456
+ * Spawn keeps parent cwd; this path is injected into child/parent context.
457
+ */
458
+ export function resolveMemberPackHome(input) {
459
+ const packDir = memberPackDir(input.rosterWorkspaceId, input.memberId, input.productHome);
460
+ if (!existsSync(packDir))
461
+ return undefined;
462
+ try {
463
+ if (!statSync(packDir).isDirectory())
464
+ return undefined;
465
+ }
466
+ catch {
467
+ return undefined;
468
+ }
469
+ return packDir;
470
+ }
471
+ /**
472
+ * Optional bundled materials under the pack (`workspace/`). Still under
473
+ * 公文包 (briefcase), not the coding session cwd.
474
+ */
475
+ export function resolveMemberPackMaterialsDir(input) {
476
+ if (!resolveMemberPackHome(input))
477
+ return undefined;
478
+ const materials = memberPackWorkspaceDir(input.rosterWorkspaceId, input.memberId, input.productHome);
479
+ try {
480
+ if (existsSync(materials) && statSync(materials).isDirectory()) {
481
+ return materials;
482
+ }
483
+ }
484
+ catch {
485
+ return undefined;
486
+ }
487
+ return undefined;
488
+ }
489
+ /** Scan an on-disk advanced pack for playbook / env / materials / skills(+references). */
490
+ export function listMemberPackInventory(input) {
491
+ const packHome = resolveMemberPackHome(input);
492
+ if (!packHome)
493
+ return undefined;
494
+ const playbookPath = path.join(packHome, "playbook.md");
495
+ const envPrepPath = path.join(packHome, "env", "SETUP.md");
496
+ const materialsPath = resolveMemberPackMaterialsDir(input);
497
+ const materialsTop = [];
498
+ if (materialsPath) {
499
+ try {
500
+ for (const name of readdirSync(materialsPath)) {
501
+ if (name === "." || name === "..")
502
+ continue;
503
+ materialsTop.push(name);
504
+ }
505
+ }
506
+ catch {
507
+ // ignore
508
+ }
509
+ }
510
+ const skills = [];
511
+ const skillsRoot = path.join(packHome, "skills");
512
+ try {
513
+ if (existsSync(skillsRoot) && statSync(skillsRoot).isDirectory()) {
514
+ for (const id of readdirSync(skillsRoot)) {
515
+ if (id === "." || id === "..")
516
+ continue;
517
+ const skillPath = path.join(skillsRoot, id);
518
+ let st;
519
+ try {
520
+ st = statSync(skillPath);
521
+ }
522
+ catch {
523
+ continue;
524
+ }
525
+ if (!st.isDirectory())
526
+ continue;
527
+ if (!existsSync(path.join(skillPath, "SKILL.md")))
528
+ continue;
529
+ const referencesPath = path.join(skillPath, "references");
530
+ let hasReferences = false;
531
+ try {
532
+ hasReferences =
533
+ existsSync(referencesPath) && statSync(referencesPath).isDirectory();
534
+ }
535
+ catch {
536
+ hasReferences = false;
537
+ }
538
+ skills.push({
539
+ id,
540
+ path: skillPath,
541
+ hasReferences,
542
+ ...(hasReferences ? { referencesPath } : {}),
543
+ });
544
+ }
545
+ }
546
+ }
547
+ catch {
548
+ // ignore
549
+ }
550
+ skills.sort((a, b) => a.id.localeCompare(b.id));
551
+ return {
552
+ packHome,
553
+ playbookPath,
554
+ ...(existsSync(envPrepPath) ? { envPrepPath } : {}),
555
+ ...(materialsPath ? { materialsPath } : {}),
556
+ materialsTop,
557
+ skills,
558
+ };
559
+ }
560
+ /** Compact capability / preamble lines for a 公文包 (briefcase) inventory. */
561
+ export function formatMemberPackInventoryLines(inv) {
562
+ const lines = [
563
+ `${MEMBER_PACK_BRIEFCASE}: not workspace cwd — read/edit here or copy into workspace`,
564
+ `pack_home: ${inv.packHome}`,
565
+ `playbook: ${inv.playbookPath}`,
566
+ ];
567
+ if (inv.envPrepPath)
568
+ lines.push(`env_prep: ${inv.envPrepPath}`);
569
+ if (inv.materialsPath) {
570
+ const tops = inv.materialsTop.length > 0
571
+ ? ` · top=${inv.materialsTop.slice(0, 12).join(",")}`
572
+ : "";
573
+ lines.push(`materials: ${inv.materialsPath}${tops}`);
574
+ }
575
+ if (inv.skills.length === 0) {
576
+ lines.push("skills: (none yet)");
577
+ }
578
+ else {
579
+ for (const s of inv.skills) {
580
+ const refs = s.hasReferences
581
+ ? ` · references=${s.referencesPath ?? path.join(s.path, "references")}`
582
+ : "";
583
+ lines.push(`skill: ${s.id} @ ${s.path}${refs}`);
584
+ }
585
+ }
586
+ return lines;
587
+ }
588
+ /**
589
+ * Merge skills / materials / env into an existing 公文包 (briefcase)
590
+ * without wiping other roots. Creates missing dirs; overwrites colliding paths.
591
+ */
592
+ export function mergeIntoMemberPack(input) {
593
+ const packHome = resolveMemberPackHome(input);
594
+ if (!packHome) {
595
+ return {
596
+ ok: false,
597
+ error: `${MEMBER_PACK_BRIEFCASE} missing on disk — re-import pack= or team_save kind=advanced`,
598
+ };
599
+ }
600
+ const writeRel = (rel, data) => {
601
+ const name = zipEntryName(rel);
602
+ if (!name || name.includes(".."))
603
+ return;
604
+ const dest = path.join(packHome, name);
605
+ mkdirSync(path.dirname(dest), { recursive: true });
606
+ writeFileSync(dest, data);
607
+ };
608
+ if (input.envPrep?.trim()) {
609
+ const text = input.envPrep.endsWith("\n")
610
+ ? input.envPrep
611
+ : `${input.envPrep}\n`;
612
+ writeRel("env/SETUP.md", Buffer.from(text, "utf8"));
613
+ }
614
+ if (input.skillsFiles) {
615
+ for (const [rel, data] of input.skillsFiles) {
616
+ writeRel(rel.startsWith("skills/") ? rel : `skills/${rel}`, data);
617
+ }
618
+ }
619
+ if (input.workspaceFiles) {
620
+ for (const [rel, data] of input.workspaceFiles) {
621
+ writeRel(rel.startsWith("workspace/") ? rel : `workspace/${rel}`, data);
622
+ }
623
+ }
624
+ const inventory = listMemberPackInventory(input);
625
+ if (!inventory) {
626
+ return {
627
+ ok: false,
628
+ error: `${MEMBER_PACK_BRIEFCASE} inventory failed after merge`,
629
+ };
630
+ }
631
+ return { ok: true, inventory };
632
+ }
633
+ /** Build a store-method zip for export (includes marker; no appearance). */
634
+ export function buildAgentMemberPackZip(card, options) {
635
+ const enc = new TextEncoder();
636
+ const manifest = {
637
+ kind: AGENT_MEMBER_PACK_KIND,
638
+ version: AGENT_MEMBER_PACK_VERSION,
639
+ name: card.name,
640
+ role: card.role,
641
+ ...(card.brief ? { brief: card.brief } : {}),
642
+ ...(card.inject ? { inject: card.inject } : {}),
643
+ ...(card.tools ? { tools: card.tools } : {}),
644
+ };
645
+ const entries = [
646
+ { name: AGENT_MEMBER_PACK_MARKER, data: enc.encode(markerPayload()) },
647
+ { name: "manifest.json", data: enc.encode(`${JSON.stringify(manifest, null, 2)}\n`) },
648
+ {
649
+ name: "playbook.md",
650
+ data: enc.encode(card.playbook.endsWith("\n") ? card.playbook : `${card.playbook}\n`),
651
+ },
652
+ ];
653
+ if (options?.envPrep?.trim()) {
654
+ entries.push({
655
+ name: "env/SETUP.md",
656
+ data: enc.encode(options.envPrep.endsWith("\n") ? options.envPrep : `${options.envPrep}\n`),
657
+ });
658
+ }
659
+ if (options?.skillsFiles) {
660
+ for (const [rel, data] of options.skillsFiles) {
661
+ const name = zipEntryName(rel.startsWith("skills/") ? rel : `skills/${rel}`);
662
+ entries.push({ name, data });
663
+ }
664
+ }
665
+ if (options?.workspaceFiles) {
666
+ for (const [rel, data] of options.workspaceFiles) {
667
+ const name = zipEntryName(rel.startsWith("workspace/") ? rel : `workspace/${rel}`);
668
+ entries.push({ name, data });
669
+ }
670
+ }
671
+ return buildStoredZip(entries);
672
+ }
673
+ /** Zip an on-disk advanced pack directory (must already contain the marker). */
674
+ export function zipMemberPackDir(packDir) {
675
+ if (!existsSync(packDir) || !statSync(packDir).isDirectory())
676
+ return undefined;
677
+ const files = walkDirFiles(packDir);
678
+ if (!hasPackMarker(files)) {
679
+ files.set(AGENT_MEMBER_PACK_MARKER, Buffer.from(markerPayload()));
680
+ }
681
+ const entries = [...files.entries()].map(([name, data]) => ({ name, data }));
682
+ return buildStoredZip(entries);
683
+ }
684
+ //# sourceMappingURL=agent-member-pack.js.map