Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions packages/cli/src/__tests__/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as path from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { CLIOptions } from '../index';
import { generateBrief, contextCommand } from '../commands/context';
import cliPkg from '../../package.json';

const options: CLIOptions = {
configPath: '.charter',
Expand Down Expand Up @@ -203,3 +204,55 @@ describe('contextCommand writes .charter/context.md', () => {
expect(content).toContain('## Identity');
});
});

describe('generateBrief version resolution', () => {
it('uses CLI package version when repo package.json is a private workspace root', async () => {
const tmp = makeTempDir();
process.chdir(tmp);

// Monorepo workspace root: private: true AND workspaces field present
fs.writeFileSync(
path.join(tmp, 'package.json'),
JSON.stringify({ name: 'my-monorepo', version: '0.1.0', private: true, workspaces: ['packages/*'] }),
'utf8'
);

const result = await generateBrief({ configPath: '.charter' });

// Should NOT show the stale workspace version
expect(result.markdown).not.toContain('v0.1.0');
// Should show the actual installed CLI version
expect(result.markdown).toContain(`v${cliPkg.version}`);
});

it('keeps the repo version for a private package that is not a workspace root', async () => {
const tmp = makeTempDir();
process.chdir(tmp);

// Internal app: private: true but NO workspaces field — must preserve its own version
fs.writeFileSync(
path.join(tmp, 'package.json'),
JSON.stringify({ name: 'internal-app', version: '2.4.1', private: true }),
'utf8'
);

const result = await generateBrief({ configPath: '.charter' });

expect(result.markdown).toContain('v2.4.1');
});

it('uses the repo package.json version for normal non-private packages', async () => {
const tmp = makeTempDir();
process.chdir(tmp);

fs.writeFileSync(
path.join(tmp, 'package.json'),
JSON.stringify({ name: 'my-app', version: '3.7.2' }),
'utf8'
);

const result = await generateBrief({ configPath: '.charter' });

expect(result.markdown).toContain('v3.7.2');
});
});
6 changes: 6 additions & 0 deletions packages/cli/src/commands/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import { execSync } from 'node:child_process';
import cliPkg from '../../package.json';
import { analyze as analyzeBlast, BlastInputSchema } from '@stackbilt/blast';
import { analyze as analyzeSurface, SurfaceInputSchema } from '@stackbilt/surface';
import { parseAdf, parseManifest } from '@stackbilt/adf';
Expand Down Expand Up @@ -477,6 +478,11 @@ export async function generateBrief(options?: BriefOptions): Promise<BriefResult
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) as Record<string, unknown>;
if (typeof pkg.name === 'string') packageName = pkg.name;
if (typeof pkg.version === 'string') version = pkg.version;
// Private *workspace* roots (monorepos with a workspaces field) carry a stale or
// meaningless version — use the actual published CLI version so the brief reflects
// what's installed. Plain private packages (internal apps without workspaces) keep
// their own version unchanged.
if (pkg.private === true && pkg.workspaces != null) version = cliPkg.version;
if (typeof pkg.description === 'string') description = pkg.description;
if (pkg.bin && typeof pkg.bin === 'object' && pkg.bin !== null) {
pkgBin = pkg.bin as Record<string, string>;
Expand Down
Loading