@zmice/zc 0.2.7 → 0.2.8
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 +430 -430
- package/dist/cli/__tests__/platform.test.js +112 -89
- package/dist/cli/__tests__/platform.test.js.map +1 -1
- package/dist/cli/__tests__/surface.test.js +1 -1
- package/dist/cli/__tests__/surface.test.js.map +1 -1
- package/dist/cli/platform.d.ts.map +1 -1
- package/dist/cli/platform.js +62 -5
- package/dist/cli/platform.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +41 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/node_modules/@zmice/platform-core/dist/index.test.js +5 -3
- package/dist/node_modules/@zmice/platform-core/dist/index.test.js.map +1 -1
- package/dist/runtime/worktree-manager.js +2 -2
- package/dist/runtime/worktree-manager.js.map +1 -1
- package/dist/utils/codex-config-merge.d.ts +3 -0
- package/dist/utils/codex-config-merge.d.ts.map +1 -0
- package/dist/utils/codex-config-merge.js +43 -0
- package/dist/utils/codex-config-merge.js.map +1 -0
- package/dist/utils/codex-config-merge.test.d.ts +2 -0
- package/dist/utils/codex-config-merge.test.d.ts.map +1 -0
- package/dist/utils/codex-config-merge.test.js +64 -0
- package/dist/utils/codex-config-merge.test.js.map +1 -0
- package/dist/utils/install-target.test.js +3 -2
- package/dist/utils/install-target.test.js.map +1 -1
- package/dist/utils/qwen-extension-cli.d.ts.map +1 -1
- package/dist/utils/qwen-extension-cli.js +4 -4
- package/dist/utils/qwen-extension-cli.js.map +1 -1
- package/dist/utils/qwen-extension-cli.test.js +9 -6
- package/dist/utils/qwen-extension-cli.test.js.map +1 -1
- package/dist/utils/workspace.js +1 -1
- package/dist/utils/workspace.js.map +1 -1
- package/dist/utils/workspace.test.js +14 -0
- package/dist/utils/workspace.test.js.map +1 -1
- package/package.json +2 -2
- package/vendor/node_modules/@zmice/platform-core/dist/index.test.js +5 -3
- package/vendor/node_modules/@zmice/platform-core/dist/index.test.js.map +1 -1
- package/vendor/packages/platform-claude/dist/index.js +75 -75
- package/vendor/packages/platform-claude/dist/index.test.js +11 -8
- package/vendor/packages/platform-claude/dist/index.test.js.map +1 -1
- package/vendor/packages/platform-codex/dist/index.js +194 -194
- package/vendor/packages/platform-codex/dist/index.test.js +16 -13
- package/vendor/packages/platform-codex/dist/index.test.js.map +1 -1
- package/vendor/packages/platform-opencode/dist/index.js +75 -75
- package/vendor/packages/platform-opencode/dist/index.test.js +19 -15
- package/vendor/packages/platform-opencode/dist/index.test.js.map +1 -1
- package/vendor/packages/platform-qwen/dist/index.js +75 -75
- package/vendor/packages/platform-qwen/dist/index.test.js +9 -7
- package/vendor/packages/platform-qwen/dist/index.test.js.map +1 -1
- package/vendor/packages/toolkit/src/content/agents/architect/body.md +42 -42
- package/vendor/packages/toolkit/src/content/agents/code-reviewer/body.md +41 -41
- package/vendor/packages/toolkit/src/content/agents/product-owner/body.md +40 -40
- package/vendor/packages/toolkit/src/content/commands/plan-review/body.md +31 -31
- package/vendor/packages/toolkit/src/content/commands/start/body.md +197 -197
- package/vendor/packages/toolkit/src/content/commands/start/meta.yaml +55 -55
- package/vendor/packages/toolkit/src/content/skills/branch-finish-and-cleanup/body.md +172 -172
- package/vendor/packages/toolkit/src/content/skills/browser-qa-testing/body.md +111 -111
- package/vendor/packages/toolkit/src/content/skills/ci-cd-and-automation/body.md +86 -86
- package/vendor/packages/toolkit/src/content/skills/code-review-and-quality/body.md +140 -140
- package/vendor/packages/toolkit/src/content/skills/code-simplification/body.md +80 -80
- package/vendor/packages/toolkit/src/content/skills/context-engineering/body.md +67 -67
- package/vendor/packages/toolkit/src/content/skills/continuous-learning/body.md +102 -102
- package/vendor/packages/toolkit/src/content/skills/multi-perspective-review/body.md +81 -81
- package/vendor/packages/toolkit/src/content/skills/parallel-agent-dispatch/body.md +113 -113
- package/vendor/packages/toolkit/src/content/skills/performance-optimization/body.md +75 -75
- package/vendor/packages/toolkit/src/content/skills/planning-and-task-breakdown/body.md +83 -83
- package/vendor/packages/toolkit/src/content/skills/sdd-tdd-workflow/body.md +95 -95
- package/vendor/packages/toolkit/src/content/skills/sprint-retrospective/body.md +99 -99
- package/vendor/packages/toolkit/src/content/skills/team-orchestration/body.md +126 -126
- package/vendor/packages/toolkit/src/content/skills/test-driven-development/body.md +78 -78
- package/vendor/packages/toolkit/src/content/skills/using-agent-skills/body.md +193 -193
- package/vendor/references/upstreams.yaml +94 -94
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
const abs = (path) => resolve(path);
|
|
3
5
|
const platformMocks = vi.hoisted(() => ({
|
|
4
6
|
ArtifactConflictError: class ArtifactConflictError extends Error {
|
|
5
7
|
conflicts;
|
|
@@ -224,15 +226,15 @@ describe("platform CLI", () => {
|
|
|
224
226
|
root: options.dir ?? options.cwd ?? process.cwd(),
|
|
225
227
|
source: options.dir ? "explicit" : "cwd",
|
|
226
228
|
}));
|
|
227
|
-
platformMocks.resolvePlatformInstallReceiptPath.mockImplementation((plan) => (
|
|
229
|
+
platformMocks.resolvePlatformInstallReceiptPath.mockImplementation((plan) => (join(plan.destinationRoot, ".zc", "platform-state", `${plan.platform}.install-receipt.json`)));
|
|
228
230
|
platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
|
|
229
231
|
kind: "up-to-date",
|
|
230
232
|
platform: "codex",
|
|
231
|
-
receiptPath: "/tmp/install
|
|
233
|
+
receiptPath: join(abs("/tmp/install"), ".zc", "platform-state", "codex.install-receipt.json"),
|
|
232
234
|
receipt: {
|
|
233
235
|
schemaVersion: 1,
|
|
234
236
|
platform: "codex",
|
|
235
|
-
destinationRoot: "/tmp/install",
|
|
237
|
+
destinationRoot: abs("/tmp/install"),
|
|
236
238
|
manifestSource: "/repo/packages/toolkit/src/content",
|
|
237
239
|
overwrite: "error",
|
|
238
240
|
installedAt: "2026-04-19T12:00:00.000Z",
|
|
@@ -264,18 +266,18 @@ describe("platform CLI", () => {
|
|
|
264
266
|
platformMocks.deletePlatformInstallReceipt.mockResolvedValue(undefined);
|
|
265
267
|
platformMocks.writePlatformInstallReceiptForPlan.mockResolvedValue({});
|
|
266
268
|
platformMocks.syncQwenOfficialCliSourceBundle.mockResolvedValue({
|
|
267
|
-
sourceDir: "/tmp/qwen-source",
|
|
269
|
+
sourceDir: abs("/tmp/qwen-source"),
|
|
268
270
|
extensionName: "zc-toolkit",
|
|
269
271
|
artifactCount: 1,
|
|
270
272
|
});
|
|
271
273
|
platformMocks.syncQwenOfficialCliReleaseBundle.mockResolvedValue({
|
|
272
|
-
bundleDir: "/tmp/qwen-release",
|
|
274
|
+
bundleDir: abs("/tmp/qwen-release"),
|
|
273
275
|
extensionName: "zc-toolkit",
|
|
274
276
|
artifactCount: 1,
|
|
275
277
|
});
|
|
276
278
|
platformMocks.toQwenOfficialCliReleaseArtifacts.mockImplementation((_plan, bundleDir) => [
|
|
277
|
-
{ path:
|
|
278
|
-
{ path:
|
|
279
|
+
{ path: join(bundleDir, "QWEN.md"), content: "# context" },
|
|
280
|
+
{ path: join(bundleDir, "commands/zc/start.md"), content: "# start" },
|
|
279
281
|
]);
|
|
280
282
|
platformMocks.installQwenExtensionFromOfficialRepoWithCli.mockResolvedValue(undefined);
|
|
281
283
|
platformMocks.installQwenExtensionWithOfficialCli.mockResolvedValue(undefined);
|
|
@@ -284,6 +286,7 @@ describe("platform CLI", () => {
|
|
|
284
286
|
platformMocks.relinkQwenExtensionWithOfficialCli.mockResolvedValue(undefined);
|
|
285
287
|
});
|
|
286
288
|
it("uses safe overwrite defaults for platform generate writes", async () => {
|
|
289
|
+
const outputRoot = abs("/tmp/out");
|
|
287
290
|
platformMocks.createQwenGenerationPlan.mockReturnValue(createGenerationPlan([{ path: "QWEN.md", content: "# context" }]));
|
|
288
291
|
platformMocks.writeArtifacts.mockResolvedValue({
|
|
289
292
|
created: 1,
|
|
@@ -293,9 +296,10 @@ describe("platform CLI", () => {
|
|
|
293
296
|
dryRun: false,
|
|
294
297
|
});
|
|
295
298
|
await runPlatformGenerate("qwen", { dir: "/tmp/out" });
|
|
296
|
-
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "
|
|
299
|
+
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: join(outputRoot, "QWEN.md"), content: "# context" }], { dryRun: false, overwrite: "error" });
|
|
297
300
|
});
|
|
298
301
|
it("prints a JSON plan without writing files when generate uses --plan", async () => {
|
|
302
|
+
const outputRoot = abs("/tmp/out");
|
|
299
303
|
platformMocks.createQwenGenerationPlan.mockReturnValue(createGenerationPlan([{ path: "QWEN.md", content: "# context" }]));
|
|
300
304
|
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
301
305
|
await runPlatformGenerate("qwen", { dir: "/tmp/out", plan: true, json: true });
|
|
@@ -305,17 +309,18 @@ describe("platform CLI", () => {
|
|
|
305
309
|
mode: "plan",
|
|
306
310
|
action: "generate",
|
|
307
311
|
target: "qwen",
|
|
308
|
-
root:
|
|
312
|
+
root: outputRoot,
|
|
309
313
|
artifactCount: 1,
|
|
310
|
-
artifacts: [{ path: "
|
|
314
|
+
artifacts: [{ path: join(outputRoot, "QWEN.md"), content: "# context" }],
|
|
311
315
|
}));
|
|
312
316
|
logSpy.mockRestore();
|
|
313
317
|
});
|
|
314
318
|
it("exports a standalone qwen release bundle during generate", async () => {
|
|
319
|
+
const bundleRoot = abs("/tmp/zc-toolkit");
|
|
315
320
|
platformMocks.createQwenGenerationPlan.mockReturnValue(createGenerationPlan([{ path: "QWEN.md", content: "# context" }]));
|
|
316
|
-
platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan(
|
|
317
|
-
{ path: "
|
|
318
|
-
{ path: "
|
|
321
|
+
platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan(bundleRoot, [
|
|
322
|
+
{ path: join(bundleRoot, "extensions/zc-toolkit/QWEN.md"), content: "# context" },
|
|
323
|
+
{ path: join(bundleRoot, "extensions/zc-toolkit/commands/zc/start.md"), content: "# start" },
|
|
319
324
|
]));
|
|
320
325
|
platformMocks.writeArtifacts.mockResolvedValue({
|
|
321
326
|
created: 2,
|
|
@@ -326,15 +331,16 @@ describe("platform CLI", () => {
|
|
|
326
331
|
});
|
|
327
332
|
await runPlatformGenerate("qwen", { dir: "/tmp/zc-toolkit", bundle: "release-bundle" });
|
|
328
333
|
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
|
|
329
|
-
{ path: "
|
|
330
|
-
{ path: "
|
|
334
|
+
{ path: join(bundleRoot, "QWEN.md"), content: "# context" },
|
|
335
|
+
{ path: join(bundleRoot, "commands/zc/start.md"), content: "# start" },
|
|
331
336
|
], { dryRun: false, overwrite: "error" });
|
|
332
337
|
});
|
|
333
338
|
it("prints qwen release bundle plan paths without native extension nesting", async () => {
|
|
339
|
+
const bundleRoot = abs("/tmp/zc-toolkit");
|
|
334
340
|
platformMocks.createQwenGenerationPlan.mockReturnValue(createGenerationPlan([{ path: "QWEN.md", content: "# context" }]));
|
|
335
|
-
platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan(
|
|
336
|
-
{ path: "
|
|
337
|
-
{ path: "
|
|
341
|
+
platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan(bundleRoot, [
|
|
342
|
+
{ path: join(bundleRoot, "extensions/zc-toolkit/QWEN.md"), content: "# context" },
|
|
343
|
+
{ path: join(bundleRoot, "extensions/zc-toolkit/commands/zc/start.md"), content: "# start" },
|
|
338
344
|
]));
|
|
339
345
|
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
340
346
|
await runPlatformGenerate("qwen", {
|
|
@@ -348,12 +354,12 @@ describe("platform CLI", () => {
|
|
|
348
354
|
mode: "plan",
|
|
349
355
|
action: "generate",
|
|
350
356
|
target: "qwen",
|
|
351
|
-
root:
|
|
357
|
+
root: bundleRoot,
|
|
352
358
|
bundleType: "release-bundle",
|
|
353
|
-
bundlePath:
|
|
359
|
+
bundlePath: bundleRoot,
|
|
354
360
|
artifacts: [
|
|
355
|
-
{ path: "
|
|
356
|
-
{ path: "
|
|
361
|
+
{ path: join(bundleRoot, "QWEN.md"), content: "# context" },
|
|
362
|
+
{ path: join(bundleRoot, "commands/zc/start.md"), content: "# start" },
|
|
357
363
|
],
|
|
358
364
|
}));
|
|
359
365
|
logSpy.mockRestore();
|
|
@@ -400,13 +406,14 @@ describe("platform CLI", () => {
|
|
|
400
406
|
});
|
|
401
407
|
expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "global" }));
|
|
402
408
|
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
|
|
403
|
-
{ path:
|
|
404
|
-
{ path:
|
|
405
|
-
{ path:
|
|
406
|
-
{ path:
|
|
409
|
+
{ path: join(homedir(), ".agents/plugins/marketplace.json"), content: "{}" },
|
|
410
|
+
{ path: join(homedir(), ".codex/AGENTS.md"), content: "# plugin entry" },
|
|
411
|
+
{ path: join(homedir(), ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json"), content: "{}" },
|
|
412
|
+
{ path: join(homedir(), ".codex/agents/zc-code-reviewer.toml"), content: "name = \"zc_code_reviewer\"\n" },
|
|
407
413
|
], { dryRun: false, overwrite: "error" });
|
|
408
414
|
});
|
|
409
415
|
it("exports codex marketplace to the resolved project root with --project", async () => {
|
|
416
|
+
const projectRoot = join("/repo", "project");
|
|
410
417
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
411
418
|
root: "/repo/project",
|
|
412
419
|
source: "project-root",
|
|
@@ -444,12 +451,13 @@ describe("platform CLI", () => {
|
|
|
444
451
|
expect(platformMocks.resolveInstallTarget).toHaveBeenCalledWith("codex", { project: true });
|
|
445
452
|
expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "project" }));
|
|
446
453
|
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
|
|
447
|
-
{ path: "
|
|
448
|
-
{ path: "
|
|
449
|
-
{ path: "
|
|
454
|
+
{ path: join(projectRoot, ".agents/plugins/marketplace.json"), content: "{}" },
|
|
455
|
+
{ path: join(projectRoot, "AGENTS.md"), content: "# plugin entry" },
|
|
456
|
+
{ path: join(projectRoot, "plugins/zc-toolkit/.codex-plugin/plugin.json"), content: "{}" },
|
|
450
457
|
], { dryRun: false, overwrite: "error" });
|
|
451
458
|
});
|
|
452
459
|
it("uses a short codex plugin command for the project marketplace by default", async () => {
|
|
460
|
+
const projectRoot = join("/repo", "project");
|
|
453
461
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
454
462
|
root: "/repo/project",
|
|
455
463
|
source: "project-root",
|
|
@@ -484,9 +492,9 @@ describe("platform CLI", () => {
|
|
|
484
492
|
expect(platformMocks.resolveInstallTarget).toHaveBeenCalledWith("codex", { project: true });
|
|
485
493
|
expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "project" }));
|
|
486
494
|
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
|
|
487
|
-
{ path: "
|
|
488
|
-
{ path: "
|
|
489
|
-
{ path: "
|
|
495
|
+
{ path: join(projectRoot, ".agents/plugins/marketplace.json"), content: "{}" },
|
|
496
|
+
{ path: join(projectRoot, "AGENTS.md"), content: "# plugin entry" },
|
|
497
|
+
{ path: join(projectRoot, "plugins/zc-toolkit/.codex-plugin/plugin.json"), content: "{}" },
|
|
490
498
|
], { dryRun: false, overwrite: "error" });
|
|
491
499
|
});
|
|
492
500
|
it("uses the personal marketplace for codex plugin when --global is explicit", async () => {
|
|
@@ -519,9 +527,9 @@ describe("platform CLI", () => {
|
|
|
519
527
|
expect(platformMocks.resolveInstallTarget).not.toHaveBeenCalled();
|
|
520
528
|
expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "global" }));
|
|
521
529
|
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
|
|
522
|
-
{ path:
|
|
523
|
-
{ path:
|
|
524
|
-
{ path:
|
|
530
|
+
{ path: join(homedir(), ".agents/plugins/marketplace.json"), content: "{}" },
|
|
531
|
+
{ path: join(homedir(), ".codex/AGENTS.md"), content: "# plugin entry" },
|
|
532
|
+
{ path: join(homedir(), ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json"), content: "{}" },
|
|
525
533
|
], { dryRun: false, overwrite: "error" });
|
|
526
534
|
});
|
|
527
535
|
it("cleans the generated Codex plugin skills directory on force writes", async () => {
|
|
@@ -553,17 +561,18 @@ describe("platform CLI", () => {
|
|
|
553
561
|
});
|
|
554
562
|
await runPlatformPlugin("codex", { global: true, force: true });
|
|
555
563
|
expect(platformMocks.removeManagedPaths).toHaveBeenCalledWith([
|
|
556
|
-
|
|
564
|
+
join(homedir(), ".codex/plugins/zc-toolkit/skills"),
|
|
557
565
|
]);
|
|
558
566
|
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
|
|
559
|
-
{ path:
|
|
560
|
-
{ path:
|
|
561
|
-
{ path:
|
|
562
|
-
{ path:
|
|
567
|
+
{ path: join(homedir(), ".agents/plugins/marketplace.json"), content: "{}" },
|
|
568
|
+
{ path: join(homedir(), ".codex/AGENTS.md"), content: "# plugin entry" },
|
|
569
|
+
{ path: join(homedir(), ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json"), content: "{}" },
|
|
570
|
+
{ path: join(homedir(), ".codex/plugins/zc-toolkit/skills/start/SKILL.md"), content: "# start" },
|
|
563
571
|
], { dryRun: false, overwrite: "force" });
|
|
564
572
|
});
|
|
565
573
|
it("uses safe overwrite defaults for platform install and writes a receipt", async () => {
|
|
566
|
-
|
|
574
|
+
const installRoot = abs("/tmp/install");
|
|
575
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", installRoot, [{ path: join(installRoot, "AGENTS.md"), content: "# agents" }]));
|
|
567
576
|
platformMocks.writeArtifacts.mockResolvedValue({
|
|
568
577
|
created: 1,
|
|
569
578
|
overwritten: 0,
|
|
@@ -573,19 +582,20 @@ describe("platform CLI", () => {
|
|
|
573
582
|
});
|
|
574
583
|
await runPlatformInstall("codex", { dir: "/tmp/install" });
|
|
575
584
|
expect(platformMocks.createCodexInstallPlan).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
576
|
-
destinationRoot:
|
|
585
|
+
destinationRoot: installRoot,
|
|
577
586
|
scope: "dir",
|
|
578
587
|
overwrite: "error",
|
|
579
588
|
}));
|
|
580
|
-
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "
|
|
589
|
+
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: join(installRoot, "AGENTS.md"), content: "# agents" }], { dryRun: false, overwrite: "error" });
|
|
581
590
|
expect(platformMocks.writePlatformInstallReceiptForPlan).toHaveBeenCalledWith(expect.objectContaining({
|
|
582
|
-
destinationRoot:
|
|
591
|
+
destinationRoot: installRoot,
|
|
583
592
|
}), expect.objectContaining({
|
|
584
593
|
zcVersion: expect.any(String),
|
|
585
594
|
}));
|
|
586
595
|
});
|
|
587
596
|
it("forwards force installs to artifact writes", async () => {
|
|
588
|
-
|
|
597
|
+
const installRoot = abs("/tmp/install");
|
|
598
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", installRoot, [{ path: join(installRoot, "AGENTS.md"), content: "# agents" }]));
|
|
589
599
|
platformMocks.writeArtifacts.mockResolvedValue({
|
|
590
600
|
created: 0,
|
|
591
601
|
overwritten: 1,
|
|
@@ -594,10 +604,11 @@ describe("platform CLI", () => {
|
|
|
594
604
|
dryRun: false,
|
|
595
605
|
});
|
|
596
606
|
await runPlatformInstall("codex", { dir: "/tmp/install", force: true });
|
|
597
|
-
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "
|
|
607
|
+
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: join(installRoot, "AGENTS.md"), content: "# agents" }], { dryRun: false, overwrite: "force" });
|
|
598
608
|
});
|
|
599
609
|
it("uses the resolved project root when install target is omitted", async () => {
|
|
600
|
-
|
|
610
|
+
const projectRoot = abs("/workspace/project");
|
|
611
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", projectRoot, [{ path: join(projectRoot, "AGENTS.md"), content: "# agents" }]));
|
|
601
612
|
platformMocks.writeArtifacts.mockResolvedValue({
|
|
602
613
|
created: 1,
|
|
603
614
|
overwritten: 0,
|
|
@@ -619,14 +630,15 @@ describe("platform CLI", () => {
|
|
|
619
630
|
global: undefined,
|
|
620
631
|
});
|
|
621
632
|
expect(platformMocks.createCodexInstallPlan).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
622
|
-
destinationRoot:
|
|
633
|
+
destinationRoot: projectRoot,
|
|
623
634
|
scope: "project",
|
|
624
635
|
}));
|
|
625
636
|
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("安装目录自动解析(project-root)"));
|
|
626
637
|
logSpy.mockRestore();
|
|
627
638
|
});
|
|
628
639
|
it("prints a JSON install plan without writing files when install uses --plan", async () => {
|
|
629
|
-
|
|
640
|
+
const installRoot = abs("/tmp/install");
|
|
641
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", installRoot, [{ path: join(installRoot, "AGENTS.md"), content: "# agents" }], "dir", "error"));
|
|
630
642
|
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
631
643
|
await runPlatformInstall("codex", { dir: "/tmp/install", plan: true, json: true });
|
|
632
644
|
expect(platformMocks.writeArtifacts).not.toHaveBeenCalled();
|
|
@@ -635,14 +647,14 @@ describe("platform CLI", () => {
|
|
|
635
647
|
mode: "plan",
|
|
636
648
|
action: "install",
|
|
637
649
|
target: "codex",
|
|
638
|
-
root:
|
|
650
|
+
root: installRoot,
|
|
639
651
|
scope: "dir",
|
|
640
652
|
rootSource: "explicit",
|
|
641
653
|
artifactCount: 1,
|
|
642
654
|
overwrite: "error",
|
|
643
655
|
artifacts: expect.arrayContaining([
|
|
644
656
|
expect.objectContaining({
|
|
645
|
-
path: "
|
|
657
|
+
path: join(installRoot, "AGENTS.md"),
|
|
646
658
|
content: "# agents",
|
|
647
659
|
}),
|
|
648
660
|
]),
|
|
@@ -650,21 +662,22 @@ describe("platform CLI", () => {
|
|
|
650
662
|
logSpy.mockRestore();
|
|
651
663
|
});
|
|
652
664
|
it("prints platform status from receipt and current plan", async () => {
|
|
665
|
+
const installRoot = abs("/tmp/install");
|
|
653
666
|
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
654
667
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
655
668
|
root: "/tmp/install",
|
|
656
669
|
source: "explicit",
|
|
657
670
|
});
|
|
658
|
-
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex",
|
|
671
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", installRoot, [{ path: join(installRoot, "AGENTS.md"), content: "# agents" }]));
|
|
659
672
|
await runPlatformStatus("codex", { dir: "/tmp/install", json: true });
|
|
660
673
|
expect(platformMocks.resolvePlatformInstallStatus).toHaveBeenCalledWith(expect.objectContaining({
|
|
661
|
-
destinationRoot:
|
|
674
|
+
destinationRoot: installRoot,
|
|
662
675
|
}));
|
|
663
676
|
const payload = JSON.parse(logSpy.mock.calls[0]?.[0] ?? "{}");
|
|
664
677
|
expect(payload).toEqual(expect.objectContaining({
|
|
665
678
|
mode: "status",
|
|
666
679
|
target: "codex",
|
|
667
|
-
root:
|
|
680
|
+
root: installRoot,
|
|
668
681
|
status: "up-to-date",
|
|
669
682
|
installedContentFingerprint: "installed-fingerprint",
|
|
670
683
|
contentFingerprint: "current-fingerprint",
|
|
@@ -672,32 +685,34 @@ describe("platform CLI", () => {
|
|
|
672
685
|
logSpy.mockRestore();
|
|
673
686
|
});
|
|
674
687
|
it("prints qwen platform status json with installed metadata from receipt", async () => {
|
|
688
|
+
const qwenRoot = abs("/home/test/.qwen");
|
|
689
|
+
const qwenBundleRoot = abs("/tmp/qwen-release/zc-toolkit");
|
|
675
690
|
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
676
691
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
677
692
|
root: "/home/test/.qwen",
|
|
678
693
|
source: "official-global",
|
|
679
694
|
hint: "Qwen 官方文档定义用户级配置目录为 `~/.qwen`。",
|
|
680
695
|
});
|
|
681
|
-
platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan(
|
|
696
|
+
platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan(qwenRoot, [{ path: join(qwenRoot, "extensions/zc-toolkit/QWEN.md"), content: "# context" }], "global"));
|
|
682
697
|
platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
|
|
683
698
|
kind: "up-to-date",
|
|
684
699
|
platform: "qwen",
|
|
685
|
-
receiptPath: "
|
|
700
|
+
receiptPath: join(qwenRoot, ".zc/platform-state/qwen.install-receipt.json"),
|
|
686
701
|
receipt: {
|
|
687
702
|
schemaVersion: 1,
|
|
688
703
|
platform: "qwen",
|
|
689
|
-
destinationRoot:
|
|
704
|
+
destinationRoot: qwenRoot,
|
|
690
705
|
manifestSource: "/repo/packages/toolkit/src/content",
|
|
691
706
|
overwrite: "error",
|
|
692
707
|
installedAt: "2026-04-19T12:00:00.000Z",
|
|
693
708
|
installMethod: "filesystem",
|
|
694
709
|
installSource: "local-bundle",
|
|
695
|
-
sourceRef:
|
|
710
|
+
sourceRef: qwenBundleRoot,
|
|
696
711
|
bundleType: "release-bundle",
|
|
697
|
-
bundlePath:
|
|
712
|
+
bundlePath: qwenBundleRoot,
|
|
698
713
|
artifacts: [
|
|
699
714
|
{
|
|
700
|
-
path: "
|
|
715
|
+
path: join(qwenRoot, "extensions/zc-toolkit/QWEN.md"),
|
|
701
716
|
sha256: "sha",
|
|
702
717
|
bytes: 9,
|
|
703
718
|
},
|
|
@@ -718,26 +733,27 @@ describe("platform CLI", () => {
|
|
|
718
733
|
expect(payload).toEqual(expect.objectContaining({
|
|
719
734
|
mode: "status",
|
|
720
735
|
target: "qwen",
|
|
721
|
-
root:
|
|
736
|
+
root: qwenRoot,
|
|
722
737
|
installMethod: "filesystem",
|
|
723
738
|
installSource: "local-bundle",
|
|
724
|
-
sourceRef:
|
|
739
|
+
sourceRef: qwenBundleRoot,
|
|
725
740
|
bundleType: "release-bundle",
|
|
726
|
-
bundlePath:
|
|
741
|
+
bundlePath: qwenBundleRoot,
|
|
727
742
|
recommendedInstallMethod: "qwen-cli",
|
|
728
743
|
recommendedInstallSource: "github-repo",
|
|
729
744
|
recommendedSourceRef: "https://github.com/zmice/zc-qwen-extension.git",
|
|
730
745
|
recommendedBundleType: "release-bundle",
|
|
731
|
-
recommendedBundlePath: "
|
|
746
|
+
recommendedBundlePath: expect.stringContaining("zc-toolkit"),
|
|
732
747
|
}));
|
|
733
748
|
logSpy.mockRestore();
|
|
734
749
|
});
|
|
735
750
|
it("prints update plan when status shows update-available", async () => {
|
|
751
|
+
const installRoot = abs("/tmp/install");
|
|
736
752
|
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
737
753
|
platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
|
|
738
754
|
kind: "update-available",
|
|
739
755
|
platform: "codex",
|
|
740
|
-
receiptPath: "
|
|
756
|
+
receiptPath: join(installRoot, ".zc/platform-state/codex.install-receipt.json"),
|
|
741
757
|
receipt: null,
|
|
742
758
|
contentFingerprint: "current-fingerprint",
|
|
743
759
|
installedContentFingerprint: "installed-fingerprint",
|
|
@@ -749,7 +765,7 @@ describe("platform CLI", () => {
|
|
|
749
765
|
},
|
|
750
766
|
artifacts: [],
|
|
751
767
|
});
|
|
752
|
-
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex",
|
|
768
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", installRoot, [{ path: join(installRoot, "AGENTS.md"), content: "# agents v2" }], "dir", "error"));
|
|
753
769
|
await runPlatformUpdate("codex", { dir: "/tmp/install", plan: true, json: true });
|
|
754
770
|
expect(platformMocks.writeArtifacts).not.toHaveBeenCalled();
|
|
755
771
|
const payload = JSON.parse(logSpy.mock.calls[0]?.[0] ?? "{}");
|
|
@@ -758,15 +774,16 @@ describe("platform CLI", () => {
|
|
|
758
774
|
action: "update",
|
|
759
775
|
target: "codex",
|
|
760
776
|
status: "update-available",
|
|
761
|
-
root:
|
|
777
|
+
root: installRoot,
|
|
762
778
|
}));
|
|
763
779
|
logSpy.mockRestore();
|
|
764
780
|
});
|
|
765
781
|
it("treats update-available as managed overwrite and refreshes the receipt", async () => {
|
|
782
|
+
const installRoot = abs("/tmp/install");
|
|
766
783
|
platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
|
|
767
784
|
kind: "update-available",
|
|
768
785
|
platform: "codex",
|
|
769
|
-
receiptPath: "
|
|
786
|
+
receiptPath: join(installRoot, ".zc/platform-state/codex.install-receipt.json"),
|
|
770
787
|
receipt: null,
|
|
771
788
|
contentFingerprint: "current-fingerprint",
|
|
772
789
|
installedContentFingerprint: "installed-fingerprint",
|
|
@@ -778,7 +795,7 @@ describe("platform CLI", () => {
|
|
|
778
795
|
},
|
|
779
796
|
artifacts: [],
|
|
780
797
|
});
|
|
781
|
-
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex",
|
|
798
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", installRoot, [{ path: join(installRoot, "AGENTS.md"), content: "# agents v2" }], "dir", "error"));
|
|
782
799
|
platformMocks.writeArtifacts.mockResolvedValue({
|
|
783
800
|
created: 0,
|
|
784
801
|
overwritten: 1,
|
|
@@ -788,10 +805,10 @@ describe("platform CLI", () => {
|
|
|
788
805
|
});
|
|
789
806
|
await runPlatformUpdate("codex", { dir: "/tmp/install" });
|
|
790
807
|
expect(platformMocks.createCodexInstallPlan).toHaveBeenLastCalledWith(expect.anything(), expect.objectContaining({
|
|
791
|
-
destinationRoot:
|
|
808
|
+
destinationRoot: installRoot,
|
|
792
809
|
overwrite: "force",
|
|
793
810
|
}));
|
|
794
|
-
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "
|
|
811
|
+
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: join(installRoot, "AGENTS.md"), content: "# agents v2" }], { dryRun: false, overwrite: "force" });
|
|
795
812
|
expect(platformMocks.writePlatformInstallReceiptForPlan).toHaveBeenCalled();
|
|
796
813
|
});
|
|
797
814
|
it("reports a no-op update when the installation is already up-to-date", async () => {
|
|
@@ -857,7 +874,8 @@ describe("platform CLI", () => {
|
|
|
857
874
|
errorSpy.mockRestore();
|
|
858
875
|
});
|
|
859
876
|
it("passes global scope through install target resolution and exposes official path hints", async () => {
|
|
860
|
-
|
|
877
|
+
const claudeRoot = abs("/home/test/.claude");
|
|
878
|
+
platformMocks.createClaudeInstallPlan.mockReturnValue(createInstallPlan("claude", claudeRoot, [{ path: join(claudeRoot, "CLAUDE.md"), content: "# claude" }]));
|
|
861
879
|
platformMocks.writeArtifacts.mockResolvedValue({
|
|
862
880
|
created: 1,
|
|
863
881
|
overwritten: 0,
|
|
@@ -879,14 +897,15 @@ describe("platform CLI", () => {
|
|
|
879
897
|
global: true,
|
|
880
898
|
});
|
|
881
899
|
expect(platformMocks.createClaudeInstallPlan).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
882
|
-
destinationRoot:
|
|
900
|
+
destinationRoot: claudeRoot,
|
|
883
901
|
scope: "global",
|
|
884
902
|
}));
|
|
885
903
|
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("提示:Claude Code 官方文档定义用户级 memory 文件位于 `~/.claude/CLAUDE.md`。"));
|
|
886
904
|
logSpy.mockRestore();
|
|
887
905
|
});
|
|
888
906
|
it("prefers the official qwen extensions CLI for global installs", async () => {
|
|
889
|
-
|
|
907
|
+
const qwenRoot = abs("/home/test/.qwen");
|
|
908
|
+
platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan(qwenRoot, [{ path: join(qwenRoot, "extensions/zc-toolkit/QWEN.md"), content: "# context" }], "global"));
|
|
890
909
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
891
910
|
root: "/home/test/.qwen",
|
|
892
911
|
source: "official-global",
|
|
@@ -895,7 +914,7 @@ describe("platform CLI", () => {
|
|
|
895
914
|
platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
|
|
896
915
|
kind: "not-installed",
|
|
897
916
|
platform: "qwen",
|
|
898
|
-
receiptPath: "
|
|
917
|
+
receiptPath: join(qwenRoot, ".zc/platform-state/qwen.install-receipt.json"),
|
|
899
918
|
receipt: null,
|
|
900
919
|
contentFingerprint: "current-fingerprint",
|
|
901
920
|
installedContentFingerprint: undefined,
|
|
@@ -915,7 +934,8 @@ describe("platform CLI", () => {
|
|
|
915
934
|
expect(platformMocks.writeArtifacts).not.toHaveBeenCalled();
|
|
916
935
|
});
|
|
917
936
|
it("prints a JSON install plan with scope metadata", async () => {
|
|
918
|
-
|
|
937
|
+
const codexRoot = abs("/home/test/.codex");
|
|
938
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", codexRoot, [{ path: join(codexRoot, "AGENTS.md"), content: "# agents" }], "global", "error"));
|
|
919
939
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
920
940
|
root: "/home/test/.codex",
|
|
921
941
|
source: "official-global",
|
|
@@ -929,7 +949,7 @@ describe("platform CLI", () => {
|
|
|
929
949
|
action: "install",
|
|
930
950
|
target: "codex",
|
|
931
951
|
scope: "global",
|
|
932
|
-
root:
|
|
952
|
+
root: codexRoot,
|
|
933
953
|
rootSource: "official-global",
|
|
934
954
|
hint: "Codex 官方文档将 Codex home(默认 `~/.codex`)定义为全局级 `AGENTS.md` 的位置。",
|
|
935
955
|
}));
|
|
@@ -1054,22 +1074,23 @@ describe("platform CLI", () => {
|
|
|
1054
1074
|
expect(platformMocks.deletePlatformInstallReceipt).toHaveBeenCalledWith("/home/test/.qwen/.zc/platform-state/qwen.install-receipt.json");
|
|
1055
1075
|
});
|
|
1056
1076
|
it("repairs drifted filesystem installs by rewriting managed artifacts", async () => {
|
|
1057
|
-
|
|
1077
|
+
const installRoot = abs("/tmp/install");
|
|
1078
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", installRoot, [{ path: join(installRoot, "AGENTS.md"), content: "# agents repaired" }]));
|
|
1058
1079
|
platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
|
|
1059
1080
|
kind: "drifted",
|
|
1060
1081
|
platform: "codex",
|
|
1061
|
-
receiptPath: "
|
|
1082
|
+
receiptPath: join(installRoot, ".zc/platform-state/codex.install-receipt.json"),
|
|
1062
1083
|
receipt: {
|
|
1063
1084
|
schemaVersion: 1,
|
|
1064
1085
|
platform: "codex",
|
|
1065
|
-
destinationRoot:
|
|
1086
|
+
destinationRoot: installRoot,
|
|
1066
1087
|
manifestSource: "/repo/packages/toolkit/src/content",
|
|
1067
1088
|
overwrite: "error",
|
|
1068
1089
|
installedAt: "2026-04-19T12:00:00.000Z",
|
|
1069
1090
|
installMethod: "filesystem",
|
|
1070
1091
|
artifacts: [
|
|
1071
1092
|
{
|
|
1072
|
-
path: "
|
|
1093
|
+
path: join(installRoot, "AGENTS.md"),
|
|
1073
1094
|
sha256: "old-sha",
|
|
1074
1095
|
bytes: 8,
|
|
1075
1096
|
},
|
|
@@ -1085,7 +1106,7 @@ describe("platform CLI", () => {
|
|
|
1085
1106
|
},
|
|
1086
1107
|
artifacts: [
|
|
1087
1108
|
{
|
|
1088
|
-
path: "
|
|
1109
|
+
path: join(installRoot, "AGENTS.md"),
|
|
1089
1110
|
receiptSha256: "old-sha",
|
|
1090
1111
|
actualSha256: "drifted-sha",
|
|
1091
1112
|
plannedSha256: "new-sha",
|
|
@@ -1103,10 +1124,10 @@ describe("platform CLI", () => {
|
|
|
1103
1124
|
});
|
|
1104
1125
|
await runPlatformRepair("codex", { dir: "/tmp/install" });
|
|
1105
1126
|
expect(platformMocks.createCodexInstallPlan).toHaveBeenLastCalledWith(expect.anything(), expect.objectContaining({
|
|
1106
|
-
destinationRoot:
|
|
1127
|
+
destinationRoot: installRoot,
|
|
1107
1128
|
overwrite: "force",
|
|
1108
1129
|
}));
|
|
1109
|
-
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "
|
|
1130
|
+
expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: join(installRoot, "AGENTS.md"), content: "# agents repaired" }], { dryRun: false, overwrite: "force" });
|
|
1110
1131
|
expect(platformMocks.writePlatformInstallReceiptForPlan).toHaveBeenCalled();
|
|
1111
1132
|
});
|
|
1112
1133
|
it("prints platform doctor health and issues", async () => {
|
|
@@ -1165,7 +1186,8 @@ describe("platform CLI", () => {
|
|
|
1165
1186
|
logSpy.mockRestore();
|
|
1166
1187
|
});
|
|
1167
1188
|
it("prints resolved install targets via platform where", async () => {
|
|
1168
|
-
|
|
1189
|
+
const codexRoot = abs("/home/test/.codex");
|
|
1190
|
+
platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", codexRoot, [{ path: join(codexRoot, "AGENTS.md"), content: "# agents" }], "global"));
|
|
1169
1191
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
1170
1192
|
root: "/home/test/.codex",
|
|
1171
1193
|
source: "official-global",
|
|
@@ -1184,7 +1206,7 @@ describe("platform CLI", () => {
|
|
|
1184
1206
|
mode: "where",
|
|
1185
1207
|
target: "codex",
|
|
1186
1208
|
scope: "global",
|
|
1187
|
-
root:
|
|
1209
|
+
root: codexRoot,
|
|
1188
1210
|
rootSource: "official-global",
|
|
1189
1211
|
capability: expect.objectContaining({
|
|
1190
1212
|
namespace: "zc",
|
|
@@ -1194,12 +1216,13 @@ describe("platform CLI", () => {
|
|
|
1194
1216
|
logSpy.mockRestore();
|
|
1195
1217
|
});
|
|
1196
1218
|
it("resolves qwen global scope to the user-level ~/.qwen directory", async () => {
|
|
1219
|
+
const qwenRoot = abs("/home/test/.qwen");
|
|
1197
1220
|
platformMocks.createQwenInstallPlan.mockReturnValue({
|
|
1198
1221
|
platform: "qwen",
|
|
1199
1222
|
packageName: "@zmice/platform-qwen",
|
|
1200
1223
|
manifestSource: "/repo/packages/toolkit/src/content#generatedAt=2026-04-19T12:00:00.000Z",
|
|
1201
1224
|
matchedAssets: [],
|
|
1202
|
-
destinationRoot:
|
|
1225
|
+
destinationRoot: qwenRoot,
|
|
1203
1226
|
scope: "global",
|
|
1204
1227
|
overwrite: "error",
|
|
1205
1228
|
capability: {
|
|
@@ -1211,7 +1234,7 @@ describe("platform CLI", () => {
|
|
|
1211
1234
|
agentsDir: "agents",
|
|
1212
1235
|
extensionDir: "extensions/zc-toolkit",
|
|
1213
1236
|
},
|
|
1214
|
-
artifacts: [{ path: "
|
|
1237
|
+
artifacts: [{ path: join(qwenRoot, "extensions/zc-toolkit/QWEN.md"), content: "# context" }],
|
|
1215
1238
|
});
|
|
1216
1239
|
platformMocks.resolveInstallTarget.mockResolvedValue({
|
|
1217
1240
|
root: "/home/test/.qwen",
|
|
@@ -1225,7 +1248,7 @@ describe("platform CLI", () => {
|
|
|
1225
1248
|
mode: "where",
|
|
1226
1249
|
target: "qwen",
|
|
1227
1250
|
scope: "global",
|
|
1228
|
-
root:
|
|
1251
|
+
root: qwenRoot,
|
|
1229
1252
|
rootSource: "official-global",
|
|
1230
1253
|
hint: "Qwen 官方文档定义用户级配置目录为 `~/.qwen`,并在官方帮助文档中给出 Qwen CLI 的用户级 `QWEN.md` 位置为 `~/.qwen/QWEN.md`。",
|
|
1231
1254
|
}));
|