@falkenslab/agent-kit 0.18.0 → 0.20.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.
Files changed (102) hide show
  1. package/assets/extension-launcher.mjs +24 -0
  2. package/dist/chat/chatController.d.ts +267 -0
  3. package/dist/chat/chatController.js +536 -0
  4. package/dist/chat/extensions.d.ts +18 -0
  5. package/dist/chat/extensions.js +44 -0
  6. package/dist/chat/history.d.ts +17 -0
  7. package/dist/chat/history.js +39 -0
  8. package/dist/{tui → chat}/runs.d.ts +11 -1
  9. package/dist/{tui → chat}/runs.js +1 -1
  10. package/dist/core/agentSpec.d.ts +28 -50
  11. package/dist/core/extensions.d.ts +128 -0
  12. package/dist/core/extensions.js +87 -0
  13. package/dist/core/externalExtensions.d.ts +143 -0
  14. package/dist/core/externalExtensions.js +368 -0
  15. package/dist/core/hooks/planGate.d.ts +2 -0
  16. package/dist/core/hooks/planGate.js +1 -10
  17. package/dist/core/hooks/stepGate.d.ts +1 -1
  18. package/dist/core/hooks/stepGate.js +3 -3
  19. package/dist/core/liveExtensions.d.ts +51 -0
  20. package/dist/core/liveExtensions.js +70 -0
  21. package/dist/core/marketplaces.d.ts +118 -0
  22. package/dist/core/marketplaces.js +242 -0
  23. package/dist/core/messages/de.js +12 -32
  24. package/dist/core/messages/en.d.ts +15 -16
  25. package/dist/core/messages/en.js +12 -32
  26. package/dist/core/messages/es.js +12 -32
  27. package/dist/core/messages/fr.js +12 -32
  28. package/dist/core/messages/index.d.ts +6 -0
  29. package/dist/core/messages/index.js +12 -1
  30. package/dist/core/packaged.d.ts +10 -0
  31. package/dist/core/packaged.js +38 -0
  32. package/dist/core/pluginAgents.d.ts +20 -0
  33. package/dist/core/pluginAgents.js +76 -0
  34. package/dist/core/pluginDownloads.d.ts +14 -0
  35. package/dist/core/pluginDownloads.js +141 -0
  36. package/dist/core/runner.js +21 -9
  37. package/dist/core/session.d.ts +38 -6
  38. package/dist/core/session.js +200 -99
  39. package/dist/core/sessionFacts.d.ts +85 -0
  40. package/dist/core/sessionFacts.js +49 -0
  41. package/dist/core/toolLabels.d.ts +24 -6
  42. package/dist/core/toolLabels.js +24 -39
  43. package/dist/extensions/awareness/index.d.ts +22 -0
  44. package/dist/extensions/awareness/index.js +42 -0
  45. package/dist/extensions/awareness/labels.d.ts +3 -0
  46. package/dist/extensions/awareness/labels.js +12 -0
  47. package/dist/extensions/awareness/tools.d.ts +5 -0
  48. package/dist/extensions/awareness/tools.js +54 -0
  49. package/dist/extensions/knowledge/index.d.ts +22 -0
  50. package/dist/extensions/knowledge/index.js +38 -0
  51. package/dist/{core → extensions/knowledge}/knowledgeStore.d.ts +2 -1
  52. package/dist/extensions/knowledge/labels.d.ts +3 -0
  53. package/dist/extensions/knowledge/labels.js +119 -0
  54. package/dist/{core/knowledge.d.ts → extensions/knowledge/prompt.d.ts} +4 -4
  55. package/dist/{core/knowledge.js → extensions/knowledge/prompt.js} +10 -7
  56. package/dist/{core/tools/knowledgeTools.d.ts → extensions/knowledge/tools.d.ts} +1 -1
  57. package/dist/{core/tools/knowledgeTools.js → extensions/knowledge/tools.js} +2 -2
  58. package/dist/extensions/memory/index.d.ts +21 -0
  59. package/dist/extensions/memory/index.js +59 -0
  60. package/dist/extensions/memory/labels.d.ts +3 -0
  61. package/dist/extensions/memory/labels.js +58 -0
  62. package/dist/extensions/memory/memoryStore.d.ts +53 -0
  63. package/dist/extensions/memory/memoryStore.js +88 -0
  64. package/dist/extensions/memory/tools.d.ts +9 -0
  65. package/dist/extensions/memory/tools.js +82 -0
  66. package/dist/extensions/sources/index.d.ts +21 -0
  67. package/dist/extensions/sources/index.js +42 -0
  68. package/dist/extensions/sources/labels.d.ts +3 -0
  69. package/dist/extensions/sources/labels.js +82 -0
  70. package/dist/{core/tools/saveToSources.d.ts → extensions/sources/tools.d.ts} +1 -1
  71. package/dist/{core/tools/saveToSources.js → extensions/sources/tools.js} +5 -5
  72. package/dist/index.d.ts +19 -9
  73. package/dist/index.js +16 -7
  74. package/dist/tui/chatTui.d.ts +11 -20
  75. package/dist/tui/chatTui.js +75 -230
  76. package/dist/tui/extensionCommand.d.ts +30 -0
  77. package/dist/tui/extensionCommand.js +257 -0
  78. package/dist/tui/ink/runChatInk.d.ts +4 -3
  79. package/dist/tui/ink/runChatInk.js +101 -217
  80. package/extensions/awareness/.claude-plugin/plugin.json +10 -0
  81. package/{assets/agent-help → extensions/awareness/skills/help}/SKILL.md +4 -3
  82. package/{assets/knowledge-plugin → extensions/knowledge}/.claude-plugin/plugin.json +7 -1
  83. package/extensions/memory/.claude-plugin/plugin.json +10 -0
  84. package/extensions/memory/commands/forget.md +8 -0
  85. package/extensions/memory/commands/list.md +5 -0
  86. package/extensions/sources/.claude-plugin/plugin.json +10 -0
  87. package/package.json +3 -2
  88. package/dist/core/agentHelp.d.ts +0 -30
  89. package/dist/core/agentHelp.js +0 -55
  90. /package/dist/{core → extensions/knowledge}/fileKnowledgeStore.d.ts +0 -0
  91. /package/dist/{core → extensions/knowledge}/fileKnowledgeStore.js +0 -0
  92. /package/dist/{core → extensions/knowledge}/knowledgeStore.js +0 -0
  93. /package/dist/{core → extensions/sources}/extractText.d.ts +0 -0
  94. /package/dist/{core → extensions/sources}/extractText.js +0 -0
  95. /package/dist/{core → extensions/sources}/sources.d.ts +0 -0
  96. /package/dist/{core → extensions/sources}/sources.js +0 -0
  97. /package/{assets/knowledge-plugin → extensions/knowledge}/commands/ingest.md +0 -0
  98. /package/{assets/knowledge-plugin → extensions/knowledge}/commands/lint.md +0 -0
  99. /package/{assets/knowledge-plugin → extensions/knowledge}/commands/query.md +0 -0
  100. /package/{assets/knowledge-plugin → extensions/knowledge}/skills/knowledge-ingest/SKILL.md +0 -0
  101. /package/{assets/knowledge-plugin → extensions/knowledge}/skills/knowledge-lint/SKILL.md +0 -0
  102. /package/{assets/knowledge-plugin → extensions/knowledge}/skills/knowledge-query/SKILL.md +0 -0
@@ -0,0 +1,118 @@
1
+ /** Where a marketplace's plugin comes from, as `marketplace.json` says (Claude Code's forms). */
2
+ export type PluginSource = string | {
3
+ source: "github";
4
+ repo: string;
5
+ ref?: string;
6
+ sha?: string;
7
+ } | {
8
+ source: "url";
9
+ url: string;
10
+ ref?: string;
11
+ sha?: string;
12
+ } | {
13
+ source: "git-subdir";
14
+ url: string;
15
+ path: string;
16
+ ref?: string;
17
+ sha?: string;
18
+ } | {
19
+ source: string;
20
+ [field: string]: unknown;
21
+ };
22
+ /** A plugin a marketplace offers. */
23
+ export interface MarketplacePlugin {
24
+ name: string;
25
+ source: PluginSource;
26
+ description?: string;
27
+ version?: string;
28
+ category?: string;
29
+ tags?: string[];
30
+ keywords?: string[];
31
+ }
32
+ /** A marketplace's `marketplace.json`, as the kit reads it. */
33
+ export interface MarketplaceManifest {
34
+ name: string;
35
+ owner: {
36
+ name: string;
37
+ email?: string;
38
+ url?: string;
39
+ };
40
+ description?: string;
41
+ /** Where bare plugin sources resolve, relative to the marketplace's root. */
42
+ pluginRoot?: string;
43
+ plugins: MarketplacePlugin[];
44
+ }
45
+ /** A marketplace an agent knows. */
46
+ export interface KnownMarketplace {
47
+ name: string;
48
+ /** Where it came from: a folder, a git URL (`#ref`), or `owner/repo`. */
49
+ source: string;
50
+ /** The commit its copy was taken at, for a git one. */
51
+ commit?: string;
52
+ /** The agent's own: known without asking, and its plugins installed without a confirmation. */
53
+ official: boolean;
54
+ /** When its copy was taken, ISO 8601 in UTC. */
55
+ updated: string;
56
+ /** Its copy, inside the agent's extensions folder. */
57
+ dir: string;
58
+ manifest?: MarketplaceManifest;
59
+ }
60
+ /** Reads and checks a marketplace's `.claude-plugin/marketplace.json`. */
61
+ export declare function readMarketplaceManifest(root: string): Promise<MarketplaceManifest>;
62
+ /** Reads a marketplace from a folder, a git URL (`#ref`) or `owner/repo` without adding it: what to show before asking. */
63
+ export declare function inspectMarketplace(source: string): Promise<MarketplaceManifest>;
64
+ /**
65
+ * Adds a marketplace to the agent (its extensions folder, `dir`) from a folder, a git URL
66
+ * (`#ref`) or `owner/repo`, taking a copy; one with the same name is replaced. Asking the person
67
+ * first is the caller's: the kit's command asks them to type its name, unless it's `official`.
68
+ */
69
+ export declare function addMarketplace(source: string, dir: string, options?: {
70
+ official?: boolean;
71
+ }): Promise<{
72
+ name: string;
73
+ replaced: boolean;
74
+ manifest: MarketplaceManifest;
75
+ commit?: string;
76
+ }>;
77
+ /** The marketplaces the agent knows, with their manifests when they can be read. */
78
+ export declare function listMarketplaces(dir: string): Promise<KnownMarketplace[]>;
79
+ /** Takes a fresh copy of a known marketplace from where it came from. */
80
+ export declare function updateMarketplace(dir: string, name: string): Promise<{
81
+ commit?: string;
82
+ }>;
83
+ /** Forgets a marketplace (its copy and its entry); the extensions installed from it stay. False when it wasn't known. */
84
+ export declare function removeMarketplace(dir: string, name: string): Promise<boolean>;
85
+ /** A plugin found in the known marketplaces: `<name>@<marketplace>`, or a name only one of them offers. */
86
+ export declare function findPlugin(dir: string, spec: string): Promise<{
87
+ marketplace: KnownMarketplace;
88
+ plugin: MarketplacePlugin;
89
+ }>;
90
+ /** Where a plugin comes from: a folder or git repository for `addExtension()`, or a download (npm, archive). */
91
+ export type ResolvedSource = {
92
+ source: string;
93
+ subdir?: string;
94
+ sha?: string;
95
+ } | {
96
+ npm: {
97
+ name: string;
98
+ version?: string;
99
+ registry?: string;
100
+ };
101
+ } | {
102
+ archive: {
103
+ url: string;
104
+ sha256?: string;
105
+ };
106
+ };
107
+ /** Where a plugin's source points: a folder of the marketplace's copy, a git repository, an npm package or a zip. */
108
+ export declare function resolvePluginSource(marketplace: KnownMarketplace, plugin: MarketplacePlugin): ResolvedSource;
109
+ /**
110
+ * Installs `<plugin>@<marketplace>` (or a plugin only one known marketplace offers) into a scope,
111
+ * locked with where it came from. Its name in the agent is its `plugin.json`'s, as for any extension.
112
+ */
113
+ export declare function installFromMarketplace(dir: string, spec: string, scopeDir: string): Promise<{
114
+ name: string;
115
+ replaced: boolean;
116
+ commit?: string;
117
+ marketplace: string;
118
+ }>;
@@ -0,0 +1,242 @@
1
+ import { execFile } from "node:child_process";
2
+ import { cp, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { addExtension, isGitSource } from "./externalExtensions.js";
7
+ import { fetchArchivePlugin, fetchNpmPlugin } from "./pluginDownloads.js";
8
+ /**
9
+ * Marketplaces (ADR-025, #29): a repository of extensions is a Claude Code marketplace as it is,
10
+ * a folder or git repository with `.claude-plugin/marketplace.json` listing plugins and where
11
+ * each comes from. An agent knows the marketplaces added to it, kept in its extensions folder
12
+ * (`marketplaces.json`, and a copy of each in `.marketplaces/<name>/`), and installs from them
13
+ * by `<plugin>@<marketplace>` into either scope, through `addExtension()`: the same copy, hash and
14
+ * lock as any extension. Its official marketplace is known without asking; any other is added
15
+ * only by hand, after a warning (the command asks to type its name).
16
+ */
17
+ const exec = promisify(execFile);
18
+ const REGISTRY = "marketplaces.json";
19
+ const COPIES = ".marketplaces";
20
+ const NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
21
+ /** Reads and checks a marketplace's `.claude-plugin/marketplace.json`. */
22
+ export async function readMarketplaceManifest(root) {
23
+ const file = path.join(root, ".claude-plugin", "marketplace.json");
24
+ const raw = JSON.parse(await readFile(file, "utf8").catch(() => {
25
+ throw new Error(`${root} isn't a marketplace: it has no .claude-plugin/marketplace.json.`);
26
+ }));
27
+ const name = raw.name;
28
+ if (typeof name !== "string" || !NAME.test(name) || name.includes(".."))
29
+ throw new Error(`${file}: "name" must be letters, digits, ".", "_" or "-".`);
30
+ const owner = raw.owner;
31
+ if (!owner || typeof owner.name !== "string")
32
+ throw new Error(`${file}: "owner" needs a "name".`);
33
+ if (!Array.isArray(raw.plugins))
34
+ throw new Error(`${file}: "plugins" must be a list.`);
35
+ const metadata = (raw.metadata ?? {});
36
+ const plugins = raw.plugins.map((plugin, index) => {
37
+ if (typeof plugin?.name !== "string" || !NAME.test(plugin.name))
38
+ throw new Error(`${file}: plugin ${index + 1} has no valid "name".`);
39
+ if (plugin.source === undefined)
40
+ throw new Error(`${file}: ${plugin.name} has no "source".`);
41
+ return plugin;
42
+ });
43
+ const description = raw.description ?? metadata.description;
44
+ return { name, owner, ...(description ? { description } : {}), ...(metadata.pluginRoot ? { pluginRoot: metadata.pluginRoot } : {}), plugins };
45
+ }
46
+ async function readRegistry(dir) {
47
+ try {
48
+ return JSON.parse(await readFile(path.join(dir, REGISTRY), "utf8"));
49
+ }
50
+ catch (error) {
51
+ if (error.code === "ENOENT")
52
+ return {};
53
+ throw error;
54
+ }
55
+ }
56
+ async function writeRegistry(dir, registry) {
57
+ await mkdir(dir, { recursive: true });
58
+ const sorted = Object.fromEntries(Object.keys(registry).sort().map((name) => [name, registry[name]]));
59
+ await writeFile(path.join(dir, REGISTRY), `${JSON.stringify(sorted, null, 2)}\n`, "utf8");
60
+ }
61
+ /** A git URL for a marketplace or plugin source: `owner/repo` is GitHub's. */
62
+ function gitUrl(source) {
63
+ return /^[\w.-]+\/[\w.-]+$/.test(source) ? `https://github.com/${source}.git` : source.replace(/^github:/, "https://github.com/");
64
+ }
65
+ /** Whether a marketplace source is a git repository rather than a folder (`owner/repo` included, unless such a folder exists). */
66
+ async function isGitMarketplace(source) {
67
+ const [location] = source.split("#");
68
+ if (isGitSource(location))
69
+ return true;
70
+ if (!/^[\w.-]+\/[\w.-]+$/.test(location))
71
+ return false;
72
+ return !(await stat(path.resolve(location)).then((found) => found.isDirectory(), () => false));
73
+ }
74
+ /** Takes a copy of a marketplace (a folder, or a git clone at its ref) into a temporary folder. */
75
+ async function fetchMarketplace(source) {
76
+ const copy = await mkdtemp(path.join(os.tmpdir(), "agent-kit-marketplace-"));
77
+ try {
78
+ if (await isGitMarketplace(source)) {
79
+ const [location, ref] = source.split("#");
80
+ await exec("git", ["clone", "--depth", "1", ...(ref ? ["--branch", ref] : []), gitUrl(location), copy]);
81
+ const commit = (await exec("git", ["-C", copy, "rev-parse", "HEAD"])).stdout.trim();
82
+ await rm(path.join(copy, ".git"), { recursive: true, force: true });
83
+ return { copy, commit };
84
+ }
85
+ await cp(path.resolve(source), copy, { recursive: true, filter: (file) => path.basename(file) !== ".git" });
86
+ return { copy };
87
+ }
88
+ catch (error) {
89
+ await rm(copy, { recursive: true, force: true });
90
+ throw error;
91
+ }
92
+ }
93
+ /** Reads a marketplace from a folder, a git URL (`#ref`) or `owner/repo` without adding it: what to show before asking. */
94
+ export async function inspectMarketplace(source) {
95
+ const { copy } = await fetchMarketplace((await isGitMarketplace(source)) ? source : path.resolve(source));
96
+ try {
97
+ return await readMarketplaceManifest(copy);
98
+ }
99
+ finally {
100
+ await rm(copy, { recursive: true, force: true });
101
+ }
102
+ }
103
+ /**
104
+ * Adds a marketplace to the agent (its extensions folder, `dir`) from a folder, a git URL
105
+ * (`#ref`) or `owner/repo`, taking a copy; one with the same name is replaced. Asking the person
106
+ * first is the caller's: the kit's command asks them to type its name, unless it's `official`.
107
+ */
108
+ export async function addMarketplace(source, dir, options = {}) {
109
+ const location = (await isGitMarketplace(source)) ? source : path.resolve(source);
110
+ const { copy, commit } = await fetchMarketplace(location);
111
+ try {
112
+ const manifest = await readMarketplaceManifest(copy);
113
+ const registry = await readRegistry(dir);
114
+ const replaced = Boolean(registry[manifest.name]);
115
+ const target = path.join(dir, COPIES, manifest.name);
116
+ await rm(target, { recursive: true, force: true });
117
+ await mkdir(path.dirname(target), { recursive: true });
118
+ await cp(copy, target, { recursive: true });
119
+ registry[manifest.name] = { source: location, ...(commit ? { commit } : {}), official: Boolean(options.official || registry[manifest.name]?.official), updated: new Date().toISOString() };
120
+ await writeRegistry(dir, registry);
121
+ return { name: manifest.name, replaced, manifest, ...(commit ? { commit } : {}) };
122
+ }
123
+ finally {
124
+ await rm(copy, { recursive: true, force: true });
125
+ }
126
+ }
127
+ /** The marketplaces the agent knows, with their manifests when they can be read. */
128
+ export async function listMarketplaces(dir) {
129
+ const registry = await readRegistry(dir);
130
+ return Promise.all(Object.entries(registry).map(async ([name, entry]) => {
131
+ const copy = path.join(dir, COPIES, name);
132
+ const manifest = await readMarketplaceManifest(copy).catch(() => undefined);
133
+ return { name, ...entry, dir: copy, ...(manifest ? { manifest } : {}) };
134
+ }));
135
+ }
136
+ /** Takes a fresh copy of a known marketplace from where it came from. */
137
+ export async function updateMarketplace(dir, name) {
138
+ const known = (await readRegistry(dir))[name];
139
+ if (!known)
140
+ throw new Error(`No marketplace ${name}.`);
141
+ const result = await addMarketplace(known.source, dir, { official: known.official });
142
+ if (result.name !== name)
143
+ throw new Error(`${known.source} is now the marketplace ${result.name}, not ${name}.`);
144
+ return result.commit ? { commit: result.commit } : {};
145
+ }
146
+ /** Forgets a marketplace (its copy and its entry); the extensions installed from it stay. False when it wasn't known. */
147
+ export async function removeMarketplace(dir, name) {
148
+ const registry = await readRegistry(dir);
149
+ if (!registry[name])
150
+ return false;
151
+ delete registry[name];
152
+ await rm(path.join(dir, COPIES, name), { recursive: true, force: true });
153
+ await writeRegistry(dir, registry);
154
+ return true;
155
+ }
156
+ /** A plugin found in the known marketplaces: `<name>@<marketplace>`, or a name only one of them offers. */
157
+ export async function findPlugin(dir, spec) {
158
+ const [name, wanted] = spec.split("@");
159
+ const marketplaces = (await listMarketplaces(dir)).filter((marketplace) => !wanted || marketplace.name === wanted);
160
+ if (wanted && !marketplaces.length)
161
+ throw new Error(`No marketplace ${wanted}.`);
162
+ const found = marketplaces.flatMap((marketplace) => (marketplace.manifest?.plugins ?? []).filter((plugin) => plugin.name === name).map((plugin) => ({ marketplace, plugin })));
163
+ if (!found.length)
164
+ throw new Error(`No marketplace offers ${name}${wanted ? ` (${wanted} doesn't)` : ""}.`);
165
+ if (found.length > 1)
166
+ throw new Error(`${name} is in several marketplaces (${found.map(({ marketplace }) => marketplace.name).join(", ")}): say which, ${name}@<marketplace>.`);
167
+ return found[0];
168
+ }
169
+ /** Where a plugin's source points: a folder of the marketplace's copy, a git repository, an npm package or a zip. */
170
+ export function resolvePluginSource(marketplace, plugin) {
171
+ const { source } = plugin;
172
+ if (typeof source === "string") {
173
+ // From the marketplace's root: `./plugins/x`, or a bare name under `pluginRoot`; never "..", as Claude Code.
174
+ if (source.split(/[\\/]/).includes(".."))
175
+ throw new Error(`${plugin.name}'s source leaves its marketplace: ${source}.`);
176
+ const relative = source.startsWith("./") || source === "." ? source : path.join(marketplace.manifest?.pluginRoot ?? ".", source);
177
+ const root = path.resolve(marketplace.dir);
178
+ const folder = path.resolve(root, relative);
179
+ if (folder !== root && !folder.startsWith(`${root}${path.sep}`))
180
+ throw new Error(`${plugin.name}'s source leaves its marketplace: ${source}.`);
181
+ return { source: folder };
182
+ }
183
+ // Its fields, as written: the union above only names the forms the kit knows.
184
+ const entry = source;
185
+ const ref = typeof entry.ref === "string" ? `#${entry.ref}` : "";
186
+ const sha = typeof entry.sha === "string" ? { sha: entry.sha } : {};
187
+ switch (source.source) {
188
+ case "github":
189
+ return { source: `https://github.com/${String(entry.repo)}.git${ref}`, ...sha };
190
+ case "url":
191
+ return { source: `${String(entry.url)}${ref}`, ...sha };
192
+ case "git-subdir":
193
+ return { source: `${gitUrl(String(entry.url))}${ref}`, subdir: String(entry.path), ...sha };
194
+ case "npm": {
195
+ // "package" may carry its version ("@scope/name@^2.0.0"); "version" wins.
196
+ const spec = String(entry.package);
197
+ const at = spec.lastIndexOf("@");
198
+ const [name, inline] = at > 0 ? [spec.slice(0, at), spec.slice(at + 1)] : [spec, undefined];
199
+ const version = typeof entry.version === "string" ? entry.version : inline;
200
+ return { npm: { name, ...(version ? { version } : {}), ...(typeof entry.registry === "string" ? { registry: entry.registry } : {}) } };
201
+ }
202
+ case "archive":
203
+ return { archive: { url: String(entry.url), ...(typeof entry.sha256 === "string" ? { sha256: entry.sha256 } : {}) } };
204
+ default:
205
+ // command runs a program of the marketplace's on this machine: never.
206
+ throw new Error(`${plugin.name} comes from a "${source.source}" source, which agent-kit doesn't install: only folders of the marketplace, github, url, git-subdir, npm and archive.`);
207
+ }
208
+ }
209
+ /**
210
+ * Installs `<plugin>@<marketplace>` (or a plugin only one known marketplace offers) into a scope,
211
+ * locked with where it came from. Its name in the agent is its `plugin.json`'s, as for any extension.
212
+ */
213
+ export async function installFromMarketplace(dir, spec, scopeDir) {
214
+ const { marketplace, plugin } = await findPlugin(dir, spec);
215
+ const resolved = resolvePluginSource(marketplace, plugin);
216
+ const from = `${plugin.name}@${marketplace.name}`;
217
+ if ("npm" in resolved) {
218
+ const { name, version, registry } = resolved.npm;
219
+ const fetched = await fetchNpmPlugin(name, version, registry);
220
+ try {
221
+ return { ...(await addExtension(fetched.dir, scopeDir, { marketplace: from, origin: `npm:${name}@${fetched.version}` })), marketplace: marketplace.name };
222
+ }
223
+ finally {
224
+ await rm(fetched.dir, { recursive: true, force: true });
225
+ }
226
+ }
227
+ if ("archive" in resolved) {
228
+ const fetched = await fetchArchivePlugin(resolved.archive.url, resolved.archive.sha256);
229
+ try {
230
+ return { ...(await addExtension(fetched.root, scopeDir, { marketplace: from, origin: resolved.archive.url })), marketplace: marketplace.name };
231
+ }
232
+ finally {
233
+ await rm(fetched.dir, { recursive: true, force: true });
234
+ }
235
+ }
236
+ const result = await addExtension(resolved.source, scopeDir, {
237
+ ...(resolved.subdir ? { subdir: resolved.subdir } : {}),
238
+ ...(resolved.sha ? { sha: resolved.sha } : {}),
239
+ marketplace: from,
240
+ });
241
+ return { ...result, marketplace: marketplace.name };
242
+ }
@@ -22,6 +22,18 @@ export const de = {
22
22
  resumeHint: "↑/↓ wählen · Enter fortsetzen · Esc abbrechen",
23
23
  resumeQuestion: "Nummer des Gesprächs, das fortgesetzt werden soll (Enter zum Abbrechen): ",
24
24
  noEarlierRuns: "(keine früheren Gespräche zum Fortsetzen)",
25
+ commandResume: "Ein früheres Gespräch wählen und fortsetzen",
26
+ commandPlan: "In den Planmodus und zurück",
27
+ commandExtensions: "Was läuft, was aus ist und warum; eine ein- oder ausschalten",
28
+ extensionsRunning: (names) => `Aktiv: ${names}`,
29
+ extensionsNone: "(keine Erweiterungen)",
30
+ extensionOff: (name, reason) => `${name} ist aus: ${reason}`,
31
+ extensionsInstalled: "Installiert:",
32
+ extensionSwitched: (name, enabled) => `(${name} ${enabled ? "aktiviert" : "deaktiviert"}, in derselben Sitzung)`,
33
+ extensionToggled: (name, enabled) => `(${name} ${enabled ? "aktiviert" : "deaktiviert"}: die Sitzung wird mit derselben Unterhaltung neu geöffnet)`,
34
+ extensionNotInstalled: (name) => `(${name} ist nicht installiert: /extensions listet sie auf)`,
35
+ extensionsUsage: "(Verwendung: /extensions, /extensions enable <Name>, /extensions disable <Name>)",
36
+ extensionsCantReopen: "(dieser Chat kann seine Sitzung nicht neu öffnen: im Terminal aktivieren oder deaktivieren und neu starten)",
25
37
  currentRun: "(dieses)",
26
38
  resumed: (date) => `(Gespräch vom ${date} fortgesetzt)`,
27
39
  suggestionKey: "Tab",
@@ -91,22 +103,6 @@ export const de = {
91
103
  Task: ["hat {n} Subagenten gestartet", "hat {n} Subagenten gestartet"],
92
104
  mcp__time__current_time: ["Uhrzeit abgefragt", "Uhrzeit {n}-mal abgefragt"],
93
105
  mcp__time__date_math: ["Daten berechnet", "Daten {n}-mal berechnet"],
94
- mcp__sourceFiles__list_sources: ["Quellen geprüft", "Quellen {n}-mal geprüft"],
95
- mcp__sourceFiles__extract_text: ["{n} Dokument gelesen", "{n} Dokumente gelesen"],
96
- mcp__sourceFiles__save_to_sources: ["{n} Quelle gespeichert", "{n} Quellen gespeichert"],
97
- mcp__sourceFiles__download_to_sources: ["{n} Quelle heruntergeladen", "{n} Quellen heruntergeladen"],
98
- mcp__sourceFiles__request_file: ["um {n} Datei gebeten", "um {n} Dateien gebeten"],
99
- mcp__sourceFiles__retire_source: ["{n} Quelle zurückgezogen", "{n} Quellen zurückgezogen"],
100
- mcp__knowledge__knowledge_index: ["Index gelesen", "Index {n}-mal gelesen"],
101
- mcp__knowledge__knowledge_search: ["Wissensbasis durchsucht", "Wissensbasis {n}-mal durchsucht"],
102
- mcp__knowledge__knowledge_read: ["{n} Seite gelesen", "{n} Seiten gelesen"],
103
- mcp__knowledge__knowledge_create: ["{n} Seite angelegt", "{n} Seiten angelegt"],
104
- mcp__knowledge__knowledge_edit: ["{n} Seite bearbeitet", "{n} Seiten bearbeitet"],
105
- mcp__knowledge__knowledge_rewrite: ["{n} Seite neu geschrieben", "{n} Seiten neu geschrieben"],
106
- mcp__knowledge__knowledge_supersede: ["{n} Seite ersetzt", "{n} Seiten ersetzt"],
107
- mcp__knowledge__knowledge_retire: ["{n} Seite zurückgezogen", "{n} Seiten zurückgezogen"],
108
- mcp__knowledge__knowledge_log: ["Protokoll aktualisiert", "Protokoll {n}-mal aktualisiert"],
109
- mcp__knowledge__knowledge_check: ["Wissensbasis geprüft", "Wissensbasis {n}-mal geprüft"],
110
106
  mcp__approvals__ask_human: ["{n} Frage gestellt", "{n} Fragen gestellt"],
111
107
  mcp__approvals__present_plan: ["Plan vorgelegt", "Plan {n}-mal vorgelegt"],
112
108
  },
@@ -120,7 +116,6 @@ export const de = {
120
116
  labels: {
121
117
  askingApproval: (summary) => `Bittet um menschliche Genehmigung: ${summary}`,
122
118
  waitingManual: "Wartet auf einen manuellen Eingriff",
123
- savingToSources: (destination) => `Speichert ${destination} in den Quellen`,
124
119
  checkingTime: "Datum und Uhrzeit abfragen",
125
120
  calculatingDates: "Daten berechnen",
126
121
  reading: (file) => `Liest ${file}`,
@@ -139,21 +134,6 @@ export const de = {
139
134
  applyingSkill: (skill) => `Wendet den Skill "${skill}" an`,
140
135
  aSkill: "einen Skill",
141
136
  updatingTasks: "Aufgabenliste aktualisieren",
142
- listingSources: "Quellen prüfen",
143
- extractingText: (source) => `${source} lesen`,
144
- downloading: (url) => `${url} in die Quellen herunterladen`,
145
- requestingFile: (description) => `Um eine Datei bitten: ${description}`,
146
- retiringSource: (source) => `Quelle ${source} zurückziehen`,
147
- knowledgeIndex: "Index der Wissensbasis lesen",
148
- knowledgeSearch: (query) => `Wissensbasis nach "${query}" durchsuchen`,
149
- knowledgeRead: (page) => `${page} lesen`,
150
- knowledgeCreate: (page) => `${page} anlegen`,
151
- knowledgeEdit: (page) => `${page} bearbeiten`,
152
- knowledgeRewrite: (page) => `${page} neu schreiben`,
153
- knowledgeSupersede: (page) => `${page} als ersetzt markieren`,
154
- knowledgeRetire: (page) => `${page} zurückziehen`,
155
- knowledgeLog: "Protokoll der Wissensbasis aktualisieren",
156
- knowledgeCheck: "Wissensbasis prüfen",
157
137
  askingHuman: (question) => `Frage: ${question}`,
158
138
  presentingPlan: "Plan vorlegen",
159
139
  },
@@ -29,6 +29,21 @@ export interface Messages {
29
29
  resumeHint: string;
30
30
  resumeQuestion: string;
31
31
  noEarlierRuns: string;
32
+ /** What the chat's own commands do, for a view that lists them. */
33
+ commandResume: string;
34
+ commandPlan: string;
35
+ commandExtensions: string;
36
+ /** /extensions (#37): what the session runs with, what's off and why, and enabling or disabling one. */
37
+ extensionsRunning(names: string): string;
38
+ extensionsNone: string;
39
+ extensionOff(name: string, reason: string): string;
40
+ extensionsInstalled: string;
41
+ /** An installed extension turned off or on in the running session (#49). */
42
+ extensionSwitched(name: string, enabled: boolean): string;
43
+ extensionToggled(name: string, enabled: boolean): string;
44
+ extensionNotInstalled(name: string): string;
45
+ extensionsUsage: string;
46
+ extensionsCantReopen: string;
32
47
  currentRun: string;
33
48
  resumed(date: string): string;
34
49
  suggestionKey: string;
@@ -82,7 +97,6 @@ export interface Messages {
82
97
  labels: {
83
98
  askingApproval(summary: string): string;
84
99
  waitingManual: string;
85
- savingToSources(destination: string): string;
86
100
  checkingTime: string;
87
101
  calculatingDates: string;
88
102
  reading(file: string): string;
@@ -101,21 +115,6 @@ export interface Messages {
101
115
  applyingSkill(skill: string): string;
102
116
  aSkill: string;
103
117
  updatingTasks: string;
104
- listingSources: string;
105
- extractingText(source: string): string;
106
- downloading(url: string): string;
107
- requestingFile(description: string): string;
108
- retiringSource(source: string): string;
109
- knowledgeIndex: string;
110
- knowledgeSearch(query: string): string;
111
- knowledgeRead(page: string): string;
112
- knowledgeCreate(page: string): string;
113
- knowledgeEdit(page: string): string;
114
- knowledgeRewrite(page: string): string;
115
- knowledgeSupersede(page: string): string;
116
- knowledgeRetire(page: string): string;
117
- knowledgeLog: string;
118
- knowledgeCheck: string;
119
118
  askingHuman(question: string): string;
120
119
  presentingPlan: string;
121
120
  };
@@ -21,6 +21,18 @@ export const en = {
21
21
  resumeHint: "↑/↓ choose · Enter resume · Esc cancel",
22
22
  resumeQuestion: "Number of the conversation to resume (Enter to cancel): ",
23
23
  noEarlierRuns: "(no earlier conversations to resume)",
24
+ commandResume: "Pick an earlier conversation to go on with",
25
+ commandPlan: "Into plan mode, or back",
26
+ commandExtensions: "What runs, what's off and why; turn one on or off",
27
+ extensionsRunning: (names) => `Running with: ${names}`,
28
+ extensionsNone: "(no extensions)",
29
+ extensionOff: (name, reason) => `${name} is off: it ${reason}`,
30
+ extensionsInstalled: "Installed:",
31
+ extensionSwitched: (name, enabled) => `(${name} ${enabled ? "enabled" : "disabled"}, in this same session)`,
32
+ extensionToggled: (name, enabled) => `(${name} ${enabled ? "enabled" : "disabled"}: the session reopens, with the same conversation)`,
33
+ extensionNotInstalled: (name) => `(${name} isn't installed: /extensions lists them)`,
34
+ extensionsUsage: "(usage: /extensions, /extensions enable <name>, /extensions disable <name>)",
35
+ extensionsCantReopen: "(this chat can't reopen its session: enable or disable it from the terminal, and start again)",
24
36
  currentRun: "(this one)",
25
37
  resumed: (date) => `(resumed the conversation of ${date})`,
26
38
  suggestionKey: "tab",
@@ -90,22 +102,6 @@ export const en = {
90
102
  Task: ["ran {n} subagent", "ran {n} subagents"],
91
103
  mcp__time__current_time: ["checked the time", "checked the time {n} times"],
92
104
  mcp__time__date_math: ["calculated dates", "calculated dates {n} times"],
93
- mcp__sourceFiles__list_sources: ["listed the sources", "listed the sources {n} times"],
94
- mcp__sourceFiles__extract_text: ["read {n} document", "read {n} documents"],
95
- mcp__sourceFiles__save_to_sources: ["saved {n} source", "saved {n} sources"],
96
- mcp__sourceFiles__download_to_sources: ["downloaded {n} source", "downloaded {n} sources"],
97
- mcp__sourceFiles__request_file: ["asked for {n} file", "asked for {n} files"],
98
- mcp__sourceFiles__retire_source: ["retired {n} source", "retired {n} sources"],
99
- mcp__knowledge__knowledge_index: ["read the index", "read the index {n} times"],
100
- mcp__knowledge__knowledge_search: ["searched the knowledge base", "searched the knowledge base {n} times"],
101
- mcp__knowledge__knowledge_read: ["read {n} page", "read {n} pages"],
102
- mcp__knowledge__knowledge_create: ["created {n} page", "created {n} pages"],
103
- mcp__knowledge__knowledge_edit: ["edited {n} page", "edited {n} pages"],
104
- mcp__knowledge__knowledge_rewrite: ["rewrote {n} page", "rewrote {n} pages"],
105
- mcp__knowledge__knowledge_supersede: ["superseded {n} page", "superseded {n} pages"],
106
- mcp__knowledge__knowledge_retire: ["retired {n} page", "retired {n} pages"],
107
- mcp__knowledge__knowledge_log: ["updated the log", "updated the log {n} times"],
108
- mcp__knowledge__knowledge_check: ["checked the knowledge base", "checked the knowledge base {n} times"],
109
105
  mcp__approvals__ask_human: ["asked {n} question", "asked {n} questions"],
110
106
  mcp__approvals__present_plan: ["presented the plan", "presented the plan {n} times"],
111
107
  },
@@ -119,7 +115,6 @@ export const en = {
119
115
  labels: {
120
116
  askingApproval: (summary) => `Asking for human approval: ${summary}`,
121
117
  waitingManual: "Waiting for a human to intervene manually",
122
- savingToSources: (destination) => `Saving ${destination} to the sources`,
123
118
  checkingTime: "Checking the date and time",
124
119
  calculatingDates: "Calculating dates",
125
120
  reading: (file) => `Reading ${file}`,
@@ -138,21 +133,6 @@ export const en = {
138
133
  applyingSkill: (skill) => `Applying the "${skill}" skill`,
139
134
  aSkill: "a skill",
140
135
  updatingTasks: "Updating the task list",
141
- listingSources: "Listing the sources",
142
- extractingText: (source) => `Reading ${source}`,
143
- downloading: (url) => `Downloading ${url} to the sources`,
144
- requestingFile: (description) => `Asking for a file: ${description}`,
145
- retiringSource: (source) => `Retiring the source ${source}`,
146
- knowledgeIndex: "Reading the knowledge base's index",
147
- knowledgeSearch: (query) => `Searching the knowledge base for "${query}"`,
148
- knowledgeRead: (page) => `Reading ${page}`,
149
- knowledgeCreate: (page) => `Creating ${page}`,
150
- knowledgeEdit: (page) => `Editing ${page}`,
151
- knowledgeRewrite: (page) => `Rewriting ${page}`,
152
- knowledgeSupersede: (page) => `Marking ${page} superseded`,
153
- knowledgeRetire: (page) => `Retiring ${page}`,
154
- knowledgeLog: "Updating the knowledge base's log",
155
- knowledgeCheck: "Checking the knowledge base",
156
136
  askingHuman: (question) => `Asking: ${question}`,
157
137
  presentingPlan: "Presenting the plan",
158
138
  },
@@ -22,6 +22,18 @@ export const es = {
22
22
  resumeHint: "↑/↓ elegir · Intro retomar · Esc cancelar",
23
23
  resumeQuestion: "Número de la conversación que retomar (Intro para cancelar): ",
24
24
  noEarlierRuns: "(no hay conversaciones anteriores que retomar)",
25
+ commandResume: "Elegir una conversación anterior para seguir con ella",
26
+ commandPlan: "Entrar en el modo plan, o volver",
27
+ commandExtensions: "Qué funciona, qué no y por qué; activar o desactivar una",
28
+ extensionsRunning: (names) => `Funcionando con: ${names}`,
29
+ extensionsNone: "(ninguna extensión)",
30
+ extensionOff: (name, reason) => `${name} está desactivada: ${reason}`,
31
+ extensionsInstalled: "Instaladas:",
32
+ extensionSwitched: (name, enabled) => `(${name} ${enabled ? "activada" : "desactivada"}, en esta misma sesión)`,
33
+ extensionToggled: (name, enabled) => `(${name} ${enabled ? "activada" : "desactivada"}: la sesión se reabre, con la misma conversación)`,
34
+ extensionNotInstalled: (name) => `(${name} no está instalada: /extensions las lista)`,
35
+ extensionsUsage: "(uso: /extensions, /extensions enable <nombre>, /extensions disable <nombre>)",
36
+ extensionsCantReopen: "(este chat no puede reabrir su sesión: actívala o desactívala desde la terminal y vuelve a empezar)",
25
37
  currentRun: "(esta)",
26
38
  resumed: (date) => `(retomada la conversación del ${date})`,
27
39
  suggestionKey: "tab",
@@ -91,22 +103,6 @@ export const es = {
91
103
  Task: ["lanzó {n} subagente", "lanzó {n} subagentes"],
92
104
  mcp__time__current_time: ["consultó la hora", "consultó la hora {n} veces"],
93
105
  mcp__time__date_math: ["calculó fechas", "calculó fechas {n} veces"],
94
- mcp__sourceFiles__list_sources: ["revisó las fuentes", "revisó las fuentes {n} veces"],
95
- mcp__sourceFiles__extract_text: ["leyó {n} documento", "leyó {n} documentos"],
96
- mcp__sourceFiles__save_to_sources: ["guardó {n} fuente", "guardó {n} fuentes"],
97
- mcp__sourceFiles__download_to_sources: ["descargó {n} fuente", "descargó {n} fuentes"],
98
- mcp__sourceFiles__request_file: ["pidió {n} fichero", "pidió {n} ficheros"],
99
- mcp__sourceFiles__retire_source: ["retiró {n} fuente", "retiró {n} fuentes"],
100
- mcp__knowledge__knowledge_index: ["leyó el índice", "leyó el índice {n} veces"],
101
- mcp__knowledge__knowledge_search: ["buscó en la base de conocimiento", "buscó en la base de conocimiento {n} veces"],
102
- mcp__knowledge__knowledge_read: ["leyó {n} página", "leyó {n} páginas"],
103
- mcp__knowledge__knowledge_create: ["creó {n} página", "creó {n} páginas"],
104
- mcp__knowledge__knowledge_edit: ["editó {n} página", "editó {n} páginas"],
105
- mcp__knowledge__knowledge_rewrite: ["reescribió {n} página", "reescribió {n} páginas"],
106
- mcp__knowledge__knowledge_supersede: ["sustituyó {n} página", "sustituyó {n} páginas"],
107
- mcp__knowledge__knowledge_retire: ["retiró {n} página", "retiró {n} páginas"],
108
- mcp__knowledge__knowledge_log: ["actualizó el registro", "actualizó el registro {n} veces"],
109
- mcp__knowledge__knowledge_check: ["revisó la base de conocimiento", "revisó la base de conocimiento {n} veces"],
110
106
  mcp__approvals__ask_human: ["hizo {n} pregunta", "hizo {n} preguntas"],
111
107
  mcp__approvals__present_plan: ["presentó el plan", "presentó el plan {n} veces"],
112
108
  },
@@ -120,7 +116,6 @@ export const es = {
120
116
  labels: {
121
117
  askingApproval: (summary) => `Pidiendo aprobación humana: ${summary}`,
122
118
  waitingManual: "Esperando a que una persona intervenga a mano",
123
- savingToSources: (destination) => `Guardando ${destination} en las fuentes`,
124
119
  checkingTime: "Consultando la fecha y la hora",
125
120
  calculatingDates: "Calculando fechas",
126
121
  reading: (file) => `Leyendo ${file}`,
@@ -139,21 +134,6 @@ export const es = {
139
134
  applyingSkill: (skill) => `Aplicando la skill "${skill}"`,
140
135
  aSkill: "una skill",
141
136
  updatingTasks: "Actualizando la lista de tareas",
142
- listingSources: "Revisando las fuentes",
143
- extractingText: (source) => `Leyendo ${source}`,
144
- downloading: (url) => `Descargando ${url} a las fuentes`,
145
- requestingFile: (description) => `Pidiendo un fichero: ${description}`,
146
- retiringSource: (source) => `Retirando la fuente ${source}`,
147
- knowledgeIndex: "Leyendo el índice de la base de conocimiento",
148
- knowledgeSearch: (query) => `Buscando "${query}" en la base de conocimiento`,
149
- knowledgeRead: (page) => `Leyendo ${page}`,
150
- knowledgeCreate: (page) => `Creando ${page}`,
151
- knowledgeEdit: (page) => `Editando ${page}`,
152
- knowledgeRewrite: (page) => `Reescribiendo ${page}`,
153
- knowledgeSupersede: (page) => `Marcando ${page} como sustituida`,
154
- knowledgeRetire: (page) => `Retirando ${page}`,
155
- knowledgeLog: "Actualizando el registro de la base de conocimiento",
156
- knowledgeCheck: "Revisando la base de conocimiento",
157
137
  askingHuman: (question) => `Preguntando: ${question}`,
158
138
  presentingPlan: "Presentando el plan",
159
139
  },