@uxmaltech/collab-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.
Files changed (109) hide show
  1. package/README.md +227 -0
  2. package/bin/collab +10 -0
  3. package/dist/cli.js +34 -0
  4. package/dist/commands/canon/index.js +16 -0
  5. package/dist/commands/canon/rebuild.js +95 -0
  6. package/dist/commands/compose/generate.js +63 -0
  7. package/dist/commands/compose/index.js +18 -0
  8. package/dist/commands/compose/validate.js +53 -0
  9. package/dist/commands/doctor.js +153 -0
  10. package/dist/commands/index.js +27 -0
  11. package/dist/commands/infra/down.js +23 -0
  12. package/dist/commands/infra/index.js +20 -0
  13. package/dist/commands/infra/shared.js +59 -0
  14. package/dist/commands/infra/status.js +64 -0
  15. package/dist/commands/infra/up.js +29 -0
  16. package/dist/commands/init.js +830 -0
  17. package/dist/commands/mcp/index.js +20 -0
  18. package/dist/commands/mcp/shared.js +57 -0
  19. package/dist/commands/mcp/start.js +45 -0
  20. package/dist/commands/mcp/status.js +62 -0
  21. package/dist/commands/mcp/stop.js +23 -0
  22. package/dist/commands/seed.js +55 -0
  23. package/dist/commands/uninstall.js +36 -0
  24. package/dist/commands/up.js +78 -0
  25. package/dist/commands/update-canons.js +48 -0
  26. package/dist/commands/upgrade.js +54 -0
  27. package/dist/index.js +14 -0
  28. package/dist/lib/ai-client.js +317 -0
  29. package/dist/lib/ansi.js +58 -0
  30. package/dist/lib/canon-index-generator.js +64 -0
  31. package/dist/lib/canon-index-targets.js +68 -0
  32. package/dist/lib/canon-resolver.js +262 -0
  33. package/dist/lib/canon-scaffold.js +57 -0
  34. package/dist/lib/cli-detection.js +149 -0
  35. package/dist/lib/command-context.js +23 -0
  36. package/dist/lib/compose-defaults.js +47 -0
  37. package/dist/lib/compose-env.js +24 -0
  38. package/dist/lib/compose-paths.js +36 -0
  39. package/dist/lib/compose-renderer.js +134 -0
  40. package/dist/lib/compose-validator.js +56 -0
  41. package/dist/lib/config.js +195 -0
  42. package/dist/lib/credentials.js +63 -0
  43. package/dist/lib/docker-checks.js +73 -0
  44. package/dist/lib/docker-compose.js +15 -0
  45. package/dist/lib/docker-status.js +151 -0
  46. package/dist/lib/domain-gen.js +376 -0
  47. package/dist/lib/ecosystem.js +150 -0
  48. package/dist/lib/env-file.js +77 -0
  49. package/dist/lib/errors.js +30 -0
  50. package/dist/lib/executor.js +85 -0
  51. package/dist/lib/github-auth.js +204 -0
  52. package/dist/lib/hash.js +7 -0
  53. package/dist/lib/health-checker.js +140 -0
  54. package/dist/lib/logger.js +87 -0
  55. package/dist/lib/mcp-client.js +88 -0
  56. package/dist/lib/mode.js +36 -0
  57. package/dist/lib/model-listing.js +102 -0
  58. package/dist/lib/model-registry.js +55 -0
  59. package/dist/lib/npm-operations.js +69 -0
  60. package/dist/lib/orchestrator.js +170 -0
  61. package/dist/lib/parsers.js +42 -0
  62. package/dist/lib/port-resolver.js +57 -0
  63. package/dist/lib/preconditions.js +35 -0
  64. package/dist/lib/preflight.js +88 -0
  65. package/dist/lib/process.js +6 -0
  66. package/dist/lib/prompt.js +125 -0
  67. package/dist/lib/providers.js +117 -0
  68. package/dist/lib/repo-analysis-helpers.js +379 -0
  69. package/dist/lib/repo-scanner.js +195 -0
  70. package/dist/lib/service-health.js +79 -0
  71. package/dist/lib/shell.js +49 -0
  72. package/dist/lib/state.js +38 -0
  73. package/dist/lib/update-checker.js +130 -0
  74. package/dist/lib/version.js +27 -0
  75. package/dist/stages/agent-skills-setup.js +301 -0
  76. package/dist/stages/assistant-setup.js +325 -0
  77. package/dist/stages/canon-ingest.js +249 -0
  78. package/dist/stages/canon-rebuild-graph.js +33 -0
  79. package/dist/stages/canon-rebuild-indexes.js +40 -0
  80. package/dist/stages/canon-rebuild-snapshot.js +75 -0
  81. package/dist/stages/canon-rebuild-validate.js +57 -0
  82. package/dist/stages/canon-rebuild-vectors.js +30 -0
  83. package/dist/stages/canon-scaffold.js +15 -0
  84. package/dist/stages/canon-sync.js +49 -0
  85. package/dist/stages/ci-setup.js +56 -0
  86. package/dist/stages/domain-gen.js +363 -0
  87. package/dist/stages/graph-seed.js +26 -0
  88. package/dist/stages/repo-analysis-fileonly.js +111 -0
  89. package/dist/stages/repo-analysis.js +112 -0
  90. package/dist/stages/repo-scaffold.js +110 -0
  91. package/dist/templates/canon/contracts-readme.js +39 -0
  92. package/dist/templates/canon/domain-readme.js +40 -0
  93. package/dist/templates/canon/evolution/changelog.js +53 -0
  94. package/dist/templates/canon/governance/confidence-levels.js +38 -0
  95. package/dist/templates/canon/governance/implementation-process.js +34 -0
  96. package/dist/templates/canon/governance/review-process.js +29 -0
  97. package/dist/templates/canon/governance/schema-versioning.js +25 -0
  98. package/dist/templates/canon/governance/what-enters-the-canon.js +44 -0
  99. package/dist/templates/canon/index.js +28 -0
  100. package/dist/templates/canon/knowledge-readme.js +129 -0
  101. package/dist/templates/canon/system-prompt.js +101 -0
  102. package/dist/templates/ci/architecture-merge.js +29 -0
  103. package/dist/templates/ci/architecture-pr.js +26 -0
  104. package/dist/templates/ci/index.js +7 -0
  105. package/dist/templates/consolidated.js +114 -0
  106. package/dist/templates/infra.js +90 -0
  107. package/dist/templates/mcp.js +32 -0
  108. package/install.sh +455 -0
  109. package/package.json +48 -0
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerMcpCommand = registerMcpCommand;
4
+ const start_1 = require("./start");
5
+ const status_1 = require("./status");
6
+ const stop_1 = require("./stop");
7
+ function registerMcpCommand(program) {
8
+ const mcp = program
9
+ .command('mcp')
10
+ .description('Manage MCP runtime lifecycle')
11
+ .addHelpText('after', `
12
+ Examples:
13
+ collab mcp start
14
+ collab mcp stop
15
+ collab mcp status
16
+ `);
17
+ (0, start_1.registerMcpStartCommand)(mcp);
18
+ (0, stop_1.registerMcpStopCommand)(mcp);
19
+ (0, status_1.registerMcpStatusCommand)(mcp);
20
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveMcpComposeFile = resolveMcpComposeFile;
7
+ exports.runMcpCompose = runMcpCompose;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const compose_paths_1 = require("../../lib/compose-paths");
10
+ const docker_compose_1 = require("../../lib/docker-compose");
11
+ const errors_1 = require("../../lib/errors");
12
+ const preconditions_1 = require("../../lib/preconditions");
13
+ const service_health_1 = require("../../lib/service-health");
14
+ /**
15
+ * Resolves the compose file to use for the MCP service.
16
+ * Prefers an explicit `--file` flag, then falls back to compose-paths resolution.
17
+ */
18
+ function resolveMcpComposeFile(config, outputDirectory, explicitFile) {
19
+ if (explicitFile) {
20
+ const filePath = node_path_1.default.resolve(config.workspaceDir, explicitFile);
21
+ return { filePath, source: 'split' };
22
+ }
23
+ const paths = (0, compose_paths_1.getComposeFilePaths)(config, outputDirectory);
24
+ const selected = (0, compose_paths_1.selectMcpComposeFile)(paths);
25
+ return {
26
+ filePath: selected.file,
27
+ source: selected.source,
28
+ };
29
+ }
30
+ /**
31
+ * Runs a docker compose action for the MCP service.
32
+ * When the action is `'up'`, waits for health checks to pass.
33
+ */
34
+ async function runMcpCompose(logger, executor, config, selection, action, options = {}) {
35
+ (0, preconditions_1.ensureCommandAvailable)('docker', { dryRun: executor.dryRun });
36
+ if (!executor.dryRun) {
37
+ (0, preconditions_1.ensureFileExists)(selection.filePath, 'Compose file');
38
+ }
39
+ const args = action === 'up' ? ['up', '-d'] : action === 'stop' ? ['stop'] : ['ps'];
40
+ const serviceScope = selection.source === 'consolidated' ? ['mcp'] : [];
41
+ (0, docker_compose_1.runDockerCompose)({
42
+ executor,
43
+ files: [selection.filePath],
44
+ arguments: [...args, ...serviceScope],
45
+ cwd: config.workspaceDir,
46
+ projectName: config.compose.projectName,
47
+ });
48
+ if (action !== 'up') {
49
+ return;
50
+ }
51
+ const env = (0, service_health_1.loadRuntimeEnv)(config);
52
+ const health = await (0, service_health_1.waitForMcpHealth)(env, (0, service_health_1.dryRunHealthOptions)(executor, options.health ?? {}));
53
+ (0, service_health_1.logServiceHealth)(logger, 'mcp health', health);
54
+ if (!health.ok) {
55
+ throw new errors_1.CliError('MCP service did not become healthy in time.');
56
+ }
57
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerMcpStartCommand = registerMcpStartCommand;
4
+ const command_context_1 = require("../../lib/command-context");
5
+ const ecosystem_1 = require("../../lib/ecosystem");
6
+ const parsers_1 = require("../../lib/parsers");
7
+ const shared_1 = require("./shared");
8
+ function healthOptions(options) {
9
+ return {
10
+ timeoutMs: (0, parsers_1.parsePositiveInt)('--timeout-ms', options.timeoutMs, 5_000),
11
+ retries: (0, parsers_1.parsePositiveInt)('--retries', options.retries, 15),
12
+ retryDelayMs: (0, parsers_1.parsePositiveInt)('--retry-delay-ms', options.retryDelayMs, 2_000),
13
+ };
14
+ }
15
+ function registerMcpStartCommand(program) {
16
+ program
17
+ .command('start')
18
+ .description('Start MCP runtime service and verify health endpoint')
19
+ .option('--file <path>', 'Compose file to use')
20
+ .option('--output-dir <directory>', 'Directory used to locate generated compose files')
21
+ .option('--timeout-ms <ms>', 'Per-check timeout in milliseconds', '5000')
22
+ .option('--retries <count>', 'Health check retries', '15')
23
+ .option('--retry-delay-ms <ms>', 'Delay between retries in milliseconds', '2000')
24
+ .addHelpText('after', `
25
+ Examples:
26
+ collab mcp start
27
+ collab mcp start --file docker-compose.mcp.yml --timeout-ms 3000 --retries 20
28
+ `)
29
+ .action(async (options, command) => {
30
+ const context = (0, command_context_1.createCommandContext)(command);
31
+ const selection = (0, shared_1.resolveMcpComposeFile)(context.config, options.outputDir, options.file);
32
+ await (0, shared_1.runMcpCompose)(context.logger, context.executor, context.config, selection, 'up', {
33
+ health: healthOptions(options),
34
+ });
35
+ const compatibility = await (0, ecosystem_1.checkEcosystemCompatibility)(context.config, {
36
+ dryRun: context.executor.dryRun,
37
+ });
38
+ for (const item of compatibility) {
39
+ if (!item.ok) {
40
+ context.logger.warn(`${item.id}: ${item.detail}${item.fix ? ` | fix: ${item.fix}` : ''}`);
41
+ }
42
+ }
43
+ context.logger.result(`MCP service started using ${selection.filePath}`);
44
+ });
45
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerMcpStatusCommand = registerMcpStatusCommand;
4
+ const command_context_1 = require("../../lib/command-context");
5
+ const docker_compose_1 = require("../../lib/docker-compose");
6
+ const docker_status_1 = require("../../lib/docker-status");
7
+ const health_checker_1 = require("../../lib/health-checker");
8
+ const preconditions_1 = require("../../lib/preconditions");
9
+ const service_health_1 = require("../../lib/service-health");
10
+ const shared_1 = require("./shared");
11
+ /** Fast health check options for status display (no retries). */
12
+ const QUICK_HEALTH_OPTIONS = { timeoutMs: 2_000, retries: 1, retryDelayMs: 0 };
13
+ const MCP_SERVICES = ['mcp'];
14
+ function registerMcpStatusCommand(program) {
15
+ program
16
+ .command('status')
17
+ .description('Show MCP runtime service status')
18
+ .option('--file <path>', 'Compose file to use')
19
+ .option('--output-dir <directory>', 'Directory used to locate generated compose files')
20
+ .addHelpText('after', `
21
+ Examples:
22
+ collab mcp status
23
+ collab mcp status --file docker-compose.mcp.yml
24
+ `)
25
+ .action(async (options, command) => {
26
+ const context = (0, command_context_1.createCommandContext)(command);
27
+ const selection = (0, shared_1.resolveMcpComposeFile)(context.config, options.outputDir, options.file);
28
+ (0, preconditions_1.ensureCommandAvailable)('docker', { dryRun: context.executor.dryRun });
29
+ if (!context.executor.dryRun) {
30
+ (0, preconditions_1.ensureFileExists)(selection.filePath, 'Compose file');
31
+ }
32
+ // Query container status via docker compose ps --format json
33
+ const serviceScope = selection.source === 'consolidated' ? ['mcp'] : [];
34
+ let containers = [];
35
+ try {
36
+ const result = (0, docker_compose_1.runDockerCompose)({
37
+ executor: context.executor,
38
+ files: [selection.filePath],
39
+ arguments: ['ps', '--format', 'json', ...serviceScope],
40
+ cwd: context.config.workspaceDir,
41
+ projectName: context.config.compose.projectName,
42
+ check: false,
43
+ });
44
+ containers = (0, docker_status_1.parseComposePs)(result.stdout);
45
+ }
46
+ catch {
47
+ // docker compose ps failed — treat as no running containers
48
+ }
49
+ // Run quick health check (skip in dry-run mode)
50
+ const env = (0, service_health_1.loadRuntimeEnv)(context.config);
51
+ const mcpHost = env['MCP_HOST'] || '127.0.0.1';
52
+ const mcpPort = Number(env['MCP_PORT']) || 7337;
53
+ const healthChecks = context.executor.dryRun
54
+ ? []
55
+ : await Promise.all([
56
+ (0, health_checker_1.checkHttpHealth)('mcp', `http://${mcpHost}:${mcpPort}/health`, QUICK_HEALTH_OPTIONS),
57
+ ]);
58
+ // Build and display status table
59
+ const services = (0, docker_status_1.buildServiceStatusList)([...MCP_SERVICES], containers, healthChecks);
60
+ (0, docker_status_1.printStatusTable)(context.logger, 'MCP Service', services, selection.filePath);
61
+ });
62
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerMcpStopCommand = registerMcpStopCommand;
4
+ const command_context_1 = require("../../lib/command-context");
5
+ const shared_1 = require("./shared");
6
+ function registerMcpStopCommand(program) {
7
+ program
8
+ .command('stop')
9
+ .description('Stop MCP runtime service')
10
+ .option('--file <path>', 'Compose file to use')
11
+ .option('--output-dir <directory>', 'Directory used to locate generated compose files')
12
+ .addHelpText('after', `
13
+ Examples:
14
+ collab mcp stop
15
+ collab mcp stop --file docker-compose.yml
16
+ `)
17
+ .action(async (options, command) => {
18
+ const context = (0, command_context_1.createCommandContext)(command);
19
+ const selection = (0, shared_1.resolveMcpComposeFile)(context.config, options.outputDir, options.file);
20
+ await (0, shared_1.runMcpCompose)(context.logger, context.executor, context.config, selection, 'stop');
21
+ context.logger.result(`MCP service stopped using ${selection.filePath}`);
22
+ });
23
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerSeedCommand = registerSeedCommand;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const command_context_1 = require("../lib/command-context");
9
+ const docker_compose_1 = require("../lib/docker-compose");
10
+ const compose_paths_1 = require("../lib/compose-paths");
11
+ const preconditions_1 = require("../lib/preconditions");
12
+ function registerSeedCommand(program) {
13
+ program
14
+ .command('seed')
15
+ .description('Run a baseline infrastructure readiness check before seeding data')
16
+ .option('--file <path>', 'Compose file to use')
17
+ .option('--output-dir <directory>', 'Directory used to locate generated compose files')
18
+ .option('--dry-run', 'Print the seed preflight command without executing it')
19
+ .addHelpText('after', `
20
+ Examples:
21
+ collab seed --dry-run
22
+ collab seed --file docker-compose.infra.yml
23
+ `)
24
+ .action((options, command) => {
25
+ const context = (0, command_context_1.createCommandContext)(command);
26
+ const composePaths = (0, compose_paths_1.getComposeFilePaths)(context.config, options.outputDir);
27
+ const selected = options.file
28
+ ? { file: node_path_1.default.resolve(context.config.workspaceDir, options.file), source: 'split' }
29
+ : (0, compose_paths_1.selectInfraComposeFile)(composePaths);
30
+ const composeFile = options.file
31
+ ? node_path_1.default.resolve(context.config.workspaceDir, options.file)
32
+ : selected.file;
33
+ const isDryRun = context.executor.dryRun;
34
+ if (!isDryRun) {
35
+ (0, preconditions_1.ensureFileExists)(composeFile, 'Compose file');
36
+ }
37
+ if (isDryRun) {
38
+ const projectArgs = context.config.compose.projectName
39
+ ? ['-p', context.config.compose.projectName]
40
+ : [];
41
+ context.logger.command(['docker', 'compose', ...projectArgs, '-f', composeFile, 'ps']);
42
+ context.logger.result('Seed preflight command rendered (dry-run).');
43
+ return;
44
+ }
45
+ (0, preconditions_1.ensureCommandAvailable)('docker', { dryRun: isDryRun });
46
+ (0, docker_compose_1.runDockerCompose)({
47
+ executor: context.executor,
48
+ files: [composeFile],
49
+ arguments: ['ps'],
50
+ cwd: context.config.workspaceDir,
51
+ projectName: context.config.compose.projectName,
52
+ });
53
+ context.logger.result('Seed preflight passed. Infrastructure is reachable; run domain seeding workflow next.');
54
+ });
55
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerUninstallCommand = registerUninstallCommand;
4
+ const ansi_1 = require("../lib/ansi");
5
+ const errors_1 = require("../lib/errors");
6
+ const npm_operations_1 = require("../lib/npm-operations");
7
+ const prompt_1 = require("../lib/prompt");
8
+ function registerUninstallCommand(program) {
9
+ program
10
+ .command('uninstall')
11
+ .description('Uninstall collab-cli from the system')
12
+ .option('--yes', 'Skip confirmation prompt')
13
+ .addHelpText('after', `
14
+ Examples:
15
+ collab uninstall # Uninstall with confirmation prompt
16
+ collab uninstall --yes # Uninstall without confirmation
17
+ `)
18
+ .action(async (options) => {
19
+ const npmPath = (0, npm_operations_1.requireNpm)();
20
+ if (!npmPath) {
21
+ throw new errors_1.CliError('npm not found in PATH. Cannot uninstall.');
22
+ }
23
+ if (!options.yes) {
24
+ const accepted = await (0, prompt_1.promptBoolean)('Are you sure you want to uninstall collab-cli?', false);
25
+ if (!accepted) {
26
+ process.stdout.write('Uninstall cancelled.\n');
27
+ return;
28
+ }
29
+ }
30
+ process.stdout.write('Uninstalling collab-cli...\n');
31
+ if (!(0, npm_operations_1.npmGlobalUninstall)(npmPath)) {
32
+ throw new errors_1.CliError('Uninstall failed.');
33
+ }
34
+ process.stdout.write((0, ansi_1.green)(`${ansi_1.CHECK} collab-cli has been uninstalled successfully.\n`));
35
+ });
36
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerUpCommand = registerUpCommand;
4
+ const command_context_1 = require("../lib/command-context");
5
+ const ecosystem_1 = require("../lib/ecosystem");
6
+ const mode_1 = require("../lib/mode");
7
+ const orchestrator_1 = require("../lib/orchestrator");
8
+ const parsers_1 = require("../lib/parsers");
9
+ const shared_1 = require("./infra/shared");
10
+ const shared_2 = require("./mcp/shared");
11
+ function registerUpCommand(program) {
12
+ program
13
+ .command('up')
14
+ .description('Run full startup pipeline (infra -> MCP)')
15
+ .option('--mode <mode>', 'Execution mode: file-only|indexed')
16
+ .option('--file <path>', 'Compose file used for infra and mcp commands')
17
+ .option('--output-dir <directory>', 'Directory used to locate generated compose files')
18
+ .option('--timeout-ms <ms>', 'Per-check timeout in milliseconds', '5000')
19
+ .option('--retries <count>', 'Health check retries', '15')
20
+ .option('--retry-delay-ms <ms>', 'Delay between retries in milliseconds', '2000')
21
+ .option('--resume', 'Resume from last incomplete stage for this workflow')
22
+ .addHelpText('after', `
23
+ Examples:
24
+ collab up
25
+ collab up --mode indexed --timeout-ms 3000 --retries 20
26
+ collab up --resume
27
+ `)
28
+ .action(async (options, command) => {
29
+ const context = (0, command_context_1.createCommandContext)(command);
30
+ const mode = (0, mode_1.parseMode)(options.mode, context.config.mode);
31
+ if (mode === 'file-only') {
32
+ context.logger.result('Mode file-only: skipping infra and MCP startup pipeline.');
33
+ return;
34
+ }
35
+ const health = (0, parsers_1.parseHealthOptions)(options);
36
+ await (0, orchestrator_1.runOrchestration)({
37
+ workflowId: 'up',
38
+ config: context.config,
39
+ executor: context.executor,
40
+ logger: context.logger,
41
+ resume: options.resume,
42
+ }, [
43
+ {
44
+ id: 'infra-up',
45
+ title: 'Start infrastructure services',
46
+ recovery: [
47
+ 'Run collab infra status to inspect service state.',
48
+ 'Run collab up --resume after fixing infra issues.',
49
+ ],
50
+ run: async () => {
51
+ const selection = (0, shared_1.resolveInfraComposeFile)(context.config, options.outputDir, options.file);
52
+ await (0, shared_1.runInfraCompose)(context.logger, context.executor, context.config, selection, 'up', { health });
53
+ },
54
+ },
55
+ {
56
+ id: 'mcp-up',
57
+ title: 'Start MCP service',
58
+ recovery: [
59
+ 'Run collab mcp status to inspect service state.',
60
+ 'Run collab up --resume after fixing MCP issues.',
61
+ ],
62
+ run: async () => {
63
+ const selection = (0, shared_2.resolveMcpComposeFile)(context.config, options.outputDir, options.file);
64
+ await (0, shared_2.runMcpCompose)(context.logger, context.executor, context.config, selection, 'up', { health });
65
+ },
66
+ },
67
+ ]);
68
+ const compatibility = await (0, ecosystem_1.checkEcosystemCompatibility)(context.config, {
69
+ dryRun: context.executor.dryRun,
70
+ });
71
+ for (const item of compatibility) {
72
+ if (!item.ok) {
73
+ context.logger.warn(`${item.id}: ${item.detail}${item.fix ? ` | fix: ${item.fix}` : ''}`);
74
+ }
75
+ }
76
+ context.logger.result('Full startup pipeline completed successfully.');
77
+ });
78
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerUpdateCanonsCommand = registerUpdateCanonsCommand;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const command_context_1 = require("../lib/command-context");
6
+ const canon_resolver_1 = require("../lib/canon-resolver");
7
+ const errors_1 = require("../lib/errors");
8
+ const github_auth_1 = require("../lib/github-auth");
9
+ /** Git log format used to display the latest commit info after sync. */
10
+ const COMMIT_FORMAT = '%h (%ci)';
11
+ function registerUpdateCanonsCommand(program) {
12
+ program
13
+ .command('update-canons')
14
+ .description('Download or update collab-architecture and business canons from GitHub')
15
+ .action((_options, command) => {
16
+ const context = (0, command_context_1.createCommandContext)(command);
17
+ // ── Framework canon ──────────────────────────────────────
18
+ const ok = (0, canon_resolver_1.syncCanons)((msg) => context.logger.info(msg));
19
+ if (!ok) {
20
+ throw new errors_1.CliError('Failed to sync framework canon.');
21
+ }
22
+ const canonsDir = (0, canon_resolver_1.getCanonsBaseDir)();
23
+ try {
24
+ const commitInfo = (0, node_child_process_1.execFileSync)('git', ['-C', canonsDir, 'log', '-1', `--format=${COMMIT_FORMAT}`], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
25
+ context.logger.info(`Framework canon up to date: ${commitInfo}`);
26
+ }
27
+ catch {
28
+ context.logger.info('Framework canon updated successfully.');
29
+ }
30
+ // ── Business canon ───────────────────────────────────────
31
+ if ((0, canon_resolver_1.isBusinessCanonConfigured)(context.config)) {
32
+ const auth = (0, github_auth_1.loadGitHubAuth)(context.config.collabDir);
33
+ const token = auth?.token;
34
+ const bizOk = (0, canon_resolver_1.syncBusinessCanon)(context.config, (msg) => context.logger.info(msg), token);
35
+ if (!bizOk) {
36
+ throw new errors_1.CliError('Failed to sync business canon.');
37
+ }
38
+ const bizDir = (0, canon_resolver_1.getBusinessCanonDir)(context.config);
39
+ try {
40
+ const bizCommitInfo = (0, node_child_process_1.execFileSync)('git', ['-C', bizDir, 'log', '-1', `--format=${COMMIT_FORMAT}`], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
41
+ context.logger.info(`Business canon up to date: ${bizCommitInfo}`);
42
+ }
43
+ catch {
44
+ context.logger.info('Business canon updated successfully.');
45
+ }
46
+ }
47
+ });
48
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerUpgradeCommand = registerUpgradeCommand;
7
+ const semver_1 = __importDefault(require("semver"));
8
+ const ansi_1 = require("../lib/ansi");
9
+ const errors_1 = require("../lib/errors");
10
+ const npm_operations_1 = require("../lib/npm-operations");
11
+ const update_checker_1 = require("../lib/update-checker");
12
+ const version_1 = require("../lib/version");
13
+ function registerUpgradeCommand(program) {
14
+ program
15
+ .command('upgrade')
16
+ .description('Check for and install the latest version of collab-cli')
17
+ .option('--check', 'Only check for updates without installing')
18
+ .addHelpText('after', `
19
+ Examples:
20
+ collab upgrade # Upgrade to the latest version
21
+ collab upgrade --check # Check for updates without installing
22
+ `)
23
+ .action(async (options) => {
24
+ const currentVersion = (0, version_1.readCliVersion)();
25
+ process.stdout.write(`Current version: ${currentVersion}\n`);
26
+ process.stdout.write('Checking for updates...\n');
27
+ const latestVersion = await (0, update_checker_1.fetchLatestVersion)();
28
+ if (!latestVersion) {
29
+ throw new errors_1.CliError('Unable to reach npm registry. Check your internet connection.');
30
+ }
31
+ const updateAvailable = semver_1.default.gt(latestVersion, currentVersion);
32
+ if (!updateAvailable) {
33
+ process.stdout.write((0, ansi_1.green)(`${ansi_1.CHECK} collab-cli is up to date (v${currentVersion})\n`));
34
+ await (0, update_checker_1.checkForUpdate)();
35
+ return;
36
+ }
37
+ process.stdout.write((0, ansi_1.yellow)(`Update available: ${currentVersion} → ${latestVersion}\n`));
38
+ if (options.check) {
39
+ process.stdout.write(`Run ${(0, ansi_1.green)('collab upgrade')} to install the update.\n`);
40
+ return;
41
+ }
42
+ // ── Perform the upgrade ────────────────────────────────────
43
+ const npmPath = (0, npm_operations_1.requireNpm)();
44
+ if (!npmPath) {
45
+ throw new errors_1.CliError('npm not found in PATH. Install Node.js/npm first.');
46
+ }
47
+ process.stdout.write(`Upgrading collab-cli: ${currentVersion} → ${latestVersion}...\n`);
48
+ if (!(0, npm_operations_1.npmGlobalInstall)(npmPath, latestVersion)) {
49
+ throw new errors_1.CliError('Upgrade failed.');
50
+ }
51
+ process.stdout.write((0, ansi_1.green)(`${ansi_1.CHECK} Successfully upgraded to v${latestVersion}\n`));
52
+ await (0, update_checker_1.checkForUpdate)();
53
+ });
54
+ }
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const cli_1 = require("./cli");
4
+ const errors_1 = require("./lib/errors");
5
+ void (0, cli_1.run)().catch((error) => {
6
+ if (error instanceof errors_1.CliError) {
7
+ console.error(error.message);
8
+ process.exitCode = error.exitCode;
9
+ return;
10
+ }
11
+ const message = error instanceof Error ? error.message : String(error);
12
+ console.error(message);
13
+ process.exitCode = 1;
14
+ });