@zmice/zc 0.2.6 → 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 (75) hide show
  1. package/README.md +430 -420
  2. package/dist/cli/__tests__/platform.test.js +158 -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 +92 -10
  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 +3 -3
  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.d.ts.map +1 -1
  43. package/vendor/packages/platform-codex/dist/index.js +262 -165
  44. package/vendor/packages/platform-codex/dist/index.js.map +1 -1
  45. package/vendor/packages/platform-codex/dist/index.test.js +42 -20
  46. package/vendor/packages/platform-codex/dist/index.test.js.map +1 -1
  47. package/vendor/packages/platform-opencode/dist/index.js +75 -75
  48. package/vendor/packages/platform-opencode/dist/index.test.js +19 -15
  49. package/vendor/packages/platform-opencode/dist/index.test.js.map +1 -1
  50. package/vendor/packages/platform-qwen/dist/index.js +75 -75
  51. package/vendor/packages/platform-qwen/dist/index.test.js +9 -7
  52. package/vendor/packages/platform-qwen/dist/index.test.js.map +1 -1
  53. package/vendor/packages/toolkit/src/content/agents/architect/body.md +42 -42
  54. package/vendor/packages/toolkit/src/content/agents/code-reviewer/body.md +41 -41
  55. package/vendor/packages/toolkit/src/content/agents/product-owner/body.md +40 -40
  56. package/vendor/packages/toolkit/src/content/commands/plan-review/body.md +31 -31
  57. package/vendor/packages/toolkit/src/content/commands/start/body.md +197 -197
  58. package/vendor/packages/toolkit/src/content/commands/start/meta.yaml +55 -55
  59. package/vendor/packages/toolkit/src/content/skills/branch-finish-and-cleanup/body.md +172 -172
  60. package/vendor/packages/toolkit/src/content/skills/browser-qa-testing/body.md +111 -111
  61. package/vendor/packages/toolkit/src/content/skills/ci-cd-and-automation/body.md +86 -86
  62. package/vendor/packages/toolkit/src/content/skills/code-review-and-quality/body.md +140 -140
  63. package/vendor/packages/toolkit/src/content/skills/code-simplification/body.md +80 -80
  64. package/vendor/packages/toolkit/src/content/skills/context-engineering/body.md +67 -67
  65. package/vendor/packages/toolkit/src/content/skills/continuous-learning/body.md +102 -102
  66. package/vendor/packages/toolkit/src/content/skills/multi-perspective-review/body.md +81 -81
  67. package/vendor/packages/toolkit/src/content/skills/parallel-agent-dispatch/body.md +113 -113
  68. package/vendor/packages/toolkit/src/content/skills/performance-optimization/body.md +75 -75
  69. package/vendor/packages/toolkit/src/content/skills/planning-and-task-breakdown/body.md +83 -83
  70. package/vendor/packages/toolkit/src/content/skills/sdd-tdd-workflow/body.md +95 -95
  71. package/vendor/packages/toolkit/src/content/skills/sprint-retrospective/body.md +99 -99
  72. package/vendor/packages/toolkit/src/content/skills/team-orchestration/body.md +126 -126
  73. package/vendor/packages/toolkit/src/content/skills/test-driven-development/body.md +78 -78
  74. package/vendor/packages/toolkit/src/content/skills/using-agent-skills/body.md +193 -193
  75. 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();
@@ -373,21 +379,22 @@ describe("platform CLI", () => {
373
379
  matchedAssets: [],
374
380
  capability: {
375
381
  namespace: "zc",
376
- surfaces: ["plugin-dir", "skills-dir", "agents-dir"],
377
- entryFile: null,
382
+ surfaces: ["entry-file", "plugin-dir", "skills-dir", "agents-dir"],
383
+ entryFile: ".codex/AGENTS.md",
378
384
  commandsDir: null,
379
- skillsDir: "skills",
385
+ skillsDir: ".codex/plugins/zc-toolkit/skills",
380
386
  agentsDir: ".codex/agents",
381
387
  extensionDir: null,
382
388
  },
383
389
  artifacts: [
384
390
  { path: ".agents/plugins/marketplace.json", content: "{}" },
391
+ { path: ".codex/AGENTS.md", content: "# plugin entry" },
385
392
  { path: ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
386
393
  { path: ".codex/agents/zc-code-reviewer.toml", content: "name = \"zc_code_reviewer\"\n" },
387
394
  ],
388
395
  });
389
396
  platformMocks.writeArtifacts.mockResolvedValue({
390
- created: 3,
397
+ created: 4,
391
398
  overwritten: 0,
392
399
  unchanged: 0,
393
400
  skipped: 0,
@@ -399,12 +406,14 @@ describe("platform CLI", () => {
399
406
  });
400
407
  expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "global" }));
401
408
  expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
402
- { path: `${homedir()}/.agents/plugins/marketplace.json`, content: "{}" },
403
- { path: `${homedir()}/.codex/plugins/zc-toolkit/.codex-plugin/plugin.json`, content: "{}" },
404
- { 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" },
405
413
  ], { dryRun: false, overwrite: "error" });
406
414
  });
407
415
  it("exports codex marketplace to the resolved project root with --project", async () => {
416
+ const projectRoot = join("/repo", "project");
408
417
  platformMocks.resolveInstallTarget.mockResolvedValue({
409
418
  root: "/repo/project",
410
419
  source: "project-root",
@@ -424,11 +433,12 @@ describe("platform CLI", () => {
424
433
  matchedAssets: [],
425
434
  artifacts: [
426
435
  { path: ".agents/plugins/marketplace.json", content: "{}" },
427
- { path: ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
436
+ { path: "AGENTS.md", content: "# plugin entry" },
437
+ { path: "plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
428
438
  ],
429
439
  });
430
440
  platformMocks.writeArtifacts.mockResolvedValue({
431
- created: 2,
441
+ created: 3,
432
442
  overwritten: 0,
433
443
  unchanged: 0,
434
444
  skipped: 0,
@@ -441,11 +451,13 @@ describe("platform CLI", () => {
441
451
  expect(platformMocks.resolveInstallTarget).toHaveBeenCalledWith("codex", { project: true });
442
452
  expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "project" }));
443
453
  expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
444
- { path: "/repo/project/.agents/plugins/marketplace.json", content: "{}" },
445
- { path: "/repo/project/.codex/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: "{}" },
446
457
  ], { dryRun: false, overwrite: "error" });
447
458
  });
448
459
  it("uses a short codex plugin command for the project marketplace by default", async () => {
460
+ const projectRoot = join("/repo", "project");
449
461
  platformMocks.resolveInstallTarget.mockResolvedValue({
450
462
  root: "/repo/project",
451
463
  source: "project-root",
@@ -465,11 +477,12 @@ describe("platform CLI", () => {
465
477
  matchedAssets: [],
466
478
  artifacts: [
467
479
  { path: ".agents/plugins/marketplace.json", content: "{}" },
468
- { path: ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
480
+ { path: "AGENTS.md", content: "# plugin entry" },
481
+ { path: "plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
469
482
  ],
470
483
  });
471
484
  platformMocks.writeArtifacts.mockResolvedValue({
472
- created: 2,
485
+ created: 3,
473
486
  overwritten: 0,
474
487
  unchanged: 0,
475
488
  skipped: 0,
@@ -479,8 +492,9 @@ describe("platform CLI", () => {
479
492
  expect(platformMocks.resolveInstallTarget).toHaveBeenCalledWith("codex", { project: true });
480
493
  expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "project" }));
481
494
  expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
482
- { path: "/repo/project/.agents/plugins/marketplace.json", content: "{}" },
483
- { path: "/repo/project/.codex/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: "{}" },
484
498
  ], { dryRun: false, overwrite: "error" });
485
499
  });
486
500
  it("uses the personal marketplace for codex plugin when --global is explicit", async () => {
@@ -498,11 +512,12 @@ describe("platform CLI", () => {
498
512
  matchedAssets: [],
499
513
  artifacts: [
500
514
  { path: ".agents/plugins/marketplace.json", content: "{}" },
515
+ { path: ".codex/AGENTS.md", content: "# plugin entry" },
501
516
  { path: ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
502
517
  ],
503
518
  });
504
519
  platformMocks.writeArtifacts.mockResolvedValue({
505
- created: 2,
520
+ created: 3,
506
521
  overwritten: 0,
507
522
  unchanged: 0,
508
523
  skipped: 0,
@@ -512,12 +527,52 @@ describe("platform CLI", () => {
512
527
  expect(platformMocks.resolveInstallTarget).not.toHaveBeenCalled();
513
528
  expect(platformMocks.createCodexMarketplaceGenerationPlan).toHaveBeenCalledWith(expect.any(Object), expect.objectContaining({ scope: "global" }));
514
529
  expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
515
- { path: `${homedir()}/.agents/plugins/marketplace.json`, content: "{}" },
516
- { 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: "{}" },
517
533
  ], { dryRun: false, overwrite: "error" });
518
534
  });
535
+ it("cleans the generated Codex plugin skills directory on force writes", async () => {
536
+ platformMocks.createCodexGenerationPlan.mockReturnValue({
537
+ platform: "codex",
538
+ packageName: "@zmice/platform-codex",
539
+ manifestSource: "/repo/packages/toolkit/src/content#generatedAt=2026-04-19T12:00:00.000Z",
540
+ matchedAssets: [],
541
+ artifacts: [],
542
+ });
543
+ platformMocks.createCodexMarketplaceGenerationPlan.mockReturnValue({
544
+ platform: "codex",
545
+ packageName: "@zmice/platform-codex",
546
+ manifestSource: "/repo/packages/toolkit/src/content#generatedAt=2026-04-19T12:00:00.000Z",
547
+ matchedAssets: [],
548
+ artifacts: [
549
+ { path: ".agents/plugins/marketplace.json", content: "{}" },
550
+ { path: ".codex/AGENTS.md", content: "# plugin entry" },
551
+ { path: ".codex/plugins/zc-toolkit/.codex-plugin/plugin.json", content: "{}" },
552
+ { path: ".codex/plugins/zc-toolkit/skills/start/SKILL.md", content: "# start" },
553
+ ],
554
+ });
555
+ platformMocks.writeArtifacts.mockResolvedValue({
556
+ created: 4,
557
+ overwritten: 0,
558
+ unchanged: 0,
559
+ skipped: 0,
560
+ dryRun: false,
561
+ });
562
+ await runPlatformPlugin("codex", { global: true, force: true });
563
+ expect(platformMocks.removeManagedPaths).toHaveBeenCalledWith([
564
+ join(homedir(), ".codex/plugins/zc-toolkit/skills"),
565
+ ]);
566
+ expect(platformMocks.writeArtifacts).toHaveBeenCalledWith([
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" },
571
+ ], { dryRun: false, overwrite: "force" });
572
+ });
519
573
  it("uses safe overwrite defaults for platform install and writes a receipt", async () => {
520
- 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" }]));
521
576
  platformMocks.writeArtifacts.mockResolvedValue({
522
577
  created: 1,
523
578
  overwritten: 0,
@@ -527,19 +582,20 @@ describe("platform CLI", () => {
527
582
  });
528
583
  await runPlatformInstall("codex", { dir: "/tmp/install" });
529
584
  expect(platformMocks.createCodexInstallPlan).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
530
- destinationRoot: "/tmp/install",
585
+ destinationRoot: installRoot,
531
586
  scope: "dir",
532
587
  overwrite: "error",
533
588
  }));
534
- 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" });
535
590
  expect(platformMocks.writePlatformInstallReceiptForPlan).toHaveBeenCalledWith(expect.objectContaining({
536
- destinationRoot: "/tmp/install",
591
+ destinationRoot: installRoot,
537
592
  }), expect.objectContaining({
538
593
  zcVersion: expect.any(String),
539
594
  }));
540
595
  });
541
596
  it("forwards force installs to artifact writes", async () => {
542
- 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" }]));
543
599
  platformMocks.writeArtifacts.mockResolvedValue({
544
600
  created: 0,
545
601
  overwritten: 1,
@@ -548,10 +604,11 @@ describe("platform CLI", () => {
548
604
  dryRun: false,
549
605
  });
550
606
  await runPlatformInstall("codex", { dir: "/tmp/install", force: true });
551
- 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" });
552
608
  });
553
609
  it("uses the resolved project root when install target is omitted", async () => {
554
- 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" }]));
555
612
  platformMocks.writeArtifacts.mockResolvedValue({
556
613
  created: 1,
557
614
  overwritten: 0,
@@ -573,14 +630,15 @@ describe("platform CLI", () => {
573
630
  global: undefined,
574
631
  });
575
632
  expect(platformMocks.createCodexInstallPlan).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
576
- destinationRoot: "/workspace/project",
633
+ destinationRoot: projectRoot,
577
634
  scope: "project",
578
635
  }));
579
636
  expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("安装目录自动解析(project-root)"));
580
637
  logSpy.mockRestore();
581
638
  });
582
639
  it("prints a JSON install plan without writing files when install uses --plan", async () => {
583
- 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"));
584
642
  const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
585
643
  await runPlatformInstall("codex", { dir: "/tmp/install", plan: true, json: true });
586
644
  expect(platformMocks.writeArtifacts).not.toHaveBeenCalled();
@@ -589,14 +647,14 @@ describe("platform CLI", () => {
589
647
  mode: "plan",
590
648
  action: "install",
591
649
  target: "codex",
592
- root: "/tmp/install",
650
+ root: installRoot,
593
651
  scope: "dir",
594
652
  rootSource: "explicit",
595
653
  artifactCount: 1,
596
654
  overwrite: "error",
597
655
  artifacts: expect.arrayContaining([
598
656
  expect.objectContaining({
599
- path: "/tmp/install/AGENTS.md",
657
+ path: join(installRoot, "AGENTS.md"),
600
658
  content: "# agents",
601
659
  }),
602
660
  ]),
@@ -604,21 +662,22 @@ describe("platform CLI", () => {
604
662
  logSpy.mockRestore();
605
663
  });
606
664
  it("prints platform status from receipt and current plan", async () => {
665
+ const installRoot = abs("/tmp/install");
607
666
  const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
608
667
  platformMocks.resolveInstallTarget.mockResolvedValue({
609
668
  root: "/tmp/install",
610
669
  source: "explicit",
611
670
  });
612
- 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" }]));
613
672
  await runPlatformStatus("codex", { dir: "/tmp/install", json: true });
614
673
  expect(platformMocks.resolvePlatformInstallStatus).toHaveBeenCalledWith(expect.objectContaining({
615
- destinationRoot: "/tmp/install",
674
+ destinationRoot: installRoot,
616
675
  }));
617
676
  const payload = JSON.parse(logSpy.mock.calls[0]?.[0] ?? "{}");
618
677
  expect(payload).toEqual(expect.objectContaining({
619
678
  mode: "status",
620
679
  target: "codex",
621
- root: "/tmp/install",
680
+ root: installRoot,
622
681
  status: "up-to-date",
623
682
  installedContentFingerprint: "installed-fingerprint",
624
683
  contentFingerprint: "current-fingerprint",
@@ -626,32 +685,34 @@ describe("platform CLI", () => {
626
685
  logSpy.mockRestore();
627
686
  });
628
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");
629
690
  const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
630
691
  platformMocks.resolveInstallTarget.mockResolvedValue({
631
692
  root: "/home/test/.qwen",
632
693
  source: "official-global",
633
694
  hint: "Qwen 官方文档定义用户级配置目录为 `~/.qwen`。",
634
695
  });
635
- 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"));
636
697
  platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
637
698
  kind: "up-to-date",
638
699
  platform: "qwen",
639
- receiptPath: "/home/test/.qwen/.zc/platform-state/qwen.install-receipt.json",
700
+ receiptPath: join(qwenRoot, ".zc/platform-state/qwen.install-receipt.json"),
640
701
  receipt: {
641
702
  schemaVersion: 1,
642
703
  platform: "qwen",
643
- destinationRoot: "/home/test/.qwen",
704
+ destinationRoot: qwenRoot,
644
705
  manifestSource: "/repo/packages/toolkit/src/content",
645
706
  overwrite: "error",
646
707
  installedAt: "2026-04-19T12:00:00.000Z",
647
708
  installMethod: "filesystem",
648
709
  installSource: "local-bundle",
649
- sourceRef: "/tmp/qwen-release/zc-toolkit",
710
+ sourceRef: qwenBundleRoot,
650
711
  bundleType: "release-bundle",
651
- bundlePath: "/tmp/qwen-release/zc-toolkit",
712
+ bundlePath: qwenBundleRoot,
652
713
  artifacts: [
653
714
  {
654
- path: "/home/test/.qwen/extensions/zc-toolkit/QWEN.md",
715
+ path: join(qwenRoot, "extensions/zc-toolkit/QWEN.md"),
655
716
  sha256: "sha",
656
717
  bytes: 9,
657
718
  },
@@ -672,26 +733,27 @@ describe("platform CLI", () => {
672
733
  expect(payload).toEqual(expect.objectContaining({
673
734
  mode: "status",
674
735
  target: "qwen",
675
- root: "/home/test/.qwen",
736
+ root: qwenRoot,
676
737
  installMethod: "filesystem",
677
738
  installSource: "local-bundle",
678
- sourceRef: "/tmp/qwen-release/zc-toolkit",
739
+ sourceRef: qwenBundleRoot,
679
740
  bundleType: "release-bundle",
680
- bundlePath: "/tmp/qwen-release/zc-toolkit",
741
+ bundlePath: qwenBundleRoot,
681
742
  recommendedInstallMethod: "qwen-cli",
682
743
  recommendedInstallSource: "github-repo",
683
744
  recommendedSourceRef: "https://github.com/zmice/zc-qwen-extension.git",
684
745
  recommendedBundleType: "release-bundle",
685
- recommendedBundlePath: "/home/test/.qwen/.zc/platform-bundles/qwen/zc-toolkit",
746
+ recommendedBundlePath: expect.stringContaining("zc-toolkit"),
686
747
  }));
687
748
  logSpy.mockRestore();
688
749
  });
689
750
  it("prints update plan when status shows update-available", async () => {
751
+ const installRoot = abs("/tmp/install");
690
752
  const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
691
753
  platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
692
754
  kind: "update-available",
693
755
  platform: "codex",
694
- receiptPath: "/tmp/install/.zc/platform-state/codex.install-receipt.json",
756
+ receiptPath: join(installRoot, ".zc/platform-state/codex.install-receipt.json"),
695
757
  receipt: null,
696
758
  contentFingerprint: "current-fingerprint",
697
759
  installedContentFingerprint: "installed-fingerprint",
@@ -703,7 +765,7 @@ describe("platform CLI", () => {
703
765
  },
704
766
  artifacts: [],
705
767
  });
706
- 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"));
707
769
  await runPlatformUpdate("codex", { dir: "/tmp/install", plan: true, json: true });
708
770
  expect(platformMocks.writeArtifacts).not.toHaveBeenCalled();
709
771
  const payload = JSON.parse(logSpy.mock.calls[0]?.[0] ?? "{}");
@@ -712,15 +774,16 @@ describe("platform CLI", () => {
712
774
  action: "update",
713
775
  target: "codex",
714
776
  status: "update-available",
715
- root: "/tmp/install",
777
+ root: installRoot,
716
778
  }));
717
779
  logSpy.mockRestore();
718
780
  });
719
781
  it("treats update-available as managed overwrite and refreshes the receipt", async () => {
782
+ const installRoot = abs("/tmp/install");
720
783
  platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
721
784
  kind: "update-available",
722
785
  platform: "codex",
723
- receiptPath: "/tmp/install/.zc/platform-state/codex.install-receipt.json",
786
+ receiptPath: join(installRoot, ".zc/platform-state/codex.install-receipt.json"),
724
787
  receipt: null,
725
788
  contentFingerprint: "current-fingerprint",
726
789
  installedContentFingerprint: "installed-fingerprint",
@@ -732,7 +795,7 @@ describe("platform CLI", () => {
732
795
  },
733
796
  artifacts: [],
734
797
  });
735
- 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"));
736
799
  platformMocks.writeArtifacts.mockResolvedValue({
737
800
  created: 0,
738
801
  overwritten: 1,
@@ -742,10 +805,10 @@ describe("platform CLI", () => {
742
805
  });
743
806
  await runPlatformUpdate("codex", { dir: "/tmp/install" });
744
807
  expect(platformMocks.createCodexInstallPlan).toHaveBeenLastCalledWith(expect.anything(), expect.objectContaining({
745
- destinationRoot: "/tmp/install",
808
+ destinationRoot: installRoot,
746
809
  overwrite: "force",
747
810
  }));
748
- 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" });
749
812
  expect(platformMocks.writePlatformInstallReceiptForPlan).toHaveBeenCalled();
750
813
  });
751
814
  it("reports a no-op update when the installation is already up-to-date", async () => {
@@ -811,7 +874,8 @@ describe("platform CLI", () => {
811
874
  errorSpy.mockRestore();
812
875
  });
813
876
  it("passes global scope through install target resolution and exposes official path hints", async () => {
814
- 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" }]));
815
879
  platformMocks.writeArtifacts.mockResolvedValue({
816
880
  created: 1,
817
881
  overwritten: 0,
@@ -833,14 +897,15 @@ describe("platform CLI", () => {
833
897
  global: true,
834
898
  });
835
899
  expect(platformMocks.createClaudeInstallPlan).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
836
- destinationRoot: "/home/test/.claude",
900
+ destinationRoot: claudeRoot,
837
901
  scope: "global",
838
902
  }));
839
903
  expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("提示:Claude Code 官方文档定义用户级 memory 文件位于 `~/.claude/CLAUDE.md`。"));
840
904
  logSpy.mockRestore();
841
905
  });
842
906
  it("prefers the official qwen extensions CLI for global installs", async () => {
843
- 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"));
844
909
  platformMocks.resolveInstallTarget.mockResolvedValue({
845
910
  root: "/home/test/.qwen",
846
911
  source: "official-global",
@@ -849,7 +914,7 @@ describe("platform CLI", () => {
849
914
  platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
850
915
  kind: "not-installed",
851
916
  platform: "qwen",
852
- receiptPath: "/home/test/.qwen/.zc/platform-state/qwen.install-receipt.json",
917
+ receiptPath: join(qwenRoot, ".zc/platform-state/qwen.install-receipt.json"),
853
918
  receipt: null,
854
919
  contentFingerprint: "current-fingerprint",
855
920
  installedContentFingerprint: undefined,
@@ -869,7 +934,8 @@ describe("platform CLI", () => {
869
934
  expect(platformMocks.writeArtifacts).not.toHaveBeenCalled();
870
935
  });
871
936
  it("prints a JSON install plan with scope metadata", async () => {
872
- 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"));
873
939
  platformMocks.resolveInstallTarget.mockResolvedValue({
874
940
  root: "/home/test/.codex",
875
941
  source: "official-global",
@@ -883,7 +949,7 @@ describe("platform CLI", () => {
883
949
  action: "install",
884
950
  target: "codex",
885
951
  scope: "global",
886
- root: "/home/test/.codex",
952
+ root: codexRoot,
887
953
  rootSource: "official-global",
888
954
  hint: "Codex 官方文档将 Codex home(默认 `~/.codex`)定义为全局级 `AGENTS.md` 的位置。",
889
955
  }));
@@ -1008,22 +1074,23 @@ describe("platform CLI", () => {
1008
1074
  expect(platformMocks.deletePlatformInstallReceipt).toHaveBeenCalledWith("/home/test/.qwen/.zc/platform-state/qwen.install-receipt.json");
1009
1075
  });
1010
1076
  it("repairs drifted filesystem installs by rewriting managed artifacts", async () => {
1011
- 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" }]));
1012
1079
  platformMocks.resolvePlatformInstallStatus.mockResolvedValue({
1013
1080
  kind: "drifted",
1014
1081
  platform: "codex",
1015
- receiptPath: "/tmp/install/.zc/platform-state/codex.install-receipt.json",
1082
+ receiptPath: join(installRoot, ".zc/platform-state/codex.install-receipt.json"),
1016
1083
  receipt: {
1017
1084
  schemaVersion: 1,
1018
1085
  platform: "codex",
1019
- destinationRoot: "/tmp/install",
1086
+ destinationRoot: installRoot,
1020
1087
  manifestSource: "/repo/packages/toolkit/src/content",
1021
1088
  overwrite: "error",
1022
1089
  installedAt: "2026-04-19T12:00:00.000Z",
1023
1090
  installMethod: "filesystem",
1024
1091
  artifacts: [
1025
1092
  {
1026
- path: "/tmp/install/AGENTS.md",
1093
+ path: join(installRoot, "AGENTS.md"),
1027
1094
  sha256: "old-sha",
1028
1095
  bytes: 8,
1029
1096
  },
@@ -1039,7 +1106,7 @@ describe("platform CLI", () => {
1039
1106
  },
1040
1107
  artifacts: [
1041
1108
  {
1042
- path: "/tmp/install/AGENTS.md",
1109
+ path: join(installRoot, "AGENTS.md"),
1043
1110
  receiptSha256: "old-sha",
1044
1111
  actualSha256: "drifted-sha",
1045
1112
  plannedSha256: "new-sha",
@@ -1057,10 +1124,10 @@ describe("platform CLI", () => {
1057
1124
  });
1058
1125
  await runPlatformRepair("codex", { dir: "/tmp/install" });
1059
1126
  expect(platformMocks.createCodexInstallPlan).toHaveBeenLastCalledWith(expect.anything(), expect.objectContaining({
1060
- destinationRoot: "/tmp/install",
1127
+ destinationRoot: installRoot,
1061
1128
  overwrite: "force",
1062
1129
  }));
1063
- 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" });
1064
1131
  expect(platformMocks.writePlatformInstallReceiptForPlan).toHaveBeenCalled();
1065
1132
  });
1066
1133
  it("prints platform doctor health and issues", async () => {
@@ -1119,7 +1186,8 @@ describe("platform CLI", () => {
1119
1186
  logSpy.mockRestore();
1120
1187
  });
1121
1188
  it("prints resolved install targets via platform where", async () => {
1122
- 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"));
1123
1191
  platformMocks.resolveInstallTarget.mockResolvedValue({
1124
1192
  root: "/home/test/.codex",
1125
1193
  source: "official-global",
@@ -1138,7 +1206,7 @@ describe("platform CLI", () => {
1138
1206
  mode: "where",
1139
1207
  target: "codex",
1140
1208
  scope: "global",
1141
- root: "/home/test/.codex",
1209
+ root: codexRoot,
1142
1210
  rootSource: "official-global",
1143
1211
  capability: expect.objectContaining({
1144
1212
  namespace: "zc",
@@ -1148,12 +1216,13 @@ describe("platform CLI", () => {
1148
1216
  logSpy.mockRestore();
1149
1217
  });
1150
1218
  it("resolves qwen global scope to the user-level ~/.qwen directory", async () => {
1219
+ const qwenRoot = abs("/home/test/.qwen");
1151
1220
  platformMocks.createQwenInstallPlan.mockReturnValue({
1152
1221
  platform: "qwen",
1153
1222
  packageName: "@zmice/platform-qwen",
1154
1223
  manifestSource: "/repo/packages/toolkit/src/content#generatedAt=2026-04-19T12:00:00.000Z",
1155
1224
  matchedAssets: [],
1156
- destinationRoot: "/home/test/.qwen",
1225
+ destinationRoot: qwenRoot,
1157
1226
  scope: "global",
1158
1227
  overwrite: "error",
1159
1228
  capability: {
@@ -1165,7 +1234,7 @@ describe("platform CLI", () => {
1165
1234
  agentsDir: "agents",
1166
1235
  extensionDir: "extensions/zc-toolkit",
1167
1236
  },
1168
- artifacts: [{ path: "/home/test/.qwen/extensions/zc-toolkit/QWEN.md", content: "# context" }],
1237
+ artifacts: [{ path: join(qwenRoot, "extensions/zc-toolkit/QWEN.md"), content: "# context" }],
1169
1238
  });
1170
1239
  platformMocks.resolveInstallTarget.mockResolvedValue({
1171
1240
  root: "/home/test/.qwen",
@@ -1179,7 +1248,7 @@ describe("platform CLI", () => {
1179
1248
  mode: "where",
1180
1249
  target: "qwen",
1181
1250
  scope: "global",
1182
- root: "/home/test/.qwen",
1251
+ root: qwenRoot,
1183
1252
  rootSource: "official-global",
1184
1253
  hint: "Qwen 官方文档定义用户级配置目录为 `~/.qwen`,并在官方帮助文档中给出 Qwen CLI 的用户级 `QWEN.md` 位置为 `~/.qwen/QWEN.md`。",
1185
1254
  }));