diff --git a/docs/plans/state.json b/docs/plans/state.json index e5309fe..94c63c9 100644 --- a/docs/plans/state.json +++ b/docs/plans/state.json @@ -3633,6 +3633,19 @@ "docs/plans/state.json" ], "test_status": "pnpm typecheck passing" + }, + "installer-planner-cli-curl-reconciliation": { + "status": "completed", + "date": "2026-02-17", + "updated": "2026-02-17", + "summary": "Reconciled installer planning and CLI output/tests to use executable curl-based download commands (`curl -fsSL -o ` and `curl -fsSL ` when destination is omitted), replacing legacy placeholder command strings while preserving install/execute flow semantics.", + "files_modified": [ + "src/skills/planner.ts", + "src/skills/planner.test.ts", + "src/cli/skills.test.ts", + "docs/plans/state.json" + ], + "test_status": "pnpm test:run src/skills/planner.test.ts src/cli/skills.test.ts + pnpm typecheck passing" } }, "overall_progress": { diff --git a/src/cli/skills.test.ts b/src/cli/skills.test.ts index f60fa07..8a408a2 100644 --- a/src/cli/skills.test.ts +++ b/src/cli/skills.test.ts @@ -426,7 +426,7 @@ describe('skills CLI helpers', () => { expect(output).toContain('Installer plan mode: dry-run'); expect(output).toContain('Installer planned steps:'); - expect(output).toContain('[download] download https://example.com/tool.tgz -> /tmp/tool.tgz'); + expect(output).toContain('[download] curl -fsSL -o /tmp/tool.tgz https://example.com/tool.tgz'); }); it('builds installer plan view for automation output', () => { @@ -452,13 +452,13 @@ describe('skills CLI helpers', () => { const output = renderSkillInstallerPlan({ skill: { name: 'plan-target', tier: 'bundled', version: '1.0.0' }, mode: 'dry-run', - steps: [{ installerType: 'download', command: 'download https://example.com/tool -> /tmp/tool' }], + steps: [{ installerType: 'download', command: 'curl -fsSL -o /tmp/tool https://example.com/tool' }], skipped: [{ installerType: 'brew', reason: 'brew not available in PATH' }], }); expect(output).toContain("Installer plan for 'plan-target'"); expect(output).toContain('Planned steps:'); - expect(output).toContain('[download] download https://example.com/tool -> /tmp/tool'); + expect(output).toContain('[download] curl -fsSL -o /tmp/tool https://example.com/tool'); expect(output).toContain('Skipped steps:'); expect(output).toContain('[brew] brew not available in PATH'); }); @@ -504,13 +504,13 @@ describe('skills CLI helpers', () => { sourcePath: '/tmp/source-skill', skill: { name: 'preflight-skill', tier: 'managed', version: '1.0.0' }, mode: 'dry-run', - steps: [{ installerType: 'download', command: 'download https://example.com/tool.tgz -> ' }], + steps: [{ installerType: 'download', command: 'curl -fsSL https://example.com/tool.tgz' }], skipped: [], }); expect(output).toContain("Install preflight for 'preflight-skill' from /tmp/source-skill"); expect(output).toContain('Planned installer steps:'); - expect(output).toContain('[download] download https://example.com/tool.tgz -> '); + expect(output).toContain('[download] curl -fsSL https://example.com/tool.tgz'); }); it('builds installer execution stub view from skill plan', () => { @@ -535,16 +535,16 @@ describe('skills CLI helpers', () => { expect(view.attempted.length).toBe(1); expect(view.attempted[0]).toEqual({ installer_type: 'download', - command: expect.stringContaining('download https://example.com/tool.tgz'), + command: expect.stringContaining('curl -fsSL https://example.com/tool.tgz'), }); expect(view.results[0]).toEqual({ installer_type: 'download', - command: expect.stringContaining('download https://example.com/tool.tgz'), + command: expect.stringContaining('curl -fsSL https://example.com/tool.tgz'), status: 'skipped', reason: 'execution_disabled', }); expect(view.wouldRun.length).toBe(1); - expect(view.wouldRun[0]).toContain('download https://example.com/tool.tgz'); + expect(view.wouldRun[0]).toContain('curl -fsSL https://example.com/tool.tgz'); }); it('renders installer execution stub output text', () => { @@ -613,7 +613,7 @@ describe('skills CLI helpers', () => { sourcePath: '/tmp/source-skill', skill: { name: 'exec-stub', tier: 'managed' as const, version: '1.0.0' }, mode: 'dry-run' as const, - steps: [{ installerType: 'download', command: 'download https://example.com/a.tgz -> /tmp/a.tgz' }], + steps: [{ installerType: 'download', command: 'curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz' }], skipped: [], }; @@ -628,18 +628,18 @@ describe('skills CLI helpers', () => { expect(view.attempted).toEqual([ { installer_type: 'download', - command: 'download https://example.com/a.tgz -> /tmp/a.tgz', + command: 'curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz', }, ]); expect(view.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/a.tgz -> /tmp/a.tgz', + command: 'curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz', status: 'skipped', reason: 'execution_disabled', }, ]); - expect(view.wouldRun).toEqual(['download https://example.com/a.tgz -> /tmp/a.tgz']); + expect(view.wouldRun).toEqual(['curl -fsSL -o /tmp/a.tgz https://example.com/a.tgz']); }); it('builds blocked step envelopes when confirmation is required', () => { @@ -720,7 +720,7 @@ describe('skills CLI helpers', () => { results: [ { installer_type: 'download', - command: 'download https://example.com/pkg.tgz -> ', + command: 'curl -fsSL https://example.com/pkg.tgz', status: 'failed', reason: 'allowlist_blocked', }, @@ -738,14 +738,14 @@ describe('skills CLI helpers', () => { ); expect(logger.skillsInstallerCommandResult).toHaveBeenCalledWith( expect.objectContaining({ - command: hashSkillInstallerAuditCommand('download https://example.com/pkg.tgz -> '), + command: hashSkillInstallerAuditCommand('curl -fsSL https://example.com/pkg.tgz'), }), ); expect(logger.skillsInstallerExecutionBlocked).not.toHaveBeenCalled(); }); it('hashes audit command values deterministically for non-sensitive commands', () => { - const command = 'download https://example.com/tool.tgz -> '; + const command = 'curl -fsSL https://example.com/tool.tgz'; expect(hashSkillInstallerAuditCommand(command)).toBe(hashSkillInstallerAuditCommand(command)); expect(hashSkillInstallerAuditCommand(command)).toMatch(/^sha256:[a-f0-9]{64}$/); }); @@ -819,7 +819,7 @@ describe('skills CLI helpers', () => { skill_name: 'audit-skill', phase: 'install', installer_type: 'download', - command: 'download https://example.com/pkg.tgz', + command: 'curl -fsSL https://example.com/pkg.tgz', status: 'failed', reason: 'allowlist_blocked', }, @@ -1550,13 +1550,13 @@ describe('skills CLI helpers', () => { expect(payload.execution.attempted).toEqual([ { installer_type: 'download', - command: 'download https://example.com/plan.tgz -> ', + command: 'curl -fsSL https://example.com/plan.tgz', }, ]); expect(payload.execution.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/plan.tgz -> ', + command: 'curl -fsSL https://example.com/plan.tgz', status: 'skipped', reason: 'execution_disabled', }, @@ -1599,13 +1599,13 @@ describe('skills CLI helpers', () => { expect(payload.execution.attempted).toEqual([ { installer_type: 'download', - command: 'download https://example.com/install.tgz -> ', + command: 'curl -fsSL https://example.com/install.tgz', }, ]); expect(payload.execution.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/install.tgz -> ', + command: 'curl -fsSL https://example.com/install.tgz', status: 'blocked', reason: 'confirmation_required', }, @@ -1645,7 +1645,7 @@ describe('skills CLI helpers', () => { expect(payload.execution.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/install-confirmed.tgz -> ', + command: 'curl -fsSL https://example.com/install-confirmed.tgz', status: 'skipped', reason: 'execution_disabled', }, @@ -1694,11 +1694,11 @@ describe('skills CLI helpers', () => { const payload = JSON.parse(String(logSpy.mock.calls[logSpy.mock.calls.length - 1]?.[0])); expect(payload.execution.execution_enabled).toBe(true); expect(payload.execution.reason).toBe('execution_enabled'); - expect(payload.execution.executed).toEqual(['download https://example.com/install-exec.tgz -> ']); + expect(payload.execution.executed).toEqual(['curl -fsSL https://example.com/install-exec.tgz']); expect(payload.execution.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/install-exec.tgz -> ', + command: 'curl -fsSL https://example.com/install-exec.tgz', status: 'succeeded', reason: 'runner_reported_success', }, @@ -1739,7 +1739,7 @@ describe('skills CLI helpers', () => { expect(runner.run).toHaveBeenCalledTimes(1); const payload = JSON.parse(String(logSpy.mock.calls[0]?.[0])); expect(payload.execution_enabled).toBe(true); - expect(payload.executed).toEqual(['download https://example.com/execute.tgz -> ']); + expect(payload.executed).toEqual(['curl -fsSL https://example.com/execute.tgz']); logSpy.mockRestore(); }); @@ -2286,7 +2286,7 @@ describe('skills CLI helpers', () => { expect(payload.execution.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/cli-install-allowlist-blocked.tgz -> ', + command: 'curl -fsSL https://example.com/cli-install-allowlist-blocked.tgz', status: 'failed', reason: 'allowlist_blocked', }, @@ -2379,7 +2379,7 @@ describe('skills CLI helpers', () => { expect(payload.execution.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/cli-install-no-exec.tgz -> ', + command: 'curl -fsSL https://example.com/cli-install-no-exec.tgz', status: 'skipped', reason: 'execution_disabled', }, @@ -2428,7 +2428,7 @@ describe('skills CLI helpers', () => { expect(payload.execution.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/cli-install-no-confirm.tgz -> ', + command: 'curl -fsSL https://example.com/cli-install-no-confirm.tgz', status: 'blocked', reason: 'confirmation_required', }, @@ -2563,7 +2563,7 @@ describe('skills CLI helpers', () => { expect(payload.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/cli-exec-policy-disabled.tgz -> ', + command: 'curl -fsSL https://example.com/cli-exec-policy-disabled.tgz', status: 'skipped', reason: 'execution_policy_disabled', }, @@ -2613,7 +2613,7 @@ describe('skills CLI helpers', () => { expect(payload.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/cli-exec-no-exec.tgz -> ', + command: 'curl -fsSL https://example.com/cli-exec-no-exec.tgz', status: 'skipped', reason: 'execution_disabled', }, @@ -2763,7 +2763,7 @@ describe('skills CLI helpers', () => { expect(payload.results).toEqual([ { installer_type: 'download', - command: 'download https://example.com/cli-exec-allowlist-blocked.tgz -> ', + command: 'curl -fsSL https://example.com/cli-exec-allowlist-blocked.tgz', status: 'failed', reason: 'allowlist_blocked', }, diff --git a/src/skills/planner.test.ts b/src/skills/planner.test.ts index acf248d..a7fb0bc 100644 --- a/src/skills/planner.test.ts +++ b/src/skills/planner.test.ts @@ -60,15 +60,15 @@ describe('buildInstallerPlan', () => { expect(plan.skipped).toEqual([]); }); - it('skips download installer when destination is missing', () => { + it('plans download installer without destination as stdout curl', () => { const plan = buildInstallerPlan( [{ type: 'download', url: 'https://example.com/tool.tgz' }], { hasBinary: () => true }, ); - expect(plan.steps).toEqual([]); - expect(plan.skipped).toEqual([ - { installerType: 'download', reason: 'download destination is required for executable install plans' }, + expect(plan.steps).toEqual([ + { installerType: 'download', command: 'curl -fsSL https://example.com/tool.tgz' }, ]); + expect(plan.skipped).toEqual([]); }); }); diff --git a/src/skills/planner.ts b/src/skills/planner.ts index fb0efe9..652094f 100644 --- a/src/skills/planner.ts +++ b/src/skills/planner.ts @@ -73,13 +73,11 @@ export function buildInstallerPlan( continue; } - if (!installer.destination) { - skipped.push({ installerType: 'download', reason: 'download destination is required for executable install plans' }); - continue; - } steps.push({ installerType: 'download', - command: `curl -fsSL -o ${installer.destination} ${installer.url}`, + command: installer.destination + ? `curl -fsSL -o ${installer.destination} ${installer.url}` + : `curl -fsSL ${installer.url}`, }); }