@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.
Files changed (73) hide show
  1. package/README.md +430 -430
  2. package/dist/cli/__tests__/platform.test.js +112 -89
  3. package/dist/cli/__tests__/platform.test.js.map +1 -1
  4. package/dist/cli/__tests__/surface.test.js +1 -1
  5. package/dist/cli/__tests__/surface.test.js.map +1 -1
  6. package/dist/cli/platform.d.ts.map +1 -1
  7. package/dist/cli/platform.js +62 -5
  8. package/dist/cli/platform.js.map +1 -1
  9. package/dist/cli/setup.d.ts +3 -0
  10. package/dist/cli/setup.d.ts.map +1 -0
  11. package/dist/cli/setup.js +41 -0
  12. package/dist/cli/setup.js.map +1 -0
  13. package/dist/node_modules/@zmice/platform-core/dist/index.test.js +5 -3
  14. package/dist/node_modules/@zmice/platform-core/dist/index.test.js.map +1 -1
  15. package/dist/runtime/worktree-manager.js +2 -2
  16. package/dist/runtime/worktree-manager.js.map +1 -1
  17. package/dist/utils/codex-config-merge.d.ts +3 -0
  18. package/dist/utils/codex-config-merge.d.ts.map +1 -0
  19. package/dist/utils/codex-config-merge.js +43 -0
  20. package/dist/utils/codex-config-merge.js.map +1 -0
  21. package/dist/utils/codex-config-merge.test.d.ts +2 -0
  22. package/dist/utils/codex-config-merge.test.d.ts.map +1 -0
  23. package/dist/utils/codex-config-merge.test.js +64 -0
  24. package/dist/utils/codex-config-merge.test.js.map +1 -0
  25. package/dist/utils/install-target.test.js +3 -2
  26. package/dist/utils/install-target.test.js.map +1 -1
  27. package/dist/utils/qwen-extension-cli.d.ts.map +1 -1
  28. package/dist/utils/qwen-extension-cli.js +4 -4
  29. package/dist/utils/qwen-extension-cli.js.map +1 -1
  30. package/dist/utils/qwen-extension-cli.test.js +9 -6
  31. package/dist/utils/qwen-extension-cli.test.js.map +1 -1
  32. package/dist/utils/workspace.js +1 -1
  33. package/dist/utils/workspace.js.map +1 -1
  34. package/dist/utils/workspace.test.js +14 -0
  35. package/dist/utils/workspace.test.js.map +1 -1
  36. package/package.json +2 -2
  37. package/vendor/node_modules/@zmice/platform-core/dist/index.test.js +5 -3
  38. package/vendor/node_modules/@zmice/platform-core/dist/index.test.js.map +1 -1
  39. package/vendor/packages/platform-claude/dist/index.js +75 -75
  40. package/vendor/packages/platform-claude/dist/index.test.js +11 -8
  41. package/vendor/packages/platform-claude/dist/index.test.js.map +1 -1
  42. package/vendor/packages/platform-codex/dist/index.js +194 -194
  43. package/vendor/packages/platform-codex/dist/index.test.js +16 -13
  44. package/vendor/packages/platform-codex/dist/index.test.js.map +1 -1
  45. package/vendor/packages/platform-opencode/dist/index.js +75 -75
  46. package/vendor/packages/platform-opencode/dist/index.test.js +19 -15
  47. package/vendor/packages/platform-opencode/dist/index.test.js.map +1 -1
  48. package/vendor/packages/platform-qwen/dist/index.js +75 -75
  49. package/vendor/packages/platform-qwen/dist/index.test.js +9 -7
  50. package/vendor/packages/platform-qwen/dist/index.test.js.map +1 -1
  51. package/vendor/packages/toolkit/src/content/agents/architect/body.md +42 -42
  52. package/vendor/packages/toolkit/src/content/agents/code-reviewer/body.md +41 -41
  53. package/vendor/packages/toolkit/src/content/agents/product-owner/body.md +40 -40
  54. package/vendor/packages/toolkit/src/content/commands/plan-review/body.md +31 -31
  55. package/vendor/packages/toolkit/src/content/commands/start/body.md +197 -197
  56. package/vendor/packages/toolkit/src/content/commands/start/meta.yaml +55 -55
  57. package/vendor/packages/toolkit/src/content/skills/branch-finish-and-cleanup/body.md +172 -172
  58. package/vendor/packages/toolkit/src/content/skills/browser-qa-testing/body.md +111 -111
  59. package/vendor/packages/toolkit/src/content/skills/ci-cd-and-automation/body.md +86 -86
  60. package/vendor/packages/toolkit/src/content/skills/code-review-and-quality/body.md +140 -140
  61. package/vendor/packages/toolkit/src/content/skills/code-simplification/body.md +80 -80
  62. package/vendor/packages/toolkit/src/content/skills/context-engineering/body.md +67 -67
  63. package/vendor/packages/toolkit/src/content/skills/continuous-learning/body.md +102 -102
  64. package/vendor/packages/toolkit/src/content/skills/multi-perspective-review/body.md +81 -81
  65. package/vendor/packages/toolkit/src/content/skills/parallel-agent-dispatch/body.md +113 -113
  66. package/vendor/packages/toolkit/src/content/skills/performance-optimization/body.md +75 -75
  67. package/vendor/packages/toolkit/src/content/skills/planning-and-task-breakdown/body.md +83 -83
  68. package/vendor/packages/toolkit/src/content/skills/sdd-tdd-workflow/body.md +95 -95
  69. package/vendor/packages/toolkit/src/content/skills/sprint-retrospective/body.md +99 -99
  70. package/vendor/packages/toolkit/src/content/skills/team-orchestration/body.md +126 -126
  71. package/vendor/packages/toolkit/src/content/skills/test-driven-development/body.md +78 -78
  72. package/vendor/packages/toolkit/src/content/skills/using-agent-skills/body.md +193 -193
  73. 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) => (`${plan.destinationRoot}/.zc/platform-state/${plan.platform}.install-receipt.json`));
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/.zc/platform-state/codex.install-receipt.json",
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: `${bundleDir}/QWEN.md`, content: "# context" },
278
- { path: `${bundleDir}/commands/zc/start.md`, content: "# start" },
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: "/tmp/out/QWEN.md", content: "# context" }], { dryRun: false, overwrite: "error" });
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: "/tmp/out",
312
+ root: outputRoot,
309
313
  artifactCount: 1,
310
- artifacts: [{ path: "/tmp/out/QWEN.md", content: "# context" }],
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("/tmp/zc-toolkit", [
317
- { path: "/tmp/zc-toolkit/extensions/zc-toolkit/QWEN.md", content: "# context" },
318
- { path: "/tmp/zc-toolkit/extensions/zc-toolkit/commands/zc/start.md", content: "# start" },
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: "/tmp/zc-toolkit/QWEN.md", content: "# context" },
330
- { path: "/tmp/zc-toolkit/commands/zc/start.md", content: "# start" },
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("/tmp/zc-toolkit", [
336
- { path: "/tmp/zc-toolkit/extensions/zc-toolkit/QWEN.md", content: "# context" },
337
- { path: "/tmp/zc-toolkit/extensions/zc-toolkit/commands/zc/start.md", content: "# start" },
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: "/tmp/zc-toolkit",
357
+ root: bundleRoot,
352
358
  bundleType: "release-bundle",
353
- bundlePath: "/tmp/zc-toolkit",
359
+ bundlePath: bundleRoot,
354
360
  artifacts: [
355
- { path: "/tmp/zc-toolkit/QWEN.md", content: "# context" },
356
- { path: "/tmp/zc-toolkit/commands/zc/start.md", content: "# start" },
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: `${homedir()}/.agents/plugins/marketplace.json`, content: "{}" },
404
- { path: `${homedir()}/.codex/AGENTS.md`, content: "# plugin entry" },
405
- { path: `${homedir()}/.codex/plugins/zc-toolkit/.codex-plugin/plugin.json`, content: "{}" },
406
- { path: `${homedir()}/.codex/agents/zc-code-reviewer.toml`, content: "name = \"zc_code_reviewer\"\n" },
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: "/repo/project/.agents/plugins/marketplace.json", content: "{}" },
448
- { path: "/repo/project/AGENTS.md", content: "# plugin entry" },
449
- { path: "/repo/project/plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
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: "/repo/project/.agents/plugins/marketplace.json", content: "{}" },
488
- { path: "/repo/project/AGENTS.md", content: "# plugin entry" },
489
- { path: "/repo/project/plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
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: `${homedir()}/.agents/plugins/marketplace.json`, content: "{}" },
523
- { path: `${homedir()}/.codex/AGENTS.md`, content: "# plugin entry" },
524
- { path: `${homedir()}/.codex/plugins/zc-toolkit/.codex-plugin/plugin.json`, content: "{}" },
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
- `${homedir()}/.codex/plugins/zc-toolkit/skills`,
564
+ join(homedir(), ".codex/plugins/zc-toolkit/skills"),
557
565
  ]);
558
566
  expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
559
- { path: `${homedir()}/.agents/plugins/marketplace.json`, content: "{}" },
560
- { path: `${homedir()}/.codex/AGENTS.md`, content: "# plugin entry" },
561
- { path: `${homedir()}/.codex/plugins/zc-toolkit/.codex-plugin/plugin.json`, content: "{}" },
562
- { path: `${homedir()}/.codex/plugins/zc-toolkit/skills/start/SKILL.md`, content: "# start" },
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
- platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", "/tmp/install", [{ path: "/tmp/install/AGENTS.md", content: "# agents" }]));
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: "/tmp/install",
585
+ destinationRoot: installRoot,
577
586
  scope: "dir",
578
587
  overwrite: "error",
579
588
  }));
580
- expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "/tmp/install/AGENTS.md", content: "# agents" }], { dryRun: false, overwrite: "error" });
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: "/tmp/install",
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
- platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", "/tmp/install", [{ path: "/tmp/install/AGENTS.md", content: "# agents" }]));
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: "/tmp/install/AGENTS.md", content: "# agents" }], { dryRun: false, overwrite: "force" });
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
- platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", "/workspace/project", [{ path: "/workspace/project/AGENTS.md", content: "# agents" }]));
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: "/workspace/project",
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
- platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", "/tmp/install", [{ path: "/tmp/install/AGENTS.md", content: "# agents" }], "dir", "error"));
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: "/tmp/install",
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: "/tmp/install/AGENTS.md",
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", "/tmp/install", [{ path: "/tmp/install/AGENTS.md", content: "# agents" }]));
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: "/tmp/install",
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: "/tmp/install",
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("/home/test/.qwen", [{ path: "/home/test/.qwen/extensions/zc-toolkit/QWEN.md", content: "# context" }], "global"));
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: "/home/test/.qwen/.zc/platform-state/qwen.install-receipt.json",
700
+ receiptPath: join(qwenRoot, ".zc/platform-state/qwen.install-receipt.json"),
686
701
  receipt: {
687
702
  schemaVersion: 1,
688
703
  platform: "qwen",
689
- destinationRoot: "/home/test/.qwen",
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: "/tmp/qwen-release/zc-toolkit",
710
+ sourceRef: qwenBundleRoot,
696
711
  bundleType: "release-bundle",
697
- bundlePath: "/tmp/qwen-release/zc-toolkit",
712
+ bundlePath: qwenBundleRoot,
698
713
  artifacts: [
699
714
  {
700
- path: "/home/test/.qwen/extensions/zc-toolkit/QWEN.md",
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: "/home/test/.qwen",
736
+ root: qwenRoot,
722
737
  installMethod: "filesystem",
723
738
  installSource: "local-bundle",
724
- sourceRef: "/tmp/qwen-release/zc-toolkit",
739
+ sourceRef: qwenBundleRoot,
725
740
  bundleType: "release-bundle",
726
- bundlePath: "/tmp/qwen-release/zc-toolkit",
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: "/home/test/.qwen/.zc/platform-bundles/qwen/zc-toolkit",
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: "/tmp/install/.zc/platform-state/codex.install-receipt.json",
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", "/tmp/install", [{ path: "/tmp/install/AGENTS.md", content: "# agents v2" }], "dir", "error"));
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: "/tmp/install",
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: "/tmp/install/.zc/platform-state/codex.install-receipt.json",
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", "/tmp/install", [{ path: "/tmp/install/AGENTS.md", content: "# agents v2" }], "dir", "error"));
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: "/tmp/install",
808
+ destinationRoot: installRoot,
792
809
  overwrite: "force",
793
810
  }));
794
- expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "/tmp/install/AGENTS.md", content: "# agents v2" }], { dryRun: false, overwrite: "force" });
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
- platformMocks.createClaudeInstallPlan.mockReturnValue(createInstallPlan("claude", "/home/test/.claude", [{ path: "/home/test/.claude/CLAUDE.md", content: "# claude" }]));
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: "/home/test/.claude",
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
- platformMocks.createQwenInstallPlan.mockReturnValue(createQwenInstallPlan("/home/test/.qwen", [{ path: "/home/test/.qwen/extensions/zc-toolkit/QWEN.md", content: "# context" }], "global"));
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: "/home/test/.qwen/.zc/platform-state/qwen.install-receipt.json",
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
- platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", "/home/test/.codex", [{ path: "/home/test/.codex/AGENTS.md", content: "# agents" }], "global", "error"));
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: "/home/test/.codex",
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
- platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", "/tmp/install", [{ path: "/tmp/install/AGENTS.md", content: "# agents repaired" }]));
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: "/tmp/install/.zc/platform-state/codex.install-receipt.json",
1082
+ receiptPath: join(installRoot, ".zc/platform-state/codex.install-receipt.json"),
1062
1083
  receipt: {
1063
1084
  schemaVersion: 1,
1064
1085
  platform: "codex",
1065
- destinationRoot: "/tmp/install",
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: "/tmp/install/AGENTS.md",
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: "/tmp/install/AGENTS.md",
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: "/tmp/install",
1127
+ destinationRoot: installRoot,
1107
1128
  overwrite: "force",
1108
1129
  }));
1109
- expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([{ path: "/tmp/install/AGENTS.md", content: "# agents repaired" }], { dryRun: false, overwrite: "force" });
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
- platformMocks.createCodexInstallPlan.mockReturnValue(createInstallPlan("codex", "/home/test/.codex", [{ path: "/home/test/.codex/AGENTS.md", content: "# agents" }], "global"));
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: "/home/test/.codex",
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: "/home/test/.qwen",
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: "/home/test/.qwen/extensions/zc-toolkit/QWEN.md", content: "# context" }],
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: "/home/test/.qwen",
1251
+ root: qwenRoot,
1229
1252
  rootSource: "official-global",
1230
1253
  hint: "Qwen 官方文档定义用户级配置目录为 `~/.qwen`,并在官方帮助文档中给出 Qwen CLI 的用户级 `QWEN.md` 位置为 `~/.qwen/QWEN.md`。",
1231
1254
  }));