-
-
Notifications
You must be signed in to change notification settings - Fork 5
refactor: migrate CLI to cac and fix monorepo workflow generation #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c103569
35e424a
9469995
67cad97
0585cbb
0533be6
d256d7b
be8c262
d49ff7f
1fc1122
f3f0d98
bb0608d
fce70da
a8edab1
9361932
185fb56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| '@tanstack/intent': patch | ||
| --- | ||
|
|
||
| Refactored the CLI to use `cac`, replacing the previous hand-rolled parsing and dispatch logic with a more structured command system. | ||
|
|
||
| This update also fixes monorepo workflow generation behavior related to `setup-github-actions`, improving repo/package fallback handling and ensuring generated workflow watch paths are monorepo-aware. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| const CLI_FAILURE = Symbol('CliFailure') | ||
|
|
||
| export type CliFailure = { | ||
| readonly [CLI_FAILURE]: true | ||
| message: string | ||
| exitCode: number | ||
| } | ||
|
|
||
| // Throws a structured CliFailure (not an Error) — this represents an expected | ||
| // user-facing failure, not an internal bug. Stack traces are intentionally | ||
| // omitted since these are anticipated exit paths (bad input, missing files, etc). | ||
| export function fail(message: string, exitCode = 1): never { | ||
| throw { [CLI_FAILURE]: true as const, message, exitCode } satisfies CliFailure | ||
| } | ||
|
|
||
| export function isCliFailure(value: unknown): value is CliFailure { | ||
| return !!value && typeof value === 'object' && CLI_FAILURE in value | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,86 @@ | ||||||||||||||||||||||
| import { existsSync, readFileSync } from 'node:fs' | ||||||||||||||||||||||
| import { dirname, join, relative } from 'node:path' | ||||||||||||||||||||||
| import { fileURLToPath } from 'node:url' | ||||||||||||||||||||||
| import { fail } from './cli-error.js' | ||||||||||||||||||||||
| import type { ScanResult, StalenessReport } from './types.js' | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export function printWarnings(warnings: Array<string>): void { | ||||||||||||||||||||||
| if (warnings.length === 0) return | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| console.log('Warnings:') | ||||||||||||||||||||||
| for (const warning of warnings) { | ||||||||||||||||||||||
| console.log(` ⚠ ${warning}`) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export function getMetaDir(): string { | ||||||||||||||||||||||
| const thisDir = dirname(fileURLToPath(import.meta.url)) | ||||||||||||||||||||||
| return join(thisDir, '..', 'meta') | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export async function scanIntentsOrFail(): Promise<ScanResult> { | ||||||||||||||||||||||
| const { scanForIntents } = await import('./scanner.js') | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| return scanForIntents() | ||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||
| fail(err instanceof Error ? err.message : String(err)) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| function readPackageName(root: string): string { | ||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| const pkgJson = JSON.parse( | ||||||||||||||||||||||
| readFileSync(join(root, 'package.json'), 'utf8'), | ||||||||||||||||||||||
| ) as { | ||||||||||||||||||||||
| name?: unknown | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return typeof pkgJson.name === 'string' | ||||||||||||||||||||||
| ? pkgJson.name | ||||||||||||||||||||||
| : relative(process.cwd(), root) || 'unknown' | ||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||
| return relative(process.cwd(), root) || 'unknown' | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export async function resolveStaleTargets( | ||||||||||||||||||||||
| targetDir?: string, | ||||||||||||||||||||||
| ): Promise<{ reports: Array<StalenessReport> }> { | ||||||||||||||||||||||
| const resolvedRoot = targetDir | ||||||||||||||||||||||
| ? join(process.cwd(), targetDir) | ||||||||||||||||||||||
| : process.cwd() | ||||||||||||||||||||||
|
Comment on lines
+49
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result:
So when
[1] (nodejs.org) Citations:
🏁 Script executed: cat -n packages/intent/src/cli-support.ts | head -50Repository: TanStack/intent Length of output: 1816 🏁 Script executed: rg -A 2 "resolveStaleTargets" --type tsRepository: TanStack/intent Length of output: 1028 🏁 Script executed: grep -A 10 -B 5 "targetDir" packages/intent/src/cli.ts | head -40Repository: TanStack/intent Length of output: 531 Use When 🔧 Proposed fix-import { dirname, join, relative } from 'node:path'
+import { dirname, join, relative, resolve } from 'node:path'
@@
- const resolvedRoot = targetDir
- ? join(process.cwd(), targetDir)
- : process.cwd()
+ const resolvedRoot = targetDir
+ ? resolve(process.cwd(), targetDir)
+ : process.cwd()📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| const { checkStaleness } = await import('./staleness.js') | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (existsSync(join(resolvedRoot, 'skills'))) { | ||||||||||||||||||||||
| return { | ||||||||||||||||||||||
| reports: [ | ||||||||||||||||||||||
| await checkStaleness(resolvedRoot, readPackageName(resolvedRoot)), | ||||||||||||||||||||||
| ], | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const { findPackagesWithSkills, findWorkspaceRoot } = | ||||||||||||||||||||||
| await import('./setup.js') | ||||||||||||||||||||||
| const workspaceRoot = findWorkspaceRoot(resolvedRoot) | ||||||||||||||||||||||
| if (workspaceRoot) { | ||||||||||||||||||||||
| const packageDirs = findPackagesWithSkills(workspaceRoot) | ||||||||||||||||||||||
| if (packageDirs.length > 0) { | ||||||||||||||||||||||
| return { | ||||||||||||||||||||||
| reports: await Promise.all( | ||||||||||||||||||||||
| packageDirs.map((packageDir) => | ||||||||||||||||||||||
| checkStaleness(packageDir, readPackageName(packageDir)), | ||||||||||||||||||||||
| ), | ||||||||||||||||||||||
| ), | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const staleResult = await scanIntentsOrFail() | ||||||||||||||||||||||
| return { | ||||||||||||||||||||||
| reports: await Promise.all( | ||||||||||||||||||||||
| staleResult.packages.map((pkg) => | ||||||||||||||||||||||
| checkStaleness(pkg.packageRoot, pkg.name), | ||||||||||||||||||||||
| ), | ||||||||||||||||||||||
| ), | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a cross-platform smoke test command.
Line 41 uses
> /dev/null, which is POSIX-specific and fails in Windows shells. Prefer keeping output or using a Node-based suppression strategy.Suggested minimal fix
📝 Committable suggestion
🤖 Prompt for AI Agents