-
Notifications
You must be signed in to change notification settings - Fork 0
feat(cli): add bgagent version command #40
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
base: linear-vercel
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -43,6 +43,7 @@ import { makeSlackCommand } from '../commands/slack'; | |
| import { makeStatusCommand } from '../commands/status'; | ||
| import { makeSubmitCommand } from '../commands/submit'; | ||
| import { makeTraceCommand } from '../commands/trace'; | ||
| import { getCliVersion, makeVersionCommand } from '../commands/version'; | ||
| import { makeWatchCommand } from '../commands/watch'; | ||
| import { makeWebhookCommand } from '../commands/webhook'; | ||
| import { setVerbose } from '../debug'; | ||
|
|
@@ -53,7 +54,7 @@ const program = new Command(); | |
| program | ||
| .name('bgagent') | ||
| .description('Background Agent CLI — submit and manage coding tasks') | ||
| .version('0.0.0') | ||
| .version(getCliVersion()) | ||
| .option('--verbose', 'Enable debug output') | ||
| .hook('preAction', (_thisCommand, actionCommand) => { | ||
| // Resolve --verbose from the root program, not the subcommand | ||
|
|
@@ -86,6 +87,7 @@ program.addCommand(makeRuntimeCommand()); | |
| program.addCommand(makeOpsCommand()); | ||
| program.addCommand(makeWatchCommand()); | ||
| program.addCommand(makeTraceCommand()); | ||
| program.addCommand(makeVersionCommand()); | ||
| program.addCommand(makeWebhookCommand()); | ||
|
Owner
Author
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. Type: question Description: |
||
| program.addCommand(makeAdminCommand()); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /** | ||
| * MIT No Attribution | ||
| * | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| * the Software without restriction, including without limitation the rights to | ||
| * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
| * the Software, and to permit persons to whom the Software is furnished to do so. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| * SOFTWARE. | ||
| */ | ||
|
|
||
| import * as fs from 'fs'; | ||
| import * as path from 'path'; | ||
| import { Command } from 'commander'; | ||
| import { formatJson } from '../format'; | ||
|
|
||
| /** | ||
| * Resolve the CLI version from the package manifest. | ||
| * | ||
| * The manifest lives at ``cli/package.json``, two directories above this | ||
| * module in both the TypeScript source tree (``src/commands``) and the | ||
| * compiled output (``lib/commands``), so ``../../package.json`` resolves | ||
| * correctly under ts-jest and from the published ``lib`` bin alike. We read | ||
| * it at runtime rather than importing it so the version reported always | ||
| * matches the installed package and the compiled ``lib`` layout stays flat | ||
| * (no ``package.json`` copied under ``rootDir``). | ||
| */ | ||
| export function getCliVersion(): string { | ||
| const manifestPath = path.join(__dirname, '..', '..', 'package.json'); | ||
| const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as { | ||
| version?: string; | ||
| }; | ||
| return manifest.version ?? '0.0.0'; | ||
| } | ||
|
Owner
Author
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. Type: issue Description: Proposed fix: Wrap the read/parse in a try/catch and return the AI prompt: In |
||
|
|
||
| /** | ||
| * `bgagent version [--output text|json]` — print the installed CLI version, | ||
| * read from ``package.json``. Mirrors the root ``--version`` flag as an | ||
| * explicit subcommand so scripts can query it uniformly (and in JSON). | ||
| */ | ||
| export function makeVersionCommand(): Command { | ||
| return new Command('version') | ||
| .description('Print the bgagent CLI version') | ||
| .option('--output <format>', 'Output format (text or json)', 'text') | ||
|
Owner
Author
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. Type: comment Description: AI prompt: Optionally add a test to |
||
| .action((opts: { output: string }) => { | ||
| const version = getCliVersion(); | ||
| if (opts.output === 'json') { | ||
| console.log(formatJson({ version })); | ||
| return; | ||
| } | ||
| console.log(version); | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /** | ||
| * MIT No Attribution | ||
| * | ||
| * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| * | ||
| * Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
| * the Software without restriction, including without limitation the rights to | ||
| * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
| * the Software, and to permit persons to whom the Software is furnished to do so. | ||
| * | ||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| * SOFTWARE. | ||
| */ | ||
|
|
||
| import * as fs from 'fs'; | ||
| import * as path from 'path'; | ||
| import { getCliVersion, makeVersionCommand } from '../../src/commands/version'; | ||
|
|
||
| describe('version command', () => { | ||
| let consoleSpy: jest.SpiedFunction<typeof console.log>; | ||
|
|
||
| // The version reported must match the single source of truth: the | ||
| // package manifest. Reading it here (rather than hardcoding) keeps the | ||
| // test correct across version bumps. | ||
| const packageVersion = ( | ||
| JSON.parse( | ||
| fs.readFileSync( | ||
| path.join(__dirname, '..', '..', 'package.json'), | ||
| 'utf-8', | ||
| ), | ||
| ) as { version: string } | ||
| ).version; | ||
|
|
||
| beforeEach(() => { | ||
| process.exitCode = undefined; | ||
| consoleSpy = jest.spyOn(console, 'log').mockImplementation(); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| process.exitCode = undefined; | ||
| consoleSpy.mockRestore(); | ||
| }); | ||
|
|
||
| test('getCliVersion returns the package.json version', () => { | ||
| expect(getCliVersion()).toBe(packageVersion); | ||
|
Owner
Author
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. Type: good_point Description: Deriving |
||
| }); | ||
|
|
||
| test('prints the plain version in text mode (default)', async () => { | ||
| await makeVersionCommand().parseAsync(['node', 'version']); | ||
| expect(consoleSpy).toHaveBeenCalledTimes(1); | ||
| expect(consoleSpy).toHaveBeenCalledWith(packageVersion); | ||
| }); | ||
|
|
||
| test('prints a JSON object with --output json', async () => { | ||
| await makeVersionCommand().parseAsync(['node', 'version', '--output', 'json']); | ||
| const out = consoleSpy.mock.calls.map((c) => c[0]).join('\n'); | ||
| expect(JSON.parse(out)).toEqual({ version: packageVersion }); | ||
| }); | ||
| }); | ||
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.
Type: comment
Severity: minor
Title: Version manifest is read synchronously on every command invocation
Description:
.version(getCliVersion())is evaluated eagerly at module load, sobgagent <anything>now performs a synchronousreadFileSync+JSON.parseofpackage.jsonon every startup — even for commands unrelated to versioning. The cost is negligible (a single small file), but it is a new unconditional I/O + parse on the hot path that previously did not exist. If you want to avoid it, Commander accepts a lazy source, or the resolved value could be memoized. Non-blocking.AI prompt: Consider memoizing
getCliVersion()incli/src/commands/version.ts(module-level cached value computed once) so the root.version()flag and theversionsubcommand share a singlepackage.jsonread per process, avoiding a redundant synchronous file read on every CLI invocation.