Skip to content

Commit 5564866

Browse files
committed
fix: derive --help URLs and banner version from package.json
`workforge --help` shipped in @juspay/workforge@1.0.0 advertising Documentation: https://github.com/yourusername/workforge Issues: https://github.com/yourusername/workforge/issues alongside a hardcoded "WorkForge v3.0" banner on a package whose actual version is 1.0.0. Both are user-facing strings in a published artifact, and both were duplicates of metadata package.json already carries. Read them from package.json instead, so they cannot drift again: the homepage and bugs.url fields are already correct, and VERSION was already being read there for --version. package.json's index signature is deliberately loose, so the two fields are narrowed rather than cast. Also drops the "v3.0" prose from CLAUDE.md, which referred to the third internal rewrite rather than any released version, and refreshes the test inventory and count. Adds test/cli-help.test.ts, which asserts the help output against package.json rather than a second copy of the same literals. Verified by reverting the fix: 3 of its 4 assertions fail.
1 parent 1986713 commit 5564866

3 files changed

Lines changed: 96 additions & 12 deletions

File tree

CLAUDE.md

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
WorkForge v3.0 is a TypeScript CLI tool for managing Git worktrees with intelligent environment variable synchronization, automatic backup management, and comprehensive audit logging. The tool provides five commands: `create`, `close`, `sync-env`, `list`, and `cleanup`.
7+
WorkForge is a TypeScript CLI tool for managing Git worktrees with intelligent environment variable synchronization, automatic backup management, and comprehensive audit logging. The tool provides five commands: `create`, `close`, `sync-env`, `list`, and `cleanup`.
88

99
**Binary aliases**: `workforge` and `wf`
1010

11+
**Versioning**: published as [`@juspay/workforge`](https://www.npmjs.com/package/@juspay/workforge),
12+
which started its public life at **1.0.0**. "v3.0" appears in older prose here
13+
and refers to the third internal rewrite — the modular architecture described
14+
below — not to a released version. Never write a version number into source or
15+
docs; read it from `package.json`, which is the only thing semantic-release
16+
updates.
17+
1118
## Development Commands
1219

1320
```bash
@@ -74,9 +81,9 @@ pnpm run release:dry-run
7481

7582
## Architecture
7683

77-
### v3.0 - Modular Design
84+
### Modular Design
7885

79-
WorkForge v3.0 uses a **modular architecture** with separation of concerns across commands, core components, UI components, and types.
86+
WorkForge uses a **modular architecture** with separation of concerns across commands, core components, UI components, and types.
8087

8188
### Directory Structure
8289

@@ -116,17 +123,19 @@ src/
116123
117124
test/
118125
├── helpers/
119-
│ └── fixtures.ts # Isolated bare remote + clone, CLI runner
120-
├── env-parser.test.ts # Parser fidelity and sync safety (unit)
121-
├── create.test.ts # create command (drives the built CLI)
122-
└── close.test.ts # close, BranchCleaner, BackupManager, ProjectIdentifier
126+
│ └── fixtures.ts # Isolated bare remote + clone, CLI runner
127+
├── env-parser.test.ts # Parser fidelity and sync safety (unit)
128+
├── create.test.ts # create command (drives the built CLI)
129+
├── close.test.ts # close, BranchCleaner, BackupManager, ProjectIdentifier
130+
├── worktree-audit.test.ts # WorktreeResolver/Remover, audit log durability
131+
└── cli-help.test.ts # --help/--version strings, asserted against package.json
123132
```
124133

125134
---
126135

127136
## Testing
128137

129-
`pnpm test` builds, then runs Vitest. 41 tests, ~16s.
138+
`pnpm test` builds, then runs Vitest. 57 tests, ~23s.
130139

131140
**How the fixtures work.** `makeRepo()` builds a bare repository standing in for
132141
the remote, a `seed` checkout used to push "someone else's" commits, and the

src/index.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
SyncEnvOptions,
1818
ListOptions,
1919
CleanupOptions,
20+
ConfigValue,
2021
PackageJson
2122
} from './types/index.js';
2223

@@ -26,8 +27,24 @@ const __dirname = dirname(__filename);
2627
const packageJson: PackageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
2728
const VERSION = packageJson.version;
2829

30+
function readString(value: ConfigValue | undefined): string | undefined {
31+
return typeof value === 'string' ? value : undefined;
32+
}
33+
34+
function readProperty(value: ConfigValue | undefined, key: string): ConfigValue | undefined {
35+
return typeof value === 'object' && value !== null && !Array.isArray(value)
36+
? value[key]
37+
: undefined;
38+
}
39+
40+
// Derived from package.json rather than written out again: a duplicated URL
41+
// silently rotted into a `yourusername` placeholder and shipped in --help.
42+
const HOMEPAGE = readString(packageJson.homepage) ?? 'https://github.com/juspay/workforge';
43+
const ISSUES_URL =
44+
readString(readProperty(packageJson.bugs, 'url')) ?? 'https://github.com/juspay/workforge/issues';
45+
2946
/**
30-
* WorkForge v3.0 - Advanced Git Worktree Manager
47+
* WorkForge - Advanced Git Worktree Manager
3148
*
3249
* Commands:
3350
* - create: Create a new worktree
@@ -312,7 +329,7 @@ async function main(): Promise<void> {
312329
.strict()
313330
.recommendCommands()
314331
.epilogue(`
315-
WorkForge v3.0 - Advanced Git Worktree Manager
332+
WorkForge v${VERSION} - Advanced Git Worktree Manager
316333
------------------------------------------------
317334
Features:
318335
• Intelligent environment variable synchronization
@@ -322,8 +339,8 @@ Features:
322339
• Complete audit trail
323340
• Safety checks before worktree closure
324341
325-
Documentation: https://github.com/yourusername/workforge
326-
Issues: https://github.com/yourusername/workforge/issues
342+
Documentation: ${HOMEPAGE}
343+
Issues: ${ISSUES_URL}
327344
`)
328345
.parseAsync();
329346
}

test/cli-help.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2+
import { readFileSync } from 'fs';
3+
import * as path from 'path';
4+
import { REPO_ROOT, runCli, makeTempDir, cleanupFixtures } from './helpers/fixtures.js';
5+
6+
/**
7+
* `--help` shipped `https://github.com/yourusername/workforge` in
8+
* @juspay/workforge@1.0.0, and a hardcoded "v3.0" banner on a 1.0.0 package.
9+
* Both are user-facing strings in a published artifact, so they are asserted
10+
* against package.json rather than against a literal copy of the same text.
11+
*/
12+
13+
let cwd: string;
14+
let pkg: { version: string; homepage: string; bugs: { url: string } };
15+
16+
beforeAll(() => {
17+
cwd = makeTempDir('wf-help-');
18+
pkg = JSON.parse(readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf8'));
19+
});
20+
21+
afterAll(cleanupFixtures);
22+
23+
describe('workforge --help', () => {
24+
// Substring checks rather than regexes: an unanchored pattern over URL-ish
25+
// text is what CodeQL's js/regex/missing-regexp-anchor exists to catch, and
26+
// these are plain literals with nothing to match loosely.
27+
const PLACEHOLDERS = ['yourusername', 'example.com', 'your-org', 'TODO'];
28+
29+
it('contains no placeholder URLs', () => {
30+
const result = runCli(cwd, ['--help']);
31+
const output = result.output.toLowerCase();
32+
33+
expect(result.status).toBe(0);
34+
for (const placeholder of PLACEHOLDERS) {
35+
expect(output).not.toContain(placeholder.toLowerCase());
36+
}
37+
});
38+
39+
it('reports the real package version rather than a hardcoded one', () => {
40+
const result = runCli(cwd, ['--help']);
41+
42+
expect(result.output).toContain(`WorkForge v${pkg.version}`);
43+
});
44+
45+
it('points at the repository recorded in package.json', () => {
46+
const result = runCli(cwd, ['--help']);
47+
48+
expect(result.output).toContain(pkg.homepage);
49+
expect(result.output).toContain(pkg.bugs.url);
50+
});
51+
52+
it('agrees with --version', () => {
53+
const result = runCli(cwd, ['--version']);
54+
55+
expect(result.status).toBe(0);
56+
expect(result.stdout.trim()).toBe(pkg.version);
57+
});
58+
});

0 commit comments

Comments
 (0)