@anhlp/alp 0.0.1-beta.3 → 0.0.1-beta.6

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 (175) hide show
  1. package/README.md +14 -410
  2. package/bin/alp.js +5 -0
  3. package/lib/launcher.js +136 -0
  4. package/package.json +19 -104
  5. package/dist/THIRD-PARTY-NOTICES.txt +0 -626
  6. package/dist/acp.js +0 -19903
  7. package/dist/alpd.js +0 -42558
  8. package/docs/modular-monorepo.md +0 -210
  9. package/packages/cli/README.md +0 -7
  10. package/packages/cli/dist/THIRD-PARTY-NOTICES.txt +0 -626
  11. package/packages/cli/dist/acp/agent.d.ts +0 -37
  12. package/packages/cli/dist/acp/agent.js +0 -489
  13. package/packages/cli/dist/acp/main.d.ts +0 -4
  14. package/packages/cli/dist/acp/main.js +0 -23
  15. package/packages/cli/dist/acp.js +0 -19903
  16. package/packages/cli/dist/alpd.js +0 -42558
  17. package/packages/cli/dist/backup.d.ts +0 -4
  18. package/packages/cli/dist/backup.js +0 -146
  19. package/packages/cli/dist/build-manifest.json +0 -142
  20. package/packages/cli/dist/cli.d.ts +0 -2
  21. package/packages/cli/dist/cli.js +0 -28
  22. package/packages/cli/dist/daemon.d.ts +0 -35
  23. package/packages/cli/dist/daemon.js +0 -126
  24. package/packages/cli/dist/index.d.ts +0 -1
  25. package/packages/cli/dist/index.js +0 -5
  26. package/packages/cli/dist/interfaces.d.ts +0 -16
  27. package/packages/cli/dist/interfaces.js +0 -96
  28. package/packages/cli/dist/library.d.ts +0 -140
  29. package/packages/cli/dist/library.js +0 -319
  30. package/packages/cli/dist/paths.d.ts +0 -3
  31. package/packages/cli/dist/paths.js +0 -18
  32. package/packages/cli/dist/projects.d.ts +0 -9
  33. package/packages/cli/dist/projects.js +0 -159
  34. package/packages/cli/dist/sessions.d.ts +0 -41
  35. package/packages/cli/dist/sessions.js +0 -447
  36. package/packages/cli/dist/tasks.d.ts +0 -29
  37. package/packages/cli/dist/tasks.js +0 -345
  38. package/packages/cli/dist/usage.d.ts +0 -3
  39. package/packages/cli/dist/usage.js +0 -72
  40. package/packages/client/README.md +0 -24
  41. package/packages/client/dist/acp-probe.d.ts +0 -44
  42. package/packages/client/dist/acp-probe.js +0 -37
  43. package/packages/client/dist/antigravity-probe.d.ts +0 -34
  44. package/packages/client/dist/antigravity-probe.js +0 -95
  45. package/packages/client/dist/command.d.ts +0 -5
  46. package/packages/client/dist/command.js +0 -33
  47. package/packages/client/dist/doctor.d.ts +0 -13
  48. package/packages/client/dist/doctor.js +0 -587
  49. package/packages/client/dist/index.d.ts +0 -116
  50. package/packages/client/dist/index.js +0 -500
  51. package/packages/client/dist/ipc.d.ts +0 -42
  52. package/packages/client/dist/ipc.js +0 -69
  53. package/packages/client/dist/library-test.d.ts +0 -94
  54. package/packages/client/dist/library-test.js +0 -37
  55. package/packages/client/dist/mcp-probe.d.ts +0 -24
  56. package/packages/client/dist/mcp-probe.js +0 -147
  57. package/packages/client/dist/portable.d.ts +0 -18
  58. package/packages/client/dist/portable.js +0 -27
  59. package/packages/client/dist/prompt.d.ts +0 -32
  60. package/packages/client/dist/prompt.js +0 -59
  61. package/packages/client/dist/render.d.ts +0 -6
  62. package/packages/client/dist/render.js +0 -120
  63. package/packages/client/dist/rpc/client.d.ts +0 -82
  64. package/packages/client/dist/rpc/client.js +0 -292
  65. package/packages/client/dist/rpc/errors.d.ts +0 -42
  66. package/packages/client/dist/rpc/errors.js +0 -34
  67. package/packages/client/dist/rpc/framing.d.ts +0 -21
  68. package/packages/client/dist/rpc/framing.js +0 -57
  69. package/packages/client/dist/rpc/handshake.d.ts +0 -19
  70. package/packages/client/dist/rpc/handshake.js +0 -8
  71. package/packages/client/dist/rpc/reconnect.d.ts +0 -97
  72. package/packages/client/dist/rpc/reconnect.js +0 -227
  73. package/packages/client/dist/rpc/timers.d.ts +0 -11
  74. package/packages/client/dist/rpc/timers.js +0 -11
  75. package/packages/client/dist/service.d.ts +0 -50
  76. package/packages/client/dist/service.js +0 -143
  77. package/packages/client/dist/setup.d.ts +0 -2
  78. package/packages/client/dist/setup.js +0 -146
  79. package/packages/client/dist/supervise.d.ts +0 -12
  80. package/packages/client/dist/supervise.js +0 -87
  81. package/packages/client/dist/websocket.d.ts +0 -51
  82. package/packages/client/dist/websocket.js +0 -190
  83. package/packages/core/README.md +0 -7
  84. package/packages/core/dist/adapter.d.ts +0 -20
  85. package/packages/core/dist/adapter.js +0 -30
  86. package/packages/core/dist/atomic.d.ts +0 -2
  87. package/packages/core/dist/atomic.js +0 -28
  88. package/packages/core/dist/backup.d.ts +0 -27
  89. package/packages/core/dist/backup.js +0 -582
  90. package/packages/core/dist/beads.d.ts +0 -65
  91. package/packages/core/dist/beads.js +0 -213
  92. package/packages/core/dist/delegation.d.ts +0 -2
  93. package/packages/core/dist/delegation.js +0 -32
  94. package/packages/core/dist/errors.d.ts +0 -4
  95. package/packages/core/dist/errors.js +0 -11
  96. package/packages/core/dist/formulas.d.ts +0 -25
  97. package/packages/core/dist/formulas.js +0 -201
  98. package/packages/core/dist/hook-run.d.ts +0 -44
  99. package/packages/core/dist/hook-run.js +0 -60
  100. package/packages/core/dist/hooks.d.ts +0 -35
  101. package/packages/core/dist/hooks.js +0 -30
  102. package/packages/core/dist/index.d.ts +0 -30
  103. package/packages/core/dist/index.js +0 -63
  104. package/packages/core/dist/init.d.ts +0 -30
  105. package/packages/core/dist/init.js +0 -89
  106. package/packages/core/dist/library-edit.d.ts +0 -93
  107. package/packages/core/dist/library-edit.js +0 -368
  108. package/packages/core/dist/library.d.ts +0 -28
  109. package/packages/core/dist/library.js +0 -108
  110. package/packages/core/dist/log.d.ts +0 -37
  111. package/packages/core/dist/log.js +0 -156
  112. package/packages/core/dist/migrations.d.ts +0 -9
  113. package/packages/core/dist/migrations.js +0 -95
  114. package/packages/core/dist/models.d.ts +0 -11
  115. package/packages/core/dist/models.js +0 -44
  116. package/packages/core/dist/permissions.d.ts +0 -14
  117. package/packages/core/dist/permissions.js +0 -229
  118. package/packages/core/dist/profiles.d.ts +0 -27
  119. package/packages/core/dist/profiles.js +0 -122
  120. package/packages/core/dist/promptsafe.d.ts +0 -1
  121. package/packages/core/dist/promptsafe.js +0 -13
  122. package/packages/core/dist/providers.d.ts +0 -90
  123. package/packages/core/dist/providers.js +0 -101
  124. package/packages/core/dist/resolver.d.ts +0 -41
  125. package/packages/core/dist/resolver.js +0 -240
  126. package/packages/core/dist/safe-path.d.ts +0 -2
  127. package/packages/core/dist/safe-path.js +0 -24
  128. package/packages/core/dist/shell.d.ts +0 -16
  129. package/packages/core/dist/shell.js +0 -22
  130. package/packages/core/dist/state-schema.d.ts +0 -9
  131. package/packages/core/dist/state-schema.js +0 -54
  132. package/packages/core/dist/task-rows.d.ts +0 -47
  133. package/packages/core/dist/task-rows.js +0 -45
  134. package/packages/core/dist/tasks.d.ts +0 -128
  135. package/packages/core/dist/tasks.js +0 -835
  136. package/packages/core/dist/teams.d.ts +0 -80
  137. package/packages/core/dist/teams.js +0 -159
  138. package/packages/core/dist/template-path.d.ts +0 -2
  139. package/packages/core/dist/template-path.js +0 -10
  140. package/packages/core/dist/trust.d.ts +0 -8
  141. package/packages/core/dist/trust.js +0 -47
  142. package/packages/core/dist/types.d.ts +0 -51
  143. package/packages/core/dist/upgrade.d.ts +0 -10
  144. package/packages/core/dist/upgrade.js +0 -200
  145. package/packages/core/dist/user-settings.d.ts +0 -6
  146. package/packages/core/dist/user-settings.js +0 -72
  147. package/packages/core/dist/validation.d.ts +0 -17
  148. package/packages/core/dist/validation.js +0 -295
  149. package/packages/core/dist/verify.d.ts +0 -15
  150. package/packages/core/dist/verify.js +0 -123
  151. package/packages/core/dist/workflow.d.ts +0 -45
  152. package/packages/core/dist/workflow.js +0 -33
  153. package/templates/ALP.md +0 -52
  154. package/templates/agents/lead/AGENT.md +0 -90
  155. package/templates/agents/main/AGENT.md +0 -150
  156. package/templates/agents/oracle/AGENT.md +0 -44
  157. package/templates/agents/peer/AGENT.md +0 -55
  158. package/templates/agents/reviewer/AGENT.md +0 -80
  159. package/templates/agents/supervisor/AGENT.md +0 -72
  160. package/templates/providers/antigravity.json +0 -6
  161. package/templates/providers/opencode.json +0 -7
  162. package/templates/role-skills.json +0 -8
  163. package/templates/skills/bug-loop/SKILL.md +0 -44
  164. package/templates/skills/bug-loop/references/test-proof.md +0 -25
  165. package/templates/skills/goal-griller/SKILL.md +0 -49
  166. package/templates/skills/prompt-leverage/SKILL.md +0 -37
  167. package/templates/skills/prompt-leverage/references/brief-template.md +0 -27
  168. package/templates/skills/sequence-execution-plan/SKILL.md +0 -42
  169. package/templates/skills/sequence-execution-plan/references/plan-template.md +0 -22
  170. package/templates/skills/smart-commits/SKILL.md +0 -40
  171. package/templates/skills/xia/SKILL.md +0 -42
  172. package/templates/teams/cafe/HOUSE_RULES.md +0 -1
  173. package/templates/teams/cafe/team.json +0 -15
  174. package/templates/teams/pho/HOUSE_RULES.md +0 -1
  175. package/templates/teams/pho/team.json +0 -14
@@ -1,368 +0,0 @@
1
- // packages/core/src/library-edit.js
2
- import { lstat, mkdir, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
3
- import { createHash, randomUUID } from "node:crypto";
4
- import path from "node:path";
5
- import { moveFile } from "./atomic.js";
6
- import { AlpError } from "./errors.js";
7
- import { assertNoSymlinks } from "./safe-path.js";
8
- import { agentSources, builtinText, jsonObject, libraryEntries, optionalText } from "./resolver.js";
9
- import { teamSources, validateTeam } from "./teams.js";
10
- import { normalizeMcp, validateAgentConfig, validateHook } from "./validation.js";
11
- import { validateProvider } from "./providers.js";
12
- var EDIT_KINDS = ["agents", "skills", "mcp", "hooks", "teams", "providers"];
13
- var validName = (name) => typeof name === "string" && /^[\w.-]+$/.test(name) && name !== "." && name !== "..";
14
- var object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
15
- var fail = (code, message) => {
16
- throw new AlpError(code, message);
17
- };
18
- var FILES = {
19
- agents: (name) => [[`${name}/AGENT.md`, "instructions", "text"], [`${name}/agent.json`, "config", "json"]],
20
- skills: (name) => [[`${name}/SKILL.md`, "body", "text"]],
21
- mcp: (name) => [[`${name}.json`, "server", "json"]],
22
- hooks: (name) => [[`${name}.json`, "hook", "json"]],
23
- teams: (name) => [[`${name}/team.json`, "team", "json"], [`${name}/HOUSE_RULES.md`, "houseRules", "text"]],
24
- providers: (name) => [[`${name}.json`, "provider", "json"]]
25
- };
26
- var ownDirectory = (kind) => ["agents", "skills", "teams"].includes(kind);
27
- function kindOf(kind) {
28
- if (!EDIT_KINDS.includes(kind)) fail("INVALID_KIND", `Unknown kind '${kind}'; use one of ${EDIT_KINDS.join(", ")}`);
29
- return kind;
30
- }
31
- function nameOf(name) {
32
- if (!validName(name)) fail("INVALID_NAME", `'${name}' is not a valid name: use letters, digits, '.', '_' or '-'`);
33
- return name;
34
- }
35
- function base(scope, { root, library }, kind) {
36
- if (kind === "providers" && scope !== "library") fail("INVALID_SCOPE", "Providers live only in your library");
37
- if (scope === "library") return library ?? fail("INVALID_SCOPE", "No library: ALP_HOME is not set");
38
- if (scope === "project") return root ? path.join(path.resolve(root), ".alp") : fail("INVALID_SCOPE", "No project directory");
39
- return fail("INVALID_SCOPE", `Scope must be library or project, not '${scope}'`);
40
- }
41
- var revisionOf = (content) => content === void 0 ? null : createHash("sha256").update(JSON.stringify(content)).digest("hex").slice(0, 16);
42
- function assertProjectDataContent(kind, content) {
43
- const deny = () => fail("UNSAFE_LIBRARY_WRITE", "Project-data writes allow only instruction entries and non-executable agent configuration");
44
- if (!object(content) || !["agents", "skills"].includes(kind)) deny();
45
- const keys = kind === "skills" ? ["body"] : ["instructions", "config"];
46
- if (Object.keys(content).some((key) => !keys.includes(key))) deny();
47
- const text = kind === "skills" ? content.body : content.instructions;
48
- if (typeof text !== "string" || !text.trim()) deny();
49
- if (content.config !== void 0) {
50
- const config = content.config;
51
- if (!object(config)) deny();
52
- const allowed = ["description", "model", "thinking", "context", "autocompact"];
53
- if (Object.keys(config).some((key) => !allowed.includes(key))) deny();
54
- for (const key of ["description", "thinking"]) if (config[key] !== void 0 && typeof config[key] !== "string") deny();
55
- if (config.model !== void 0 && (typeof config.model !== "string" || !/^(codex|claude):[^\s/\\:]{1,240}$/.test(config.model))) deny();
56
- for (const key of ["context", "autocompact"]) if (config[key] !== void 0 && !(typeof config[key] === "string" || typeof config[key] === "number" && Number.isFinite(config[key]))) deny();
57
- }
58
- }
59
- async function projectDataEntry(kind, name, { scope, root, library, templates }) {
60
- if (scope !== "project" || !root || !["agents", "skills"].includes(kind)) fail("UNSAFE_LIBRARY_WRITE", "Project-data writes require project scope and agents or skills");
61
- nameOf(name);
62
- const directory = base(scope, { root, library }, kind);
63
- await safeEntryPaths(directory, kind, name);
64
- const found = await layers(kind, name, { root, library, templates });
65
- if (found.library !== void 0 || found.builtin !== void 0) fail("UNSAFE_LIBRARY_WRITE", "Project-data writes cannot shadow or reveal a library or built-in entry");
66
- if (found.project !== void 0) assertProjectDataContent(kind, found.project);
67
- const owned = path.join(directory, kind, name);
68
- const files = await readdir(owned).catch((error) => {
69
- if (error.code === "ENOENT") return [];
70
- throw error;
71
- });
72
- const allowed = new Set(FILES[kind](name).map(([file]) => path.basename(file)));
73
- for (const file of files) {
74
- const info = await lstat(path.join(owned, file));
75
- if (!allowed.has(file) || !info.isFile() || info.isSymbolicLink() || info.nlink !== 1) fail("UNSAFE_LIBRARY_WRITE", "Project-data entries cannot contain sidecars, links or executable assets");
76
- }
77
- }
78
- async function safeEntryPaths(directory, kind, name) {
79
- for (const [file] of FILES[kind](name)) {
80
- await assertNoSymlinks(directory, path.join(kind, file));
81
- }
82
- }
83
- async function readLayer(kind, name, directory) {
84
- await safeEntryPaths(directory, kind, name);
85
- const content = {};
86
- let found = false;
87
- for (const [file, key, type] of FILES[kind](name)) {
88
- const target = path.join(directory, kind, file);
89
- const text = await optionalText(target);
90
- if (text === void 0) continue;
91
- found = true;
92
- if (type === "text") {
93
- content[key] = text;
94
- continue;
95
- }
96
- try {
97
- content[key] = JSON.parse(text);
98
- } catch (cause) {
99
- throw new AlpError("INVALID_ENTRY", `${target}: ${cause.message}`, { cause });
100
- }
101
- }
102
- const main = { agents: "instructions", skills: "body", teams: "team" }[kind];
103
- return found && (!main || content[main] !== void 0) ? content : void 0;
104
- }
105
- async function readBuiltin(kind, name, templates) {
106
- if (kind === "agents") {
107
- const instructions = await builtinText(`agents/${name}/AGENT.md`, templates);
108
- if (instructions === void 0) return void 0;
109
- const config = await builtinText(`agents/${name}/agent.json`, templates);
110
- return { instructions, ...config !== void 0 ? { config: JSON.parse(config) } : {} };
111
- }
112
- if (kind === "teams") {
113
- const team = await builtinText(`teams/${name}/team.json`, templates);
114
- if (team === void 0) return void 0;
115
- return { team: JSON.parse(team), houseRules: await builtinText(`teams/${name}/HOUSE_RULES.md`, templates) ?? "" };
116
- }
117
- return void 0;
118
- }
119
- async function layers(kind, name, { root, library, templates }) {
120
- return {
121
- builtin: await readBuiltin(kind, name, templates),
122
- library: library ? await readLayer(kind, name, library) : void 0,
123
- project: root && kind !== "providers" ? await readLayer(kind, name, path.join(path.resolve(root), ".alp")) : void 0
124
- };
125
- }
126
- async function getEntry(kind, name, { root, library, templates, scope } = {}) {
127
- kindOf(kind);
128
- nameOf(name);
129
- const found = await layers(kind, name, { root, library, templates });
130
- const order = scope ? [scope] : ["project", "library", "builtin"];
131
- const source = order.find((layer) => found[layer] !== void 0);
132
- if (!source) fail("NOT_FOUND", `No ${kind} entry '${name}'${scope ? ` in the ${scope}` : ""}`);
133
- const below = ["builtin", "library", "project"].slice(0, ["builtin", "library", "project"].indexOf(source)).reverse().find((layer) => found[layer] !== void 0);
134
- return {
135
- kind,
136
- name,
137
- source: (
138
- /** @type {'builtin' | 'library' | 'project'} */
139
- source
140
- ),
141
- ...below ? { overrides: (
142
- /** @type {'builtin' | 'library'} */
143
- below
144
- ) } : {},
145
- content: found[source],
146
- revision: revisionOf(found[source]),
147
- usedBy: await usersOf(kind, name, { root, library, templates }),
148
- // The skills the library gives an agent by name, whatever layer defines it (ALPD §49).
149
- ...kind === "agents" && library ? { librarySkills: await givenSkills(library, name) } : {}
150
- };
151
- }
152
- async function readGiven(library) {
153
- const file = path.join(library, "role-skills.json");
154
- const text = await optionalText(file);
155
- if (text === void 0) return { file, roles: {} };
156
- let roles;
157
- try {
158
- roles = JSON.parse(text);
159
- } catch (cause) {
160
- throw new AlpError("INVALID_LIBRARY", `${file}: ${cause.message}`, { cause });
161
- }
162
- if (!object(roles)) fail("INVALID_LIBRARY", `${file}: expected an object of agent names to skill lists`);
163
- return { file, roles };
164
- }
165
- async function givenSkills(library, agent) {
166
- const { roles } = await readGiven(library);
167
- return Array.isArray(roles[agent]) ? roles[agent].filter((name) => typeof name === "string") : [];
168
- }
169
- async function setGivenSkills(agent, skills, { library } = {}) {
170
- nameOf(agent);
171
- if (!library) fail("INVALID_SCOPE", "No library: ALP_HOME is not set");
172
- if (!Array.isArray(skills) || !skills.every(validName)) fail("INVALID_ENTRY", "Skills must be a list of skill names");
173
- const unique = [...new Set(skills)];
174
- for (const skill of unique) if (!await exists(path.join(library, "skills", skill, "SKILL.md"))) fail("NOT_FOUND", `No skill '${skill}' in the library`);
175
- const { file, roles } = await readGiven(library);
176
- if (unique.length) roles[agent] = unique;
177
- else delete roles[agent];
178
- await writeAtomic(file, JSON.stringify(roles, null, 2) + "\n");
179
- return { skills: unique };
180
- }
181
- async function listEntries(kind, { root, library, templates } = {}) {
182
- kindOf(kind);
183
- if (kind !== "teams") return libraryEntries(kind, root, { library, templates });
184
- const rows = [];
185
- for (const [name, source] of await teamSources(root, { library, templates })) rows.push({ name, source: source.source, ...source.overrides ? { overrides: source.overrides } : {}, ...source.directory ? { path: source.directory } : {}, usedBy: await usersOf(kind, name, { root, library, templates }) });
186
- return rows;
187
- }
188
- async function usersOf(kind, name, { root, library, templates } = {}) {
189
- const users = /* @__PURE__ */ new Set();
190
- if (kind === "providers" && root) {
191
- const settings = await jsonObject(path.join(path.resolve(root), ".alp", "settings.json"), "INVALID_SETTINGS").catch(() => ({}));
192
- if (settings.runtime?.provider === name) users.add("project settings");
193
- }
194
- if (kind === "teams") {
195
- if (root) {
196
- const settings = await jsonObject(path.join(path.resolve(root), ".alp", "settings.json"), "INVALID_SETTINGS").catch(() => ({}));
197
- if (settings.workflow?.mode === name) users.add("project settings");
198
- }
199
- return [...users];
200
- }
201
- if (kind === "agents") {
202
- for (const [team, source] of await teamSources(root, { library, templates })) {
203
- const text = source.directory ? await optionalText(path.join(source.directory, "team.json")) : await builtinText(`teams/${team}/team.json`, templates);
204
- try {
205
- const config = JSON.parse(text ?? "{}");
206
- if (config.main === name || Object.hasOwn(config.members ?? {}, name) || config.supervisor?.agent === name) users.add(`team ${team}`);
207
- } catch {
208
- }
209
- }
210
- if (root) {
211
- const settings = await jsonObject(path.join(path.resolve(root), ".alp", "settings.json"), "INVALID_SETTINGS").catch(() => ({}));
212
- if (settings.defaultAgent === name) users.add("project settings");
213
- for (const [owner, targets] of Object.entries(object(settings.delegation) ? settings.delegation : {})) if (owner === name || Array.isArray(targets) && targets.includes(name)) users.add("project delegation");
214
- }
215
- return [...users].sort();
216
- }
217
- const rows = await libraryEntries(kind, root, { library, templates });
218
- for (const user of rows.find((row) => row.name === name)?.usedBy ?? []) users.add(`agent ${user}`);
219
- return [...users].sort();
220
- }
221
- async function check(kind, name, content, { scope, root, library, templates }) {
222
- if (!object(content)) fail("INVALID_ENTRY", `${kind} '${name}': content must be an object`);
223
- const where = `${scope} ${kind} '${name}'`;
224
- const visible = scope === "project" ? { root, library, templates } : { library, templates };
225
- const text = (key, required) => {
226
- if (content[key] === void 0 && !required) return;
227
- if (typeof content[key] !== "string" || required && !content[key].trim()) fail("INVALID_ENTRY", `${where}: ${key} must be ${required ? "nonempty " : ""}text`);
228
- };
229
- const only = (keys) => {
230
- for (const key of Object.keys(content)) if (!keys.includes(key)) fail("INVALID_ENTRY", `${where}: unknown field '${key}'; expected ${keys.join(", ")}`);
231
- };
232
- if (kind === "agents") {
233
- only(["instructions", "config"]);
234
- text("instructions", true);
235
- if (content.config !== void 0) {
236
- validateAgentConfig(content.config, `${where} agent.json`);
237
- for (const [list, entries] of [["skills", "skills"], ["mcp", "mcp"], ["hooks", "hooks"]]) {
238
- if (!content.config[list]?.length) continue;
239
- const known = new Set((await libraryEntries(entries, visible.root, visible)).map((row) => row.name));
240
- for (const entry of content.config[list]) if (!known.has(entry)) fail("NOT_FOUND", `${where}: ${list} names '${entry}', which is not in the ${scope === "project" ? "project or the library" : "library"}`);
241
- }
242
- const provider = content.config.provider;
243
- if (provider !== void 0 && !["codex", "claude"].includes(provider) && !(await libraryEntries("providers", void 0, { library })).some((row) => row.name === provider)) {
244
- fail("NOT_FOUND", `${where}: provider '${provider}' is neither codex, claude nor an ACP provider in your library`);
245
- }
246
- }
247
- } else if (kind === "skills") {
248
- only(["body"]);
249
- text("body", true);
250
- } else if (kind === "mcp") {
251
- only(["server"]);
252
- normalizeMcp({ mcpServers: { [name]: content.server } }, base(scope, { root, library }, kind), where);
253
- } else if (kind === "hooks") {
254
- only(["hook"]);
255
- validateHook(content.hook, where);
256
- } else if (kind === "providers") {
257
- only(["provider"]);
258
- validateProvider(content.provider, where, name);
259
- } else {
260
- only(["team", "houseRules"]);
261
- text("houseRules", false);
262
- validateTeam(content.team, `${where} team.json`);
263
- const agents = await agentSources(visible.root, visible);
264
- const supervisor = content.team.supervisor ? [content.team.supervisor.agent] : [];
265
- for (const agent of [...Object.keys(content.team.members), ...supervisor]) if (!agents.has(agent)) fail("NOT_FOUND", `${where}: agent '${agent}' is not built in, nor in the ${scope === "project" ? "project or the library" : "library"}`);
266
- }
267
- }
268
- async function writeAtomic(file, text) {
269
- await mkdir(path.dirname(file), { recursive: true });
270
- const temporary = `${file}.${randomUUID().slice(0, 8)}.tmp`;
271
- await writeFile(temporary, text);
272
- await moveFile(temporary, file);
273
- }
274
- var exists = (target) => stat(target).then(() => true, () => false);
275
- async function saveEntry(kind, name, content, { scope, root, library, templates, revision, policy } = {}) {
276
- kindOf(kind);
277
- nameOf(name);
278
- if (policy === "project-data") {
279
- assertProjectDataContent(kind, content);
280
- await projectDataEntry(kind, name, { scope, root, library, templates });
281
- }
282
- const directory = base(scope, { root, library }, kind);
283
- await safeEntryPaths(directory, kind, name);
284
- await check(kind, name, content, { scope, root, library, templates });
285
- const current = await readLayer(kind, name, directory);
286
- if (revision !== void 0 && revision !== revisionOf(current)) {
287
- fail("REVISION_CONFLICT", current === void 0 ? `${kind} '${name}' was removed from the ${scope} since you opened it` : revision === null ? `${kind} '${name}' already exists in the ${scope}` : `${kind} '${name}' changed in the ${scope} since you opened it; reload it and apply your change again`);
288
- }
289
- if (scope === "library") await mkdir(directory, { recursive: true, mode: 448 });
290
- for (const [file, key, type] of FILES[kind](name)) {
291
- const target = path.join(directory, kind, file);
292
- const value = content[key];
293
- const empty = value === void 0 || type === "json" && key === "config" && !Object.keys(value).length || type === "text" && key === "houseRules" && !value.trim();
294
- if (empty) {
295
- await rm(target, { force: true });
296
- continue;
297
- }
298
- await writeAtomic(target, type === "json" ? JSON.stringify(value, null, 2) + "\n" : value.endsWith("\n") ? value : `${value}
299
- `);
300
- }
301
- return { revision: revisionOf(await readLayer(kind, name, directory)) };
302
- }
303
- async function deleteEntry(kind, name, { scope, root, library, templates, revision, policy } = {}) {
304
- kindOf(kind);
305
- nameOf(name);
306
- if (policy === "project-data") await projectDataEntry(kind, name, { scope, root, library, templates });
307
- const directory = base(scope, { root, library }, kind);
308
- const current = await readLayer(kind, name, directory);
309
- if (current === void 0) fail("NOT_FOUND", `No ${kind} entry '${name}' in the ${scope}`);
310
- if (revision !== void 0 && revision !== revisionOf(current)) fail("REVISION_CONFLICT", `${kind} '${name}' changed in the ${scope} since you opened it`);
311
- const found = await layers(kind, name, { root: scope === "project" ? root : void 0, library: scope === "project" ? library : void 0, templates });
312
- const fallback = scope === "project" ? found.library ?? found.builtin : found.builtin;
313
- if (fallback === void 0) {
314
- const users = await usersOf(kind, name, { root, library, templates });
315
- if (users.length) fail("IN_USE", `${kind} '${name}' is used by ${users.join(", ")}; change them first`);
316
- }
317
- if (ownDirectory(kind)) await rm(path.join(directory, kind, name), { recursive: true, force: true });
318
- else for (const [file] of FILES[kind](name)) await rm(path.join(directory, kind, file), { force: true });
319
- const now = scope === "project" && found.library !== void 0 ? "library" : "builtin";
320
- return { removed: true, ...fallback !== void 0 ? { now } : {} };
321
- }
322
- async function duplicateEntry(kind, from, to, { scope, root, library, templates, policy } = {}) {
323
- kindOf(kind);
324
- nameOf(from);
325
- nameOf(to);
326
- if (policy === "project-data") {
327
- await projectDataEntry(kind, from, { scope, root, library, templates });
328
- await projectDataEntry(kind, to, { scope, root, library, templates });
329
- }
330
- const { content } = await getEntry(kind, from, { root, library, templates });
331
- return saveEntry(kind, to, structuredClone(content), { scope, root, library, templates, revision: null, policy });
332
- }
333
- async function renameEntry(kind, from, to, { scope, root, library, templates, policy } = {}) {
334
- kindOf(kind);
335
- nameOf(from);
336
- nameOf(to);
337
- if (policy === "project-data") {
338
- await projectDataEntry(kind, from, { scope, root, library, templates });
339
- await projectDataEntry(kind, to, { scope, root, library, templates });
340
- }
341
- const directory = base(scope, { root, library }, kind);
342
- const content = await readLayer(kind, from, directory);
343
- if (content === void 0) fail("NOT_FOUND", `No ${kind} entry '${from}' in the ${scope}`);
344
- if (await readLayer(kind, to, directory) !== void 0) fail("REVISION_CONFLICT", `${kind} '${to}' already exists in the ${scope}`);
345
- const users = await usersOf(kind, from, { root, library, templates });
346
- if (users.length) fail("IN_USE", `${kind} '${from}' is used by ${users.join(", ")}; change them first`);
347
- await check(kind, to, content, { scope, root, library, templates });
348
- if (ownDirectory(kind)) {
349
- if (await exists(path.join(directory, kind, to))) fail("REVISION_CONFLICT", `${path.join(directory, kind, to)} already exists`);
350
- await rename(path.join(directory, kind, from), path.join(directory, kind, to));
351
- } else {
352
- await rename(path.join(directory, kind, `${from}.json`), path.join(directory, kind, `${to}.json`));
353
- }
354
- return { revision: revisionOf(await readLayer(kind, to, directory)) };
355
- }
356
- export {
357
- EDIT_KINDS,
358
- assertProjectDataContent,
359
- deleteEntry,
360
- duplicateEntry,
361
- getEntry,
362
- givenSkills,
363
- listEntries,
364
- renameEntry,
365
- saveEntry,
366
- setGivenSkills,
367
- usersOf
368
- };
@@ -1,28 +0,0 @@
1
- /**
2
- * The user's library: skills, role assignments and default providers, kept in
3
- * ALP_HOME and shared by every project. ALP seeds it from its templates once,
4
- * then updates only files the user has not changed; a file the user edited or
5
- * deleted stays that way across app updates.
6
- */
7
- /** ALP_HOME, the user's ALP directory: alpd's home and the skill library. */
8
- export function alpHome(env?: NodeJS.ProcessEnv): string;
9
- /**
10
- * Seeds or updates the library in `home`. A file is written when ALP never
11
- * shipped it before, and replaced only while it still holds what ALP last wrote.
12
- * @param {string} home
13
- * @param {{ templateRoot?: URL, templates?: Record<string, string> }} [options]
14
- */
15
- export function seedLibrary(home: string, options?: {
16
- templateRoot?: URL;
17
- templates?: Record<string, string>;
18
- }): Promise<{
19
- created: string[];
20
- updated: string[];
21
- }>;
22
- /** Seeds a library once per process; sessions call it on every open. */
23
- export function ensureLibrary(home: any, options?: {}): any;
24
- /** The library skills assigned to an agent, as named absolute SKILL.md paths that exist. */
25
- export function librarySkills(home: any, agent: any): Promise<{
26
- name: any;
27
- path: string;
28
- }[]>;
@@ -1,108 +0,0 @@
1
- // packages/core/src/library.js
2
- import { shippedTemplateRoot } from "./template-path.js";
3
- import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
4
- import { moveFile } from "./atomic.js";
5
- import { createHash, randomUUID } from "node:crypto";
6
- import os from "node:os";
7
- import path from "node:path";
8
- import { AlpError } from "./errors.js";
9
- function alpHome(env = process.env) {
10
- return path.resolve(env.ALP_HOME || path.join(os.homedir(), ".alp"));
11
- }
12
- var MANIFEST = "library.json";
13
- var sha = (text) => createHash("sha256").update(text.replaceAll("\r\n", "\n")).digest("hex");
14
- async function optional(file) {
15
- try {
16
- return await readFile(file, "utf8");
17
- } catch (error) {
18
- if (error.code === "ENOENT") return void 0;
19
- throw error;
20
- }
21
- }
22
- async function shippedFiles({ templateRoot, templates }) {
23
- if (templates) return Object.fromEntries(Object.entries(templates).filter(([name]) => name === "role-skills.json" || name.startsWith("skills/") || name.startsWith("providers/")));
24
- const root = templateRoot ?? shippedTemplateRoot();
25
- const files = { "role-skills.json": await readFile(new URL("role-skills.json", root), "utf8") };
26
- async function walk(relative) {
27
- for (const entry of await readdir(new URL(`${relative}/`, root), { withFileTypes: true })) {
28
- if (entry.isDirectory()) await walk(`${relative}/${entry.name}`);
29
- else if (entry.isFile()) files[`${relative}/${entry.name}`] = await readFile(new URL(`${relative}/${entry.name}`, root), "utf8");
30
- }
31
- }
32
- await walk("skills");
33
- await walk("providers");
34
- return files;
35
- }
36
- async function seedLibrary(home, options = {}) {
37
- const shipped = await shippedFiles(options);
38
- await mkdir(home, { recursive: true, mode: 448 });
39
- const manifestPath = path.join(home, MANIFEST);
40
- const manifestText = await optional(manifestPath);
41
- const manifest = manifestText ? JSON.parse(manifestText) : { files: {} };
42
- const created = [];
43
- const updated = [];
44
- for (const [name, content] of Object.entries(shipped).sort(([a], [b]) => a.localeCompare(b))) {
45
- const target = path.join(home, ...name.split("/"));
46
- const current = await optional(target);
47
- const seeded = manifest.files[name];
48
- if (seeded === void 0) {
49
- if (current !== void 0) continue;
50
- await mkdir(path.dirname(target), { recursive: true });
51
- await writeFile(target, content, { flag: "wx" }).catch((error) => {
52
- if (error.code !== "EEXIST") throw error;
53
- });
54
- manifest.files[name] = sha(content);
55
- created.push(name);
56
- } else if (current !== void 0 && sha(current) === seeded && seeded !== sha(content)) {
57
- await writeFile(target, content);
58
- manifest.files[name] = sha(content);
59
- updated.push(name);
60
- }
61
- }
62
- if (created.length || updated.length || manifestText === void 0) {
63
- await mkdir(home, { recursive: true });
64
- const temporary = `${manifestPath}.${randomUUID().slice(0, 8)}.tmp`;
65
- await writeFile(temporary, JSON.stringify(manifest, null, 2) + "\n");
66
- await moveFile(temporary, manifestPath);
67
- }
68
- return { created, updated };
69
- }
70
- var seeding = /* @__PURE__ */ new Map();
71
- function ensureLibrary(home, options = {}) {
72
- let done = seeding.get(home);
73
- if (!done) {
74
- done = seedLibrary(home, options).catch((error) => {
75
- seeding.delete(home);
76
- throw error;
77
- });
78
- seeding.set(home, done);
79
- }
80
- return done;
81
- }
82
- var skillName = (name) => typeof name === "string" && /^[\w.-]+$/.test(name) && name !== "." && name !== "..";
83
- async function librarySkills(home, agent) {
84
- const file = path.join(home, "role-skills.json");
85
- const text = await optional(file);
86
- if (text === void 0) return [];
87
- let roles;
88
- try {
89
- roles = JSON.parse(text);
90
- } catch (cause) {
91
- throw new AlpError("INVALID_LIBRARY", `${file}: ${cause.message}`, { cause });
92
- }
93
- if (!roles || typeof roles !== "object" || Array.isArray(roles)) throw new AlpError("INVALID_LIBRARY", `${file}: expected an object of role names to skill lists`);
94
- const names = roles[agent] ?? [];
95
- if (!Array.isArray(names) || !names.every(skillName)) throw new AlpError("INVALID_LIBRARY", `${file}: ${agent} must list skill directory names`);
96
- const skills = [];
97
- for (const name of new Set(names)) {
98
- const skillPath = path.join(home, "skills", name, "SKILL.md");
99
- if (await optional(skillPath) !== void 0) skills.push({ name, path: skillPath });
100
- }
101
- return skills;
102
- }
103
- export {
104
- alpHome,
105
- ensureLibrary,
106
- librarySkills,
107
- seedLibrary
108
- };
@@ -1,37 +0,0 @@
1
- export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
- export const LEVELS: Readonly<Record<LogLevel, number>>;
3
- export const REDACTED: string;
4
- export const OMITTED: string;
5
- export type LogFields = {
6
- code?: string;
7
- error?: unknown;
8
- sessionId?: string;
9
- rootId?: string;
10
- assignmentId?: string;
11
- clientMessageId?: string;
12
- requestId?: string;
13
- agent?: string;
14
- [key: string]: unknown;
15
- };
16
- export type LogRecord = {
17
- time: string;
18
- level: LogLevel;
19
- component: string;
20
- code?: string;
21
- msg: string;
22
- error?: { name?: string; code?: string | number; message: string; stack?: string; cause?: unknown };
23
- [key: string]: unknown;
24
- };
25
- export type Logger = {
26
- component: string;
27
- debug(message: string, fields?: LogFields): void;
28
- info(message: string, fields?: LogFields): void;
29
- warn(message: string, fields?: LogFields): void;
30
- error(message: string, fields?: LogFields): void;
31
- child(extra?: LogFields, component?: string): Logger;
32
- };
33
- export function scrubText(text: unknown, options?: { env?: Record<string, string | undefined>; max?: number }): string;
34
- export function describeError(error: unknown, options?: { env?: Record<string, string | undefined> }): { name?: string; code?: string | number; message: string; stack?: string; cause?: unknown };
35
- export function redact(value: unknown, options?: { env?: Record<string, string | undefined> }): any;
36
- export function createLogger(options: { component: string; level?: LogLevel; context?: LogFields; write?: (line: string) => void; now?: () => Date; env?: Record<string, string | undefined> }): Logger;
37
- export function readLogTail(file: string, options?: { bytes?: number; env?: Record<string, string | undefined> }): Promise<Array<LogRecord | { legacy: string }>>;