@mycelish/cli 0.1.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.
- package/LICENSE +21 -0
- package/dist/__tests__/init-auto.test.d.ts +2 -0
- package/dist/__tests__/init-auto.test.d.ts.map +1 -0
- package/dist/__tests__/init-auto.test.js +60 -0
- package/dist/__tests__/init-auto.test.js.map +1 -0
- package/dist/commands/add.d.ts +20 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +99 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/add.test.d.ts +25 -0
- package/dist/commands/add.test.d.ts.map +1 -0
- package/dist/commands/add.test.js +574 -0
- package/dist/commands/add.test.js.map +1 -0
- package/dist/commands/disable.d.ts +33 -0
- package/dist/commands/disable.d.ts.map +1 -0
- package/dist/commands/disable.js +201 -0
- package/dist/commands/disable.js.map +1 -0
- package/dist/commands/disable.test.d.ts +6 -0
- package/dist/commands/disable.test.d.ts.map +1 -0
- package/dist/commands/disable.test.js +290 -0
- package/dist/commands/disable.test.js.map +1 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +33 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/doctor.test.d.ts +17 -0
- package/dist/commands/doctor.test.d.ts.map +1 -0
- package/dist/commands/doctor.test.js +691 -0
- package/dist/commands/doctor.test.js.map +1 -0
- package/dist/commands/enable.d.ts +33 -0
- package/dist/commands/enable.d.ts.map +1 -0
- package/dist/commands/enable.js +199 -0
- package/dist/commands/enable.js.map +1 -0
- package/dist/commands/enable.test.d.ts +6 -0
- package/dist/commands/enable.test.d.ts.map +1 -0
- package/dist/commands/enable.test.js +301 -0
- package/dist/commands/enable.test.js.map +1 -0
- package/dist/commands/health-checks/config-check.d.ts +34 -0
- package/dist/commands/health-checks/config-check.d.ts.map +1 -0
- package/dist/commands/health-checks/config-check.js +267 -0
- package/dist/commands/health-checks/config-check.js.map +1 -0
- package/dist/commands/health-checks/formatter.d.ts +13 -0
- package/dist/commands/health-checks/formatter.d.ts.map +1 -0
- package/dist/commands/health-checks/formatter.js +72 -0
- package/dist/commands/health-checks/formatter.js.map +1 -0
- package/dist/commands/health-checks/index.d.ts +8 -0
- package/dist/commands/health-checks/index.d.ts.map +1 -0
- package/dist/commands/health-checks/index.js +7 -0
- package/dist/commands/health-checks/index.js.map +1 -0
- package/dist/commands/health-checks/mcp-check.d.ts +9 -0
- package/dist/commands/health-checks/mcp-check.d.ts.map +1 -0
- package/dist/commands/health-checks/mcp-check.js +37 -0
- package/dist/commands/health-checks/mcp-check.js.map +1 -0
- package/dist/commands/health-checks/memory-check.d.ts +15 -0
- package/dist/commands/health-checks/memory-check.d.ts.map +1 -0
- package/dist/commands/health-checks/memory-check.js +92 -0
- package/dist/commands/health-checks/memory-check.js.map +1 -0
- package/dist/commands/health-checks/runner.d.ts +9 -0
- package/dist/commands/health-checks/runner.d.ts.map +1 -0
- package/dist/commands/health-checks/runner.js +74 -0
- package/dist/commands/health-checks/runner.js.map +1 -0
- package/dist/commands/health-checks/tool-version-check.d.ts +9 -0
- package/dist/commands/health-checks/tool-version-check.d.ts.map +1 -0
- package/dist/commands/health-checks/tool-version-check.js +35 -0
- package/dist/commands/health-checks/tool-version-check.js.map +1 -0
- package/dist/commands/health-checks/types.d.ts +16 -0
- package/dist/commands/health-checks/types.d.ts.map +1 -0
- package/dist/commands/health-checks/types.js +2 -0
- package/dist/commands/health-checks/types.js.map +1 -0
- package/dist/commands/init.d.ts +120 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +436 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/init.test.d.ts +16 -0
- package/dist/commands/init.test.d.ts.map +1 -0
- package/dist/commands/init.test.js +286 -0
- package/dist/commands/init.test.js.map +1 -0
- package/dist/commands/marketplace.d.ts +13 -0
- package/dist/commands/marketplace.d.ts.map +1 -0
- package/dist/commands/marketplace.js +85 -0
- package/dist/commands/marketplace.js.map +1 -0
- package/dist/commands/marketplace.test.d.ts +2 -0
- package/dist/commands/marketplace.test.d.ts.map +1 -0
- package/dist/commands/marketplace.test.js +78 -0
- package/dist/commands/marketplace.test.js.map +1 -0
- package/dist/commands/migrate.d.ts +15 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +179 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/migrate.test.d.ts +2 -0
- package/dist/commands/migrate.test.d.ts.map +1 -0
- package/dist/commands/migrate.test.js +109 -0
- package/dist/commands/migrate.test.js.map +1 -0
- package/dist/commands/preset.d.ts +11 -0
- package/dist/commands/preset.d.ts.map +1 -0
- package/dist/commands/preset.js +99 -0
- package/dist/commands/preset.js.map +1 -0
- package/dist/commands/preset.test.d.ts +2 -0
- package/dist/commands/preset.test.d.ts.map +1 -0
- package/dist/commands/preset.test.js +106 -0
- package/dist/commands/preset.test.js.map +1 -0
- package/dist/commands/remote.d.ts +9 -0
- package/dist/commands/remote.d.ts.map +1 -0
- package/dist/commands/remote.js +212 -0
- package/dist/commands/remote.js.map +1 -0
- package/dist/commands/remote.test.d.ts +2 -0
- package/dist/commands/remote.test.d.ts.map +1 -0
- package/dist/commands/remote.test.js +125 -0
- package/dist/commands/remote.test.js.map +1 -0
- package/dist/commands/remove.d.ts +34 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +296 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/remove.test.d.ts +2 -0
- package/dist/commands/remove.test.d.ts.map +1 -0
- package/dist/commands/remove.test.js +109 -0
- package/dist/commands/remove.test.js.map +1 -0
- package/dist/commands/serve.d.ts +3 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +29 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/serve.test.d.ts +2 -0
- package/dist/commands/serve.test.d.ts.map +1 -0
- package/dist/commands/serve.test.js +74 -0
- package/dist/commands/serve.test.js.map +1 -0
- package/dist/commands/status.d.ts +49 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +272 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/status.test.d.ts +11 -0
- package/dist/commands/status.test.d.ts.map +1 -0
- package/dist/commands/status.test.js +334 -0
- package/dist/commands/status.test.js.map +1 -0
- package/dist/commands/sync.d.ts +38 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +286 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/sync.test.d.ts +6 -0
- package/dist/commands/sync.test.d.ts.map +1 -0
- package/dist/commands/sync.test.js +341 -0
- package/dist/commands/sync.test.js.map +1 -0
- package/dist/commands/teams.d.ts +18 -0
- package/dist/commands/teams.d.ts.map +1 -0
- package/dist/commands/teams.js +68 -0
- package/dist/commands/teams.js.map +1 -0
- package/dist/commands/teams.test.d.ts +2 -0
- package/dist/commands/teams.test.d.ts.map +1 -0
- package/dist/commands/teams.test.js +94 -0
- package/dist/commands/teams.test.js.map +1 -0
- package/dist/core/adapter-base.d.ts +51 -0
- package/dist/core/adapter-base.d.ts.map +1 -0
- package/dist/core/adapter-base.js +89 -0
- package/dist/core/adapter-base.js.map +1 -0
- package/dist/core/add-helpers.d.ts +55 -0
- package/dist/core/add-helpers.d.ts.map +1 -0
- package/dist/core/add-helpers.js +273 -0
- package/dist/core/add-helpers.js.map +1 -0
- package/dist/core/agent-teams.d.ts +10 -0
- package/dist/core/agent-teams.d.ts.map +1 -0
- package/dist/core/agent-teams.js +40 -0
- package/dist/core/agent-teams.js.map +1 -0
- package/dist/core/agent-teams.test.d.ts +2 -0
- package/dist/core/agent-teams.test.d.ts.map +1 -0
- package/dist/core/agent-teams.test.js +64 -0
- package/dist/core/agent-teams.test.js.map +1 -0
- package/dist/core/auto-adapter.d.ts +27 -0
- package/dist/core/auto-adapter.d.ts.map +1 -0
- package/dist/core/auto-adapter.js +324 -0
- package/dist/core/auto-adapter.js.map +1 -0
- package/dist/core/auto-adapter.test.d.ts +2 -0
- package/dist/core/auto-adapter.test.d.ts.map +1 -0
- package/dist/core/auto-adapter.test.js +125 -0
- package/dist/core/auto-adapter.test.js.map +1 -0
- package/dist/core/config-merger.d.ts +33 -0
- package/dist/core/config-merger.d.ts.map +1 -0
- package/dist/core/config-merger.js +238 -0
- package/dist/core/config-merger.js.map +1 -0
- package/dist/core/config-merger.test.d.ts +10 -0
- package/dist/core/config-merger.test.d.ts.map +1 -0
- package/dist/core/config-merger.test.js +406 -0
- package/dist/core/config-merger.test.js.map +1 -0
- package/dist/core/conflict-detector.d.ts +23 -0
- package/dist/core/conflict-detector.d.ts.map +1 -0
- package/dist/core/conflict-detector.js +58 -0
- package/dist/core/conflict-detector.js.map +1 -0
- package/dist/core/conflict-detector.test.d.ts +2 -0
- package/dist/core/conflict-detector.test.d.ts.map +1 -0
- package/dist/core/conflict-detector.test.js +40 -0
- package/dist/core/conflict-detector.test.js.map +1 -0
- package/dist/core/env-template.d.ts +22 -0
- package/dist/core/env-template.d.ts.map +1 -0
- package/dist/core/env-template.js +125 -0
- package/dist/core/env-template.js.map +1 -0
- package/dist/core/env-template.test.d.ts +2 -0
- package/dist/core/env-template.test.d.ts.map +1 -0
- package/dist/core/env-template.test.js +145 -0
- package/dist/core/env-template.test.js.map +1 -0
- package/dist/core/fs-helpers.d.ts +8 -0
- package/dist/core/fs-helpers.d.ts.map +1 -0
- package/dist/core/fs-helpers.js +26 -0
- package/dist/core/fs-helpers.js.map +1 -0
- package/dist/core/fs-helpers.test.d.ts +2 -0
- package/dist/core/fs-helpers.test.d.ts.map +1 -0
- package/dist/core/fs-helpers.test.js +51 -0
- package/dist/core/fs-helpers.test.js.map +1 -0
- package/dist/core/machine-overrides.d.ts +13 -0
- package/dist/core/machine-overrides.d.ts.map +1 -0
- package/dist/core/machine-overrides.js +155 -0
- package/dist/core/machine-overrides.js.map +1 -0
- package/dist/core/machine-overrides.test.d.ts +2 -0
- package/dist/core/machine-overrides.test.d.ts.map +1 -0
- package/dist/core/machine-overrides.test.js +160 -0
- package/dist/core/machine-overrides.test.js.map +1 -0
- package/dist/core/marketplace-registry.d.ts +14 -0
- package/dist/core/marketplace-registry.d.ts.map +1 -0
- package/dist/core/marketplace-registry.js +215 -0
- package/dist/core/marketplace-registry.js.map +1 -0
- package/dist/core/marketplace-registry.test.d.ts +2 -0
- package/dist/core/marketplace-registry.test.d.ts.map +1 -0
- package/dist/core/marketplace-registry.test.js +232 -0
- package/dist/core/marketplace-registry.test.js.map +1 -0
- package/dist/core/marketplace-sources.d.ts +39 -0
- package/dist/core/marketplace-sources.d.ts.map +1 -0
- package/dist/core/marketplace-sources.js +199 -0
- package/dist/core/marketplace-sources.js.map +1 -0
- package/dist/core/marketplace.d.ts +18 -0
- package/dist/core/marketplace.d.ts.map +1 -0
- package/dist/core/marketplace.js +211 -0
- package/dist/core/marketplace.js.map +1 -0
- package/dist/core/marketplace.test.d.ts +2 -0
- package/dist/core/marketplace.test.d.ts.map +1 -0
- package/dist/core/marketplace.test.js +177 -0
- package/dist/core/marketplace.test.js.map +1 -0
- package/dist/core/mcp-injector.d.ts +43 -0
- package/dist/core/mcp-injector.d.ts.map +1 -0
- package/dist/core/mcp-injector.js +164 -0
- package/dist/core/mcp-injector.js.map +1 -0
- package/dist/core/mcp-injector.test.d.ts +6 -0
- package/dist/core/mcp-injector.test.d.ts.map +1 -0
- package/dist/core/mcp-injector.test.js +315 -0
- package/dist/core/mcp-injector.test.js.map +1 -0
- package/dist/core/mcp-registry.d.ts +17 -0
- package/dist/core/mcp-registry.d.ts.map +1 -0
- package/dist/core/mcp-registry.js +52 -0
- package/dist/core/mcp-registry.js.map +1 -0
- package/dist/core/mcp-registry.test.d.ts +2 -0
- package/dist/core/mcp-registry.test.d.ts.map +1 -0
- package/dist/core/mcp-registry.test.js +78 -0
- package/dist/core/mcp-registry.test.js.map +1 -0
- package/dist/core/mcp-router.d.ts +21 -0
- package/dist/core/mcp-router.d.ts.map +1 -0
- package/dist/core/mcp-router.js +67 -0
- package/dist/core/mcp-router.js.map +1 -0
- package/dist/core/mcp-router.test.d.ts +2 -0
- package/dist/core/mcp-router.test.d.ts.map +1 -0
- package/dist/core/mcp-router.test.js +40 -0
- package/dist/core/mcp-router.test.js.map +1 -0
- package/dist/core/memory-scoper.d.ts +43 -0
- package/dist/core/memory-scoper.d.ts.map +1 -0
- package/dist/core/memory-scoper.js +165 -0
- package/dist/core/memory-scoper.js.map +1 -0
- package/dist/core/memory-scoper.test.d.ts +6 -0
- package/dist/core/memory-scoper.test.d.ts.map +1 -0
- package/dist/core/memory-scoper.test.js +467 -0
- package/dist/core/memory-scoper.test.js.map +1 -0
- package/dist/core/migrator/executor.d.ts +9 -0
- package/dist/core/migrator/executor.d.ts.map +1 -0
- package/dist/core/migrator/executor.js +233 -0
- package/dist/core/migrator/executor.js.map +1 -0
- package/dist/core/migrator/index.d.ts +8 -0
- package/dist/core/migrator/index.d.ts.map +1 -0
- package/dist/core/migrator/index.js +12 -0
- package/dist/core/migrator/index.js.map +1 -0
- package/dist/core/migrator/manifest.d.ts +8 -0
- package/dist/core/migrator/manifest.d.ts.map +1 -0
- package/dist/core/migrator/manifest.js +83 -0
- package/dist/core/migrator/manifest.js.map +1 -0
- package/dist/core/migrator/planner.d.ts +6 -0
- package/dist/core/migrator/planner.d.ts.map +1 -0
- package/dist/core/migrator/planner.js +112 -0
- package/dist/core/migrator/planner.js.map +1 -0
- package/dist/core/migrator/scanners.d.ts +19 -0
- package/dist/core/migrator/scanners.d.ts.map +1 -0
- package/dist/core/migrator/scanners.js +612 -0
- package/dist/core/migrator/scanners.js.map +1 -0
- package/dist/core/migrator/scanners.test.d.ts +2 -0
- package/dist/core/migrator/scanners.test.d.ts.map +1 -0
- package/dist/core/migrator/scanners.test.js +257 -0
- package/dist/core/migrator/scanners.test.js.map +1 -0
- package/dist/core/migrator.test.d.ts +2 -0
- package/dist/core/migrator.test.d.ts.map +1 -0
- package/dist/core/migrator.test.js +451 -0
- package/dist/core/migrator.test.js.map +1 -0
- package/dist/core/plugin-scanner.d.ts +30 -0
- package/dist/core/plugin-scanner.d.ts.map +1 -0
- package/dist/core/plugin-scanner.js +174 -0
- package/dist/core/plugin-scanner.js.map +1 -0
- package/dist/core/plugin-scanner.test.d.ts +2 -0
- package/dist/core/plugin-scanner.test.d.ts.map +1 -0
- package/dist/core/plugin-scanner.test.js +279 -0
- package/dist/core/plugin-scanner.test.js.map +1 -0
- package/dist/core/presets.d.ts +55 -0
- package/dist/core/presets.d.ts.map +1 -0
- package/dist/core/presets.js +84 -0
- package/dist/core/presets.js.map +1 -0
- package/dist/core/presets.test.d.ts +2 -0
- package/dist/core/presets.test.d.ts.map +1 -0
- package/dist/core/presets.test.js +40 -0
- package/dist/core/presets.test.js.map +1 -0
- package/dist/core/skill-parser.d.ts +21 -0
- package/dist/core/skill-parser.d.ts.map +1 -0
- package/dist/core/skill-parser.js +53 -0
- package/dist/core/skill-parser.js.map +1 -0
- package/dist/core/skill-parser.test.d.ts +2 -0
- package/dist/core/skill-parser.test.d.ts.map +1 -0
- package/dist/core/skill-parser.test.js +83 -0
- package/dist/core/skill-parser.test.js.map +1 -0
- package/dist/core/smart-memory.d.ts +22 -0
- package/dist/core/smart-memory.d.ts.map +1 -0
- package/dist/core/smart-memory.js +72 -0
- package/dist/core/smart-memory.js.map +1 -0
- package/dist/core/smart-memory.test.d.ts +2 -0
- package/dist/core/smart-memory.test.d.ts.map +1 -0
- package/dist/core/smart-memory.test.js +114 -0
- package/dist/core/smart-memory.test.js.map +1 -0
- package/dist/core/snapshot.d.ts +6 -0
- package/dist/core/snapshot.d.ts.map +1 -0
- package/dist/core/snapshot.js +179 -0
- package/dist/core/snapshot.js.map +1 -0
- package/dist/core/snapshot.test.d.ts +2 -0
- package/dist/core/snapshot.test.d.ts.map +1 -0
- package/dist/core/snapshot.test.js +132 -0
- package/dist/core/snapshot.test.js.map +1 -0
- package/dist/core/symlink-manager.d.ts +78 -0
- package/dist/core/symlink-manager.d.ts.map +1 -0
- package/dist/core/symlink-manager.js +227 -0
- package/dist/core/symlink-manager.js.map +1 -0
- package/dist/core/symlink-manager.test.d.ts +8 -0
- package/dist/core/symlink-manager.test.d.ts.map +1 -0
- package/dist/core/symlink-manager.test.js +354 -0
- package/dist/core/symlink-manager.test.js.map +1 -0
- package/dist/core/sync-writer.d.ts +20 -0
- package/dist/core/sync-writer.d.ts.map +1 -0
- package/dist/core/sync-writer.js +150 -0
- package/dist/core/sync-writer.js.map +1 -0
- package/dist/core/sync-writer.test.d.ts +2 -0
- package/dist/core/sync-writer.test.d.ts.map +1 -0
- package/dist/core/sync-writer.test.js +134 -0
- package/dist/core/sync-writer.test.js.map +1 -0
- package/dist/core/toml-helpers.d.ts +9 -0
- package/dist/core/toml-helpers.d.ts.map +1 -0
- package/dist/core/toml-helpers.js +33 -0
- package/dist/core/toml-helpers.js.map +1 -0
- package/dist/core/tool-adapter.d.ts +24 -0
- package/dist/core/tool-adapter.d.ts.map +1 -0
- package/dist/core/tool-adapter.js +156 -0
- package/dist/core/tool-adapter.js.map +1 -0
- package/dist/core/tool-adapter.test.d.ts +2 -0
- package/dist/core/tool-adapter.test.d.ts.map +1 -0
- package/dist/core/tool-adapter.test.js +264 -0
- package/dist/core/tool-adapter.test.js.map +1 -0
- package/dist/core/tool-detector.d.ts +31 -0
- package/dist/core/tool-detector.d.ts.map +1 -0
- package/dist/core/tool-detector.js +67 -0
- package/dist/core/tool-detector.js.map +1 -0
- package/dist/core/tool-detector.test.d.ts +2 -0
- package/dist/core/tool-detector.test.d.ts.map +1 -0
- package/dist/core/tool-detector.test.js +126 -0
- package/dist/core/tool-detector.test.js.map +1 -0
- package/dist/core/watcher.d.ts +22 -0
- package/dist/core/watcher.d.ts.map +1 -0
- package/dist/core/watcher.js +68 -0
- package/dist/core/watcher.js.map +1 -0
- package/dist/core/watcher.test.d.ts +2 -0
- package/dist/core/watcher.test.d.ts.map +1 -0
- package/dist/core/watcher.test.js +26 -0
- package/dist/core/watcher.test.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/async-handler.d.ts +6 -0
- package/dist/routes/async-handler.d.ts.map +1 -0
- package/dist/routes/async-handler.js +9 -0
- package/dist/routes/async-handler.js.map +1 -0
- package/dist/routes/marketplace.d.ts +3 -0
- package/dist/routes/marketplace.d.ts.map +1 -0
- package/dist/routes/marketplace.js +51 -0
- package/dist/routes/marketplace.js.map +1 -0
- package/dist/routes/migrate.d.ts +3 -0
- package/dist/routes/migrate.d.ts.map +1 -0
- package/dist/routes/migrate.js +22 -0
- package/dist/routes/migrate.js.map +1 -0
- package/dist/routes/plugin-map.d.ts +11 -0
- package/dist/routes/plugin-map.d.ts.map +1 -0
- package/dist/routes/plugin-map.js +37 -0
- package/dist/routes/plugin-map.js.map +1 -0
- package/dist/routes/plugins.d.ts +3 -0
- package/dist/routes/plugins.d.ts.map +1 -0
- package/dist/routes/plugins.js +51 -0
- package/dist/routes/plugins.js.map +1 -0
- package/dist/routes/remove.d.ts +3 -0
- package/dist/routes/remove.d.ts.map +1 -0
- package/dist/routes/remove.js +27 -0
- package/dist/routes/remove.js.map +1 -0
- package/dist/routes/state.d.ts +3 -0
- package/dist/routes/state.d.ts.map +1 -0
- package/dist/routes/state.js +177 -0
- package/dist/routes/state.js.map +1 -0
- package/dist/routes/sync.d.ts +3 -0
- package/dist/routes/sync.d.ts.map +1 -0
- package/dist/routes/sync.js +12 -0
- package/dist/routes/sync.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +59 -0
- package/dist/server.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for doctor command - written FIRST following TDD
|
|
3
|
+
*
|
|
4
|
+
* The doctor command checks system health and offers to fix issues:
|
|
5
|
+
* - Checks if global mycelium dir exists
|
|
6
|
+
* - Checks if manifest.yaml is valid
|
|
7
|
+
* - Checks if each configured tool path exists
|
|
8
|
+
* - Detects broken symlinks
|
|
9
|
+
* - Validates MCP config JSON syntax
|
|
10
|
+
* - Validates MCP config YAML syntax
|
|
11
|
+
* - Reports all issues found
|
|
12
|
+
* - Shows green checkmarks for passing checks
|
|
13
|
+
* - Shows red X for failing checks
|
|
14
|
+
* - Offers fix suggestions
|
|
15
|
+
*/
|
|
16
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
17
|
+
import * as path from "node:path";
|
|
18
|
+
// Mock fs/promises module
|
|
19
|
+
vi.mock("node:fs/promises", () => ({
|
|
20
|
+
mkdir: vi.fn(),
|
|
21
|
+
writeFile: vi.fn(),
|
|
22
|
+
access: vi.fn(),
|
|
23
|
+
readFile: vi.fn(),
|
|
24
|
+
readdir: vi.fn(),
|
|
25
|
+
lstat: vi.fn(),
|
|
26
|
+
stat: vi.fn(),
|
|
27
|
+
readlink: vi.fn(),
|
|
28
|
+
}));
|
|
29
|
+
// Mock @mycelish/core
|
|
30
|
+
vi.mock("@mycelish/core", () => ({
|
|
31
|
+
expandPath: (p) => {
|
|
32
|
+
if (p.startsWith("~")) {
|
|
33
|
+
return path.join("/mock/home", p.slice(1));
|
|
34
|
+
}
|
|
35
|
+
return p;
|
|
36
|
+
},
|
|
37
|
+
ensureDir: vi.fn(),
|
|
38
|
+
pathExists: vi.fn(),
|
|
39
|
+
TOOL_REGISTRY: {
|
|
40
|
+
"claude-code": {
|
|
41
|
+
id: "claude-code",
|
|
42
|
+
display: { name: "Claude Code", icon: "claude", color: "#D97706" },
|
|
43
|
+
cli: { command: "claude" },
|
|
44
|
+
paths: {
|
|
45
|
+
mcp: "~/.claude.json",
|
|
46
|
+
projectMcp: null,
|
|
47
|
+
skills: "~/.claude/skills",
|
|
48
|
+
projectSkills: null,
|
|
49
|
+
globalMemory: "~/.claude/CLAUDE.md",
|
|
50
|
+
projectMemory: null,
|
|
51
|
+
agents: null,
|
|
52
|
+
projectAgents: null,
|
|
53
|
+
rules: null,
|
|
54
|
+
hooks: null,
|
|
55
|
+
backupDirs: [],
|
|
56
|
+
},
|
|
57
|
+
mcp: { format: "json", key: "mcpServers", entryShape: "standard" },
|
|
58
|
+
scopes: ["shared", "coding"],
|
|
59
|
+
capabilities: ["mcp", "skills", "memory", "hooks"],
|
|
60
|
+
enabled: true,
|
|
61
|
+
memoryMaxLines: 200,
|
|
62
|
+
},
|
|
63
|
+
codex: {
|
|
64
|
+
id: "codex",
|
|
65
|
+
display: { name: "Codex CLI", icon: "codex", color: "#10B981" },
|
|
66
|
+
cli: { command: "codex" },
|
|
67
|
+
paths: {
|
|
68
|
+
mcp: "~/.codex/config.toml",
|
|
69
|
+
projectMcp: null,
|
|
70
|
+
skills: "~/.codex/skills",
|
|
71
|
+
projectSkills: null,
|
|
72
|
+
globalMemory: "~/.codex/AGENTS.md",
|
|
73
|
+
projectMemory: null,
|
|
74
|
+
agents: null,
|
|
75
|
+
projectAgents: null,
|
|
76
|
+
rules: null,
|
|
77
|
+
hooks: null,
|
|
78
|
+
backupDirs: [],
|
|
79
|
+
},
|
|
80
|
+
mcp: { format: "toml", key: "mcp.servers", entryShape: "standard" },
|
|
81
|
+
scopes: ["shared", "coding"],
|
|
82
|
+
capabilities: ["mcp", "skills", "memory"],
|
|
83
|
+
enabled: true,
|
|
84
|
+
memoryMaxLines: null,
|
|
85
|
+
},
|
|
86
|
+
"gemini-cli": {
|
|
87
|
+
id: "gemini-cli",
|
|
88
|
+
display: { name: "Gemini CLI", icon: "gemini", color: "#4285F4" },
|
|
89
|
+
cli: { command: "gemini" },
|
|
90
|
+
paths: {
|
|
91
|
+
mcp: "~/.gemini/settings.json",
|
|
92
|
+
projectMcp: null,
|
|
93
|
+
skills: "~/.gemini/extensions",
|
|
94
|
+
projectSkills: null,
|
|
95
|
+
globalMemory: "~/.gemini/GEMINI.md",
|
|
96
|
+
projectMemory: null,
|
|
97
|
+
agents: null,
|
|
98
|
+
projectAgents: null,
|
|
99
|
+
rules: null,
|
|
100
|
+
hooks: null,
|
|
101
|
+
backupDirs: [],
|
|
102
|
+
},
|
|
103
|
+
mcp: { format: "json", key: "mcpServers", entryShape: "standard" },
|
|
104
|
+
scopes: ["shared", "coding"],
|
|
105
|
+
capabilities: ["mcp", "skills", "memory"],
|
|
106
|
+
enabled: true,
|
|
107
|
+
memoryMaxLines: null,
|
|
108
|
+
},
|
|
109
|
+
opencode: {
|
|
110
|
+
id: "opencode",
|
|
111
|
+
display: { name: "OpenCode", icon: "opencode", color: "#8B5CF6" },
|
|
112
|
+
cli: { command: "opencode" },
|
|
113
|
+
paths: {
|
|
114
|
+
mcp: "~/.config/opencode/opencode.json",
|
|
115
|
+
projectMcp: null,
|
|
116
|
+
skills: "~/.config/opencode/plugin",
|
|
117
|
+
projectSkills: null,
|
|
118
|
+
globalMemory: "~/.opencode/context.md",
|
|
119
|
+
projectMemory: null,
|
|
120
|
+
agents: null,
|
|
121
|
+
projectAgents: null,
|
|
122
|
+
rules: null,
|
|
123
|
+
hooks: null,
|
|
124
|
+
backupDirs: [],
|
|
125
|
+
},
|
|
126
|
+
mcp: { format: "json", key: "mcp", entryShape: "opencode" },
|
|
127
|
+
scopes: ["shared", "coding"],
|
|
128
|
+
capabilities: ["mcp", "skills", "memory"],
|
|
129
|
+
enabled: true,
|
|
130
|
+
memoryMaxLines: null,
|
|
131
|
+
},
|
|
132
|
+
openclaw: {
|
|
133
|
+
id: "openclaw",
|
|
134
|
+
display: { name: "OpenClaw", icon: "openclaw", color: "#EC4899" },
|
|
135
|
+
cli: null,
|
|
136
|
+
paths: {
|
|
137
|
+
mcp: "~/.openclaw/openclaw.json",
|
|
138
|
+
projectMcp: null,
|
|
139
|
+
skills: "~/.openclaw/skills",
|
|
140
|
+
projectSkills: null,
|
|
141
|
+
globalMemory: "~/.openclaw/MEMORY.md",
|
|
142
|
+
projectMemory: null,
|
|
143
|
+
agents: null,
|
|
144
|
+
projectAgents: null,
|
|
145
|
+
rules: null,
|
|
146
|
+
hooks: null,
|
|
147
|
+
backupDirs: [],
|
|
148
|
+
},
|
|
149
|
+
mcp: { format: "json", key: "plugins.entries", entryShape: "openclaw" },
|
|
150
|
+
scopes: ["shared", "personal"],
|
|
151
|
+
capabilities: ["mcp", "skills", "memory"],
|
|
152
|
+
enabled: true,
|
|
153
|
+
memoryMaxLines: null,
|
|
154
|
+
},
|
|
155
|
+
aider: {
|
|
156
|
+
id: "aider",
|
|
157
|
+
display: { name: "Aider", icon: "aider", color: "#F59E0B" },
|
|
158
|
+
cli: { command: "aider" },
|
|
159
|
+
paths: {
|
|
160
|
+
mcp: "~/.aider.conf.yml",
|
|
161
|
+
projectMcp: null,
|
|
162
|
+
skills: "~/.aider/plugins",
|
|
163
|
+
projectSkills: null,
|
|
164
|
+
globalMemory: "~/.aider/MEMORY.md",
|
|
165
|
+
projectMemory: null,
|
|
166
|
+
agents: null,
|
|
167
|
+
projectAgents: null,
|
|
168
|
+
rules: null,
|
|
169
|
+
hooks: null,
|
|
170
|
+
backupDirs: [],
|
|
171
|
+
},
|
|
172
|
+
mcp: { format: "yaml", key: "mcps", entryShape: "standard" },
|
|
173
|
+
scopes: ["shared", "coding"],
|
|
174
|
+
capabilities: ["mcp", "memory"],
|
|
175
|
+
enabled: true,
|
|
176
|
+
memoryMaxLines: null,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
ALL_TOOL_IDS: ["claude-code", "codex", "gemini-cli", "opencode", "openclaw", "aider"],
|
|
180
|
+
resolvePath: (p) => {
|
|
181
|
+
if (p === null)
|
|
182
|
+
return null;
|
|
183
|
+
if (typeof p === "string") {
|
|
184
|
+
if (p.startsWith("~"))
|
|
185
|
+
return path.join("/mock/home", p.slice(1));
|
|
186
|
+
return p;
|
|
187
|
+
}
|
|
188
|
+
const platform = process.platform;
|
|
189
|
+
const val = p[platform] ?? p.linux;
|
|
190
|
+
if (val?.startsWith("~"))
|
|
191
|
+
return path.join("/mock/home", val.slice(1));
|
|
192
|
+
return val;
|
|
193
|
+
},
|
|
194
|
+
toolsForScope: (scope) => {
|
|
195
|
+
// Simple mock for test purposes
|
|
196
|
+
const registry = {
|
|
197
|
+
"claude-code": { id: "claude-code", scopes: ["shared", "coding"] },
|
|
198
|
+
codex: { id: "codex", scopes: ["shared", "coding"] },
|
|
199
|
+
"gemini-cli": { id: "gemini-cli", scopes: ["shared", "coding"] },
|
|
200
|
+
opencode: { id: "opencode", scopes: ["shared", "coding"] },
|
|
201
|
+
openclaw: { id: "openclaw", scopes: ["shared", "personal"] },
|
|
202
|
+
aider: { id: "aider", scopes: ["shared", "coding"] },
|
|
203
|
+
};
|
|
204
|
+
return Object.values(registry).filter((t) => t.scopes.includes(scope));
|
|
205
|
+
},
|
|
206
|
+
}));
|
|
207
|
+
// ============================================================================
|
|
208
|
+
// Test Suites
|
|
209
|
+
// ============================================================================
|
|
210
|
+
describe("doctor command", () => {
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
vi.resetAllMocks();
|
|
213
|
+
});
|
|
214
|
+
describe("checkGlobalMyceliumExists", () => {
|
|
215
|
+
it("returns pass when ~/.mycelium exists", async () => {
|
|
216
|
+
const { pathExists } = await import("@mycelish/core");
|
|
217
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
218
|
+
const { checkGlobalMyceliumExists } = await import("./doctor.js");
|
|
219
|
+
const result = await checkGlobalMyceliumExists();
|
|
220
|
+
expect(result.status).toBe("pass");
|
|
221
|
+
expect(result.name).toBe("Global Mycelium Directory");
|
|
222
|
+
expect(result.message).toContain("exists");
|
|
223
|
+
});
|
|
224
|
+
it("returns fail when ~/.mycelium does not exist", async () => {
|
|
225
|
+
const { pathExists } = await import("@mycelish/core");
|
|
226
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
227
|
+
const { checkGlobalMyceliumExists } = await import("./doctor.js");
|
|
228
|
+
const result = await checkGlobalMyceliumExists();
|
|
229
|
+
expect(result.status).toBe("fail");
|
|
230
|
+
expect(result.message).toContain("not found");
|
|
231
|
+
expect(result.fix).toContain("mycelium init --global");
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
describe("checkManifestValid", () => {
|
|
235
|
+
it("returns pass when manifest.yaml is valid", async () => {
|
|
236
|
+
const { pathExists } = await import("@mycelish/core");
|
|
237
|
+
const fs = await import("node:fs/promises");
|
|
238
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
239
|
+
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
240
|
+
version: "1.0"
|
|
241
|
+
tools:
|
|
242
|
+
claude-code:
|
|
243
|
+
enabled: true
|
|
244
|
+
memory:
|
|
245
|
+
scopes:
|
|
246
|
+
shared:
|
|
247
|
+
sync_to: [claude-code]
|
|
248
|
+
path: global/memory/shared/
|
|
249
|
+
`);
|
|
250
|
+
const { checkManifestValid } = await import("./doctor.js");
|
|
251
|
+
const result = await checkManifestValid();
|
|
252
|
+
expect(result.status).toBe("pass");
|
|
253
|
+
expect(result.name).toBe("Manifest Configuration");
|
|
254
|
+
expect(result.message).toContain("valid");
|
|
255
|
+
});
|
|
256
|
+
it("returns fail when manifest.yaml does not exist", async () => {
|
|
257
|
+
const { pathExists } = await import("@mycelish/core");
|
|
258
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
259
|
+
const { checkManifestValid } = await import("./doctor.js");
|
|
260
|
+
const result = await checkManifestValid();
|
|
261
|
+
expect(result.status).toBe("fail");
|
|
262
|
+
expect(result.message).toContain("not found");
|
|
263
|
+
expect(result.fix).toContain("mycelium init --global");
|
|
264
|
+
});
|
|
265
|
+
it("returns fail when manifest.yaml has invalid YAML syntax", async () => {
|
|
266
|
+
const { pathExists } = await import("@mycelish/core");
|
|
267
|
+
const fs = await import("node:fs/promises");
|
|
268
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
269
|
+
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
270
|
+
version: "1.0"
|
|
271
|
+
tools:
|
|
272
|
+
claude-code:
|
|
273
|
+
enabled: [true # Invalid YAML - unclosed bracket
|
|
274
|
+
`);
|
|
275
|
+
const { checkManifestValid } = await import("./doctor.js");
|
|
276
|
+
const result = await checkManifestValid();
|
|
277
|
+
expect(result.status).toBe("fail");
|
|
278
|
+
expect(result.message).toContain("Invalid");
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
describe("checkToolPathExists", () => {
|
|
282
|
+
it("returns pass when tool skills directory exists", async () => {
|
|
283
|
+
const { pathExists } = await import("@mycelish/core");
|
|
284
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
285
|
+
const { checkToolPathExists } = await import("./doctor.js");
|
|
286
|
+
const result = await checkToolPathExists("claude-code");
|
|
287
|
+
expect(result.status).toBe("pass");
|
|
288
|
+
expect(result.name).toContain("Claude Code");
|
|
289
|
+
});
|
|
290
|
+
it("returns warn when tool skills directory does not exist", async () => {
|
|
291
|
+
const { pathExists } = await import("@mycelish/core");
|
|
292
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
293
|
+
const { checkToolPathExists } = await import("./doctor.js");
|
|
294
|
+
const result = await checkToolPathExists("claude-code");
|
|
295
|
+
expect(result.status).toBe("warn");
|
|
296
|
+
expect(result.message).toContain("not found");
|
|
297
|
+
expect(result.fix).toContain("mycelium sync");
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
describe("checkBrokenSymlinks", () => {
|
|
301
|
+
it("returns pass when no symlinks are broken", async () => {
|
|
302
|
+
const { pathExists } = await import("@mycelish/core");
|
|
303
|
+
const fs = await import("node:fs/promises");
|
|
304
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
305
|
+
vi.mocked(fs.readdir).mockResolvedValue([
|
|
306
|
+
{ name: "skill1", isSymbolicLink: () => true },
|
|
307
|
+
{ name: "skill2", isSymbolicLink: () => true },
|
|
308
|
+
]);
|
|
309
|
+
// Both symlinks resolve correctly
|
|
310
|
+
vi.mocked(fs.stat).mockResolvedValue({ isDirectory: () => true });
|
|
311
|
+
const { checkBrokenSymlinks } = await import("./doctor.js");
|
|
312
|
+
const result = await checkBrokenSymlinks("/mock/home/.claude/skills");
|
|
313
|
+
expect(result.status).toBe("pass");
|
|
314
|
+
expect(result.message).toContain("valid");
|
|
315
|
+
});
|
|
316
|
+
it("returns fail when symlinks are broken", async () => {
|
|
317
|
+
const { pathExists } = await import("@mycelish/core");
|
|
318
|
+
const fs = await import("node:fs/promises");
|
|
319
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
320
|
+
vi.mocked(fs.readdir).mockResolvedValue([
|
|
321
|
+
{ name: "skill1", isSymbolicLink: () => true },
|
|
322
|
+
{ name: "broken_skill", isSymbolicLink: () => true },
|
|
323
|
+
]);
|
|
324
|
+
// First symlink works, second is broken
|
|
325
|
+
vi.mocked(fs.stat)
|
|
326
|
+
.mockResolvedValueOnce({ isDirectory: () => true })
|
|
327
|
+
.mockRejectedValueOnce(new Error("ENOENT"));
|
|
328
|
+
const { checkBrokenSymlinks } = await import("./doctor.js");
|
|
329
|
+
const result = await checkBrokenSymlinks("/mock/home/.claude/skills");
|
|
330
|
+
expect(result.status).toBe("fail");
|
|
331
|
+
expect(result.message).toContain("broken");
|
|
332
|
+
expect(result.fix).toContain("mycelium sync");
|
|
333
|
+
});
|
|
334
|
+
it("returns pass when directory does not exist", async () => {
|
|
335
|
+
const { pathExists } = await import("@mycelish/core");
|
|
336
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
337
|
+
const { checkBrokenSymlinks } = await import("./doctor.js");
|
|
338
|
+
const result = await checkBrokenSymlinks("/mock/home/.claude/skills");
|
|
339
|
+
expect(result.status).toBe("pass");
|
|
340
|
+
expect(result.message).toContain("not present");
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
describe("checkMcpConfigJson", () => {
|
|
344
|
+
it("returns pass when MCP JSON config is valid", async () => {
|
|
345
|
+
const { pathExists } = await import("@mycelish/core");
|
|
346
|
+
const fs = await import("node:fs/promises");
|
|
347
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
348
|
+
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify({
|
|
349
|
+
mcpServers: {
|
|
350
|
+
"mcp-server-1": { command: "npx", args: ["-y", "mcp-server-1"] },
|
|
351
|
+
},
|
|
352
|
+
}));
|
|
353
|
+
const { checkMcpConfigJson } = await import("./doctor.js");
|
|
354
|
+
const result = await checkMcpConfigJson("/mock/home/.claude/mcp.json");
|
|
355
|
+
expect(result.status).toBe("pass");
|
|
356
|
+
expect(result.message).toContain("valid");
|
|
357
|
+
});
|
|
358
|
+
it("returns fail when MCP JSON config has invalid syntax", async () => {
|
|
359
|
+
const { pathExists } = await import("@mycelish/core");
|
|
360
|
+
const fs = await import("node:fs/promises");
|
|
361
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
362
|
+
vi.mocked(fs.readFile).mockResolvedValue('{ "mcpServers": { invalid }');
|
|
363
|
+
const { checkMcpConfigJson } = await import("./doctor.js");
|
|
364
|
+
const result = await checkMcpConfigJson("/mock/home/.claude/mcp.json");
|
|
365
|
+
expect(result.status).toBe("fail");
|
|
366
|
+
expect(result.message).toContain("Invalid JSON");
|
|
367
|
+
});
|
|
368
|
+
it("returns pass when config file does not exist (not required)", async () => {
|
|
369
|
+
const { pathExists } = await import("@mycelish/core");
|
|
370
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
371
|
+
const { checkMcpConfigJson } = await import("./doctor.js");
|
|
372
|
+
const result = await checkMcpConfigJson("/mock/home/.claude/mcp.json");
|
|
373
|
+
expect(result.status).toBe("pass");
|
|
374
|
+
expect(result.message).toContain("not present");
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
describe("checkMcpConfigYaml", () => {
|
|
378
|
+
it("returns pass when MCP YAML config is valid", async () => {
|
|
379
|
+
const { pathExists } = await import("@mycelish/core");
|
|
380
|
+
const fs = await import("node:fs/promises");
|
|
381
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
382
|
+
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
383
|
+
mcps:
|
|
384
|
+
mcp-server-1:
|
|
385
|
+
command: npx
|
|
386
|
+
args:
|
|
387
|
+
- -y
|
|
388
|
+
- mcp-server-1
|
|
389
|
+
`);
|
|
390
|
+
const { checkMcpConfigYaml } = await import("./doctor.js");
|
|
391
|
+
const result = await checkMcpConfigYaml("/mock/home/.config/opencode/config.yaml");
|
|
392
|
+
expect(result.status).toBe("pass");
|
|
393
|
+
expect(result.message).toContain("valid");
|
|
394
|
+
});
|
|
395
|
+
it("returns fail when MCP YAML config has invalid syntax", async () => {
|
|
396
|
+
const { pathExists } = await import("@mycelish/core");
|
|
397
|
+
const fs = await import("node:fs/promises");
|
|
398
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
399
|
+
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
400
|
+
mcps:
|
|
401
|
+
mcp-server-1:
|
|
402
|
+
command: npx
|
|
403
|
+
args: [invalid # Unclosed bracket
|
|
404
|
+
`);
|
|
405
|
+
const { checkMcpConfigYaml } = await import("./doctor.js");
|
|
406
|
+
const result = await checkMcpConfigYaml("/mock/home/.config/opencode/config.yaml");
|
|
407
|
+
expect(result.status).toBe("fail");
|
|
408
|
+
expect(result.message).toContain("Invalid YAML");
|
|
409
|
+
});
|
|
410
|
+
it("returns pass when config file does not exist (not required)", async () => {
|
|
411
|
+
const { pathExists } = await import("@mycelish/core");
|
|
412
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
413
|
+
const { checkMcpConfigYaml } = await import("./doctor.js");
|
|
414
|
+
const result = await checkMcpConfigYaml("/mock/home/.config/opencode/config.yaml");
|
|
415
|
+
expect(result.status).toBe("pass");
|
|
416
|
+
expect(result.message).toContain("not present");
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
describe("checkMemoryFilesExist", () => {
|
|
420
|
+
it("returns pass when memory directories have files", async () => {
|
|
421
|
+
const { pathExists } = await import("@mycelish/core");
|
|
422
|
+
const fs = await import("node:fs/promises");
|
|
423
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
424
|
+
vi.mocked(fs.readdir).mockResolvedValue(["memory1.md", "memory2.md"]);
|
|
425
|
+
const { checkMemoryFilesExist } = await import("./doctor.js");
|
|
426
|
+
const result = await checkMemoryFilesExist();
|
|
427
|
+
expect(result.status).toBe("pass");
|
|
428
|
+
expect(result.message).toContain("memory files found");
|
|
429
|
+
});
|
|
430
|
+
it("returns warn when no memory files exist", async () => {
|
|
431
|
+
const { pathExists } = await import("@mycelish/core");
|
|
432
|
+
const fs = await import("node:fs/promises");
|
|
433
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
434
|
+
vi.mocked(fs.readdir).mockResolvedValue([]);
|
|
435
|
+
const { checkMemoryFilesExist } = await import("./doctor.js");
|
|
436
|
+
const result = await checkMemoryFilesExist();
|
|
437
|
+
expect(result.status).toBe("warn");
|
|
438
|
+
expect(result.message).toContain("No memory files");
|
|
439
|
+
});
|
|
440
|
+
it("returns warn when memory directory does not exist", async () => {
|
|
441
|
+
const { pathExists } = await import("@mycelish/core");
|
|
442
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
443
|
+
const { checkMemoryFilesExist } = await import("./doctor.js");
|
|
444
|
+
const result = await checkMemoryFilesExist();
|
|
445
|
+
expect(result.status).toBe("warn");
|
|
446
|
+
expect(result.message).toContain("directory not found");
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
describe("checkOrphanedConfigs", () => {
|
|
450
|
+
it("returns pass when no orphaned configs exist", async () => {
|
|
451
|
+
const { pathExists } = await import("@mycelish/core");
|
|
452
|
+
const fs = await import("node:fs/promises");
|
|
453
|
+
// Manifest has claude-code enabled
|
|
454
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
455
|
+
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
456
|
+
version: "1.0"
|
|
457
|
+
tools:
|
|
458
|
+
claude-code:
|
|
459
|
+
enabled: true
|
|
460
|
+
`);
|
|
461
|
+
// Skills directory has skill for enabled tool
|
|
462
|
+
vi.mocked(fs.readdir).mockResolvedValue([
|
|
463
|
+
{ name: "skill1", isSymbolicLink: () => true },
|
|
464
|
+
]);
|
|
465
|
+
const { checkOrphanedConfigs } = await import("./doctor.js");
|
|
466
|
+
const result = await checkOrphanedConfigs();
|
|
467
|
+
expect(result.status).toBe("pass");
|
|
468
|
+
});
|
|
469
|
+
it("returns warn when orphaned skill symlinks exist for disabled tools", async () => {
|
|
470
|
+
const { pathExists } = await import("@mycelish/core");
|
|
471
|
+
const fs = await import("node:fs/promises");
|
|
472
|
+
// Manifest has claude-code disabled
|
|
473
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
474
|
+
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
475
|
+
version: "1.0"
|
|
476
|
+
tools:
|
|
477
|
+
claude-code:
|
|
478
|
+
enabled: false
|
|
479
|
+
`);
|
|
480
|
+
// But skills still exist in tool directory
|
|
481
|
+
vi.mocked(fs.readdir).mockResolvedValue([
|
|
482
|
+
{ name: "skill1", isSymbolicLink: () => true },
|
|
483
|
+
]);
|
|
484
|
+
const { checkOrphanedConfigs } = await import("./doctor.js");
|
|
485
|
+
const result = await checkOrphanedConfigs();
|
|
486
|
+
expect(result.status).toBe("warn");
|
|
487
|
+
expect(result.message).toContain("orphaned");
|
|
488
|
+
expect(result.fix).toContain("mycelium sync");
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
describe("runAllChecks", () => {
|
|
492
|
+
it("runs all diagnostic checks and returns summary", async () => {
|
|
493
|
+
const { pathExists } = await import("@mycelish/core");
|
|
494
|
+
const fs = await import("node:fs/promises");
|
|
495
|
+
// Setup: everything is healthy
|
|
496
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
497
|
+
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
498
|
+
version: "1.0"
|
|
499
|
+
tools:
|
|
500
|
+
claude-code:
|
|
501
|
+
enabled: true
|
|
502
|
+
memory:
|
|
503
|
+
scopes:
|
|
504
|
+
shared:
|
|
505
|
+
sync_to: [claude-code]
|
|
506
|
+
path: global/memory/shared/
|
|
507
|
+
`);
|
|
508
|
+
vi.mocked(fs.readdir).mockResolvedValue(["memory1.md"]);
|
|
509
|
+
vi.mocked(fs.stat).mockResolvedValue({ isDirectory: () => true });
|
|
510
|
+
const { runAllChecks } = await import("./doctor.js");
|
|
511
|
+
const result = await runAllChecks();
|
|
512
|
+
expect(result.checks).toBeDefined();
|
|
513
|
+
expect(result.checks.length).toBeGreaterThan(0);
|
|
514
|
+
expect(result.summary).toBeDefined();
|
|
515
|
+
expect(result.summary.passed).toBeGreaterThanOrEqual(0);
|
|
516
|
+
expect(result.summary.failed).toBeGreaterThanOrEqual(0);
|
|
517
|
+
expect(result.summary.warnings).toBeGreaterThanOrEqual(0);
|
|
518
|
+
});
|
|
519
|
+
it("reports all issues found", async () => {
|
|
520
|
+
const { pathExists } = await import("@mycelish/core");
|
|
521
|
+
// Setup: mycelium not initialized
|
|
522
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
523
|
+
const { runAllChecks } = await import("./doctor.js");
|
|
524
|
+
const result = await runAllChecks();
|
|
525
|
+
// Should have at least one failure
|
|
526
|
+
expect(result.summary.failed).toBeGreaterThanOrEqual(1);
|
|
527
|
+
// First check should be about global directory
|
|
528
|
+
expect(result.checks[0].status).toBe("fail");
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
describe("formatDoctorOutput", () => {
|
|
532
|
+
it("shows green checkmarks for passing checks", async () => {
|
|
533
|
+
const { formatDoctorOutput } = await import("./doctor.js");
|
|
534
|
+
const result = {
|
|
535
|
+
success: true,
|
|
536
|
+
checks: [
|
|
537
|
+
{ name: "Test Check", status: "pass", message: "All good" },
|
|
538
|
+
],
|
|
539
|
+
summary: { passed: 1, failed: 0, warnings: 0 },
|
|
540
|
+
};
|
|
541
|
+
const output = formatDoctorOutput(result);
|
|
542
|
+
// Should contain green color code and checkmark
|
|
543
|
+
expect(output).toContain("\u001b[32m"); // Green
|
|
544
|
+
expect(output).toContain("\u2714"); // Checkmark
|
|
545
|
+
});
|
|
546
|
+
it("shows red X for failing checks", async () => {
|
|
547
|
+
const { formatDoctorOutput } = await import("./doctor.js");
|
|
548
|
+
const result = {
|
|
549
|
+
success: false,
|
|
550
|
+
checks: [
|
|
551
|
+
{
|
|
552
|
+
name: "Test Check",
|
|
553
|
+
status: "fail",
|
|
554
|
+
message: "Something broke",
|
|
555
|
+
fix: "Run this command",
|
|
556
|
+
},
|
|
557
|
+
],
|
|
558
|
+
summary: { passed: 0, failed: 1, warnings: 0 },
|
|
559
|
+
};
|
|
560
|
+
const output = formatDoctorOutput(result);
|
|
561
|
+
// Should contain red color code and X mark
|
|
562
|
+
expect(output).toContain("\u001b[31m"); // Red
|
|
563
|
+
expect(output).toContain("\u2718"); // X mark
|
|
564
|
+
});
|
|
565
|
+
it("shows yellow warning for warnings", async () => {
|
|
566
|
+
const { formatDoctorOutput } = await import("./doctor.js");
|
|
567
|
+
const result = {
|
|
568
|
+
success: true,
|
|
569
|
+
checks: [
|
|
570
|
+
{ name: "Test Check", status: "warn", message: "Minor issue" },
|
|
571
|
+
],
|
|
572
|
+
summary: { passed: 0, failed: 0, warnings: 1 },
|
|
573
|
+
};
|
|
574
|
+
const output = formatDoctorOutput(result);
|
|
575
|
+
// Should contain yellow color code and warning symbol
|
|
576
|
+
expect(output).toContain("\u001b[33m"); // Yellow
|
|
577
|
+
expect(output).toContain("\u26A0"); // Warning
|
|
578
|
+
});
|
|
579
|
+
it("shows fix suggestions for failed checks", async () => {
|
|
580
|
+
const { formatDoctorOutput } = await import("./doctor.js");
|
|
581
|
+
const result = {
|
|
582
|
+
success: false,
|
|
583
|
+
checks: [
|
|
584
|
+
{
|
|
585
|
+
name: "Test Check",
|
|
586
|
+
status: "fail",
|
|
587
|
+
message: "Something broke",
|
|
588
|
+
fix: "mycelium init --global",
|
|
589
|
+
},
|
|
590
|
+
],
|
|
591
|
+
summary: { passed: 0, failed: 1, warnings: 0 },
|
|
592
|
+
};
|
|
593
|
+
const output = formatDoctorOutput(result);
|
|
594
|
+
expect(output).toContain("Fix:");
|
|
595
|
+
expect(output).toContain("mycelium init --global");
|
|
596
|
+
});
|
|
597
|
+
it("shows summary at the end", async () => {
|
|
598
|
+
const { formatDoctorOutput } = await import("./doctor.js");
|
|
599
|
+
const result = {
|
|
600
|
+
success: true,
|
|
601
|
+
checks: [
|
|
602
|
+
{ name: "Check 1", status: "pass", message: "OK" },
|
|
603
|
+
{ name: "Check 2", status: "warn", message: "Minor" },
|
|
604
|
+
{ name: "Check 3", status: "fail", message: "Bad", fix: "Fix it" },
|
|
605
|
+
],
|
|
606
|
+
summary: { passed: 1, failed: 1, warnings: 1 },
|
|
607
|
+
};
|
|
608
|
+
const output = formatDoctorOutput(result);
|
|
609
|
+
expect(output).toContain("Summary:");
|
|
610
|
+
expect(output).toContain("1 passed");
|
|
611
|
+
expect(output).toContain("1 failed");
|
|
612
|
+
expect(output).toContain("1 warning");
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
describe("checkMcpServerConnectivity", () => {
|
|
616
|
+
it("returns pass for valid command", async () => {
|
|
617
|
+
const { checkMcpServerConnectivity } = await import("./doctor.js");
|
|
618
|
+
const result = await checkMcpServerConnectivity("echo", ["hello"]);
|
|
619
|
+
expect(result.status).toBe("pass");
|
|
620
|
+
});
|
|
621
|
+
it("returns fail for invalid command", async () => {
|
|
622
|
+
const { checkMcpServerConnectivity } = await import("./doctor.js");
|
|
623
|
+
const result = await checkMcpServerConnectivity("nonexistent-cmd-xyz", []);
|
|
624
|
+
expect(result.status).toBe("fail");
|
|
625
|
+
});
|
|
626
|
+
it("includes command name in message", async () => {
|
|
627
|
+
const { checkMcpServerConnectivity } = await import("./doctor.js");
|
|
628
|
+
const result = await checkMcpServerConnectivity("echo", ["test"]);
|
|
629
|
+
expect(result.message).toContain("echo");
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
describe("checkToolVersions", () => {
|
|
633
|
+
it("returns a diagnostic result", async () => {
|
|
634
|
+
const { checkToolVersions } = await import("./doctor.js");
|
|
635
|
+
const result = await checkToolVersions();
|
|
636
|
+
expect(result.status).toBeDefined();
|
|
637
|
+
expect(result.name).toContain("Tool Versions");
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
describe("checkMemoryFileSize", () => {
|
|
641
|
+
it("returns pass when memory files are within limits", async () => {
|
|
642
|
+
const { pathExists } = await import("@mycelish/core");
|
|
643
|
+
const fs = await import("node:fs/promises");
|
|
644
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
645
|
+
vi.mocked(fs.readFile).mockResolvedValue("Line 1\nLine 2\nLine 3");
|
|
646
|
+
const { checkMemoryFileSize } = await import("./doctor.js");
|
|
647
|
+
const result = await checkMemoryFileSize("/mock/path/MEMORY.md", 200);
|
|
648
|
+
expect(result.status).toBe("pass");
|
|
649
|
+
});
|
|
650
|
+
it("returns warn when memory file exceeds limit", async () => {
|
|
651
|
+
const { pathExists } = await import("@mycelish/core");
|
|
652
|
+
const fs = await import("node:fs/promises");
|
|
653
|
+
vi.mocked(pathExists).mockResolvedValue(true);
|
|
654
|
+
const longContent = Array.from({ length: 250 }, (_, i) => `Line ${i}`).join("\n");
|
|
655
|
+
vi.mocked(fs.readFile).mockResolvedValue(longContent);
|
|
656
|
+
const { checkMemoryFileSize } = await import("./doctor.js");
|
|
657
|
+
const result = await checkMemoryFileSize("/mock/path/MEMORY.md", 200);
|
|
658
|
+
expect(result.status).toBe("warn");
|
|
659
|
+
expect(result.message).toContain("250");
|
|
660
|
+
});
|
|
661
|
+
it("returns pass when file does not exist", async () => {
|
|
662
|
+
const { pathExists } = await import("@mycelish/core");
|
|
663
|
+
vi.mocked(pathExists).mockResolvedValue(false);
|
|
664
|
+
const { checkMemoryFileSize } = await import("./doctor.js");
|
|
665
|
+
const result = await checkMemoryFileSize("/mock/path/MEMORY.md", 200);
|
|
666
|
+
expect(result.status).toBe("pass");
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
describe("doctorCommand (Commander.js)", () => {
|
|
670
|
+
it("exports a Command instance", async () => {
|
|
671
|
+
const { doctorCommand } = await import("./doctor.js");
|
|
672
|
+
expect(doctorCommand).toBeDefined();
|
|
673
|
+
expect(doctorCommand.name()).toBe("doctor");
|
|
674
|
+
});
|
|
675
|
+
it("has --json option", async () => {
|
|
676
|
+
const { doctorCommand } = await import("./doctor.js");
|
|
677
|
+
const jsonOption = doctorCommand.options.find((opt) => opt.short === "-j" || opt.long === "--json");
|
|
678
|
+
expect(jsonOption).toBeDefined();
|
|
679
|
+
});
|
|
680
|
+
it("has --fix option", async () => {
|
|
681
|
+
const { doctorCommand } = await import("./doctor.js");
|
|
682
|
+
const fixOption = doctorCommand.options.find((opt) => opt.short === "-f" || opt.long === "--fix");
|
|
683
|
+
expect(fixOption).toBeDefined();
|
|
684
|
+
});
|
|
685
|
+
it("has description", async () => {
|
|
686
|
+
const { doctorCommand } = await import("./doctor.js");
|
|
687
|
+
expect(doctorCommand.description()).toContain("health");
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
//# sourceMappingURL=doctor.test.js.map
|