@mison/ag-kit-cn 3.0.0-beta.0 → 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 (254) hide show
  1. package/CHANGELOG.md +13 -11
  2. package/README.md +70 -35
  3. package/bin/adapters/codex.js +105 -372
  4. package/bin/adapters/gemini.js +133 -13
  5. package/bin/ag-kit.js +557 -155
  6. package/bin/core/builder.js +1 -1
  7. package/bin/core/generator.js +4 -4
  8. package/bin/core/resource-loader.js +2 -2
  9. package/bin/interactive.js +36 -81
  10. package/bin/utils/manifest.js +3 -3
  11. package/bin/utils.js +8 -3
  12. package/docs/PLAN.md +39 -0
  13. package/docs/TECH.md +136 -0
  14. package/package.json +11 -10
  15. package/scripts/ci-verify.js +95 -0
  16. package/scripts/clean.js +123 -0
  17. package/scripts/health-check.js +132 -0
  18. package/scripts/health-check.sh +6 -0
  19. package/tests/atomic-writer.test.js +47 -0
  20. package/tests/clean-script.test.js +77 -0
  21. package/tests/cli-smoke.test.js +479 -0
  22. package/tests/codex-adapter.test.js +132 -0
  23. package/tests/doctor.test.js +94 -0
  24. package/tests/gemini-adapter.test.js +30 -0
  25. package/tests/generator.test.js +48 -0
  26. package/tests/git-helper.test.js +53 -0
  27. package/tests/global-sync.test.js +133 -0
  28. package/tests/health-check-script.test.js +30 -0
  29. package/tests/managed-block.test.js +41 -0
  30. package/tests/manifest.test.js +97 -0
  31. package/tests/package-tarball.test.js +27 -0
  32. package/tests/phase-c.test.js +107 -0
  33. package/tests/standards-compliance.test.js +266 -0
  34. package/tests/transformer.test.js +74 -0
  35. package/docs/codex-rules-template.md +0 -36
  36. package/docs/mapping-spec.md +0 -62
  37. package/docs/multi-target-adapter.md +0 -46
  38. package/docs/official/README.md +0 -45
  39. package/docs/official/antigravity/agent-modes-settings.md +0 -64
  40. package/docs/official/antigravity/rules-workflows.md +0 -96
  41. package/docs/official/antigravity/skills.md +0 -147
  42. package/docs/official/codex/agents-md.md +0 -119
  43. package/docs/official/codex/config-advanced.md +0 -358
  44. package/docs/official/codex/config-basic.md +0 -141
  45. package/docs/official/codex/config-reference.md +0 -223
  46. package/docs/official/codex/config-sample.md +0 -216
  47. package/docs/official/codex/mcp.md +0 -107
  48. package/docs/official/codex/rules.md +0 -79
  49. package/docs/official/codex/skills.md +0 -114
  50. package/docs/official/mechanism-compat-audit-2026-03-04.md +0 -36
  51. package/docs/official/rules-baseline.md +0 -67
  52. package/docs/official/sources-index.md +0 -79
  53. package/docs/operations.md +0 -75
  54. package/docs/terminology-style-guide.md +0 -69
  55. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/charts.csv +0 -0
  56. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/colors.csv +0 -0
  57. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/icons.csv +0 -0
  58. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/landing.csv +0 -0
  59. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/products.csv +0 -0
  60. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/prompts.csv +0 -0
  61. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/react-performance.csv +0 -0
  62. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/flutter.csv +0 -0
  63. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -0
  64. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -0
  65. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/nextjs.csv +0 -0
  66. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -0
  67. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -0
  68. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/react-native.csv +0 -0
  69. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/react.csv +0 -0
  70. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/shadcn.csv +0 -0
  71. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/svelte.csv +0 -0
  72. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/swiftui.csv +0 -0
  73. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/stacks/vue.csv +0 -0
  74. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/styles.csv +0 -0
  75. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/typography.csv +0 -0
  76. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/ui-reasoning.csv +0 -0
  77. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/ux-guidelines.csv +0 -0
  78. /package/{.agent → .agents}/.shared/ui-ux-pro-max/data/web-interface.csv +0 -0
  79. /package/{.agent → .agents}/.shared/ui-ux-pro-max/scripts/core.py +0 -0
  80. /package/{.agent → .agents}/.shared/ui-ux-pro-max/scripts/design_system.py +0 -0
  81. /package/{.agent → .agents}/.shared/ui-ux-pro-max/scripts/search.py +0 -0
  82. /package/{.agent → .agents}/ARCHITECTURE.md +0 -0
  83. /package/{.agent → .agents}/agents/backend-specialist.md +0 -0
  84. /package/{.agent → .agents}/agents/code-archaeologist.md +0 -0
  85. /package/{.agent → .agents}/agents/database-architect.md +0 -0
  86. /package/{.agent → .agents}/agents/debugger.md +0 -0
  87. /package/{.agent → .agents}/agents/devops-engineer.md +0 -0
  88. /package/{.agent → .agents}/agents/documentation-writer.md +0 -0
  89. /package/{.agent → .agents}/agents/explorer-agent.md +0 -0
  90. /package/{.agent → .agents}/agents/frontend-specialist.md +0 -0
  91. /package/{.agent → .agents}/agents/game-developer.md +0 -0
  92. /package/{.agent → .agents}/agents/mobile-developer.md +0 -0
  93. /package/{.agent → .agents}/agents/orchestrator.md +0 -0
  94. /package/{.agent → .agents}/agents/penetration-tester.md +0 -0
  95. /package/{.agent → .agents}/agents/performance-optimizer.md +0 -0
  96. /package/{.agent → .agents}/agents/product-manager.md +0 -0
  97. /package/{.agent → .agents}/agents/product-owner.md +0 -0
  98. /package/{.agent → .agents}/agents/project-planner.md +0 -0
  99. /package/{.agent → .agents}/agents/qa-automation-engineer.md +0 -0
  100. /package/{.agent → .agents}/agents/security-auditor.md +0 -0
  101. /package/{.agent → .agents}/agents/seo-specialist.md +0 -0
  102. /package/{.agent → .agents}/agents/test-engineer.md +0 -0
  103. /package/{.agent → .agents}/mcp_config.json +0 -0
  104. /package/{.agent → .agents}/rules/GEMINI.md +0 -0
  105. /package/{.agent → .agents}/scripts/auto_preview.py +0 -0
  106. /package/{.agent → .agents}/scripts/checklist.py +0 -0
  107. /package/{.agent → .agents}/scripts/session_manager.py +0 -0
  108. /package/{.agent → .agents}/scripts/verify_all.py +0 -0
  109. /package/{.agent → .agents}/skills/api-patterns/SKILL.md +0 -0
  110. /package/{.agent → .agents}/skills/api-patterns/api-style.md +0 -0
  111. /package/{.agent → .agents}/skills/api-patterns/auth.md +0 -0
  112. /package/{.agent → .agents}/skills/api-patterns/documentation.md +0 -0
  113. /package/{.agent → .agents}/skills/api-patterns/graphql.md +0 -0
  114. /package/{.agent → .agents}/skills/api-patterns/rate-limiting.md +0 -0
  115. /package/{.agent → .agents}/skills/api-patterns/response.md +0 -0
  116. /package/{.agent → .agents}/skills/api-patterns/rest.md +0 -0
  117. /package/{.agent → .agents}/skills/api-patterns/scripts/api_validator.py +0 -0
  118. /package/{.agent → .agents}/skills/api-patterns/security-testing.md +0 -0
  119. /package/{.agent → .agents}/skills/api-patterns/trpc.md +0 -0
  120. /package/{.agent → .agents}/skills/api-patterns/versioning.md +0 -0
  121. /package/{.agent → .agents}/skills/app-builder/SKILL.md +0 -0
  122. /package/{.agent → .agents}/skills/app-builder/agent-coordination.md +0 -0
  123. /package/{.agent → .agents}/skills/app-builder/feature-building.md +0 -0
  124. /package/{.agent → .agents}/skills/app-builder/project-detection.md +0 -0
  125. /package/{.agent → .agents}/skills/app-builder/scaffolding.md +0 -0
  126. /package/{.agent → .agents}/skills/app-builder/tech-stack.md +0 -0
  127. /package/{.agent → .agents}/skills/app-builder/templates/SKILL.md +0 -0
  128. /package/{.agent → .agents}/skills/app-builder/templates/astro-static/TEMPLATE.md +0 -0
  129. /package/{.agent → .agents}/skills/app-builder/templates/chrome-extension/TEMPLATE.md +0 -0
  130. /package/{.agent → .agents}/skills/app-builder/templates/cli-tool/TEMPLATE.md +0 -0
  131. /package/{.agent → .agents}/skills/app-builder/templates/electron-desktop/TEMPLATE.md +0 -0
  132. /package/{.agent → .agents}/skills/app-builder/templates/express-api/TEMPLATE.md +0 -0
  133. /package/{.agent → .agents}/skills/app-builder/templates/flutter-app/TEMPLATE.md +0 -0
  134. /package/{.agent → .agents}/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +0 -0
  135. /package/{.agent → .agents}/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +0 -0
  136. /package/{.agent → .agents}/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +0 -0
  137. /package/{.agent → .agents}/skills/app-builder/templates/nextjs-static/TEMPLATE.md +0 -0
  138. /package/{.agent → .agents}/skills/app-builder/templates/nuxt-app/TEMPLATE.md +0 -0
  139. /package/{.agent → .agents}/skills/app-builder/templates/python-fastapi/TEMPLATE.md +0 -0
  140. /package/{.agent → .agents}/skills/app-builder/templates/react-native-app/TEMPLATE.md +0 -0
  141. /package/{.agent → .agents}/skills/architecture/SKILL.md +0 -0
  142. /package/{.agent → .agents}/skills/architecture/context-discovery.md +0 -0
  143. /package/{.agent → .agents}/skills/architecture/examples.md +0 -0
  144. /package/{.agent → .agents}/skills/architecture/pattern-selection.md +0 -0
  145. /package/{.agent → .agents}/skills/architecture/patterns-reference.md +0 -0
  146. /package/{.agent → .agents}/skills/architecture/trade-off-analysis.md +0 -0
  147. /package/{.agent → .agents}/skills/bash-linux/SKILL.md +0 -0
  148. /package/{.agent → .agents}/skills/behavioral-modes/SKILL.md +0 -0
  149. /package/{.agent → .agents}/skills/brainstorming/SKILL.md +0 -0
  150. /package/{.agent → .agents}/skills/brainstorming/dynamic-questioning.md +0 -0
  151. /package/{.agent → .agents}/skills/clean-code/SKILL.md +0 -0
  152. /package/{.agent → .agents}/skills/code-review-checklist/SKILL.md +0 -0
  153. /package/{.agent → .agents}/skills/database-design/SKILL.md +0 -0
  154. /package/{.agent → .agents}/skills/database-design/database-selection.md +0 -0
  155. /package/{.agent → .agents}/skills/database-design/indexing.md +0 -0
  156. /package/{.agent → .agents}/skills/database-design/migrations.md +0 -0
  157. /package/{.agent → .agents}/skills/database-design/optimization.md +0 -0
  158. /package/{.agent → .agents}/skills/database-design/orm-selection.md +0 -0
  159. /package/{.agent → .agents}/skills/database-design/schema-design.md +0 -0
  160. /package/{.agent → .agents}/skills/database-design/scripts/schema_validator.py +0 -0
  161. /package/{.agent → .agents}/skills/deployment-procedures/SKILL.md +0 -0
  162. /package/{.agent → .agents}/skills/doc.md +0 -0
  163. /package/{.agent → .agents}/skills/documentation-templates/SKILL.md +0 -0
  164. /package/{.agent → .agents}/skills/frontend-design/SKILL.md +0 -0
  165. /package/{.agent → .agents}/skills/frontend-design/animation-guide.md +0 -0
  166. /package/{.agent → .agents}/skills/frontend-design/color-system.md +0 -0
  167. /package/{.agent → .agents}/skills/frontend-design/decision-trees.md +0 -0
  168. /package/{.agent → .agents}/skills/frontend-design/motion-graphics.md +0 -0
  169. /package/{.agent → .agents}/skills/frontend-design/scripts/accessibility_checker.py +0 -0
  170. /package/{.agent → .agents}/skills/frontend-design/scripts/ux_audit.py +0 -0
  171. /package/{.agent → .agents}/skills/frontend-design/typography-system.md +0 -0
  172. /package/{.agent → .agents}/skills/frontend-design/ux-psychology.md +0 -0
  173. /package/{.agent → .agents}/skills/frontend-design/visual-effects.md +0 -0
  174. /package/{.agent → .agents}/skills/game-development/2d-games/SKILL.md +0 -0
  175. /package/{.agent → .agents}/skills/game-development/3d-games/SKILL.md +0 -0
  176. /package/{.agent → .agents}/skills/game-development/SKILL.md +0 -0
  177. /package/{.agent → .agents}/skills/game-development/game-art/SKILL.md +0 -0
  178. /package/{.agent → .agents}/skills/game-development/game-audio/SKILL.md +0 -0
  179. /package/{.agent → .agents}/skills/game-development/game-design/SKILL.md +0 -0
  180. /package/{.agent → .agents}/skills/game-development/mobile-games/SKILL.md +0 -0
  181. /package/{.agent → .agents}/skills/game-development/multiplayer/SKILL.md +0 -0
  182. /package/{.agent → .agents}/skills/game-development/pc-games/SKILL.md +0 -0
  183. /package/{.agent → .agents}/skills/game-development/vr-ar/SKILL.md +0 -0
  184. /package/{.agent → .agents}/skills/game-development/web-games/SKILL.md +0 -0
  185. /package/{.agent → .agents}/skills/geo-fundamentals/SKILL.md +0 -0
  186. /package/{.agent → .agents}/skills/geo-fundamentals/scripts/geo_checker.py +0 -0
  187. /package/{.agent → .agents}/skills/i18n-localization/SKILL.md +0 -0
  188. /package/{.agent → .agents}/skills/i18n-localization/scripts/i18n_checker.py +0 -0
  189. /package/{.agent → .agents}/skills/intelligent-routing/SKILL.md +0 -0
  190. /package/{.agent → .agents}/skills/lint-and-validate/SKILL.md +0 -0
  191. /package/{.agent → .agents}/skills/lint-and-validate/scripts/lint_runner.py +0 -0
  192. /package/{.agent → .agents}/skills/lint-and-validate/scripts/type_coverage.py +0 -0
  193. /package/{.agent → .agents}/skills/mcp-builder/SKILL.md +0 -0
  194. /package/{.agent → .agents}/skills/mobile-design/SKILL.md +0 -0
  195. /package/{.agent → .agents}/skills/mobile-design/decision-trees.md +0 -0
  196. /package/{.agent → .agents}/skills/mobile-design/mobile-backend.md +0 -0
  197. /package/{.agent → .agents}/skills/mobile-design/mobile-color-system.md +0 -0
  198. /package/{.agent → .agents}/skills/mobile-design/mobile-debugging.md +0 -0
  199. /package/{.agent → .agents}/skills/mobile-design/mobile-design-thinking.md +0 -0
  200. /package/{.agent → .agents}/skills/mobile-design/mobile-navigation.md +0 -0
  201. /package/{.agent → .agents}/skills/mobile-design/mobile-performance.md +0 -0
  202. /package/{.agent → .agents}/skills/mobile-design/mobile-testing.md +0 -0
  203. /package/{.agent → .agents}/skills/mobile-design/mobile-typography.md +0 -0
  204. /package/{.agent → .agents}/skills/mobile-design/platform-android.md +0 -0
  205. /package/{.agent → .agents}/skills/mobile-design/platform-ios.md +0 -0
  206. /package/{.agent → .agents}/skills/mobile-design/scripts/mobile_audit.py +0 -0
  207. /package/{.agent → .agents}/skills/mobile-design/touch-psychology.md +0 -0
  208. /package/{.agent → .agents}/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +0 -0
  209. /package/{.agent → .agents}/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +0 -0
  210. /package/{.agent → .agents}/skills/nextjs-react-expert/3-server-server-side-performance.md +0 -0
  211. /package/{.agent → .agents}/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +0 -0
  212. /package/{.agent → .agents}/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +0 -0
  213. /package/{.agent → .agents}/skills/nextjs-react-expert/6-rendering-rendering-performance.md +0 -0
  214. /package/{.agent → .agents}/skills/nextjs-react-expert/7-js-javascript-performance.md +0 -0
  215. /package/{.agent → .agents}/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +0 -0
  216. /package/{.agent → .agents}/skills/nextjs-react-expert/SKILL.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/parallel-agents/SKILL.md +0 -0
  221. /package/{.agent → .agents}/skills/performance-profiling/SKILL.md +0 -0
  222. /package/{.agent → .agents}/skills/performance-profiling/scripts/lighthouse_audit.py +0 -0
  223. /package/{.agent → .agents}/skills/plan-writing/SKILL.md +0 -0
  224. /package/{.agent → .agents}/skills/powershell-windows/SKILL.md +0 -0
  225. /package/{.agent → .agents}/skills/python-patterns/SKILL.md +0 -0
  226. /package/{.agent → .agents}/skills/red-team-tactics/SKILL.md +0 -0
  227. /package/{.agent → .agents}/skills/refactoring-patterns/SKILL.md +0 -0
  228. /package/{.agent → .agents}/skills/rust-pro/SKILL.md +0 -0
  229. /package/{.agent → .agents}/skills/seo-fundamentals/SKILL.md +0 -0
  230. /package/{.agent → .agents}/skills/seo-fundamentals/scripts/seo_checker.py +0 -0
  231. /package/{.agent → .agents}/skills/server-management/SKILL.md +0 -0
  232. /package/{.agent → .agents}/skills/systematic-debugging/SKILL.md +0 -0
  233. /package/{.agent → .agents}/skills/tailwind-patterns/SKILL.md +0 -0
  234. /package/{.agent → .agents}/skills/tdd-workflow/SKILL.md +0 -0
  235. /package/{.agent → .agents}/skills/testing-patterns/SKILL.md +0 -0
  236. /package/{.agent → .agents}/skills/testing-patterns/scripts/test_runner.py +0 -0
  237. /package/{.agent → .agents}/skills/vulnerability-scanner/SKILL.md +0 -0
  238. /package/{.agent → .agents}/skills/vulnerability-scanner/checklists.md +0 -0
  239. /package/{.agent → .agents}/skills/vulnerability-scanner/scripts/security_scan.py +0 -0
  240. /package/{.agent → .agents}/skills/web-design-guidelines/SKILL.md +0 -0
  241. /package/{.agent → .agents}/skills/webapp-testing/SKILL.md +0 -0
  242. /package/{.agent → .agents}/skills/webapp-testing/scripts/playwright_runner.py +0 -0
  243. /package/{.agent → .agents}/workflows/brainstorm.md +0 -0
  244. /package/{.agent → .agents}/workflows/create.md +0 -0
  245. /package/{.agent → .agents}/workflows/debug.md +0 -0
  246. /package/{.agent → .agents}/workflows/deploy.md +0 -0
  247. /package/{.agent → .agents}/workflows/enhance.md +0 -0
  248. /package/{.agent → .agents}/workflows/orchestrate.md +0 -0
  249. /package/{.agent → .agents}/workflows/plan.md +0 -0
  250. /package/{.agent → .agents}/workflows/preview.md +0 -0
  251. /package/{.agent → .agents}/workflows/restore-localize-compat.md +0 -0
  252. /package/{.agent → .agents}/workflows/status.md +0 -0
  253. /package/{.agent → .agents}/workflows/test.md +0 -0
  254. /package/{.agent → .agents}/workflows/ui-ux-pro-max.md +0 -0
@@ -0,0 +1,94 @@
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 CodexAdapter = require('../bin/adapters/codex');
7
+ const GeminiAdapter = require('../bin/adapters/gemini');
8
+
9
+ describe('Doctor Command', () => {
10
+ let workDir;
11
+
12
+ beforeEach(() => {
13
+ workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'doctor-test-'));
14
+ // Setup mock environment
15
+ // Gemini
16
+ fs.mkdirSync(path.join(workDir, '.agent', 'agents'), { recursive: true });
17
+ fs.mkdirSync(path.join(workDir, '.agent', 'skills'), { recursive: true });
18
+ fs.mkdirSync(path.join(workDir, '.agent', 'rules'), { recursive: true });
19
+ fs.mkdirSync(path.join(workDir, '.agent', 'workflows'), { recursive: true });
20
+ fs.writeFileSync(path.join(workDir, '.agent', 'agents', 'orchestrator.md'), '# orchestrator');
21
+ fs.writeFileSync(path.join(workDir, '.agent', 'skills', 'doc.md'), '# skills');
22
+ fs.writeFileSync(path.join(workDir, '.agent', 'rules', 'GEMINI.md'), '# rules');
23
+ fs.writeFileSync(path.join(workDir, '.agent', 'workflows', 'create.md'), '# workflow');
24
+ // Codex
25
+ fs.mkdirSync(path.join(workDir, '.agents'));
26
+ fs.writeFileSync(path.join(workDir, '.agents', 'manifest.json'), JSON.stringify({ version: 2, target: 'codex', files: {} }));
27
+ });
28
+
29
+ afterEach(() => {
30
+ fs.rmSync(workDir, { recursive: true, force: true });
31
+ });
32
+
33
+ test('GeminiAdapter checkIntegrity ok', () => {
34
+ const adapter = new GeminiAdapter(workDir, { quiet: true });
35
+ const res = adapter.checkIntegrity();
36
+ assert.strictEqual(res.status, 'ok');
37
+ });
38
+
39
+ test('GeminiAdapter checkIntegrity missing', () => {
40
+ fs.rmSync(path.join(workDir, '.agent'), { recursive: true });
41
+ const adapter = new GeminiAdapter(workDir, { quiet: true });
42
+ const res = adapter.checkIntegrity();
43
+ assert.strictEqual(res.status, 'missing');
44
+ });
45
+
46
+ test('GeminiAdapter checkIntegrity should flag missing required files', () => {
47
+ fs.rmSync(path.join(workDir, '.agent', 'rules', 'GEMINI.md'));
48
+ const adapter = new GeminiAdapter(workDir, { quiet: true });
49
+ const res = adapter.checkIntegrity();
50
+ assert.strictEqual(res.status, 'broken');
51
+ assert.ok(res.issues.some((issue) => issue.includes('.agent/rules/GEMINI.md')));
52
+ });
53
+
54
+ test('CodexAdapter checkIntegrity ok', () => {
55
+ const adapter = new CodexAdapter(workDir, { quiet: true });
56
+ const res = adapter.checkIntegrity();
57
+ assert.strictEqual(res.status, 'ok');
58
+ });
59
+
60
+ test('CodexAdapter checkIntegrity missing', () => {
61
+ fs.rmSync(path.join(workDir, '.agents'), { recursive: true });
62
+ const adapter = new CodexAdapter(workDir, { quiet: true });
63
+ const res = adapter.checkIntegrity();
64
+ assert.strictEqual(res.status, 'missing');
65
+ });
66
+
67
+ test('CodexAdapter checkIntegrity should flag legacy .codex directory', () => {
68
+ fs.mkdirSync(path.join(workDir, '.codex'), { recursive: true });
69
+ const adapter = new CodexAdapter(workDir, { quiet: true });
70
+ const res = adapter.checkIntegrity();
71
+ assert.strictEqual(res.status, 'broken');
72
+ assert.ok(res.issues.some((issue) => issue.includes('.codex')));
73
+ });
74
+
75
+ test('CodexAdapter checkIntegrity should flag invalid manifest JSON', () => {
76
+ fs.writeFileSync(path.join(workDir, '.agents', 'manifest.json'), '{invalid-json');
77
+ const adapter = new CodexAdapter(workDir, { quiet: true });
78
+ const res = adapter.checkIntegrity();
79
+ assert.strictEqual(res.status, 'broken');
80
+ assert.ok(res.issues.some((issue) => issue.includes('invalid JSON')));
81
+ });
82
+
83
+ test('CodexAdapter fixIntegrity should migrate legacy .codex directory', () => {
84
+ fs.rmSync(path.join(workDir, '.agents'), { recursive: true, force: true });
85
+ fs.mkdirSync(path.join(workDir, '.codex'), { recursive: true });
86
+ fs.writeFileSync(path.join(workDir, '.codex', 'manifest.json'), JSON.stringify({ version: 2, target: 'codex', files: {} }));
87
+ fs.writeFileSync(path.join(workDir, '.codex', 'legacy.txt'), 'legacy');
88
+ const adapter = new CodexAdapter(workDir, { quiet: true });
89
+ const res = adapter.fixIntegrity();
90
+ assert.ok(res.fixed);
91
+ assert.ok(fs.existsSync(path.join(workDir, '.agents')));
92
+ assert.ok(!fs.existsSync(path.join(workDir, '.codex')));
93
+ });
94
+ });
@@ -0,0 +1,30 @@
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 GeminiAdapter = require("../bin/adapters/gemini");
7
+
8
+ describe("GeminiAdapter", () => {
9
+ let workDir;
10
+
11
+ beforeEach(() => {
12
+ workDir = fs.mkdtempSync(path.join(os.tmpdir(), "gemini-adapter-test-"));
13
+ });
14
+
15
+ afterEach(() => {
16
+ fs.rmSync(workDir, { recursive: true, force: true });
17
+ });
18
+
19
+ test("update should work when source and target are the same directory", () => {
20
+ const agentDir = path.join(workDir, ".agent");
21
+ fs.mkdirSync(agentDir, { recursive: true });
22
+ fs.writeFileSync(path.join(agentDir, "SKILL.md"), "same-source-target", "utf8");
23
+
24
+ const adapter = new GeminiAdapter(workDir, { quiet: true });
25
+ adapter.update(agentDir);
26
+
27
+ assert.ok(fs.existsSync(path.join(agentDir, "SKILL.md")));
28
+ assert.strictEqual(fs.readFileSync(path.join(agentDir, "SKILL.md"), "utf8"), "same-source-target");
29
+ });
30
+ });
@@ -0,0 +1,48 @@
1
+ const { test, describe } = require('node:test');
2
+ const assert = require('node:assert');
3
+ const RuleGenerator = require('../bin/core/generator');
4
+
5
+ describe('RuleGenerator', () => {
6
+ test('generate should keep codex managed guidance on .agents layout', () => {
7
+ const transformResult = {
8
+ metadata: [
9
+ {
10
+ id: 'test-skill',
11
+ originalName: 'test-skill',
12
+ path: 'skills/test-skill/SKILL.md',
13
+ type: 'skill',
14
+ },
15
+ ],
16
+ };
17
+
18
+ const { agentsMd, antigravityRules, codexJson } = RuleGenerator.generate(transformResult, '2.0.1');
19
+
20
+ assert.ok(agentsMd.includes('test-skill'));
21
+ assert.ok(agentsMd.includes('ag-kit doctor --target codex --fix'));
22
+ assert.ok(agentsMd.includes('.agents/skills'));
23
+
24
+ assert.ok(antigravityRules.includes('Managed skills are stored under `.agents/skills`.'));
25
+ assert.ok(!antigravityRules.includes('.codex'));
26
+
27
+ assert.strictEqual(codexJson.version, '2.0.1');
28
+ assert.strictEqual(codexJson.skills.length, 1);
29
+ assert.strictEqual(codexJson.skills[0].id, 'test-skill');
30
+ });
31
+
32
+ test('generate should work with empty metadata', () => {
33
+ const transformResult = { metadata: [] };
34
+ const { agentsMd, antigravityRules, codexJson } = RuleGenerator.generate(transformResult, '2.0.1');
35
+
36
+ assert.ok(agentsMd.includes('Codex Managed'));
37
+ assert.ok(antigravityRules.includes('Antigravity Risk Controls'));
38
+ assert.deepStrictEqual(codexJson.skills, []);
39
+ });
40
+
41
+ test('generate should require explicit version', () => {
42
+ const transformResult = { metadata: [] };
43
+ assert.throws(
44
+ () => RuleGenerator.generate(transformResult),
45
+ /需要显式传入版本号/,
46
+ );
47
+ });
48
+ });
@@ -0,0 +1,53 @@
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 GitHelper = require('../bin/utils/git-helper');
7
+
8
+ describe('GitHelper', () => {
9
+ let workDir;
10
+ let gitIgnorePath;
11
+
12
+ beforeEach(() => {
13
+ workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-helper-test-'));
14
+ gitIgnorePath = path.join(workDir, '.gitignore');
15
+ });
16
+
17
+ afterEach(() => {
18
+ fs.rmSync(workDir, { recursive: true, force: true });
19
+ });
20
+
21
+ test('removeIgnoreRules should remove target patterns', () => {
22
+ const content = `
23
+ # Comment
24
+ node_modules
25
+ .agent
26
+ src/
27
+ .codex/
28
+ `.trim();
29
+ fs.writeFileSync(gitIgnorePath, content);
30
+
31
+ const result = GitHelper.removeIgnoreRules(workDir, ['.agent', '.codex'], { dryRun: false });
32
+
33
+ assert.strictEqual(result.removedCount, 2);
34
+
35
+ const newContent = fs.readFileSync(gitIgnorePath, 'utf8');
36
+ assert.ok(!newContent.includes('.agent'));
37
+ assert.ok(!newContent.includes('.codex'));
38
+ assert.ok(newContent.includes('node_modules'));
39
+ });
40
+
41
+ test('removeIgnoreRules should respect dryRun', () => {
42
+ const content = `.agent\n.codex`;
43
+ fs.writeFileSync(gitIgnorePath, content);
44
+
45
+ const result = GitHelper.removeIgnoreRules(workDir, ['.agent'], { dryRun: true });
46
+
47
+ assert.strictEqual(result.removedCount, 1);
48
+ assert.strictEqual(result.dryRun, true);
49
+
50
+ const newContent = fs.readFileSync(gitIgnorePath, 'utf8');
51
+ assert.strictEqual(newContent, content); // Should not change
52
+ });
53
+ });
@@ -0,0 +1,133 @@
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("Global Sync", () => {
26
+ let tempDir;
27
+
28
+ beforeEach(() => {
29
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-global-sync-test-"));
30
+ });
31
+
32
+ afterEach(() => {
33
+ fs.rmSync(tempDir, { recursive: true, force: true });
34
+ });
35
+
36
+ test("global status should report missing when no global skills installed", () => {
37
+ const result = runCli(["global", "status", "--quiet"], {
38
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
39
+ });
40
+ assert.strictEqual(result.status, 2);
41
+ assert.strictEqual((result.stdout || "").trim(), "missing");
42
+ });
43
+
44
+ test("global status should report broken when target root exists but skills are incomplete", () => {
45
+ fs.mkdirSync(path.join(tempDir, ".codex"), { recursive: true });
46
+
47
+ const result = runCli(["global", "status", "--quiet"], {
48
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
49
+ });
50
+ assert.strictEqual(result.status, 1);
51
+ assert.strictEqual((result.stdout || "").trim(), "broken");
52
+ });
53
+
54
+ test("global sync should install codex skills into $HOME/.codex/skills", () => {
55
+ const result = runCli(["global", "sync", "--target", "codex", "--quiet"], {
56
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
57
+ });
58
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
59
+
60
+ const skillsRoot = path.join(tempDir, ".codex", "skills");
61
+ assert.ok(fs.existsSync(skillsRoot), "missing global codex skills root");
62
+ assert.ok(fs.existsSync(path.join(skillsRoot, "clean-code", "SKILL.md")), "missing expected skill: clean-code");
63
+ assert.ok(fs.existsSync(path.join(skillsRoot, "workflow-plan", "SKILL.md")), "missing expected workflow skill: workflow-plan");
64
+ });
65
+
66
+ test("global sync should default to syncing codex and gemini when no target is provided", () => {
67
+ const result = runCli(["global", "sync", "--quiet"], {
68
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
69
+ });
70
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
71
+
72
+ const codexRoot = path.join(tempDir, ".codex", "skills");
73
+ assert.ok(fs.existsSync(path.join(codexRoot, "clean-code", "SKILL.md")), "missing expected codex skill: clean-code");
74
+ assert.ok(fs.existsSync(path.join(codexRoot, "workflow-plan", "SKILL.md")), "missing expected codex workflow skill: workflow-plan");
75
+
76
+ const geminiCliRoot = path.join(tempDir, ".gemini", "skills");
77
+ assert.ok(fs.existsSync(path.join(geminiCliRoot, "clean-code", "SKILL.md")), "missing expected gemini-cli skill: clean-code");
78
+
79
+ const antigravityRoot = path.join(tempDir, ".gemini", "antigravity", "skills");
80
+ assert.ok(fs.existsSync(path.join(antigravityRoot, "clean-code", "SKILL.md")), "missing expected antigravity skill: clean-code");
81
+ });
82
+
83
+ test("global status should report installed after global sync", () => {
84
+ const syncResult = runCli(["global", "sync", "--quiet"], {
85
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
86
+ });
87
+ assert.strictEqual(syncResult.status, 0, syncResult.stderr || syncResult.stdout);
88
+
89
+ const statusResult = runCli(["global", "status", "--quiet"], {
90
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
91
+ });
92
+ assert.strictEqual(statusResult.status, 0, statusResult.stderr || statusResult.stdout);
93
+ assert.strictEqual((statusResult.stdout || "").trim(), "installed");
94
+ });
95
+
96
+ test("global sync should install gemini skills into both ~/.gemini/skills and ~/.gemini/antigravity/skills", () => {
97
+ const result = runCli(["global", "sync", "--target", "gemini", "--quiet"], {
98
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
99
+ });
100
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
101
+
102
+ const geminiCliRoot = path.join(tempDir, ".gemini", "skills");
103
+ assert.ok(fs.existsSync(geminiCliRoot), "missing global gemini-cli skills root");
104
+ assert.ok(fs.existsSync(path.join(geminiCliRoot, "clean-code", "SKILL.md")), "missing expected gemini-cli skill: clean-code");
105
+
106
+ const antigravityRoot = path.join(tempDir, ".gemini", "antigravity", "skills");
107
+ assert.ok(fs.existsSync(antigravityRoot), "missing global antigravity skills root");
108
+ assert.ok(fs.existsSync(path.join(antigravityRoot, "clean-code", "SKILL.md")), "missing expected antigravity skill: clean-code");
109
+ });
110
+
111
+ test("global sync should create backup when overwriting an existing skill", () => {
112
+ const skillsRoot = path.join(tempDir, ".codex", "skills", "clean-code");
113
+ fs.mkdirSync(skillsRoot, { recursive: true });
114
+ fs.writeFileSync(path.join(skillsRoot, "SKILL.md"), "modified", "utf8");
115
+
116
+ const result = runCli(["global", "sync", "--target", "codex", "--quiet"], {
117
+ env: { AG_KIT_GLOBAL_ROOT: tempDir },
118
+ });
119
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
120
+
121
+ const backupRoot = path.join(tempDir, ".ag-kit", "backups", "global");
122
+ assert.ok(fs.existsSync(backupRoot), "missing backup root");
123
+
124
+ const timestamps = fs
125
+ .readdirSync(backupRoot, { withFileTypes: true })
126
+ .filter((entry) => entry.isDirectory())
127
+ .map((entry) => entry.name);
128
+ assert.ok(timestamps.length > 0, "backup timestamp directory missing");
129
+
130
+ const backupSkillPath = path.join(backupRoot, timestamps[0], "codex", "clean-code", "SKILL.md");
131
+ assert.ok(fs.existsSync(backupSkillPath), "backup for clean-code skill missing");
132
+ });
133
+ });
@@ -0,0 +1,30 @@
1
+ const { test, describe } = require("node:test");
2
+ const assert = require("node:assert");
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { spawnSync } = require("node:child_process");
6
+
7
+ describe("Health Check Script", () => {
8
+ test("health-check script should exist and be executable", () => {
9
+ const scriptPath = path.resolve(__dirname, "..", "scripts", "health-check.sh");
10
+ assert.ok(fs.existsSync(scriptPath), "missing scripts/health-check.sh");
11
+ fs.accessSync(scriptPath, fs.constants.X_OK);
12
+ });
13
+
14
+ test("health-check node entry should exist", () => {
15
+ const scriptPath = path.resolve(__dirname, "..", "scripts", "health-check.js");
16
+ assert.ok(fs.existsSync(scriptPath), "missing scripts/health-check.js");
17
+ });
18
+
19
+ test("health-check script should pass bash syntax check", () => {
20
+ const scriptPath = path.resolve(__dirname, "..", "scripts", "health-check.sh");
21
+ const result = spawnSync("bash", ["-n", scriptPath], { encoding: "utf8" });
22
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
23
+ });
24
+
25
+ test("package scripts should expose health-check command", () => {
26
+ const packageJsonPath = path.resolve(__dirname, "..", "package.json");
27
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
28
+ assert.strictEqual(packageJson.scripts["health-check"], "node scripts/health-check.js");
29
+ });
30
+ });
@@ -0,0 +1,41 @@
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 { upsertManagedBlock } = require("../bin/utils/managed-block");
7
+
8
+ describe("ManagedBlock", () => {
9
+ let tempDir;
10
+ let targetFile;
11
+
12
+ beforeEach(() => {
13
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "managed-block-test-"));
14
+ targetFile = path.join(tempDir, "AGENTS.md");
15
+ });
16
+
17
+ afterEach(() => {
18
+ fs.rmSync(tempDir, { recursive: true, force: true });
19
+ });
20
+
21
+ test("should append managed block without removing user content", () => {
22
+ fs.writeFileSync(targetFile, "# User Content\n\ncustom text\n", "utf8");
23
+ const result = upsertManagedBlock(targetFile, "codex-core-rules", "managed line");
24
+
25
+ assert.strictEqual(result.action, "appended");
26
+ const content = fs.readFileSync(targetFile, "utf8");
27
+ assert.ok(content.includes("User Content"));
28
+ assert.ok(content.includes("BEGIN AG-KIT MANAGED BLOCK: codex-core-rules"));
29
+ assert.ok(content.includes("managed line"));
30
+ });
31
+
32
+ test("should update existing managed block in place", () => {
33
+ upsertManagedBlock(targetFile, "codex-core-rules", "v1");
34
+ const result = upsertManagedBlock(targetFile, "codex-core-rules", "v2");
35
+
36
+ assert.strictEqual(result.action, "updated");
37
+ const content = fs.readFileSync(targetFile, "utf8");
38
+ assert.ok(content.includes("v2"));
39
+ assert.ok(!content.includes("v1"));
40
+ });
41
+ });
@@ -0,0 +1,97 @@
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 ManifestManager = require('../bin/utils/manifest');
7
+
8
+ describe('ManifestManager', () => {
9
+ let tempDir;
10
+
11
+ beforeEach(() => {
12
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'manifest-test-'));
13
+ });
14
+
15
+ afterEach(() => {
16
+ fs.rmSync(tempDir, { recursive: true, force: true });
17
+ });
18
+
19
+ test('computeHash should return correct sha256', () => {
20
+ const content = 'hello world';
21
+ // echo -n "hello world" | shasum -a 256
22
+ const expected = 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9';
23
+ assert.strictEqual(ManifestManager.computeHash(content), expected);
24
+ });
25
+
26
+ test('generateFromDir should exclude directories and map files', () => {
27
+ const file1 = path.join(tempDir, 'file1.txt');
28
+ const dir1 = path.join(tempDir, 'dir1');
29
+ const file2 = path.join(dir1, 'file2.txt');
30
+
31
+ fs.writeFileSync(file1, 'content1');
32
+ fs.mkdirSync(dir1);
33
+ fs.writeFileSync(file2, 'content2');
34
+
35
+ const manifest = ManifestManager.generateFromDir(tempDir);
36
+
37
+ // Rel paths depend on OS separator, but manifest logic normalizes to /
38
+ assert.strictEqual(manifest['file1.txt'], ManifestManager.computeHash('content1'));
39
+ assert.strictEqual(manifest['dir1/file2.txt'], ManifestManager.computeHash('content2'));
40
+ });
41
+
42
+ test('checkDrift should detect modifications and missing files', () => {
43
+ const manifestPath = path.join(tempDir, 'manifest.json');
44
+ const file1 = path.join(tempDir, 'file1.txt');
45
+
46
+ fs.writeFileSync(file1, 'original');
47
+
48
+ const mgr = new ManifestManager(manifestPath);
49
+ mgr.manifest.files = ManifestManager.generateFromDir(tempDir);
50
+ mgr.save();
51
+
52
+ // 1. No Drift
53
+ let drift = mgr.checkDrift(tempDir);
54
+ assert.strictEqual(drift.modified.length, 0);
55
+ assert.strictEqual(drift.missing.length, 0);
56
+
57
+ // 2. Modified
58
+ fs.writeFileSync(file1, 'modified');
59
+ drift = mgr.checkDrift(tempDir);
60
+ assert.strictEqual(drift.modified.length, 1);
61
+ assert.strictEqual(drift.modified[0], 'file1.txt');
62
+
63
+ // 3. Missing
64
+ fs.rmSync(file1);
65
+ drift = mgr.checkDrift(tempDir);
66
+ assert.strictEqual(drift.missing.length, 1);
67
+ assert.strictEqual(drift.missing[0], 'file1.txt');
68
+ });
69
+
70
+ test('collectSmartOverwriteConflicts should only return true user conflicts', () => {
71
+ const manifestPath = path.join(tempDir, 'manifest.json');
72
+ const file1 = path.join(tempDir, 'file1.txt');
73
+
74
+ fs.writeFileSync(file1, 'original');
75
+
76
+ const mgr = new ManifestManager(manifestPath);
77
+ mgr.manifest.files = ManifestManager.generateFileEntriesFromDir(tempDir, { baseDir: tempDir });
78
+ mgr.save();
79
+
80
+ // user modified file
81
+ fs.writeFileSync(file1, 'user-modified');
82
+
83
+ const incoming = {
84
+ 'file1.txt': {
85
+ hash: ManifestManager.computeHash('next-version'),
86
+ source: 'bundled/file1.txt',
87
+ },
88
+ };
89
+ const conflicts = mgr.collectSmartOverwriteConflicts(tempDir, incoming);
90
+ assert.deepStrictEqual(conflicts, ['file1.txt']);
91
+
92
+ // if local already equals incoming hash, no conflict backup needed
93
+ fs.writeFileSync(file1, 'next-version');
94
+ const noConflicts = mgr.collectSmartOverwriteConflicts(tempDir, incoming);
95
+ assert.deepStrictEqual(noConflicts, []);
96
+ });
97
+ });
@@ -0,0 +1,27 @@
1
+ const { test, describe } = require("node:test");
2
+ const assert = require("node:assert");
3
+ const path = require("path");
4
+ const { spawnSync } = require("node:child_process");
5
+
6
+ const REPO_ROOT = path.resolve(__dirname, "..");
7
+
8
+ describe("Package Tarball", () => {
9
+ test("npm pack --dry-run should include maintenance scripts and tests", () => {
10
+ const result = spawnSync("npm", ["pack", "--json", "--dry-run"], {
11
+ cwd: REPO_ROOT,
12
+ encoding: "utf8",
13
+ });
14
+ assert.strictEqual(result.status, 0, result.stderr || result.stdout);
15
+
16
+ const payload = JSON.parse(result.stdout);
17
+ assert.ok(Array.isArray(payload) && payload.length > 0, "missing npm pack json payload");
18
+
19
+ const files = new Set(payload[0].files.map((item) => item.path));
20
+ assert.ok(files.has("scripts/clean.js"), "tarball missing scripts/clean.js");
21
+ assert.ok(files.has("scripts/ci-verify.js"), "tarball missing scripts/ci-verify.js");
22
+ assert.ok(files.has("scripts/health-check.js"), "tarball missing scripts/health-check.js");
23
+ assert.ok(files.has("scripts/health-check.sh"), "tarball missing scripts/health-check.sh");
24
+ assert.ok(files.has("tests/cli-smoke.test.js"), "tarball missing tests/cli-smoke.test.js");
25
+ assert.ok(files.has("tests/global-sync.test.js"), "tarball missing tests/global-sync.test.js");
26
+ });
27
+ });
@@ -0,0 +1,107 @@
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 CodexAdapter = require('../bin/adapters/codex');
7
+ const pkg = require('../package.json');
8
+
9
+ describe('Phase C Integration', () => {
10
+ let workDir;
11
+ let geminiRepo;
12
+
13
+ beforeEach(() => {
14
+ workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'phase-c-test-'));
15
+ geminiRepo = path.join(workDir, 'gemini-repo');
16
+
17
+ // Setup specialized Gemini-like structure
18
+ const agentDir = path.join(geminiRepo, '.agent');
19
+ const skillsDir = path.join(agentDir, 'skills');
20
+ const workflowsDir = path.join(agentDir, 'workflows');
21
+
22
+ fs.mkdirSync(skillsDir, { recursive: true });
23
+ fs.mkdirSync(workflowsDir, { recursive: true });
24
+
25
+ // Create a Skill
26
+ const skillPath = path.join(skillsDir, 'test-skill');
27
+ fs.mkdirSync(skillPath);
28
+ fs.writeFileSync(path.join(skillPath, 'SKILL.md'), '# Test Skill');
29
+
30
+ // Create a Workflow
31
+ fs.writeFileSync(path.join(workflowsDir, 'test-flow.md'), '# Test Workflow');
32
+ });
33
+
34
+ afterEach(() => {
35
+ fs.rmSync(workDir, { recursive: true, force: true });
36
+ });
37
+
38
+ test('CodexAdapter should build from Gemini structure on the fly', () => {
39
+ const options = { quiet: true, force: true };
40
+ const adapter = new CodexAdapter(workDir, options);
41
+
42
+ // Install from Gemini Repo Root
43
+ adapter.install(geminiRepo);
44
+
45
+ const codexDir = path.join(workDir, '.agents');
46
+ // Verification 1: Structure Created
47
+ assert.ok(fs.existsSync(codexDir));
48
+
49
+ // Verification 2: Transformation Happened
50
+ const builtSkill = path.join(codexDir, 'skills', 'test-skill', 'SKILL.md');
51
+ assert.ok(fs.existsSync(builtSkill));
52
+
53
+ // Workflow: workflow-test-flow
54
+ const builtWorkflow = path.join(codexDir, 'skills', 'workflow-test-flow', 'SKILL.md');
55
+ assert.ok(fs.existsSync(builtWorkflow));
56
+
57
+ // Verification 3: Manifest & Meta
58
+ assert.ok(fs.existsSync(path.join(codexDir, 'codex.json')));
59
+ assert.ok(fs.existsSync(path.join(codexDir, 'AGENTS.md')));
60
+ assert.ok(fs.existsSync(path.join(codexDir, 'antigravity.rules')));
61
+
62
+ const agentsMd = fs.readFileSync(path.join(codexDir, 'AGENTS.md'), 'utf8');
63
+ assert.ok(agentsMd.includes('test-skill'));
64
+ assert.ok(agentsMd.includes('(Codex Managed)'));
65
+
66
+ // Verification 4: Workspace managed block injection
67
+ const workspaceAgents = fs.readFileSync(path.join(workDir, 'AGENTS.md'), 'utf8');
68
+ const workspaceRules = fs.readFileSync(path.join(workDir, 'antigravity.rules'), 'utf8');
69
+ assert.ok(workspaceAgents.includes('BEGIN AG-KIT MANAGED BLOCK: codex-core-rules'));
70
+ assert.ok(workspaceAgents.includes('test-skill'));
71
+ assert.ok(workspaceRules.includes('BEGIN AG-KIT MANAGED BLOCK: codex-risk-controls'));
72
+
73
+ const codexJson = JSON.parse(fs.readFileSync(path.join(codexDir, 'codex.json'), 'utf8'));
74
+ assert.strictEqual(codexJson.version, pkg.version);
75
+ assert.ok(agentsMd.includes(`Version: ${pkg.version}`));
76
+ assert.ok(workspaceRules.includes(`version: ${pkg.version}`));
77
+ });
78
+
79
+ test('CodexBuilder should avoid ID collisions between skills and workflows', () => {
80
+ const skillCollisionPath = path.join(geminiRepo, '.agent', 'skills', 'wf-test-flow');
81
+ fs.mkdirSync(skillCollisionPath, { recursive: true });
82
+ fs.writeFileSync(path.join(skillCollisionPath, 'SKILL.md'), '# Skill wf-test-flow');
83
+
84
+ const options = { quiet: true, force: true };
85
+ const adapter = new CodexAdapter(workDir, options);
86
+ adapter.install(geminiRepo);
87
+
88
+ const codexJson = JSON.parse(
89
+ fs.readFileSync(path.join(workDir, '.agents', 'codex.json'), 'utf8')
90
+ );
91
+ const collisionEntries = (codexJson.skills || []).filter(
92
+ (item) => item.originalName === 'wf-test-flow' || item.originalName === 'test-flow'
93
+ );
94
+
95
+ assert.strictEqual(collisionEntries.length, 2, 'skill/workflow should both be preserved');
96
+ assert.notStrictEqual(
97
+ collisionEntries[0].id,
98
+ collisionEntries[1].id,
99
+ 'colliding IDs should be disambiguated',
100
+ );
101
+
102
+ for (const entry of collisionEntries) {
103
+ const skillFile = path.join(workDir, '.agents', entry.path);
104
+ assert.ok(fs.existsSync(skillFile), `missing transformed file: ${entry.path}`);
105
+ }
106
+ });
107
+ });