Skip to content

fix: actionable error messages + fire cli.link event on --template#62

Merged
tonychang04 merged 3 commits intomainfrom
feat/improve-error-messages-and-link-event
Apr 12, 2026
Merged

fix: actionable error messages + fire cli.link event on --template#62
tonychang04 merged 3 commits intomainfrom
feat/improve-error-messages-and-link-event

Conversation

@tonychang04
Copy link
Copy Markdown
Contributor

@tonychang04 tonychang04 commented Apr 12, 2026

Summary

  • Actionable network errors. fetch failed / generic catch-alls now get unwrapped. formatFetchError reads the undici cause.code (ENOTFOUND, ECONNREFUSED, ETIMEDOUT, CERT_HAS_EXPIRED, etc.) and returns a sentence the user can act on. Wired into platformFetch and the OAuth token endpoints in auth.ts.
  • Actionable skill-install errors. describeExecError in skills.ts classifies execAsync failures (timeout, missing npx, DNS, TLS, 404, EACCES, ENOSPC, npm auth). Warning now explains the real reason and tells the user to run the manual command once resolved — not a useless "try again".
  • Fire cli.link on --template. Regression: when --template is passed, saveProjectConfig is deferred until the template subdirectory is created, so reportCliUsage('cli.link') ran before config existed in cwd and returned early with no event. reportCliUsage now accepts an explicit config; both link branches pass it through.

Test plan

  • insforge link --template react from an empty dir — confirm the link event reaches the project (previously missing).
  • Simulate offline (networksetup -setairportpower en0 off or turn off Wi-Fi) and run insforge link — should see a DNS-failure message, not "fetch failed".
  • With no network, run insforge create or link --template — "Could not install agent skills: cannot reach the npm registry …" instead of generic warn.
  • insforge link on a known-good project (no template) — unchanged behavior, cli.link still fires.

🤖 Generated with Claude Code

Note

Add actionable network error messages and fire cli.link event on --template

  • Adds formatFetchError in src/lib/errors.ts to map common network/TLS error codes to user-facing messages; used in platformFetch and the OAuth token exchange/refresh flows.
  • Adds describeExecError in src/lib/skills.ts to classify npx/npm exec failures (timeout, DNS, TLS, 404, auth, disk full, etc.) into actionable log messages during skill installation.
  • Extends reportCliUsage to accept an optional explicit config, allowing cli.link to be fired from the --template path without reading project config from disk.
  • Merges the unit test run into the lint script so npm run lint executes Vitest before ESLint.

Macroscope summarized 48a70dd.

Summary by CodeRabbit

  • Bug Fixes
    • Clearer, actionable network error messages for DNS, connection, timeout, and TLS failures.
    • More informative OAuth token and skill-install failure messages, including HTTP status codes and actionable retry hints.
  • New Features
    • CLI usage reporting accepts explicit config to send usage with provided host/key.
    • Skill install timeout made configurable.
  • Tests
    • Added unit tests covering error formatting, skill install errors, and usage reporting.
  • Chores
    • CI/workflow labels adjusted; package version bumped.

Network failures from fetch and spawned npx previously surfaced as
"fetch failed" or "Failed to install agent skills" with no root cause.
Added formatFetchError (unwraps undici cause codes: ENOTFOUND, ECONNREFUSED,
ETIMEDOUT, CERT_HAS_EXPIRED, etc.) and describeExecError (classifies
timeout, missing npx, DNS, TLS, 404, EACCES, ENOSPC, npm auth) so users
see what to fix instead of being told to retry.

Also fixes reportCliUsage("cli.link") being silently skipped when
--template is passed: config isn't written to cwd until after the
template subdirectory is created, so the event returned early on a
null config. reportCliUsage now accepts an explicit config, wired
through from both link branches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 12, 2026

Walkthrough

Updated network fetch error handling with a new formatFetchError and try/catch wrapping for platform and OAuth token requests; enhanced skill install error reporting and timeout constant; extended reportCliUsage to accept explicit project config and updated link command call sites; added tests and adjusted CI/test scripts.

Changes

Cohort / File(s) Summary
Network error handling
src/lib/errors.ts, src/lib/api/platform.ts, src/lib/auth.ts
Added formatFetchError() to convert fetch/undici errors into host-aware messages; wrapped fetch calls with try/catch in platformFetch, exchangeCodeForTokens, and refreshOAuthToken; improved HTTP error messages.
Skills installation & reporting
src/lib/skills.ts, src/commands/projects/link.ts, src/lib/skills.test.ts
Introduced SKILL_INSTALL_TIMEOUT_MS, describeExecError(), enriched install error logging, extended reportCliUsage to accept explicitConfig and updated link command calls to pass projectConfig; added tests for exec error descriptions and reporting behavior.
Tests & CI / packaging
src/lib/errors.test.ts, package.json, .github/workflows/ci.yml, .github/workflows/publish.yml
Added tests for formatFetchError; bumped package version to 0.1.46; changed lint script to run vitest before eslint; removed unit-test step from CI workflow and renamed lint steps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • jwfing
  • CarmenDou

Poem

🐇 I dug through logs and network trails tonight,

Fetch errors softened, their messages now right.
Skills install timed out? I name what went wrong,
Usage reports carry config along.
Hop, hop — small fixes, big clarity in sight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: actionable error messages (formatFetchError, describeExecError) and fixing cli.link event reporting on --template.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/improve-error-messages-and-link-event

Comment @coderabbitai help to get the list of available commands and usage tips.

tonychang04 and others added 2 commits April 12, 2026 09:55
…ting, gate publish on tests

Covers formatFetchError (all cause codes + fallbacks), describeExecError
(timeout/ENOENT/stderr classifiers and priority), and reportCliUsage
with an explicit config (the path that fixes the --template regression).

prepublishOnly now runs test:unit so a broken build can't be published.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`npm run lint` now runs vitest first, then eslint — so a broken test
fails the same command as a style regression. CI and the publish
workflow call `npm run lint` before build, so tests gate both paths
without an extra step.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/lib/skills.ts (1)

74-83: Consider extracting install commands into constants to prevent drift.

The executed npx command and the “run this manually” hint are currently maintained separately; centralizing command strings will keep them in sync.

♻️ Suggested refactor
+const INSTALL_AGENT_SKILLS_CMD =
+  'npx skills add insforge/agent-skills -y -a antigravity -a augment -a claude-code -a cline -a codex -a cursor -a gemini-cli -a github-copilot -a kilo -a qoder -a qwen-code -a roo -a trae -a windsurf';
+const INSTALL_FIND_SKILLS_CMD =
+  'npx skills add https://github.com/vercel-labs/skills --skill find-skills -y';

-    await execAsync('npx skills add insforge/agent-skills -y -a antigravity -a augment -a claude-code -a cline -a codex -a cursor -a gemini-cli -a github-copilot -a kilo -a qoder -a qwen-code -a roo -a trae -a windsurf', {
+    await execAsync(INSTALL_AGENT_SKILLS_CMD, {
       cwd: process.cwd(),
       timeout: SKILL_INSTALL_TIMEOUT_MS,
     });
...
-      clack.log.info('Run `npx skills add insforge/agent-skills` once resolved to see the full output.');
+      clack.log.info(`Run \`${INSTALL_AGENT_SKILLS_CMD}\` once resolved to see the full output.`);
...
-    await execAsync('npx skills add https://github.com/vercel-labs/skills --skill find-skills -y', {
+    await execAsync(INSTALL_FIND_SKILLS_CMD, {
       cwd: process.cwd(),
       timeout: SKILL_INSTALL_TIMEOUT_MS,
     });
...
-      clack.log.info('Run `npx skills add https://github.com/vercel-labs/skills --skill find-skills` once resolved.');
+      clack.log.info(`Run \`${INSTALL_FIND_SKILLS_CMD}\` once resolved.`);

Also applies to: 89-98

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/skills.ts` around lines 74 - 83, Extract the long npx install command
into a named constant (e.g., AGENT_SKILLS_INSTALL_CMD) and replace the inline
string in the execAsync call inside install logic (the call using execAsync and
SKILL_INSTALL_TIMEOUT_MS) with that constant, then reuse the same constant in
the user-facing hint passed to clack.log.info (and similarly refactor the second
occurrence around lines 89-98) so the executed command and the "run this
manually" message always stay in sync; keep describeExecError(err),
clack.log.warn and the timeout usage unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/skills.ts`:
- Around line 74-83: Extract the long npx install command into a named constant
(e.g., AGENT_SKILLS_INSTALL_CMD) and replace the inline string in the execAsync
call inside install logic (the call using execAsync and
SKILL_INSTALL_TIMEOUT_MS) with that constant, then reuse the same constant in
the user-facing hint passed to clack.log.info (and similarly refactor the second
occurrence around lines 89-98) so the executed command and the "run this
manually" message always stay in sync; keep describeExecError(err),
clack.log.warn and the timeout usage unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e2314482-010a-403c-a457-e58cb3b21fd9

📥 Commits

Reviewing files that changed from the base of the PR and between ccc5fe8 and 822034c.

📒 Files selected for processing (5)
  • src/commands/projects/link.ts
  • src/lib/api/platform.ts
  • src/lib/auth.ts
  • src/lib/errors.ts
  • src/lib/skills.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
package.json (1)

14-14: Remove --passWithNoTests to fail hard when test discovery breaks.

The --passWithNoTests flag causes Vitest to exit with code 0 when no tests are discovered, allowing the lint command to pass even if test discovery fails. This masks issues in CI/prepublish pipelines. Remove the flag to fail when no tests are found.

♻️ Proposed change
-    "lint": "vitest run --passWithNoTests && eslint src/",
+    "lint": "vitest run && eslint src/",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 14, The "lint" npm script in package.json currently
includes the Vitest flag --passWithNoTests which masks test discovery failures;
remove that flag from the "lint" script (the "lint" script value referencing
vitest run --passWithNoTests && eslint src/) so vitest fails hard when no tests
are found, leaving the rest of the script unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/skills.ts`:
- Around line 24-31: The ENOENT check in the npx error handling doesn't catch
shell-reported "npx not found" messages; update the logic around the existing
e.code === 'ENOENT' branch and the computed stderr (the stderr variable) to also
test stderr for patterns like /npx: not found/i and /'npx' is not recognized/i
(and similar OS variants), and return the same '`npx` is not on your PATH —
install Node.js 18+ and reopen your shell' message when either e.code ===
'ENOENT' OR stderr matches those patterns so missing-npx cases surfaced by
child_process.exec() are handled.

---

Nitpick comments:
In `@package.json`:
- Line 14: The "lint" npm script in package.json currently includes the Vitest
flag --passWithNoTests which masks test discovery failures; remove that flag
from the "lint" script (the "lint" script value referencing vitest run
--passWithNoTests && eslint src/) so vitest fails hard when no tests are found,
leaving the rest of the script unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e01b2428-0e4c-4ff6-a65a-0e0d3b3be2dc

📥 Commits

Reviewing files that changed from the base of the PR and between 822034c and 48a70dd.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml
  • .github/workflows/publish.yml
  • package.json
  • src/lib/errors.test.ts
  • src/lib/skills.test.ts
  • src/lib/skills.ts
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/publish.yml

Comment thread src/lib/skills.ts
Comment on lines +24 to +31
if (e.code === 'ENOENT') {
return '`npx` is not on your PATH — install Node.js 18+ and reopen your shell';
}

const stderr = (typeof e.stderr === 'string' ? e.stderr : e.stderr?.toString()) ?? '';
if (/ENOTFOUND|EAI_AGAIN|getaddrinfo/i.test(stderr)) return 'cannot reach the npm registry (DNS lookup failed) — check your internet connection';
if (/ECONNREFUSED/i.test(stderr)) return 'connection to the npm registry was refused — a proxy or firewall is likely blocking it';
if (/ETIMEDOUT|ESOCKETTIMEDOUT|network timeout/i.test(stderr)) return 'the npm registry timed out — check your VPN, proxy, or corporate network';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Node.js child_process.exec, when a command is missing (e.g., npx not installed), what values are typically returned on the error object (code, signal, stderr) on Linux/macOS and Windows?

💡 Result:

In Node.js child_process.exec, when a command like npx is missing (not found in PATH), the shell (/bin/sh on Linux/macOS, cmd.exe on Windows) fails to execute it and exits with a non-zero exit code. The error object in the callback receives this shell exit code in error.code, null signal (no signal termination), and the "command not found" message in stderr. Linux/macOS (using /bin/sh): - error.code: 127 (standard for command not found in many shells like bash/dash) - error.signal: null - error.stderr: "/bin/sh: 1: npx: not found" Windows (using cmd.exe): - error.code: 1 (cmd.exe standard non-zero for command errors) - error.signal: null - error.stderr: "'npx' is not recognized as an internal or external command, operable program or batch file." This behavior is consistent across Node.js versions per official docs: error.code is the shell's exit code for exec (non-shell spawn uses ENOENT). Examples from StackOverflow confirm code=1 for Windows command failures and code=127 implied for Unix "not found" cases like arp/node/mongodump.

Citations:


Add stderr pattern checks to catch real "npx not found" shell errors.

On Line 24–26, the condition e.code === 'ENOENT' alone will not catch missing npx. When using child_process.exec(), a missing command surfaces as a shell exit code (typically 127 on Linux/macOS, 1 on Windows) with stderr like "npx: not found" or "'npx' is not recognized". Without matching these stderr patterns, the specific "install Node.js" message is lost and a generic error displays instead.

Suggested fix
-  if (e.code === 'ENOENT') {
-    return '`npx` is not on your PATH — install Node.js 18+ and reopen your shell';
-  }
-
   const stderr = (typeof e.stderr === 'string' ? e.stderr : e.stderr?.toString()) ?? '';
+  if (
+    e.code === 'ENOENT' ||
+    /\bnpx\b.*(not found|not recognized)/i.test(stderr) ||
+    /command not found/i.test(stderr)
+  ) {
+    return '`npx` is not on your PATH — install Node.js 18+ and reopen your shell';
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (e.code === 'ENOENT') {
return '`npx` is not on your PATH — install Node.js 18+ and reopen your shell';
}
const stderr = (typeof e.stderr === 'string' ? e.stderr : e.stderr?.toString()) ?? '';
if (/ENOTFOUND|EAI_AGAIN|getaddrinfo/i.test(stderr)) return 'cannot reach the npm registry (DNS lookup failed) — check your internet connection';
if (/ECONNREFUSED/i.test(stderr)) return 'connection to the npm registry was refused — a proxy or firewall is likely blocking it';
if (/ETIMEDOUT|ESOCKETTIMEDOUT|network timeout/i.test(stderr)) return 'the npm registry timed out — check your VPN, proxy, or corporate network';
const stderr = (typeof e.stderr === 'string' ? e.stderr : e.stderr?.toString()) ?? '';
if (
e.code === 'ENOENT' ||
/\bnpx\b.*(not found|not recognized)/i.test(stderr) ||
/command not found/i.test(stderr)
) {
return '`npx` is not on your PATH — install Node.js 18+ and reopen your shell';
}
if (/ENOTFOUND|EAI_AGAIN|getaddrinfo/i.test(stderr)) return 'cannot reach the npm registry (DNS lookup failed) — check your internet connection';
if (/ECONNREFUSED/i.test(stderr)) return 'connection to the npm registry was refused — a proxy or firewall is likely blocking it';
if (/ETIMEDOUT|ESOCKETTIMEDOUT|network timeout/i.test(stderr)) return 'the npm registry timed out — check your VPN, proxy, or corporate network';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/skills.ts` around lines 24 - 31, The ENOENT check in the npx error
handling doesn't catch shell-reported "npx not found" messages; update the logic
around the existing e.code === 'ENOENT' branch and the computed stderr (the
stderr variable) to also test stderr for patterns like /npx: not found/i and
/'npx' is not recognized/i (and similar OS variants), and return the same '`npx`
is not on your PATH — install Node.js 18+ and reopen your shell' message when
either e.code === 'ENOENT' OR stderr matches those patterns so missing-npx cases
surfaced by child_process.exec() are handled.

@tonychang04 tonychang04 merged commit 6a78962 into main Apr 12, 2026
3 checks passed
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.

2 participants