@pome-sh/cli 0.4.0 → 0.5.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.
@@ -18,7 +18,6 @@ import { runDocsCommand } from "./docs.js";
18
18
  import { runCompileSeeds } from "./compile-seeds.js";
19
19
  import { runScenariosCommand } from "./scenarios.js";
20
20
  import { runEvalCommand } from "./eval.js";
21
- import { runSkillsInstall } from "./skills.js";
22
21
  import { copyAnnounceLine, ensureDefaultTask, runYoursFrameLines, trialsPinFallbackLine, } from "./default-task.js";
23
22
  import { findTwin, runnableScenarios, } from "./scenarios-catalog.js";
24
23
  import { friendlyHostedError, runSessionCreate, runSessionList, runSessionStop, } from "./session.js";
@@ -141,19 +140,16 @@ export function createProgram() {
141
140
  });
142
141
  program
143
142
  .command("install")
144
- .description("Wire this repo to pome with your own coding agent: checks auth (routes through pome login when needed), runs the wiring headless on your own Claude credentials, shows the full diff in this terminal, and applies it only on [y] — then verifies with pome doctor. --interactive hands off to a live agent session instead; no coding agent on PATH → prints manual wiring steps + a paste-into-any-agent prompt.")
145
- .option("--interactive", "Hand off to an interactive agent session (approve edits in the agent's own UI) instead of the headless staged-diff flow.")
146
- .option("--api-url <url>", "Control-plane base URL.", process.env.POME_API_URL ?? DEFAULT_CONTROL_PLANE_URL)
147
- .option("--dashboard-url <url>", "App URL for Clerk sign-in (must serve /cli/login).", process.env.POME_DASHBOARD_URL ?? DEFAULT_DASHBOARD_URL)
148
- .action(async (opts) => {
149
- // Dynamic import mirrors the doctor/run commands: keep install's
150
- // dependency graph out of every other command's startup path.
143
+ // F-893 the Gen-1 agent-driven wiring is retired; this is a redirect to
144
+ // the Gen-2 path. allowUnknownOption + allowExcessArguments keep old
145
+ // invocations (`pome install --interactive`, `--api-url …`) landing on the
146
+ // redirect instead of erroring on a now-removed flag or stray operand.
147
+ .allowUnknownOption()
148
+ .allowExcessArguments()
149
+ .description("Retired (F-893). Prints the Gen-2 wiring path: `claude mcp add … pome` + `npx skills add pome-sh/digital-twins`, then the pome-intake / REST-launch preflight.")
150
+ .action(async () => {
151
151
  const { runInstall } = await import("./install.js");
152
- await runInstall({
153
- apiUrl: opts.apiUrl,
154
- dashboardUrl: opts.dashboardUrl,
155
- interactive: opts.interactive,
156
- });
152
+ runInstall();
157
153
  });
158
154
  program
159
155
  .command("login")
@@ -211,22 +207,9 @@ export function createProgram() {
211
207
  if (code !== 0)
212
208
  process.exitCode = code;
213
209
  });
214
- const skills = program
215
- .command("skills")
216
- .description("Manage the bundled pome agent skills (/pome-setup, /pome-test)");
217
- skills
218
- .command("install")
219
- .description("Install /pome-setup and /pome-test into ~/.claude/skills/ (symlinked to this pome install by default)")
220
- .option("--copy", "Copy each skill instead of symlinking (CI, Windows without symlink permission)", false)
221
- .option("--force", "Overwrite an existing install of the same skill", false)
222
- .option("--dest <dir>", "Install into <dir> instead of ~/.claude/skills/ (testing and advanced users)")
223
- .action(async (opts) => {
224
- await runSkillsInstall({
225
- copy: opts.copy,
226
- force: opts.force,
227
- dest: opts.dest,
228
- });
229
- });
210
+ // F-893 — `pome skills` / `pome skills install` retired. It only symlinked
211
+ // the two Gen-1 tombstone skills into ~/.claude/skills/; the Gen-2 coach set
212
+ // installs via `npx skills add pome-sh/digital-twins`.
230
213
  const register = program
231
214
  .command("register")
232
215
  .description("Register a cloud entity (agent, ...) and link this project to it");
@@ -11,26 +11,4 @@ interface RegisterAgentOptions extends InteractiveSeams {
11
11
  /** Normalize a `--twins github,slack` comma list to a validated twin array. */
12
12
  export declare function normalizeRegisterTwins(raw: string | undefined): string[] | undefined;
13
13
  export declare function runRegisterAgent(opts: RegisterAgentOptions): Promise<void>;
14
- export interface EnsureAgentRegisteredOptions extends InteractiveSeams {
15
- apiBaseUrl: string;
16
- /** Where to look for the manifest. Defaults to process.cwd(). */
17
- cwd?: string;
18
- /** Test seam — forwarded to resolveCredentials. */
19
- credentialsPath?: string;
20
- }
21
- export type EnsureAgentRegisteredResult = {
22
- status: "registered";
23
- agentId: string;
24
- agentSlug: string;
25
- } | {
26
- status: "already-registered";
27
- agentId: string;
28
- agentSlug?: string;
29
- } | {
30
- status: "no-config";
31
- };
32
- /** Idempotent registration for `pome install`: a repo already linked under the
33
- * caller's team keeps its id (no network, no duplicate); a fresh repo registers
34
- * under the manifest name (or the config directory's basename). */
35
- export declare function ensureAgentRegistered(opts: EnsureAgentRegisteredOptions): Promise<EnsureAgentRegisteredResult>;
36
14
  export { friendlyHostedError };
@@ -19,7 +19,7 @@ import { postAgentResolver, resolveSeams, } from "./agent-resolver.js";
19
19
  import { resolveCredentials } from "./credentials.js";
20
20
  import { suggestFramework } from "./frameworks.js";
21
21
  import { ensurePomeGitignored, readLinkCache, resolveCachedAgentId, writeLinkCache, } from "./link-cache.js";
22
- import { readManifest, readRequiredManifest, writeManifest, } from "./project-config.js";
22
+ import { readRequiredManifest, writeManifest, } from "./project-config.js";
23
23
  import { friendlyHostedError } from "./session.js";
24
24
  const SCHEMA_URL = "https://pome.sh/schemas/v1/pome.json";
25
25
  /** Normalize a `--twins github,slack` comma list to a validated twin array. */
@@ -160,36 +160,6 @@ export async function runRegisterAgent(opts) {
160
160
  console.error("Enabled services: not reported by this pome cloud (older control plane) — twin scoping may not have taken effect.");
161
161
  }
162
162
  }
163
- /** Idempotent registration for `pome install`: a repo already linked under the
164
- * caller's team keeps its id (no network, no duplicate); a fresh repo registers
165
- * under the manifest name (or the config directory's basename). */
166
- export async function ensureAgentRegistered(opts) {
167
- const manifestRead = await readManifest(opts.cwd ?? process.cwd());
168
- if (!manifestRead)
169
- return { status: "no-config" };
170
- const projectDir = dirname(manifestRead.path);
171
- const creds = await resolveCredentials({
172
- apiBaseUrl: opts.apiBaseUrl,
173
- credentialsPath: opts.credentialsPath,
174
- });
175
- const cachedId = resolveCachedAgentId(await readLinkCache(projectDir), creds.teamId);
176
- if (cachedId) {
177
- return {
178
- status: "already-registered",
179
- agentId: cachedId,
180
- agentSlug: manifestRead.manifest.agent.slug,
181
- };
182
- }
183
- const name = manifestRead.manifest.agent.name ?? basename(projectDir);
184
- const agent = await createAndPersistAgent({
185
- creds,
186
- name,
187
- manifestRead,
188
- projectDir,
189
- seams: resolveSeams(opts),
190
- });
191
- return { status: "registered", agentId: agent.id, agentSlug: agent.slug };
192
- }
193
163
  function stripControlCharacters(value) {
194
164
  return value.replace(/[\u0000-\u001f\u007f]/g, "");
195
165
  }
@@ -164,7 +164,7 @@ async function checkRouting(configDir) {
164
164
  cause: `no POME_*_REST_URL / POME_*_MCP_URL read and no @pome-sh adapter found in the ${scan.filesScanned} source file(s) under ${relative(process.cwd(), configDir) || "."} — the agent has no path to the twin.`,
165
165
  fix: [
166
166
  'wire the adapter: import { withPome } from "@pome-sh/adapter-claude-sdk"; call withPome() at startup;',
167
- "read the twin base URL from POME_GITHUB_REST_URL (injected by the runner) — or run pome install to have your coding agent wire it.",
167
+ "read the twin base URL from POME_GITHUB_REST_URL (injected by the runner) — or let your own coding agent wire it with the Gen-2 skills (`npx skills add pome-sh/digital-twins`).",
168
168
  ].join("\n"),
169
169
  };
170
170
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pome-sh/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Digital-twin testing for AI agents — run tasks against resettable local or hosted twins and record tool-call traces for evaluation on pome.sh.",
5
5
  "keywords": [
6
6
  "ai",
@@ -34,7 +34,6 @@
34
34
  "dist",
35
35
  "examples",
36
36
  "scenarios",
37
- "skills",
38
37
  "LICENSE",
39
38
  "package.json",
40
39
  "README.md"
@@ -1,32 +0,0 @@
1
- /** Pinned driver version — bump deliberately, in lockstep with a re-test
2
- * against the oldest Claude Code binary we expect on user machines. */
3
- export declare const AGENT_SDK_VERSION = "0.3.202";
4
- /**
5
- * True when the machine has credentials the Claude Code binary can
6
- * authenticate with on its own: a stored `/login` session (keychain or
7
- * `$CLAUDE_CONFIG_DIR/.credentials.json`) or an explicit env credential.
8
- * Mirrors the SDK's own resolution; pome never reads the secret values.
9
- */
10
- export declare function detectClaudeLogin(env?: Record<string, string | undefined>, home?: string, platform?: NodeJS.Platform): boolean;
11
- /** Directory the pinned SDK driver is provisioned into. */
12
- export declare function agentSdkDir(home?: string): string;
13
- export declare function isAgentSdkProvisioned(dir?: string): boolean;
14
- /** Minimal structural view of the SDK's query() — the driver is loaded at
15
- * runtime, so pome compiles against this shape, not the SDK's types. */
16
- export type AgentSdkQueryFn = (args: {
17
- prompt: string;
18
- options: Record<string, unknown>;
19
- }) => AsyncIterable<Record<string, unknown>>;
20
- export interface AgentSdkModule {
21
- query: AgentSdkQueryFn;
22
- }
23
- /**
24
- * Install the pinned driver into `dir` using whichever package manager is
25
- * on PATH. `--omit=optional` skips the ~244 MB platform runtime — the
26
- * session runs on the user's own `claude` binary instead.
27
- * Returns null (with a printed reason) when no package manager is found
28
- * or the install fails; callers fall back to the interactive session.
29
- */
30
- export declare function provisionAgentSdk(dir?: string): Promise<boolean>;
31
- /** Import the provisioned driver. Returns null when not provisioned. */
32
- export declare function loadAgentSdk(dir?: string): Promise<AgentSdkModule | null>;
@@ -1,105 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // FDRS-661 — Claude Agent SDK access for the embedded (headless) wiring
3
- // session: detect whether the user's own credentials exist, and lazily
4
- // provision the SDK driver into ~/.pome/agent-sdk/<version>.
5
- //
6
- // The SDK is deliberately NOT a dependency of @pome-sh/cli: its per-platform
7
- // optionalDependency bundles the Claude Code runtime (~244 MB unpacked),
8
- // which would sink `npx @pome-sh/cli demo` cold-start. Instead we install the
9
- // driver package alone (--omit=optional, a few MB) on first use, and point
10
- // it at the user's already-installed `claude` binary via
11
- // `pathToClaudeCodeExecutable` — verified against SDK 0.3.202 driving
12
- // Claude Code 2.1.201.
13
- import { spawnSync } from "node:child_process";
14
- import { existsSync } from "node:fs";
15
- import { mkdir, writeFile } from "node:fs/promises";
16
- import { homedir } from "node:os";
17
- import { join } from "node:path";
18
- import { pathToFileURL } from "node:url";
19
- /** Pinned driver version — bump deliberately, in lockstep with a re-test
20
- * against the oldest Claude Code binary we expect on user machines. */
21
- export const AGENT_SDK_VERSION = "0.3.202";
22
- /** macOS keychain service Claude Code stores its `/login` credential under.
23
- * Existence-only probe (no `-w`): the secret is never read or printed. */
24
- const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
25
- /**
26
- * True when the machine has credentials the Claude Code binary can
27
- * authenticate with on its own: a stored `/login` session (keychain or
28
- * `$CLAUDE_CONFIG_DIR/.credentials.json`) or an explicit env credential.
29
- * Mirrors the SDK's own resolution; pome never reads the secret values.
30
- */
31
- export function detectClaudeLogin(env = process.env, home = homedir(), platform = process.platform) {
32
- if (env.ANTHROPIC_API_KEY || env.CLAUDE_CODE_OAUTH_TOKEN || env.ANTHROPIC_AUTH_TOKEN) {
33
- return true;
34
- }
35
- const configDir = env.CLAUDE_CONFIG_DIR || join(home, ".claude");
36
- try {
37
- if (existsSync(join(configDir, ".credentials.json")))
38
- return true;
39
- }
40
- catch {
41
- /* unreadable — treat as absent */
42
- }
43
- if (platform === "darwin") {
44
- try {
45
- const res = spawnSync("security", ["find-generic-password", "-s", CLAUDE_KEYCHAIN_SERVICE], { stdio: "ignore", timeout: 5000 });
46
- if (res.status === 0)
47
- return true;
48
- }
49
- catch {
50
- /* lookup failed — treat as absent */
51
- }
52
- }
53
- return false;
54
- }
55
- /** Directory the pinned SDK driver is provisioned into. */
56
- export function agentSdkDir(home = homedir()) {
57
- return join(home, ".pome", "agent-sdk", AGENT_SDK_VERSION);
58
- }
59
- function sdkEntryPath(dir) {
60
- return join(dir, "node_modules", "@anthropic-ai", "claude-agent-sdk", "sdk.mjs");
61
- }
62
- export function isAgentSdkProvisioned(dir = agentSdkDir()) {
63
- return existsSync(sdkEntryPath(dir));
64
- }
65
- /**
66
- * Install the pinned driver into `dir` using whichever package manager is
67
- * on PATH. `--omit=optional` skips the ~244 MB platform runtime — the
68
- * session runs on the user's own `claude` binary instead.
69
- * Returns null (with a printed reason) when no package manager is found
70
- * or the install fails; callers fall back to the interactive session.
71
- */
72
- export async function provisionAgentSdk(dir = agentSdkDir()) {
73
- await mkdir(dir, { recursive: true });
74
- const manifest = join(dir, "package.json");
75
- if (!existsSync(manifest)) {
76
- await writeFile(manifest, JSON.stringify({ name: "pome-agent-sdk", private: true }, null, 2));
77
- }
78
- const spec = `@anthropic-ai/claude-agent-sdk@${AGENT_SDK_VERSION}`;
79
- const attempts = [
80
- ["npm", ["install", "--omit=optional", "--no-audit", "--no-fund", spec]],
81
- ["bun", ["add", "--omit=optional", spec]],
82
- ];
83
- for (const [bin, args] of attempts) {
84
- const probe = spawnSync(bin, ["--version"], { stdio: "ignore", timeout: 10_000 });
85
- if (probe.error || probe.status !== 0)
86
- continue;
87
- console.error(`downloading the Claude Agent SDK driver (${spec}, a few MB, one-time) …`);
88
- const res = spawnSync(bin, args, { cwd: dir, stdio: "ignore", timeout: 300_000 });
89
- if (res.status === 0 && isAgentSdkProvisioned(dir))
90
- return true;
91
- console.error(`(${bin} install of ${spec} failed)`);
92
- }
93
- console.error("couldn't provision the Claude Agent SDK driver (is npm or bun on PATH?).");
94
- return false;
95
- }
96
- /** Import the provisioned driver. Returns null when not provisioned. */
97
- export async function loadAgentSdk(dir = agentSdkDir()) {
98
- const entry = sdkEntryPath(dir);
99
- if (!existsSync(entry))
100
- return null;
101
- const mod = (await import(pathToFileURL(entry).href));
102
- if (typeof mod.query !== "function")
103
- return null;
104
- return { query: mod.query };
105
- }
@@ -1,47 +0,0 @@
1
- import type { AgentSdkQueryFn } from "./agent-sdk.js";
2
- /** Tools the staging session may use. Everything else — Bash above all —
3
- * is refused: package installs and verification are pome's job, after
4
- * the diff is approved. NOTE: never mirror these into `allowedTools`;
5
- * bare allowedTools entries auto-approve before canUseTool is consulted. */
6
- export declare const STAGING_TOOLS: readonly ["Read", "Edit", "Write", "Glob", "Grep"];
7
- /** First message of the headless session. The pome-setup skill (injected
8
- * into the shadow's .claude/skills) carries the wiring knowledge; this
9
- * adapts its interactive contract to staging: edits land automatically,
10
- * the human approves ONE combined diff in pome's terminal afterwards. */
11
- export declare const EMBEDDED_KICKOFF_PROMPT: string;
12
- export type EmbeddedWiringOutcome = {
13
- kind: "applied";
14
- files: number;
15
- packageJsonChanged: boolean;
16
- } | {
17
- kind: "declined";
18
- } | {
19
- kind: "no-diff";
20
- reason: string;
21
- } | {
22
- kind: "session-error";
23
- reason: string;
24
- };
25
- export interface EmbeddedWiringOptions {
26
- /** The real repo root the approved diff is applied to. */
27
- cwd: string;
28
- /** The user's Claude Code binary — passed as pathToClaudeCodeExecutable. */
29
- claudePath: string;
30
- /** Packaged pome-setup skill directory (contains SKILL.md). */
31
- skillSourceDir: string;
32
- /** The provisioned SDK's query(). Injected — the test seam. */
33
- query: AgentSdkQueryFn;
34
- /** [y/N] prompt. */
35
- confirm: (question: string) => Promise<boolean>;
36
- /** Line sink, default console.error. */
37
- log?: (line: string) => void;
38
- }
39
- export declare function runEmbeddedWiring(options: EmbeddedWiringOptions): Promise<EmbeddedWiringOutcome>;
40
- /** Path-confining permission gate. Exported for direct unit tests. */
41
- export declare function buildStagingCanUseTool(shadowRoot: string, log: (line: string) => void): (toolName: string, input: Record<string, unknown>) => Promise<{
42
- behavior: "allow";
43
- updatedInput: Record<string, unknown>;
44
- } | {
45
- behavior: "deny";
46
- message: string;
47
- }>;