-
Notifications
You must be signed in to change notification settings - Fork 56
chore: rolling promotion dev -> main #557
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
9958df1
f0f65ff
4fd5fc2
8c0d5f7
cd828f2
ac5390f
c4d52c7
ae6a940
5b45a83
149074f
afd9bad
e0944db
ccd43f7
c0f0b2a
523f1c5
abcacf3
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 | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,9 @@ | ||||||||||||||||||||||||||||||||||||||||||||||
| import { spawn } from 'node:child_process'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { existsSync } from 'node:fs'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { chmod, copyFile, mkdir, unlink } from 'node:fs/promises'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { homedir } from 'node:os'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { join } from 'node:path'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { genieConfigExists, loadGenieConfig } from '../lib/genie-config.js'; | ||||||||||||||||||||||||||||||||||||||||||||||
| import { genieConfigExists, loadGenieConfig, saveGenieConfig } from '../lib/genie-config.js'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const GENIE_HOME = process.env.GENIE_HOME || join(homedir(), '.genie'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const GENIE_SRC = join(GENIE_HOME, 'src'); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -144,26 +144,26 @@ async function detectInstallationType(): Promise<InstallationType> { | |||||||||||||||||||||||||||||||||||||||||||||
| return hasBun ? 'bun' : 'npm'; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| async function updateViaBun(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| log('Updating via bun...'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const result = await runCommand('bun', ['install', '-g', '@automagik/genie@latest']); | ||||||||||||||||||||||||||||||||||||||||||||||
| async function updateViaBun(channel: string): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| log(`Updating via bun (channel: ${channel})...`); | ||||||||||||||||||||||||||||||||||||||||||||||
| const result = await runCommand('bun', ['install', '-g', `@automagik/genie@${channel}`]); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!result.success) { | ||||||||||||||||||||||||||||||||||||||||||||||
| error('Failed to update via bun'); | ||||||||||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log(); | ||||||||||||||||||||||||||||||||||||||||||||||
| success('Genie CLI updated!'); | ||||||||||||||||||||||||||||||||||||||||||||||
| success(`Genie CLI updated (${channel})!`); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| async function updateViaNpm(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| log('Updating via npm...'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const result = await runCommand('npm', ['install', '-g', '@automagik/genie@latest']); | ||||||||||||||||||||||||||||||||||||||||||||||
| async function updateViaNpm(channel: string): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| log(`Updating via npm (channel: ${channel})...`); | ||||||||||||||||||||||||||||||||||||||||||||||
| const result = await runCommand('npm', ['install', '-g', `@automagik/genie@${channel}`]); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!result.success) { | ||||||||||||||||||||||||||||||||||||||||||||||
| error('Failed to update via npm'); | ||||||||||||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log(); | ||||||||||||||||||||||||||||||||||||||||||||||
| success('Genie CLI updated!'); | ||||||||||||||||||||||||||||||||||||||||||||||
| success(`Genie CLI updated (${channel})!`); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| async function updateSource(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -286,14 +286,164 @@ async function symlinkOrCopy(src: string, dest: string): Promise<void> { | |||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export async function updateCommand(): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| // Plugin Sync — update Claude Code plugin cache after CLI update | ||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| function copyDirSync(src: string, dest: string): void { | ||||||||||||||||||||||||||||||||||||||||||||||
| mkdirSync(dest, { recursive: true }); | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const entry of readdirSync(src, { withFileTypes: true })) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const srcPath = join(src, entry.name); | ||||||||||||||||||||||||||||||||||||||||||||||
| const destPath = join(dest, entry.name); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (entry.isDirectory()) { | ||||||||||||||||||||||||||||||||||||||||||||||
| copyDirSync(srcPath, destPath); | ||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||
| copyFileSync(srcPath, destPath); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+293
to
+304
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. 🧹 Nitpick | 🔵 Trivial Symlinks are followed but not explicitly handled.
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| async function resolveGlobalPkgDir(installType: InstallationType): Promise<string | null> { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Prefer the package manager that was actually used for this update | ||||||||||||||||||||||||||||||||||||||||||||||
| if (installType === 'bun') { | ||||||||||||||||||||||||||||||||||||||||||||||
| const bunPath = join(homedir(), '.bun', 'install', 'global', 'node_modules', '@automagik', 'genie'); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(bunPath)) return bunPath; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (installType === 'npm') { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Dynamic resolution via npm root -g (handles nvm/fnm/volta) | ||||||||||||||||||||||||||||||||||||||||||||||
| const npmRootResult = await runCommandSilent('npm', ['root', '-g']); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (npmRootResult.success) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const npmPath = join(npmRootResult.output.trim(), '@automagik', 'genie'); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(npmPath)) return npmPath; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Fallback: try both regardless of installType | ||||||||||||||||||||||||||||||||||||||||||||||
| const bunFallback = join(homedir(), '.bun', 'install', 'global', 'node_modules', '@automagik', 'genie'); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(bunFallback)) return bunFallback; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const npmRootFallback = await runCommandSilent('npm', ['root', '-g']); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (npmRootFallback.success) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const npmPath = join(npmRootFallback.output.trim(), '@automagik', 'genie'); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(npmPath)) return npmPath; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| async function syncPlugin(installType: InstallationType): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| log('Syncing Claude Code plugin...'); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const globalPkgDir = await resolveGlobalPkgDir(installType); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!globalPkgDir) { | ||||||||||||||||||||||||||||||||||||||||||||||
| log('Could not find installed package — skipping plugin sync'); | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const pluginSrc = join(globalPkgDir, 'plugins', 'genie'); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!existsSync(pluginSrc)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| log('Plugin source not found in package — skipping plugin sync'); | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Read version from installed package | ||||||||||||||||||||||||||||||||||||||||||||||
| let version: string; | ||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||
| const pkg = JSON.parse(readFileSync(join(globalPkgDir, 'package.json'), 'utf-8')); | ||||||||||||||||||||||||||||||||||||||||||||||
| version = pkg.version; | ||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||
| log('Could not read package version — skipping plugin sync'); | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+350
to
+358
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. Version string used in path construction without sanitization. The Proposed fix try {
const pkg = JSON.parse(readFileSync(join(globalPkgDir, 'package.json'), 'utf-8'));
- version = pkg.version;
+ version = pkg.version?.replace(/[/\\]/g, '_');
+ if (!version || !/^\d+\.\d+/.test(version)) {
+ log('Invalid package version — skipping plugin sync');
+ return;
+ }
} catch {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Copy to Claude Code plugin cache | ||||||||||||||||||||||||||||||||||||||||||||||
| const claudePlugins = join(homedir(), '.claude', 'plugins'); | ||||||||||||||||||||||||||||||||||||||||||||||
| const cacheDir = join(claudePlugins, 'cache', 'automagik', 'genie', version); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Clean existing cache dir if it exists (stale version) | ||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(cacheDir)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| rmSync(cacheDir, { recursive: true, force: true }); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| copyDirSync(pluginSrc, cacheDir); | ||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||
| error(`Failed to copy plugin: ${err}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Update installed_plugins.json registry | ||||||||||||||||||||||||||||||||||||||||||||||
| const registryPath = join(claudePlugins, 'installed_plugins.json'); | ||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (existsSync(registryPath)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| const registry = JSON.parse(readFileSync(registryPath, 'utf-8')); | ||||||||||||||||||||||||||||||||||||||||||||||
| const entries = registry.plugins?.['genie@automagik']; | ||||||||||||||||||||||||||||||||||||||||||||||
| if (Array.isArray(entries)) { | ||||||||||||||||||||||||||||||||||||||||||||||
| for (const entry of entries) { | ||||||||||||||||||||||||||||||||||||||||||||||
| if (entry.scope === 'user') { | ||||||||||||||||||||||||||||||||||||||||||||||
| entry.installPath = cacheDir; | ||||||||||||||||||||||||||||||||||||||||||||||
| entry.version = version; | ||||||||||||||||||||||||||||||||||||||||||||||
| entry.lastUpdated = new Date().toISOString(); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| writeFileSync(registryPath, JSON.stringify(registry, null, 2)); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||
| log(`Registry update failed (non-fatal): ${err}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+375
to
+394
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. 🧹 Nitpick | 🔵 Trivial Registry update is not atomic. Read-modify-write on 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| success(`Plugin synced to v${version}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
| // Channel Management | ||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================ | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| async function resolveChannel(options: { next?: boolean; stable?: boolean }): Promise<string> { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Explicit flags override everything | ||||||||||||||||||||||||||||||||||||||||||||||
| if (options.next) return 'next'; | ||||||||||||||||||||||||||||||||||||||||||||||
| if (options.stable) return 'latest'; | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+403
to
+407
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. Reject conflicting channel flags instead of silently preferring If users pass both Proposed fix export async function updateCommand(options: { next?: boolean; stable?: boolean } = {}): Promise<void> {
+ if (options.next && options.stable) {
+ error('Choose either --next or --stable, not both.');
+ process.exit(1);
+ }
+
console.log();
console.log('\x1b[1m🧞 Genie CLI Update\x1b[0m');
console.log('\x1b[2m────────────────────────────────────\x1b[0m');
console.log();Also applies to: 317-327 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| // Read saved channel from config | ||||||||||||||||||||||||||||||||||||||||||||||
| if (genieConfigExists()) { | ||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||
| const config = await loadGenieConfig(); | ||||||||||||||||||||||||||||||||||||||||||||||
| if (config.updateChannel) return config.updateChannel; | ||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Ignore config errors | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| return 'latest'; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| async function persistChannel(channel: string): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||
| const config = await loadGenieConfig(); | ||||||||||||||||||||||||||||||||||||||||||||||
| config.updateChannel = channel as 'latest' | 'next'; | ||||||||||||||||||||||||||||||||||||||||||||||
| await saveGenieConfig(config); | ||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||
| // Non-fatal — channel preference lost but update still works | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+421
to
+428
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. Don’t silently ignore channel persistence failures after explicit switch requests. When users run Proposed fix async function persistChannel(channel: string): Promise<void> {
try {
const config = await loadGenieConfig();
config.updateChannel = channel as 'latest' | 'next';
await saveGenieConfig(config);
- } catch {
- // Non-fatal — channel preference lost but update still works
+ } catch (err) {
+ const message = err instanceof Error ? err.message : String(err);
+ console.warn(`Warning: failed to persist update channel "${channel}": ${message}`);
}
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| export async function updateCommand(options: { next?: boolean; stable?: boolean } = {}): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log(); | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log('\x1b[1m🧞 Genie CLI Update\x1b[0m'); | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log('\x1b[2m────────────────────────────────────\x1b[0m'); | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const channel = await resolveChannel(options); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // Persist channel when explicitly switching | ||||||||||||||||||||||||||||||||||||||||||||||
| if (options.next || options.stable) { | ||||||||||||||||||||||||||||||||||||||||||||||
| await persistChannel(channel); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| const installType = await detectInstallationType(); | ||||||||||||||||||||||||||||||||||||||||||||||
| log(`Detected installation: ${installType}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| log(`Channel: ${channel}${channel === 'next' ? ' (dev builds)' : ' (stable)'}`); | ||||||||||||||||||||||||||||||||||||||||||||||
| console.log(); | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| if (installType === 'unknown') { | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -312,10 +462,12 @@ export async function updateCommand(): Promise<void> { | |||||||||||||||||||||||||||||||||||||||||||||
| await updateSource(); | ||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||
| case 'bun': | ||||||||||||||||||||||||||||||||||||||||||||||
| await updateViaBun(); | ||||||||||||||||||||||||||||||||||||||||||||||
| await updateViaBun(channel); | ||||||||||||||||||||||||||||||||||||||||||||||
| await syncPlugin(installType); | ||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||
| case 'npm': | ||||||||||||||||||||||||||||||||||||||||||||||
| await updateViaNpm(); | ||||||||||||||||||||||||||||||||||||||||||||||
| await updateViaNpm(channel); | ||||||||||||||||||||||||||||||||||||||||||||||
| await syncPlugin(installType); | ||||||||||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,7 +92,12 @@ program | |
| }); | ||
|
|
||
| program.command('doctor').description('Run diagnostic checks on genie installation').action(doctorCommand); | ||
| program.command('update').description('Update Genie CLI to the latest version').action(updateCommand); | ||
| program | ||
| .command('update') | ||
| .description('Update Genie CLI to the latest version') | ||
| .option('--next', 'Switch to dev builds (npm @next tag)') | ||
| .option('--stable', 'Switch to stable releases (npm @latest tag)') | ||
|
Comment on lines
+97
to
+99
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. Update command description is outdated. Line 97 says “latest version”, but Lines 98-99 now support both stable ( 🤖 Prompt for AI Agents |
||
| .action(updateCommand); | ||
| program.command('uninstall').description('Remove Genie CLI and clean up hooks').action(uninstallCommand); | ||
|
|
||
| const shortcuts = program.command('shortcuts').description('Manage tmux keyboard shortcuts'); | ||
|
|
||
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.
Tag pushed before publish—orphan tags possible on failure.
The workflow pushes the git tag at line 83 before attempting npm publish. If the build or publish step fails, the tag remains in the repo without a corresponding npm release. Consider either:
Potential fix: reorder tag push after publish
- name: Commit and tag run: | VERSION="${{ steps.version.outputs.version }}" git add -A '*.json' 'src/lib/version.ts' if git diff --cached --quiet; then echo "No version changes to commit" else git commit -m "chore(version): bump to ${VERSION} [skip ci]" fi git tag "v${VERSION}" - git push --atomic origin HEAD:refs/heads/dev "refs/tags/v${VERSION}" + git push origin HEAD:refs/heads/dev - name: Build CLI run: bun run build - name: Publish dev release to npm env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }} HUSKY: "0" run: | if [ -z "$NPM_TOKEN" ]; then echo "⚠️ NPM_TOKEN not set — skipping dev publish" exit 0 fi bun publish --access public --tag next + - name: Push tag after successful publish + run: | + VERSION="${{ steps.version.outputs.version }}" + git push origin "refs/tags/v${VERSION}"🤖 Prompt for AI Agents