Skip to content

fix(update): mark command-configured tools as needing update when skill version is missing - #1442

Open
hsusul wants to merge 4 commits into
Fission-AI:mainfrom
hsusul:fix/update-command-only-tools
Open

fix(update): mark command-configured tools as needing update when skill version is missing#1442
hsusul wants to merge 4 commits into
Fission-AI:mainfrom
hsusul:fix/update-command-only-tools

Conversation

@hsusul

@hsusul hsusul commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes an issue where openspec update (without --force) skips updating tools that are configured via command files when no skill files exist on disk (such as under delivery: "commands" or when only slash command files exist).

Reproduction

  1. Initialize or configure a project with delivery: "commands" (or a tool having only command files present under .claude/commands/opsx/ and no skill files under .claude/skills/).
  2. Write existing command files with older or out-of-date content.
  3. Run openspec update (without --force).
  4. Actual behavior: UpdateCommand computes status.configured = false in getToolVersionStatus() (since skill files are absent), so status.needsUpdate is computed as false. update.ts patches { ...status, configured: true }, but needsUpdate remains false. As a result, toolsNeedingVersionUpdate filters out the tool, toolsToUpdateSet is empty, and openspec update reports "All configured tools are up to date" without updating the command files.
  5. Expected behavior: openspec update detects that the command-configured tool requires an update (since its version is missing/untracked) and updates its command files to the current OpenSpec version.

Root Cause

In src/core/update.ts:
toolStatuses maps over configuredTools and calls getToolVersionStatus(). Because getToolVersionStatus() inspects only skill directories to find generatedByVersion and set configured, a tool configured purely by command files returns { configured: false, generatedByVersion: null, needsUpdate: false }.
When update.ts overrides configured: true via commandConfiguredSet.has(toolId), it did not recompute needsUpdate. Thus, needsUpdate remained false, causing openspec update to bypass the tool during version updates.

Implementation

In src/core/update.ts, when mapping toolStatuses, if !status.configured && commandConfiguredSet.has(toolId), we set configured: true AND re-evaluate needsUpdate (status.generatedByVersion === null || status.generatedByVersion !== OPENSPEC_VERSION).

Regression Coverage

Added a unit test in test/core/update.test.ts (should update command files when tool is configured via commands-only delivery without skills) that verifies openspec update correctly updates command files for a command-configured tool when no skill files are present.

Affected Adapters / Platforms

All tool command adapters when operating in commands-only delivery mode or when only command files are present. Tested cross-platform on Node 20+.

Validation Results

pnpm install          # Passed
pnpm test             # Passed (112 test files, 2254 tests)
pnpm run build        # Passed
pnpm run lint         # Passed (0 errors/warnings)
pnpm exec tsc --noEmit # Passed
git diff --check      # Passed

Compatibility Impact

None. Preserves all existing CLI and adapter contracts while ensuring command files are properly refreshed during openspec update.

AI-Assistance Disclosure

Developed with pair-programming assistance from Antigravity AI coding assistant.

Unrelated Changes Confirmation

Confirmed no unrelated changes, lockfile churn, or extra files were included.

Summary by CodeRabbit

  • Bug Fixes
    • Improved update/version detection to account for generated command files in command-only installations (including correct “up to date” behavior on subsequent runs).
    • Core command markdown files refresh reliably when command delivery is selected, even when no skill files exist.
  • Tests
    • Added coverage for command-only update and repeat-run “up to date” scenarios.
    • Updated CLI test runs to use an isolated config directory to keep environment behavior consistent.
  • Refactor
    • Shared command-file detection logic was centralized for drift detection.

@hsusul
hsusul requested a review from TabishB as a code owner July 26, 2026 00:57
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The update pipeline now treats generated command files as configured tool artifacts, validates workflow-specific contents, and passes delivery context through status calculations. Tests cover command-only refreshes, stale command detection, repeated no-op updates, and isolated CLI configuration.

Changes

Command-aware status and update behavior

Layer / File(s) Summary
Command status detection and shared exports
src/core/shared/tool-detection.ts, src/core/shared/index.ts
Command files are detected alongside skills, validated against selected workflows and delivery, and included in tool version status and configured-tool selection.
Update execution and drift integration
src/core/update.ts, src/core/profile-sync-drift.ts
Update status calculations receive workflow and delivery context, while profile drift reuses the shared command detection helper.
Command-only validation and isolated test configuration
test/core/shared/tool-detection.test.ts, test/core/update.test.ts, test/helpers/run-cli.ts
Tests cover command-only configuration, stale files, command refreshes without skills, repeated no-op updates, and per-invocation CLI configuration directories.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant UpdateCommand
  participant ToolVersionStatus
  participant CommandFiles
  UpdateCommand->>ToolVersionStatus: pass workflows and delivery
  ToolVersionStatus->>CommandFiles: inspect generated command files
  CommandFiles-->>ToolVersionStatus: configured and freshness state
  ToolVersionStatus-->>UpdateCommand: return update status
Loading

Possibly related PRs

Suggested reviewers: tabishb

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: command-configured tools are now treated as needing update when skill version data is missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/core/update.test.ts (1)

390-403: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert every core command is refreshed.

The test creates six command files but checks only explore.md, so it can pass while the other five remain stale. Iterate over coreCommandIds and verify each generated file changed and contains frontmatter.

Proposed test adjustment
-      const updatedContent = await fs.readFile(path.join(commandsDir, 'explore.md'), 'utf-8');
-      expect(updatedContent).not.toBe('old command content');
-      expect(updatedContent).toContain('---');
+      for (const cmdId of coreCommandIds) {
+        const updatedContent = await fs.readFile(
+          path.join(commandsDir, `${cmdId}.md`),
+          'utf-8'
+        );
+        expect(updatedContent).not.toBe('old command content');
+        expect(updatedContent).toContain('---');
+      }

Run with pnpm exec vitest run test/core/update.test.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/core/update.test.ts` around lines 390 - 403, Update the test case around
updateCommand.execute to iterate over every ID in coreCommandIds, read each
corresponding command file, and assert its content is no longer the old content
and contains frontmatter. Keep the existing commands-only configuration and
setup unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/core/update.test.ts`:
- Around line 390-403: Update the test case around updateCommand.execute to
iterate over every ID in coreCommandIds, read each corresponding command file,
and assert its content is no longer the old content and contains frontmatter.
Keep the existing commands-only configuration and setup unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 953a918a-8314-4d90-9479-6ceb5244a240

📥 Commits

Reviewing files that changed from the base of the PR and between 19d4171 and 5c37249.

📒 Files selected for processing (2)
  • src/core/update.ts
  • test/core/update.test.ts

@clay-good
clay-good requested a review from a team as a code owner July 27, 2026 17:30
@clay-good

Copy link
Copy Markdown
Collaborator

Code review — verdict: good to merge

I verified this end-to-end rather than taking the description at its word. Everything checks out.

1. Real need — yes, and I reproduced the bug

The described root cause is accurate. getToolVersionStatus() in src/core/shared/tool-detection.ts only ever looks at <skillsDir>/skills/<name>/SKILL.md, and derives needsUpdate = configured && ... where configured comes from the skill-only getToolSkillStatus(). So a commands-only project returns {configured: false, generatedByVersion: null, needsUpdate: false}, and update.ts patched configured without recomputing needsUpdate.

Reproduced against main with a real project (delivery: "commands", .claude/commands/opsx/ only, no skills dir), after corrupting one command file:

$ printf 'STALE\n' > .claude/commands/opsx/apply.md
$ openspec update
✓ All 1 tool(s) up to date (v1.6.0)
  Tools: claude

$ head -2 .claude/commands/opsx/apply.md
STALE

That's a genuine user-facing bug, and it's worse than "skips an update" — it reports up to date (v1.6.0) when no version was ever read and the files are stale. Users on commands-only delivery could never refresh their command files without --force.

With this PR applied, the same project correctly updates the file.

2. Does the fix work — yes, and the test is a real regression test

This is the part I most wanted to confirm, since a test that passes both with and without the fix proves nothing. It's a true regression test:

  • PR branch as-is: test/core/update.test.ts76 passed.
  • PR branch with only src/core/update.ts reverted to main: → 1 failed / 75 passed, failing precisely on the new case:
    FAIL  should update command files when tool is configured via commands-only delivery without skills
    AssertionError: expected 'old command content' not to be 'old command content'
    

Full suite on the PR branch (rebased on c33fcb3): 112 files / 2254 tests, all passing.

3. Breaking changes — none

The new branch is guarded by !status.configured && commandConfiguredSet.has(toolId), so it only fires for tools that have command files but no skill files. For the default delivery: "both" and for delivery: "skills", skills exist, configured is already true, the branch isn't taken, and behaviour is bit-for-bit unchanged. Confirmed by the full suite passing with zero pre-existing tests modified — the diff adds a test, it doesn't adjust any existing expectations, which is exactly what you want to see on a fix like this.

4. Scope — correctly surgical

Four lines in one .map() callback, plus one test. No new abstractions, no config surface, no adapter or template changes. This is the right shape.


One follow-up worth knowing about (not a blocker)

Command files carry no version stamp. I checked the generated frontmatter:

---
name: "OPSX: Apply"
description: Implement tasks from an OpenSpec change (Experimental)
allowed-tools: Bash(openspec:*)
category: Workflow
tags: [workflow, artifacts, experimental]
---

No generatedBy, unlike SKILL.md. So for a commands-only project generatedByVersion is permanently null, and this fix makes needsUpdate permanently true. Verified — the same project reports the same thing on every consecutive run:

--- update run 1:  Updating 1 tool(s): claude (unknown → 1.6.0)
--- update run 2:  Updating 1 tool(s): claude (unknown → 1.6.0)

So commands-only users will never see "up to date"; openspec update rewrites their command files (with identical content) every time and always displays unknown → 1.6.0.

To be clear, I don't think this should block the merge. Always-refreshing is strictly better than never-refreshing, the rewrite is content-idempotent, and given no version is recorded anywhere there's no way for this function to do better. But it does mean the fix trades one wrong report ("up to date" when stale) for a milder one ("needs update" when current).

The clean follow-up is to stamp generatedBy into command-file frontmatter the way SKILL.md does, and teach getToolVersionStatus() to fall back to reading it from a command file when no skill file exists. That would make the status accurate in both directions and let this branch compute a real version delta. Worth a separate issue — it touches the adapters, so it doesn't belong in this PR.

Nit (optional)

status.generatedByVersion === null || status.generatedByVersion !== OPENSPEC_VERSION — the first clause is subsumed by the second (null !== "1.6.0" is already true). It could just be status.generatedByVersion !== OPENSPEC_VERSION. That said, it mirrors the existing expression on tool-detection.ts:191 verbatim, so keeping it parallel is a defensible call. No change required.

@alfred-openspec alfred-openspec 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.

Commands-only installs have no persisted generatedBy version, so this makes every later update rewrite the commands and report unknown to current again; the affected code is unchanged from the reproduced two-run hold. Please persist or compare a version/content fingerprint and add a second-run no-op regression.

@hsusul

hsusul commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

I've updated the PR to fix the issue where commands-only installations re-triggered command file generation on every subsequent update.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/core/shared/tool-detection.ts (2)

116-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate COMMAND_IDS existence-scan logic.

toolHasAnyConfiguredCommand (Lines 119-129) and the version-lookup loop in getToolVersionStatus (Lines 291-301) both iterate COMMAND_IDS, resolve the adapter path, and check fs.existsSync. Consider extracting a shared helper (e.g., returning the first existing command file path) that both call sites can reuse.

♻️ Proposed extraction
+function findFirstExistingCommandFilePath(
+  projectPath: string,
+  adapter: ReturnType<typeof CommandAdapterRegistry.get>
+): string | null {
+  if (!adapter) return null;
+  for (const commandId of COMMAND_IDS) {
+    const cmdPath = adapter.getFilePath(commandId);
+    const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
+    if (fs.existsSync(fullPath)) return fullPath;
+  }
+  return null;
+}

Also applies to: 291-301

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/shared/tool-detection.ts` around lines 116 - 132, Extract the
duplicated COMMAND_IDS path-resolution and fs.existsSync scan into a shared
helper that returns the first existing command file path, using the adapter and
projectPath inputs. Update toolHasAnyConfiguredCommand and the version-lookup
loop in getToolVersionStatus to reuse this helper while preserving their current
boolean and version-status behavior.

137-159: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Drop or use delivery in areCommandFilesUpToDate.

delivery is accepted on the options signatures and passed through callers, but areCommandFilesUpToDate only reads options.workflows; keeping dead plumbing makes the API invite future behavior that currently can’t differ by delivery type.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/shared/tool-detection.ts` around lines 137 - 159, Remove the unused
delivery property from the options parameter of areCommandFilesUpToDate and
update its callers to stop passing delivery, or implement delivery-specific
behavior there; prefer removing the dead plumbing since the function currently
only selects workflows and does not vary by delivery.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/shared/tool-detection.ts`:
- Around line 283-310: Update the getToolVersionStatus call in the generation
loop to pass legacyWorkflowOverrides[tool.value] ?? desiredWorkflows, matching
the workflow set used for generation. Ensure tools with legacy overrides
evaluate command fingerprints and needsUpdate status against their tool-specific
workflows.

In `@test/helpers/run-cli.ts`:
- Line 151: The default XDG_CONFIG_HOME in runCLI() must be isolated for each
invocation. Replace the shared test/fixtures/.tmp-config fallback with a unique
temporary directory created per call, while preserving
options.env?.XDG_CONFIG_HOME when explicitly provided.

---

Nitpick comments:
In `@src/core/shared/tool-detection.ts`:
- Around line 116-132: Extract the duplicated COMMAND_IDS path-resolution and
fs.existsSync scan into a shared helper that returns the first existing command
file path, using the adapter and projectPath inputs. Update
toolHasAnyConfiguredCommand and the version-lookup loop in getToolVersionStatus
to reuse this helper while preserving their current boolean and version-status
behavior.
- Around line 137-159: Remove the unused delivery property from the options
parameter of areCommandFilesUpToDate and update its callers to stop passing
delivery, or implement delivery-specific behavior there; prefer removing the
dead plumbing since the function currently only selects workflows and does not
vary by delivery.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 12668755-8c21-45a7-814a-e165303009ac

📥 Commits

Reviewing files that changed from the base of the PR and between 5c37249 and 1f51ae5.

📒 Files selected for processing (7)
  • src/core/profile-sync-drift.ts
  • src/core/shared/index.ts
  • src/core/shared/tool-detection.ts
  • src/core/update.ts
  • test/core/shared/tool-detection.test.ts
  • test/core/update.test.ts
  • test/helpers/run-cli.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/update.ts

Comment on lines +283 to +310
const skillConfigured = getToolSkillStatus(projectRoot, toolId).configured;
const commandConfigured = toolHasAnyConfiguredCommand(projectRoot, toolId);
const configured = skillConfigured || commandConfigured;

// 2. If version is not found in skills, check command files
if (generatedByVersion === null && commandConfigured) {
const adapter = CommandAdapterRegistry.get(toolId);
if (adapter) {
for (const commandId of COMMAND_IDS) {
const cmdPath = adapter.getFilePath(commandId);
const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectRoot, cmdPath);
if (fs.existsSync(fullPath)) {
const version = extractGeneratedByVersion(fullPath);
if (version !== null) {
generatedByVersion = version;
break;
}
}
}

if (generatedByVersion === null) {
if (areCommandFilesUpToDate(projectRoot, toolId, options)) {
generatedByVersion = currentVersion;
}
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect how getToolVersionStatus is invoked relative to legacyWorkflowOverrides in update.ts
rg -n "legacyWorkflowOverrides|desiredWorkflows|getToolVersionStatus" src/core/update.ts -C3

Repository: Fission-AI/OpenSpec

Length of output: 5283


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tool-detection outline =="
ast-grep outline src/core/shared/tool-detection.ts --view expanded || true

echo "== relevant tool-detection sections =="
sed -n '240,330p' src/core/shared/tool-detection.ts
printf '\n'
rg -n "function areCommandFilesUpToDate|const areCommandFilesUpToDate|areCommandFilesUpToDate|extractGeneratedByVersion|getCommandContents|generate.*Command|CommandAdapterRegistry|COMMAND_IDS|workflow" src/core/shared/tool-detection.ts src/core -g '*.ts' -C2 | head -n 220

echo "== generate command functions =="
rg -n "function getCommandContents|getCommandContents|generateCommand" src/core/command-generation src/core/shared -g '*.ts' -C4 | head -n 260

Repository: Fission-AI/OpenSpec

Length of output: 24564


Compute getToolVersionStatus with tool-specific workflow overrides.

Lines 166-170 call getToolVersionStatus(..., { workflows: desiredWorkflows }), but the generation loop uses legacyWorkflowOverrides[tool.value] ?? desiredWorkflows. For tools with a legacy override, the command fingerprint is checked against the wrong workflow set and can yield an incorrect needsUpdate result. Pass the tool-specific workflows to the version-status call, or compute override-aware status inside getToolVersionStatus.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/shared/tool-detection.ts` around lines 283 - 310, Update the
getToolVersionStatus call in the generation loop to pass
legacyWorkflowOverrides[tool.value] ?? desiredWorkflows, matching the workflow
set used for generation. Ensure tools with legacy overrides evaluate command
fingerprints and needsUpdate status against their tool-specific workflows.

Comment thread test/helpers/run-cli.ts Outdated

@alfred-openspec alfred-openspec 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.

The repeated-regeneration bug is fixed at 1f51ae5, including the two-run no-op. One blocker remains: the new tool-detection tests call saveGlobalConfig without isolating XDG_CONFIG_HOME, so the focused test run overwrote the real ~/.config/openspec/config.json; the new runCLI fallback also shares one repo-wide .tmp-config across parallel invocations. Please give each test/temp project an isolated config home (while preserving explicit overrides), and expand the first-run regression to assert all six command files.

@hsusul

hsusul commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both blockers: tool-detection tests now isolate XDG_CONFIG_HOME within each test project, and runCLI() creates and cleans a unique config home per invocation while preserving explicit overrides. The first-run regression now checks all six core command files. Full suite passes: 112 files, 2257 tests; build, lint, typecheck, and diff checks also pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/helpers/run-cli.ts`:
- Around line 233-236: Update the cleanup logic in runCLI’s finally callback so
failures from fs.rm are caught and reported without being rethrown. Preserve the
original CLI result or error regardless of cleanup outcome, while retaining
conditional removal of isolatedConfigHome.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 99227021-e5a8-4f26-bf17-ea2ffda2dbdc

📥 Commits

Reviewing files that changed from the base of the PR and between 1f51ae5 and 7a9a88f.

📒 Files selected for processing (3)
  • test/core/shared/tool-detection.test.ts
  • test/core/update.test.ts
  • test/helpers/run-cli.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/core/shared/tool-detection.test.ts

Comment thread test/helpers/run-cli.ts
Comment on lines +233 to +236
}).finally(async () => {
if (isolatedConfigHome) {
await fs.rm(isolatedConfigHome, { recursive: true, force: true });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Prevent cleanup failures from masking the CLI result.

If fs.rm(...) rejects, .finally() replaces a successful result—or the original CLI error—with the cleanup failure. Catch and report cleanup errors without rejecting the main runCLI operation.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { type ChildProcess, spawn } from 'child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/helpers/run-cli.ts` around lines 233 - 236, Update the cleanup logic in
runCLI’s finally callback so failures from fs.rm are caught and reported without
being rethrown. Preserve the original CLI result or error regardless of cleanup
outcome, while retaining conditional removal of isolatedConfigHome.

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