From db157c5370ba2ab3fb9aae00836ca4d6c1752282 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Mon, 7 Sep 2026 22:50:19 -0400 Subject: [PATCH 1/3] fix(agents): make integration checks explicit and recovery scoped --- AGENTS.md | 30 +++++++++++------------- CLAUDE.md | 16 ++++++------- docs/cli.md | 13 +++++++---- docs/integrations.md | 18 +++++++++------ docs/reference/cli.md | 1 + examples/basic/AGENTS.md | 16 ++++++------- examples/basic/CLAUDE.md | 16 ++++++------- src/agents/instruction-source.ts | 12 +++++----- src/agents/integration-revision.ts | 2 +- src/cli/integration-sync.ts | 13 ++++++----- src/commands/agent.ts | 18 ++++++++++++--- src/commands/doctor.ts | 4 ++-- src/commands/recovery-guidance.ts | 4 ++-- tests/agent-instruction-source.test.ts | 5 ++-- tests/doctor-command.test.ts | 24 ++++++++++++++++--- tests/e2e/scenarios/agent-docs-sync.ts | 32 +++++++++++++++++++++----- 16 files changed, 140 insertions(+), 84 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ecaffa79..0e0e7b8a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,7 +57,7 @@ Never use any types and always default to leveraging generics and smart types to - Docs currency is non-negotiable: ANY interface or behavior change (commands, flags, config keys, file layouts, env vars, defaults) must update the affected docs/ pages in the same patch — no matter what. - The CLI reference is generated: after changing the CLI surface, run `bun run docs:cli-reference` and commit `docs/reference/cli.md` (a drift test fails otherwise). -- Agent-facing behavior phrasing lives in `src/agents/instruction-source.ts`; update it (not the generated surfaces) and run `hack setup sync --all-scopes`. +- Agent-facing behavior phrasing lives in `src/agents/instruction-source.ts`; update it and render the affected targets. Use scoped setup commands; a global refresh requires authorization for that scope. - If a change affects `hack run`, `hack exec`, env resolution, runtime-state reconciliation, or lifecycle shell/process semantics, the patch must include both targeted tests and matching docs updates. - For env-sensitive command changes, verify the requested env, effective env, cached runtime-state env, and target-service-running matrix instead of a single happy path. - For lifecycle changes, verify `sh -c` semantics, process-group cleanup, stale pane/process metadata reconciliation, and interactive stdin behavior. @@ -70,15 +70,11 @@ Never use any types and always default to leveraging generics and smart types to - Before adding new branch-heavy logic there, prefer extracting a small helper with a narrow contract and direct tests. - Do not grow top-level command handlers when the real change is a decision table, state transition, or reusable readiness check. -## Landing the Plane (Session Completion) -**When ending a work session**, you MUST complete ALL steps below. - -**MANDATORY WORKFLOW:** -1. **File tickets for remaining work** - Create tickets for anything that needs follow-up -2. **Run quality gates** (if code changed) - Tests, linters, builds -3. **Update issue status** - Close finished work, update in-progress items -4. **Hand off** - Provide context for next session +## Session Completion +Run the relevant quality gates and report the verified result and remaining work. Update tickets +only when tracker changes are authorized; otherwise leave a concrete follow-up in the handoff. +Do not create tickets or handoff artifacts when there is no actionable remaining work. # Ultracite Code Standards @@ -210,11 +206,11 @@ Most formatting and common issues are automatically fixed by Biome. Run `bun x u Use `hack` as the single interface for local-first runtime orchestration (compose, DNS/TLS, logs, env, and persistent project workspaces). Integration freshness: -- These instructions were generated by hack CLI v3.5.2; treat cached rules from another version as potentially stale. -- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`. -- If anything is stale or missing, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced. -- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command. -- Content revision: `994ef1552d14` (version alone is not a freshness guarantee). +- These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. +- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. +- Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. +- Content revision: `140421b48da0` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -368,19 +364,19 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through `hack setup sync --all-scopes --check`, `hack doctor`, and `hack agent prime`. +- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` -- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- CLI upgrades do not authorize integration changes. After an upgrade, refresh affected integrations only when requested or needed for the authorized task. - When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). Agent setup (CLI-first): - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Refresh all local agent integrations: `hack setup sync --all-scopes` +- Explicit full integration refresh: `hack setup sync --all-scopes` - Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` diff --git a/CLAUDE.md b/CLAUDE.md index da80ca0f..ac6311cf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,11 +80,11 @@ This project uses Obsidian for project context, specs, research, and progress tr Use `hack` as the single interface for local-first runtime orchestration (compose, DNS/TLS, logs, env, and persistent project workspaces). Integration freshness: -- These instructions were generated by hack CLI v3.5.2; treat cached rules from another version as potentially stale. -- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`. -- If anything is stale or missing, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced. -- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command. -- Content revision: `994ef1552d14` (version alone is not a freshness guarantee). +- These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. +- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. +- Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. +- Content revision: `140421b48da0` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -238,19 +238,19 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through `hack setup sync --all-scopes --check`, `hack doctor`, and `hack agent prime`. +- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` -- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- CLI upgrades do not authorize integration changes. After an upgrade, refresh affected integrations only when requested or needed for the authorized task. - When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). Agent setup (CLI-first): - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Refresh all local agent integrations: `hack setup sync --all-scopes` +- Explicit full integration refresh: `hack setup sync --all-scopes` - Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` diff --git a/docs/cli.md b/docs/cli.md index 48fabc6b..57121aed 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -58,11 +58,14 @@ See [Beta workflows](beta.md) for guides on this surface. - `NO_COLOR` (or `HACK_NO_COLOR`) disables colored/decorated output. Generated agent docs, Cursor rules, Codex skills, and the shared `~/.ai/skills/hack-cli` skill carry -the Hack CLI version that generated them. Audit both project and global surfaces with -`hack setup sync --all-scopes --check`; repair them with the explicit -`hack setup sync --all-scopes`, then reload the agent session so it stops using cached guidance. -Ordinary commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, -repair, remove, or otherwise mutate these files. +the Hack CLI version that generated them. Plain `hack agent prime` prints current guidance without +an integration scan. Use `hack agent prime --check` for an explicit project/global inventory or a +targeted `hack setup ... --check` when diagnosing drift. Missing optional integrations do not +require installation. Repair only affected targets within the authorized scope; use +`hack setup sync --all-scopes` for an explicitly requested full refresh. Read updated guidance after +repair; restart only if the client cannot reload changed hooks or skills. +Ordinary commands, `hack update`, and `hack doctor --fix` never render, repair, remove, or otherwise +mutate integration files. CLI upgrades do not trigger integration updates. ## First-run path diff --git a/docs/integrations.md b/docs/integrations.md index 5574f8e7..8fbf8f16 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -27,10 +27,14 @@ Recommended replacements: Hack maintains project instructions plus global Cursor, Claude, Codex, and shared `~/.ai/skills` surfaces. Generated guidance identifies the CLI version that rendered it. -- Audit without writing: `hack setup sync --all-scopes --check` -- Repair project and global integrations: `hack setup sync --all-scopes` -- After repair: reload the agent session so cached rules are discarded - -Ordinary commands, `hack update`, and `hack doctor --fix` do not inspect or modify these surfaces. -`hack agent prime` performs a read-only audit at session start and prints a warning before any Hack -operating guidance. +Run checks when Hack is relevant and there is evidence of drift; there is no mandatory startup audit. +Plain `hack agent prime`, including installed SessionStart/PreCompact hooks, prints the current +primer without scanning project or global integrations. Use `hack agent prime --check` for an +explicit read-only inventory, or a targeted `hack setup ... --check` for per-artifact detail. +Inventory findings do not block unrelated work, and missing optional integrations need not be installed. + +Ordinary commands, `hack update`, and `hack doctor --fix` never modify these surfaces. Doctor may +report freshness findings. Repair only the affected integration and scope covered by the request; +`hack setup sync --all-scopes` remains available for an explicitly requested full refresh. +Existing authorization for that action and scope is sufficient. After repair, read the updated +guidance; restart only if the client cannot reload changed hooks or skills. diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 44a3c124..7a9c92de 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1858,6 +1858,7 @@ hack agent prime [options] | Option | Description | | --- | --- | +| `--check` | Include a read-only project and global integration inventory | | `--no-interactive` | Never prompt: apply documented defaults or fail with E_INTERACTIVE_REQUIRED (also via HACK_NO_INTERACTIVE=1) | | `--help, -h` | Show help | | `--version, -v` | Show version | diff --git a/examples/basic/AGENTS.md b/examples/basic/AGENTS.md index be27c1e8..b230ce85 100644 --- a/examples/basic/AGENTS.md +++ b/examples/basic/AGENTS.md @@ -4,11 +4,11 @@ Use `hack` as the single interface for local-first runtime orchestration (compose, DNS/TLS, logs, env, and persistent project workspaces). Integration freshness: -- These instructions were generated by hack CLI v3.5.2; treat cached rules from another version as potentially stale. -- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`. -- If anything is stale or missing, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced. -- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command. -- Content revision: `994ef1552d14` (version alone is not a freshness guarantee). +- These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. +- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. +- Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. +- Content revision: `140421b48da0` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -162,19 +162,19 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through `hack setup sync --all-scopes --check`, `hack doctor`, and `hack agent prime`. +- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` -- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- CLI upgrades do not authorize integration changes. After an upgrade, refresh affected integrations only when requested or needed for the authorized task. - When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). Agent setup (CLI-first): - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Refresh all local agent integrations: `hack setup sync --all-scopes` +- Explicit full integration refresh: `hack setup sync --all-scopes` - Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` diff --git a/examples/basic/CLAUDE.md b/examples/basic/CLAUDE.md index be27c1e8..b230ce85 100644 --- a/examples/basic/CLAUDE.md +++ b/examples/basic/CLAUDE.md @@ -4,11 +4,11 @@ Use `hack` as the single interface for local-first runtime orchestration (compose, DNS/TLS, logs, env, and persistent project workspaces). Integration freshness: -- These instructions were generated by hack CLI v3.5.2; treat cached rules from another version as potentially stale. -- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`. -- If anything is stale or missing, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced. -- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command. -- Content revision: `994ef1552d14` (version alone is not a freshness guarantee). +- These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. +- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. +- Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. +- Content revision: `140421b48da0` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -162,19 +162,19 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through `hack setup sync --all-scopes --check`, `hack doctor`, and `hack agent prime`. +- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` -- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- CLI upgrades do not authorize integration changes. After an upgrade, refresh affected integrations only when requested or needed for the authorized task. - When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). Agent setup (CLI-first): - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Refresh all local agent integrations: `hack setup sync --all-scopes` +- Explicit full integration refresh: `hack setup sync --all-scopes` - Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` diff --git a/src/agents/instruction-source.ts b/src/agents/instruction-source.ts index cd7af368..c80b126d 100644 --- a/src/agents/instruction-source.ts +++ b/src/agents/instruction-source.ts @@ -43,9 +43,9 @@ export const INSTRUCTION_SECTIONS: readonly InstructionSection[] = [ surfaces: ALL_SURFACES, bullets: [ `These instructions were generated by hack CLI v${HACK_AGENT_INTEGRATION_CLI_VERSION}; treat cached rules from another version as potentially stale.`, - "At session start, audit project and global integrations with `hack setup sync --all-scopes --check`.", - "If anything is stale or missing, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced.", - "Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command.", + "Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit.", + "Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh.", + "Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient.", ], }, { @@ -309,12 +309,12 @@ export const INSTRUCTION_SECTIONS: readonly InstructionSection[] = [ bullets: [ "Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files.", "Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config.", - "Read-only freshness checks are available through `hack setup sync --all-scopes --check`, `hack doctor`, and `hack agent prime`.", + "Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations.", "Refresh project + user integrations: `hack setup sync --all-scopes`", "Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review.", "Audit integration state only: `hack setup sync --all-scopes --check`", "Remove generated integration artifacts: `hack setup sync --all-scopes --remove`", - "After upgrading CLI: `hack update` then `hack setup sync --all-scopes`", + "CLI upgrades do not authorize integration changes. After an upgrade, refresh affected integrations only when requested or needed for the authorized task.", "When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`).", ], }, @@ -326,7 +326,7 @@ export const INSTRUCTION_SECTIONS: readonly InstructionSection[] = [ "Cursor rules: `hack setup cursor`", "Claude hooks: `hack setup claude`", "Codex skill: `hack setup codex`", - "Refresh all local agent integrations: `hack setup sync --all-scopes`", + "Explicit full integration refresh: `hack setup sync --all-scopes`", "Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content.", "Init prompt: `hack agent init` (use --client cursor|claude|codex to open)", "Init patterns: `hack agent patterns`", diff --git a/src/agents/integration-revision.ts b/src/agents/integration-revision.ts index 489b6b76..925e1503 100644 --- a/src/agents/integration-revision.ts +++ b/src/agents/integration-revision.ts @@ -3,4 +3,4 @@ * source test recomputes this value and fails whenever guidance changes * without a revision update. */ -export const HACK_AGENT_INTEGRATION_CONTENT_REVISION = "994ef1552d14"; +export const HACK_AGENT_INTEGRATION_CONTENT_REVISION = "140421b48da0"; diff --git a/src/cli/integration-sync.ts b/src/cli/integration-sync.ts index 8870c2aa..4974fc7a 100644 --- a/src/cli/integration-sync.ts +++ b/src/cli/integration-sync.ts @@ -33,7 +33,7 @@ export async function inspectAgentIntegrationFreshness(opts: { }; } -/** Render an upfront status block suitable for SessionStart hooks and agents. */ +/** Render a non-blocking result for an explicitly requested integration inventory. */ export function renderAgentIntegrationFreshnessNotice(opts: { readonly report: AgentIntegrationFreshnessReport; }): string { @@ -41,11 +41,12 @@ export function renderAgentIntegrationFreshnessNotice(opts: { return `Hack agent integration freshness: current (CLI v${opts.report.cliVersion}).`; } return [ - `WARNING: Hack agent integrations are stale for CLI v${opts.report.cliVersion}.`, - "Do not rely on cached Hack rules or skills until they are refreshed.", - `Fix project + global integrations: ${opts.report.fixCommand}`, - `Verify: ${opts.report.verifyCommand}`, - "Then reload the agent session so it reads the updated rules.", + `Hack integration inventory: review needed (CLI v${opts.report.cliVersion}).`, + "Some integrations are missing, stale, or could not be checked. Missing optional integrations need not be installed.", + `Inspect affected paths: ${opts.report.verifyCommand}`, + "Repair only the affected integration and scope authorized for this task.", + `For an explicitly requested full refresh: ${opts.report.fixCommand}`, + "Read refreshed guidance; restart only if the client cannot reload changed hooks or skills. Unrelated work can continue.", ].join("\n"); } diff --git a/src/commands/agent.ts b/src/commands/agent.ts index 73d180ec..713d62b8 100644 --- a/src/commands/agent.ts +++ b/src/commands/agent.ts @@ -31,7 +31,15 @@ import { import { findExecutableInPath, run } from "../lib/shell.ts"; import { logger } from "../ui/logger.ts"; -type PrimeArgs = CommandArgs; +const primeOptions = [ + defineOption({ + name: "check", + type: "boolean", + long: "--check", + description: "Include a read-only project and global integration inventory", + } as const), +] as const; +type PrimeArgs = CommandArgs; type PatternsArgs = CommandArgs; const onboardOptions = [optPath] as const; @@ -55,7 +63,7 @@ const primeSpec = defineCommand({ name: "prime", summary: "Print agent primer text", group: "Agents", - options: [], + options: primeOptions, positionals: [], subcommands: [], } as const); @@ -103,11 +111,15 @@ export const agentCommand = defineCommand({ async function handleAgentPrime({ ctx, - args: _args, + args, }: { readonly ctx: CliContext; readonly args: PrimeArgs; }): Promise { + if (!args.options.check) { + process.stdout.write(`${renderAgentPrimer()}\n`); + return 0; + } const project = await findProjectContext(ctx.cwd); if (!project) { process.stdout.write( diff --git a/src/commands/doctor.ts b/src/commands/doctor.ts index e0e64710..4343c275 100644 --- a/src/commands/doctor.ts +++ b/src/commands/doctor.ts @@ -1064,8 +1064,8 @@ async function checkAgentIntegrationsUnsafe(opts: { name: "agent integrations", status: "warn", message: project - ? "Project or global guidance is stale (run: hack setup sync --all-scopes, reload the agent session, verify: hack setup sync --all-scopes --check)" - : "Global guidance is stale (run: hack setup sync --global, reload the agent session, verify: hack setup sync --global --check)", + ? "Agent integration review needed (inspect: hack setup sync --all-scopes --check; repair only affected, authorized targets; missing optional integrations need not be installed)" + : "Global agent integration review needed (inspect: hack setup sync --global --check; repair only affected, authorized targets; missing optional integrations need not be installed)", }; } diff --git a/src/commands/recovery-guidance.ts b/src/commands/recovery-guidance.ts index 64baae37..db501508 100644 --- a/src/commands/recovery-guidance.ts +++ b/src/commands/recovery-guidance.ts @@ -111,12 +111,12 @@ export function buildDoctorRecoveryGuidance(input: { } if (result.message.includes("hack setup sync --all-scopes")) { - pushUnique(configurationRepair, "hack setup sync --all-scopes"); + pushUnique(configurationRepair, "hack setup sync --all-scopes --check"); continue; } if (result.message.includes("hack setup sync --global")) { - pushUnique(configurationRepair, "hack setup sync --global"); + pushUnique(configurationRepair, "hack setup sync --global --check"); continue; } diff --git a/tests/agent-instruction-source.test.ts b/tests/agent-instruction-source.test.ts index 2381af14..d82e52a9 100644 --- a/tests/agent-instruction-source.test.ts +++ b/tests/agent-instruction-source.test.ts @@ -140,14 +140,15 @@ test("active contributor guidance and examples do not advertise retired Tickets" expect(await Bun.file("examples/tickets/README.md").exists()).toBe(false); }); -test("all generated surfaces expose integration freshness and repair upfront", () => { +test("all generated surfaces keep freshness checks scoped and optional", () => { for (const [surface, rendered] of Object.entries(RENDERED_SURFACES)) { expect(rendered, `surface "${surface}" lacks freshness status`).toContain( "Integration freshness" ); expect(rendered).toContain("hack setup sync --all-scopes --check"); expect(rendered).toContain("hack setup sync --all-scopes"); - expect(rendered).toContain("reload the agent session"); + expect(rendered).toContain("restart only if"); + expect(rendered).not.toContain("At session start, audit"); } }); diff --git a/tests/doctor-command.test.ts b/tests/doctor-command.test.ts index 4041ce95..d7d942c8 100644 --- a/tests/doctor-command.test.ts +++ b/tests/doctor-command.test.ts @@ -176,19 +176,21 @@ test("doctor guidance includes daemon recovery for stale local api state", () => expect(guidance.configurationRepair).toEqual([]); }); -test("doctor guidance routes global agent drift to global sync", () => { +test("doctor guidance keeps global integration recovery read-only", () => { const guidance = buildDoctorRecoveryGuidance({ results: [ { name: "agent integrations", status: "warn", message: - "Global guidance is stale (run: hack setup sync --global, reload the agent session)", + "Global agent integration review needed (inspect: hack setup sync --global --check; repair only affected, authorized targets; missing optional integrations need not be installed)", }, ], }); - expect(guidance.configurationRepair).toEqual(["hack setup sync --global"]); + expect(guidance.configurationRepair).toEqual([ + "hack setup sync --global --check", + ]); }); test("doctor audits global agent guidance without a project", async () => { @@ -538,3 +540,19 @@ test("recovery workflow lines scope repo-specific commands for doctor output", ( " - `hack crash-capture --path '/tmp/work repo'`", ]); }); + +test("doctor does not turn an all-scope inventory into an automatic repair", () => { + const guidance = buildDoctorRecoveryGuidance({ + results: [ + { + name: "agent integrations", + status: "warn", + message: + "Freshness audit unavailable (verify: hack setup sync --all-scopes --check)", + }, + ], + }); + expect(guidance.configurationRepair).toEqual([ + "hack setup sync --all-scopes --check", + ]); +}); diff --git a/tests/e2e/scenarios/agent-docs-sync.ts b/tests/e2e/scenarios/agent-docs-sync.ts index fe6deead..f33e7293 100644 --- a/tests/e2e/scenarios/agent-docs-sync.ts +++ b/tests/e2e/scenarios/agent-docs-sync.ts @@ -79,11 +79,31 @@ export const agentDocsSyncScenario: Scenario = { result: staleCheck, }); - const stalePrime = await ctx.cli({ + const plainPrime = await ctx.cli({ args: ["agent", "prime"], cwd: fixture.root, env: isolatedUserEnv, }); + expectExit({ + result: plainPrime, + codes: [0], + message: "plain primer renders despite drift", + }); + expect({ + that: + !( + plainPrime.stdout.includes("Hack integration inventory:") || + plainPrime.stdout.includes("WARNING:") + ) && (await Bun.file(agentsPath).text()) === corrupted, + message: "plain primer must not audit or repair stale integrations", + result: plainPrime, + }); + + const stalePrime = await ctx.cli({ + args: ["agent", "prime", "--check"], + cwd: fixture.root, + env: isolatedUserEnv, + }); expectExit({ result: stalePrime, codes: [0], @@ -92,10 +112,10 @@ export const agentDocsSyncScenario: Scenario = { expect({ that: stalePrime.stdout.includes( - "WARNING: Hack agent integrations are stale" + "Hack integration inventory: review needed" ) && stalePrime.stdout.includes("hack setup sync --all-scopes") && - stalePrime.stdout.includes("reload the agent session"), + stalePrime.stdout.includes("restart only if"), message: "agent primer should expose stale project/global guidance upfront", result: stalePrime, @@ -150,7 +170,7 @@ export const agentDocsSyncScenario: Scenario = { }); const currentPrime = await ctx.cli({ - args: ["agent", "prime"], + args: ["agent", "prime", "--check"], cwd: fixture.root, env: isolatedUserEnv, }); @@ -178,13 +198,13 @@ export const agentDocsSyncScenario: Scenario = { `${syncedAgents}\n\nRetired ticket guidance\n\n` ); const legacyPrime = await ctx.cli({ - args: ["agent", "prime"], + args: ["agent", "prime", "--check"], cwd: fixture.root, env: isolatedUserEnv, }); expect({ that: legacyPrime.stdout.includes( - "WARNING: Hack agent integrations are stale" + "Hack integration inventory: review needed" ), message: "agent primer should report retained legacy artifacts as stale", result: legacyPrime, From 33c33c14b551ad77640e7309390c2d7bec65cd21 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Mon, 7 Sep 2026 22:58:27 -0400 Subject: [PATCH 2/3] docs(agents): keep rendered guidance compatible with installed clients --- AGENTS.md | 6 +++--- CLAUDE.md | 6 +++--- examples/basic/AGENTS.md | 6 +++--- examples/basic/CLAUDE.md | 6 +++--- src/agents/instruction-source.ts | 4 ++-- src/agents/integration-revision.ts | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0e0e7b8a..7496f74d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -207,10 +207,10 @@ Use `hack` as the single interface for local-first runtime orchestration (compos Integration freshness: - These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. -- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit. - Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. - Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. -- Content revision: `140421b48da0` (version alone is not a freshness guarantee). +- Content revision: `4f99d4a27609` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -364,7 +364,7 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. +- Read-only freshness checks are available through targeted `hack setup` checks and `hack doctor`. Primer output is guidance; stale-integration notices do not authorize repairs or block unrelated work. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` diff --git a/CLAUDE.md b/CLAUDE.md index ac6311cf..22dc8861 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,10 +81,10 @@ Use `hack` as the single interface for local-first runtime orchestration (compos Integration freshness: - These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. -- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit. - Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. - Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. -- Content revision: `140421b48da0` (version alone is not a freshness guarantee). +- Content revision: `4f99d4a27609` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -238,7 +238,7 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. +- Read-only freshness checks are available through targeted `hack setup` checks and `hack doctor`. Primer output is guidance; stale-integration notices do not authorize repairs or block unrelated work. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` diff --git a/examples/basic/AGENTS.md b/examples/basic/AGENTS.md index b230ce85..b6b80c01 100644 --- a/examples/basic/AGENTS.md +++ b/examples/basic/AGENTS.md @@ -5,10 +5,10 @@ Use `hack` as the single interface for local-first runtime orchestration (compos Integration freshness: - These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. -- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit. - Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. - Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. -- Content revision: `140421b48da0` (version alone is not a freshness guarantee). +- Content revision: `4f99d4a27609` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -162,7 +162,7 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. +- Read-only freshness checks are available through targeted `hack setup` checks and `hack doctor`. Primer output is guidance; stale-integration notices do not authorize repairs or block unrelated work. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` diff --git a/examples/basic/CLAUDE.md b/examples/basic/CLAUDE.md index b230ce85..b6b80c01 100644 --- a/examples/basic/CLAUDE.md +++ b/examples/basic/CLAUDE.md @@ -5,10 +5,10 @@ Use `hack` as the single interface for local-first runtime orchestration (compos Integration freshness: - These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. -- Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit. +- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit. - Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. - Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. -- Content revision: `140421b48da0` (version alone is not a freshness guarantee). +- Content revision: `4f99d4a27609` (version alone is not a freshness guarantee). Product boundary: - Supported v3 surface: project init, up/down/restart, open, logs, env, host exec/shell, sessions, doctor, and daemon. @@ -162,7 +162,7 @@ Docker compose notes: Agent integration maintenance: - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations. +- Read-only freshness checks are available through targeted `hack setup` checks and `hack doctor`. Primer output is guidance; stale-integration notices do not authorize repairs or block unrelated work. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` diff --git a/src/agents/instruction-source.ts b/src/agents/instruction-source.ts index c80b126d..af5417dc 100644 --- a/src/agents/instruction-source.ts +++ b/src/agents/instruction-source.ts @@ -43,7 +43,7 @@ export const INSTRUCTION_SECTIONS: readonly InstructionSection[] = [ surfaces: ALL_SURFACES, bullets: [ `These instructions were generated by hack CLI v${HACK_AGENT_INTEGRATION_CLI_VERSION}; treat cached rules from another version as potentially stale.`, - "Check guidance only when Hack is relevant and there is evidence of drift; use `hack agent prime --check` for a read-only inventory. Routine tasks do not require a startup audit.", + "Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit.", "Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh.", "Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient.", ], @@ -309,7 +309,7 @@ export const INSTRUCTION_SECTIONS: readonly InstructionSection[] = [ bullets: [ "Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files.", "Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config.", - "Read-only freshness checks are available through targeted `hack setup` checks, `hack doctor`, and `hack agent prime --check`. Plain `hack agent prime` renders current guidance without scanning integrations.", + "Read-only freshness checks are available through targeted `hack setup` checks and `hack doctor`. Primer output is guidance; stale-integration notices do not authorize repairs or block unrelated work.", "Refresh project + user integrations: `hack setup sync --all-scopes`", "Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review.", "Audit integration state only: `hack setup sync --all-scopes --check`", diff --git a/src/agents/integration-revision.ts b/src/agents/integration-revision.ts index 925e1503..a6e85071 100644 --- a/src/agents/integration-revision.ts +++ b/src/agents/integration-revision.ts @@ -3,4 +3,4 @@ * source test recomputes this value and fails whenever guidance changes * without a revision update. */ -export const HACK_AGENT_INTEGRATION_CONTENT_REVISION = "140421b48da0"; +export const HACK_AGENT_INTEGRATION_CONTENT_REVISION = "4f99d4a27609"; From 02c7a4495a2baaf514220d2236e0a0d10e9e5cf9 Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Mon, 7 Sep 2026 23:21:56 -0400 Subject: [PATCH 3/3] fix(agents): address inventory and generated guidance review feedback --- .codex/skills/hack-cli/SKILL.md | 16 ++-- .cursor/rules/hack.mdc | 14 ++-- docs/integrations.md | 2 + src/cli/integration-sync.ts | 107 +++++++++++-------------- src/commands/agent.ts | 13 +-- src/commands/recovery-guidance.ts | 18 ++++- tests/agent-instruction-source.test.ts | 16 +++- tests/doctor-command.test.ts | 16 ++-- tests/e2e/scenarios/agent-docs-sync.ts | 46 +++++++++++ 9 files changed, 151 insertions(+), 97 deletions(-) diff --git a/.codex/skills/hack-cli/SKILL.md b/.codex/skills/hack-cli/SKILL.md index feb192be..51ba805a 100644 --- a/.codex/skills/hack-cli/SKILL.md +++ b/.codex/skills/hack-cli/SKILL.md @@ -12,11 +12,11 @@ Use `hack` as the primary interface for local-first development. ## Integration freshness -- These instructions were generated by hack CLI v3.5.2; treat cached rules from another version as potentially stale. -- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`. -- If anything is stale or missing, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced. -- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command. -- Content revision: `994ef1552d14` (version alone is not a freshness guarantee). +- These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. +- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit. +- Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. +- Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. +- Content revision: `4f99d4a27609` (version alone is not a freshness guarantee). ## Product boundary @@ -192,12 +192,12 @@ Use `hack` as the primary interface for local-first development. - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through `hack setup sync --all-scopes --check`, `hack doctor`, and `hack agent prime`. +- Read-only freshness checks are available through targeted `hack setup` checks and `hack doctor`. Primer output is guidance; stale-integration notices do not authorize repairs or block unrelated work. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` -- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- CLI upgrades do not authorize integration changes. After an upgrade, refresh affected integrations only when requested or needed for the authorized task. - When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). ## Agent setup (CLI-first) @@ -205,7 +205,7 @@ Use `hack` as the primary interface for local-first development. - Cursor rules: `hack setup cursor` - Claude hooks: `hack setup claude` - Codex skill: `hack setup codex` -- Refresh all local agent integrations: `hack setup sync --all-scopes` +- Explicit full integration refresh: `hack setup sync --all-scopes` - Agent-assisted onboarding: `hack init --with claude|codex|both` (new repos) or `hack agent onboard` (existing projects) print/hand off the full setup prompt; the `/hack-init` skill and the `hack-init` MCP prompt return the same content. - Init prompt: `hack agent init` (use --client cursor|claude|codex to open) - Init patterns: `hack agent patterns` diff --git a/.cursor/rules/hack.mdc b/.cursor/rules/hack.mdc index 2f6dcab4..5d0983df 100644 --- a/.cursor/rules/hack.mdc +++ b/.cursor/rules/hack.mdc @@ -7,11 +7,11 @@ Prefer `hack` when shell access is available. Use MCP only when shell access is ## Integration freshness -- These instructions were generated by hack CLI v3.5.2; treat cached rules from another version as potentially stale. -- At session start, audit project and global integrations with `hack setup sync --all-scopes --check`. -- If anything is stale or missing, run `hack setup sync --all-scopes`, then reload the agent session so cached instructions are replaced. -- Never copy or hand-edit generated Hack rules to refresh them; update the CLI and run the sync command. -- Content revision: `994ef1552d14` (version alone is not a freshness guarantee). +- These instructions were generated by hack CLI v4.0.2; treat cached rules from another version as potentially stale. +- Check guidance only when Hack is relevant and there is evidence of drift; use targeted `hack setup` checks for a read-only inventory. Routine tasks do not require a startup audit. +- Repair only the affected integration and scope covered by the request, using the matching `hack setup` command. Missing optional integrations do not require installation. Use `--all-scopes` only for an explicitly requested full refresh. +- Edit Hack guidance at its generator source and render the affected targets. After a refresh, read the updated guidance; restart only if the client cannot reload the changed hooks or skills. Existing authorization for the same action and scope is sufficient. +- Content revision: `4f99d4a27609` (version alone is not a freshness guarantee). ## Product boundary @@ -93,12 +93,12 @@ Prefer `hack` when shell access is available. Use MCP only when shell access is - Ordinary Hack commands, `hack update`, and `hack doctor --fix` may audit freshness but never render, repair, remove, or otherwise mutate agent integration files. - Use `hack setup sync` only when explicitly choosing to manage project or user docs, skills, rules, hooks, or MCP config. -- Read-only freshness checks are available through `hack setup sync --all-scopes --check`, `hack doctor`, and `hack agent prime`. +- Read-only freshness checks are available through targeted `hack setup` checks and `hack doctor`. Primer output is guidance; stale-integration notices do not authorize repairs or block unrelated work. - Refresh project + user integrations: `hack setup sync --all-scopes` - Explicit sync removes recognized Hack-owned artifacts from retired integrations and preserves unrecognized files for manual review. - Audit integration state only: `hack setup sync --all-scopes --check` - Remove generated integration artifacts: `hack setup sync --all-scopes --remove` -- After upgrading CLI: `hack update` then `hack setup sync --all-scopes` +- CLI upgrades do not authorize integration changes. After an upgrade, refresh affected integrations only when requested or needed for the authorized task. - When changing hack itself: interface or behavior changes must update docs/ in the same change (regenerate the CLI reference with `bun run docs:cli-reference`). # END HACK INTEGRATION diff --git a/docs/integrations.md b/docs/integrations.md index 8fbf8f16..c6d1c51e 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -31,6 +31,8 @@ Run checks when Hack is relevant and there is evidence of drift; there is no man Plain `hack agent prime`, including installed SessionStart/PreCompact hooks, prints the current primer without scanning project or global integrations. Use `hack agent prime --check` for an explicit read-only inventory, or a targeted `hack setup ... --check` for per-artifact detail. +Outside a Hack project, `hack agent prime --check` inventories global integrations only. Doctor +classifies inventory commands as follow-up investigation and keeps scoped repair guidance separate. Inventory findings do not block unrelated work, and missing optional integrations need not be installed. Ordinary commands, `hack update`, and `hack doctor --fix` never modify these surfaces. Doctor may diff --git a/src/cli/integration-sync.ts b/src/cli/integration-sync.ts index 4974fc7a..f3a4e664 100644 --- a/src/cli/integration-sync.ts +++ b/src/cli/integration-sync.ts @@ -22,14 +22,16 @@ const VERIFY_COMMAND = "hack setup sync --all-scopes --check"; /** Inspect project and global generated guidance without mutating it. */ export async function inspectAgentIntegrationFreshness(opts: { - readonly projectRoot: string; + readonly projectRoot: string | null; }): Promise { const drift = await detectIntegrationDrift(opts); return { status: drift.hasDrift ? "stale" : "current", cliVersion: HACK_AGENT_INTEGRATION_CLI_VERSION, - fixCommand: SYNC_COMMAND, - verifyCommand: VERIFY_COMMAND, + fixCommand: opts.projectRoot ? SYNC_COMMAND : "hack setup sync --global", + verifyCommand: opts.projectRoot + ? VERIFY_COMMAND + : "hack setup sync --global --check", }; } @@ -51,66 +53,51 @@ export function renderAgentIntegrationFreshnessNotice(opts: { } async function detectIntegrationDrift(opts: { - readonly projectRoot: string; + readonly projectRoot: string | null; }): Promise<{ readonly hasDrift: boolean }> { - const [ - cursorProject, - cursorUser, - claudeProject, - claudeUser, - codexProject, - codexUser, - sharedSkill, - mcpProject, - mcpUser, - docs, - legacyProject, - legacyUser, - ] = await Promise.all([ - checkCursorRules({ scope: "project", projectRoot: opts.projectRoot }), - checkCursorRules({ scope: "user" }), - checkClaudeHooks({ scope: "project", projectRoot: opts.projectRoot }), - checkClaudeHooks({ scope: "user" }), - checkCodexSkill({ scope: "project", projectRoot: opts.projectRoot }), - checkCodexSkill({ scope: "user" }), - checkSharedHackSkill(), - checkMcpConfig({ - scope: "project", - projectRoot: opts.projectRoot, - targets: ["cursor", "claude", "codex"], - }), - checkMcpConfig({ - scope: "user", - targets: ["cursor", "claude", "codex"], - }), - checkAgentDocs({ - projectRoot: opts.projectRoot, - targets: ["agents", "claude"], - }), - checkLegacyProjectAgentArtifacts({ projectRoot: opts.projectRoot }), - checkLegacyUserAgentArtifacts(), + const projectRoot = opts.projectRoot; + const [singleChecks, mcpChecks, docs, legacy] = await Promise.all([ + Promise.all([ + checkCursorRules({ scope: "user" }), + checkClaudeHooks({ scope: "user" }), + checkCodexSkill({ scope: "user" }), + checkSharedHackSkill(), + ...(projectRoot + ? [ + checkCursorRules({ scope: "project", projectRoot }), + checkClaudeHooks({ scope: "project", projectRoot }), + checkCodexSkill({ scope: "project", projectRoot }), + ] + : []), + ]), + Promise.all([ + checkMcpConfig({ scope: "user", targets: ["cursor", "claude", "codex"] }), + ...(projectRoot + ? [ + checkMcpConfig({ + scope: "project", + projectRoot, + targets: ["cursor", "claude", "codex"], + }), + ] + : []), + ]), + projectRoot + ? checkAgentDocs({ projectRoot, targets: ["agents", "claude"] }) + : [], + Promise.all([ + checkLegacyUserAgentArtifacts(), + ...(projectRoot + ? [checkLegacyProjectAgentArtifacts({ projectRoot })] + : []), + ]), ]); - - const singleChecks = [ - cursorProject.status, - cursorUser.status, - claudeProject.status, - claudeUser.status, - codexProject.status, - codexUser.status, - sharedSkill.status, - ] as const; - - const singleDrift = singleChecks.some((status) => - hasSingleCheckDrift(status) - ); - const mcpDrift = hasMcpDrift({ checks: [...mcpProject, ...mcpUser] }); - const docsDrift = hasDocDrift({ checks: docs }); - const legacyDrift = [...legacyProject, ...legacyUser].some( - (check) => check.status !== "absent" - ); return { - hasDrift: singleDrift || mcpDrift || docsDrift || legacyDrift, + hasDrift: + singleChecks.some((check) => hasSingleCheckDrift(check.status)) || + hasMcpDrift({ checks: mcpChecks.flat() }) || + hasDocDrift({ checks: docs }) || + legacy.flat().some((check) => check.status !== "absent"), }; } diff --git a/src/commands/agent.ts b/src/commands/agent.ts index 713d62b8..be9e465b 100644 --- a/src/commands/agent.ts +++ b/src/commands/agent.ts @@ -121,19 +121,8 @@ async function handleAgentPrime({ return 0; } const project = await findProjectContext(ctx.cwd); - if (!project) { - process.stdout.write( - [ - "Hack agent integration freshness: not checked outside a Hack project.", - "From a project, run: hack setup sync --all-scopes --check", - "", - renderAgentPrimer(), - ].join("\n") - ); - return 0; - } const report = await inspectAgentIntegrationFreshness({ - projectRoot: project.projectRoot, + projectRoot: project?.projectRoot ?? null, }); process.stdout.write( `${renderAgentIntegrationFreshnessNotice({ report })}\n\n${renderAgentPrimer()}` diff --git a/src/commands/recovery-guidance.ts b/src/commands/recovery-guidance.ts index db501508..d249cb42 100644 --- a/src/commands/recovery-guidance.ts +++ b/src/commands/recovery-guidance.ts @@ -111,12 +111,26 @@ export function buildDoctorRecoveryGuidance(input: { } if (result.message.includes("hack setup sync --all-scopes")) { - pushUnique(configurationRepair, "hack setup sync --all-scopes --check"); + pushUnique( + followUp, + "Inspect integration paths: hack setup sync --all-scopes --check" + ); + pushUnique( + followUp, + "Repair only reported targets with the matching hack setup command and authorized project/global scope; missing optional integrations need not be installed." + ); continue; } if (result.message.includes("hack setup sync --global")) { - pushUnique(configurationRepair, "hack setup sync --global --check"); + pushUnique( + followUp, + "Inspect global integration paths: hack setup sync --global --check" + ); + pushUnique( + followUp, + "Repair only reported global targets with the matching hack setup --global command when authorized; missing optional integrations need not be installed." + ); continue; } diff --git a/tests/agent-instruction-source.test.ts b/tests/agent-instruction-source.test.ts index d82e52a9..31f169ee 100644 --- a/tests/agent-instruction-source.test.ts +++ b/tests/agent-instruction-source.test.ts @@ -157,13 +157,25 @@ test("checked-in agent examples use the current integration contract", async () (section) => section.id === "maintenance" ); expect(maintenance).toBeDefined(); + const freshness = INSTRUCTION_SECTIONS.find( + (section) => section.id === "freshness" + ); + expect(freshness).toBeDefined(); - for (const path of ["examples/basic/AGENTS.md", "examples/basic/CLAUDE.md"]) { + for (const path of [ + "examples/basic/AGENTS.md", + "examples/basic/CLAUDE.md", + ".codex/skills/hack-cli/SKILL.md", + ".cursor/rules/hack.mdc", + ]) { const content = await Bun.file(path).text(); expect(content).toContain( `Content revision: \`${HACK_AGENT_INTEGRATION_CONTENT_REVISION}\`` ); - for (const bullet of maintenance?.bullets ?? []) { + for (const bullet of [ + ...(maintenance?.bullets ?? []), + ...(freshness?.bullets.slice(1) ?? []), + ]) { expect(content, `${path} lacks current maintenance guidance`).toContain( bullet ); diff --git a/tests/doctor-command.test.ts b/tests/doctor-command.test.ts index d7d942c8..d0d1c546 100644 --- a/tests/doctor-command.test.ts +++ b/tests/doctor-command.test.ts @@ -188,9 +188,11 @@ test("doctor guidance keeps global integration recovery read-only", () => { ], }); - expect(guidance.configurationRepair).toEqual([ - "hack setup sync --global --check", - ]); + expect(guidance.configurationRepair).toEqual([]); + expect(guidance.followUp[0]).toBe( + "Inspect global integration paths: hack setup sync --global --check" + ); + expect(guidance.followUp[1]).toContain("reported global targets"); }); test("doctor audits global agent guidance without a project", async () => { @@ -552,7 +554,9 @@ test("doctor does not turn an all-scope inventory into an automatic repair", () }, ], }); - expect(guidance.configurationRepair).toEqual([ - "hack setup sync --all-scopes --check", - ]); + expect(guidance.configurationRepair).toEqual([]); + expect(guidance.followUp[0]).toBe( + "Inspect integration paths: hack setup sync --all-scopes --check" + ); + expect(guidance.followUp[1]).toContain("authorized project/global scope"); }); diff --git a/tests/e2e/scenarios/agent-docs-sync.ts b/tests/e2e/scenarios/agent-docs-sync.ts index f33e7293..afdf47f6 100644 --- a/tests/e2e/scenarios/agent-docs-sync.ts +++ b/tests/e2e/scenarios/agent-docs-sync.ts @@ -169,6 +169,52 @@ export const agentDocsSyncScenario: Scenario = { "hack setup sync --check right after hack setup sync should be clean", }); + const globalPrime = await ctx.cli({ + args: ["agent", "prime", "--check"], + cwd: ctx.hackHome, + env: isolatedUserEnv, + }); + expectExit({ + result: globalPrime, + codes: [0], + message: "global inventory works outside a project", + }); + expect({ + that: globalPrime.stdout.includes( + "Hack agent integration freshness: current" + ), + message: + "outside-project inventory must inspect installed global integrations", + result: globalPrime, + }); + const globalSkillPath = join( + ctx.hackHome, + ".codex", + "skills", + "hack-cli", + "SKILL.md" + ); + const globalSkill = await Bun.file(globalSkillPath).text(); + await Bun.write(globalSkillPath, `${globalSkill}\nSTALE-GLOBAL-PROBE\n`); + const staleGlobalPrime = await ctx.cli({ + args: ["agent", "prime", "--check"], + cwd: ctx.hackHome, + env: isolatedUserEnv, + }); + expect({ + that: + staleGlobalPrime.stdout.includes( + "Hack integration inventory: review needed" + ) && + staleGlobalPrime.stdout.includes( + "Inspect affected paths: hack setup sync --global --check" + ) && + (await Bun.file(globalSkillPath).text()).includes("STALE-GLOBAL-PROBE"), + message: "global inventory reports drift without repairing it", + result: staleGlobalPrime, + }); + await Bun.write(globalSkillPath, globalSkill); + const currentPrime = await ctx.cli({ args: ["agent", "prime", "--check"], cwd: fixture.root,