fix(coding-agent): add package subcommand help and friendly errors (#1347)

* fix(coding-agent): add package subcommand help and friendly errors

* refactor(coding-agent): simplify package command parsing and dispatch

* fix(coding-agent): add plain git URL examples to install help
This commit is contained in:
Fero 2026-02-07 03:30:28 +01:00 committed by GitHub
parent 2ae668823c
commit f9161c4d4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 241 additions and 81 deletions

View file

@ -1,7 +1,7 @@
import { mkdirSync, readFileSync, realpathSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ENV_AGENT_DIR } from "../src/config.js";
import { main } from "../src/main.js";
@ -12,6 +12,7 @@ describe("package commands", () => {
let packageDir: string;
let originalCwd: string;
let originalAgentDir: string | undefined;
let originalExitCode: typeof process.exitCode;
beforeEach(() => {
tempDir = join(tmpdir(), `pi-package-commands-${Date.now()}-${Math.random().toString(36).slice(2)}`);
@ -24,12 +25,15 @@ describe("package commands", () => {
originalCwd = process.cwd();
originalAgentDir = process.env[ENV_AGENT_DIR];
originalExitCode = process.exitCode;
process.exitCode = undefined;
process.env[ENV_AGENT_DIR] = agentDir;
process.chdir(projectDir);
});
afterEach(() => {
process.chdir(originalCwd);
process.exitCode = originalExitCode;
if (originalAgentDir === undefined) {
delete process.env[ENV_AGENT_DIR];
} else {
@ -64,4 +68,53 @@ describe("package commands", () => {
const removedSettings = JSON.parse(readFileSync(settingsPath, "utf-8")) as { packages?: string[] };
expect(removedSettings.packages ?? []).toHaveLength(0);
});
it("shows install subcommand help", async () => {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
await expect(main(["install", "--help"])).resolves.toBeUndefined();
const stdout = logSpy.mock.calls.map(([message]) => String(message)).join("\n");
expect(stdout).toContain("Usage:");
expect(stdout).toContain("pi install <source> [-l]");
expect(errorSpy).not.toHaveBeenCalled();
expect(process.exitCode).toBeUndefined();
} finally {
logSpy.mockRestore();
errorSpy.mockRestore();
}
});
it("shows a friendly error for unknown install options", async () => {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
await expect(main(["install", "--unknown"])).resolves.toBeUndefined();
const stderr = errorSpy.mock.calls.map(([message]) => String(message)).join("\n");
expect(stderr).toContain('Unknown option --unknown for "install".');
expect(stderr).toContain('Use "pi --help" or "pi install <source> [-l]".');
expect(process.exitCode).toBe(1);
} finally {
errorSpy.mockRestore();
}
});
it("shows a friendly error for missing install source", async () => {
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
await expect(main(["install"])).resolves.toBeUndefined();
const stderr = errorSpy.mock.calls.map(([message]) => String(message)).join("\n");
expect(stderr).toContain("Missing install source.");
expect(stderr).toContain("Usage: pi install <source> [-l]");
expect(stderr).not.toContain("at ");
expect(process.exitCode).toBe(1);
} finally {
errorSpy.mockRestore();
}
});
});