@hypercarrier/rarebit 0.1.0-alpha.4 → 0.1.0-alpha.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.
@@ -1,257 +1,59 @@
1
1
  import assert from "node:assert/strict";
2
- import { access, mkdtemp, readFile, rm } from "node:fs/promises";
2
+ import { execFileSync, spawnSync } from "node:child_process";
3
+ import { mkdtemp, rm } from "node:fs/promises";
3
4
  import { tmpdir } from "node:os";
4
- import { dirname, join, resolve } from "node:path";
5
- import { setTimeout as delay } from "node:timers/promises";
6
- import { pathToFileURL } from "node:url";
5
+ import { join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
7
 
8
- import registerPiRarebit from "../src/extension.mjs";
8
+ const root = fileURLToPath(new URL("..", import.meta.url));
9
+ const piVersion = "0.83.0";
10
+ const work = await mkdtemp(join(tmpdir(), "rarebit-pi083-recall-"));
9
11
 
10
- async function waitFor(predicate, label, timeout = 2_000) {
11
- const end = Date.now() + timeout;
12
- while (!predicate()) {
13
- if (Date.now() >= end) throw new Error(`Timed out waiting for ${label}`);
14
- await delay(5);
15
- }
12
+ function run(command, args, cwd = root) {
13
+ return execFileSync(command, args, { cwd, encoding: "utf8" });
16
14
  }
17
15
 
18
- function expectedRecallMarkdown(conversationPath, detailedPath, request) {
19
- return `# Rarebit Recall
20
-
21
- *Use these local private evidence files to recover historical context for this turn.*
22
-
23
- ## How to use this bundle
24
-
25
- 1. Treat **Current request** below as the exact current user request.
26
- 2. Read **Conversation** first for meaning.
27
- 3. Use **Detailed evidence** only for source, Session, branch, or lineage facts.
28
- 4. Answer the current request using this evidence.
29
-
30
- ## Local private evidence files
31
-
32
- **Conversation** — \`rarebit_conversation/v1\`
33
-
34
- \`\`\`text
35
- ${conversationPath}
36
- \`\`\`
37
-
38
- **Detailed evidence** — \`rarebit_message_recall/v1\`
39
-
40
- \`\`\`text
41
- ${detailedPath}
42
- \`\`\`
43
-
44
- ## Current request
45
-
46
- \`\`\`text
47
- ${request}
48
- \`\`\``;
49
- }
50
-
51
- function conversationPathFrom(message) {
52
- const match = message.match(
53
- /\*\*Conversation\*\* — `rarebit_conversation\/v1`\n\n```text\n([^\n]*rarebit-conversation\.json)\n```/,
54
- );
55
- assert.ok(match, "Recall Markdown must contain the fenced conversation path");
56
- return match[1];
57
- }
58
-
59
- function detailedPathFrom(message) {
60
- const match = message.match(
61
- /\*\*Detailed evidence\*\* — `rarebit_message_recall\/v1`\n\n```text\n([^\n]*rarebit-evidence\.json)\n```/,
62
- );
63
- assert.ok(match, "Recall Markdown must contain the fenced detailed path");
64
- return match[1];
65
- }
66
-
67
- const root =
68
- process.env.PI_CODING_AGENT_ROOT ??
69
- resolve(
70
- dirname(process.execPath),
71
- "../lib/node_modules/@earendil-works/pi-coding-agent",
72
- );
73
- const packageJson = JSON.parse(
74
- await readFile(join(root, "package.json"), "utf8"),
75
- );
76
- assert.equal(
77
- packageJson.version,
78
- "0.83.0",
79
- `Pi 0.83.0 is required; found ${packageJson.version}`,
80
- );
81
- const pi = await import(pathToFileURL(join(root, "dist/index.js")).href);
82
- const faux = await import(
83
- pathToFileURL(
84
- join(root, "node_modules/@earendil-works/pi-ai/dist/providers/faux.js"),
85
- ).href
86
- );
87
-
88
- const temp = await mkdtemp(join(tmpdir(), "hc-rarebit-pi083-recall-"));
89
- const sessions = join(temp, "sessions");
90
- const manager = pi.SessionManager.create(temp, sessions);
91
- const runtime = await pi.ModelRuntime.create({ modelsPath: null });
92
- const provider = faux.fauxProvider({
93
- provider: "recall-faux",
94
- models: [{ id: "recall-faux" }],
95
- });
96
- runtime.registerNativeProvider(provider.provider);
97
- let releaseBusy;
98
- let busyEntered;
99
- let fourthContext;
100
- const busyRelease = new Promise((resolve) => {
101
- releaseBusy = resolve;
102
- });
103
- const busyEntry = new Promise((resolve) => {
104
- busyEntered = resolve;
105
- });
106
- provider.setResponses([
107
- faux.fauxAssistantMessage("seed complete"),
108
- faux.fauxAssistantMessage("idle complete"),
109
- async () => {
110
- busyEntered();
111
- return busyRelease.then(() => faux.fauxAssistantMessage("busy complete"));
112
- },
113
- (context) => {
114
- fourthContext = context;
115
- return faux.fauxAssistantMessage("steered complete");
116
- },
117
- ]);
118
- const settings = pi.SettingsManager.inMemory({
119
- compaction: { enabled: false },
120
- });
121
- const loader = new pi.DefaultResourceLoader({
122
- cwd: temp,
123
- agentDir: temp,
124
- settingsManager: settings,
125
- noSkills: true,
126
- noPromptTemplates: true,
127
- noThemes: true,
128
- noContextFiles: true,
129
- extensionFactories: [registerPiRarebit],
130
- });
131
- await loader.reload();
132
- const { session } = await pi.createAgentSession({
133
- cwd: temp,
134
- agentDir: temp,
135
- model: provider.getModel(),
136
- modelRuntime: runtime,
137
- noTools: "all",
138
- resourceLoader: loader,
139
- sessionManager: manager,
140
- settingsManager: settings,
141
- });
142
- const recallDirectories = [];
143
16
  try {
144
- await session.prompt("seed persistent Session");
145
- await session.agent.waitForIdle();
146
- await session.prompt("/rarebit recall exact idle request", {
147
- streamingBehavior: "steer",
17
+ const packed = JSON.parse(
18
+ run("npm", ["pack", "--json", "--ignore-scripts", "--pack-destination", work]),
19
+ )[0];
20
+ const tarball = join(work, packed.filename);
21
+ run("npm", ["init", "-y"], work);
22
+ run(
23
+ "npm",
24
+ [
25
+ "install",
26
+ "--ignore-scripts",
27
+ tarball,
28
+ `@earendil-works/pi-coding-agent@${piVersion}`,
29
+ ],
30
+ work,
31
+ );
32
+ const installedRoot = join(
33
+ work,
34
+ "node_modules",
35
+ "@earendil-works",
36
+ "pi-coding-agent",
37
+ );
38
+ const recall = join(
39
+ work,
40
+ "node_modules",
41
+ "@hypercarrier",
42
+ "rarebit",
43
+ "scripts",
44
+ "e2e-recall-pi0842.mjs",
45
+ );
46
+ const result = spawnSync(process.execPath, [recall], {
47
+ cwd: work,
48
+ encoding: "utf8",
49
+ env: {
50
+ ...process.env,
51
+ PI_CODING_AGENT_ROOT: installedRoot,
52
+ RAREBIT_EXPECTED_PI_VERSION: piVersion,
53
+ },
148
54
  });
149
- await waitFor(
150
- () => provider.state.callCount === 2,
151
- "idle Recall provider call",
152
- );
153
- await session.agent.waitForIdle();
154
- const userEntries = manager
155
- .getBranch()
156
- .filter((entry) => JSON.stringify(entry).includes("exact idle request"));
157
- assert.equal(userEntries.length, 1);
158
- const message = userEntries[0].message ?? userEntries[0];
159
- const idleMessageText = message.content[0].text;
160
- const idleConversationPath = conversationPathFrom(idleMessageText);
161
- const idleDetailedPath = detailedPathFrom(idleMessageText);
162
- assert.equal(
163
- idleMessageText,
164
- expectedRecallMarkdown(
165
- idleConversationPath,
166
- idleDetailedPath,
167
- "exact idle request",
168
- ),
169
- );
170
- recallDirectories.push(dirname(idleConversationPath));
171
- await access(idleConversationPath);
172
- await access(idleDetailedPath);
173
- assert.equal(
174
- manager
175
- .getBranch()
176
- .some(
177
- (entry) =>
178
- entry.customType === "rarebit.recall" ||
179
- entry.message?.customType === "rarebit.recall",
180
- ),
181
- false,
182
- );
183
-
184
- assert.equal(provider.state.callCount, 2);
185
- const queueUpdates = [];
186
- session.subscribe((event) => {
187
- if (event.type === "queue_update") queueUpdates.push(event);
188
- });
189
- const busyRun = session.prompt("busy seed");
190
- await busyEntry;
191
- await session.prompt("/rarebit recall exact busy request", {
192
- streamingBehavior: "steer",
193
- });
194
- await waitFor(
195
- () => queueUpdates.some((event) => event.steering?.length === 1),
196
- "busy Recall steering queue",
197
- );
198
- const queued = queueUpdates.find((event) => event.steering?.length === 1);
199
- assert.equal(queued.steering.length, 1);
200
- assert.equal(queued.followUp?.length ?? 0, 0);
201
- const queuedMessage = queued.steering[0];
202
- const busyMessageText =
203
- typeof queuedMessage === "string"
204
- ? queuedMessage
205
- : (queuedMessage.content?.[0]?.text ?? queuedMessage.content);
206
- const busyConversationPath = conversationPathFrom(busyMessageText);
207
- const busyDetailedPath = detailedPathFrom(busyMessageText);
208
- assert.equal(
209
- busyMessageText,
210
- expectedRecallMarkdown(
211
- busyConversationPath,
212
- busyDetailedPath,
213
- "exact busy request",
214
- ),
215
- );
216
- recallDirectories.push(dirname(busyConversationPath));
217
- await access(busyConversationPath);
218
- await access(busyDetailedPath);
219
- releaseBusy();
220
- await busyRun;
221
- await session.agent.waitForIdle();
222
- assert.equal(provider.state.callCount, 4);
223
- const recallCount = (texts) =>
224
- texts.filter((text) => text === busyMessageText).length;
225
- const fourthUserTexts = (fourthContext?.messages ?? [])
226
- .filter((message) => message.role === "user")
227
- .map((message) => message.content?.[0]?.text ?? "");
228
- assert.equal(recallCount(fourthUserTexts), 1);
229
- const persistedUserTexts = manager
230
- .getBranch()
231
- .filter((entry) => (entry.message ?? entry).role === "user")
232
- .map((entry) => (entry.message ?? entry).content?.[0]?.text ?? "");
233
- assert.equal(recallCount(persistedUserTexts), 1);
234
- assert.equal(
235
- manager
236
- .getBranch()
237
- .some(
238
- (entry) =>
239
- entry.customType === "rarebit.recall" ||
240
- entry.message?.customType === "rarebit.recall",
241
- ),
242
- false,
243
- );
244
- console.log(
245
- `Pi ${packageJson.version} Recall E2E passed: idle and busy steer Markdown messages; no custom receipt.`,
246
- );
55
+ assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
56
+ process.stdout.write(`Pi ${piVersion} Recall E2E passed from the packed artifact.\n`);
247
57
  } finally {
248
- releaseBusy?.();
249
- await Promise.all(
250
- recallDirectories.map((directory) =>
251
- rm(directory, { recursive: true, force: true }),
252
- ),
253
- );
254
- session.dispose();
255
- runtime.unregisterProvider("recall-faux");
256
- await rm(temp, { recursive: true, force: true });
58
+ await rm(work, { recursive: true, force: true });
257
59
  }
@@ -0,0 +1,259 @@
1
+ import assert from "node:assert/strict";
2
+ import { access, mkdtemp, readFile, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { setTimeout as delay } from "node:timers/promises";
6
+ import { fileURLToPath, pathToFileURL } from "node:url";
7
+
8
+ import registerPiRarebit from "../src/extension.mjs";
9
+
10
+ async function waitFor(predicate, label, timeout = 2_000) {
11
+ const end = Date.now() + timeout;
12
+ while (!predicate()) {
13
+ if (Date.now() >= end) throw new Error(`Timed out waiting for ${label}`);
14
+ await delay(5);
15
+ }
16
+ }
17
+
18
+ function expectedRecallMarkdown(conversationPath, detailedPath, request) {
19
+ return `# Rarebit Recall
20
+
21
+ *Use these local private evidence files to recover historical context for this turn.*
22
+
23
+ ## How to use this bundle
24
+
25
+ 1. Treat **Current request** below as the exact current user request.
26
+ 2. Read **Conversation** first for meaning.
27
+ 3. Use **Detailed evidence** only for source, Session, branch, or lineage facts.
28
+ 4. Answer the current request using this evidence.
29
+
30
+ ## Local private evidence files
31
+
32
+ **Conversation** — \`rarebit_conversation/v1\`
33
+
34
+ \`\`\`text
35
+ ${conversationPath}
36
+ \`\`\`
37
+
38
+ **Detailed evidence** — \`rarebit_message_recall/v1\`
39
+
40
+ \`\`\`text
41
+ ${detailedPath}
42
+ \`\`\`
43
+
44
+ ## Current request
45
+
46
+ \`\`\`text
47
+ ${request}
48
+ \`\`\``;
49
+ }
50
+
51
+ function conversationPathFrom(message) {
52
+ const match = message.match(
53
+ /\*\*Conversation\*\* — `rarebit_conversation\/v1`\n\n```text\n([^\n]*rarebit-conversation\.json)\n```/,
54
+ );
55
+ assert.ok(match, "Recall Markdown must contain the fenced conversation path");
56
+ return match[1];
57
+ }
58
+
59
+ function detailedPathFrom(message) {
60
+ const match = message.match(
61
+ /\*\*Detailed evidence\*\* — `rarebit_message_recall\/v1`\n\n```text\n([^\n]*rarebit-evidence\.json)\n```/,
62
+ );
63
+ assert.ok(match, "Recall Markdown must contain the fenced detailed path");
64
+ return match[1];
65
+ }
66
+
67
+ const bundledPiRoot = resolve(
68
+ dirname(fileURLToPath(import.meta.url)),
69
+ "../node_modules/@earendil-works/pi-coding-agent",
70
+ );
71
+ const root =
72
+ process.env.PI_CODING_AGENT_ROOT ??
73
+ bundledPiRoot;
74
+ const expectedPiVersion = process.env.RAREBIT_EXPECTED_PI_VERSION ?? "0.84.2";
75
+ const packageJson = JSON.parse(
76
+ await readFile(join(root, "package.json"), "utf8"),
77
+ );
78
+ assert.equal(
79
+ packageJson.version,
80
+ expectedPiVersion,
81
+ `Pi ${expectedPiVersion} is required; found ${packageJson.version}`,
82
+ );
83
+ const pi = await import(pathToFileURL(join(root, "dist/index.js")).href);
84
+ const faux = await import(
85
+ pathToFileURL(
86
+ join(root, "node_modules/@earendil-works/pi-ai/dist/providers/faux.js"),
87
+ ).href
88
+ );
89
+
90
+ const temp = await mkdtemp(join(tmpdir(), "rarebit-pi0842-recall-"));
91
+ const sessions = join(temp, "sessions");
92
+ const manager = pi.SessionManager.create(temp, sessions);
93
+ const runtime = await pi.ModelRuntime.create({ modelsPath: null });
94
+ const provider = faux.fauxProvider({
95
+ provider: "recall-faux",
96
+ models: [{ id: "recall-faux" }],
97
+ });
98
+ runtime.registerNativeProvider(provider.provider);
99
+ let releaseBusy;
100
+ let busyEntered;
101
+ let fourthContext;
102
+ const busyRelease = new Promise((resolve) => {
103
+ releaseBusy = resolve;
104
+ });
105
+ const busyEntry = new Promise((resolve) => {
106
+ busyEntered = resolve;
107
+ });
108
+ provider.setResponses([
109
+ faux.fauxAssistantMessage("seed complete"),
110
+ faux.fauxAssistantMessage("idle complete"),
111
+ async () => {
112
+ busyEntered();
113
+ return busyRelease.then(() => faux.fauxAssistantMessage("busy complete"));
114
+ },
115
+ (context) => {
116
+ fourthContext = context;
117
+ return faux.fauxAssistantMessage("steered complete");
118
+ },
119
+ ]);
120
+ const settings = pi.SettingsManager.inMemory({
121
+ compaction: { enabled: false },
122
+ });
123
+ const loader = new pi.DefaultResourceLoader({
124
+ cwd: temp,
125
+ agentDir: temp,
126
+ settingsManager: settings,
127
+ noSkills: true,
128
+ noPromptTemplates: true,
129
+ noThemes: true,
130
+ noContextFiles: true,
131
+ extensionFactories: [registerPiRarebit],
132
+ });
133
+ await loader.reload();
134
+ const { session } = await pi.createAgentSession({
135
+ cwd: temp,
136
+ agentDir: temp,
137
+ model: provider.getModel(),
138
+ modelRuntime: runtime,
139
+ noTools: "all",
140
+ resourceLoader: loader,
141
+ sessionManager: manager,
142
+ settingsManager: settings,
143
+ });
144
+ const recallDirectories = [];
145
+ try {
146
+ await session.prompt("seed persistent Session");
147
+ await session.agent.waitForIdle();
148
+ await session.prompt("/rarebit recall exact idle request", {
149
+ streamingBehavior: "steer",
150
+ });
151
+ await waitFor(
152
+ () => provider.state.callCount === 2,
153
+ "idle Recall provider call",
154
+ );
155
+ await session.agent.waitForIdle();
156
+ const userEntries = manager
157
+ .getBranch()
158
+ .filter((entry) => JSON.stringify(entry).includes("exact idle request"));
159
+ assert.equal(userEntries.length, 1);
160
+ const message = userEntries[0].message ?? userEntries[0];
161
+ const idleMessageText = message.content[0].text;
162
+ const idleConversationPath = conversationPathFrom(idleMessageText);
163
+ const idleDetailedPath = detailedPathFrom(idleMessageText);
164
+ assert.equal(
165
+ idleMessageText,
166
+ expectedRecallMarkdown(
167
+ idleConversationPath,
168
+ idleDetailedPath,
169
+ "exact idle request",
170
+ ),
171
+ );
172
+ recallDirectories.push(dirname(idleConversationPath));
173
+ await access(idleConversationPath);
174
+ await access(idleDetailedPath);
175
+ assert.equal(
176
+ manager
177
+ .getBranch()
178
+ .some(
179
+ (entry) =>
180
+ entry.customType === "rarebit.recall" ||
181
+ entry.message?.customType === "rarebit.recall",
182
+ ),
183
+ false,
184
+ );
185
+
186
+ assert.equal(provider.state.callCount, 2);
187
+ const queueUpdates = [];
188
+ session.subscribe((event) => {
189
+ if (event.type === "queue_update") queueUpdates.push(event);
190
+ });
191
+ const busyRun = session.prompt("busy seed");
192
+ await busyEntry;
193
+ await session.prompt("/rarebit recall exact busy request", {
194
+ streamingBehavior: "steer",
195
+ });
196
+ await waitFor(
197
+ () => queueUpdates.some((event) => event.steering?.length === 1),
198
+ "busy Recall steering queue",
199
+ );
200
+ const queued = queueUpdates.find((event) => event.steering?.length === 1);
201
+ assert.equal(queued.steering.length, 1);
202
+ assert.equal(queued.followUp?.length ?? 0, 0);
203
+ const queuedMessage = queued.steering[0];
204
+ const busyMessageText =
205
+ typeof queuedMessage === "string"
206
+ ? queuedMessage
207
+ : (queuedMessage.content?.[0]?.text ?? queuedMessage.content);
208
+ const busyConversationPath = conversationPathFrom(busyMessageText);
209
+ const busyDetailedPath = detailedPathFrom(busyMessageText);
210
+ assert.equal(
211
+ busyMessageText,
212
+ expectedRecallMarkdown(
213
+ busyConversationPath,
214
+ busyDetailedPath,
215
+ "exact busy request",
216
+ ),
217
+ );
218
+ recallDirectories.push(dirname(busyConversationPath));
219
+ await access(busyConversationPath);
220
+ await access(busyDetailedPath);
221
+ releaseBusy();
222
+ await busyRun;
223
+ await session.agent.waitForIdle();
224
+ assert.equal(provider.state.callCount, 4);
225
+ const recallCount = (texts) =>
226
+ texts.filter((text) => text === busyMessageText).length;
227
+ const fourthUserTexts = (fourthContext?.messages ?? [])
228
+ .filter((message) => message.role === "user")
229
+ .map((message) => message.content?.[0]?.text ?? "");
230
+ assert.equal(recallCount(fourthUserTexts), 1);
231
+ const persistedUserTexts = manager
232
+ .getBranch()
233
+ .filter((entry) => (entry.message ?? entry).role === "user")
234
+ .map((entry) => (entry.message ?? entry).content?.[0]?.text ?? "");
235
+ assert.equal(recallCount(persistedUserTexts), 1);
236
+ assert.equal(
237
+ manager
238
+ .getBranch()
239
+ .some(
240
+ (entry) =>
241
+ entry.customType === "rarebit.recall" ||
242
+ entry.message?.customType === "rarebit.recall",
243
+ ),
244
+ false,
245
+ );
246
+ console.log(
247
+ `Pi ${packageJson.version} Recall E2E passed: idle and busy steer Markdown messages; no custom receipt.`,
248
+ );
249
+ } finally {
250
+ releaseBusy?.();
251
+ await Promise.all(
252
+ recallDirectories.map((directory) =>
253
+ rm(directory, { recursive: true, force: true }),
254
+ ),
255
+ );
256
+ session.dispose();
257
+ runtime.unregisterProvider("recall-faux");
258
+ await rm(temp, { recursive: true, force: true });
259
+ }
@@ -13,16 +13,19 @@ const npm = (args, cwd = root) => run("npm", args, cwd);
13
13
  const required = [
14
14
  "package/README.md", "package/RUNBOOK.md", "package/VISUAL-LANGUAGE.md",
15
15
  "package/AGENTS.md", "package/CHANGELOG.md", "package/LICENSE",
16
- "package/SECURITY.md", "package/bin/rarebit.mjs",
17
- "package/src/index.mjs", "package/src/types.d.ts",
16
+ "package/SECURITY.md", "package/bin/rarebit.mjs", "package/bin/piq.mjs",
17
+ "package/src/index.mjs", "package/src/rarebit-fork-lineage.mjs", "package/src/rarebit-fork.mjs", "package/src/types.d.ts",
18
18
  "package/schemas/rarebit.schema.json", "package/test/rarebit-cli.test.mjs",
19
- "package/scripts/e2e-recall-pi083.mjs", "package/scripts/verify-package.mjs",
19
+ "package/scripts/e2e-recall-pi083.mjs", "package/scripts/e2e-recall-pi0842.mjs", "package/scripts/verify-package.mjs",
20
20
  ];
21
21
  const forbiddenPath = /(^|\/)(\.git|node_modules|\.github|hc-rarebit\.mjs)(\/|$)|HyperCarrier|timeline|pi-team/i;
22
- const allowedBare = new Set(["@earendil-works/pi-ai"]);
22
+ const allowedBare = new Set(["@earendil-works/pi-ai", "@earendil-works/pi-coding-agent"]);
23
23
  const packageFor = (specifier) => specifier.startsWith("@") ? specifier.split("/").slice(0, 2).join("/") : specifier.split("/")[0];
24
24
  const packageJson = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
25
- assert.equal(packageJson.version, "0.1.0-alpha.4", "release version mismatch");
25
+ assert.equal(packageJson.version, "0.1.0-alpha.6", "release version mismatch");
26
+ assert.equal(packageJson.peerDependencies?.["@earendil-works/pi-ai"], ">=0.83.0", "Pi AI peer range mismatch");
27
+ for (const peer of ["@earendil-works/pi-ai", "@earendil-works/pi-coding-agent"])
28
+ assert.equal(packageJson.peerDependenciesMeta?.[peer]?.optional, true, `${peer} peer must remain optional`);
26
29
 
27
30
  const packed = JSON.parse(npm(["pack", "--json", "--ignore-scripts"]))[0];
28
31
  const tarball = join(root, packed.filename);
@@ -34,7 +37,7 @@ assert(!entries.includes("package/bin/hc-rarebit.mjs"), "legacy CLI is packed");
34
37
  const sourceFiles = entries.filter((path) => path.endsWith(".mjs") && !path.startsWith("package/test/"));
35
38
  for (const entry of sourceFiles) {
36
39
  const text = run("tar", ["-xOf", tarball, entry]);
37
- for (const match of text.matchAll(/(?:from\s*|import\s*\()\s*["']([^"']+)["']/g)) {
40
+ for (const match of text.matchAll(/(?:\bfrom\s+|import\s*\()\s*["']([^"']+)["']/g)) {
38
41
  const specifier = match[1];
39
42
  if (!specifier.startsWith(".") && !specifier.startsWith("node:") && !allowedBare.has(packageFor(specifier))) {
40
43
  throw new Error(`undeclared runtime import ${specifier} in ${entry}`);
@@ -51,10 +54,10 @@ try {
51
54
  npm(["init", "-y"], temp);
52
55
  npm(["install", "--ignore-scripts", "--omit=dev", tarball], temp);
53
56
  const installed = "@hypercarrier/rarebit";
54
- for (const subpath of ["", "/core", "/session", "/service", "/artifact-state", "/visual-language", "/extension"]) {
57
+ for (const subpath of ["", "/core", "/session", "/service", "/artifact-state", "/fork", "/fork-lineage", "/visual-language", "/extension"]) {
55
58
  run(process.execPath, ["--input-type=module", "-e", `await import(${JSON.stringify(installed + subpath)})`], temp);
56
59
  }
57
- const imports = ["", "/core", "/session", "/service", "/artifact-state", "/visual-language", "/extension"]
60
+ const imports = ["", "/core", "/session", "/service", "/artifact-state", "/fork", "/fork-lineage", "/visual-language", "/extension"]
58
61
  .map((subpath) => `import ${JSON.stringify(installed + subpath)};`).join("\n");
59
62
  await writeFile(join(temp, "imports.mts"), imports);
60
63
  run(join(root, "node_modules/.bin/tsc"), ["--noEmit", "--module", "NodeNext", "--moduleResolution", "NodeNext", "--target", "ES2022", "--skipLibCheck", "imports.mts"], temp);