diff --git a/CHANGELOG.md b/CHANGELOG.md index becea6d..d43d6c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - `agentguard subscribe --cron` now requires a saved agent host when `--cron-target auto` is used; run `agentguard init --agent ` first or pass an explicit cron target. - `agentguard status` now shows the saved agent host when one is configured. - Install and postinstall guidance now recommends `agentguard init --agent `, `agentguard connect`, and `agentguard checkup` as the focused next steps. +- Postinstall now writes persistent next-step guidance to `~/.agentguard/next-steps.txt` and the package directory so agent installers can discover it even when npm hides lifecycle output. - System cron installation now writes and invokes a validated AgentGuard wrapper script instead of embedding config-derived paths directly in crontab. ## [1.1.9] - 2026-05-20 diff --git a/src/cli.ts b/src/cli.ts index b61bcee..1c0c6b8 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -65,10 +65,11 @@ async function main() { console.log(`AgentGuard initialized at ${paths.home}`); console.log(`Config: ${paths.configPath}`); if (options.agent) { - if (!['claude-code', 'codex', 'openclaw', 'hermes', 'qclaw'].includes(options.agent)) { + const normalizedAgent = String(options.agent).trim().toLowerCase(); + if (!['claude-code', 'codex', 'openclaw', 'hermes', 'qclaw'].includes(normalizedAgent)) { throw new Error('Invalid agent. Use claude-code, codex, openclaw, hermes, or qclaw.'); } - const agent = options.agent as AgentInstaller; + const agent = normalizedAgent as AgentInstaller; config.agentHost = agent; saveConfig(config); const result = installAgentTemplates(agent, { force: options.force }); diff --git a/src/postinstall.ts b/src/postinstall.ts index 374dbcd..715aec4 100644 --- a/src/postinstall.ts +++ b/src/postinstall.ts @@ -1,20 +1,43 @@ #!/usr/bin/env node +import { writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; import { ensureConfig, getAgentGuardPaths } from './config.js'; +const NEXT_STEPS = [ + 'Next steps:', + ' agentguard init --agent ', + ' agentguard connect', + ' agentguard checkup', + '', +].join('\n'); + function printNextSteps(): void { - console.log('Next steps:'); - console.log(' agentguard init --agent '); - console.log(' agentguard connect'); - console.log(' agentguard checkup'); + process.stdout.write(NEXT_STEPS); +} + +function writeNextStepsFile(path: string, mode = 0o600): void { + try { + writeFileSync(path, NEXT_STEPS, { mode }); + } catch { + // Installation guidance is best-effort and must never break npm install. + } +} + +function writePackageNextStepsFile(): void { + if (process.env.AGENTGUARD_SKIP_PACKAGE_NEXT_STEPS === '1') return; + const packageDir = resolve(__dirname, '..'); + writeNextStepsFile(resolve(packageDir, 'next-steps.txt'), 0o644); } try { ensureConfig(); const paths = getAgentGuardPaths(); + writeNextStepsFile(resolve(paths.home, 'next-steps.txt')); console.log(`AgentGuard local config ready: ${paths.configPath}`); } catch { // Postinstall must never break package installation. } finally { + writePackageNextStepsFile(); printNextSteps(); } diff --git a/src/tests/cli-init.test.ts b/src/tests/cli-init.test.ts index 589930a..c172817 100644 --- a/src/tests/cli-init.test.ts +++ b/src/tests/cli-init.test.ts @@ -38,4 +38,19 @@ describe('init CLI', () => { assert.equal(config.agentHost, agent); } }); + + it('normalizes --agent values to lowercase', async () => { + const home = mkdtempSync(join(tmpdir(), 'agentguard-init-uppercase-home-')); + const cwd = mkdtempSync(join(tmpdir(), 'agentguard-init-uppercase-cwd-')); + const cliPath = resolve('dist', 'cli.js'); + + const { stdout } = await execFileAsync(process.execPath, [cliPath, 'init', '--agent', 'Hermes', '--force'], { + cwd, + env: { ...process.env, AGENTGUARD_HOME: home }, + }); + + const config = JSON.parse(readFileSync(join(home, 'config.json'), 'utf8')) as { agentHost?: string }; + assert.equal(config.agentHost, 'hermes'); + assert.match(stdout, /Installed hermes template:/); + }); }); diff --git a/src/tests/postinstall.test.ts b/src/tests/postinstall.test.ts index 20a9642..3b9e3da 100644 --- a/src/tests/postinstall.test.ts +++ b/src/tests/postinstall.test.ts @@ -1,7 +1,7 @@ import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { execFile } from 'node:child_process'; -import { mkdtempSync } from 'node:fs'; +import { mkdtempSync, readFileSync } from 'node:fs'; import { join, resolve } from 'node:path'; import { tmpdir } from 'node:os'; import { promisify } from 'node:util'; @@ -14,12 +14,17 @@ describe('postinstall', () => { const postinstallPath = resolve('dist', 'postinstall.js'); const { stdout } = await execFileAsync(process.execPath, [postinstallPath], { - env: { ...process.env, AGENTGUARD_HOME: home }, + env: { ...process.env, AGENTGUARD_HOME: home, AGENTGUARD_SKIP_PACKAGE_NEXT_STEPS: '1' }, }); assert.match(stdout, /AgentGuard local config ready:/); - assert.match(stdout, /agentguard init --agent /); + assert.match(stdout, /agentguard init --agent /); assert.match(stdout, /agentguard connect/); assert.match(stdout, /agentguard checkup/); + + const nextSteps = readFileSync(join(home, 'next-steps.txt'), 'utf8'); + assert.match(nextSteps, /agentguard init --agent /); + assert.match(nextSteps, /agentguard connect/); + assert.match(nextSteps, /agentguard checkup/); }); });