@mison/ag-kit-cn 2.0.1 → 3.0.0

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 (251) hide show
  1. package/{.agent → .agents}/agents/frontend-specialist.md +2 -2
  2. package/{.agent → .agents}/agents/orchestrator.md +1 -3
  3. package/.agents/mcp_config.json +22 -0
  4. package/{.agent → .agents}/rules/GEMINI.md +2 -2
  5. package/{.agent → .agents}/skills/api-patterns/SKILL.md +2 -2
  6. package/{.agent → .agents}/skills/app-builder/project-detection.md +1 -1
  7. package/{.agent → .agents}/skills/nextjs-react-expert/SKILL.md +2 -2
  8. package/{.agent → .agents}/skills/parallel-agents/SKILL.md +0 -1
  9. package/.agents/skills/refactoring-patterns/SKILL.md +43 -0
  10. package/{.agent → .agents}/workflows/create.md +1 -1
  11. package/CHANGELOG.md +21 -0
  12. package/README.md +54 -8
  13. package/bin/adapters/codex.js +29 -6
  14. package/bin/adapters/gemini.js +23 -3
  15. package/bin/ag-kit.js +512 -17
  16. package/bin/core/builder.js +1 -1
  17. package/bin/core/resource-loader.js +2 -2
  18. package/bin/utils/manifest.js +3 -0
  19. package/bin/utils.js +10 -3
  20. package/docs/PLAN.md +39 -0
  21. package/docs/TECH.md +136 -0
  22. package/package.json +12 -10
  23. package/scripts/ci-verify.js +95 -0
  24. package/scripts/clean.js +123 -0
  25. package/scripts/health-check.js +132 -0
  26. package/scripts/health-check.sh +6 -0
  27. package/tests/atomic-writer.test.js +47 -0
  28. package/tests/clean-script.test.js +77 -0
  29. package/tests/cli-smoke.test.js +479 -0
  30. package/tests/codex-adapter.test.js +132 -0
  31. package/tests/doctor.test.js +94 -0
  32. package/tests/gemini-adapter.test.js +30 -0
  33. package/tests/generator.test.js +48 -0
  34. package/tests/git-helper.test.js +53 -0
  35. package/tests/global-sync.test.js +133 -0
  36. package/tests/health-check-script.test.js +30 -0
  37. package/tests/managed-block.test.js +41 -0
  38. package/tests/manifest.test.js +97 -0
  39. package/tests/package-tarball.test.js +27 -0
  40. package/tests/phase-c.test.js +107 -0
  41. package/tests/standards-compliance.test.js +266 -0
  42. package/tests/transformer.test.js +74 -0
  43. package/.agent/mcp_config.json +0 -12
  44. package/docs/codex-rules-template.md +0 -36
  45. package/docs/mapping-spec.md +0 -68
  46. package/docs/multi-target-adapter.md +0 -80
  47. package/docs/official/README.md +0 -53
  48. package/docs/official/antigravity/agent-modes-settings.md +0 -64
  49. package/docs/official/antigravity/rules-workflows.md +0 -96
  50. package/docs/official/antigravity/skills.md +0 -147
  51. package/docs/official/codex/agents-md.md +0 -119
  52. package/docs/official/codex/config-advanced.md +0 -358
  53. package/docs/official/codex/config-basic.md +0 -141
  54. package/docs/official/codex/config-reference.md +0 -223
  55. package/docs/official/codex/config-sample.md +0 -216
  56. package/docs/official/codex/mcp.md +0 -107
  57. package/docs/official/codex/rules.md +0 -79
  58. package/docs/official/codex/skills.md +0 -114
  59. package/docs/official/sources-index.md +0 -32
  60. package/docs/operations.md +0 -145
  61. package/docs/terminology-style-guide.md +0 -69
  62. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/charts.csv +0 -0
  63. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/colors.csv +0 -0
  64. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/icons.csv +0 -0
  65. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/landing.csv +0 -0
  66. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/products.csv +0 -0
  67. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/prompts.csv +0 -0
  68. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/react-performance.csv +0 -0
  69. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -0
  70. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -0
  71. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -0
  72. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -0
  73. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -0
  74. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -0
  75. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -0
  76. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -0
  77. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -0
  78. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -0
  79. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -0
  80. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -0
  81. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/styles.csv +0 -0
  82. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/typography.csv +0 -0
  83. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -0
  84. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -0
  85. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/web-interface.csv +0 -0
  86. /package/{.agent → .agents}/.shared/ui-ux-pro-max/scripts/core.py +0 -0
  87. /package/{.agent → .agents}/.shared/ui-ux-pro-max/scripts/design_system.py +0 -0
  88. /package/{.agent → .agents}/.shared/ui-ux-pro-max/scripts/search.py +0 -0
  89. /package/{.agent → .agents}/ARCHITECTURE.md +0 -0
  90. /package/{.agent → .agents}/agents/backend-specialist.md +0 -0
  91. /package/{.agent → .agents}/agents/code-archaeologist.md +0 -0
  92. /package/{.agent → .agents}/agents/database-architect.md +0 -0
  93. /package/{.agent → .agents}/agents/debugger.md +0 -0
  94. /package/{.agent → .agents}/agents/devops-engineer.md +0 -0
  95. /package/{.agent → .agents}/agents/documentation-writer.md +0 -0
  96. /package/{.agent → .agents}/agents/explorer-agent.md +0 -0
  97. /package/{.agent → .agents}/agents/game-developer.md +0 -0
  98. /package/{.agent → .agents}/agents/mobile-developer.md +0 -0
  99. /package/{.agent → .agents}/agents/penetration-tester.md +0 -0
  100. /package/{.agent → .agents}/agents/performance-optimizer.md +0 -0
  101. /package/{.agent → .agents}/agents/product-manager.md +0 -0
  102. /package/{.agent → .agents}/agents/product-owner.md +0 -0
  103. /package/{.agent → .agents}/agents/project-planner.md +0 -0
  104. /package/{.agent → .agents}/agents/qa-automation-engineer.md +0 -0
  105. /package/{.agent → .agents}/agents/security-auditor.md +0 -0
  106. /package/{.agent → .agents}/agents/seo-specialist.md +0 -0
  107. /package/{.agent → .agents}/agents/test-engineer.md +0 -0
  108. /package/{.agent → .agents}/scripts/auto_preview.py +0 -0
  109. /package/{.agent → .agents}/scripts/checklist.py +0 -0
  110. /package/{.agent → .agents}/scripts/session_manager.py +0 -0
  111. /package/{.agent → .agents}/scripts/verify_all.py +0 -0
  112. /package/{.agent → .agents}/skills/api-patterns/api-style.md +0 -0
  113. /package/{.agent → .agents}/skills/api-patterns/auth.md +0 -0
  114. /package/{.agent → .agents}/skills/api-patterns/documentation.md +0 -0
  115. /package/{.agent → .agents}/skills/api-patterns/graphql.md +0 -0
  116. /package/{.agent → .agents}/skills/api-patterns/rate-limiting.md +0 -0
  117. /package/{.agent → .agents}/skills/api-patterns/response.md +0 -0
  118. /package/{.agent → .agents}/skills/api-patterns/rest.md +0 -0
  119. /package/{.agent → .agents}/skills/api-patterns/scripts/api_validator.py +0 -0
  120. /package/{.agent → .agents}/skills/api-patterns/security-testing.md +0 -0
  121. /package/{.agent → .agents}/skills/api-patterns/trpc.md +0 -0
  122. /package/{.agent → .agents}/skills/api-patterns/versioning.md +0 -0
  123. /package/{.agent → .agents}/skills/app-builder/SKILL.md +0 -0
  124. /package/{.agent → .agents}/skills/app-builder/agent-coordination.md +0 -0
  125. /package/{.agent → .agents}/skills/app-builder/feature-building.md +0 -0
  126. /package/{.agent → .agents}/skills/app-builder/scaffolding.md +0 -0
  127. /package/{.agent → .agents}/skills/app-builder/tech-stack.md +0 -0
  128. /package/{.agent → .agents}/skills/app-builder/templates/SKILL.md +0 -0
  129. /package/{.agent → .agents}/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -0
  130. /package/{.agent → .agents}/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -0
  131. /package/{.agent → .agents}/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -0
  132. /package/{.agent → .agents}/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -0
  133. /package/{.agent → .agents}/skills/app-builder/templates/express-api/TEMPLATE.md +0 -0
  134. /package/{.agent → .agents}/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -0
  135. /package/{.agent → .agents}/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -0
  136. /package/{.agent → .agents}/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -0
  137. /package/{.agent → .agents}/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -0
  138. /package/{.agent → .agents}/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -0
  139. /package/{.agent → .agents}/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -0
  140. /package/{.agent → .agents}/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -0
  141. /package/{.agent → .agents}/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -0
  142. /package/{.agent → .agents}/skills/architecture/SKILL.md +0 -0
  143. /package/{.agent → .agents}/skills/architecture/context-discovery.md +0 -0
  144. /package/{.agent → .agents}/skills/architecture/examples.md +0 -0
  145. /package/{.agent → .agents}/skills/architecture/pattern-selection.md +0 -0
  146. /package/{.agent → .agents}/skills/architecture/patterns-reference.md +0 -0
  147. /package/{.agent → .agents}/skills/architecture/trade-off-analysis.md +0 -0
  148. /package/{.agent → .agents}/skills/bash-linux/SKILL.md +0 -0
  149. /package/{.agent → .agents}/skills/behavioral-modes/SKILL.md +0 -0
  150. /package/{.agent → .agents}/skills/brainstorming/SKILL.md +0 -0
  151. /package/{.agent → .agents}/skills/brainstorming/dynamic-questioning.md +0 -0
  152. /package/{.agent → .agents}/skills/clean-code/SKILL.md +0 -0
  153. /package/{.agent → .agents}/skills/code-review-checklist/SKILL.md +0 -0
  154. /package/{.agent → .agents}/skills/database-design/SKILL.md +0 -0
  155. /package/{.agent → .agents}/skills/database-design/database-selection.md +0 -0
  156. /package/{.agent → .agents}/skills/database-design/indexing.md +0 -0
  157. /package/{.agent → .agents}/skills/database-design/migrations.md +0 -0
  158. /package/{.agent → .agents}/skills/database-design/optimization.md +0 -0
  159. /package/{.agent → .agents}/skills/database-design/orm-selection.md +0 -0
  160. /package/{.agent → .agents}/skills/database-design/schema-design.md +0 -0
  161. /package/{.agent → .agents}/skills/database-design/scripts/schema_validator.py +0 -0
  162. /package/{.agent → .agents}/skills/deployment-procedures/SKILL.md +0 -0
  163. /package/{.agent → .agents}/skills/doc.md +0 -0
  164. /package/{.agent → .agents}/skills/documentation-templates/SKILL.md +0 -0
  165. /package/{.agent → .agents}/skills/frontend-design/SKILL.md +0 -0
  166. /package/{.agent → .agents}/skills/frontend-design/animation-guide.md +0 -0
  167. /package/{.agent → .agents}/skills/frontend-design/color-system.md +0 -0
  168. /package/{.agent → .agents}/skills/frontend-design/decision-trees.md +0 -0
  169. /package/{.agent → .agents}/skills/frontend-design/motion-graphics.md +0 -0
  170. /package/{.agent → .agents}/skills/frontend-design/scripts/accessibility_checker.py +0 -0
  171. /package/{.agent → .agents}/skills/frontend-design/scripts/ux_audit.py +0 -0
  172. /package/{.agent → .agents}/skills/frontend-design/typography-system.md +0 -0
  173. /package/{.agent → .agents}/skills/frontend-design/ux-psychology.md +0 -0
  174. /package/{.agent → .agents}/skills/frontend-design/visual-effects.md +0 -0
  175. /package/{.agent → .agents}/skills/game-development/2d-games/SKILL.md +0 -0
  176. /package/{.agent → .agents}/skills/game-development/3d-games/SKILL.md +0 -0
  177. /package/{.agent → .agents}/skills/game-development/SKILL.md +0 -0
  178. /package/{.agent → .agents}/skills/game-development/game-art/SKILL.md +0 -0
  179. /package/{.agent → .agents}/skills/game-development/game-audio/SKILL.md +0 -0
  180. /package/{.agent → .agents}/skills/game-development/game-design/SKILL.md +0 -0
  181. /package/{.agent → .agents}/skills/game-development/mobile-games/SKILL.md +0 -0
  182. /package/{.agent → .agents}/skills/game-development/multiplayer/SKILL.md +0 -0
  183. /package/{.agent → .agents}/skills/game-development/pc-games/SKILL.md +0 -0
  184. /package/{.agent → .agents}/skills/game-development/vr-ar/SKILL.md +0 -0
  185. /package/{.agent → .agents}/skills/game-development/web-games/SKILL.md +0 -0
  186. /package/{.agent → .agents}/skills/geo-fundamentals/SKILL.md +0 -0
  187. /package/{.agent → .agents}/skills/geo-fundamentals/scripts/geo_checker.py +0 -0
  188. /package/{.agent → .agents}/skills/i18n-localization/SKILL.md +0 -0
  189. /package/{.agent → .agents}/skills/i18n-localization/scripts/i18n_checker.py +0 -0
  190. /package/{.agent → .agents}/skills/intelligent-routing/SKILL.md +0 -0
  191. /package/{.agent → .agents}/skills/lint-and-validate/SKILL.md +0 -0
  192. /package/{.agent → .agents}/skills/lint-and-validate/scripts/lint_runner.py +0 -0
  193. /package/{.agent → .agents}/skills/lint-and-validate/scripts/type_coverage.py +0 -0
  194. /package/{.agent → .agents}/skills/mcp-builder/SKILL.md +0 -0
  195. /package/{.agent → .agents}/skills/mobile-design/SKILL.md +0 -0
  196. /package/{.agent → .agents}/skills/mobile-design/decision-trees.md +0 -0
  197. /package/{.agent → .agents}/skills/mobile-design/mobile-backend.md +0 -0
  198. /package/{.agent → .agents}/skills/mobile-design/mobile-color-system.md +0 -0
  199. /package/{.agent → .agents}/skills/mobile-design/mobile-debugging.md +0 -0
  200. /package/{.agent → .agents}/skills/mobile-design/mobile-design-thinking.md +0 -0
  201. /package/{.agent → .agents}/skills/mobile-design/mobile-navigation.md +0 -0
  202. /package/{.agent → .agents}/skills/mobile-design/mobile-performance.md +0 -0
  203. /package/{.agent → .agents}/skills/mobile-design/mobile-testing.md +0 -0
  204. /package/{.agent → .agents}/skills/mobile-design/mobile-typography.md +0 -0
  205. /package/{.agent → .agents}/skills/mobile-design/platform-android.md +0 -0
  206. /package/{.agent → .agents}/skills/mobile-design/platform-ios.md +0 -0
  207. /package/{.agent → .agents}/skills/mobile-design/scripts/mobile_audit.py +0 -0
  208. /package/{.agent → .agents}/skills/mobile-design/touch-psychology.md +0 -0
  209. /package/{.agent → .agents}/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +0 -0
  210. /package/{.agent → .agents}/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +0 -0
  211. /package/{.agent → .agents}/skills/nextjs-react-expert/3-server-server-side-performance.md +0 -0
  212. /package/{.agent → .agents}/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +0 -0
  213. /package/{.agent → .agents}/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +0 -0
  214. /package/{.agent → .agents}/skills/nextjs-react-expert/6-rendering-rendering-performance.md +0 -0
  215. /package/{.agent → .agents}/skills/nextjs-react-expert/7-js-javascript-performance.md +0 -0
  216. /package/{.agent → .agents}/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +0 -0
  217. /package/{.agent → .agents}/skills/nextjs-react-expert/scripts/convert_rules.py +0 -0
  218. /package/{.agent → .agents}/skills/nextjs-react-expert/scripts/react_performance_checker.py +0 -0
  219. /package/{.agent → .agents}/skills/nodejs-best-practices/SKILL.md +0 -0
  220. /package/{.agent → .agents}/skills/performance-profiling/SKILL.md +0 -0
  221. /package/{.agent → .agents}/skills/performance-profiling/scripts/lighthouse_audit.py +0 -0
  222. /package/{.agent → .agents}/skills/plan-writing/SKILL.md +0 -0
  223. /package/{.agent → .agents}/skills/powershell-windows/SKILL.md +0 -0
  224. /package/{.agent → .agents}/skills/python-patterns/SKILL.md +0 -0
  225. /package/{.agent → .agents}/skills/red-team-tactics/SKILL.md +0 -0
  226. /package/{.agent → .agents}/skills/rust-pro/SKILL.md +0 -0
  227. /package/{.agent → .agents}/skills/seo-fundamentals/SKILL.md +0 -0
  228. /package/{.agent → .agents}/skills/seo-fundamentals/scripts/seo_checker.py +0 -0
  229. /package/{.agent → .agents}/skills/server-management/SKILL.md +0 -0
  230. /package/{.agent → .agents}/skills/systematic-debugging/SKILL.md +0 -0
  231. /package/{.agent → .agents}/skills/tailwind-patterns/SKILL.md +0 -0
  232. /package/{.agent → .agents}/skills/tdd-workflow/SKILL.md +0 -0
  233. /package/{.agent → .agents}/skills/testing-patterns/SKILL.md +0 -0
  234. /package/{.agent → .agents}/skills/testing-patterns/scripts/test_runner.py +0 -0
  235. /package/{.agent → .agents}/skills/vulnerability-scanner/SKILL.md +0 -0
  236. /package/{.agent → .agents}/skills/vulnerability-scanner/checklists.md +0 -0
  237. /package/{.agent → .agents}/skills/vulnerability-scanner/scripts/security_scan.py +0 -0
  238. /package/{.agent → .agents}/skills/web-design-guidelines/SKILL.md +0 -0
  239. /package/{.agent → .agents}/skills/webapp-testing/SKILL.md +0 -0
  240. /package/{.agent → .agents}/skills/webapp-testing/scripts/playwright_runner.py +0 -0
  241. /package/{.agent → .agents}/workflows/brainstorm.md +0 -0
  242. /package/{.agent → .agents}/workflows/debug.md +0 -0
  243. /package/{.agent → .agents}/workflows/deploy.md +0 -0
  244. /package/{.agent → .agents}/workflows/enhance.md +0 -0
  245. /package/{.agent → .agents}/workflows/orchestrate.md +0 -0
  246. /package/{.agent → .agents}/workflows/plan.md +0 -0
  247. /package/{.agent → .agents}/workflows/preview.md +0 -0
  248. /package/{.agent → .agents}/workflows/restore-localize-compat.md +0 -0
  249. /package/{.agent → .agents}/workflows/status.md +0 -0
  250. /package/{.agent → .agents}/workflows/test.md +0 -0
  251. /package/{.agent → .agents}/workflows/ui-ux-pro-max.md +0 -0
@@ -0,0 +1,47 @@
1
+ const { test, describe, beforeEach, afterEach } = require('node:test');
2
+ const assert = require('node:assert');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+ const AtomicWriter = require('../bin/utils/atomic-writer');
7
+
8
+ describe('AtomicWriter', () => {
9
+ let workDir;
10
+ let sourceDir;
11
+ let targetDir;
12
+
13
+ beforeEach(() => {
14
+ workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'atomic-test-'));
15
+ sourceDir = path.join(workDir, 'source');
16
+ targetDir = path.join(workDir, 'target');
17
+
18
+ fs.mkdirSync(sourceDir);
19
+ fs.writeFileSync(path.join(sourceDir, 'data.txt'), 'version 1');
20
+ });
21
+
22
+ afterEach(() => {
23
+ fs.rmSync(workDir, { recursive: true, force: true });
24
+ });
25
+
26
+ test('atomicCopyDir should copy directory when target does not exist', () => {
27
+ AtomicWriter.atomicCopyDir(sourceDir, targetDir);
28
+
29
+ assert.ok(fs.existsSync(targetDir));
30
+ assert.strictEqual(fs.readFileSync(path.join(targetDir, 'data.txt'), 'utf8'), 'version 1');
31
+ });
32
+
33
+ test('atomicCopyDir should overwrite existing directory', () => {
34
+ // Setup initial target
35
+ fs.mkdirSync(targetDir);
36
+ fs.writeFileSync(path.join(targetDir, 'old.txt'), 'old data');
37
+
38
+ // Prepare new source
39
+ fs.writeFileSync(path.join(sourceDir, 'data.txt'), 'version 2');
40
+
41
+ AtomicWriter.atomicCopyDir(sourceDir, targetDir);
42
+
43
+ assert.ok(fs.existsSync(targetDir));
44
+ assert.strictEqual(fs.readFileSync(path.join(targetDir, 'data.txt'), 'utf8'), 'version 2');
45
+ assert.ok(!fs.existsSync(path.join(targetDir, 'old.txt')), 'Old file should be gone');
46
+ });
47
+ });
@@ -0,0 +1,77 @@
1
+ const { test, describe, beforeEach, afterEach } = require("node:test");
2
+ const assert = require("node:assert");
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+ const { parseArgs, runClean } = require("../scripts/clean");
7
+
8
+ describe("Clean Script", () => {
9
+ let tempRoot;
10
+
11
+ beforeEach(() => {
12
+ tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-clean-test-"));
13
+ });
14
+
15
+ afterEach(() => {
16
+ fs.rmSync(tempRoot, { recursive: true, force: true });
17
+ });
18
+
19
+ test("parseArgs should parse dry-run and quiet options", () => {
20
+ const options = parseArgs(["--dry-run", "--quiet"]);
21
+ assert.strictEqual(options.dryRun, true);
22
+ assert.strictEqual(options.quiet, true);
23
+ });
24
+
25
+ test("runClean should remove existing generated directories", () => {
26
+ const targetA = path.join(tempRoot, "web", ".next");
27
+ const targetB = path.join(tempRoot, "web", "node_modules");
28
+ fs.mkdirSync(targetA, { recursive: true });
29
+ fs.mkdirSync(targetB, { recursive: true });
30
+ fs.writeFileSync(path.join(targetA, "cache.txt"), "x");
31
+ fs.writeFileSync(path.join(targetB, "dep.txt"), "x");
32
+
33
+ const result = runClean({
34
+ rootDir: tempRoot,
35
+ quiet: true,
36
+ targets: ["web/.next", "web/node_modules"],
37
+ });
38
+
39
+ assert.strictEqual(result.removedCount, 2);
40
+ assert.ok(!fs.existsSync(targetA));
41
+ assert.ok(!fs.existsSync(targetB));
42
+ });
43
+
44
+ test("runClean dry-run should not remove directories", () => {
45
+ const target = path.join(tempRoot, "web", ".next");
46
+ fs.mkdirSync(target, { recursive: true });
47
+
48
+ const result = runClean({
49
+ rootDir: tempRoot,
50
+ dryRun: true,
51
+ quiet: true,
52
+ targets: ["web/.next"],
53
+ });
54
+
55
+ assert.strictEqual(result.wouldRemoveCount, 1);
56
+ assert.ok(fs.existsSync(target));
57
+ });
58
+
59
+ test("runClean should skip missing paths", () => {
60
+ const result = runClean({
61
+ rootDir: tempRoot,
62
+ quiet: true,
63
+ targets: ["web/.next"],
64
+ });
65
+
66
+ assert.strictEqual(result.skippedCount, 1);
67
+ assert.strictEqual(result.results[0].reason, "not_found");
68
+ });
69
+
70
+ test("package scripts should keep scoped test and clean commands", () => {
71
+ const packageJsonPath = path.resolve(__dirname, "..", "package.json");
72
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
73
+
74
+ assert.strictEqual(packageJson.scripts.test, "node --test \"tests/**/*.test.js\"");
75
+ assert.strictEqual(packageJson.scripts.clean, "node scripts/clean.js");
76
+ });
77
+ });
@@ -0,0 +1,479 @@
1
+ const { test, describe, beforeEach, afterEach } = require("node:test");
2
+ const assert = require("node:assert");
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+ const { spawnSync } = require("node:child_process");
7
+
8
+ const REPO_ROOT = path.resolve(__dirname, "..");
9
+ const CLI_PATH = path.join(REPO_ROOT, "bin", "ag-kit.js");
10
+
11
+ function runCli(args, options = {}) {
12
+ const env = {
13
+ ...process.env,
14
+ AG_KIT_SKIP_UPSTREAM_CHECK: "1",
15
+ ...options.env,
16
+ };
17
+
18
+ return spawnSync(process.execPath, [CLI_PATH, ...args], {
19
+ cwd: options.cwd || REPO_ROOT,
20
+ env,
21
+ encoding: "utf8",
22
+ });
23
+ }
24
+
25
+ describe("CLI Smoke", () => {
26
+ let tempDir;
27
+ let workspaceDir;
28
+ let indexPath;
29
+
30
+ beforeEach(() => {
31
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-cli-test-"));
32
+ workspaceDir = path.join(tempDir, "workspace");
33
+ indexPath = path.join(tempDir, "workspaces.json");
34
+ fs.mkdirSync(workspaceDir, { recursive: true });
35
+ });
36
+
37
+ afterEach(() => {
38
+ fs.rmSync(tempDir, { recursive: true, force: true });
39
+ });
40
+
41
+ test("doctor should work after codex init", () => {
42
+ const initResult = runCli(
43
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
44
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
45
+ );
46
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
47
+
48
+ const doctorResult = runCli(
49
+ ["doctor", "--target", "codex", "--path", workspaceDir, "--quiet"],
50
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
51
+ );
52
+ assert.strictEqual(doctorResult.status, 0, doctorResult.stderr || doctorResult.stdout);
53
+ });
54
+
55
+ test("doctor --quiet should not print diagnostic details", () => {
56
+ const initResult = runCli(
57
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
58
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
59
+ );
60
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
61
+
62
+ const doctorResult = runCli(
63
+ ["doctor", "--target", "codex", "--path", workspaceDir, "--quiet"],
64
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
65
+ );
66
+ assert.strictEqual(doctorResult.status, 0, doctorResult.stderr || doctorResult.stdout);
67
+ assert.strictEqual((doctorResult.stdout || "").trim(), "");
68
+ });
69
+
70
+ test("doctor --fix should remove stale .codex directory", () => {
71
+ const initResult = runCli(
72
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
73
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
74
+ );
75
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
76
+
77
+ fs.mkdirSync(path.join(workspaceDir, ".codex"), { recursive: true });
78
+ fs.writeFileSync(path.join(workspaceDir, ".codex", "legacy.txt"), "legacy");
79
+ assert.ok(fs.existsSync(path.join(workspaceDir, ".codex")));
80
+
81
+ const doctorFixResult = runCli(
82
+ ["doctor", "--target", "codex", "--path", workspaceDir, "--fix", "--quiet"],
83
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
84
+ );
85
+ assert.strictEqual(doctorFixResult.status, 0, doctorFixResult.stderr || doctorFixResult.stdout);
86
+ assert.ok(!fs.existsSync(path.join(workspaceDir, ".codex")));
87
+ });
88
+
89
+ test("init should not index temporary workspace by default", () => {
90
+ const initResult = runCli(
91
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
92
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
93
+ );
94
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
95
+
96
+ if (!fs.existsSync(indexPath)) {
97
+ return;
98
+ }
99
+
100
+ const indexData = JSON.parse(fs.readFileSync(indexPath, "utf8"));
101
+ const hasWorkspace = (indexData.workspaces || []).some((item) => item.path === workspaceDir);
102
+ assert.ok(!hasWorkspace, "temp workspace should not be persisted into global index");
103
+ });
104
+
105
+ test("codex init should create .agents managed directory", () => {
106
+ const initResult = runCli(
107
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
108
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
109
+ );
110
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
111
+ assert.ok(fs.existsSync(path.join(workspaceDir, ".agents")));
112
+ assert.ok(!fs.existsSync(path.join(workspaceDir, ".codex")));
113
+ });
114
+
115
+ test("codex update should remove pre-existing legacy .codex directory", () => {
116
+ const initResult = runCli(
117
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
118
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
119
+ );
120
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
121
+ assert.ok(fs.existsSync(path.join(workspaceDir, ".agents")));
122
+ fs.mkdirSync(path.join(workspaceDir, ".codex"), { recursive: true });
123
+ fs.writeFileSync(path.join(workspaceDir, ".codex", "legacy.txt"), "legacy");
124
+ assert.ok(fs.existsSync(path.join(workspaceDir, ".codex")));
125
+
126
+ const updateResult = runCli(
127
+ ["update", "--target", "codex", "--path", workspaceDir, "--quiet"],
128
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
129
+ );
130
+ assert.strictEqual(updateResult.status, 0, updateResult.stderr || updateResult.stdout);
131
+ assert.ok(!fs.existsSync(path.join(workspaceDir, ".codex")));
132
+ assert.ok(fs.existsSync(path.join(workspaceDir, ".agents")));
133
+ });
134
+
135
+ test("update-all should auto-clean temp workspace records from index", () => {
136
+ const now = new Date().toISOString();
137
+ const tempWorkspacePath = path.join(os.tmpdir(), "ag-kit-indexed-temp", "workspace");
138
+ const seedIndex = {
139
+ version: 2,
140
+ updatedAt: now,
141
+ workspaces: [
142
+ {
143
+ path: tempWorkspacePath,
144
+ targets: {
145
+ gemini: {
146
+ version: "2.0.1",
147
+ installedAt: now,
148
+ updatedAt: now,
149
+ },
150
+ },
151
+ },
152
+ ],
153
+ excludedPaths: [],
154
+ };
155
+ fs.writeFileSync(indexPath, `${JSON.stringify(seedIndex, null, 2)}\n`, "utf8");
156
+
157
+ const result = runCli(["update-all", "--quiet"], {
158
+ env: { AG_KIT_INDEX_PATH: indexPath },
159
+ });
160
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
161
+
162
+ const indexData = JSON.parse(fs.readFileSync(indexPath, "utf8"));
163
+ const hasTempWorkspace = (indexData.workspaces || []).some((item) => item.path === tempWorkspacePath);
164
+ assert.ok(!hasTempWorkspace, "temp workspace record should be removed during update-all");
165
+ });
166
+
167
+ test("update-all should normalize legacy full target records in v2 index", () => {
168
+ const now = new Date().toISOString();
169
+ const persistentRoot = fs.mkdtempSync(path.join(REPO_ROOT, ".temp-ag-kit-legacy-index-"));
170
+ const persistentWorkspace = path.join(persistentRoot, "workspace");
171
+ fs.mkdirSync(persistentWorkspace, { recursive: true });
172
+
173
+ try {
174
+ const initResult = runCli(
175
+ ["init", "--target", "codex", "--path", persistentWorkspace, "--quiet"],
176
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
177
+ );
178
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
179
+
180
+ const seedIndex = {
181
+ version: 2,
182
+ updatedAt: now,
183
+ workspaces: [
184
+ {
185
+ path: persistentWorkspace,
186
+ targets: {
187
+ full: {
188
+ version: "1.9.0",
189
+ installedAt: now,
190
+ updatedAt: now,
191
+ },
192
+ },
193
+ },
194
+ ],
195
+ excludedPaths: [],
196
+ };
197
+ fs.writeFileSync(indexPath, `${JSON.stringify(seedIndex, null, 2)}\n`, "utf8");
198
+
199
+ const result = runCli(["update-all", "--quiet"], {
200
+ env: { AG_KIT_INDEX_PATH: indexPath },
201
+ });
202
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
203
+
204
+ const normalized = JSON.parse(fs.readFileSync(indexPath, "utf8"));
205
+ assert.ok(!("full" in normalized.workspaces[0].targets), "legacy full target should be removed from rewritten index");
206
+ assert.ok("codex" in normalized.workspaces[0].targets, "codex target should be preserved after update-all");
207
+ } finally {
208
+ fs.rmSync(persistentRoot, { recursive: true, force: true });
209
+ }
210
+ });
211
+
212
+ test("update-all should auto-clean macOS /private/var temp alias records", () => {
213
+ if (process.platform !== "darwin") {
214
+ return;
215
+ }
216
+
217
+ const tmpRoot = os.tmpdir();
218
+ let aliasTempPath = "";
219
+ if (tmpRoot.startsWith("/var/")) {
220
+ aliasTempPath = `/private${tmpRoot}/ag-kit-indexed-temp/workspace`;
221
+ } else if (tmpRoot.startsWith("/private/var/")) {
222
+ aliasTempPath = `${tmpRoot.replace(/^\/private/, "")}/ag-kit-indexed-temp/workspace`;
223
+ } else {
224
+ return;
225
+ }
226
+
227
+ const now = new Date().toISOString();
228
+ const seedIndex = {
229
+ version: 2,
230
+ updatedAt: now,
231
+ workspaces: [
232
+ {
233
+ path: aliasTempPath,
234
+ targets: {
235
+ gemini: {
236
+ version: "2.0.1",
237
+ installedAt: now,
238
+ updatedAt: now,
239
+ },
240
+ },
241
+ },
242
+ ],
243
+ excludedPaths: [],
244
+ };
245
+ fs.writeFileSync(indexPath, `${JSON.stringify(seedIndex, null, 2)}\n`, "utf8");
246
+
247
+ const result = runCli(["update-all", "--quiet"], {
248
+ env: { AG_KIT_INDEX_PATH: indexPath },
249
+ });
250
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
251
+
252
+ const indexData = JSON.parse(fs.readFileSync(indexPath, "utf8"));
253
+ const hasAliasTempWorkspace = (indexData.workspaces || []).some((item) => item.path === aliasTempPath);
254
+ assert.ok(!hasAliasTempWorkspace, "macOS alias temp path should be removed during update-all");
255
+ });
256
+
257
+ test("init in non-interactive mode should require explicit target", () => {
258
+ const result = runCli(
259
+ ["init", "--non-interactive", "--path", workspaceDir, "--quiet"],
260
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
261
+ );
262
+ assert.notStrictEqual(result.status, 0);
263
+ assert.match(result.stderr || result.stdout, /非交互模式下必须通过 --target 或 --targets 指定目标/);
264
+ });
265
+
266
+ test("update should run in gemini dry-run mode", () => {
267
+ fs.mkdirSync(path.join(workspaceDir, ".agent"), { recursive: true });
268
+
269
+ const result = runCli(
270
+ ["update", "--target", "gemini", "--path", workspaceDir, "--dry-run", "--quiet"],
271
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
272
+ );
273
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
274
+ });
275
+
276
+ test("status --quiet should report missing with exit code 2 when nothing is installed", () => {
277
+ const result = runCli(
278
+ ["status", "--path", workspaceDir, "--quiet"],
279
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
280
+ );
281
+ assert.strictEqual(result.status, 2);
282
+ assert.strictEqual((result.stdout || "").trim(), "missing");
283
+ });
284
+
285
+ test("status --quiet should report broken when installation is incomplete", () => {
286
+ fs.mkdirSync(path.join(workspaceDir, ".agent"), { recursive: true });
287
+
288
+ const result = runCli(
289
+ ["status", "--path", workspaceDir, "--quiet"],
290
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
291
+ );
292
+ assert.strictEqual(result.status, 1);
293
+ assert.strictEqual((result.stdout || "").trim(), "broken");
294
+ });
295
+
296
+ test("status --quiet should report broken for codex drift", () => {
297
+ const initResult = runCli(
298
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
299
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
300
+ );
301
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
302
+
303
+ fs.writeFileSync(path.join(workspaceDir, ".agents", "AGENTS.md"), "drifted", "utf8");
304
+
305
+ const result = runCli(
306
+ ["status", "--path", workspaceDir, "--quiet"],
307
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
308
+ );
309
+ assert.strictEqual(result.status, 1);
310
+ assert.strictEqual((result.stdout || "").trim(), "broken");
311
+ });
312
+
313
+ test("status --quiet should report installed when installation is healthy", () => {
314
+ const initResult = runCli(
315
+ ["init", "--target", "codex", "--path", workspaceDir, "--quiet"],
316
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
317
+ );
318
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
319
+
320
+ const result = runCli(
321
+ ["status", "--path", workspaceDir, "--quiet"],
322
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
323
+ );
324
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
325
+ assert.strictEqual((result.stdout || "").trim(), "installed");
326
+ });
327
+
328
+ test("status should reject unsupported --no-index option", () => {
329
+ fs.mkdirSync(path.join(workspaceDir, ".agent"), { recursive: true });
330
+
331
+ const result = runCli(
332
+ ["status", "--path", workspaceDir, "--no-index"],
333
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
334
+ );
335
+ assert.notStrictEqual(result.status, 0);
336
+ assert.match(result.stderr || result.stdout, /命令 status 不支持参数: --no-index/);
337
+ });
338
+
339
+ test("exclude list should reject unsupported --path option", () => {
340
+ const result = runCli(
341
+ ["exclude", "list", "--path", workspaceDir],
342
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
343
+ );
344
+ assert.notStrictEqual(result.status, 0);
345
+ assert.match(result.stderr || result.stdout, /命令 exclude list 不支持参数: --path/);
346
+ });
347
+
348
+ test("update-all should run in dry-run mode with indexed workspace", () => {
349
+ fs.mkdirSync(path.join(workspaceDir, ".agent"), { recursive: true });
350
+ const now = new Date().toISOString();
351
+ const seedIndex = {
352
+ version: 2,
353
+ updatedAt: now,
354
+ workspaces: [
355
+ {
356
+ path: workspaceDir,
357
+ targets: {
358
+ gemini: {
359
+ version: "2.0.1",
360
+ installedAt: now,
361
+ updatedAt: now,
362
+ },
363
+ },
364
+ },
365
+ ],
366
+ excludedPaths: [],
367
+ };
368
+ fs.writeFileSync(indexPath, `${JSON.stringify(seedIndex, null, 2)}\n`, "utf8");
369
+
370
+ const result = runCli(["update-all", "--dry-run", "--quiet"], {
371
+ env: { AG_KIT_INDEX_PATH: indexPath },
372
+ });
373
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
374
+ });
375
+
376
+ test("update-all should update installed target even when index target list is stale", () => {
377
+ const localWorkspace = fs.mkdtempSync(path.join(REPO_ROOT, ".tmp-ag-kit-update-all-stale-target-"));
378
+ try {
379
+ fs.mkdirSync(path.join(localWorkspace, ".codex"), { recursive: true });
380
+ fs.writeFileSync(
381
+ path.join(localWorkspace, ".codex", "manifest.json"),
382
+ JSON.stringify({ version: 2, target: "codex", files: {} }),
383
+ "utf8",
384
+ );
385
+
386
+ const now = new Date().toISOString();
387
+ const seedIndex = {
388
+ version: 2,
389
+ updatedAt: now,
390
+ workspaces: [
391
+ {
392
+ path: localWorkspace,
393
+ targets: {
394
+ gemini: {
395
+ version: "2.0.1",
396
+ installedAt: now,
397
+ updatedAt: now,
398
+ },
399
+ },
400
+ },
401
+ ],
402
+ excludedPaths: [],
403
+ };
404
+ fs.writeFileSync(indexPath, `${JSON.stringify(seedIndex, null, 2)}\n`, "utf8");
405
+
406
+ const result = runCli(["update-all", "--targets", "codex", "--quiet"], {
407
+ env: { AG_KIT_INDEX_PATH: indexPath },
408
+ });
409
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
410
+
411
+ const indexData = JSON.parse(fs.readFileSync(indexPath, "utf8"));
412
+ const record = (indexData.workspaces || []).find((item) => item.path === localWorkspace);
413
+ assert.ok(record, "workspace should remain in index");
414
+ assert.ok(record.targets && record.targets.codex, "codex target should be refreshed into index");
415
+ } finally {
416
+ fs.rmSync(localWorkspace, { recursive: true, force: true });
417
+ }
418
+ });
419
+
420
+ test("init should respect --no-index on non-temp workspace", () => {
421
+ const localWorkspace = fs.mkdtempSync(path.join(REPO_ROOT, ".tmp-ag-kit-no-index-"));
422
+ try {
423
+ const initResult = runCli(
424
+ ["init", "--target", "codex", "--path", localWorkspace, "--no-index", "--quiet"],
425
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
426
+ );
427
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
428
+
429
+ if (!fs.existsSync(indexPath)) {
430
+ return;
431
+ }
432
+
433
+ const indexData = JSON.parse(fs.readFileSync(indexPath, "utf8"));
434
+ const hasWorkspace = (indexData.workspaces || []).some((item) => item.path === localWorkspace);
435
+ assert.ok(!hasWorkspace, "--no-index should skip workspace registration");
436
+
437
+ const updateResult = runCli(
438
+ ["update", "--quiet"],
439
+ { cwd: localWorkspace, env: { AG_KIT_INDEX_PATH: indexPath } },
440
+ );
441
+ assert.strictEqual(updateResult.status, 0, updateResult.stderr || updateResult.stdout);
442
+
443
+ const indexAfterUpdate = JSON.parse(fs.readFileSync(indexPath, "utf8"));
444
+ const hasWorkspaceAfterUpdate = (indexAfterUpdate.workspaces || []).some((item) => item.path === localWorkspace);
445
+ assert.ok(hasWorkspaceAfterUpdate, "local update without --no-index should re-register workspace");
446
+ } finally {
447
+ fs.rmSync(localWorkspace, { recursive: true, force: true });
448
+ }
449
+ });
450
+
451
+ test("init should skip indexing toolkit source-like workspace by package name", () => {
452
+ const toolkitWorkspace = fs.mkdtempSync(path.join(REPO_ROOT, ".tmp-ag-kit-toolkit-source-"));
453
+ try {
454
+ fs.mkdirSync(path.join(toolkitWorkspace, "bin"), { recursive: true });
455
+ fs.writeFileSync(
456
+ path.join(toolkitWorkspace, "package.json"),
457
+ JSON.stringify({ name: "@mison/ag-kit-cn", version: "2.0.1" }, null, 2),
458
+ "utf8",
459
+ );
460
+ fs.writeFileSync(path.join(toolkitWorkspace, "bin", "ag-kit.js"), "#!/usr/bin/env node\n", "utf8");
461
+
462
+ const initResult = runCli(
463
+ ["init", "--target", "gemini", "--path", toolkitWorkspace, "--quiet"],
464
+ { env: { AG_KIT_INDEX_PATH: indexPath } },
465
+ );
466
+ assert.strictEqual(initResult.status, 0, initResult.stderr || initResult.stdout);
467
+
468
+ if (!fs.existsSync(indexPath)) {
469
+ return;
470
+ }
471
+
472
+ const indexData = JSON.parse(fs.readFileSync(indexPath, "utf8"));
473
+ const hasWorkspace = (indexData.workspaces || []).some((item) => item.path === toolkitWorkspace);
474
+ assert.ok(!hasWorkspace, "toolkit source-like workspace should be excluded from index");
475
+ } finally {
476
+ fs.rmSync(toolkitWorkspace, { recursive: true, force: true });
477
+ }
478
+ });
479
+ });