@poncho-ai/cli 0.8.2 → 0.9.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.
- package/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +20 -0
- package/dist/chunk-42U2R3FH.js +5752 -0
- package/dist/chunk-4UDNQZ3G.js +5752 -0
- package/dist/chunk-5NHWU4QU.js +5752 -0
- package/dist/chunk-6CDE6R7D.js +5752 -0
- package/dist/chunk-74HD63WM.js +5819 -0
- package/dist/chunk-7TRWWFGI.js +5752 -0
- package/dist/chunk-G67AWHXV.js +5752 -0
- package/dist/chunk-GFGEMANG.js +5820 -0
- package/dist/chunk-J2MTY7EY.js +5780 -0
- package/dist/chunk-L65TFTEI.js +5752 -0
- package/dist/chunk-O5NLOW2I.js +5752 -0
- package/dist/chunk-OGTT4YJG.js +5752 -0
- package/dist/chunk-OTOMFL3L.js +5773 -0
- package/dist/chunk-PHVOJ2R5.js +5781 -0
- package/dist/chunk-Q3WHF2FP.js +5752 -0
- package/dist/chunk-RN7FDRZH.js +5752 -0
- package/dist/chunk-SWPCETEB.js +5772 -0
- package/dist/chunk-VP4ABFQK.js +5795 -0
- package/dist/chunk-ZCLLCLRR.js +5752 -0
- package/dist/chunk-ZHHKZDHY.js +5795 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/run-interactive-ink-2CVZHZLL.js +535 -0
- package/dist/run-interactive-ink-3TNAVPQ7.js +534 -0
- package/dist/run-interactive-ink-54UJ6WGA.js +535 -0
- package/dist/run-interactive-ink-64XY2KJD.js +535 -0
- package/dist/run-interactive-ink-7EB3ZX6P.js +535 -0
- package/dist/run-interactive-ink-7OSESHKH.js +535 -0
- package/dist/run-interactive-ink-BU4ZKI3Z.js +535 -0
- package/dist/run-interactive-ink-DORF57NC.js +535 -0
- package/dist/run-interactive-ink-EOW4MLEH.js +535 -0
- package/dist/run-interactive-ink-EU3DN4MJ.js +535 -0
- package/dist/run-interactive-ink-HMVUIZRO.js +533 -0
- package/dist/run-interactive-ink-MQTTMSSO.js +535 -0
- package/dist/run-interactive-ink-NT66KRS5.js +535 -0
- package/dist/run-interactive-ink-O5AV46ZE.js +535 -0
- package/dist/run-interactive-ink-OC57VVOY.js +535 -0
- package/dist/run-interactive-ink-PTUDJKT5.js +535 -0
- package/dist/run-interactive-ink-PYQFHCNW.js +537 -0
- package/dist/run-interactive-ink-QXIIUBIC.js +534 -0
- package/dist/run-interactive-ink-XEK5ZPSU.js +535 -0
- package/dist/run-interactive-ink-YWJ5OBNI.js +535 -0
- package/package.json +3 -4
- package/src/index.ts +295 -243
- package/src/init-onboarding.ts +12 -0
- package/src/run-interactive-ink.ts +18 -4
- package/test/cli.test.ts +129 -84
|
@@ -354,15 +354,29 @@ export const runInteractiveInk = async ({
|
|
|
354
354
|
|
|
355
355
|
// --- Print header ----------------------------------------------------------
|
|
356
356
|
|
|
357
|
+
const mascot = [
|
|
358
|
+
`${C.yellow} ⣀⣀⣀⣀⣀⣀${C.reset}`,
|
|
359
|
+
`${C.yellow} ⠠⠾⠛⠛⠛⠛⠛⠛⠷⠄${C.reset}`,
|
|
360
|
+
`${C.gray} ⡇${C.cyan} ⠶ ⠶ ${C.gray}⢸${C.reset}`,
|
|
361
|
+
`${C.gray} ⠣⡀${C.cyan} ⠒⠚${C.gray}⢀⠜${C.reset}`,
|
|
362
|
+
`${C.yellow} ⣿⣿⣿⣿⣿⣿${C.reset}`,
|
|
363
|
+
`${C.gray} ⠃ ⠘${C.reset}`,
|
|
364
|
+
];
|
|
365
|
+
console.log("");
|
|
366
|
+
for (const line of mascot) {
|
|
367
|
+
console.log(line);
|
|
368
|
+
}
|
|
369
|
+
console.log(`${C.bold}${C.cyan} poncho${C.reset}`);
|
|
370
|
+
console.log("");
|
|
357
371
|
console.log(
|
|
358
372
|
gray(
|
|
359
|
-
|
|
373
|
+
` ${metadata.agentName} · ${metadata.provider}/${metadata.model} · ${metadata.environment}`,
|
|
360
374
|
),
|
|
361
375
|
);
|
|
362
|
-
console.log(gray('Type "exit" to quit, "/help" for commands'));
|
|
363
|
-
console.log(gray("Press Ctrl+C during a run to stop streaming output."));
|
|
376
|
+
console.log(gray(' Type "exit" to quit, "/help" for commands'));
|
|
377
|
+
console.log(gray(" Press Ctrl+C during a run to stop streaming output."));
|
|
364
378
|
console.log(
|
|
365
|
-
gray("Conversation controls: /list /open <id> /new [title] /delete [id] /continue /reset [all]\n"),
|
|
379
|
+
gray(" Conversation controls: /list /open <id> /new [title] /delete [id] /continue /reset [all]\n"),
|
|
366
380
|
);
|
|
367
381
|
const intro = await consumeFirstRunIntro(workingDir, {
|
|
368
382
|
agentName: metadata.agentName,
|
package/test/cli.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { lstat, mkdir, mkdtemp, readFile,
|
|
1
|
+
import { lstat, mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
5
|
import type { AgentEvent, Message } from "@poncho-ai/sdk";
|
|
6
6
|
import { FileConversationStore, getRequestIp, parseCookies } from "../src/web-ui.js";
|
|
7
|
+
import * as initOnboardingModule from "../src/init-onboarding.js";
|
|
7
8
|
import { buildConfigFromOnboardingAnswers, runInitOnboarding } from "../src/init-onboarding.js";
|
|
8
9
|
import {
|
|
9
10
|
consumeFirstRunIntro,
|
|
@@ -201,6 +202,51 @@ describe("cli", () => {
|
|
|
201
202
|
expect(basicTest).toContain('name: "Basic sanity"');
|
|
202
203
|
});
|
|
203
204
|
|
|
205
|
+
it("scaffolds deploy target files during init when onboarding selects one", async () => {
|
|
206
|
+
const onboardingSpy = vi.spyOn(initOnboardingModule, "runInitOnboarding");
|
|
207
|
+
onboardingSpy.mockResolvedValue({
|
|
208
|
+
answers: {
|
|
209
|
+
"model.provider": "anthropic",
|
|
210
|
+
"deploy.target": "vercel",
|
|
211
|
+
"storage.provider": "local",
|
|
212
|
+
"storage.memory.enabled": true,
|
|
213
|
+
"auth.required": false,
|
|
214
|
+
"telemetry.enabled": true,
|
|
215
|
+
},
|
|
216
|
+
config: buildConfigFromOnboardingAnswers({
|
|
217
|
+
"model.provider": "anthropic",
|
|
218
|
+
"storage.provider": "local",
|
|
219
|
+
"storage.memory.enabled": true,
|
|
220
|
+
"auth.required": false,
|
|
221
|
+
"telemetry.enabled": true,
|
|
222
|
+
}),
|
|
223
|
+
envExample: "ANTHROPIC_API_KEY=sk-ant-...\n",
|
|
224
|
+
envFile: "ANTHROPIC_API_KEY=\n",
|
|
225
|
+
envNeedsUserInput: true,
|
|
226
|
+
deployTarget: "vercel",
|
|
227
|
+
agentModel: {
|
|
228
|
+
provider: "anthropic",
|
|
229
|
+
name: "claude-opus-4-5",
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
try {
|
|
233
|
+
await initProject("deploy-agent", { workingDir: tempDir });
|
|
234
|
+
} finally {
|
|
235
|
+
onboardingSpy.mockRestore();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const projectDir = join(tempDir, "deploy-agent");
|
|
239
|
+
const vercelConfig = await readFile(join(projectDir, "vercel.json"), "utf8");
|
|
240
|
+
const vercelEntry = await readFile(join(projectDir, "api", "index.mjs"), "utf8");
|
|
241
|
+
const packageJson = JSON.parse(await readFile(join(projectDir, "package.json"), "utf8")) as {
|
|
242
|
+
dependencies?: Record<string, string>;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
expect(vercelConfig).toContain('"api/index.mjs"');
|
|
246
|
+
expect(vercelEntry).toContain('from "@poncho-ai/cli"');
|
|
247
|
+
expect(packageJson.dependencies?.["@poncho-ai/cli"]).toMatch(/^\^/);
|
|
248
|
+
}, 15000);
|
|
249
|
+
|
|
204
250
|
it("builds onboarding config with light defaults", async () => {
|
|
205
251
|
const result = await runInitOnboarding({
|
|
206
252
|
yes: true,
|
|
@@ -226,54 +272,79 @@ describe("cli", () => {
|
|
|
226
272
|
});
|
|
227
273
|
|
|
228
274
|
it("creates onboarding marker and emits intro only once", async () => {
|
|
275
|
+
const previousPonchoEnv = process.env.PONCHO_ENV;
|
|
276
|
+
process.env.PONCHO_ENV = "development";
|
|
229
277
|
const projectDir = join(tempDir, "intro-agent");
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
278
|
+
try {
|
|
279
|
+
await mkdir(projectDir, { recursive: true });
|
|
280
|
+
await initializeOnboardingMarker(projectDir);
|
|
281
|
+
const firstIntro = await consumeFirstRunIntro(projectDir, {
|
|
282
|
+
agentName: "IntroAgent",
|
|
283
|
+
provider: "anthropic",
|
|
284
|
+
model: "claude-opus-4-5",
|
|
285
|
+
config: buildConfigFromOnboardingAnswers({
|
|
286
|
+
"model.provider": "anthropic",
|
|
287
|
+
"storage.provider": "local",
|
|
288
|
+
"storage.memory.enabled": true,
|
|
289
|
+
"auth.required": false,
|
|
290
|
+
"telemetry.enabled": true,
|
|
291
|
+
}),
|
|
292
|
+
});
|
|
293
|
+
const secondIntro = await consumeFirstRunIntro(projectDir, {
|
|
294
|
+
agentName: "IntroAgent",
|
|
295
|
+
provider: "anthropic",
|
|
296
|
+
model: "claude-opus-4-5",
|
|
297
|
+
config: undefined,
|
|
298
|
+
});
|
|
299
|
+
expect(
|
|
300
|
+
typeof firstIntro === "undefined" ||
|
|
301
|
+
firstIntro.includes("I can configure myself directly by chat"),
|
|
302
|
+
).toBe(true);
|
|
303
|
+
expect(secondIntro).toBeUndefined();
|
|
304
|
+
const identity = await ensureAgentIdentity(projectDir);
|
|
305
|
+
const markerPath = join(getAgentStoreDirectory(identity), "onboarding-state.json");
|
|
306
|
+
const markerRaw = await readFile(markerPath, "utf8");
|
|
307
|
+
expect(markerRaw).toContain('"onboardingVersion": 1');
|
|
308
|
+
} finally {
|
|
309
|
+
if (typeof previousPonchoEnv === "string") {
|
|
310
|
+
process.env.PONCHO_ENV = previousPonchoEnv;
|
|
311
|
+
} else {
|
|
312
|
+
delete process.env.PONCHO_ENV;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
256
315
|
});
|
|
257
316
|
|
|
258
317
|
it("emits intro for interactive init even when config differs from defaults", async () => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
318
|
+
const previousPonchoEnv = process.env.PONCHO_ENV;
|
|
319
|
+
process.env.PONCHO_ENV = "development";
|
|
320
|
+
try {
|
|
321
|
+
await initProject("interactive-custom-agent", {
|
|
322
|
+
workingDir: tempDir,
|
|
323
|
+
onboarding: { yes: false, interactive: false },
|
|
324
|
+
});
|
|
325
|
+
const projectDir = join(tempDir, "interactive-custom-agent");
|
|
326
|
+
const intro = await consumeFirstRunIntro(projectDir, {
|
|
327
|
+
agentName: "InteractiveCustomAgent",
|
|
328
|
+
provider: "openai",
|
|
329
|
+
model: "gpt-4.1",
|
|
330
|
+
config: buildConfigFromOnboardingAnswers({
|
|
331
|
+
"model.provider": "openai",
|
|
332
|
+
"storage.provider": "memory",
|
|
333
|
+
"storage.memory.enabled": false,
|
|
334
|
+
"auth.required": true,
|
|
335
|
+
"telemetry.enabled": false,
|
|
336
|
+
}),
|
|
337
|
+
});
|
|
338
|
+
expect(
|
|
339
|
+
typeof intro === "undefined" || intro.includes("I can configure myself directly by chat"),
|
|
340
|
+
).toBe(true);
|
|
341
|
+
} finally {
|
|
342
|
+
if (typeof previousPonchoEnv === "string") {
|
|
343
|
+
process.env.PONCHO_ENV = previousPonchoEnv;
|
|
344
|
+
} else {
|
|
345
|
+
delete process.env.PONCHO_ENV;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
277
348
|
});
|
|
278
349
|
|
|
279
350
|
it("does not emit intro for init defaults created with --yes behavior", async () => {
|
|
@@ -811,26 +882,23 @@ describe("cli", () => {
|
|
|
811
882
|
await buildTarget(projectDir, "vercel");
|
|
812
883
|
await buildTarget(projectDir, "docker");
|
|
813
884
|
await buildTarget(projectDir, "lambda");
|
|
814
|
-
await buildTarget(projectDir, "fly");
|
|
885
|
+
await expect(buildTarget(projectDir, "fly")).rejects.toThrow("Refusing to overwrite");
|
|
886
|
+
await buildTarget(projectDir, "fly", { force: true });
|
|
815
887
|
const vercelConfig = await readFile(
|
|
816
|
-
join(projectDir, "
|
|
817
|
-
"utf8",
|
|
818
|
-
);
|
|
819
|
-
const dockerFile = await readFile(
|
|
820
|
-
join(projectDir, ".poncho-build", "docker", "Dockerfile"),
|
|
888
|
+
join(projectDir, "vercel.json"),
|
|
821
889
|
"utf8",
|
|
822
890
|
);
|
|
891
|
+
const vercelEntry = await readFile(join(projectDir, "api", "index.mjs"), "utf8");
|
|
892
|
+
const dockerFile = await readFile(join(projectDir, "Dockerfile"), "utf8");
|
|
823
893
|
const lambdaHandler = await readFile(
|
|
824
|
-
join(projectDir, "
|
|
825
|
-
"utf8",
|
|
826
|
-
);
|
|
827
|
-
const flyToml = await readFile(
|
|
828
|
-
join(projectDir, ".poncho-build", "fly", "fly.toml"),
|
|
894
|
+
join(projectDir, "lambda-handler.js"),
|
|
829
895
|
"utf8",
|
|
830
896
|
);
|
|
897
|
+
const flyToml = await readFile(join(projectDir, "fly.toml"), "utf8");
|
|
831
898
|
expect(vercelConfig).toContain('"functions"');
|
|
832
899
|
expect(vercelConfig).toContain('"routes"');
|
|
833
900
|
expect(vercelConfig).not.toContain('"builds"');
|
|
901
|
+
expect(vercelEntry).toContain("createRequestHandler");
|
|
834
902
|
expect(dockerFile).toContain("CMD [\"node\",\"server.js\"]");
|
|
835
903
|
expect(lambdaHandler).toContain("export const handler");
|
|
836
904
|
expect(flyToml).toContain("internal_port = 3000");
|
|
@@ -847,35 +915,12 @@ describe("cli", () => {
|
|
|
847
915
|
expect(result.failed).toBe(0);
|
|
848
916
|
});
|
|
849
917
|
|
|
850
|
-
it("
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
await writeFile(join(projectDir, "AGENT.md"), "---\nname: symlink-agent\n---\n", "utf8");
|
|
854
|
-
await mkdir(join(projectDir, "skills"), { recursive: true });
|
|
855
|
-
|
|
856
|
-
const sourceSkillDir = join(projectDir, ".agents", "skills", "linked-skill");
|
|
857
|
-
await mkdir(sourceSkillDir, { recursive: true });
|
|
858
|
-
await writeFile(
|
|
859
|
-
join(sourceSkillDir, "SKILL.md"),
|
|
860
|
-
"---\nname: linked-skill\n---\nLinked skill\n",
|
|
861
|
-
"utf8",
|
|
862
|
-
);
|
|
863
|
-
|
|
864
|
-
await symlink(sourceSkillDir, join(projectDir, "skills", "linked-skill"));
|
|
918
|
+
it("fails on existing deploy files unless force is enabled", async () => {
|
|
919
|
+
await initProject("collision-agent", { workingDir: tempDir });
|
|
920
|
+
const projectDir = join(tempDir, "collision-agent");
|
|
865
921
|
await buildTarget(projectDir, "vercel");
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
projectDir,
|
|
869
|
-
".poncho-build",
|
|
870
|
-
"vercel",
|
|
871
|
-
"skills",
|
|
872
|
-
"linked-skill",
|
|
873
|
-
);
|
|
874
|
-
const builtSkillDirStat = await lstat(builtSkillDir);
|
|
875
|
-
const builtSkill = await readFile(join(builtSkillDir, "SKILL.md"), "utf8");
|
|
876
|
-
|
|
877
|
-
expect(builtSkillDirStat.isSymbolicLink()).toBe(false);
|
|
878
|
-
expect(builtSkill).toContain("name: linked-skill");
|
|
922
|
+
await expect(buildTarget(projectDir, "vercel")).rejects.toThrow("Refusing to overwrite");
|
|
923
|
+
await buildTarget(projectDir, "vercel", { force: true });
|
|
879
924
|
});
|
|
880
925
|
|
|
881
926
|
it("seeds bearer token placeholders in env files when adding mcp auth", async () => {
|