Skip to content

Conversation

@ClankerGuru
Copy link
Owner

@ClankerGuru ClankerGuru commented Dec 28, 2025

Summary

  • Migrate from Node.js/pnpm to Bun runtime
  • Change package name to @clanker-guru/openspec
  • Reset version to 0.0.1-alpha.1 (fresh start)
  • Simplify CI workflows
  • Fix publish workflow to use NPM_CONFIG_TOKEN
  • Update README (remove Discord, add @ClankerGuru X handle)

Summary by CodeRabbit

  • New Features

    • Added opencode automation and a Publish workflow to support Bun-based package publishing.
  • Documentation

    • Updated README, install/usage examples, badges, and author/repo references to use Bun and the new owner.
  • Refactor

    • Migrated CI, devcontainer, build and test infrastructure to Bun; tests switched to the Bun runner and CLI adapted accordingly.
  • Chores

    • Removed Changesets release automation and configs; package renamed to the new scope/version.

✏️ Tip: You can customize this high-level summary in your review settings.

slop and others added 8 commits December 27, 2025 19:29
- Rename package from @fission-ai/openspec to @clanker-guru/openspec
- Switch all scripts and tooling to use bun instead of npm/pnpm
- Convert test framework from vitest to bun:test
- Migrate all JavaScript files to TypeScript (.ts)
- Update CI/CD workflows for bun runtime
- Remove npm, node, pnpm, vitest, and changesets dependencies
- Add bun.lock and bun-based devcontainer configuration
- Update version to 0.0.1-alpha.1 (fresh start for @clanker-guru/openspec)
- Simplify CI workflow: unified matrix testing across OS
- Fix publish workflow: use NPM_CONFIG_TOKEN, add --tag and --tolerate-republish
- Make bin/openspec.ts executable
@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

Warning

Rate limit exceeded

@ClankerGuru has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a5cba5e and 4c8e89b.

📒 Files selected for processing (1)
  • test/commands/validate.test.ts
📝 Walkthrough

Walkthrough

Migrates repository tooling and CI from Node/PNPM/Vitest to Bun: replaces build, pack, postinstall, CLI shebangs, tests, devcontainer and workflows; removes Changesets and Vitest configs; updates package metadata and many tests to use bun and OS-temporary directories.

Changes

Cohort / File(s) Summary
Package metadata & docs
package.json, README.md, .changeset/README.md, .changeset/config.json
Rename package to @clanker-guru/openspec, version reset; README switched to Bun commands and repo/badge updates; changesets README and config removed.
Build, pack & postinstall scripts
build.js (deleted), build.ts, scripts/pack-version-check.mjs (deleted), scripts/pack-version-check.ts, scripts/postinstall.ts
Node-based build/pack/postinstall removed and reimplemented for Bun/TypeScript; pack-version-check rewritten to use bun tooling; build orchestration moved to build.ts.
CLI entrypoints
bin/openspec.js (removed), bin/openspec.ts
CLI shebangs and executable entry updated to Bun; bin path updated in package.json.
Devcontainer & linting
.devcontainer/devcontainer.json, .devcontainer/README.md, eslint.config.ts
Devcontainer base image and remoteUser changed to Bun, post-create command → bun install, SSH path updated; ESLint ignores narrowed (now lint .js/.mjs).
GitHub workflows
.github/workflows/ci.yml, .github/workflows/release-prepare.yml (deleted), .github/workflows/publish.yml, .github/workflows/opencode.yml
CI simplified and switched to Bun; release-prepare workflow removed; new Publish workflow (bun-based) and opencode comment-trigger workflow added.
Test tooling & configs
vitest.config.ts (deleted), vitest.setup.ts (deleted), many test/**/*.test.ts files
Vitest config & setup removed; tests migrated from vitest to bun:test; mocking moved to Bun mock/spyOn; many tests updated to use OS temp dirs and randomUUID(); CLI test invocations use bun.
Test helpers & runner
test/helpers/run-cli.ts, test/utils/*, test/core/*, test/commands/*
Runner and helpers updated to call bun for build and CLI runs; added cliProjectRoot export; Windows shell handling added for spawned bun processes; many test-support changes to reflect Bun runtime.
Source/type adjustments
src/core/init.ts, src/core/templates/index.ts
ProjectContext converted to type-only imports/exports to remove runtime coupling; no behavior change.
Miscellaneous removals/rewrites
build.js, scripts/pack-version-check.mjs, bin/openspec.js, vitest.*
Several Node/PNPM/Vitest-specific files deleted and replaced with Bun/TypeScript equivalents.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I hopped from Node to Bun tonight,
Swapped shebangs, tests, and CI in flight.
Temp folders bloom with UUIDs bright,
Scripts rewritten, pack checks light.
The rabbit grins — builds now take flight.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main changes: migrating from Node.js/pnpm to Bun runtime and renaming the package to @clanker-guru/openspec.

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

Copy link

@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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/commands/validate.enriched-output.test.ts (1)

29-30: Cross-platform compatibility issue with shell commands.

The use of mkdir -p and bash -lc with heredoc syntax will fail on Windows. Since the CI matrix now includes windows-latest, consider using Node.js/Bun fs APIs instead:

Suggested cross-platform fix
-    execSync(`mkdir -p ${changePath}`);
-    execSync(`bash -lc "cat > ${path.join(changePath, 'proposal.md')} <<'EOF'\n${changeContent}\nEOF"`);
+    await fs.mkdir(changePath, { recursive: true });
+    await fs.writeFile(path.join(changePath, 'proposal.md'), changeContent, 'utf-8');

Note: The test function signature would need to be async (which it can be in bun:test).

🧹 Nitpick comments (8)
test/core/commands/change-command.show-validate.test.ts (2)

49-58: Simplify the type coercion.

The type coercion undefined as unknown as string is unnecessary. According to the function signature in the relevant code snippet, changeName is optional (changeName?: string), so you can pass undefined directly without casting.

🔎 Proposed simplification
-    await cmd.show(undefined as unknown as string, { json: false, noInteractive: true });
+    await cmd.show(undefined, { json: false, noInteractive: true });

60-74: Consider verifying the deprecation warning message.

The test validates that the deprecated --requirements-only flag still works correctly, but doesn't verify that the deprecation warning is actually printed. According to the relevant code snippet, the implementation should print: "Flag --requirements-only is deprecated; use --deltas-only instead."

Adding a verification would ensure the warning isn't accidentally removed and helps users migrate to the new flag.

🔎 Suggested addition to verify deprecation warning

After line 63, add verification of the deprecation warning:

await cmd.show(changeName, { json: true, requirementsOnly: true });

// Verify deprecation warning is printed to stderr
const warningOutput = consoleErrorSpy.mock.calls.flat().join('\n');
expect(warningOutput).toMatch(/--requirements-only is deprecated/);

const output = consoleSpy.mock.calls.flat().join('\n');
.devcontainer/devcontainer.json (1)

3-3: Consider pinning Bun container version for stability.

Using oven/bun:latest may introduce breaking changes when Bun releases new versions. Consider pinning to a specific version (e.g., oven/bun:1.1 or a specific semver) to ensure consistent development environments across the team and prevent unexpected breakage.

🔎 Recommended version pinning
-  "image": "oven/bun:latest",
+  "image": "oven/bun:1.1",

Or check the latest stable version:

What is the latest stable version of oven/bun Docker image?
test/core/update.test.ts (1)

1655-1662: Replace vi.spyOn with spyOn for consistency.

The code uses spyOn from bun:test throughout the file, but lines 1655-1662 use vi.spyOn. For consistency with bun:test patterns and the rest of the file, use the standalone spyOn function.

🔎 Proposed consistency fix
-    const writeSpy = vi
-      .spyOn(FileSystemUtils, 'writeFile')
-      .mockImplementation(async (filePath, content) => {
+    const writeSpy = spyOn(FileSystemUtils, 'writeFile').mockImplementation(
+      async (filePath, content) => {
         if (filePath.endsWith('CLAUDE.md')) {
           throw new Error('EACCES: permission denied, open');
         }
         return originalWriteFile(filePath, content);
-      });
+      }
+    );
test/core/archive.test.ts (1)

555-556: Clarify intent of standalone fs.access calls.

These lines call await fs.access(changeDir) without assertions. If the intent is to verify the directory still exists (wasn't archived), consider making this explicit:

-      await fs.access(changeDir);
+      // Verify change was NOT archived (directory still exists)
+      await expect(fs.access(changeDir)).resolves.toBeUndefined();

This improves readability and makes the test intent clear.

Also applies to: 672-673, 796-797

test/commands/completion.test.ts (1)

36-37: Consider aligning mock result types to avoid as any casts.

The as any casts on mockResolvedValue suggest the mock result types don't fully match the expected return types. Consider updating mockInstallResult to match the actual InstallResult interface.

🔎 Type-safe mock result suggestion
+import type { InstallResult, UninstallResult } from '../../src/core/completions/installers/base-installer.js';

-const mockInstallResult = {
+const mockInstallResult: InstallResult = {
   success: true,
   installedPath: '/home/user/.oh-my-zsh/completions/_openspec',
   isOhMyZsh: true,
   message: 'Completion script installed successfully for Oh My Zsh',
   instructions: [
     'Completion script installed to Oh My Zsh completions directory.',
     'Restart your shell or run: exec zsh',
     'Completions should activate automatically.',
   ],
 };

Also applies to: 198-202, 214-217

.github/workflows/publish.yml (1)

36-42: Prefer jq or ESM syntax for package.json manipulation.

The inline script uses require() which relies on Bun's CommonJS compatibility. For robustness, consider using jq:

🔎 Alternative using jq
       - name: Set version
         run: |
-          bun -e "
-            const pkg = require('./package.json');
-            pkg.version = '${{ inputs.version }}';
-            require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
-          "
+          jq --arg v "${{ inputs.version }}" '.version = $v' package.json > tmp.json && mv tmp.json package.json

Or use ESM imports with Bun:

🔎 Alternative using ESM
       - name: Set version
         run: |
-          bun -e "
-            const pkg = require('./package.json');
-            pkg.version = '${{ inputs.version }}';
-            require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2) + '\n');
-          "
+          bun -e "
+            import pkg from './package.json';
+            pkg.version = '${{ inputs.version }}';
+            await Bun.write('./package.json', JSON.stringify(pkg, null, 2) + '\n');
+          "
test/commands/spec.interactive-show.test.ts (1)

30-43: Consider explicit environment key restoration.

Reassigning process.env = originalEnv doesn't remove keys that were added during the test. While this is a pre-existing pattern, consider explicitly deleting or restoring the specific key for cleaner test isolation:

Suggested approach
-      const originalEnv = { ...process.env };
+      const originalInteractive = process.env.OPEN_SPEC_INTERACTIVE;
       // ...
-      process.env = originalEnv;
+      if (originalInteractive === undefined) {
+        delete process.env.OPEN_SPEC_INTERACTIVE;
+      } else {
+        process.env.OPEN_SPEC_INTERACTIVE = originalInteractive;
+      }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8dcd170 and 0b446d2.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (61)
  • .changeset/README.md
  • .changeset/config.json
  • .devcontainer/README.md
  • .devcontainer/devcontainer.json
  • .github/workflows/ci.yml
  • .github/workflows/opencode.yml
  • .github/workflows/publish.yml
  • .github/workflows/release-prepare.yml
  • README.md
  • bin/openspec.js
  • bin/openspec.ts
  • build.js
  • build.ts
  • eslint.config.ts
  • package.json
  • scripts/pack-version-check.mjs
  • scripts/pack-version-check.ts
  • scripts/postinstall.ts
  • src/core/init.ts
  • src/core/templates/index.ts
  • test/cli-e2e/basic.test.ts
  • test/commands/change.interactive-show.test.ts
  • test/commands/change.interactive-validate.test.ts
  • test/commands/completion.test.ts
  • test/commands/config.test.ts
  • test/commands/show.test.ts
  • test/commands/spec.interactive-show.test.ts
  • test/commands/spec.interactive-validate.test.ts
  • test/commands/spec.test.ts
  • test/commands/validate.enriched-output.test.ts
  • test/commands/validate.test.ts
  • test/core/archive.test.ts
  • test/core/artifact-graph/graph.test.ts
  • test/core/artifact-graph/resolver.test.ts
  • test/core/artifact-graph/schema.test.ts
  • test/core/artifact-graph/state.test.ts
  • test/core/artifact-graph/workflow.integration.test.ts
  • test/core/commands/change-command.list.test.ts
  • test/core/commands/change-command.show-validate.test.ts
  • test/core/completions/completion-provider.test.ts
  • test/core/completions/generators/zsh-generator.test.ts
  • test/core/completions/installers/zsh-installer.test.ts
  • test/core/config-schema.test.ts
  • test/core/converters/json-converter.test.ts
  • test/core/global-config.test.ts
  • test/core/init.test.ts
  • test/core/list.test.ts
  • test/core/parsers/change-parser.test.ts
  • test/core/parsers/markdown-parser.test.ts
  • test/core/update.test.ts
  • test/core/validation.enriched-messages.test.ts
  • test/core/validation.test.ts
  • test/core/view.test.ts
  • test/helpers/run-cli.ts
  • test/utils/change-utils.test.ts
  • test/utils/file-system.test.ts
  • test/utils/interactive.test.ts
  • test/utils/marker-updates.test.ts
  • test/utils/shell-detection.test.ts
  • vitest.config.ts
  • vitest.setup.ts
💤 Files with no reviewable changes (8)
  • .changeset/config.json
  • bin/openspec.js
  • vitest.setup.ts
  • scripts/pack-version-check.mjs
  • .github/workflows/release-prepare.yml
  • .changeset/README.md
  • vitest.config.ts
  • build.js
🧰 Additional context used
🧬 Code graph analysis (4)
test/core/archive.test.ts (1)
src/core/archive.ts (1)
  • ArchiveCommand (19-625)
test/utils/file-system.test.ts (1)
src/utils/file-system.ts (1)
  • FileSystemUtils (44-209)
scripts/postinstall.ts (1)
src/utils/shell-detection.ts (1)
  • detectShell (21-62)
test/core/update.test.ts (1)
src/utils/file-system.ts (1)
  • FileSystemUtils (44-209)
🔇 Additional comments (83)
test/utils/change-utils.test.ts (3)

1-1: ✓ Clean migration to bun:test.

The import statement correctly transitions from vitest to bun:test, with all required test utilities (describe, it, expect, beforeEach, afterEach) properly imported. The migration is mechanical and accurate.


6-6: ✓ Source import path correct for Bun module resolution.

The .js extension on the TypeScript source import is the correct pattern for ESM in Bun and will resolve properly to the actual change-utils.ts file with standard TypeScript configuration.


114-121: ✓ Proper test setup/teardown with isolated temp directories.

The beforeEach/afterEach pattern correctly creates unique temporary directories per test (using randomUUID) and properly cleans them up with recursive force removal, preventing test pollution and resource leaks.

src/core/init.ts (1)

16-17: LGTM! Clean separation of runtime and type-only imports.

This change correctly splits the import to keep TemplateManager as a runtime import while importing ProjectContext as a type-only dependency. This aligns with the concurrent change in src/core/templates/index.ts and follows TypeScript best practices for reducing runtime coupling while preserving type safety.

src/core/templates/index.ts (1)

49-49: LGTM! Appropriate type-only export.

Converting ProjectContext to a type-only export is correct. The module still imports ProjectContext as a value at line 2 for internal use in TemplateManager.getTemplates, but external consumers (like src/core/init.ts) only need it as a type annotation, not as a runtime value. This change reduces runtime coupling while maintaining full type safety.

test/core/artifact-graph/state.test.ts (1)

1-1: LGTM! Clean test framework migration.

The import change from Vitest to Bun's built-in test runner is correct. The test logic remains unchanged, which is the appropriate approach for a framework migration.

test/core/commands/change-command.list.test.ts (1)

1-1: LGTM! Migration to bun:test is complete and consistent.

The test framework import has been correctly updated from vitest to bun:test. The APIs used (describe, it, expect, beforeAll, afterAll) are fully compatible, and across the 38 test files in the codebase, all have been consistently migrated to bun:test with no remaining vitest imports.

test/commands/spec.interactive-validate.test.ts (2)

1-1: LGTM! Clean migration to Bun test framework.

The import changes correctly migrate from Vitest to bun:test, and the addition of os and randomUUID enables proper test isolation through unique temporary directories.

Also applies to: 5-6


10-11: Excellent test isolation strategy.

Converting testDir and specsDir to let and initializing them dynamically with randomUUID() in beforeEach ensures proper test isolation and prevents conflicts between concurrent test runs.

Also applies to: 16-17

test/core/commands/change-command.show-validate.test.ts (6)

1-1: LGTM: Clean migration to bun:test imports.

The test framework migration from Vitest to bun:test is correct, and the addition of randomUUID for temp directory isolation is a best practice for preventing conflicts in parallel test execution.

Also applies to: 6-6


13-14: LGTM: Proper spy type declarations.

Using ReturnType<typeof spyOn> correctly captures the bun:test spy type and enables type-safe access to mock methods and properties.


16-28: LGTM: Comprehensive test setup with proper isolation.

The beforeEach hook correctly:

  • Creates isolated temp directories using randomUUID for parallel-safe execution
  • Sets up test fixtures with appropriate directory structure
  • Initializes console spies to capture output without polluting test logs
  • Manages process working directory appropriately

30-36: LGTM: Thorough cleanup with proper state restoration.

The afterEach hook correctly restores all test state:

  • Mock restoration in reverse order of setup
  • Working directory restoration
  • Resetting process.exitCode to prevent test interference
  • Cleanup of temp directories with force: true for reliability

38-47: LGTM: Correct spy-based output validation.

The test properly uses consoleSpy.mock.calls.flat().join('\n') to retrieve console output and validates the JSON structure. This pattern correctly handles multiple console.log calls and is idiomatic for bun:test.


76-86: LGTM: Proper validation of JSON report structure.

The test correctly uses the spy-based approach to capture and validate the JSON report output, ensuring both the valid boolean and issues array are present in the response.

test/utils/shell-detection.test.ts (1)

1-1: Clean migration to Bun test framework.

The import change from Vitest to bun:test is straightforward, and the test APIs are compatible. No issues observed.

test/core/artifact-graph/resolver.test.ts (1)

1-1: Clean migration to Bun test framework.

The test framework migration is straightforward with compatible APIs.

test/utils/interactive.test.ts (1)

1-1: Clean migration to Bun test framework.

The import change is correct and test logic remains intact.

test/core/parsers/markdown-parser.test.ts (1)

1-1: Clean migration to Bun test framework.

Import change is correct and preserves all test functionality.

test/utils/marker-updates.test.ts (1)

1-1: Clean migration to Bun test framework.

The framework migration is correctly implemented.

test/core/view.test.ts (1)

1-1: Clean migration to Bun test framework.

Import change is correct and maintains test compatibility.

test/core/completions/installers/zsh-installer.test.ts (1)

1-1: Clean migration to Bun test framework.

The framework migration preserves all test logic correctly.

test/cli-e2e/basic.test.ts (1)

1-1: Clean migration to Bun test framework.

The test framework migration is correctly implemented. All test APIs (describe, it, expect, afterAll) are compatible between Vitest and bun:test.

test/core/list.test.ts (1)

1-1: LGTM!

The migration to bun:test is consistent with the project-wide testing framework update.

test/core/validation.test.ts (1)

1-1: LGTM!

Test framework migration is consistent with the broader Bun adoption across the test suite.

test/core/completions/completion-provider.test.ts (1)

1-1: LGTM!

The test framework migration maintains the existing test structure and timing-sensitive test logic.

test/core/artifact-graph/schema.test.ts (1)

1-1: LGTM!

Test framework migration aligns with the project-wide Bun adoption.

test/commands/validate.test.ts (1)

1-1: LGTM!

The test framework migration is consistent with the project-wide changes. The CLI execution tests should work correctly with Bun's test runner.

test/core/validation.enriched-messages.test.ts (1)

1-1: LGTM!

Test framework migration maintains existing validation test logic.

bin/openspec.ts (1)

1-3: Build setup is correctly configured.

The Bun shebang and import structure are properly configured for the migration:

  • dist/cli/index.js will be built by the bun run build command (tsconfig.json configured with rootDir="./src" and outDir="./dist")
  • bin/openspec.ts has executable permissions (verified: -rwxr-xr-x)
  • package.json bin field correctly references ./bin/openspec.ts

The import path ../dist/cli/index.js correctly resolves from the bin/ directory to the compiled output location.

test/core/converters/json-converter.test.ts (1)

1-1: Test framework is properly configured for Bun's test runner.

The import from bun:test is correct, and all test primitives (describe, it, expect, beforeEach, afterEach) are compatible. The assertions used throughout the file (toBe, toContain, toHaveLength, toBeDefined, not.toThrow) are all supported by Bun's test framework.

test/core/artifact-graph/graph.test.ts (1)

1-1: LGTM! Clean test framework migration.

The import change from Vitest to bun:test is consistent with the PR's migration objectives.

test/core/config-schema.test.ts (1)

1-1: LGTM! Test framework migration.

The switch to bun:test is appropriate and aligns with the PR objectives.

test/core/parsers/change-parser.test.ts (1)

1-1: LGTM! Test framework migration.

test/core/completions/generators/zsh-generator.test.ts (1)

1-1: LGTM! Test framework migration.

Adding beforeEach to the bun:test imports is appropriate for the test setup pattern used in this file.

test/utils/file-system.test.ts (2)

1-1: LGTM! Test framework migration.


37-42: Pattern is safe; bun:test properly fails tests on unhandled promise rejections.

The implicit test pattern (awaiting the call without explicit assertion) is valid in bun:test. The test runtime automatically catches unhandled promise rejections during test execution and marks the test as failed, so if FileSystemUtils.createDirectory(dirPath) throws, the test will fail as expected. This pattern is also consistent with other tests in the codebase.

test/core/global-config.test.ts (2)

1-1: LGTM! Test framework migration.

The addition of mock and spyOn imports from bun:test is appropriate for the spy/mock patterns used in this test suite.


18-18: LGTM! Proper spy API migration.

The changes correctly migrate from Vitest's vi.spyOn to Bun's spyOn API, including the appropriate type annotations.

Also applies to: 29-29

test/core/artifact-graph/workflow.integration.test.ts (1)

1-1: LGTM! Test framework migration.

.devcontainer/devcontainer.json (1)

40-46: LGTM! Dev container migration to Bun is well-executed.

All changes are consistent:

  • Bun image and runtime
  • Updated user from node to bun
  • SSH mount path correctly adjusted
  • Package installation using bun install
.devcontainer/README.md (1)

1-87: LGTM! Documentation accurately reflects Bun migration.

All command examples and references have been properly updated from Node.js/pnpm to Bun, including:

  • Runtime and package manager descriptions
  • Setup instructions
  • Daily development commands
  • Container user references

The documentation is clear, consistent, and helpful for developers.

test/core/update.test.ts (1)

10-24: Excellent addition of robust log message finder!

The findLogMessage helper function is a great improvement that makes tests more resilient to parallel execution and call order variations. It searches through all spy calls instead of assuming a specific index, which prevents flaky test failures.

scripts/pack-version-check.ts (2)

1-73: LGTM! Version check script is well-implemented.

The script correctly:

  • Uses Bun runtime with proper shebang
  • Creates a packed tarball with bun pm pack
  • Sets up an isolated test environment
  • Installs and verifies the packed version
  • Cleans up resources properly in finally block
  • Provides clear error messages

The logic is sound and the implementation follows best practices.


46-47: Bin path is correctly configured.

The path node_modules/@clanker-guru/openspec/bin/openspec.ts matches the package.json bin field ("openspec": "./bin/openspec.ts"). When the package is installed, the bin file will be placed at this location. Bun executes the TypeScript file directly via the #!/usr/bin/env bun shebang without requiring pre-compilation.

eslint.config.ts (1)

33-33: The ESLint configuration is correctly restricted to TypeScript files only via files: ['src/**/*.ts'] and will not lint any JavaScript files. The ignores section appropriately excludes dist/** and node_modules/**. No .js or .mjs files exist in the repository that would be affected by the ignores configuration.

Likely an incorrect or invalid review comment.

test/commands/config.test.ts (1)

36-36: No test isolation issue from removing vi.resetModules().

The functions in global-config.ts read process.env.XDG_CONFIG_HOME dynamically at call time (not at module initialization), so module caching does not affect test isolation. Each test properly sets and restores the environment variable in beforeEach/afterEach, ensuring tests work correctly without module cache resets.

Likely an incorrect or invalid review comment.

.github/workflows/opencode.yml (1)

1-31: LGTM! OpenCode integration looks good.

The workflow is well-configured with:

  • Appropriate trigger events for issue and PR review comments
  • Flexible condition checking for /oc and /opencode commands
  • Properly scoped permissions
  • Correct action usage with API key from secrets

The use of sst/opencode/github@latest aligns with the official action documentation's recommended approach.

test/commands/change.interactive-validate.test.ts (1)

36-36: No changes needed — the test's use of execSync is cross-platform and not affected by the Windows-specific spawn/timeout fixes mentioned in the PR.

The Windows fixes in commit 0b446d2 addressed spawn() hanging issues in the helper file (test/helpers/run-cli.ts), which uses shell: process.platform === 'win32' and timeout management. This test uses execSync, a simpler synchronous API that avoids spawn-related complexity. Multiple other tests throughout the codebase use the identical execSync(bun ${path}pattern, and the CI test matrix includeswindows-latest`, confirming cross-platform compatibility.

test/helpers/run-cli.ts (3)

62-62: LGTM on Bun build command migration.

The build invocation correctly switches to Bun runtime.


75-104: Good consolidation of timeout handling.

The DEFAULT_TIMEOUT_MS constant (30s) is reasonable for CLI tests, and the timeout logic is cleanly structured. The Windows shell handling on line 93 ensures cross-platform compatibility.


84-84: CI workflows already configure Bun setup before test execution via oven-sh/setup-bun@v2 on all platforms (ubuntu-latest, macos-latest, windows-latest), so this concern is already addressed.

test/core/archive.test.ts (3)

1-7: Clean migration to bun:test with improved test isolation.

Good use of randomUUID for unique temp directories, preventing test interference.


362-378: Good pattern: spy restoration in finally block.

The try/finally pattern ensures spies are restored even if assertions fail, preventing test pollution.


10-13: Bun's mock.module correctly intercepts dynamic imports.

Verified that Bun's mock.module API works with dynamic import() calls. The test properly mocks both select and confirm from '@inquirer/prompts' before ArchiveCommand.selectChange executes its dynamic imports, so the mocked functions will be returned instead of the original module.

test/core/init.test.ts (4)

1-1: Consistent bun:test migration with proper mock patterns.

The mock() function usage for the prompt mock is appropriate for Bun's API.

Also applies to: 13-18


46-46: Note: mockClear vs mockReset behavior difference.

Bun's mockClear clears call history but preserves implementation, while Vitest's mockReset also resets the implementation. Verify this is the intended behavior for mockPrompt.


59-60: Good documentation of Bun spy behavior.

The comment explaining that spyOn does NOT auto-restore is helpful for maintainers.


1647-1667: Proper error handling test with cleanup.

The try/finally pattern ensures writeFileSpy is restored even if the test fails, and the test correctly validates permission error handling.

test/commands/completion.test.ts (2)

1-5: Clean migration to targeted spying pattern.

Using spyOn(ZshInstaller.prototype, 'method') is more precise than broad module mocking and aligns with Bun's testing philosophy.


31-47: Complete spy lifecycle management.

All spies are created in beforeEach and restored in afterEach, preventing test pollution.

README.md (2)

133-144: Documentation correctly reflects Bun migration.

The installation instructions are clear, showing both bunx for one-time use and bun install -g for global installation.


377-380: Contributing section updated consistently.

All development commands correctly use Bun equivalents.

test/commands/change.interactive-show.test.ts (2)

1-6: Consistent test isolation pattern.

Using randomUUID() for temp directories ensures parallel test runs don't interfere.


36-36: CLI invocation updated to Bun.

The bun ${bin} pattern is consistent with the project's Bun migration.

test/commands/show.test.ts (2)

1-6: Well-structured test setup with proper isolation and cleanup.

The beforeEach/afterEach pattern with UUID-based temp directories is consistent across the test suite.

Also applies to: 16-34


44-44: All CLI invocations consistently use Bun.

The bun ${openspecBin} pattern is applied uniformly across all test cases.

Also applies to: 63-63, 76-76, 97-97, 115-115

.github/workflows/publish.yml (2)

1-14: Well-structured manual publish workflow.

The workflow_dispatch trigger with version and tag inputs provides controlled releases.


44-47: bun publish is a supported command with official documentation and is used correctly in this workflow. No action needed.

test/commands/spec.test.ts (3)

1-6: LGTM! Clean migration to bun:test with proper test isolation.

The switch to bun:test imports and the addition of os and randomUUID for generating unique per-test temporary directories is a solid pattern for test isolation.


10-17: Good isolation pattern with unique temp directories.

The dynamic testDir and specsDir initialization per test run using os.tmpdir() and randomUUID() ensures proper test isolation and avoids conflicts in parallel test execution.


66-68: Consistent Bun CLI invocation across spec show tests.

The migration from node to bun for CLI invocation is consistently applied across all spec show test cases.

test/commands/spec.interactive-show.test.ts (1)

1-6: LGTM! Consistent migration pattern.

The test file follows the same migration pattern as other test files with proper bun:test imports and dynamic temp directory setup.

test/commands/validate.enriched-output.test.ts (1)

1-6: LGTM! Consistent migration pattern.

The test file follows the same Bun migration conventions as the other test files.

.github/workflows/ci.yml (2)

17-36: Clean CI structure with cross-platform matrix.

The test job structure with OS matrix and fail-fast: false is appropriate for catching platform-specific issues. The 10-minute timeout is reasonable for a test suite.


38-54: LGTM! Lint job properly configured.

Running lint and typecheck on a single OS (ubuntu-latest) is efficient, and building before typecheck ensures necessary artifacts are available.

package.json (2)

2-3: Package rename and version reset acknowledged.

The rename to @clanker-guru/openspec and version reset to 0.0.1-alpha.1 aligns with the fork/migration objectives.


55-61: LGTM! Clean devDependencies for Bun-based development.

The updated devDependencies properly reflect the Bun migration, with @types/bun for type support and jiti for ESLint TypeScript config loading.

scripts/postinstall.ts (5)

1-1: LGTM! Bun shebang for direct execution.

The #!/usr/bin/env bun shebang enables direct execution of the TypeScript file via Bun.


10-20: LGTM! Clean skip logic with structured return type.

The shouldSkipInstallation function properly checks for CI environments and respects the OPENSPEC_NO_COMPLETIONS environment variable.


32-40: LGTM! Proper shell detection wrapper with graceful fallback.

The function correctly extracts the shell property from the detection result and silently falls back to undefined on errors, which is appropriate for postinstall behavior.


42-76: LGTM! User-friendly completion installation with helpful guidance.

The function provides clear, context-specific instructions for different zsh configurations (Oh My Zsh, manual fpath setup) and gracefully falls back to a manual tip on failure.


78-103: LGTM! Robust main flow with graceful error handling.

The orchestration flow is clean, and the top-level .catch(() => process.exit(0)) ensures that postinstall errors never break the package installation—an important property for postinstall scripts.

Comment on lines +28 to +30
- uses: oven-sh/setup-bun@v2
with:
version: 9

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'

- name: Print environment diagnostics
run: |
node -p "JSON.stringify({ platform: process.platform, arch: process.arch, shell: process.env.SHELL || process.env.ComSpec || '' })"
bun-version: latest
Copy link

@coderabbitai coderabbitai bot Dec 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

latest stable version of Bun runtime December 2025

💡 Result:

The latest stable Bun runtime is v1.3.5 — released December 17, 2025. [1][2]

Sources:
[1] Bun release notes: Bun v1.3.5.
[2] endoflife.date entry for Bun (shows latest 1.3.5, Dec 17, 2025).


Consider pinning Bun version for reproducible builds.

Using bun-version: latest may lead to unpredictable CI behavior when Bun releases updates. Consider pinning to a specific version for more reproducible builds:

Suggested fix
       - uses: oven-sh/setup-bun@v2
         with:
-          bun-version: latest
+          bun-version: "1.3.x"
📝 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
- uses: oven-sh/setup-bun@v2
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Print environment diagnostics
run: |
node -p "JSON.stringify({ platform: process.platform, arch: process.arch, shell: process.env.SHELL || process.env.ComSpec || '' })"
bun-version: latest
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.x"
🤖 Prompt for AI Agents
.github/workflows/ci.yml around lines 28 to 30: the workflow pins Bun with
bun-version: latest which can cause non-reproducible CI when Bun updates; change
this to a specific, tested Bun version (e.g., a fixed semantic version or
commit) by replacing "latest" with the chosen version string and update any
related workflow docs or matrix entries so CI consistently uses that pinned
version.

Copy link
Owner Author

Choose a reason for hiding this comment

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

YOLO my fren

Copy link

Choose a reason for hiding this comment

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

@ClankerGuru, got it! I'll keep that in mind for future reviews. 🚀


✏️ Learnings added
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: .github/workflows/ci.yml:28-30
Timestamp: 2025-12-28T09:37:00.257Z
Learning: In the clanker-guru/openspec project CI workflows, the maintainers prefer using `bun-version: latest` rather than pinning to a specific Bun version, accepting the trade-off for staying current with the latest Bun releases.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:29-31
Timestamp: 2025-12-28T09:27:28.326Z
Learning: The clanker-guru/openspec package is intentionally designed to run exclusively on the Bun runtime. The bin entry points to a TypeScript file (bin/openspec.ts) with a Bun shebang (#!/usr/bin/env bun), and this is by design, not a compatibility issue.

Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:51-51
Timestamp: 2025-12-28T09:28:29.872Z
Learning: The clanker-guru/openspec package maintainers explicitly do not want npm compatibility or fallbacks. The package is intentionally Bun-exclusive, and npm-based workflows should not be suggested.

Comment on lines +14 to +21
try {
await $`bun x tsc --version`;
await $`bun x tsc`;
console.log('\n✅ Build completed successfully!');
} catch {
console.error('\n❌ Build failed!');
process.exit(1);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Log the error before exiting.

The catch block at line 18 suppresses all error details from TypeScript compilation failures. Without logging the error, developers won't see what went wrong during the build.

🔎 Proposed fix to log compilation errors
 console.log('Compiling TypeScript...');
 try {
   await $`bun x tsc --version`;
   await $`bun x tsc`;
   console.log('\n✅ Build completed successfully!');
-} catch {
+} catch (error) {
   console.error('\n❌ Build failed!');
+  console.error(error);
   process.exit(1);
 }
📝 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
try {
await $`bun x tsc --version`;
await $`bun x tsc`;
console.log('\n✅ Build completed successfully!');
} catch {
console.error('\n❌ Build failed!');
process.exit(1);
}
try {
await $`bun x tsc --version`;
await $`bun x tsc`;
console.log('\n✅ Build completed successfully!');
} catch (error) {
console.error('\n❌ Build failed!');
console.error(error);
process.exit(1);
}
🤖 Prompt for AI Agents
In build.ts around lines 14 to 21, the catch block currently swallows the
TypeScript compilation error; update the catch to accept the error (e.g., catch
(err)) and log a helpful message and the error details before calling
process.exit(1) so developers can see the compilation failure and its
stack/diagnostics.

Comment on lines 29 to 31
"bin": {
"openspec": "./bin/openspec.js"
"openspec": "./bin/openspec.ts"
},
Copy link

@coderabbitai coderabbitai bot Dec 28, 2025

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

🏁 Script executed:

cd /tmp/repository && pwd

Repository: ClankerGuru/OpenSpec

Length of output: 129


🏁 Script executed:

# First, check if the bin/openspec.ts file exists and read its shebang
if [ -f "bin/openspec.ts" ]; then
  echo "=== bin/openspec.ts (first 20 lines) ==="
  head -20 bin/openspec.ts
else
  echo "bin/openspec.ts not found"
fi

Repository: ClankerGuru/OpenSpec

Length of output: 155


🏁 Script executed:

# Read package.json to understand the full context
echo "=== package.json (full content) ==="
cat package.json

Repository: ClankerGuru/OpenSpec

Length of output: 1839


🏁 Script executed:

# Check if compiled CLI entry point exists
if [ -f "dist/cli/index.js" ]; then
  echo "=== dist/cli/index.js exists ==="
  head -5 dist/cli/index.js
else
  echo "dist/cli/index.js does not exist"
fi

# Also check dist structure
echo ""
echo "=== dist directory structure ==="
find dist -type f -name "*.js" | head -20

Repository: ClankerGuru/OpenSpec

Length of output: 187


🌐 Web query:

npm bin entry point TypeScript file with Bun shebang global installation

💡 Result:

Short answer — two practical options:

  1. Ship a real npm global CLI that works for npm/yarn users (recommended)
  • Compile your TypeScript to JS in build step (tsc/bundler) and set package.json "bin" to the compiled JS file (with a Node shebang).
    • package.json:
      "bin": { "my-cli": "dist/index.js" }
    • dist/index.js first line: #!/usr/bin/env node
  • Publish; npm install -g will place the executable in PATH and run with Node. (This is the standard, portable approach.) [1][2]
  1. If you want a TypeScript source entry and Bun runtime
  • You can publish with a .ts file in "bin" and either:
    • Use a Bun shebang in the TS file: #!/usr/bin/env bun — that will run with Bun (requires users to have Bun or run via bunx). Or
    • Rely on bunx to execute .ts files directly (bunx respects shebangs and can run .ts bins). This is NOT portable to Node-only users and may be run only via Bun/bunx. [3][4]

Notes / caveats

  • A ts-node/ts-node-script shebang is possible but fragile for global installs (requires ts-node and extra flags; not cross-platform). Prefer compiling for npm global installs. [5]
  • Bun can also produce a single-file native executable (bun build --compile) if you want a standalone binary. [4]

Sources:
[1] bunx / bin + shebang docs (Bun).
[2] Best practice: compile TS to JS and use node shebang (npm bin behavior / bundlers like bunchee).
[3] Publish-to-npm + bunx (.ts as bin) discussion.
[4] Bun single-file executable docs.
[5] ts-node shebang/limitations (community discussion).


Compile bin entry to JavaScript with Node shebang for npm compatibility.

The bin entry points to ./bin/openspec.ts with a Bun shebang, which requires Bun to be installed and in PATH. This breaks the standard npm global install workflow—users installing via npm install -g @clanker-guru/openspec without Bun will be unable to run the openspec command.

Additionally, the dist/ directory does not yet exist, so the import in bin/openspec.ts (../dist/cli/index.js) would fail even on Bun installations.

Compile the TypeScript file to dist/bin/openspec.js (or similar) with a Node shebang (#!/usr/bin/env node) and update the bin entry to point to the compiled file instead.

🤖 Prompt for AI Agents
In package.json around lines 29-31, the bin entry points to a TypeScript file
with a Bun shebang which breaks npm installs and the dist directory/imports;
compile the bin TypeScript to a JavaScript file under dist (e.g.,
dist/bin/openspec.js) that starts with a Node shebang (#!/usr/bin/env node),
ensure the compiled file imports the built CLI at dist/cli/index.js (or adjusts
relative paths), add the compiled file to your build step and output directory,
and update package.json's bin entry to point to the compiled
dist/bin/openspec.js so npm global installs work without Bun.

Copy link
Owner Author

Choose a reason for hiding this comment

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

this is for bun only

Copy link

Choose a reason for hiding this comment

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

@ClankerGuru, understood! Thanks for clarifying that this package is intentionally Bun-only. That makes sense given the full migration from Node.js to Bun throughout the PR.

The README already documents Bun as a prerequisite, which is good. If you'd like to make the Bun requirement even more explicit and prevent installation attempts with other package managers, you could optionally add an engines field to package.json:

"engines": {
  "bun": ">=1.0.0"
}

This would make the Bun requirement machine-readable, though it's not strictly necessary since the shebang and documentation already make it clear.


✏️ Learnings added
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:29-31
Timestamp: 2025-12-28T09:27:28.326Z
Learning: The clanker-guru/openspec package is intentionally designed to run exclusively on the Bun runtime. The bin entry points to a TypeScript file (bin/openspec.ts) with a Bun shebang (#!/usr/bin/env bun), and this is by design, not a compatibility issue.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

"test:postinstall": "bun scripts/postinstall.ts",
"prepare": "bun run build",
"prepublishOnly": "bun run build",
"postinstall": "bun scripts/postinstall.ts",
Copy link

@coderabbitai coderabbitai bot Dec 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Postinstall script requires Bun runtime.

The postinstall script bun scripts/postinstall.ts requires Bun to be installed on the user's system. If users install this package via npm install without having Bun available, the postinstall will fail. Consider handling this gracefully or documenting Bun as a prerequisite.

Possible mitigation

Consider wrapping the postinstall in a check or using a compiled JS file:

"postinstall": "command -v bun >/dev/null 2>&1 && bun scripts/postinstall.ts || node dist/scripts/postinstall.js || true"

Or document that Bun is required for this package.

🤖 Prompt for AI Agents
In package.json at line 51, the postinstall script currently invokes Bun
directly which will fail if Bun is not installed; update the script to first
detect Bun and only run bun scripts/postinstall.ts when available, otherwise
fall back to a compiled Node script (e.g., dist/scripts/postinstall.js) or
no-op, and/or add documentation that Bun is a prerequisite; implement the change
by replacing the postinstall entry with a command that checks for bun and runs
bun when present, otherwise runs Node on a built JS fallback or exits
gracefully, and update README/install docs to mention Bun if you choose the
prerequisite route.

Copy link
Owner Author

Choose a reason for hiding this comment

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

we dont want npm in this repo

Copy link

Choose a reason for hiding this comment

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

@ClankerGuru, understood! Since this is a Bun-exclusive package by design, you'll want to ensure the Bun requirement is clearly documented so users know upfront.

I see the README already mentions Bun as a prerequisite and shows bunx as the recommended installation method. You might also consider adding an engines field to package.json to make the requirement explicit:

"engines": {
  "bun": ">=1.0.0"
}

This will help package managers and tooling understand the runtime requirement.


✏️ Learnings added
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:51-51
Timestamp: 2025-12-28T09:28:29.872Z
Learning: The clanker-guru/openspec package maintainers explicitly do not want npm compatibility or fallbacks. The package is intentionally Bun-exclusive, and npm-based workflows should not be suggested.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:29-31
Timestamp: 2025-12-28T09:27:28.326Z
Learning: The clanker-guru/openspec package is intentionally designed to run exclusively on the Bun runtime. The bin entry points to a TypeScript file (bin/openspec.ts) with a Bun shebang (#!/usr/bin/env bun), and this is by design, not a compatibility issue.

@@ -1,5 +1,5 @@
<p align="center">
<a href="https://github.com/Fission-AI/OpenSpec">
<a href="https://github.com/clanker-guru/OpenSpec">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

curl -s "https://registry.npmjs.org/@clanker-guru/openspec" | jq '.name // "Package not found"'

Repository: ClankerGuru/OpenSpec

Length of output: 83


🏁 Script executed:

git ls-files | grep -i readme

Repository: ClankerGuru/OpenSpec

Length of output: 189


🏁 Script executed:

cat -n README.md | head -150

Repository: ClankerGuru/OpenSpec

Length of output: 8479


Publish package to npm before merge or update installation instructions.

The @clanker-guru/openspec package is not yet published on npm. The README contains references to the npm package at lines 13–14 (npm badge) and lines 138–143 (installation commands via bunx and bun install). These will fail or display incorrectly until the package is published. Either publish the package before merging this PR, or remove the npm references from the README and update the installation instructions post-publish.

🤖 Prompt for AI Agents
In README.md around line 2 (and noting npm badge at lines 13–14 and installation
commands at lines 138–143), the README references the unpublished
@clanker-guru/openspec npm package; either publish the package before merging or
remove/replace those npm-specific references. Fix by removing the npm badge and
any "npm install @clanker-guru/openspec" or "bunx" install lines, then replace
them with valid alternatives (e.g., instructions to install from the GitHub repo
or to clone/build locally) so the README contains working installation steps
until the package is published.

@ClankerGuru
Copy link
Owner Author

@coderabbitai this is a bun only utility , don't expect to see js in this repository :)

@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

Tip

For best results, initiate chat on the files or code changes.

@ClankerGuru Thank you for the clarification! I completely understand - this aligns with the learnings I have stored about @clanker-guru/openspec being intentionally Bun-exclusive.

Since Bun can directly execute TypeScript files without additional configuration, there's no need for compiled JavaScript files in the repository. The migration from Node.js to Bun with full TypeScript conversion makes perfect sense.

The eslint.config.ts change removing *.js and *.mjs from the ignores list is actually a smart safeguard - if any JavaScript files accidentally get added to the codebase, they'll be caught by linting rather than silently ignored. This helps maintain the TypeScript-only standard you've established.

I've noted this is a pure TypeScript + Bun project, so I won't flag TypeScript-specific patterns or suggest JavaScript/Node.js compatibility concerns in future reviews. 🚀


🧠 Learnings used
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:29-31
Timestamp: 2025-12-28T09:27:28.326Z
Learning: The clanker-guru/openspec package is intentionally designed to run exclusively on the Bun runtime. The bin entry points to a TypeScript file (bin/openspec.ts) with a Bun shebang (#!/usr/bin/env bun), and this is by design, not a compatibility issue.

Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:51-51
Timestamp: 2025-12-28T09:28:29.872Z
Learning: The clanker-guru/openspec package maintainers explicitly do not want npm compatibility or fallbacks. The package is intentionally Bun-exclusive, and npm-based workflows should not be suggested.

Copy link

@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: 0

🧹 Nitpick comments (1)
test/commands/validate.enriched-output.test.ts (1)

29-30: Consider using fs APIs for consistency and cross-platform reliability.

These lines use shell commands (mkdir -p, bash -lc) to create test files, while the rest of the test (and other test files) uses fs.mkdir and fs.writeFile. This inconsistency could cause issues on Windows where bash may not be available.

🔎 Suggested refactor for consistency
-    execSync(`mkdir -p ${changePath}`);
-    execSync(`bash -lc "cat > ${path.join(changePath, 'proposal.md')} <<'EOF'\n${changeContent}\nEOF"`);
+    await fs.mkdir(changePath, { recursive: true });
+    await fs.writeFile(path.join(changePath, 'proposal.md'), changeContent, 'utf-8');

Note: The test function would need to be async for this change.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b446d2 and 7206863.

📒 Files selected for processing (8)
  • test/commands/change.interactive-show.test.ts
  • test/commands/change.interactive-validate.test.ts
  • test/commands/show.test.ts
  • test/commands/spec.interactive-show.test.ts
  • test/commands/spec.interactive-validate.test.ts
  • test/commands/spec.test.ts
  • test/commands/validate.enriched-output.test.ts
  • test/helpers/run-cli.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/commands/spec.interactive-validate.test.ts
  • test/commands/change.interactive-show.test.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:29-31
Timestamp: 2025-12-28T09:27:28.326Z
Learning: The clanker-guru/openspec package is intentionally designed to run exclusively on the Bun runtime. The bin entry points to a TypeScript file (bin/openspec.ts) with a Bun shebang (#!/usr/bin/env bun), and this is by design, not a compatibility issue.
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:51-51
Timestamp: 2025-12-28T09:28:29.872Z
Learning: The clanker-guru/openspec package maintainers explicitly do not want npm compatibility or fallbacks. The package is intentionally Bun-exclusive, and npm-based workflows should not be suggested.
📚 Learning: 2025-12-28T09:27:28.326Z
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:29-31
Timestamp: 2025-12-28T09:27:28.326Z
Learning: The clanker-guru/openspec package is intentionally designed to run exclusively on the Bun runtime. The bin entry points to a TypeScript file (bin/openspec.ts) with a Bun shebang (#!/usr/bin/env bun), and this is by design, not a compatibility issue.

Applied to files:

  • test/commands/change.interactive-validate.test.ts
  • test/commands/spec.test.ts
  • test/commands/validate.enriched-output.test.ts
  • test/helpers/run-cli.ts
  • test/commands/spec.interactive-show.test.ts
  • test/commands/show.test.ts
📚 Learning: 2025-12-28T09:28:29.872Z
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:51-51
Timestamp: 2025-12-28T09:28:29.872Z
Learning: The clanker-guru/openspec package maintainers explicitly do not want npm compatibility or fallbacks. The package is intentionally Bun-exclusive, and npm-based workflows should not be suggested.

Applied to files:

  • test/commands/change.interactive-validate.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test (windows-latest)
🔇 Additional comments (10)
test/commands/change.interactive-validate.test.ts (1)

1-46: LGTM! Clean migration to Bun test runner with proper test isolation.

The test correctly:

  • Uses bun:test imports and Bun-based CLI execution
  • Creates isolated temp directories per test run using os.tmpdir() + randomUUID()
  • Properly restores process.env and process.cwd() in the finally block
  • References the correct bin/openspec.ts entry point

Based on learnings, the Bun-exclusive design is intentional.

test/commands/validate.enriched-output.test.ts (1)

1-51: Migration to Bun test runner looks correct.

The Bun-specific changes (imports, temp directory handling, CLI execution) are properly implemented and aligned with the project's Bun-exclusive design.

test/commands/spec.test.ts (2)

1-59: LGTM! Comprehensive test setup with proper Bun migration.

The test file correctly:

  • Migrates all imports to bun:test
  • Uses OS temp directories with UUIDs for test isolation
  • Updates bin path to openspec.ts
  • Cleans up resources in afterEach

61-327: All CLI invocations correctly updated to Bun.

All 15 execSync calls throughout the test cases have been properly updated from node to bun execution, maintaining consistent invocation patterns across spec show, spec list, and spec validate commands with various flags.

test/commands/spec.interactive-show.test.ts (1)

1-46: LGTM! Clean migration matching the established pattern.

The test properly:

  • Uses isolated temp directories per test run
  • Invokes CLI via Bun
  • Restores environment state in the finally block
  • References the correct bin/openspec.ts entry point
test/commands/show.test.ts (2)

1-34: LGTM! Well-structured test setup with comprehensive directory scaffolding.

The test properly sets up both changesDir and specsDir to test the show command's auto-detection and ambiguity handling logic. The Bun migration is correctly applied.


36-126: All test cases correctly invoke CLI via Bun.

The five test scenarios (no-args hint, change detection, spec detection, ambiguity handling, nearest matches) all use proper Bun-based CLI execution and maintain appropriate environment isolation with try/finally blocks.

test/helpers/run-cli.ts (3)

56-73: LGTM: Build command migrated to Bun.

The build orchestration correctly switches from pnpm run build to bun run build, consistent with the project's migration to Bun runtime. The caching logic and error handling remain sound.

Based on learnings, the package is intentionally Bun-exclusive.


75-146: LGTM: CLI execution migrated to Bun with Windows compatibility.

The changes correctly switch CLI execution from Node to Bun:

  • Line 82: Spawns bun instead of node to execute the built CLI
  • Line 91: Adds Windows shell flag (consistent with runCommand at line 41) for reliable executable resolution
  • Line 136: Updates command reporting to reflect actual bun invocation

All cleanup paths (stream destruction, timeout handling, unref behavior) are preserved.

Based on learnings, the package runs exclusively on Bun runtime.


148-148: LGTM: New export alias for project root.

The cliProjectRoot export provides test files with access to the project root path. This is a minor, non-breaking addition.

Copy link

@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: 0

🧹 Nitpick comments (1)
test/cli-e2e/basic.test.ts (1)

35-35: LGTM! Appropriate workaround for Bun's Windows spawn limitations.

The describe.skipIf(isWindows) correctly addresses the known Bun spawn issues on Windows mentioned in the commit message. Skipping the entire suite is appropriate since all tests depend on CLI spawning via runCLI.

Optional: Add explanatory comment for maintainability

Consider adding a brief comment to document why tests are skipped on Windows:

 const isWindows = process.platform === 'win32';
 
+// Skip e2e tests on Windows due to known Bun spawn limitations
 describe.skipIf(isWindows)('openspec CLI e2e basics', () => {

This helps future maintainers understand the reasoning without needing to search commit history.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7206863 and b2fea7a.

📒 Files selected for processing (1)
  • test/cli-e2e/basic.test.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:29-31
Timestamp: 2025-12-28T09:27:28.326Z
Learning: The clanker-guru/openspec package is intentionally designed to run exclusively on the Bun runtime. The bin entry points to a TypeScript file (bin/openspec.ts) with a Bun shebang (#!/usr/bin/env bun), and this is by design, not a compatibility issue.
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: package.json:51-51
Timestamp: 2025-12-28T09:28:29.872Z
Learning: The clanker-guru/openspec package maintainers explicitly do not want npm compatibility or fallbacks. The package is intentionally Bun-exclusive, and npm-based workflows should not be suggested.
Learnt from: ClankerGuru
Repo: ClankerGuru/OpenSpec PR: 1
File: .github/workflows/ci.yml:28-30
Timestamp: 2025-12-28T09:37:00.257Z
Learning: In the clanker-guru/openspec project CI workflows, the maintainers prefer using `bun-version: latest` rather than pinning to a specific Bun version, accepting the trade-off for staying current with the latest Bun releases.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test (windows-latest)
🔇 Additional comments (2)
test/cli-e2e/basic.test.ts (2)

1-1: LGTM! Correct migration to Bun's test runner.

The import statement correctly migrates from Vitest to Bun's native test runner, aligning with the PR's runtime migration objectives.


8-9: LGTM! Standard Windows platform detection.

The platform check is correctly implemented using the standard process.platform === 'win32' approach.

@ClankerGuru ClankerGuru merged commit c0ef0e3 into main Dec 28, 2025
5 checks passed
@ClankerGuru ClankerGuru deleted the feature/bun-migration-testing branch December 28, 2025 10:06
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