Skip to content

fix(cli): install skills into project dir on init#1671

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/cli-noninteractive-skill-install
Jun 23, 2026
Merged

fix(cli): install skills into project dir on init#1671
miguel-heygen merged 1 commit into
mainfrom
fix/cli-noninteractive-skill-install

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Supersedes #1667. Incorporates the original fix from @kiritowoo (non-interactive init now installs skills), simplified and with additional fixes found during outside-the-monorepo testing.

What

Both interactive and non-interactive hyperframes init now install skills into the correct project directory.

Why

Three things were broken:

  1. Non-interactive init skipped skill install — it printed npx skills add ... but never ran it. Agents hit Unknown skill: <workflow> and free-handed compositions.

  2. Wrong CWD (both paths) — neither the interactive nor non-interactive branch set cwd when spawning npx skills add. Skills landed in the caller's working directory, not inside the scaffolded project.

  3. Template pointed to wrong command — CLAUDE.md / AGENTS.md told agents to run npx hyperframes skills, but the canonical path is npx skills add heygen-com/hyperframes.

How

  • runSkillsAdd accepts cwd and extraArgscwd sets the install target, extraArgs lets callers pass flags like --agent claude-code --yes.
  • Both init paths (interactive confirm + non-interactive auto-install) now pass cwd: destDir. Non-interactive also detects Claude Code via the CLAUDECODE env var to pass --agent claude-code.
  • CLAUDE.md / AGENTS.md templates now point to npx skills add heygen-com/hyperframes.

Test plan

Built the branch, tested outside the monorepo with CLAUDECODE=1:

  • hyperframes init test-project --non-interactive → 18 skills in test-project/.claude/skills/, nothing in parent CWD
  • --skip-skills flag still skips installation
  • CLAUDE.md in scaffolded project says npx skills add heygen-com/hyperframes
  • Build, lint, format, typecheck all pass

@miguel-heygen miguel-heygen force-pushed the fix/cli-noninteractive-skill-install branch 3 times, most recently from 8e8fad0 to 4cbe4c8 Compare June 23, 2026 16:50

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Verdict: NIT (close to LGTM — one scope-gap to call out, otherwise a tight, surgical fix).

Head SHA: 4cbe4c8. Base: main (standalone, not a stack — Graphite URL notwithstanding).

The three claimed bugs are real and the fixes line up:

  • Non-interactive branch now actually invokes the installer rather than just printing a hint — packages/cli/src/commands/init.ts:804-810.
  • runSkillsAdd correctly threads cwd to spawnpackages/cli/src/commands/skills.ts:21-26.
  • Templates updated to the canonical npx skills add heygen-com/hyperframesAGENTS.md / CLAUDE.md lines 24-25.

Refactor is clean: installAllSkills is a thin, well-typed wrapper, the default-run arm just delegates, and extraArgs ?? ["--all"] preserves the prior interactive contract on the standalone hyperframes skills command.

Finding (scope gap vs PR description — NIT, not block)

The PR body's "Why #2 — Wrong CWD" reads as a general root-cause statement, but the fix is non-interactive-only. The interactive path still has the bug:

packages/cli/src/commands/init.ts:1027-1039:

if (!skipSkills) {
  const installSkills = await clack.confirm({ ... });
  ...
  if (installSkills) {
    const skillsCmd = await import("./skills.js").then((m) => m.default);
    await runCommand(skillsCmd, { rawArgs: [] });
  }
}

runCommand(skillsCmd, { rawArgs: [] })installAllSkills() (no opts) → runSkillsAdd(repo, {})spawn(..., { cwd: undefined }), which means process.cwd() — i.e. the caller's directory, not destDir. A human running hyperframes init my-video in their home dir and confirming the skill prompt will still get skills written next to wherever they invoked the CLI, not inside my-video/. No process.chdir between the prompt and the spawn (greped the file).

Two reasonable resolutions:

  1. Tighten the PR description to "fixes wrong-CWD on the non-interactive path; interactive parity tracked separately" and ship as-is.
  2. One-line fix in the interactive arm:
    await installAllSkills({ cwd: destDir });
    instead of runCommand(skillsCmd, { rawArgs: [] }). Same behavior, same --all default, just with the correct target. Symmetric with the non-interactive arm.

I'd prefer (2) — the interactive path is the one humans actually use, and shipping a description that overstates the fix makes the next regression harder to spot.

Smaller nits (non-blocking)

  • The non-interactive arm passes ["--yes"] or ["--agent","claude-code","--yes"] — neither includes --all. Test plan reports 18 skills landed via --yes alone, so the upstream skills CLI presumably defaults to all in non-interactive mode. Worth a brief code-comment confirming that's intentional (since the interactive branch still relies on the explicit --all default in runSkillsAdd).
  • "Agent's native dir" in the title is fulfilled only for Claude Code (via CLAUDECODE). Cursor, Codex, Windsurf, etc. in non-interactive mode get the upstream default (presumably .agents/skills/). That matches the PR body's narrower "When Claude Code is driving" wording, but the title is slightly oversold. Not blocking — the missing-agent fallback is "do nothing surprising," which is the safe behavior.

CI: all required green at 4cbe4c8 (Build / Lint / Typecheck / Test / Fallow / CLI smoke / CLI shim ubuntu+macos+windows / SDK / runtime contract / Studio smoke / Windows render / preview-regression / regression / CodeQL all passing). Earlier CANCELLED runs are a superseded workflow generation.

Review by Via

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R1 review @ HEAD 4cbe4c80

Scope-correct fix for the stated non-interactive bug. Verified against upstream skills semantics + existing in-repo helpers. LGTM-with-followups — no merge blockers; two asymmetries are worth tracking before / shortly after.

file:line finding
⚠️ packages/cli/src/commands/init.ts:1037-1038 Interactive path still calls runCommand(skillsCmd, { rawArgs: [] }) which goes through installAllSkills() with no opts — so cwd is NOT set to destDir and there's no --agent claude-code detection. PR body item #2 ("Wrong CWD … Skills landed in the caller's working directory, not inside the scaffolded project") reads as a general bug, but only the non-interactive branch is patched. A human running hyperframes init my-vid inside Claude Code's TTY hits the same wrong-CWD landing the PR fixes for agents. Title scopes to "non-interactive", so non-blocking; flag as the obvious follow-up. Fix shape: have the interactive branch also call installAllSkills({ cwd: destDir, extraArgs: claudeCodeArgs }).
⚠️ packages/cli/src/commands/init.ts:809 Agent detection narrowed to CLAUDECODE only. packages/cli/src/telemetry/agent_runtime.ts (lines 49-183) already ships a multi-agent fingerprinter (Codex, Cursor, Gemini CLI, Windsurf, Cline, …) — and the superseded #1667 reused it via resolveSkillsAgent(). This rewrite drops that mapping. Worst-case is benign: a Cursor/Codex driver falls through to ["--yes"], upstream skills add auto-detects 0 installed-agents in the fresh destDir, and installs into all-agents (i.e. .agents/skills/ + everything else, per vercel-labs/skills add.ts:661-663). Works, but noisy. Suggest a tiny resolveSkillsAgent() mapping over detectAgentRuntime()--agent <id> so Cursor/Codex drivers also land natively.
🟡 packages/cli/src/commands/init.ts:809 const args = … shadows the outer run({ args }) destructured parameter. Lint accepts it, but rename to skillArgs / extraArgs for readability — esp. since args.something lookups appear elsewhere in the same function.
🟡 .github/workflows/ci.yml (CLI smoke) Both inside-monorepo and outside-monorepo CLI smoke runs hyperframes init … --skip-skills, so the new installAllSkills({ cwd, extraArgs }) code path has no CI coverage — the manual test plan in the body is the only signal. Existing skills.test.ts happens to still cover the standalone hyperframes skills command because installAllSkills() with no opts falls through to the --all default. Suggest a tiny unit test that asserts (a) cwd is threaded into spawn, (b) CLAUDECODE=1--agent claude-code --yes, (c) CLAUDECODE unset → --yes only. The existing node:child_process mock in skills.test.ts already exposes spawnCalls[].env / opts, so the lift is small.
🟢 packages/cli/src/commands/skills.ts:17-43 cwd + extraArgs plumbing into spawn is clean; --all default preserved for the existing standalone hyperframes skills path; existing skills.test.ts assertions on --all and Windows shim still hold.
🟢 upstream skills@1.5.12 semantics Verified against vercel-labs/skills src/add.ts:587-614: --yes without --skill installs all skills (test plan's "18 skills" matches). --agent claude-code lands in .claude/skills/ per src/agents.ts:124. PR's flag combos are correct.
🟢 packages/cli/src/templates/_shared/{CLAUDE,AGENTS}.md Files remain byte-identical (intentional — shared content, two filenames for two conventions). Wording change is correct + clearer than the prior "ask the user to run npx hyperframes skills" path.

CI: all required green (CLI smoke, build, lint, typecheck, win shim, smoke: global install). Note the caveat above re: smoke coverage of the new code path.

Stamp routing: I'm not stamping. PR is non-trivial enough (changes the install contract for agent drivers) that it deserves an explicit author go-ahead on the asymmetry follow-up before lighting up. Miguel — if you want this stamped as-is and the interactive-parity follow-up tracked separately, tag <@U0ARJFN5S6Q> (Rames Jusso) and he can land it; I'll have vetted it first.

— Rames D Jusso

`hyperframes init` only installed AI coding skills on the interactive path
(behind a clack confirm). When an agent drives it non-interactively (no TTY),
it just printed `npx skills add ...` and returned — so skills were never
installed and the agent later hit `Unknown skill: <workflow>`.

- init: both interactive and non-interactive branches now run
  `npx skills add` with `cwd` set to the new project dir so skills land
  there, not in the caller's working directory. Non-interactive additionally
  passes `--yes`; when Claude Code is driving (CLAUDECODE env var), adds
  `--agent claude-code` so skills target `.claude/skills/`.
- skills: `runSkillsAdd` accepts `cwd` and `extraArgs` so callers can
  control where and how skills are installed.
- templates: CLAUDE.md / AGENTS.md now tell agents to run
  `npx skills add heygen-com/hyperframes` to install or update skills.

Co-Authored-By: Wenbo Zhu <295860553+kiritowoo@users.noreply.github.com>
@miguel-heygen miguel-heygen changed the title fix(cli): install skills into the agent's native dir on non-interactive init fix(cli): install skills into project dir on init Jun 23, 2026
@miguel-heygen miguel-heygen force-pushed the fix/cli-noninteractive-skill-install branch from 4cbe4c8 to 7b8823d Compare June 23, 2026 17:08
@miguel-heygen miguel-heygen merged commit bf4f34b into main Jun 23, 2026
65 of 68 checks passed
@miguel-heygen miguel-heygen deleted the fix/cli-noninteractive-skill-install branch June 23, 2026 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants