Visitar URL original
refactor(cli): extract CLI-plugin contract into @forinda/kickjs-cli-kit by forinda · Pull Request #333 · forinda/kick-js · GitHub
Skip to content

refactor(cli): extract CLI-plugin contract into @forinda/kickjs-cli-kit - #333

Merged
forinda merged 2 commits into
mainfrom
feat/cli-kit-contract
Jun 10, 2026
Merged

forinda merged 2 commits into
mainfrom
feat/cli-kit-contract

Conversation

@forinda

@forinda forinda commented Jun 10, 2026 •

Copy link
Copy Markdown
Owner

Why

Packages that want to ship kick-compatible commands or generators (notably the database tooling, which the CLI itself mounts) couldn't import defineCliPlugin from @forinda/kickjs-cli — the CLI already depends on those packages, so it would form a dependency cycle. The CLI-plugin contract needed to live somewhere both sides can import.

What

New dependency-free package @forinda/kickjs-cli-kit (commander peer only) owns the contract:

  • defineCliPlugin, KickCliPlugin<TConfig>, KickCliPluginContext<TConfig>
  • defineGenerator, GeneratorSpec / GeneratorContext / GeneratorFile / GeneratorArg / GeneratorFlag
  • KickCommandDefinition, CliTypegen, DiscoveredGenerator, KickPluginConflictError

Design notes:

  • ctx.config is generic (TConfig, default unknown) — the host CLI passes its full config; @forinda/kickjs-cli narrows it to KickConfig. This is what lets a package read its slice of the config without importing the 795-line KickConfig.
  • typegens use a structural CliTypegen (the CLI's TypegenPlugin satisfies it via any ctx + matching return), so the kit never drags in the typegen scanner.

@forinda/kickjs-cli now re-exports the whole contract — import { defineCliPlugin } from '@forinda/kickjs-cli' keeps working unchanged. generator-extension/define.ts re-exports from the kit (the file moved). No behaviour change — pure extraction.

This unblocks shipping @forinda/kickjs-db/cli (a mountable db command plugin + standalone bin) in a follow-up.

Verified

Full workspace build 23/23; cli plugin + generator-extension tests 36/36; cli typecheck clean.

Design + the follow-up plan: docs/db/plan-cli-kit-and-db-cli.md.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation

    • Updated architecture documentation for CLI plugin system restructuring.
  • Chores

    • Reorganized CLI plugin contract into a new shared, dependency-free package for improved maintainability and to eliminate circular dependencies between packages. All existing imports and functionality remain unchanged.

New dependency-free package owns defineCliPlugin / defineGenerator /
KickCliPlugin / KickCliPluginContext / GeneratorSpec / KickCommandDefinition
so packages can ship kick commands + generators without depending on
@forinda/kickjs-cli (which would cycle). The CLI re-exports the whole
contract for back-compat and narrows the plugin context config generic to
its KickConfig. No behaviour change.

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

coderabbitai Bot commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@forinda, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 53 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, 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 include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a31a11b4-f0dc-4715-a1aa-f6e09b0e36e2

📥 Commits

Reviewing files that changed from the base of the PR and between b6b6832 and 2fbee82.

📒 Files selected for processing (1)
  • packages/cli-kit/src/generators.ts
📝 Walkthrough

Walkthrough

A new dependency-free package @forinda/kickjs-cli-kit is created to centralize the CLI plugin contract and generator types. The @forinda/kickjs-cli package is updated to depend on and re-export this contract, introducing type aliases to narrow the kit's generic types to the host package's KickConfig.

Changes

CLI Plugin Contract Extraction to @forinda/kickjs-cli-kit

Layer / File(s) Summary
New @forinda/kickjs-cli-kit contract package
packages/cli-kit/package.json, packages/cli-kit/src/generators.ts, packages/cli-kit/src/index.ts, packages/cli-kit/tsconfig.json, packages/cli-kit/tsdown.config.ts
GeneratorContext, GeneratorFile, GeneratorArg, GeneratorFlag, GeneratorSpec, and defineGenerator helper are defined as the kit's generator API. KickCommandDefinition, CliTypegen, DiscoveredGenerator, KickCliPluginContext<TConfig>, KickCliPlugin<TConfig>, and defineCliPlugin form the plugin contract. KickPluginConflictError reports plugin/command/typegen/generator identifier collisions. Build config targets ESM with DTS generation and marks commander as external peer dependency.
@forinda/kickjs-cli backward-compatible re-exports
packages/cli/package.json, packages/cli/src/generator-extension/define.ts, packages/cli/src/plugin/types.ts
@forinda/kickjs-cli adds @forinda/kickjs-cli-kit as a workspace dependency. generator-extension/define.ts re-exports all generator types and helpers from the kit. plugin/types.ts re-exports the kit's plugin contract, defineCliPlugin, and KickPluginConflictError, and introduces type aliases KickCliPluginContext = KitCliPluginContext<KickConfig> and KickCliPlugin = KitCliPlugin<KickConfig> to specialize generic types to the host config.
Documentation and changeset
.changeset/cli-kit-contract.md, docs/db/plan-cli-kit-and-db-cli.md
Changeset declares minor version for the new kit, patch for the CLI, and notes the re-export strategy for backward compatibility. Plan document outlines the package split to break the kickjs-db ↔ kickjs-cli cycle, describes the generic config narrowing in the kit, and proposes DB CLI plugin implementation and standalone executable for database tooling without the main CLI package.

Sequence Diagram

sequenceDiagram
  participant Plugin as Downstream Plugin
  participant CliKit as `@forinda/kickjs-cli-kit`
  participant Cli as `@forinda/kickjs-cli`
  Plugin->>CliKit: import KickCliPlugin, defineCliPlugin
  Cli->>CliKit: re-export plugin contract
  Plugin->>Cli: import KickCliPlugin (backward-compat)
  Note over Cli,CliKit: KickCliPlugin<KickConfig> narrows<br/>KitCliPlugin<TConfig = unknown>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

The PR involves extracting and reorganizing type contracts and build configuration across two packages with clear intent but requires verification that all contract surfaces are correctly narrowed, re-exported for backward compatibility, and properly wired through the dependency graph.

Possibly related PRs

  • forinda/kick-js#244: Adds generators?: DiscoveredGenerator[] to KickCliPluginContext and wires it through plugin registration, directly extending the contract type being extracted and reorganized in this PR.

Poem

🐰 A kit to share, contracts held tight,
No more cycles, types shining bright,
Generics dance, configs bow down,
Reexports crown the stable town! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately and concisely describes the main change: extracting the CLI-plugin contract into a new package (@forinda/kickjs-cli-kit).
Description check ✅ Passed The description provides comprehensive context (Why/What), detailed design notes, verification results, and references supporting documentation. All key information is present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cli-kit-contract

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 and usage tips.

@socket-security

socket-security Bot commented Jun 10, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcommander@​14.0.310010010089100

View full report

@socket-security

socket-security Bot commented Jun 10, 2026 •

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm seroval is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yaml → npm/seroval@1.5.4

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/seroval@1.5.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@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

🧹 Nitpick comments (1)
packages/cli-kit/src/generators.ts (1)

18-18: ⚡ Quick win

Use kit package imports in examples to avoid reintroducing CLI coupling.

Line 18 and Line 140 should import defineGenerator from @forinda/kickjs-cli-kit, not
@forinda/kickjs-cli, so plugin authors follow the dependency-free path.

Suggested patch
- * import { defineGenerator } from '`@forinda/kickjs-cli`'
+ * import { defineGenerator } from '`@forinda/kickjs-cli-kit`'
...
- * import { defineGenerator } from '`@forinda/kickjs-cli`'
+ * import { defineGenerator } from '`@forinda/kickjs-cli-kit`'

Also applies to: 140-140

🤖 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 `@packages/cli-kit/src/generators.ts` at line 18, The example imports currently
reference defineGenerator from '`@forinda/kickjs-cli`' which reintroduces CLI
coupling; update the import statements that reference defineGenerator (the
example at the top and the later usage near the second example) to import from
'`@forinda/kickjs-cli-kit`' instead, so both occurrences use the kit package and
keep plugin authors dependency-free.
🤖 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 `@packages/cli-kit/tsdown.config.ts`:
- Line 4: The code uses import.meta.dirname when calling readPkg (see "pkg =
readPkg(import.meta.dirname)"), which fails on Node 20.0–20.10; replace
import.meta.dirname with a Node-20-wide pattern: compute the directory via
dirname(fileURLToPath(import.meta.url)) and pass that to readPkg. Add the
required imports (fileURLToPath from 'url' and dirname from 'path') and update
the readPkg call to use the computed directory variable.

---

Nitpick comments:
In `@packages/cli-kit/src/generators.ts`:
- Line 18: The example imports currently reference defineGenerator from
'`@forinda/kickjs-cli`' which reintroduces CLI coupling; update the import
statements that reference defineGenerator (the example at the top and the later
usage near the second example) to import from '`@forinda/kickjs-cli-kit`' instead,
so both occurrences use the kit package and keep plugin authors dependency-free.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aa90b541-079a-41b8-8935-769b36e9367d

📥 Commits

Reviewing files that changed from the base of the PR and between 523d504 and b6b6832.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • .changeset/cli-kit-contract.md
  • docs/db/plan-cli-kit-and-db-cli.md
  • packages/cli-kit/package.json
  • packages/cli-kit/src/generators.ts
  • packages/cli-kit/src/index.ts
  • packages/cli-kit/tsconfig.json
  • packages/cli-kit/tsdown.config.ts
  • packages/cli/package.json
  • packages/cli/src/generator-extension/define.ts
  • packages/cli/src/plugin/types.ts

import { defineConfig } from 'tsdown'
import { createBanner, readPkg } from '../../build.utils.mjs'

const pkg = readPkg(import.meta.dirname)

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Node engine declarations:"
fd -a package.json | xargs -I{} sh -c 'jq -r "\"{} -> \(.engines.node // \"(none)\")\"" {}'

echo
echo "import.meta.dirname usages:"
rg -n 'import\.meta\.dirname' -C2

Repository: forinda/kick-js

Length of output: 9238


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the specific file + helper
echo "---- packages/cli-kit/tsdown.config.ts (relevant lines) ----"
sed -n '1,120p' packages/cli-kit/tsdown.config.ts | nl -ba

echo
echo "---- build.utils.mjs (readPkg implementation) ----"
# find exact file
rg -n "function readPkg|const readPkg|export .*readPkg" -S build.utils.mjs packages --hidden --no-ignore-vcs || true
# show likely location
if [ -f build.utils.mjs ]; then
  sed -n '1,220p' build.utils.mjs | nl -ba
else
  # fallback: locate file by name
  fpath="$(fd -a -t f 'build.utils.mjs' . | head -n 1 || true)"
  echo "Found build utils at: ${fpath:-<none>}"
  if [ -n "${fpath:-}" ]; then
    sed -n '1,220p' "$fpath" | nl -ba
  fi
fi

echo
echo "---- root package.json engines ----"
cat package.json | jq -r '.engines' 2>/dev/null || true

Repository: forinda/kick-js

Length of output: 181


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- packages/cli-kit/tsdown.config.ts (relevant lines) ----"
if [ -f packages/cli-kit/tsdown.config.ts ]; then
  sed -n '1,120p' packages/cli-kit/tsdown.config.ts | cat -n
else
  echo "MISSING: packages/cli-kit/tsdown.config.ts"
fi

echo
echo "---- locating build.utils.mjs ----"
# try common places / search by name
bpath="$(fd -a 'build.utils.mjs' . | head -n 1 || true)"
if [ -z "${bpath:-}" ]; then
  bpath="$(fd -a 'build.utils.*' . | head -n 1 || true)"
fi
echo "build.utils path: ${bpath:-<none>}"

if [ -n "${bpath:-}" ] && [ -f "$bpath" ]; then
  echo
  echo "---- build.utils.mjs (first ~220 lines) ----"
  sed -n '1,220p' "$bpath" | cat -n
fi

echo
echo "---- root package.json engines ----"
if [ -f package.json ]; then
  cat package.json | jq -r '.engines' 2>/dev/null || true
fi

Repository: forinda/kick-js

Length of output: 1375


🌐 Web query:

Node.js when was import.meta.dirname introduced Node 20 minor versions

💡 Result:

The import.meta.dirname property was introduced in Node.js v20.11.0 and v21.2.0 [1][2][3]. It was initially added as an experimental feature [4][5] and was later designated as stable [1][4]. The functionality was backported to the Node.js 20 release line after its initial introduction in Node.js 21 [6][7].

Citations:


Use a Node-20-wide dirname source (avoid import.meta.dirname).
File: packages/cli-kit/tsdown.config.ts (line 4)

import.meta.dirname was added in Node v20.11.0, but the repo sets engines.node: ">=20.0", so Node 20.0–20.10 can fail. Switch to dirname(fileURLToPath(import.meta.url)).

Suggested patch
 import { defineConfig } from 'tsdown'
+import { dirname } from 'node:path'
+import { fileURLToPath } from 'node:url'
 import { createBanner, readPkg } from '../../build.utils.mjs'
 
-const pkg = readPkg(import.meta.dirname)
+const pkg = readPkg(dirname(fileURLToPath(import.meta.url)))
📝 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
const pkg = readPkg(import.meta.dirname)
import { defineConfig } from 'tsdown'
import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createBanner, readPkg } from '../../build.utils.mjs'
const pkg = readPkg(dirname(fileURLToPath(import.meta.url)))
🤖 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 `@packages/cli-kit/tsdown.config.ts` at line 4, The code uses
import.meta.dirname when calling readPkg (see "pkg =
readPkg(import.meta.dirname)"), which fails on Node 20.0–20.10; replace
import.meta.dirname with a Node-20-wide pattern: compute the directory via
dirname(fileURLToPath(import.meta.url)) and pass that to readPkg. Add the
required imports (fileURLToPath from 'url' and dirname from 'path') and update
the readPkg call to use the computed directory variable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@forinda
forinda merged commit f173f18 into main Jun 10, 2026
13 checks passed
@forinda
forinda deleted the feat/cli-kit-contract branch June 10, 2026 02:26
@github-actions github-actions Bot mentioned this pull request Jun 10, 2026
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.

1 participant