projects/agent-native/slices/generate-skill-offer/briefs/d1-r1.md
On a new branch tml-2971-s2-one-time-skill-offer-on-prisma-generate created from the S1
branch head, implement the generate-time skills offer module per the S2 slice spec
(projects/agent-native/slices/generate-skill-offer/spec.md in the main checkout — read it
first; it is the contract), extract the timeout() helper from nps/survey.ts into
packages/cli/src/utils/prompt-timeout.ts, and unit-test the module.
In: packages/cli/src/utils/skills/skills-offer.ts (new);
packages/cli/src/utils/prompt-timeout.ts (new); packages/cli/src/utils/nps/survey.ts
(import swap only); packages/cli/src/__tests__/skills-offer.vitest.ts (new).
Out: Generate.ts (dispatch 2); any other NPS change; live network or real PostHog in
tests; the init path.
handleSkillsOffer(): Promise<{ prompted: boolean }> implements the spec's gate order
(acknowledgement file → already-installed detection with already-installed
acknowledgement write → isInteractive → Deno guard → isCi → maybeInGitHook →
isInNpmLifecycleHook → isInContainer → daysSinceFirstCommand >= 1), the 30s
prompt (default/timeout = No), the four-outcome skills-offer.json persistence in
env-paths('prisma').config, the accept path via S1's installSkills with the
non-fatal manual-command warning, and skills_offer_resolved telemetry through the
existing PosthogEventCapture path only when prompted. The whole body never throws.timeout() lives in prompt-timeout.ts with a doc comment; survey.ts imports it;
NPS tests still green.timeout; decline persists
declined; accept invokes the injected runner and persists accepted; telemetry
fires only when prompted; a thrown injected dependency does not propagate.pnpm run tsc; pnpm exec vitest run src/__tests__/skills-offer.vitest.ts src/__tests__/nps.test.ts — note nps.test.ts
is Jest, not vitest; run it via the package's Jest runner instead and record the
command.Stay focused on the goal; control scope. Trivial-and-related fixes with a one-line note; drift halts and surfaces.