@linzumi/cli 0.0.80-beta → 0.0.82-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +6404 -5049
- package/package.json +3 -2
- package/scripts/bump-cli-version.mjs +306 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linzumi/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.82-beta",
|
|
4
4
|
"description": "Linzumi CLI — point a Codex agent at the real code on your laptop, with your team watching and steering from shared threads.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "node scripts/build.mjs",
|
|
17
|
+
"bump-version": "node scripts/bump-cli-version.mjs",
|
|
17
18
|
"codex:history-table": "node scripts/codex_history_table.mjs",
|
|
18
19
|
"test": "vitest run --config vitest.config.mjs",
|
|
19
20
|
"prepack": "node scripts/build.mjs",
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"access": "public"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@anthropic-ai/claude-agent-sdk": "
|
|
34
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.170",
|
|
34
35
|
"@inquirer/prompts": "^7.8.6",
|
|
35
36
|
"@modelcontextprotocol/sdk": "^1.18.2",
|
|
36
37
|
"@openai/codex": "^0.131.0",
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// Single-command CLI version bump.
|
|
2
|
+
//
|
|
3
|
+
// Releasing the CLI:
|
|
4
|
+
// (Example versions below use 1.2.3-beta on purpose: the leftover scan reads
|
|
5
|
+
// this script too, so a REAL version literal here would collide with the
|
|
6
|
+
// live package version and abort a bump.)
|
|
7
|
+
//
|
|
8
|
+
// 1. pnpm bump-version <new-version> (for example: 1.2.3-beta)
|
|
9
|
+
// 2. Run the test suite: pnpm vitest run --config vitest.config.mjs
|
|
10
|
+
// 3. Commit with a "[deploy-cli]" title and merge to main.
|
|
11
|
+
// 4. The main publish workflow (.github/workflows/linzumi-cli-publish.yml)
|
|
12
|
+
// publishes @linzumi/cli@<new-version> to npm.
|
|
13
|
+
//
|
|
14
|
+
// The package version is pinned in several places (package.json,
|
|
15
|
+
// src/version.ts, README.md, and version-pinning tests). This script rewrites
|
|
16
|
+
// every known site, FAILS LOUDLY if any expected pattern is missing, and then
|
|
17
|
+
// scans package.json/src/README/test/scripts/bin for any leftover
|
|
18
|
+
// old-version string - so a future, not-yet-listed pin site cannot silently
|
|
19
|
+
// drift past a release.
|
|
20
|
+
//
|
|
21
|
+
// Flags:
|
|
22
|
+
// --dry-run Plan and verify every rewrite without writing files.
|
|
23
|
+
// --root <dir> Operate on a different package root (used by tests).
|
|
24
|
+
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
25
|
+
import { dirname, join, relative } from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
|
|
28
|
+
function fail(message) {
|
|
29
|
+
process.stderr.write(`bump-cli-version: ${message}\n`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const usage =
|
|
34
|
+
'usage: node scripts/bump-cli-version.mjs <new-version> [--dry-run] [--root <dir>]';
|
|
35
|
+
|
|
36
|
+
const rawArgs = process.argv.slice(2);
|
|
37
|
+
let dryRun = false;
|
|
38
|
+
let explicitRoot;
|
|
39
|
+
const positional = [];
|
|
40
|
+
|
|
41
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
42
|
+
const arg = rawArgs[index];
|
|
43
|
+
|
|
44
|
+
if (arg === '--dry-run') {
|
|
45
|
+
dryRun = true;
|
|
46
|
+
} else if (arg === '--root') {
|
|
47
|
+
explicitRoot = rawArgs[index + 1];
|
|
48
|
+
index += 1;
|
|
49
|
+
|
|
50
|
+
if (explicitRoot === undefined) {
|
|
51
|
+
fail(`--root requires a directory argument\n${usage}`);
|
|
52
|
+
}
|
|
53
|
+
} else if (arg.startsWith('--')) {
|
|
54
|
+
fail(`unknown flag ${arg}\n${usage}`);
|
|
55
|
+
} else {
|
|
56
|
+
positional.push(arg);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const newVersion = positional[0];
|
|
61
|
+
|
|
62
|
+
if (newVersion === undefined || positional.length !== 1) {
|
|
63
|
+
fail(usage);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!/^\d+\.\d+\.\d+(-[0-9A-Za-z][0-9A-Za-z.-]*)?$/.test(newVersion)) {
|
|
67
|
+
fail(
|
|
68
|
+
`'${newVersion}' does not look like a version (expected something like 1.2.3-beta)`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const packageRoot =
|
|
73
|
+
explicitRoot ?? dirname(dirname(fileURLToPath(import.meta.url)));
|
|
74
|
+
|
|
75
|
+
let manifest;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
manifest = JSON.parse(
|
|
79
|
+
readFileSync(join(packageRoot, 'package.json'), 'utf8')
|
|
80
|
+
);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
fail(`could not read package.json under ${packageRoot}: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const oldVersion = manifest.version;
|
|
86
|
+
|
|
87
|
+
if (typeof oldVersion !== 'string' || oldVersion.trim() === '') {
|
|
88
|
+
fail('package.json has no "version" field to bump from');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (oldVersion === newVersion) {
|
|
92
|
+
fail(`package.json is already at version ${oldVersion}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Every place the version is pinned. Each entry must match at least once or
|
|
96
|
+
// the bump aborts; add new entries here when a new pin site appears (the
|
|
97
|
+
// leftover scan below is what tells you one appeared).
|
|
98
|
+
const knownSites = [
|
|
99
|
+
{
|
|
100
|
+
file: 'package.json',
|
|
101
|
+
description: 'npm package manifest version',
|
|
102
|
+
pattern: `"version": "${oldVersion}"`,
|
|
103
|
+
replacement: `"version": "${newVersion}"`,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
file: 'src/version.ts',
|
|
107
|
+
description: 'linzumiCliVersion constant',
|
|
108
|
+
pattern: `export const linzumiCliVersion = '${oldVersion}';`,
|
|
109
|
+
replacement: `export const linzumiCliVersion = '${newVersion}';`,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
file: 'README.md',
|
|
113
|
+
description: 'pinned npx --version example',
|
|
114
|
+
pattern: `npx -y @linzumi/cli@${oldVersion} --version`,
|
|
115
|
+
replacement: `npx -y @linzumi/cli@${newVersion} --version`,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
file: 'test/packageManifest.test.ts',
|
|
119
|
+
description: 'package manifest version expectation',
|
|
120
|
+
pattern: `expect(manifest.version).toBe('${oldVersion}');`,
|
|
121
|
+
replacement: `expect(manifest.version).toBe('${newVersion}');`,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
file: 'test/cli.test.ts',
|
|
125
|
+
description: '--version output expectation',
|
|
126
|
+
pattern: `'linzumi ${oldVersion}'`,
|
|
127
|
+
replacement: `'linzumi ${newVersion}'`,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
file: 'test/runnerBridge.test.ts',
|
|
131
|
+
description: 'runner join metadata version pin',
|
|
132
|
+
pattern: `version: '${oldVersion}',`,
|
|
133
|
+
replacement: `version: '${newVersion}',`,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
file: 'test/runnerLock.test.ts',
|
|
137
|
+
description: 'runner lock cliVersion pins',
|
|
138
|
+
pattern: `cliVersion: '${oldVersion}',`,
|
|
139
|
+
replacement: `cliVersion: '${newVersion}',`,
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
function countOccurrences(haystack, needle) {
|
|
144
|
+
return haystack.split(needle).length - 1;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/version.ts keeps a spec-comment block where each bump appends an entry.
|
|
148
|
+
function appendVersionSpecComment(content) {
|
|
149
|
+
const anchor = `*/\nexport const linzumiCliVersion = '${newVersion}';`;
|
|
150
|
+
|
|
151
|
+
if (!content.includes(anchor)) {
|
|
152
|
+
fail(
|
|
153
|
+
'src/version.ts spec-comment block convention not found (expected the comment to close right before the linzumiCliVersion export)'
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
158
|
+
const entry = [
|
|
159
|
+
`- Date: ${today}`,
|
|
160
|
+
' Spec: scripts/bump-cli-version.mjs (automated version bump)',
|
|
161
|
+
` Relationship: Bumps the published CLI version so the`,
|
|
162
|
+
' current mainline CLI can be released under a fresh immutable npm',
|
|
163
|
+
' version with `[deploy-cli]`.',
|
|
164
|
+
'',
|
|
165
|
+
].join('\n');
|
|
166
|
+
|
|
167
|
+
return content.replace(anchor, `${entry}${anchor}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const plannedWrites = new Map();
|
|
171
|
+
const summary = [];
|
|
172
|
+
|
|
173
|
+
for (const site of knownSites) {
|
|
174
|
+
const absolutePath = join(packageRoot, site.file);
|
|
175
|
+
let content;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
content =
|
|
179
|
+
plannedWrites.get(site.file) ?? readFileSync(absolutePath, 'utf8');
|
|
180
|
+
} catch (error) {
|
|
181
|
+
fail(`expected pin site ${site.file} could not be read: ${error.message}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const occurrences = countOccurrences(content, site.pattern);
|
|
185
|
+
|
|
186
|
+
if (occurrences === 0) {
|
|
187
|
+
fail(
|
|
188
|
+
`expected pattern not found in ${site.file}: ${JSON.stringify(site.pattern)} (${site.description}). The known-sites list in scripts/bump-cli-version.mjs is out of date; fix it before bumping.`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let rewritten = content.split(site.pattern).join(site.replacement);
|
|
193
|
+
|
|
194
|
+
if (site.file === 'src/version.ts') {
|
|
195
|
+
rewritten = appendVersionSpecComment(rewritten);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
plannedWrites.set(site.file, rewritten);
|
|
199
|
+
summary.push({
|
|
200
|
+
file: site.file,
|
|
201
|
+
description: site.description,
|
|
202
|
+
occurrences,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Leftover scan: after the planned rewrites, no old-version string may remain
|
|
207
|
+
// anywhere in the package surface. This is what catches an unlisted 8th site.
|
|
208
|
+
function scanTargets() {
|
|
209
|
+
const roots = ['src', 'test', 'scripts', 'bin'];
|
|
210
|
+
const files = ['package.json', 'README.md'];
|
|
211
|
+
|
|
212
|
+
for (const root of roots) {
|
|
213
|
+
const rootPath = join(packageRoot, root);
|
|
214
|
+
let entries;
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
entries = walk(rootPath);
|
|
218
|
+
} catch {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
for (const entry of entries) {
|
|
223
|
+
files.push(relative(packageRoot, entry));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return files;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function walk(directory) {
|
|
231
|
+
const results = [];
|
|
232
|
+
|
|
233
|
+
for (const name of readdirSync(directory)) {
|
|
234
|
+
if (name === 'node_modules' || name === 'dist') {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const path = join(directory, name);
|
|
239
|
+
|
|
240
|
+
if (statSync(path).isDirectory()) {
|
|
241
|
+
results.push(...walk(path));
|
|
242
|
+
} else {
|
|
243
|
+
results.push(path);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return results;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const leftovers = [];
|
|
251
|
+
|
|
252
|
+
for (const file of scanTargets()) {
|
|
253
|
+
let content = plannedWrites.get(file);
|
|
254
|
+
|
|
255
|
+
if (content === undefined) {
|
|
256
|
+
try {
|
|
257
|
+
content = readFileSync(join(packageRoot, file), 'utf8');
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (error?.code === 'ENOENT') {
|
|
260
|
+
// The file disappeared between walk() and this read; a file that no
|
|
261
|
+
// longer exists cannot hold a leftover pin, so skip it.
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Anything else (permissions, I/O) means the scan could MISS a
|
|
266
|
+
// leftover, so fail loudly in the script's own format instead of
|
|
267
|
+
// letting a raw Node.js stack trace escape.
|
|
268
|
+
fail(
|
|
269
|
+
`could not read ${file} while scanning for leftover '${oldVersion}' pins: ${error?.message ?? error}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (content.includes(oldVersion)) {
|
|
275
|
+
leftovers.push(file);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (leftovers.length > 0) {
|
|
280
|
+
fail(
|
|
281
|
+
`'${oldVersion}' still appears in: ${leftovers.join(', ')}. These pin sites are not in the known-sites list in scripts/bump-cli-version.mjs; add them so the bump rewrites every site, then re-run.`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
for (const [file, content] of plannedWrites) {
|
|
286
|
+
if (!dryRun) {
|
|
287
|
+
writeFileSync(join(packageRoot, file), content);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const verb = dryRun ? 'Would rewrite' : 'Rewrote';
|
|
292
|
+
process.stdout.write(`bump-cli-version: ${oldVersion} -> ${newVersion}\n`);
|
|
293
|
+
|
|
294
|
+
for (const item of summary) {
|
|
295
|
+
process.stdout.write(
|
|
296
|
+
` ${verb} ${item.file} (${item.occurrences} occurrence${item.occurrences === 1 ? '' : 's'}: ${item.description})\n`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (dryRun) {
|
|
301
|
+
process.stdout.write('Dry run - no files were written.\n');
|
|
302
|
+
} else {
|
|
303
|
+
process.stdout.write(
|
|
304
|
+
'Done. Now run the test suite before committing:\n pnpm vitest run --config vitest.config.mjs\nThen commit with a "[deploy-cli]" title and merge to main to publish.\n'
|
|
305
|
+
);
|
|
306
|
+
}
|