cc-codeconductor 0.2.5 → 0.2.7

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 (58) hide show
  1. package/README.md +24 -24
  2. package/dist/index.js +1151 -1065
  3. package/package.json +5 -2
  4. package/policy.yml +22 -2
  5. package/presets/claude/skills/api-versioning/SKILL.md +8 -2
  6. package/presets/claude/skills/django-orm/SKILL.md +7 -1
  7. package/presets/claude/skills/django-testing/SKILL.md +7 -1
  8. package/presets/claude/skills/jpa-postgres/SKILL.md +8 -2
  9. package/presets/claude/skills/python/SKILL.md +7 -1
  10. package/presets/claude/skills/python-django-stack/SKILL.md +7 -1
  11. package/presets/claude/skills/python-fastapi-stack/SKILL.md +7 -1
  12. package/presets/claude/skills/spring-boot-feature/SKILL.md +8 -2
  13. package/presets/claude/skills/spring-boot-kotlin/SKILL.md +8 -2
  14. package/presets/claude/skills/sqlalchemy/SKILL.md +7 -1
  15. package/presets/claude/skills/testing-strategy/SKILL.md +8 -2
  16. package/presets/codex/skills/api-versioning/SKILL.md +8 -2
  17. package/presets/codex/skills/django-orm/SKILL.md +7 -1
  18. package/presets/codex/skills/django-testing/SKILL.md +7 -1
  19. package/presets/codex/skills/jpa-postgres/SKILL.md +8 -2
  20. package/presets/codex/skills/python/SKILL.md +7 -1
  21. package/presets/codex/skills/python-django-stack/SKILL.md +7 -1
  22. package/presets/codex/skills/python-fastapi-stack/SKILL.md +7 -1
  23. package/presets/codex/skills/spring-boot-feature/SKILL.md +8 -2
  24. package/presets/codex/skills/spring-boot-kotlin/SKILL.md +8 -2
  25. package/presets/codex/skills/sqlalchemy/SKILL.md +7 -1
  26. package/presets/codex/skills/testing-strategy/SKILL.md +8 -2
  27. package/presets/opencode/agents/architect.md +3 -0
  28. package/presets/opencode/agents/docs.md +3 -0
  29. package/presets/opencode/agents/implementer.md +7 -4
  30. package/presets/opencode/agents/orchestrator.md +3 -0
  31. package/presets/opencode/agents/repo-explorer.md +3 -0
  32. package/presets/opencode/agents/reviewer.md +3 -0
  33. package/presets/opencode/agents/task-coach.md +3 -0
  34. package/presets/opencode/agents/tester.md +3 -0
  35. package/presets/opencode/skills/api-versioning/SKILL.md +8 -2
  36. package/presets/opencode/skills/astro/SKILL.md +7 -1
  37. package/presets/opencode/skills/code-review/SKILL.md +7 -1
  38. package/presets/opencode/skills/django-orm/SKILL.md +7 -1
  39. package/presets/opencode/skills/django-testing/SKILL.md +7 -1
  40. package/presets/opencode/skills/django-uv/SKILL.md +7 -1
  41. package/presets/opencode/skills/jpa-postgres/SKILL.md +8 -2
  42. package/presets/opencode/skills/nextjs-typescript/SKILL.md +7 -1
  43. package/presets/opencode/skills/python/SKILL.md +7 -1
  44. package/presets/opencode/skills/python-django-stack/SKILL.md +7 -1
  45. package/presets/opencode/skills/python-fastapi-stack/SKILL.md +7 -1
  46. package/presets/opencode/skills/security/SKILL.md +7 -1
  47. package/presets/opencode/skills/spring-boot-feature/SKILL.md +8 -2
  48. package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +8 -2
  49. package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +7 -1
  50. package/presets/opencode/skills/sqlalchemy/SKILL.md +7 -1
  51. package/presets/opencode/skills/testing-tdd/SKILL.md +7 -1
  52. package/src/presets/manifests/cursor.yml +9 -0
  53. package/src/presets/manifests/gemini.yml +9 -0
  54. package/src/presets/models/claude.yml +60 -1
  55. package/src/presets/models/codex.yml +60 -1
  56. package/src/presets/models/cursor.yml +96 -0
  57. package/src/presets/models/gemini.yml +96 -0
  58. package/src/presets/models/opencode.yml +60 -1
package/dist/index.js CHANGED
@@ -29,6 +29,45 @@ var __export = (target, all) => {
29
29
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
30
30
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
31
31
 
32
+ // src/core/filesystem/safety.ts
33
+ var exports_safety = {};
34
+ __export(exports_safety, {
35
+ validateWritePath: () => validateWritePath,
36
+ isWritable: () => isWritable,
37
+ isProtectedPath: () => isProtectedPath,
38
+ fileExists: () => fileExists
39
+ });
40
+ import { constants } from "node:fs";
41
+ import { access } from "node:fs/promises";
42
+ async function fileExists(dir, filename) {
43
+ try {
44
+ const path = filename.startsWith("/") ? filename : `${dir}/${filename}`;
45
+ await access(path, constants.F_OK);
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+ function isProtectedPath(path) {
52
+ const normalized = path.toLowerCase();
53
+ return PROTECTED_PATHS.some((p) => normalized.includes(p.toLowerCase()));
54
+ }
55
+ function validateWritePath(path) {
56
+ return !isProtectedPath(path);
57
+ }
58
+ async function isWritable(dir) {
59
+ try {
60
+ await access(dir, constants.W_OK);
61
+ return true;
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+ var PROTECTED_PATHS;
67
+ var init_safety = __esm(() => {
68
+ PROTECTED_PATHS = [".git", ".env", ".env.local", ".env.production", "secrets", "credentials"];
69
+ });
70
+
32
71
  // node_modules/yaml/dist/nodes/identity.js
33
72
  var require_identity = __commonJS((exports) => {
34
73
  var ALIAS = Symbol.for("yaml.alias");
@@ -6970,137 +7009,6 @@ var require_public_api = __commonJS((exports) => {
6970
7009
  exports.stringify = stringify;
6971
7010
  });
6972
7011
 
6973
- // src/core/filesystem/safety.ts
6974
- var exports_safety = {};
6975
- __export(exports_safety, {
6976
- validateWritePath: () => validateWritePath,
6977
- isWritable: () => isWritable,
6978
- isProtectedPath: () => isProtectedPath,
6979
- fileExists: () => fileExists
6980
- });
6981
- import { access as access2 } from "node:fs/promises";
6982
- import { constants } from "node:fs";
6983
- async function fileExists(dir, filename) {
6984
- try {
6985
- const path = filename.startsWith("/") ? filename : `${dir}/${filename}`;
6986
- await access2(path, constants.F_OK);
6987
- return true;
6988
- } catch {
6989
- return false;
6990
- }
6991
- }
6992
- function isProtectedPath(path) {
6993
- const normalized = path.toLowerCase();
6994
- return PROTECTED_PATHS.some((p) => normalized.includes(p.toLowerCase()));
6995
- }
6996
- function validateWritePath(path) {
6997
- return !isProtectedPath(path);
6998
- }
6999
- async function isWritable(dir) {
7000
- try {
7001
- await access2(dir, constants.W_OK);
7002
- return true;
7003
- } catch {
7004
- return false;
7005
- }
7006
- }
7007
- var PROTECTED_PATHS;
7008
- var init_safety = __esm(() => {
7009
- PROTECTED_PATHS = [
7010
- ".git",
7011
- ".env",
7012
- ".env.local",
7013
- ".env.production",
7014
- "secrets",
7015
- "credentials"
7016
- ];
7017
- });
7018
-
7019
- // src/commands/init.command.ts
7020
- import { readFile, writeFile as writeFile2, mkdir as mkdir2, access as access3 } from "node:fs/promises";
7021
- import { resolve as resolve3, basename } from "node:path";
7022
- import { homedir } from "node:os";
7023
-
7024
- // src/core/config/config-writer.ts
7025
- import { mkdir, writeFile, access } from "node:fs/promises";
7026
- import { resolve } from "node:path";
7027
-
7028
- // node_modules/yaml/dist/index.js
7029
- var composer = require_composer();
7030
- var Document = require_Document();
7031
- var Schema = require_Schema();
7032
- var errors = require_errors();
7033
- var Alias = require_Alias();
7034
- var identity = require_identity();
7035
- var Pair = require_Pair();
7036
- var Scalar = require_Scalar();
7037
- var YAMLMap = require_YAMLMap();
7038
- var YAMLSeq = require_YAMLSeq();
7039
- var cst = require_cst();
7040
- var lexer = require_lexer();
7041
- var lineCounter = require_line_counter();
7042
- var parser = require_parser();
7043
- var publicApi = require_public_api();
7044
- var visit = require_visit();
7045
- var $Composer = composer.Composer;
7046
- var $Document = Document.Document;
7047
- var $Schema = Schema.Schema;
7048
- var $YAMLError = errors.YAMLError;
7049
- var $YAMLParseError = errors.YAMLParseError;
7050
- var $YAMLWarning = errors.YAMLWarning;
7051
- var $Alias = Alias.Alias;
7052
- var $isAlias = identity.isAlias;
7053
- var $isCollection = identity.isCollection;
7054
- var $isDocument = identity.isDocument;
7055
- var $isMap = identity.isMap;
7056
- var $isNode = identity.isNode;
7057
- var $isPair = identity.isPair;
7058
- var $isScalar = identity.isScalar;
7059
- var $isSeq = identity.isSeq;
7060
- var $Pair = Pair.Pair;
7061
- var $Scalar = Scalar.Scalar;
7062
- var $YAMLMap = YAMLMap.YAMLMap;
7063
- var $YAMLSeq = YAMLSeq.YAMLSeq;
7064
- var $Lexer = lexer.Lexer;
7065
- var $LineCounter = lineCounter.LineCounter;
7066
- var $Parser = parser.Parser;
7067
- var $parse = publicApi.parse;
7068
- var $parseAllDocuments = publicApi.parseAllDocuments;
7069
- var $parseDocument = publicApi.parseDocument;
7070
- var $stringify = publicApi.stringify;
7071
- var $visit = visit.visit;
7072
- var $visitAsync = visit.visitAsync;
7073
-
7074
- // src/core/config/codeconductor-config.ts
7075
- var DEFAULT_CONFIG = {
7076
- version: "0.2.0",
7077
- project: {
7078
- name: "unnamed-project"
7079
- },
7080
- defaults: {
7081
- target: "opencode",
7082
- overwrite: false
7083
- },
7084
- presets: {
7085
- council: {
7086
- enabled: true,
7087
- version: "0.1.0"
7088
- }
7089
- },
7090
- safety: {
7091
- destructiveCommands: ["rm -rf", "drop table", "delete from"],
7092
- secretPatterns: ["password", "secret", "api_key", "token"]
7093
- }
7094
- };
7095
-
7096
- // src/utils/result.ts
7097
- function ok(data) {
7098
- return { success: true, data };
7099
- }
7100
- function err(error) {
7101
- return { success: false, error };
7102
- }
7103
-
7104
7012
  // src/cli/errors.ts
7105
7013
  var ExitCode = {
7106
7014
  SUCCESS: 0,
@@ -7133,48 +7041,62 @@ function getExitCode(error) {
7133
7041
  }
7134
7042
  return ExitCode.VALIDATION_ERROR;
7135
7043
  }
7136
-
7137
- // src/core/config/config-writer.ts
7138
- var CONFIG_DIR = ".codeconductor";
7139
- var CONFIG_FILE = "config.yml";
7140
- async function writeConfig(projectRoot, config = {}, force = false) {
7141
- try {
7142
- const configDir = resolve(projectRoot, CONFIG_DIR);
7143
- await mkdir(configDir, { recursive: true });
7144
- const configPath = resolve(configDir, CONFIG_FILE);
7145
- if (!force) {
7146
- try {
7147
- await access(configPath);
7148
- return ok(undefined);
7149
- } catch {}
7150
- }
7151
- const mergedConfig = {
7152
- ...DEFAULT_CONFIG,
7153
- ...config,
7154
- project: {
7155
- ...DEFAULT_CONFIG.project,
7156
- ...config.project
7157
- },
7158
- defaults: {
7159
- ...DEFAULT_CONFIG.defaults,
7160
- ...config.defaults
7161
- },
7162
- presets: {
7163
- ...DEFAULT_CONFIG.presets,
7164
- ...config.presets
7165
- },
7166
- safety: {
7167
- ...DEFAULT_CONFIG.safety,
7168
- ...config.safety
7169
- }
7170
- };
7171
- const yamlContent = $stringify(mergedConfig);
7172
- await writeFile(configPath, yamlContent, "utf-8");
7173
- return ok(undefined);
7174
- } catch (error) {
7175
- return err(new ValidationError("Failed to write config", error));
7044
+ // package.json
7045
+ var package_default = {
7046
+ name: "cc-codeconductor",
7047
+ version: "0.2.7",
7048
+ description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
7049
+ keywords: [
7050
+ "ai",
7051
+ "agents",
7052
+ "orchestration",
7053
+ "workflow",
7054
+ "opencode",
7055
+ "claude",
7056
+ "codex",
7057
+ "gemini",
7058
+ "cursor"
7059
+ ],
7060
+ license: "MIT",
7061
+ files: [
7062
+ "dist/index.js",
7063
+ "policy.yml",
7064
+ "presets/",
7065
+ "src/presets/"
7066
+ ],
7067
+ repository: {
7068
+ type: "git",
7069
+ url: "https://github.com/lgzarturo/codeconductor"
7070
+ },
7071
+ engines: {
7072
+ node: ">=20.11"
7073
+ },
7074
+ type: "module",
7075
+ bin: {
7076
+ "cc-codeconductor": "./dist/index.js",
7077
+ codeconductor: "./dist/index.js"
7078
+ },
7079
+ scripts: {
7080
+ dev: "bun run src/cli/main.ts",
7081
+ build: "bun build src/cli/main.ts --target=node --outfile=dist/index.js",
7082
+ test: "bun test",
7083
+ typecheck: "tsc --noEmit",
7084
+ "check:prompt-changelog": "bun run scripts/check-prompt-changelog.ts",
7085
+ "release:patch": "bash release.sh patch",
7086
+ "release:minor": "bash release.sh minor",
7087
+ "release:major": "bash release.sh major",
7088
+ "release:notes": "git-cliff --config .cliff.toml --output CHANGELOG.md",
7089
+ release: "bash release.sh"
7090
+ },
7091
+ dependencies: {
7092
+ zod: "^3.23.8",
7093
+ yaml: "^2.4.5"
7094
+ },
7095
+ devDependencies: {
7096
+ "@types/bun": "^1.1.8",
7097
+ typescript: "^5.5.3"
7176
7098
  }
7177
- }
7099
+ };
7178
7100
 
7179
7101
  // src/core/detection/project-detector.ts
7180
7102
  async function detectProject(rootDir) {
@@ -7297,216 +7219,6 @@ async function detectAstro(rootDir) {
7297
7219
  return signals;
7298
7220
  }
7299
7221
 
7300
- // src/core/presets/package-paths.ts
7301
- import { dirname, resolve as resolve2, sep } from "node:path";
7302
- import { fileURLToPath } from "node:url";
7303
- var moduleDir = dirname(fileURLToPath(import.meta.url));
7304
- var PACKAGE_ROOT = moduleDir.endsWith(`${sep}dist`) ? resolve2(moduleDir, "..") : resolve2(moduleDir, "..", "..", "..");
7305
- var SRC_PRESETS_DIR = resolve2(PACKAGE_ROOT, "src", "presets");
7306
- var ROOT_PRESETS_DIR = resolve2(PACKAGE_ROOT, "presets");
7307
- var POLICY_PATH = resolve2(PACKAGE_ROOT, "policy.yml");
7308
-
7309
- // src/core/presets/preset-resolver.ts
7310
- var CURRENT_PRESET_VERSION = "v0.2.0";
7311
- function resolvePreset(target, profile) {
7312
- const stack = resolveStack(profile);
7313
- const architecture = profile.signals.length > 0 ? "single-project" : "unknown";
7314
- const warnings = [];
7315
- if (profile.confidence === "low") {
7316
- warnings.push("Detection confidence is low; review the selected preset before applying it.");
7317
- }
7318
- if (stack === "unknown") {
7319
- warnings.push("No supported stack was detected; using the generic CodeConductor workflow preset.");
7320
- } else {
7321
- warnings.push(`${stack} projects currently receive the generic ${target} workflow plus matching skills; stack-specific asset pruning is not implemented yet.`);
7322
- }
7323
- if (architecture === "unknown") {
7324
- warnings.push("Project architecture could not be inferred from available signals.");
7325
- }
7326
- return {
7327
- target,
7328
- stack,
7329
- architecture,
7330
- confidence: profile.confidence,
7331
- presetVersion: CURRENT_PRESET_VERSION,
7332
- assets: resolveAssets(target),
7333
- warnings
7334
- };
7335
- }
7336
- function resolveStack(profile) {
7337
- if (profile.frameworks.includes("spring"))
7338
- return "spring";
7339
- if (profile.frameworks.includes("django"))
7340
- return "django";
7341
- if (profile.frameworks.includes("astro"))
7342
- return "astro";
7343
- if (profile.runtimes.includes("bun"))
7344
- return "bun";
7345
- if (profile.runtimes.includes("node"))
7346
- return "node";
7347
- return "unknown";
7348
- }
7349
- function resolveAssets(target) {
7350
- switch (target) {
7351
- case "opencode":
7352
- return ["opencode.jsonc", "agents", "commands", "prompts/v0.2.0", "skills"];
7353
- case "claude":
7354
- return ["CLAUDE.md", "settings.json", "commands", "skills", "agents", "prompts/v0.2.0"];
7355
- case "codex":
7356
- return ["AGENTS.md", "skills", "prompts/v0.2.0"];
7357
- }
7358
- }
7359
-
7360
- // src/commands/init.command.ts
7361
- async function initCommand(options) {
7362
- const { projectRoot, dryRun, force, global: isGlobal, output } = options;
7363
- const baseDir = isGlobal ? homedir() : projectRoot;
7364
- try {
7365
- let profile = null;
7366
- if (!isGlobal) {
7367
- profile = await detectProject(projectRoot);
7368
- if (profile.signals.length === 0) {
7369
- return {
7370
- code: 3,
7371
- data: {
7372
- success: false,
7373
- command: "init",
7374
- errors: ["No project signals detected. Project may be empty or unsupported."]
7375
- }
7376
- };
7377
- }
7378
- }
7379
- const config = profile ? {
7380
- project: {
7381
- name: basename(projectRoot) || "unnamed-project",
7382
- profile: profile.runtimes[0] || "unknown"
7383
- },
7384
- defaults: {
7385
- target: "opencode",
7386
- overwrite: force
7387
- }
7388
- } : {
7389
- project: {
7390
- name: "global",
7391
- profile: "global"
7392
- },
7393
- defaults: {
7394
- target: "opencode",
7395
- overwrite: force
7396
- }
7397
- };
7398
- const presetResolution = profile ? resolvePreset("opencode", profile) : null;
7399
- const wouldCreate = [".codeconductor/config.yml"];
7400
- const presetsToCopy = await resolvePresetsToCopy();
7401
- for (const p of presetsToCopy) {
7402
- wouldCreate.push(`.codeconductor/presets/${p.name}`);
7403
- }
7404
- if (dryRun) {
7405
- return {
7406
- code: 0,
7407
- data: {
7408
- success: true,
7409
- command: "init",
7410
- message: "Dry run - would create config",
7411
- wouldCreate,
7412
- ...profile ? {
7413
- detected: {
7414
- languages: profile.languages,
7415
- runtimes: profile.runtimes,
7416
- frameworks: profile.frameworks,
7417
- signals: profile.signals,
7418
- confidence: profile.confidence
7419
- },
7420
- presetResolution
7421
- } : { global: true }
7422
- }
7423
- };
7424
- }
7425
- const result = await writeConfig(baseDir, config, force);
7426
- if (!result.success) {
7427
- return {
7428
- code: 1,
7429
- data: {
7430
- success: false,
7431
- command: "init",
7432
- errors: [result.error.message]
7433
- }
7434
- };
7435
- }
7436
- const copiedPresets = await copyPresets(baseDir, presetsToCopy, force);
7437
- return {
7438
- code: 0,
7439
- data: {
7440
- success: true,
7441
- command: "init",
7442
- created: [".codeconductor/config.yml", ...copiedPresets],
7443
- ...profile ? {
7444
- detected: {
7445
- languages: profile.languages,
7446
- runtimes: profile.runtimes,
7447
- frameworks: profile.frameworks,
7448
- signals: profile.signals,
7449
- confidence: profile.confidence
7450
- },
7451
- presetResolution
7452
- } : { global: true }
7453
- }
7454
- };
7455
- } catch (error) {
7456
- return {
7457
- code: 1,
7458
- data: {
7459
- success: false,
7460
- command: "init",
7461
- errors: [String(error)]
7462
- }
7463
- };
7464
- }
7465
- }
7466
- async function resolvePresetsToCopy() {
7467
- const sources = [];
7468
- const bundledCouncil = resolve3(SRC_PRESETS_DIR, "council", "council.yml");
7469
- if (await fileExists2(bundledCouncil)) {
7470
- sources.push({ name: "council.yml", sourcePath: bundledCouncil });
7471
- }
7472
- const policyPath = POLICY_PATH;
7473
- if (await fileExists2(policyPath)) {
7474
- sources.push({ name: "policy.yml", sourcePath: policyPath });
7475
- }
7476
- return sources;
7477
- }
7478
- async function copyPresets(baseDir, presets, force) {
7479
- const presetsDir = resolve3(baseDir, ".codeconductor", "presets");
7480
- await mkdir2(presetsDir, { recursive: true });
7481
- const copied = [];
7482
- for (const preset of presets) {
7483
- const destPath = resolve3(presetsDir, preset.name);
7484
- if (!force && await fileExists2(destPath)) {
7485
- continue;
7486
- }
7487
- try {
7488
- const content = await readFile(preset.sourcePath, "utf-8");
7489
- await writeFile2(destPath, content, "utf-8");
7490
- copied.push(`.codeconductor/presets/${preset.name}`);
7491
- } catch (err2) {
7492
- const code = err2.code;
7493
- if (code !== "ENOENT") {
7494
- process.stderr.write(`warn: could not copy ${preset.name}: ${String(err2)}
7495
- `);
7496
- }
7497
- }
7498
- }
7499
- return copied;
7500
- }
7501
- async function fileExists2(path) {
7502
- try {
7503
- await access3(path);
7504
- return true;
7505
- } catch {
7506
- return false;
7507
- }
7508
- }
7509
-
7510
7222
  // src/commands/detect.command.ts
7511
7223
  async function detectCommand(options) {
7512
7224
  const { projectRoot, output } = options;
@@ -7563,13 +7275,66 @@ function getRecommendedPresets(profile) {
7563
7275
  return presets;
7564
7276
  }
7565
7277
 
7566
- // src/commands/install.command.ts
7567
- import { resolve as resolve6 } from "node:path";
7568
- import { homedir as homedir2 } from "node:os";
7278
+ // src/commands/doctor.command.ts
7279
+ import { join } from "node:path";
7569
7280
 
7570
- // src/core/presets/preset-loader.ts
7571
- import { readFile as readFile2 } from "node:fs/promises";
7572
- import { resolve as resolve4 } from "node:path";
7281
+ // src/core/config/config-loader.ts
7282
+ import { readFile } from "node:fs/promises";
7283
+ import { resolve } from "node:path";
7284
+
7285
+ // node_modules/yaml/dist/index.js
7286
+ var composer = require_composer();
7287
+ var Document = require_Document();
7288
+ var Schema = require_Schema();
7289
+ var errors = require_errors();
7290
+ var Alias = require_Alias();
7291
+ var identity = require_identity();
7292
+ var Pair = require_Pair();
7293
+ var Scalar = require_Scalar();
7294
+ var YAMLMap = require_YAMLMap();
7295
+ var YAMLSeq = require_YAMLSeq();
7296
+ var cst = require_cst();
7297
+ var lexer = require_lexer();
7298
+ var lineCounter = require_line_counter();
7299
+ var parser = require_parser();
7300
+ var publicApi = require_public_api();
7301
+ var visit = require_visit();
7302
+ var $Composer = composer.Composer;
7303
+ var $Document = Document.Document;
7304
+ var $Schema = Schema.Schema;
7305
+ var $YAMLError = errors.YAMLError;
7306
+ var $YAMLParseError = errors.YAMLParseError;
7307
+ var $YAMLWarning = errors.YAMLWarning;
7308
+ var $Alias = Alias.Alias;
7309
+ var $isAlias = identity.isAlias;
7310
+ var $isCollection = identity.isCollection;
7311
+ var $isDocument = identity.isDocument;
7312
+ var $isMap = identity.isMap;
7313
+ var $isNode = identity.isNode;
7314
+ var $isPair = identity.isPair;
7315
+ var $isScalar = identity.isScalar;
7316
+ var $isSeq = identity.isSeq;
7317
+ var $Pair = Pair.Pair;
7318
+ var $Scalar = Scalar.Scalar;
7319
+ var $YAMLMap = YAMLMap.YAMLMap;
7320
+ var $YAMLSeq = YAMLSeq.YAMLSeq;
7321
+ var $Lexer = lexer.Lexer;
7322
+ var $LineCounter = lineCounter.LineCounter;
7323
+ var $Parser = parser.Parser;
7324
+ var $parse = publicApi.parse;
7325
+ var $parseAllDocuments = publicApi.parseAllDocuments;
7326
+ var $parseDocument = publicApi.parseDocument;
7327
+ var $stringify = publicApi.stringify;
7328
+ var $visit = visit.visit;
7329
+ var $visitAsync = visit.visitAsync;
7330
+
7331
+ // src/utils/result.ts
7332
+ function ok(data) {
7333
+ return { success: true, data };
7334
+ }
7335
+ function err(error) {
7336
+ return { success: false, error };
7337
+ }
7573
7338
 
7574
7339
  // node_modules/zod/v3/external.js
7575
7340
  var exports_external = {};
@@ -11549,7 +11314,14 @@ var CouncilAgentSpecSchema = exports_external.object({
11549
11314
  id: exports_external.string(),
11550
11315
  role: exports_external.string(),
11551
11316
  context: exports_external.enum(["repo-readonly", "prompt-only"]),
11552
- modelHint: exports_external.enum(["strong-reasoning", "security-reasoning", "balanced", "practical-coding", "analytical", "adversarial"]),
11317
+ modelHint: exports_external.enum([
11318
+ "strong-reasoning",
11319
+ "security-reasoning",
11320
+ "balanced",
11321
+ "practical-coding",
11322
+ "analytical",
11323
+ "adversarial"
11324
+ ]),
11553
11325
  focus: exports_external.array(exports_external.string())
11554
11326
  });
11555
11327
  var CouncilSpecSchema = exports_external.object({
@@ -11575,7 +11347,7 @@ var CodeConductorConfigSchema = exports_external.object({
11575
11347
  profile: exports_external.string().optional()
11576
11348
  }),
11577
11349
  defaults: exports_external.object({
11578
- target: exports_external.enum(["opencode", "claude", "codex"]),
11350
+ target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
11579
11351
  overwrite: exports_external.boolean()
11580
11352
  }),
11581
11353
  presets: exports_external.object({
@@ -11589,8 +11361,21 @@ var CodeConductorConfigSchema = exports_external.object({
11589
11361
  secretPatterns: exports_external.array(exports_external.string())
11590
11362
  })
11591
11363
  });
11592
- var RunnerTargetSchema = exports_external.enum(["opencode", "claude", "codex", "all"]);
11593
- var InstallStrategySchema = exports_external.enum(["overwrite", "append", "merge-json", "merge-managed", "skip"]);
11364
+ var RunnerTargetSchema = exports_external.enum([
11365
+ "opencode",
11366
+ "claude",
11367
+ "codex",
11368
+ "gemini",
11369
+ "cursor",
11370
+ "all"
11371
+ ]);
11372
+ var InstallStrategySchema = exports_external.enum([
11373
+ "overwrite",
11374
+ "append",
11375
+ "merge-json",
11376
+ "merge-managed",
11377
+ "skip"
11378
+ ]);
11594
11379
  var ManifestEntrySchema = exports_external.object({
11595
11380
  src: exports_external.string(),
11596
11381
  dest: exports_external.string(),
@@ -11599,16 +11384,20 @@ var ManifestEntrySchema = exports_external.object({
11599
11384
  template: exports_external.boolean().optional()
11600
11385
  });
11601
11386
  var InstallManifestSchema = exports_external.object({
11602
- target: exports_external.enum(["opencode", "claude", "codex"]),
11387
+ target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
11603
11388
  entries: exports_external.array(ManifestEntrySchema)
11604
11389
  });
11390
+ var ToolProviderNamesSchema = exports_external.record(exports_external.string(), exports_external.string());
11605
11391
  var ModelConfigSchema = exports_external.object({
11606
- target: exports_external.enum(["opencode", "claude", "codex"]),
11392
+ target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
11607
11393
  agents: exports_external.record(exports_external.string(), exports_external.object({
11608
- claude: exports_external.string(),
11609
- opencode: exports_external.string(),
11610
- codex: exports_external.string()
11611
- }))
11394
+ claude: exports_external.string().optional(),
11395
+ opencode: exports_external.string().optional(),
11396
+ codex: exports_external.string().optional(),
11397
+ gemini: exports_external.string().optional(),
11398
+ cursor: exports_external.string().optional()
11399
+ })),
11400
+ tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional()
11612
11401
  });
11613
11402
  function validateCouncilSpec(data) {
11614
11403
  return CouncilSpecSchema.parse(data);
@@ -11617,316 +11406,478 @@ function validateConfig(data) {
11617
11406
  return CodeConductorConfigSchema.parse(data);
11618
11407
  }
11619
11408
 
11620
- // src/core/presets/preset-loader.ts
11621
- async function loadPreset(name, projectRoot = process.cwd()) {
11622
- const candidates = [
11623
- resolve4(projectRoot, ".codeconductor", "presets", `${name}.yml`),
11624
- resolve4(SRC_PRESETS_DIR, name, `${name}.yml`)
11625
- ];
11626
- for (const presetPath of candidates) {
11627
- try {
11628
- const content = await readFile2(presetPath, "utf-8");
11629
- const data = $parse(content);
11630
- if (!data)
11631
- continue;
11632
- const validated = validateCouncilSpec(data);
11633
- return ok(validated);
11634
- } catch (error) {
11635
- if (error instanceof ValidationError) {
11636
- return err(error);
11637
- }
11638
- const code = error.code;
11639
- if (code !== "ENOENT") {
11640
- return err(new ValidationError(`Invalid preset at ${presetPath}: ${String(error)}`));
11641
- }
11409
+ // src/core/config/config-loader.ts
11410
+ var CONFIG_FILE = ".codeconductor/config.yml";
11411
+ async function loadConfig(projectRoot) {
11412
+ try {
11413
+ const configPath = resolve(projectRoot, CONFIG_FILE);
11414
+ const content = await readFile(configPath, "utf-8");
11415
+ const data = $parse(content);
11416
+ if (!data) {
11417
+ return err(new ValidationError("Empty config file"));
11642
11418
  }
11419
+ const validated = validateConfig(data);
11420
+ return ok(validated);
11421
+ } catch (error) {
11422
+ if (error instanceof ValidationError) {
11423
+ return err(error);
11424
+ }
11425
+ if (error.code === "ENOENT") {
11426
+ return err(new ValidationError("Config file not found. Run `codeconductor init` first."));
11427
+ }
11428
+ return err(new ValidationError("Failed to load config", error));
11643
11429
  }
11644
- return err(new ValidationError(`Preset not found: ${name}. Run \`codeconductor init\` first.`));
11645
11430
  }
11646
- async function loadCouncilPreset(projectRoot) {
11647
- return loadPreset("council", projectRoot);
11648
- }
11649
-
11650
- // src/core/presets/manifest-loader.ts
11651
- import { readFile as readFile3 } from "node:fs/promises";
11652
- import { join } from "node:path";
11653
- var MANIFESTS_DIR = join(SRC_PRESETS_DIR, "manifests");
11654
- var MODELS_DIR = join(SRC_PRESETS_DIR, "models");
11655
- var PRESETS_DIR = ROOT_PRESETS_DIR;
11656
- async function loadManifest(target) {
11657
- const manifestPath = join(MANIFESTS_DIR, `${target}.yml`);
11658
- const content = await readFile3(manifestPath, "utf-8");
11659
- const data = $parse(content);
11660
- return InstallManifestSchema.parse(data);
11661
- }
11662
- async function loadModelConfig(target) {
11663
- const modelPath = join(MODELS_DIR, `${target}.yml`);
11664
- const content = await readFile3(modelPath, "utf-8");
11665
- const data = $parse(content);
11666
- return ModelConfigSchema.parse(data);
11431
+ async function configExists(projectRoot) {
11432
+ try {
11433
+ const configPath = resolve(projectRoot, CONFIG_FILE);
11434
+ await readFile(configPath, "utf-8");
11435
+ return true;
11436
+ } catch {
11437
+ return false;
11438
+ }
11667
11439
  }
11668
11440
 
11669
- // src/core/presets/file-copier.ts
11670
- import { readFile as readFile4, writeFile as writeFile3, mkdir as mkdir3, readdir, stat } from "node:fs/promises";
11671
- import { resolve as resolve5, join as join2, relative, dirname as dirname2 } from "node:path";
11441
+ // src/core/security/target-compatibility.ts
11442
+ import { readFile as readFile2 } from "node:fs/promises";
11672
11443
 
11673
- // src/core/filesystem/safe-merger.ts
11674
- var MANAGED_BEGIN_MARKER = "<!-- CODECONDUCTOR:BEGIN managed -->";
11675
- var MANAGED_END_MARKER = "<!-- CODECONDUCTOR:END managed -->";
11676
- function mergeManagedBlock(existing, incoming) {
11677
- validateMarkers(incoming, "incoming content");
11678
- if (!existing) {
11679
- return { content: incoming, action: "written" };
11680
- }
11681
- validateMarkers(existing, "existing content");
11682
- const existingBlock = getManagedBlockRange(existing);
11683
- const incomingBlock = getManagedBlockRange(incoming);
11684
- return {
11685
- content: existing.slice(0, existingBlock.start) + incoming.slice(incomingBlock.start, incomingBlock.end) + existing.slice(existingBlock.end),
11686
- action: "merged"
11687
- };
11688
- }
11689
- function validateMarkers(content, label) {
11690
- const beginCount = countOccurrences(content, MANAGED_BEGIN_MARKER);
11691
- const endCount = countOccurrences(content, MANAGED_END_MARKER);
11692
- if (beginCount !== 1 || endCount !== 1) {
11693
- throw new Error(`${label} must contain exactly one managed begin marker and one managed end marker`);
11694
- }
11695
- if (content.indexOf(MANAGED_BEGIN_MARKER) > content.indexOf(MANAGED_END_MARKER)) {
11696
- throw new Error(`${label} has managed markers in the wrong order`);
11697
- }
11698
- }
11699
- function getManagedBlockRange(content) {
11700
- const start = content.indexOf(MANAGED_BEGIN_MARKER);
11701
- const end = content.indexOf(MANAGED_END_MARKER) + MANAGED_END_MARKER.length;
11702
- return { start, end };
11703
- }
11704
- function countOccurrences(content, needle) {
11705
- return content.split(needle).length - 1;
11706
- }
11444
+ // src/core/presets/package-paths.ts
11445
+ import { dirname, resolve as resolve2, sep } from "node:path";
11446
+ import { fileURLToPath } from "node:url";
11447
+ var moduleDir = dirname(fileURLToPath(import.meta.url));
11448
+ var PACKAGE_ROOT = moduleDir.endsWith(`${sep}dist`) ? resolve2(moduleDir, "..") : resolve2(moduleDir, "..", "..", "..");
11449
+ var SRC_PRESETS_DIR = resolve2(PACKAGE_ROOT, "src", "presets");
11450
+ var ROOT_PRESETS_DIR = resolve2(PACKAGE_ROOT, "presets");
11451
+ var POLICY_PATH = resolve2(PACKAGE_ROOT, "policy.yml");
11707
11452
 
11708
- // src/core/presets/file-copier.ts
11709
- async function listFilesRecursive(dir, base = dir) {
11710
- const entries = await readdir(dir, { withFileTypes: true });
11711
- const files = [];
11712
- for (const entry of entries) {
11713
- const full = join2(dir, entry.name);
11714
- if (entry.isDirectory()) {
11715
- files.push(...await listFilesRecursive(full, base));
11716
- } else {
11717
- files.push(relative(base, full));
11453
+ // src/core/security/target-compatibility.ts
11454
+ async function loadTargetSecurityCompatibility() {
11455
+ const policy = $parse(await readFile2(POLICY_PATH, "utf-8"));
11456
+ const targets = [
11457
+ "opencode",
11458
+ "claude",
11459
+ "codex",
11460
+ "gemini",
11461
+ "cursor"
11462
+ ];
11463
+ return targets.map((target) => {
11464
+ const policyTarget = policy.targets?.[target];
11465
+ if (!policyTarget) {
11466
+ return {
11467
+ target,
11468
+ status: "fail",
11469
+ unsupportedRules: [],
11470
+ warnings: [`No security compatibility policy found for ${target}.`]
11471
+ };
11718
11472
  }
11719
- }
11720
- return files;
11473
+ const unsupportedRules = policyTarget.unsupportedRules ?? [];
11474
+ const warnings = policyTarget.warnings ?? [];
11475
+ return {
11476
+ target,
11477
+ status: unsupportedRules.length > 0 || warnings.length > 0 ? "warn" : "pass",
11478
+ unsupportedRules,
11479
+ warnings
11480
+ };
11481
+ });
11721
11482
  }
11722
- async function resolveEntryFiles(entry, presetsDir, baseDir) {
11723
- const srcAbsolute = resolve5(presetsDir, entry.src);
11483
+
11484
+ // src/commands/doctor.command.ts
11485
+ async function doctorCommand(options) {
11486
+ const { projectRoot, output } = options;
11487
+ const checks = [];
11724
11488
  try {
11725
- const s = await stat(srcAbsolute);
11726
- if (s.isDirectory()) {
11727
- const files = await listFilesRecursive(srcAbsolute);
11728
- return files.map((f) => ({
11729
- src: join2(srcAbsolute, f),
11730
- dest: resolve5(baseDir, entry.dest, f)
11731
- }));
11489
+ const hasConfig = await configExists(projectRoot);
11490
+ if (hasConfig) {
11491
+ checks.push({
11492
+ name: "config-exists",
11493
+ status: "pass",
11494
+ message: ".codeconductor/config.yml exists"
11495
+ });
11496
+ } else {
11497
+ checks.push({
11498
+ name: "config-exists",
11499
+ status: "fail",
11500
+ message: ".codeconductor/config.yml not found. Run `codeconductor init` first."
11501
+ });
11502
+ return {
11503
+ code: 4,
11504
+ data: {
11505
+ success: false,
11506
+ command: "doctor",
11507
+ checks
11508
+ }
11509
+ };
11732
11510
  }
11733
- return [{ src: srcAbsolute, dest: resolve5(baseDir, entry.dest) }];
11734
- } catch {
11735
- return [];
11736
- }
11737
- }
11738
- function mergeDeep(target, source) {
11739
- const result = { ...target };
11740
- for (const key of Object.keys(source)) {
11741
- const srcVal = source[key];
11742
- const tgtVal = target[key];
11743
- if (Array.isArray(srcVal) && Array.isArray(tgtVal)) {
11744
- result[key] = [...new Set([...tgtVal, ...srcVal])];
11745
- } else if (srcVal && typeof srcVal === "object" && !Array.isArray(srcVal) && tgtVal && typeof tgtVal === "object" && !Array.isArray(tgtVal)) {
11746
- result[key] = mergeDeep(tgtVal, srcVal);
11511
+ const configResult = await loadConfig(projectRoot);
11512
+ if (configResult.success) {
11513
+ checks.push({
11514
+ name: "config-valid",
11515
+ status: "pass",
11516
+ message: "Config is valid"
11517
+ });
11747
11518
  } else {
11748
- result[key] = srcVal;
11519
+ checks.push({
11520
+ name: "config-valid",
11521
+ status: "fail",
11522
+ message: `Config validation failed: ${configResult.error.message}`
11523
+ });
11524
+ return {
11525
+ code: 1,
11526
+ data: {
11527
+ success: false,
11528
+ command: "doctor",
11529
+ checks
11530
+ }
11531
+ };
11749
11532
  }
11750
- }
11751
- return result;
11752
- }
11753
- function extractAgentRole(filePath) {
11754
- const parts = filePath.replace(/\\/g, "/").split("/");
11755
- const basename2 = parts[parts.length - 1];
11756
- const name = basename2.replace(/\.md$/, "");
11757
- const knownRoles = ["architect", "implementer", "tester", "orchestrator", "reviewer", "docs", "task-coach", "repo-explorer"];
11758
- return knownRoles.includes(name) ? name : null;
11759
- }
11760
- function renderTemplate(content, modelConfig, filePath) {
11761
- const agentRole = extractAgentRole(filePath);
11762
- if (agentRole && modelConfig.agents[agentRole]) {
11763
- const agentModels = modelConfig.agents[agentRole];
11764
- const targetModel = agentModels[modelConfig.target];
11765
- return content.replace(/\{\{MODEL\}\}/g, targetModel).replace(/\{\{MODEL_CLAUDE\}\}/g, agentModels.claude).replace(/\{\{MODEL_OPENCODE\}\}/g, agentModels.opencode).replace(/\{\{MODEL_CODEX\}\}/g, agentModels.codex);
11766
- }
11767
- const knownRoles = ["orchestrator", "task-coach", "architect", "implementer", "tester", "reviewer", "docs", "repo-explorer"];
11768
- let result = content;
11769
- for (const role of knownRoles) {
11770
- if (!modelConfig.agents[role])
11771
- continue;
11772
- const agentModels = modelConfig.agents[role];
11773
- const sectionRegex = new RegExp(`(### ${role}[\\s\\S]*?)(?=### |## |$)`, "gi");
11774
- const sectionMatch = result.match(sectionRegex);
11775
- if (sectionMatch) {
11776
- for (const section of sectionMatch) {
11777
- const renderedSection = section.replace(/\{\{MODEL_CLAUDE\}\}/g, agentModels.claude).replace(/\{\{MODEL_OPENCODE\}\}/g, agentModels.opencode).replace(/\{\{MODEL_CODEX\}\}/g, agentModels.codex);
11778
- result = result.replace(section, renderedSection);
11533
+ const runnerDirs = [".opencode", ".claude", ".codex"];
11534
+ for (const dir of runnerDirs) {
11535
+ try {
11536
+ const { access: access2 } = await import("node:fs/promises");
11537
+ await access2(join(projectRoot, dir));
11538
+ checks.push({
11539
+ name: `dir-${dir}`,
11540
+ status: "pass",
11541
+ message: `${dir}/ exists`
11542
+ });
11543
+ } catch {
11544
+ checks.push({
11545
+ name: `dir-${dir}`,
11546
+ status: "warn",
11547
+ message: `${dir}/ not found (optional)`
11548
+ });
11779
11549
  }
11780
11550
  }
11551
+ const config = configResult.data;
11552
+ if (config.presets.council.enabled) {
11553
+ checks.push({
11554
+ name: "council-enabled",
11555
+ status: "pass",
11556
+ message: `Council preset enabled (v${config.presets.council.version})`
11557
+ });
11558
+ }
11559
+ const securityCompatibility = await loadTargetSecurityCompatibility();
11560
+ for (const compatibility of securityCompatibility) {
11561
+ checks.push({
11562
+ name: `security-${compatibility.target}`,
11563
+ status: compatibility.status,
11564
+ message: compatibility.status === "pass" ? `${compatibility.target} can represent the canonical policy model` : `${compatibility.target} cannot enforce: ${compatibility.unsupportedRules.join(", ") || "see warnings"}`
11565
+ });
11566
+ }
11567
+ const failedCount = checks.filter((c) => c.status === "fail").length;
11568
+ if (failedCount > 0) {
11569
+ return {
11570
+ code: 4,
11571
+ data: {
11572
+ success: false,
11573
+ command: "doctor",
11574
+ checks
11575
+ }
11576
+ };
11577
+ }
11578
+ return {
11579
+ code: 0,
11580
+ data: {
11581
+ success: true,
11582
+ command: "doctor",
11583
+ checks,
11584
+ securityCompatibility
11585
+ }
11586
+ };
11587
+ } catch (error) {
11588
+ return {
11589
+ code: 1,
11590
+ data: {
11591
+ success: false,
11592
+ command: "doctor",
11593
+ errors: [String(error)]
11594
+ }
11595
+ };
11781
11596
  }
11782
- return result;
11783
11597
  }
11784
- async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTemplate, modelConfig) {
11785
- if (strategy === "skip") {
11786
- return { src: srcPath, dest: destPath, action: "skipped", dryRun };
11787
- }
11788
- let content;
11789
- try {
11790
- content = await readFile4(srcPath, "utf-8");
11791
- } catch (e) {
11792
- return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
11793
- }
11794
- const incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath) : content;
11795
- let finalContent = incomingContent;
11796
- let action = "written";
11797
- if (strategy === "append" && !force) {
11798
- let existing = "";
11799
- try {
11800
- existing = await readFile4(destPath, "utf-8");
11801
- } catch {}
11802
- if (existing) {
11803
- finalContent = existing + `
11804
11598
 
11805
- ---
11599
+ // src/commands/init.command.ts
11600
+ import { access as access3, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
11601
+ import { homedir } from "node:os";
11602
+ import { basename, resolve as resolve4 } from "node:path";
11806
11603
 
11807
- ` + incomingContent;
11808
- }
11809
- action = "appended";
11810
- } else if (strategy === "merge-json" && !force) {
11811
- let existing = {};
11812
- try {
11813
- existing = JSON.parse(await readFile4(destPath, "utf-8"));
11814
- } catch {}
11815
- try {
11816
- const incoming = JSON.parse(incomingContent);
11817
- finalContent = JSON.stringify(mergeDeep(existing, incoming), null, 2);
11818
- } catch (e) {
11819
- return { src: srcPath, dest: destPath, action: "error", error: `JSON merge failed: ${e}` };
11604
+ // src/core/config/config-writer.ts
11605
+ import { access as access2, mkdir, writeFile } from "node:fs/promises";
11606
+ import { resolve as resolve3 } from "node:path";
11607
+
11608
+ // src/core/config/codeconductor-config.ts
11609
+ var DEFAULT_CONFIG = {
11610
+ version: "0.2.0",
11611
+ project: {
11612
+ name: "unnamed-project"
11613
+ },
11614
+ defaults: {
11615
+ target: "opencode",
11616
+ overwrite: false
11617
+ },
11618
+ presets: {
11619
+ council: {
11620
+ enabled: true,
11621
+ version: "0.1.0"
11820
11622
  }
11821
- action = "merged";
11822
- } else if (strategy === "merge-managed") {
11823
- let existing = null;
11824
- try {
11825
- existing = await readFile4(destPath, "utf-8");
11826
- } catch {}
11827
- try {
11828
- const merged = mergeManagedBlock(existing, incomingContent);
11829
- finalContent = merged.content;
11830
- action = merged.action;
11831
- } catch (e) {
11832
- return { src: srcPath, dest: destPath, action: "error", error: `Managed merge failed: ${e}` };
11623
+ },
11624
+ safety: {
11625
+ destructiveCommands: ["rm -rf", "drop table", "delete from"],
11626
+ secretPatterns: ["password", "secret", "api_key", "token"]
11627
+ }
11628
+ };
11629
+
11630
+ // src/core/config/config-writer.ts
11631
+ var CONFIG_DIR = ".codeconductor";
11632
+ var CONFIG_FILE2 = "config.yml";
11633
+ async function writeConfig(projectRoot, config = {}, force = false) {
11634
+ try {
11635
+ const configDir = resolve3(projectRoot, CONFIG_DIR);
11636
+ await mkdir(configDir, { recursive: true });
11637
+ const configPath = resolve3(configDir, CONFIG_FILE2);
11638
+ if (!force) {
11639
+ try {
11640
+ await access2(configPath);
11641
+ return ok(undefined);
11642
+ } catch {}
11833
11643
  }
11644
+ const mergedConfig = {
11645
+ ...DEFAULT_CONFIG,
11646
+ ...config,
11647
+ project: {
11648
+ ...DEFAULT_CONFIG.project,
11649
+ ...config.project
11650
+ },
11651
+ defaults: {
11652
+ ...DEFAULT_CONFIG.defaults,
11653
+ ...config.defaults
11654
+ },
11655
+ presets: {
11656
+ ...DEFAULT_CONFIG.presets,
11657
+ ...config.presets
11658
+ },
11659
+ safety: {
11660
+ ...DEFAULT_CONFIG.safety,
11661
+ ...config.safety
11662
+ }
11663
+ };
11664
+ const yamlContent = $stringify(mergedConfig);
11665
+ await writeFile(configPath, yamlContent, "utf-8");
11666
+ return ok(undefined);
11667
+ } catch (error) {
11668
+ return err(new ValidationError("Failed to write config", error));
11834
11669
  }
11835
- if (dryRun) {
11836
- return { src: srcPath, dest: destPath, action, dryRun: true };
11670
+ }
11671
+
11672
+ // src/core/presets/preset-resolver.ts
11673
+ var CURRENT_PRESET_VERSION = "v0.2.0";
11674
+ function resolvePreset(target, profile) {
11675
+ const stack = resolveStack(profile);
11676
+ const architecture = profile.signals.length > 0 ? "single-project" : "unknown";
11677
+ const warnings = [];
11678
+ if (profile.confidence === "low") {
11679
+ warnings.push("Detection confidence is low; review the selected preset before applying it.");
11837
11680
  }
11838
- try {
11839
- await mkdir3(dirname2(destPath), { recursive: true });
11840
- await writeFile3(destPath, finalContent, "utf-8");
11841
- return { src: srcPath, dest: destPath, action };
11842
- } catch (e) {
11843
- return { src: srcPath, dest: destPath, action: "error", error: String(e) };
11681
+ if (stack === "unknown") {
11682
+ warnings.push("No supported stack was detected; using the generic CodeConductor workflow preset.");
11683
+ } else {
11684
+ warnings.push(`${stack} projects currently receive the generic ${target} workflow plus matching skills; stack-specific asset pruning is not implemented yet.`);
11685
+ }
11686
+ if (architecture === "unknown") {
11687
+ warnings.push("Project architecture could not be inferred from available signals.");
11844
11688
  }
11689
+ return {
11690
+ target,
11691
+ stack,
11692
+ architecture,
11693
+ confidence: profile.confidence,
11694
+ presetVersion: CURRENT_PRESET_VERSION,
11695
+ assets: resolveAssets(target),
11696
+ warnings
11697
+ };
11698
+ }
11699
+ function resolveStack(profile) {
11700
+ if (profile.frameworks.includes("spring"))
11701
+ return "spring";
11702
+ if (profile.frameworks.includes("django"))
11703
+ return "django";
11704
+ if (profile.frameworks.includes("astro"))
11705
+ return "astro";
11706
+ if (profile.runtimes.includes("bun"))
11707
+ return "bun";
11708
+ if (profile.runtimes.includes("node"))
11709
+ return "node";
11710
+ return "unknown";
11845
11711
  }
11846
- async function copyFromManifest(manifest, presetsDir, baseDir, isGlobal, dryRun, force, modelConfig = null) {
11847
- const results = [];
11848
- for (const entry of manifest.entries) {
11849
- const strategy = isGlobal && entry.globalStrategy ? entry.globalStrategy : entry.strategy;
11850
- const files = await resolveEntryFiles(entry, presetsDir, baseDir);
11851
- const isTemplate = entry.template === true;
11852
- for (const { src, dest } of files) {
11853
- results.push(await applySingleFile(src, dest, strategy, force, dryRun, isTemplate, modelConfig));
11854
- }
11712
+ function resolveAssets(target) {
11713
+ switch (target) {
11714
+ case "opencode":
11715
+ return ["opencode.jsonc", "agents", "commands", "prompts/v0.2.0", "skills"];
11716
+ case "claude":
11717
+ return ["CLAUDE.md", "settings.json", "commands", "skills", "agents", "prompts/v0.2.0"];
11718
+ case "codex":
11719
+ return ["AGENTS.md", "skills", "prompts/v0.2.0"];
11720
+ case "gemini":
11721
+ return ["agents", "prompts/v0.2.0"];
11722
+ case "cursor":
11723
+ return ["agents", "prompts/v0.2.0"];
11855
11724
  }
11856
- return results;
11857
11725
  }
11858
11726
 
11859
- // src/core/filesystem/file-writer.ts
11860
- init_safety();
11861
- import { writeFile as writeFile4, mkdir as mkdir4, access as access4 } from "node:fs/promises";
11862
- import { dirname as dirname3 } from "node:path";
11863
- async function writeGeneratedFiles(files, options) {
11864
- const results = [];
11865
- for (const file of files) {
11866
- if (!validateWritePath(file.path)) {
11867
- results.push({
11868
- path: file.path,
11869
- success: false,
11870
- error: "Protected path"
11871
- });
11872
- continue;
11727
+ // src/commands/init.command.ts
11728
+ async function initCommand(options) {
11729
+ const { projectRoot, dryRun, force, global: isGlobal, output } = options;
11730
+ const baseDir = isGlobal ? homedir() : projectRoot;
11731
+ try {
11732
+ let profile = null;
11733
+ if (!isGlobal) {
11734
+ profile = await detectProject(projectRoot);
11735
+ if (profile.signals.length === 0) {
11736
+ return {
11737
+ code: 3,
11738
+ data: {
11739
+ success: false,
11740
+ command: "init",
11741
+ errors: ["No project signals detected. Project may be empty or unsupported."]
11742
+ }
11743
+ };
11744
+ }
11873
11745
  }
11874
- if (options.dryRun) {
11875
- results.push({
11876
- path: file.path,
11877
- success: true
11878
- });
11879
- continue;
11746
+ const config = profile ? {
11747
+ project: {
11748
+ name: basename(projectRoot) || "unnamed-project",
11749
+ profile: profile.runtimes[0] || "unknown"
11750
+ },
11751
+ defaults: {
11752
+ target: "opencode",
11753
+ overwrite: force
11754
+ }
11755
+ } : {
11756
+ project: {
11757
+ name: "global",
11758
+ profile: "global"
11759
+ },
11760
+ defaults: {
11761
+ target: "opencode",
11762
+ overwrite: force
11763
+ }
11764
+ };
11765
+ const presetResolution = profile ? resolvePreset("opencode", profile) : null;
11766
+ const wouldCreate = [".codeconductor/config.yml"];
11767
+ const presetsToCopy = await resolvePresetsToCopy();
11768
+ for (const p of presetsToCopy) {
11769
+ wouldCreate.push(`.codeconductor/presets/${p.name}`);
11880
11770
  }
11881
- if (!options.force) {
11882
- try {
11883
- await access4(file.path);
11884
- results.push({
11885
- path: file.path,
11771
+ if (dryRun) {
11772
+ return {
11773
+ code: 0,
11774
+ data: {
11775
+ success: true,
11776
+ command: "init",
11777
+ message: "Dry run - would create config",
11778
+ wouldCreate,
11779
+ ...profile ? {
11780
+ detected: {
11781
+ languages: profile.languages,
11782
+ runtimes: profile.runtimes,
11783
+ frameworks: profile.frameworks,
11784
+ signals: profile.signals,
11785
+ confidence: profile.confidence
11786
+ },
11787
+ presetResolution
11788
+ } : { global: true }
11789
+ }
11790
+ };
11791
+ }
11792
+ const result = await writeConfig(baseDir, config, force);
11793
+ if (!result.success) {
11794
+ return {
11795
+ code: 1,
11796
+ data: {
11886
11797
  success: false,
11887
- error: "File exists, use --force to overwrite"
11888
- });
11889
- continue;
11890
- } catch {}
11798
+ command: "init",
11799
+ errors: [result.error.message]
11800
+ }
11801
+ };
11891
11802
  }
11892
- try {
11893
- const dir = dirname3(file.path);
11894
- await mkdir4(dir, { recursive: true });
11895
- await writeFile4(file.path, file.content, "utf-8");
11896
- results.push({
11897
- path: file.path,
11898
- success: true
11899
- });
11900
- } catch (error) {
11901
- results.push({
11902
- path: file.path,
11803
+ const copiedPresets = await copyPresets(baseDir, presetsToCopy, force);
11804
+ return {
11805
+ code: 0,
11806
+ data: {
11807
+ success: true,
11808
+ command: "init",
11809
+ created: [".codeconductor/config.yml", ...copiedPresets],
11810
+ ...profile ? {
11811
+ detected: {
11812
+ languages: profile.languages,
11813
+ runtimes: profile.runtimes,
11814
+ frameworks: profile.frameworks,
11815
+ signals: profile.signals,
11816
+ confidence: profile.confidence
11817
+ },
11818
+ presetResolution
11819
+ } : { global: true }
11820
+ }
11821
+ };
11822
+ } catch (error) {
11823
+ return {
11824
+ code: 1,
11825
+ data: {
11903
11826
  success: false,
11904
- error: String(error)
11905
- });
11906
- }
11827
+ command: "init",
11828
+ errors: [String(error)]
11829
+ }
11830
+ };
11907
11831
  }
11908
- return results;
11909
11832
  }
11910
-
11911
- // src/core/runner/runner-target.ts
11912
- var RUNNER_TARGETS = ["opencode", "claude", "codex", "all"];
11913
- var INDIVIDUAL_TARGETS = ["opencode", "claude", "codex"];
11914
- function isRunnerTarget(value) {
11915
- return RUNNER_TARGETS.includes(value);
11833
+ async function resolvePresetsToCopy() {
11834
+ const sources = [];
11835
+ const bundledCouncil = resolve4(SRC_PRESETS_DIR, "council", "council.yml");
11836
+ if (await fileExists2(bundledCouncil)) {
11837
+ sources.push({ name: "council.yml", sourcePath: bundledCouncil });
11838
+ }
11839
+ const policyPath = POLICY_PATH;
11840
+ if (await fileExists2(policyPath)) {
11841
+ sources.push({ name: "policy.yml", sourcePath: policyPath });
11842
+ }
11843
+ return sources;
11916
11844
  }
11917
- function parseRunnerTarget(value) {
11918
- if (!isRunnerTarget(value)) {
11919
- throw new Error(`Invalid runner target: ${value}. Valid targets: ${RUNNER_TARGETS.join(", ")}`);
11845
+ async function copyPresets(baseDir, presets, force) {
11846
+ const presetsDir = resolve4(baseDir, ".codeconductor", "presets");
11847
+ await mkdir2(presetsDir, { recursive: true });
11848
+ const copied = [];
11849
+ for (const preset of presets) {
11850
+ const destPath = resolve4(presetsDir, preset.name);
11851
+ if (!force && await fileExists2(destPath)) {
11852
+ continue;
11853
+ }
11854
+ try {
11855
+ const content = await readFile3(preset.sourcePath, "utf-8");
11856
+ await writeFile2(destPath, content, "utf-8");
11857
+ copied.push(`.codeconductor/presets/${preset.name}`);
11858
+ } catch (err2) {
11859
+ const code = err2.code;
11860
+ if (code !== "ENOENT") {
11861
+ process.stderr.write(`warn: could not copy ${preset.name}: ${String(err2)}
11862
+ `);
11863
+ }
11864
+ }
11920
11865
  }
11921
- return value;
11866
+ return copied;
11922
11867
  }
11923
- function getIndividualTargets(target) {
11924
- if (target === "all") {
11925
- return [...INDIVIDUAL_TARGETS];
11868
+ async function fileExists2(path) {
11869
+ try {
11870
+ await access3(path);
11871
+ return true;
11872
+ } catch {
11873
+ return false;
11926
11874
  }
11927
- return [target];
11928
11875
  }
11929
11876
 
11877
+ // src/commands/install.command.ts
11878
+ import { homedir as homedir2 } from "node:os";
11879
+ import { resolve as resolve7 } from "node:path";
11880
+
11930
11881
  // src/domain/council/council-agent.ts
11931
11882
  function getResponsibilities(agentId) {
11932
11883
  const responsibilities = {
@@ -11960,24 +11911,173 @@ function getResponsibilities(agentId) {
11960
11911
  function generateAgentContent(agent) {
11961
11912
  return `# ${agent.role} Agent
11962
11913
 
11963
- ## Role
11964
- ${agent.role}
11914
+ ## Role
11915
+ ${agent.role}
11916
+
11917
+ ## Context
11918
+ ${agent.context === "repo-readonly" ? "Can read repository but cannot modify files" : "Only receives prompts, no direct repository access"}
11919
+
11920
+ ## Model Hint
11921
+ ${agent.modelHint}
11922
+
11923
+ ## Focus Areas
11924
+ ${agent.focus.map((f) => `- ${f}`).join(`
11925
+ `)}
11926
+
11927
+ ## Responsibilities
11928
+ ${getResponsibilities(agent.id)}
11929
+ `;
11930
+ }
11931
+
11932
+ // src/adapters/claude/claude-council-generator.ts
11933
+ function generateClaudeFiles(spec) {
11934
+ const files = [];
11935
+ files.push({
11936
+ path: ".claude/skills/council/SKILL.md",
11937
+ content: generateCouncilSkill(spec),
11938
+ overwrite: false
11939
+ });
11940
+ for (const agent of spec.agents) {
11941
+ files.push({
11942
+ path: `.claude/agents/council-${agent.id}.md`,
11943
+ content: generateAgentContent(agent),
11944
+ overwrite: false
11945
+ });
11946
+ }
11947
+ return files;
11948
+ }
11949
+ function generateCouncilSkill(spec) {
11950
+ return `# Council Skill
11951
+
11952
+ ## Description
11953
+ ${spec.description}
11954
+
11955
+ ## Version
11956
+ ${spec.version}
11957
+
11958
+ ## Agents
11959
+ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).join(`
11960
+ `)}
11961
+
11962
+ ## Usage
11963
+ Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
11965
11964
 
11966
11965
  ## Context
11967
- ${agent.context === "repo-readonly" ? "Can read repository but cannot modify files" : "Only receives prompts, no direct repository access"}
11966
+ ${spec.outputContract}
11967
+ `;
11968
+ }
11968
11969
 
11969
- ## Model Hint
11970
- ${agent.modelHint}
11970
+ // src/adapters/claude/claude-installer.ts
11971
+ class ClaudeInstaller {
11972
+ name = "claude";
11973
+ target = "claude";
11974
+ spec = null;
11975
+ setSpec(spec) {
11976
+ this.spec = spec;
11977
+ }
11978
+ async generate() {
11979
+ if (!this.spec) {
11980
+ throw new Error("Council spec not set");
11981
+ }
11982
+ return generateClaudeFiles(this.spec);
11983
+ }
11984
+ async isAvailable() {
11985
+ return true;
11986
+ }
11987
+ }
11988
+ function createClaudeInstaller(spec) {
11989
+ const installer = new ClaudeInstaller;
11990
+ installer.setSpec(spec);
11991
+ return installer;
11992
+ }
11971
11993
 
11972
- ## Focus Areas
11973
- ${agent.focus.map((f) => `- ${f}`).join(`
11994
+ // src/adapters/codex/codex-council-generator.ts
11995
+ function generateCodexFiles(spec) {
11996
+ const files = [];
11997
+ files.push({
11998
+ path: ".codex/config.toml",
11999
+ content: generateCodexConfig(spec),
12000
+ overwrite: false
12001
+ });
12002
+ for (const agent of spec.agents) {
12003
+ files.push({
12004
+ path: `.codex/agents/council_${agent.id}.toml`,
12005
+ content: generateCodexAgent(agent),
12006
+ overwrite: true
12007
+ });
12008
+ }
12009
+ files.push({
12010
+ path: ".codex/skills/council/SKILL.md",
12011
+ content: generateCodexSkill(spec),
12012
+ overwrite: true
12013
+ });
12014
+ return files;
12015
+ }
12016
+ function generateCodexConfig(spec) {
12017
+ const agentTables = spec.agents.map((agent) => {
12018
+ const focusAreas = agent.focus.join(", ");
12019
+ return `
12020
+ [agents.${agent.id}]
12021
+ description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
12022
+ nickname_candidates = ["${agent.role}", "Council ${agent.role}"]`;
12023
+ }).join(`
12024
+ `);
12025
+ return `# Codex Council Configuration
12026
+
12027
+ [project]
12028
+ name = "council"
12029
+ version = "${spec.version}"
12030
+ ${agentTables}
12031
+ `;
12032
+ }
12033
+ function generateCodexAgent(agent) {
12034
+ const focusAreas = agent.focus.join(", ");
12035
+ return `name = "${agent.role}"
12036
+ description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
12037
+ nickname_candidates = ["${agent.role}", "Council ${agent.role}"]
12038
+ developer_instructions = "You are the ${agent.role} council agent. Your focus areas are: ${focusAreas}. Context: ${agent.context}. Apply ${agent.modelHint} reasoning to your analysis."
12039
+ `;
12040
+ }
12041
+ function generateCodexSkill(spec) {
12042
+ return `---
12043
+ name: council
12044
+ description: ${spec.description}
12045
+ version: ${spec.version}
12046
+ ---
12047
+
12048
+ ## Agents
12049
+ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).join(`
11974
12050
  `)}
11975
12051
 
11976
- ## Responsibilities
11977
- ${getResponsibilities(agent.id)}
12052
+ ## Usage
12053
+ Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
11978
12054
  `;
11979
12055
  }
11980
12056
 
12057
+ // src/adapters/codex/codex-installer.ts
12058
+ class CodexInstaller {
12059
+ name = "codex";
12060
+ target = "codex";
12061
+ spec = null;
12062
+ setSpec(spec) {
12063
+ this.spec = spec;
12064
+ }
12065
+ async generate() {
12066
+ if (!this.spec) {
12067
+ throw new Error("Council spec not set");
12068
+ }
12069
+ return generateCodexFiles(this.spec);
12070
+ }
12071
+ async isAvailable() {
12072
+ return true;
12073
+ }
12074
+ }
12075
+ function createCodexInstaller(spec) {
12076
+ const installer = new CodexInstaller;
12077
+ installer.setSpec(spec);
12078
+ return installer;
12079
+ }
12080
+
11981
12081
  // src/adapters/opencode/opencode-council-generator.ts
11982
12082
  function generateOpenCodeFiles(spec) {
11983
12083
  const files = [];
@@ -12046,169 +12146,380 @@ class OpenCodeInstaller {
12046
12146
  setSpec(spec) {
12047
12147
  this.spec = spec;
12048
12148
  }
12049
- async generate() {
12050
- if (!this.spec) {
12051
- throw new Error("Council spec not set");
12149
+ async generate() {
12150
+ if (!this.spec) {
12151
+ throw new Error("Council spec not set");
12152
+ }
12153
+ return generateOpenCodeFiles(this.spec);
12154
+ }
12155
+ async isAvailable() {
12156
+ return true;
12157
+ }
12158
+ }
12159
+ function createOpenCodeInstaller(spec) {
12160
+ const installer = new OpenCodeInstaller;
12161
+ installer.setSpec(spec);
12162
+ return installer;
12163
+ }
12164
+
12165
+ // src/core/filesystem/file-writer.ts
12166
+ import { access as access4, mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
12167
+ import { dirname as dirname2 } from "node:path";
12168
+ init_safety();
12169
+ async function writeGeneratedFiles(files, options) {
12170
+ const results = [];
12171
+ for (const file of files) {
12172
+ if (!validateWritePath(file.path)) {
12173
+ results.push({
12174
+ path: file.path,
12175
+ success: false,
12176
+ error: "Protected path"
12177
+ });
12178
+ continue;
12179
+ }
12180
+ if (options.dryRun) {
12181
+ results.push({
12182
+ path: file.path,
12183
+ success: true
12184
+ });
12185
+ continue;
12186
+ }
12187
+ if (!options.force) {
12188
+ try {
12189
+ await access4(file.path);
12190
+ results.push({
12191
+ path: file.path,
12192
+ success: false,
12193
+ error: "File exists, use --force to overwrite"
12194
+ });
12195
+ continue;
12196
+ } catch {}
12197
+ }
12198
+ try {
12199
+ const dir = dirname2(file.path);
12200
+ await mkdir3(dir, { recursive: true });
12201
+ await writeFile3(file.path, file.content, "utf-8");
12202
+ results.push({
12203
+ path: file.path,
12204
+ success: true
12205
+ });
12206
+ } catch (error) {
12207
+ results.push({
12208
+ path: file.path,
12209
+ success: false,
12210
+ error: String(error)
12211
+ });
12212
+ }
12213
+ }
12214
+ return results;
12215
+ }
12216
+
12217
+ // src/core/presets/file-copier.ts
12218
+ import { mkdir as mkdir4, readdir, readFile as readFile4, stat, writeFile as writeFile4 } from "node:fs/promises";
12219
+ import { dirname as dirname3, join as join2, relative, resolve as resolve5 } from "node:path";
12220
+
12221
+ // src/core/filesystem/safe-merger.ts
12222
+ var MANAGED_BEGIN_MARKER = "<!-- CODECONDUCTOR:BEGIN managed -->";
12223
+ var MANAGED_END_MARKER = "<!-- CODECONDUCTOR:END managed -->";
12224
+ function mergeManagedBlock(existing, incoming) {
12225
+ validateMarkers(incoming, "incoming content");
12226
+ if (!existing) {
12227
+ return { content: incoming, action: "written" };
12228
+ }
12229
+ validateMarkers(existing, "existing content");
12230
+ const existingBlock = getManagedBlockRange(existing);
12231
+ const incomingBlock = getManagedBlockRange(incoming);
12232
+ return {
12233
+ content: existing.slice(0, existingBlock.start) + incoming.slice(incomingBlock.start, incomingBlock.end) + existing.slice(existingBlock.end),
12234
+ action: "merged"
12235
+ };
12236
+ }
12237
+ function validateMarkers(content, label) {
12238
+ const beginCount = countOccurrences(content, MANAGED_BEGIN_MARKER);
12239
+ const endCount = countOccurrences(content, MANAGED_END_MARKER);
12240
+ if (beginCount !== 1 || endCount !== 1) {
12241
+ throw new Error(`${label} must contain exactly one managed begin marker and one managed end marker`);
12242
+ }
12243
+ if (content.indexOf(MANAGED_BEGIN_MARKER) > content.indexOf(MANAGED_END_MARKER)) {
12244
+ throw new Error(`${label} has managed markers in the wrong order`);
12245
+ }
12246
+ }
12247
+ function getManagedBlockRange(content) {
12248
+ const start = content.indexOf(MANAGED_BEGIN_MARKER);
12249
+ const end = content.indexOf(MANAGED_END_MARKER) + MANAGED_END_MARKER.length;
12250
+ return { start, end };
12251
+ }
12252
+ function countOccurrences(content, needle) {
12253
+ return content.split(needle).length - 1;
12254
+ }
12255
+
12256
+ // src/core/presets/file-copier.ts
12257
+ async function listFilesRecursive(dir, base = dir) {
12258
+ const entries = await readdir(dir, { withFileTypes: true });
12259
+ const files = [];
12260
+ for (const entry of entries) {
12261
+ const full = join2(dir, entry.name);
12262
+ if (entry.isDirectory()) {
12263
+ files.push(...await listFilesRecursive(full, base));
12264
+ } else {
12265
+ files.push(relative(base, full));
12266
+ }
12267
+ }
12268
+ return files;
12269
+ }
12270
+ async function resolveEntryFiles(entry, presetsDir, baseDir) {
12271
+ const srcAbsolute = resolve5(presetsDir, entry.src);
12272
+ try {
12273
+ const s = await stat(srcAbsolute);
12274
+ if (s.isDirectory()) {
12275
+ const files = await listFilesRecursive(srcAbsolute);
12276
+ return files.map((f) => ({
12277
+ src: join2(srcAbsolute, f),
12278
+ dest: resolve5(baseDir, entry.dest, f)
12279
+ }));
12280
+ }
12281
+ return [{ src: srcAbsolute, dest: resolve5(baseDir, entry.dest) }];
12282
+ } catch {
12283
+ return [];
12284
+ }
12285
+ }
12286
+ function mergeDeep(target, source) {
12287
+ const result = { ...target };
12288
+ for (const key of Object.keys(source)) {
12289
+ const srcVal = source[key];
12290
+ const tgtVal = target[key];
12291
+ if (Array.isArray(srcVal) && Array.isArray(tgtVal)) {
12292
+ result[key] = [...new Set([...tgtVal, ...srcVal])];
12293
+ } else if (srcVal && typeof srcVal === "object" && !Array.isArray(srcVal) && tgtVal && typeof tgtVal === "object" && !Array.isArray(tgtVal)) {
12294
+ result[key] = mergeDeep(tgtVal, srcVal);
12295
+ } else {
12296
+ result[key] = srcVal;
12297
+ }
12298
+ }
12299
+ return result;
12300
+ }
12301
+ function extractAgentRole(filePath) {
12302
+ const parts = filePath.replace(/\\/g, "/").split("/");
12303
+ const basename2 = parts[parts.length - 1];
12304
+ const name = basename2.replace(/\.md$/, "");
12305
+ const knownRoles = [
12306
+ "architect",
12307
+ "implementer",
12308
+ "tester",
12309
+ "orchestrator",
12310
+ "reviewer",
12311
+ "docs",
12312
+ "task-coach",
12313
+ "repo-explorer"
12314
+ ];
12315
+ return knownRoles.includes(name) ? name : null;
12316
+ }
12317
+ function renderTemplate(content, modelConfig, filePath) {
12318
+ const agentRole = extractAgentRole(filePath);
12319
+ if (agentRole && modelConfig.agents[agentRole]) {
12320
+ const agentModels = modelConfig.agents[agentRole];
12321
+ const targetModel = agentModels[modelConfig.target];
12322
+ let result2 = content.replace(/\{\{MODEL\}\}/g, targetModel ?? "").replace(/\{\{MODEL_CLAUDE\}\}/g, agentModels.claude ?? "").replace(/\{\{MODEL_OPENCODE\}\}/g, agentModels.opencode ?? "").replace(/\{\{MODEL_CODEX\}\}/g, agentModels.codex ?? "").replace(/\{\{MODEL_GEMINI\}\}/g, agentModels.gemini ?? "").replace(/\{\{MODEL_CURSOR\}\}/g, agentModels.cursor ?? "");
12323
+ if (modelConfig.tools) {
12324
+ result2 = substituteToolNames(result2, modelConfig);
12325
+ }
12326
+ return result2;
12327
+ }
12328
+ const knownRoles = [
12329
+ "orchestrator",
12330
+ "task-coach",
12331
+ "architect",
12332
+ "implementer",
12333
+ "tester",
12334
+ "reviewer",
12335
+ "docs",
12336
+ "repo-explorer"
12337
+ ];
12338
+ let result = content;
12339
+ for (const role of knownRoles) {
12340
+ if (!modelConfig.agents[role])
12341
+ continue;
12342
+ const agentModels = modelConfig.agents[role];
12343
+ const sectionRegex = new RegExp(`(### ${role}[\\s\\S]*?)(?=### |## |$)`, "gi");
12344
+ const sectionMatch = result.match(sectionRegex);
12345
+ if (sectionMatch) {
12346
+ for (const section of sectionMatch) {
12347
+ const renderedSection = section.replace(/\{\{MODEL_CLAUDE\}\}/g, agentModels.claude ?? "").replace(/\{\{MODEL_OPENCODE\}\}/g, agentModels.opencode ?? "").replace(/\{\{MODEL_CODEX\}\}/g, agentModels.codex ?? "").replace(/\{\{MODEL_GEMINI\}\}/g, agentModels.gemini ?? "").replace(/\{\{MODEL_CURSOR\}\}/g, agentModels.cursor ?? "");
12348
+ result = result.replace(section, renderedSection);
12349
+ }
12052
12350
  }
12053
- return generateOpenCodeFiles(this.spec);
12054
12351
  }
12055
- async isAvailable() {
12056
- return true;
12352
+ if (modelConfig.tools) {
12353
+ result = substituteToolNames(result, modelConfig);
12057
12354
  }
12355
+ return result;
12058
12356
  }
12059
- function createOpenCodeInstaller(spec) {
12060
- const installer = new OpenCodeInstaller;
12061
- installer.setSpec(spec);
12062
- return installer;
12063
- }
12064
-
12065
- // src/adapters/claude/claude-council-generator.ts
12066
- function generateClaudeFiles(spec) {
12067
- const files = [];
12068
- files.push({
12069
- path: ".claude/skills/council/SKILL.md",
12070
- content: generateCouncilSkill(spec),
12071
- overwrite: false
12072
- });
12073
- for (const agent of spec.agents) {
12074
- files.push({
12075
- path: `.claude/agents/council-${agent.id}.md`,
12076
- content: generateAgentContent(agent),
12077
- overwrite: false
12357
+ function substituteToolNames(content, modelConfig) {
12358
+ if (!modelConfig.tools)
12359
+ return content;
12360
+ const target = modelConfig.target;
12361
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
12362
+ if (!fmMatch)
12363
+ return content;
12364
+ const frontmatter = fmMatch[1];
12365
+ const updatedFrontmatter = frontmatter.replace(/^tools:\s*(.+)$/m, (_match, toolsLine) => {
12366
+ const baseNames = toolsLine.split(",").map((t) => t.trim());
12367
+ const mappedNames = baseNames.map((baseName) => {
12368
+ const toolMapping = modelConfig.tools?.[baseName];
12369
+ if (toolMapping && toolMapping[target]) {
12370
+ return toolMapping[target];
12371
+ }
12372
+ return baseName;
12078
12373
  });
12079
- }
12080
- return files;
12374
+ return `tools: ${mappedNames.join(", ")}`;
12375
+ });
12376
+ return content.replace(fmMatch[0], `---
12377
+ ${updatedFrontmatter}
12378
+ ---`);
12081
12379
  }
12082
- function generateCouncilSkill(spec) {
12083
- return `# Council Skill
12084
-
12085
- ## Description
12086
- ${spec.description}
12087
-
12088
- ## Version
12089
- ${spec.version}
12090
-
12091
- ## Agents
12092
- ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).join(`
12093
- `)}
12094
-
12095
- ## Usage
12096
- Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
12380
+ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTemplate, modelConfig) {
12381
+ if (strategy === "skip") {
12382
+ return { src: srcPath, dest: destPath, action: "skipped", dryRun };
12383
+ }
12384
+ let content;
12385
+ try {
12386
+ content = await readFile4(srcPath, "utf-8");
12387
+ } catch (e) {
12388
+ return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
12389
+ }
12390
+ const incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath) : content;
12391
+ let finalContent = incomingContent;
12392
+ let action = "written";
12393
+ if (strategy === "append" && !force) {
12394
+ let existing = "";
12395
+ try {
12396
+ existing = await readFile4(destPath, "utf-8");
12397
+ } catch {}
12398
+ if (existing) {
12399
+ finalContent = existing + `
12097
12400
 
12098
- ## Context
12099
- ${spec.outputContract}
12100
- `;
12101
- }
12401
+ ---
12102
12402
 
12103
- // src/adapters/claude/claude-installer.ts
12104
- class ClaudeInstaller {
12105
- name = "claude";
12106
- target = "claude";
12107
- spec = null;
12108
- setSpec(spec) {
12109
- this.spec = spec;
12110
- }
12111
- async generate() {
12112
- if (!this.spec) {
12113
- throw new Error("Council spec not set");
12403
+ ` + incomingContent;
12404
+ }
12405
+ action = "appended";
12406
+ } else if (strategy === "merge-json" && !force) {
12407
+ let existing = {};
12408
+ try {
12409
+ existing = JSON.parse(await readFile4(destPath, "utf-8"));
12410
+ } catch {}
12411
+ try {
12412
+ const incoming = JSON.parse(incomingContent);
12413
+ finalContent = JSON.stringify(mergeDeep(existing, incoming), null, 2);
12414
+ } catch (e) {
12415
+ return { src: srcPath, dest: destPath, action: "error", error: `JSON merge failed: ${e}` };
12416
+ }
12417
+ action = "merged";
12418
+ } else if (strategy === "merge-managed") {
12419
+ let existing = null;
12420
+ try {
12421
+ existing = await readFile4(destPath, "utf-8");
12422
+ } catch {}
12423
+ try {
12424
+ const merged = mergeManagedBlock(existing, incomingContent);
12425
+ finalContent = merged.content;
12426
+ action = merged.action;
12427
+ } catch (e) {
12428
+ return { src: srcPath, dest: destPath, action: "error", error: `Managed merge failed: ${e}` };
12114
12429
  }
12115
- return generateClaudeFiles(this.spec);
12116
12430
  }
12117
- async isAvailable() {
12118
- return true;
12431
+ if (dryRun) {
12432
+ return { src: srcPath, dest: destPath, action, dryRun: true };
12433
+ }
12434
+ try {
12435
+ await mkdir4(dirname3(destPath), { recursive: true });
12436
+ await writeFile4(destPath, finalContent, "utf-8");
12437
+ return { src: srcPath, dest: destPath, action };
12438
+ } catch (e) {
12439
+ return { src: srcPath, dest: destPath, action: "error", error: String(e) };
12119
12440
  }
12120
12441
  }
12121
- function createClaudeInstaller(spec) {
12122
- const installer = new ClaudeInstaller;
12123
- installer.setSpec(spec);
12124
- return installer;
12125
- }
12126
-
12127
- // src/adapters/codex/codex-council-generator.ts
12128
- function generateCodexFiles(spec) {
12129
- const files = [];
12130
- files.push({
12131
- path: ".codex/config.toml",
12132
- content: generateCodexConfig(spec),
12133
- overwrite: false
12134
- });
12135
- for (const agent of spec.agents) {
12136
- files.push({
12137
- path: `.codex/agents/council_${agent.id}.toml`,
12138
- content: generateCodexAgent(agent),
12139
- overwrite: true
12140
- });
12442
+ async function copyFromManifest(manifest, presetsDir, baseDir, isGlobal, dryRun, force, modelConfig = null) {
12443
+ const results = [];
12444
+ for (const entry of manifest.entries) {
12445
+ const strategy = isGlobal && entry.globalStrategy ? entry.globalStrategy : entry.strategy;
12446
+ const files = await resolveEntryFiles(entry, presetsDir, baseDir);
12447
+ const isTemplate = entry.template === true;
12448
+ for (const { src, dest } of files) {
12449
+ results.push(await applySingleFile(src, dest, strategy, force, dryRun, isTemplate, modelConfig));
12450
+ }
12141
12451
  }
12142
- files.push({
12143
- path: ".codex/skills/council/SKILL.md",
12144
- content: generateCodexSkill(spec),
12145
- overwrite: true
12146
- });
12147
- return files;
12452
+ return results;
12148
12453
  }
12149
- function generateCodexConfig(spec) {
12150
- const agentTables = spec.agents.map((agent) => {
12151
- const focusAreas = agent.focus.join(", ");
12152
- return `
12153
- [agents.${agent.id}]
12154
- description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
12155
- nickname_candidates = ["${agent.role}", "Council ${agent.role}"]`;
12156
- }).join(`
12157
- `);
12158
- return `# Codex Council Configuration
12159
12454
 
12160
- [project]
12161
- name = "council"
12162
- version = "${spec.version}"
12163
- ${agentTables}
12164
- `;
12165
- }
12166
- function generateCodexAgent(agent) {
12167
- const focusAreas = agent.focus.join(", ");
12168
- return `name = "${agent.role}"
12169
- description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
12170
- nickname_candidates = ["${agent.role}", "Council ${agent.role}"]
12171
- developer_instructions = "You are the ${agent.role} council agent. Your focus areas are: ${focusAreas}. Context: ${agent.context}. Apply ${agent.modelHint} reasoning to your analysis."
12172
- `;
12455
+ // src/core/presets/manifest-loader.ts
12456
+ import { readFile as readFile5 } from "node:fs/promises";
12457
+ import { join as join3 } from "node:path";
12458
+ var MANIFESTS_DIR = join3(SRC_PRESETS_DIR, "manifests");
12459
+ var MODELS_DIR = join3(SRC_PRESETS_DIR, "models");
12460
+ var PRESETS_DIR = ROOT_PRESETS_DIR;
12461
+ async function loadManifest(target) {
12462
+ const manifestPath = join3(MANIFESTS_DIR, `${target}.yml`);
12463
+ const content = await readFile5(manifestPath, "utf-8");
12464
+ const data = $parse(content);
12465
+ return InstallManifestSchema.parse(data);
12173
12466
  }
12174
- function generateCodexSkill(spec) {
12175
- return `---
12176
- name: council
12177
- description: ${spec.description}
12178
- version: ${spec.version}
12179
- ---
12180
-
12181
- ## Agents
12182
- ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).join(`
12183
- `)}
12184
-
12185
- ## Usage
12186
- Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
12187
- `;
12467
+ async function loadModelConfig(target) {
12468
+ const modelPath = join3(MODELS_DIR, `${target}.yml`);
12469
+ const content = await readFile5(modelPath, "utf-8");
12470
+ const data = $parse(content);
12471
+ return ModelConfigSchema.parse(data);
12188
12472
  }
12189
12473
 
12190
- // src/adapters/codex/codex-installer.ts
12191
- class CodexInstaller {
12192
- name = "codex";
12193
- target = "codex";
12194
- spec = null;
12195
- setSpec(spec) {
12196
- this.spec = spec;
12197
- }
12198
- async generate() {
12199
- if (!this.spec) {
12200
- throw new Error("Council spec not set");
12474
+ // src/core/presets/preset-loader.ts
12475
+ import { readFile as readFile6 } from "node:fs/promises";
12476
+ import { resolve as resolve6 } from "node:path";
12477
+ async function loadPreset(name, projectRoot = process.cwd()) {
12478
+ const candidates = [
12479
+ resolve6(projectRoot, ".codeconductor", "presets", `${name}.yml`),
12480
+ resolve6(SRC_PRESETS_DIR, name, `${name}.yml`)
12481
+ ];
12482
+ for (const presetPath of candidates) {
12483
+ try {
12484
+ const content = await readFile6(presetPath, "utf-8");
12485
+ const data = $parse(content);
12486
+ if (!data)
12487
+ continue;
12488
+ const validated = validateCouncilSpec(data);
12489
+ return ok(validated);
12490
+ } catch (error) {
12491
+ if (error instanceof ValidationError) {
12492
+ return err(error);
12493
+ }
12494
+ const code = error.code;
12495
+ if (code !== "ENOENT") {
12496
+ return err(new ValidationError(`Invalid preset at ${presetPath}: ${String(error)}`));
12497
+ }
12201
12498
  }
12202
- return generateCodexFiles(this.spec);
12203
12499
  }
12204
- async isAvailable() {
12205
- return true;
12500
+ return err(new ValidationError(`Preset not found: ${name}. Run \`codeconductor init\` first.`));
12501
+ }
12502
+ async function loadCouncilPreset(projectRoot) {
12503
+ return loadPreset("council", projectRoot);
12504
+ }
12505
+
12506
+ // src/core/runner/runner-target.ts
12507
+ var RUNNER_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "all"];
12508
+ var INDIVIDUAL_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor"];
12509
+ function isRunnerTarget(value) {
12510
+ return RUNNER_TARGETS.includes(value);
12511
+ }
12512
+ function parseRunnerTarget(value) {
12513
+ if (!isRunnerTarget(value)) {
12514
+ throw new Error(`Invalid runner target: ${value}. Valid targets: ${RUNNER_TARGETS.join(", ")}`);
12206
12515
  }
12516
+ return value;
12207
12517
  }
12208
- function createCodexInstaller(spec) {
12209
- const installer = new CodexInstaller;
12210
- installer.setSpec(spec);
12211
- return installer;
12518
+ function getIndividualTargets(target) {
12519
+ if (target === "all") {
12520
+ return [...INDIVIDUAL_TARGETS];
12521
+ }
12522
+ return [target];
12212
12523
  }
12213
12524
 
12214
12525
  // src/commands/install.command.ts
@@ -12250,7 +12561,7 @@ async function installCommand(options) {
12250
12561
  const generatedFiles = await installer.generate();
12251
12562
  const resolvedFiles = generatedFiles.map((f) => ({
12252
12563
  ...f,
12253
- path: resolve6(baseDir, f.path)
12564
+ path: resolve7(baseDir, f.path)
12254
12565
  }));
12255
12566
  const results = await writeGeneratedFiles(resolvedFiles, writeOptions);
12256
12567
  for (const result of results) {
@@ -12350,184 +12661,6 @@ async function installPresetCommand(options) {
12350
12661
  }
12351
12662
  }
12352
12663
 
12353
- // src/commands/doctor.command.ts
12354
- import { join as join3 } from "node:path";
12355
-
12356
- // src/core/config/config-loader.ts
12357
- import { readFile as readFile5 } from "node:fs/promises";
12358
- import { resolve as resolve7 } from "node:path";
12359
- var CONFIG_FILE2 = ".codeconductor/config.yml";
12360
- async function loadConfig(projectRoot) {
12361
- try {
12362
- const configPath = resolve7(projectRoot, CONFIG_FILE2);
12363
- const content = await readFile5(configPath, "utf-8");
12364
- const data = $parse(content);
12365
- if (!data) {
12366
- return err(new ValidationError("Empty config file"));
12367
- }
12368
- const validated = validateConfig(data);
12369
- return ok(validated);
12370
- } catch (error) {
12371
- if (error instanceof ValidationError) {
12372
- return err(error);
12373
- }
12374
- if (error.code === "ENOENT") {
12375
- return err(new ValidationError("Config file not found. Run `codeconductor init` first."));
12376
- }
12377
- return err(new ValidationError("Failed to load config", error));
12378
- }
12379
- }
12380
- async function configExists(projectRoot) {
12381
- try {
12382
- const configPath = resolve7(projectRoot, CONFIG_FILE2);
12383
- await readFile5(configPath, "utf-8");
12384
- return true;
12385
- } catch {
12386
- return false;
12387
- }
12388
- }
12389
-
12390
- // src/core/security/target-compatibility.ts
12391
- import { readFile as readFile6 } from "node:fs/promises";
12392
- async function loadTargetSecurityCompatibility() {
12393
- const policy = $parse(await readFile6(POLICY_PATH, "utf-8"));
12394
- const targets = ["opencode", "claude", "codex"];
12395
- return targets.map((target) => {
12396
- const policyTarget = policy.targets?.[target];
12397
- if (!policyTarget) {
12398
- return {
12399
- target,
12400
- status: "fail",
12401
- unsupportedRules: [],
12402
- warnings: [`No security compatibility policy found for ${target}.`]
12403
- };
12404
- }
12405
- const unsupportedRules = policyTarget.unsupportedRules ?? [];
12406
- const warnings = policyTarget.warnings ?? [];
12407
- return {
12408
- target,
12409
- status: unsupportedRules.length > 0 || warnings.length > 0 ? "warn" : "pass",
12410
- unsupportedRules,
12411
- warnings
12412
- };
12413
- });
12414
- }
12415
-
12416
- // src/commands/doctor.command.ts
12417
- async function doctorCommand(options) {
12418
- const { projectRoot, output } = options;
12419
- const checks = [];
12420
- try {
12421
- const hasConfig = await configExists(projectRoot);
12422
- if (hasConfig) {
12423
- checks.push({
12424
- name: "config-exists",
12425
- status: "pass",
12426
- message: ".codeconductor/config.yml exists"
12427
- });
12428
- } else {
12429
- checks.push({
12430
- name: "config-exists",
12431
- status: "fail",
12432
- message: ".codeconductor/config.yml not found. Run `codeconductor init` first."
12433
- });
12434
- return {
12435
- code: 4,
12436
- data: {
12437
- success: false,
12438
- command: "doctor",
12439
- checks
12440
- }
12441
- };
12442
- }
12443
- const configResult = await loadConfig(projectRoot);
12444
- if (configResult.success) {
12445
- checks.push({
12446
- name: "config-valid",
12447
- status: "pass",
12448
- message: "Config is valid"
12449
- });
12450
- } else {
12451
- checks.push({
12452
- name: "config-valid",
12453
- status: "fail",
12454
- message: `Config validation failed: ${configResult.error.message}`
12455
- });
12456
- return {
12457
- code: 1,
12458
- data: {
12459
- success: false,
12460
- command: "doctor",
12461
- checks
12462
- }
12463
- };
12464
- }
12465
- const runnerDirs = [".opencode", ".claude", ".codex"];
12466
- for (const dir of runnerDirs) {
12467
- try {
12468
- const { access: access5 } = await import("node:fs/promises");
12469
- await access5(join3(projectRoot, dir));
12470
- checks.push({
12471
- name: `dir-${dir}`,
12472
- status: "pass",
12473
- message: `${dir}/ exists`
12474
- });
12475
- } catch {
12476
- checks.push({
12477
- name: `dir-${dir}`,
12478
- status: "warn",
12479
- message: `${dir}/ not found (optional)`
12480
- });
12481
- }
12482
- }
12483
- const config = configResult.data;
12484
- if (config.presets.council.enabled) {
12485
- checks.push({
12486
- name: "council-enabled",
12487
- status: "pass",
12488
- message: `Council preset enabled (v${config.presets.council.version})`
12489
- });
12490
- }
12491
- const securityCompatibility = await loadTargetSecurityCompatibility();
12492
- for (const compatibility of securityCompatibility) {
12493
- checks.push({
12494
- name: `security-${compatibility.target}`,
12495
- status: compatibility.status,
12496
- message: compatibility.status === "pass" ? `${compatibility.target} can represent the canonical policy model` : `${compatibility.target} cannot enforce: ${compatibility.unsupportedRules.join(", ") || "see warnings"}`
12497
- });
12498
- }
12499
- const failedCount = checks.filter((c) => c.status === "fail").length;
12500
- if (failedCount > 0) {
12501
- return {
12502
- code: 4,
12503
- data: {
12504
- success: false,
12505
- command: "doctor",
12506
- checks
12507
- }
12508
- };
12509
- }
12510
- return {
12511
- code: 0,
12512
- data: {
12513
- success: true,
12514
- command: "doctor",
12515
- checks,
12516
- securityCompatibility
12517
- }
12518
- };
12519
- } catch (error) {
12520
- return {
12521
- code: 1,
12522
- data: {
12523
- success: false,
12524
- command: "doctor",
12525
- errors: [String(error)]
12526
- }
12527
- };
12528
- }
12529
- }
12530
-
12531
12664
  // src/commands/update.command.ts
12532
12665
  async function updateCommand(options) {
12533
12666
  const { dryRun, force, output, projectRoot } = options;
@@ -12652,59 +12785,6 @@ async function updateCommand(options) {
12652
12785
  };
12653
12786
  }
12654
12787
  }
12655
- // package.json
12656
- var package_default = {
12657
- name: "cc-codeconductor",
12658
- version: "0.2.5",
12659
- description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
12660
- keywords: [
12661
- "ai",
12662
- "agents",
12663
- "orchestration",
12664
- "workflow",
12665
- "opencode",
12666
- "claude",
12667
- "codex"
12668
- ],
12669
- license: "MIT",
12670
- files: [
12671
- "dist/index.js",
12672
- "policy.yml",
12673
- "presets/",
12674
- "src/presets/"
12675
- ],
12676
- repository: {
12677
- type: "git",
12678
- url: "https://github.com/lgzarturo/codeconductor"
12679
- },
12680
- engines: {
12681
- node: ">=20.11"
12682
- },
12683
- type: "module",
12684
- bin: {
12685
- codeconductor: "./dist/index.js"
12686
- },
12687
- scripts: {
12688
- dev: "bun run src/cli/main.ts",
12689
- build: "bun build src/cli/main.ts --target=node --outfile=dist/index.js",
12690
- test: "bun test",
12691
- typecheck: "tsc --noEmit",
12692
- "check:prompt-changelog": "bun run scripts/check-prompt-changelog.ts",
12693
- "release:patch": "bash release.sh patch",
12694
- "release:minor": "bash release.sh minor",
12695
- "release:major": "bash release.sh major",
12696
- "release:notes": "git-cliff --config .cliff.toml --output CHANGELOG.md",
12697
- release: "bash release.sh"
12698
- },
12699
- dependencies: {
12700
- zod: "^3.23.8",
12701
- yaml: "^2.4.5"
12702
- },
12703
- devDependencies: {
12704
- "@types/bun": "^1.1.8",
12705
- typescript: "^5.5.3"
12706
- }
12707
- };
12708
12788
 
12709
12789
  // src/cli/router.ts
12710
12790
  function parseArgs(args) {
@@ -12770,7 +12850,7 @@ function getVersion() {
12770
12850
  function getHelp() {
12771
12851
  return `CodeConductor CLI v${package_default.version}
12772
12852
 
12773
- Usage: codeconductor <command> [options]
12853
+ Usage: npx cc-codeconductor <command> [options]
12774
12854
 
12775
12855
  Commands:
12776
12856
  init Initialize CodeConductor in a project
@@ -12789,20 +12869,20 @@ Options:
12789
12869
  --output, -o Output mode: human or json
12790
12870
 
12791
12871
  Examples:
12792
- codeconductor init
12793
- codeconductor init --global
12794
- codeconductor detect
12795
- codeconductor install preset --target opencode
12796
- codeconductor install preset --target claude
12797
- codeconductor install preset --target codex
12798
- codeconductor install preset --target all
12799
- codeconductor install preset --target claude --global
12800
- codeconductor install council --target opencode
12801
- codeconductor install council --target claude
12802
- codeconductor install council --target codex
12803
- codeconductor install council --target all
12804
- codeconductor doctor
12805
- codeconductor update --dry-run
12872
+ npx cc-codeconductor init
12873
+ npx cc-codeconductor init --global
12874
+ npx cc-codeconductor detect
12875
+ npx cc-codeconductor install preset --target opencode
12876
+ npx cc-codeconductor install preset --target claude
12877
+ npx cc-codeconductor install preset --target codex
12878
+ npx cc-codeconductor install preset --target all
12879
+ npx cc-codeconductor install preset --target claude --global
12880
+ npx cc-codeconductor install council --target opencode
12881
+ npx cc-codeconductor install council --target claude
12882
+ npx cc-codeconductor install council --target codex
12883
+ npx cc-codeconductor install council --target all
12884
+ npx cc-codeconductor doctor
12885
+ npx cc-codeconductor update --dry-run
12806
12886
  `;
12807
12887
  }
12808
12888
  async function routeCommand(args, projectRoot) {
@@ -12909,7 +12989,13 @@ async function runCli(args) {
12909
12989
  });
12910
12990
  } else if ("fileResults" in data) {
12911
12991
  const fileResults = data.fileResults;
12912
- const actionIcon = { written: "✓", appended: "→", merged: "~", skipped: "∅", error: "✗" };
12992
+ const actionIcon = {
12993
+ written: "✓",
12994
+ appended: "→",
12995
+ merged: "~",
12996
+ skipped: "∅",
12997
+ error: "✗"
12998
+ };
12913
12999
  fileResults.forEach((r) => {
12914
13000
  if (r.action === "skipped")
12915
13001
  return;