cc-codeconductor 0.2.5 → 0.2.6

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 +1150 -1065
  3. package/package.json +4 -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,61 @@ 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.6",
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
+ codeconductor: "./dist/index.js"
7077
+ },
7078
+ scripts: {
7079
+ dev: "bun run src/cli/main.ts",
7080
+ build: "bun build src/cli/main.ts --target=node --outfile=dist/index.js",
7081
+ test: "bun test",
7082
+ typecheck: "tsc --noEmit",
7083
+ "check:prompt-changelog": "bun run scripts/check-prompt-changelog.ts",
7084
+ "release:patch": "bash release.sh patch",
7085
+ "release:minor": "bash release.sh minor",
7086
+ "release:major": "bash release.sh major",
7087
+ "release:notes": "git-cliff --config .cliff.toml --output CHANGELOG.md",
7088
+ release: "bash release.sh"
7089
+ },
7090
+ dependencies: {
7091
+ zod: "^3.23.8",
7092
+ yaml: "^2.4.5"
7093
+ },
7094
+ devDependencies: {
7095
+ "@types/bun": "^1.1.8",
7096
+ typescript: "^5.5.3"
7176
7097
  }
7177
- }
7098
+ };
7178
7099
 
7179
7100
  // src/core/detection/project-detector.ts
7180
7101
  async function detectProject(rootDir) {
@@ -7297,216 +7218,6 @@ async function detectAstro(rootDir) {
7297
7218
  return signals;
7298
7219
  }
7299
7220
 
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
7221
  // src/commands/detect.command.ts
7511
7222
  async function detectCommand(options) {
7512
7223
  const { projectRoot, output } = options;
@@ -7563,13 +7274,66 @@ function getRecommendedPresets(profile) {
7563
7274
  return presets;
7564
7275
  }
7565
7276
 
7566
- // src/commands/install.command.ts
7567
- import { resolve as resolve6 } from "node:path";
7568
- import { homedir as homedir2 } from "node:os";
7277
+ // src/commands/doctor.command.ts
7278
+ import { join } from "node:path";
7569
7279
 
7570
- // src/core/presets/preset-loader.ts
7571
- import { readFile as readFile2 } from "node:fs/promises";
7572
- import { resolve as resolve4 } from "node:path";
7280
+ // src/core/config/config-loader.ts
7281
+ import { readFile } from "node:fs/promises";
7282
+ import { resolve } from "node:path";
7283
+
7284
+ // node_modules/yaml/dist/index.js
7285
+ var composer = require_composer();
7286
+ var Document = require_Document();
7287
+ var Schema = require_Schema();
7288
+ var errors = require_errors();
7289
+ var Alias = require_Alias();
7290
+ var identity = require_identity();
7291
+ var Pair = require_Pair();
7292
+ var Scalar = require_Scalar();
7293
+ var YAMLMap = require_YAMLMap();
7294
+ var YAMLSeq = require_YAMLSeq();
7295
+ var cst = require_cst();
7296
+ var lexer = require_lexer();
7297
+ var lineCounter = require_line_counter();
7298
+ var parser = require_parser();
7299
+ var publicApi = require_public_api();
7300
+ var visit = require_visit();
7301
+ var $Composer = composer.Composer;
7302
+ var $Document = Document.Document;
7303
+ var $Schema = Schema.Schema;
7304
+ var $YAMLError = errors.YAMLError;
7305
+ var $YAMLParseError = errors.YAMLParseError;
7306
+ var $YAMLWarning = errors.YAMLWarning;
7307
+ var $Alias = Alias.Alias;
7308
+ var $isAlias = identity.isAlias;
7309
+ var $isCollection = identity.isCollection;
7310
+ var $isDocument = identity.isDocument;
7311
+ var $isMap = identity.isMap;
7312
+ var $isNode = identity.isNode;
7313
+ var $isPair = identity.isPair;
7314
+ var $isScalar = identity.isScalar;
7315
+ var $isSeq = identity.isSeq;
7316
+ var $Pair = Pair.Pair;
7317
+ var $Scalar = Scalar.Scalar;
7318
+ var $YAMLMap = YAMLMap.YAMLMap;
7319
+ var $YAMLSeq = YAMLSeq.YAMLSeq;
7320
+ var $Lexer = lexer.Lexer;
7321
+ var $LineCounter = lineCounter.LineCounter;
7322
+ var $Parser = parser.Parser;
7323
+ var $parse = publicApi.parse;
7324
+ var $parseAllDocuments = publicApi.parseAllDocuments;
7325
+ var $parseDocument = publicApi.parseDocument;
7326
+ var $stringify = publicApi.stringify;
7327
+ var $visit = visit.visit;
7328
+ var $visitAsync = visit.visitAsync;
7329
+
7330
+ // src/utils/result.ts
7331
+ function ok(data) {
7332
+ return { success: true, data };
7333
+ }
7334
+ function err(error) {
7335
+ return { success: false, error };
7336
+ }
7573
7337
 
7574
7338
  // node_modules/zod/v3/external.js
7575
7339
  var exports_external = {};
@@ -11549,7 +11313,14 @@ var CouncilAgentSpecSchema = exports_external.object({
11549
11313
  id: exports_external.string(),
11550
11314
  role: exports_external.string(),
11551
11315
  context: exports_external.enum(["repo-readonly", "prompt-only"]),
11552
- modelHint: exports_external.enum(["strong-reasoning", "security-reasoning", "balanced", "practical-coding", "analytical", "adversarial"]),
11316
+ modelHint: exports_external.enum([
11317
+ "strong-reasoning",
11318
+ "security-reasoning",
11319
+ "balanced",
11320
+ "practical-coding",
11321
+ "analytical",
11322
+ "adversarial"
11323
+ ]),
11553
11324
  focus: exports_external.array(exports_external.string())
11554
11325
  });
11555
11326
  var CouncilSpecSchema = exports_external.object({
@@ -11575,7 +11346,7 @@ var CodeConductorConfigSchema = exports_external.object({
11575
11346
  profile: exports_external.string().optional()
11576
11347
  }),
11577
11348
  defaults: exports_external.object({
11578
- target: exports_external.enum(["opencode", "claude", "codex"]),
11349
+ target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
11579
11350
  overwrite: exports_external.boolean()
11580
11351
  }),
11581
11352
  presets: exports_external.object({
@@ -11589,8 +11360,21 @@ var CodeConductorConfigSchema = exports_external.object({
11589
11360
  secretPatterns: exports_external.array(exports_external.string())
11590
11361
  })
11591
11362
  });
11592
- var RunnerTargetSchema = exports_external.enum(["opencode", "claude", "codex", "all"]);
11593
- var InstallStrategySchema = exports_external.enum(["overwrite", "append", "merge-json", "merge-managed", "skip"]);
11363
+ var RunnerTargetSchema = exports_external.enum([
11364
+ "opencode",
11365
+ "claude",
11366
+ "codex",
11367
+ "gemini",
11368
+ "cursor",
11369
+ "all"
11370
+ ]);
11371
+ var InstallStrategySchema = exports_external.enum([
11372
+ "overwrite",
11373
+ "append",
11374
+ "merge-json",
11375
+ "merge-managed",
11376
+ "skip"
11377
+ ]);
11594
11378
  var ManifestEntrySchema = exports_external.object({
11595
11379
  src: exports_external.string(),
11596
11380
  dest: exports_external.string(),
@@ -11599,16 +11383,20 @@ var ManifestEntrySchema = exports_external.object({
11599
11383
  template: exports_external.boolean().optional()
11600
11384
  });
11601
11385
  var InstallManifestSchema = exports_external.object({
11602
- target: exports_external.enum(["opencode", "claude", "codex"]),
11386
+ target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
11603
11387
  entries: exports_external.array(ManifestEntrySchema)
11604
11388
  });
11389
+ var ToolProviderNamesSchema = exports_external.record(exports_external.string(), exports_external.string());
11605
11390
  var ModelConfigSchema = exports_external.object({
11606
- target: exports_external.enum(["opencode", "claude", "codex"]),
11391
+ target: exports_external.enum(["opencode", "claude", "codex", "gemini", "cursor"]),
11607
11392
  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
- }))
11393
+ claude: exports_external.string().optional(),
11394
+ opencode: exports_external.string().optional(),
11395
+ codex: exports_external.string().optional(),
11396
+ gemini: exports_external.string().optional(),
11397
+ cursor: exports_external.string().optional()
11398
+ })),
11399
+ tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional()
11612
11400
  });
11613
11401
  function validateCouncilSpec(data) {
11614
11402
  return CouncilSpecSchema.parse(data);
@@ -11617,316 +11405,478 @@ function validateConfig(data) {
11617
11405
  return CodeConductorConfigSchema.parse(data);
11618
11406
  }
11619
11407
 
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
- }
11408
+ // src/core/config/config-loader.ts
11409
+ var CONFIG_FILE = ".codeconductor/config.yml";
11410
+ async function loadConfig(projectRoot) {
11411
+ try {
11412
+ const configPath = resolve(projectRoot, CONFIG_FILE);
11413
+ const content = await readFile(configPath, "utf-8");
11414
+ const data = $parse(content);
11415
+ if (!data) {
11416
+ return err(new ValidationError("Empty config file"));
11642
11417
  }
11418
+ const validated = validateConfig(data);
11419
+ return ok(validated);
11420
+ } catch (error) {
11421
+ if (error instanceof ValidationError) {
11422
+ return err(error);
11423
+ }
11424
+ if (error.code === "ENOENT") {
11425
+ return err(new ValidationError("Config file not found. Run `codeconductor init` first."));
11426
+ }
11427
+ return err(new ValidationError("Failed to load config", error));
11643
11428
  }
11644
- return err(new ValidationError(`Preset not found: ${name}. Run \`codeconductor init\` first.`));
11645
11429
  }
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);
11430
+ async function configExists(projectRoot) {
11431
+ try {
11432
+ const configPath = resolve(projectRoot, CONFIG_FILE);
11433
+ await readFile(configPath, "utf-8");
11434
+ return true;
11435
+ } catch {
11436
+ return false;
11437
+ }
11667
11438
  }
11668
11439
 
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";
11440
+ // src/core/security/target-compatibility.ts
11441
+ import { readFile as readFile2 } from "node:fs/promises";
11672
11442
 
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
- }
11443
+ // src/core/presets/package-paths.ts
11444
+ import { dirname, resolve as resolve2, sep } from "node:path";
11445
+ import { fileURLToPath } from "node:url";
11446
+ var moduleDir = dirname(fileURLToPath(import.meta.url));
11447
+ var PACKAGE_ROOT = moduleDir.endsWith(`${sep}dist`) ? resolve2(moduleDir, "..") : resolve2(moduleDir, "..", "..", "..");
11448
+ var SRC_PRESETS_DIR = resolve2(PACKAGE_ROOT, "src", "presets");
11449
+ var ROOT_PRESETS_DIR = resolve2(PACKAGE_ROOT, "presets");
11450
+ var POLICY_PATH = resolve2(PACKAGE_ROOT, "policy.yml");
11707
11451
 
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));
11452
+ // src/core/security/target-compatibility.ts
11453
+ async function loadTargetSecurityCompatibility() {
11454
+ const policy = $parse(await readFile2(POLICY_PATH, "utf-8"));
11455
+ const targets = [
11456
+ "opencode",
11457
+ "claude",
11458
+ "codex",
11459
+ "gemini",
11460
+ "cursor"
11461
+ ];
11462
+ return targets.map((target) => {
11463
+ const policyTarget = policy.targets?.[target];
11464
+ if (!policyTarget) {
11465
+ return {
11466
+ target,
11467
+ status: "fail",
11468
+ unsupportedRules: [],
11469
+ warnings: [`No security compatibility policy found for ${target}.`]
11470
+ };
11718
11471
  }
11719
- }
11720
- return files;
11472
+ const unsupportedRules = policyTarget.unsupportedRules ?? [];
11473
+ const warnings = policyTarget.warnings ?? [];
11474
+ return {
11475
+ target,
11476
+ status: unsupportedRules.length > 0 || warnings.length > 0 ? "warn" : "pass",
11477
+ unsupportedRules,
11478
+ warnings
11479
+ };
11480
+ });
11721
11481
  }
11722
- async function resolveEntryFiles(entry, presetsDir, baseDir) {
11723
- const srcAbsolute = resolve5(presetsDir, entry.src);
11482
+
11483
+ // src/commands/doctor.command.ts
11484
+ async function doctorCommand(options) {
11485
+ const { projectRoot, output } = options;
11486
+ const checks = [];
11724
11487
  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
- }));
11488
+ const hasConfig = await configExists(projectRoot);
11489
+ if (hasConfig) {
11490
+ checks.push({
11491
+ name: "config-exists",
11492
+ status: "pass",
11493
+ message: ".codeconductor/config.yml exists"
11494
+ });
11495
+ } else {
11496
+ checks.push({
11497
+ name: "config-exists",
11498
+ status: "fail",
11499
+ message: ".codeconductor/config.yml not found. Run `codeconductor init` first."
11500
+ });
11501
+ return {
11502
+ code: 4,
11503
+ data: {
11504
+ success: false,
11505
+ command: "doctor",
11506
+ checks
11507
+ }
11508
+ };
11732
11509
  }
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);
11510
+ const configResult = await loadConfig(projectRoot);
11511
+ if (configResult.success) {
11512
+ checks.push({
11513
+ name: "config-valid",
11514
+ status: "pass",
11515
+ message: "Config is valid"
11516
+ });
11747
11517
  } else {
11748
- result[key] = srcVal;
11518
+ checks.push({
11519
+ name: "config-valid",
11520
+ status: "fail",
11521
+ message: `Config validation failed: ${configResult.error.message}`
11522
+ });
11523
+ return {
11524
+ code: 1,
11525
+ data: {
11526
+ success: false,
11527
+ command: "doctor",
11528
+ checks
11529
+ }
11530
+ };
11749
11531
  }
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);
11532
+ const runnerDirs = [".opencode", ".claude", ".codex"];
11533
+ for (const dir of runnerDirs) {
11534
+ try {
11535
+ const { access: access2 } = await import("node:fs/promises");
11536
+ await access2(join(projectRoot, dir));
11537
+ checks.push({
11538
+ name: `dir-${dir}`,
11539
+ status: "pass",
11540
+ message: `${dir}/ exists`
11541
+ });
11542
+ } catch {
11543
+ checks.push({
11544
+ name: `dir-${dir}`,
11545
+ status: "warn",
11546
+ message: `${dir}/ not found (optional)`
11547
+ });
11779
11548
  }
11780
11549
  }
11550
+ const config = configResult.data;
11551
+ if (config.presets.council.enabled) {
11552
+ checks.push({
11553
+ name: "council-enabled",
11554
+ status: "pass",
11555
+ message: `Council preset enabled (v${config.presets.council.version})`
11556
+ });
11557
+ }
11558
+ const securityCompatibility = await loadTargetSecurityCompatibility();
11559
+ for (const compatibility of securityCompatibility) {
11560
+ checks.push({
11561
+ name: `security-${compatibility.target}`,
11562
+ status: compatibility.status,
11563
+ message: compatibility.status === "pass" ? `${compatibility.target} can represent the canonical policy model` : `${compatibility.target} cannot enforce: ${compatibility.unsupportedRules.join(", ") || "see warnings"}`
11564
+ });
11565
+ }
11566
+ const failedCount = checks.filter((c) => c.status === "fail").length;
11567
+ if (failedCount > 0) {
11568
+ return {
11569
+ code: 4,
11570
+ data: {
11571
+ success: false,
11572
+ command: "doctor",
11573
+ checks
11574
+ }
11575
+ };
11576
+ }
11577
+ return {
11578
+ code: 0,
11579
+ data: {
11580
+ success: true,
11581
+ command: "doctor",
11582
+ checks,
11583
+ securityCompatibility
11584
+ }
11585
+ };
11586
+ } catch (error) {
11587
+ return {
11588
+ code: 1,
11589
+ data: {
11590
+ success: false,
11591
+ command: "doctor",
11592
+ errors: [String(error)]
11593
+ }
11594
+ };
11781
11595
  }
11782
- return result;
11783
11596
  }
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
11597
 
11805
- ---
11598
+ // src/commands/init.command.ts
11599
+ import { access as access3, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
11600
+ import { homedir } from "node:os";
11601
+ import { basename, resolve as resolve4 } from "node:path";
11806
11602
 
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}` };
11603
+ // src/core/config/config-writer.ts
11604
+ import { access as access2, mkdir, writeFile } from "node:fs/promises";
11605
+ import { resolve as resolve3 } from "node:path";
11606
+
11607
+ // src/core/config/codeconductor-config.ts
11608
+ var DEFAULT_CONFIG = {
11609
+ version: "0.2.0",
11610
+ project: {
11611
+ name: "unnamed-project"
11612
+ },
11613
+ defaults: {
11614
+ target: "opencode",
11615
+ overwrite: false
11616
+ },
11617
+ presets: {
11618
+ council: {
11619
+ enabled: true,
11620
+ version: "0.1.0"
11820
11621
  }
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}` };
11622
+ },
11623
+ safety: {
11624
+ destructiveCommands: ["rm -rf", "drop table", "delete from"],
11625
+ secretPatterns: ["password", "secret", "api_key", "token"]
11626
+ }
11627
+ };
11628
+
11629
+ // src/core/config/config-writer.ts
11630
+ var CONFIG_DIR = ".codeconductor";
11631
+ var CONFIG_FILE2 = "config.yml";
11632
+ async function writeConfig(projectRoot, config = {}, force = false) {
11633
+ try {
11634
+ const configDir = resolve3(projectRoot, CONFIG_DIR);
11635
+ await mkdir(configDir, { recursive: true });
11636
+ const configPath = resolve3(configDir, CONFIG_FILE2);
11637
+ if (!force) {
11638
+ try {
11639
+ await access2(configPath);
11640
+ return ok(undefined);
11641
+ } catch {}
11833
11642
  }
11643
+ const mergedConfig = {
11644
+ ...DEFAULT_CONFIG,
11645
+ ...config,
11646
+ project: {
11647
+ ...DEFAULT_CONFIG.project,
11648
+ ...config.project
11649
+ },
11650
+ defaults: {
11651
+ ...DEFAULT_CONFIG.defaults,
11652
+ ...config.defaults
11653
+ },
11654
+ presets: {
11655
+ ...DEFAULT_CONFIG.presets,
11656
+ ...config.presets
11657
+ },
11658
+ safety: {
11659
+ ...DEFAULT_CONFIG.safety,
11660
+ ...config.safety
11661
+ }
11662
+ };
11663
+ const yamlContent = $stringify(mergedConfig);
11664
+ await writeFile(configPath, yamlContent, "utf-8");
11665
+ return ok(undefined);
11666
+ } catch (error) {
11667
+ return err(new ValidationError("Failed to write config", error));
11834
11668
  }
11835
- if (dryRun) {
11836
- return { src: srcPath, dest: destPath, action, dryRun: true };
11669
+ }
11670
+
11671
+ // src/core/presets/preset-resolver.ts
11672
+ var CURRENT_PRESET_VERSION = "v0.2.0";
11673
+ function resolvePreset(target, profile) {
11674
+ const stack = resolveStack(profile);
11675
+ const architecture = profile.signals.length > 0 ? "single-project" : "unknown";
11676
+ const warnings = [];
11677
+ if (profile.confidence === "low") {
11678
+ warnings.push("Detection confidence is low; review the selected preset before applying it.");
11837
11679
  }
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) };
11680
+ if (stack === "unknown") {
11681
+ warnings.push("No supported stack was detected; using the generic CodeConductor workflow preset.");
11682
+ } else {
11683
+ warnings.push(`${stack} projects currently receive the generic ${target} workflow plus matching skills; stack-specific asset pruning is not implemented yet.`);
11684
+ }
11685
+ if (architecture === "unknown") {
11686
+ warnings.push("Project architecture could not be inferred from available signals.");
11844
11687
  }
11688
+ return {
11689
+ target,
11690
+ stack,
11691
+ architecture,
11692
+ confidence: profile.confidence,
11693
+ presetVersion: CURRENT_PRESET_VERSION,
11694
+ assets: resolveAssets(target),
11695
+ warnings
11696
+ };
11697
+ }
11698
+ function resolveStack(profile) {
11699
+ if (profile.frameworks.includes("spring"))
11700
+ return "spring";
11701
+ if (profile.frameworks.includes("django"))
11702
+ return "django";
11703
+ if (profile.frameworks.includes("astro"))
11704
+ return "astro";
11705
+ if (profile.runtimes.includes("bun"))
11706
+ return "bun";
11707
+ if (profile.runtimes.includes("node"))
11708
+ return "node";
11709
+ return "unknown";
11845
11710
  }
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
- }
11711
+ function resolveAssets(target) {
11712
+ switch (target) {
11713
+ case "opencode":
11714
+ return ["opencode.jsonc", "agents", "commands", "prompts/v0.2.0", "skills"];
11715
+ case "claude":
11716
+ return ["CLAUDE.md", "settings.json", "commands", "skills", "agents", "prompts/v0.2.0"];
11717
+ case "codex":
11718
+ return ["AGENTS.md", "skills", "prompts/v0.2.0"];
11719
+ case "gemini":
11720
+ return ["agents", "prompts/v0.2.0"];
11721
+ case "cursor":
11722
+ return ["agents", "prompts/v0.2.0"];
11855
11723
  }
11856
- return results;
11857
11724
  }
11858
11725
 
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;
11726
+ // src/commands/init.command.ts
11727
+ async function initCommand(options) {
11728
+ const { projectRoot, dryRun, force, global: isGlobal, output } = options;
11729
+ const baseDir = isGlobal ? homedir() : projectRoot;
11730
+ try {
11731
+ let profile = null;
11732
+ if (!isGlobal) {
11733
+ profile = await detectProject(projectRoot);
11734
+ if (profile.signals.length === 0) {
11735
+ return {
11736
+ code: 3,
11737
+ data: {
11738
+ success: false,
11739
+ command: "init",
11740
+ errors: ["No project signals detected. Project may be empty or unsupported."]
11741
+ }
11742
+ };
11743
+ }
11873
11744
  }
11874
- if (options.dryRun) {
11875
- results.push({
11876
- path: file.path,
11877
- success: true
11878
- });
11879
- continue;
11745
+ const config = profile ? {
11746
+ project: {
11747
+ name: basename(projectRoot) || "unnamed-project",
11748
+ profile: profile.runtimes[0] || "unknown"
11749
+ },
11750
+ defaults: {
11751
+ target: "opencode",
11752
+ overwrite: force
11753
+ }
11754
+ } : {
11755
+ project: {
11756
+ name: "global",
11757
+ profile: "global"
11758
+ },
11759
+ defaults: {
11760
+ target: "opencode",
11761
+ overwrite: force
11762
+ }
11763
+ };
11764
+ const presetResolution = profile ? resolvePreset("opencode", profile) : null;
11765
+ const wouldCreate = [".codeconductor/config.yml"];
11766
+ const presetsToCopy = await resolvePresetsToCopy();
11767
+ for (const p of presetsToCopy) {
11768
+ wouldCreate.push(`.codeconductor/presets/${p.name}`);
11880
11769
  }
11881
- if (!options.force) {
11882
- try {
11883
- await access4(file.path);
11884
- results.push({
11885
- path: file.path,
11770
+ if (dryRun) {
11771
+ return {
11772
+ code: 0,
11773
+ data: {
11774
+ success: true,
11775
+ command: "init",
11776
+ message: "Dry run - would create config",
11777
+ wouldCreate,
11778
+ ...profile ? {
11779
+ detected: {
11780
+ languages: profile.languages,
11781
+ runtimes: profile.runtimes,
11782
+ frameworks: profile.frameworks,
11783
+ signals: profile.signals,
11784
+ confidence: profile.confidence
11785
+ },
11786
+ presetResolution
11787
+ } : { global: true }
11788
+ }
11789
+ };
11790
+ }
11791
+ const result = await writeConfig(baseDir, config, force);
11792
+ if (!result.success) {
11793
+ return {
11794
+ code: 1,
11795
+ data: {
11886
11796
  success: false,
11887
- error: "File exists, use --force to overwrite"
11888
- });
11889
- continue;
11890
- } catch {}
11797
+ command: "init",
11798
+ errors: [result.error.message]
11799
+ }
11800
+ };
11891
11801
  }
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,
11802
+ const copiedPresets = await copyPresets(baseDir, presetsToCopy, force);
11803
+ return {
11804
+ code: 0,
11805
+ data: {
11806
+ success: true,
11807
+ command: "init",
11808
+ created: [".codeconductor/config.yml", ...copiedPresets],
11809
+ ...profile ? {
11810
+ detected: {
11811
+ languages: profile.languages,
11812
+ runtimes: profile.runtimes,
11813
+ frameworks: profile.frameworks,
11814
+ signals: profile.signals,
11815
+ confidence: profile.confidence
11816
+ },
11817
+ presetResolution
11818
+ } : { global: true }
11819
+ }
11820
+ };
11821
+ } catch (error) {
11822
+ return {
11823
+ code: 1,
11824
+ data: {
11903
11825
  success: false,
11904
- error: String(error)
11905
- });
11906
- }
11826
+ command: "init",
11827
+ errors: [String(error)]
11828
+ }
11829
+ };
11907
11830
  }
11908
- return results;
11909
11831
  }
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);
11832
+ async function resolvePresetsToCopy() {
11833
+ const sources = [];
11834
+ const bundledCouncil = resolve4(SRC_PRESETS_DIR, "council", "council.yml");
11835
+ if (await fileExists2(bundledCouncil)) {
11836
+ sources.push({ name: "council.yml", sourcePath: bundledCouncil });
11837
+ }
11838
+ const policyPath = POLICY_PATH;
11839
+ if (await fileExists2(policyPath)) {
11840
+ sources.push({ name: "policy.yml", sourcePath: policyPath });
11841
+ }
11842
+ return sources;
11916
11843
  }
11917
- function parseRunnerTarget(value) {
11918
- if (!isRunnerTarget(value)) {
11919
- throw new Error(`Invalid runner target: ${value}. Valid targets: ${RUNNER_TARGETS.join(", ")}`);
11844
+ async function copyPresets(baseDir, presets, force) {
11845
+ const presetsDir = resolve4(baseDir, ".codeconductor", "presets");
11846
+ await mkdir2(presetsDir, { recursive: true });
11847
+ const copied = [];
11848
+ for (const preset of presets) {
11849
+ const destPath = resolve4(presetsDir, preset.name);
11850
+ if (!force && await fileExists2(destPath)) {
11851
+ continue;
11852
+ }
11853
+ try {
11854
+ const content = await readFile3(preset.sourcePath, "utf-8");
11855
+ await writeFile2(destPath, content, "utf-8");
11856
+ copied.push(`.codeconductor/presets/${preset.name}`);
11857
+ } catch (err2) {
11858
+ const code = err2.code;
11859
+ if (code !== "ENOENT") {
11860
+ process.stderr.write(`warn: could not copy ${preset.name}: ${String(err2)}
11861
+ `);
11862
+ }
11863
+ }
11920
11864
  }
11921
- return value;
11865
+ return copied;
11922
11866
  }
11923
- function getIndividualTargets(target) {
11924
- if (target === "all") {
11925
- return [...INDIVIDUAL_TARGETS];
11867
+ async function fileExists2(path) {
11868
+ try {
11869
+ await access3(path);
11870
+ return true;
11871
+ } catch {
11872
+ return false;
11926
11873
  }
11927
- return [target];
11928
11874
  }
11929
11875
 
11876
+ // src/commands/install.command.ts
11877
+ import { homedir as homedir2 } from "node:os";
11878
+ import { resolve as resolve7 } from "node:path";
11879
+
11930
11880
  // src/domain/council/council-agent.ts
11931
11881
  function getResponsibilities(agentId) {
11932
11882
  const responsibilities = {
@@ -11960,24 +11910,173 @@ function getResponsibilities(agentId) {
11960
11910
  function generateAgentContent(agent) {
11961
11911
  return `# ${agent.role} Agent
11962
11912
 
11963
- ## Role
11964
- ${agent.role}
11913
+ ## Role
11914
+ ${agent.role}
11915
+
11916
+ ## Context
11917
+ ${agent.context === "repo-readonly" ? "Can read repository but cannot modify files" : "Only receives prompts, no direct repository access"}
11918
+
11919
+ ## Model Hint
11920
+ ${agent.modelHint}
11921
+
11922
+ ## Focus Areas
11923
+ ${agent.focus.map((f) => `- ${f}`).join(`
11924
+ `)}
11925
+
11926
+ ## Responsibilities
11927
+ ${getResponsibilities(agent.id)}
11928
+ `;
11929
+ }
11930
+
11931
+ // src/adapters/claude/claude-council-generator.ts
11932
+ function generateClaudeFiles(spec) {
11933
+ const files = [];
11934
+ files.push({
11935
+ path: ".claude/skills/council/SKILL.md",
11936
+ content: generateCouncilSkill(spec),
11937
+ overwrite: false
11938
+ });
11939
+ for (const agent of spec.agents) {
11940
+ files.push({
11941
+ path: `.claude/agents/council-${agent.id}.md`,
11942
+ content: generateAgentContent(agent),
11943
+ overwrite: false
11944
+ });
11945
+ }
11946
+ return files;
11947
+ }
11948
+ function generateCouncilSkill(spec) {
11949
+ return `# Council Skill
11950
+
11951
+ ## Description
11952
+ ${spec.description}
11953
+
11954
+ ## Version
11955
+ ${spec.version}
11956
+
11957
+ ## Agents
11958
+ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).join(`
11959
+ `)}
11960
+
11961
+ ## Usage
11962
+ Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
11965
11963
 
11966
11964
  ## Context
11967
- ${agent.context === "repo-readonly" ? "Can read repository but cannot modify files" : "Only receives prompts, no direct repository access"}
11965
+ ${spec.outputContract}
11966
+ `;
11967
+ }
11968
11968
 
11969
- ## Model Hint
11970
- ${agent.modelHint}
11969
+ // src/adapters/claude/claude-installer.ts
11970
+ class ClaudeInstaller {
11971
+ name = "claude";
11972
+ target = "claude";
11973
+ spec = null;
11974
+ setSpec(spec) {
11975
+ this.spec = spec;
11976
+ }
11977
+ async generate() {
11978
+ if (!this.spec) {
11979
+ throw new Error("Council spec not set");
11980
+ }
11981
+ return generateClaudeFiles(this.spec);
11982
+ }
11983
+ async isAvailable() {
11984
+ return true;
11985
+ }
11986
+ }
11987
+ function createClaudeInstaller(spec) {
11988
+ const installer = new ClaudeInstaller;
11989
+ installer.setSpec(spec);
11990
+ return installer;
11991
+ }
11971
11992
 
11972
- ## Focus Areas
11973
- ${agent.focus.map((f) => `- ${f}`).join(`
11993
+ // src/adapters/codex/codex-council-generator.ts
11994
+ function generateCodexFiles(spec) {
11995
+ const files = [];
11996
+ files.push({
11997
+ path: ".codex/config.toml",
11998
+ content: generateCodexConfig(spec),
11999
+ overwrite: false
12000
+ });
12001
+ for (const agent of spec.agents) {
12002
+ files.push({
12003
+ path: `.codex/agents/council_${agent.id}.toml`,
12004
+ content: generateCodexAgent(agent),
12005
+ overwrite: true
12006
+ });
12007
+ }
12008
+ files.push({
12009
+ path: ".codex/skills/council/SKILL.md",
12010
+ content: generateCodexSkill(spec),
12011
+ overwrite: true
12012
+ });
12013
+ return files;
12014
+ }
12015
+ function generateCodexConfig(spec) {
12016
+ const agentTables = spec.agents.map((agent) => {
12017
+ const focusAreas = agent.focus.join(", ");
12018
+ return `
12019
+ [agents.${agent.id}]
12020
+ description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
12021
+ nickname_candidates = ["${agent.role}", "Council ${agent.role}"]`;
12022
+ }).join(`
12023
+ `);
12024
+ return `# Codex Council Configuration
12025
+
12026
+ [project]
12027
+ name = "council"
12028
+ version = "${spec.version}"
12029
+ ${agentTables}
12030
+ `;
12031
+ }
12032
+ function generateCodexAgent(agent) {
12033
+ const focusAreas = agent.focus.join(", ");
12034
+ return `name = "${agent.role}"
12035
+ description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
12036
+ nickname_candidates = ["${agent.role}", "Council ${agent.role}"]
12037
+ developer_instructions = "You are the ${agent.role} council agent. Your focus areas are: ${focusAreas}. Context: ${agent.context}. Apply ${agent.modelHint} reasoning to your analysis."
12038
+ `;
12039
+ }
12040
+ function generateCodexSkill(spec) {
12041
+ return `---
12042
+ name: council
12043
+ description: ${spec.description}
12044
+ version: ${spec.version}
12045
+ ---
12046
+
12047
+ ## Agents
12048
+ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).join(`
11974
12049
  `)}
11975
12050
 
11976
- ## Responsibilities
11977
- ${getResponsibilities(agent.id)}
12051
+ ## Usage
12052
+ Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
11978
12053
  `;
11979
12054
  }
11980
12055
 
12056
+ // src/adapters/codex/codex-installer.ts
12057
+ class CodexInstaller {
12058
+ name = "codex";
12059
+ target = "codex";
12060
+ spec = null;
12061
+ setSpec(spec) {
12062
+ this.spec = spec;
12063
+ }
12064
+ async generate() {
12065
+ if (!this.spec) {
12066
+ throw new Error("Council spec not set");
12067
+ }
12068
+ return generateCodexFiles(this.spec);
12069
+ }
12070
+ async isAvailable() {
12071
+ return true;
12072
+ }
12073
+ }
12074
+ function createCodexInstaller(spec) {
12075
+ const installer = new CodexInstaller;
12076
+ installer.setSpec(spec);
12077
+ return installer;
12078
+ }
12079
+
11981
12080
  // src/adapters/opencode/opencode-council-generator.ts
11982
12081
  function generateOpenCodeFiles(spec) {
11983
12082
  const files = [];
@@ -12046,169 +12145,380 @@ class OpenCodeInstaller {
12046
12145
  setSpec(spec) {
12047
12146
  this.spec = spec;
12048
12147
  }
12049
- async generate() {
12050
- if (!this.spec) {
12051
- throw new Error("Council spec not set");
12148
+ async generate() {
12149
+ if (!this.spec) {
12150
+ throw new Error("Council spec not set");
12151
+ }
12152
+ return generateOpenCodeFiles(this.spec);
12153
+ }
12154
+ async isAvailable() {
12155
+ return true;
12156
+ }
12157
+ }
12158
+ function createOpenCodeInstaller(spec) {
12159
+ const installer = new OpenCodeInstaller;
12160
+ installer.setSpec(spec);
12161
+ return installer;
12162
+ }
12163
+
12164
+ // src/core/filesystem/file-writer.ts
12165
+ import { access as access4, mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
12166
+ import { dirname as dirname2 } from "node:path";
12167
+ init_safety();
12168
+ async function writeGeneratedFiles(files, options) {
12169
+ const results = [];
12170
+ for (const file of files) {
12171
+ if (!validateWritePath(file.path)) {
12172
+ results.push({
12173
+ path: file.path,
12174
+ success: false,
12175
+ error: "Protected path"
12176
+ });
12177
+ continue;
12178
+ }
12179
+ if (options.dryRun) {
12180
+ results.push({
12181
+ path: file.path,
12182
+ success: true
12183
+ });
12184
+ continue;
12185
+ }
12186
+ if (!options.force) {
12187
+ try {
12188
+ await access4(file.path);
12189
+ results.push({
12190
+ path: file.path,
12191
+ success: false,
12192
+ error: "File exists, use --force to overwrite"
12193
+ });
12194
+ continue;
12195
+ } catch {}
12196
+ }
12197
+ try {
12198
+ const dir = dirname2(file.path);
12199
+ await mkdir3(dir, { recursive: true });
12200
+ await writeFile3(file.path, file.content, "utf-8");
12201
+ results.push({
12202
+ path: file.path,
12203
+ success: true
12204
+ });
12205
+ } catch (error) {
12206
+ results.push({
12207
+ path: file.path,
12208
+ success: false,
12209
+ error: String(error)
12210
+ });
12211
+ }
12212
+ }
12213
+ return results;
12214
+ }
12215
+
12216
+ // src/core/presets/file-copier.ts
12217
+ import { mkdir as mkdir4, readdir, readFile as readFile4, stat, writeFile as writeFile4 } from "node:fs/promises";
12218
+ import { dirname as dirname3, join as join2, relative, resolve as resolve5 } from "node:path";
12219
+
12220
+ // src/core/filesystem/safe-merger.ts
12221
+ var MANAGED_BEGIN_MARKER = "<!-- CODECONDUCTOR:BEGIN managed -->";
12222
+ var MANAGED_END_MARKER = "<!-- CODECONDUCTOR:END managed -->";
12223
+ function mergeManagedBlock(existing, incoming) {
12224
+ validateMarkers(incoming, "incoming content");
12225
+ if (!existing) {
12226
+ return { content: incoming, action: "written" };
12227
+ }
12228
+ validateMarkers(existing, "existing content");
12229
+ const existingBlock = getManagedBlockRange(existing);
12230
+ const incomingBlock = getManagedBlockRange(incoming);
12231
+ return {
12232
+ content: existing.slice(0, existingBlock.start) + incoming.slice(incomingBlock.start, incomingBlock.end) + existing.slice(existingBlock.end),
12233
+ action: "merged"
12234
+ };
12235
+ }
12236
+ function validateMarkers(content, label) {
12237
+ const beginCount = countOccurrences(content, MANAGED_BEGIN_MARKER);
12238
+ const endCount = countOccurrences(content, MANAGED_END_MARKER);
12239
+ if (beginCount !== 1 || endCount !== 1) {
12240
+ throw new Error(`${label} must contain exactly one managed begin marker and one managed end marker`);
12241
+ }
12242
+ if (content.indexOf(MANAGED_BEGIN_MARKER) > content.indexOf(MANAGED_END_MARKER)) {
12243
+ throw new Error(`${label} has managed markers in the wrong order`);
12244
+ }
12245
+ }
12246
+ function getManagedBlockRange(content) {
12247
+ const start = content.indexOf(MANAGED_BEGIN_MARKER);
12248
+ const end = content.indexOf(MANAGED_END_MARKER) + MANAGED_END_MARKER.length;
12249
+ return { start, end };
12250
+ }
12251
+ function countOccurrences(content, needle) {
12252
+ return content.split(needle).length - 1;
12253
+ }
12254
+
12255
+ // src/core/presets/file-copier.ts
12256
+ async function listFilesRecursive(dir, base = dir) {
12257
+ const entries = await readdir(dir, { withFileTypes: true });
12258
+ const files = [];
12259
+ for (const entry of entries) {
12260
+ const full = join2(dir, entry.name);
12261
+ if (entry.isDirectory()) {
12262
+ files.push(...await listFilesRecursive(full, base));
12263
+ } else {
12264
+ files.push(relative(base, full));
12265
+ }
12266
+ }
12267
+ return files;
12268
+ }
12269
+ async function resolveEntryFiles(entry, presetsDir, baseDir) {
12270
+ const srcAbsolute = resolve5(presetsDir, entry.src);
12271
+ try {
12272
+ const s = await stat(srcAbsolute);
12273
+ if (s.isDirectory()) {
12274
+ const files = await listFilesRecursive(srcAbsolute);
12275
+ return files.map((f) => ({
12276
+ src: join2(srcAbsolute, f),
12277
+ dest: resolve5(baseDir, entry.dest, f)
12278
+ }));
12279
+ }
12280
+ return [{ src: srcAbsolute, dest: resolve5(baseDir, entry.dest) }];
12281
+ } catch {
12282
+ return [];
12283
+ }
12284
+ }
12285
+ function mergeDeep(target, source) {
12286
+ const result = { ...target };
12287
+ for (const key of Object.keys(source)) {
12288
+ const srcVal = source[key];
12289
+ const tgtVal = target[key];
12290
+ if (Array.isArray(srcVal) && Array.isArray(tgtVal)) {
12291
+ result[key] = [...new Set([...tgtVal, ...srcVal])];
12292
+ } else if (srcVal && typeof srcVal === "object" && !Array.isArray(srcVal) && tgtVal && typeof tgtVal === "object" && !Array.isArray(tgtVal)) {
12293
+ result[key] = mergeDeep(tgtVal, srcVal);
12294
+ } else {
12295
+ result[key] = srcVal;
12296
+ }
12297
+ }
12298
+ return result;
12299
+ }
12300
+ function extractAgentRole(filePath) {
12301
+ const parts = filePath.replace(/\\/g, "/").split("/");
12302
+ const basename2 = parts[parts.length - 1];
12303
+ const name = basename2.replace(/\.md$/, "");
12304
+ const knownRoles = [
12305
+ "architect",
12306
+ "implementer",
12307
+ "tester",
12308
+ "orchestrator",
12309
+ "reviewer",
12310
+ "docs",
12311
+ "task-coach",
12312
+ "repo-explorer"
12313
+ ];
12314
+ return knownRoles.includes(name) ? name : null;
12315
+ }
12316
+ function renderTemplate(content, modelConfig, filePath) {
12317
+ const agentRole = extractAgentRole(filePath);
12318
+ if (agentRole && modelConfig.agents[agentRole]) {
12319
+ const agentModels = modelConfig.agents[agentRole];
12320
+ const targetModel = agentModels[modelConfig.target];
12321
+ 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 ?? "");
12322
+ if (modelConfig.tools) {
12323
+ result2 = substituteToolNames(result2, modelConfig);
12324
+ }
12325
+ return result2;
12326
+ }
12327
+ const knownRoles = [
12328
+ "orchestrator",
12329
+ "task-coach",
12330
+ "architect",
12331
+ "implementer",
12332
+ "tester",
12333
+ "reviewer",
12334
+ "docs",
12335
+ "repo-explorer"
12336
+ ];
12337
+ let result = content;
12338
+ for (const role of knownRoles) {
12339
+ if (!modelConfig.agents[role])
12340
+ continue;
12341
+ const agentModels = modelConfig.agents[role];
12342
+ const sectionRegex = new RegExp(`(### ${role}[\\s\\S]*?)(?=### |## |$)`, "gi");
12343
+ const sectionMatch = result.match(sectionRegex);
12344
+ if (sectionMatch) {
12345
+ for (const section of sectionMatch) {
12346
+ 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 ?? "");
12347
+ result = result.replace(section, renderedSection);
12348
+ }
12052
12349
  }
12053
- return generateOpenCodeFiles(this.spec);
12054
12350
  }
12055
- async isAvailable() {
12056
- return true;
12351
+ if (modelConfig.tools) {
12352
+ result = substituteToolNames(result, modelConfig);
12057
12353
  }
12354
+ return result;
12058
12355
  }
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
12356
+ function substituteToolNames(content, modelConfig) {
12357
+ if (!modelConfig.tools)
12358
+ return content;
12359
+ const target = modelConfig.target;
12360
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
12361
+ if (!fmMatch)
12362
+ return content;
12363
+ const frontmatter = fmMatch[1];
12364
+ const updatedFrontmatter = frontmatter.replace(/^tools:\s*(.+)$/m, (_match, toolsLine) => {
12365
+ const baseNames = toolsLine.split(",").map((t) => t.trim());
12366
+ const mappedNames = baseNames.map((baseName) => {
12367
+ const toolMapping = modelConfig.tools?.[baseName];
12368
+ if (toolMapping && toolMapping[target]) {
12369
+ return toolMapping[target];
12370
+ }
12371
+ return baseName;
12078
12372
  });
12079
- }
12080
- return files;
12373
+ return `tools: ${mappedNames.join(", ")}`;
12374
+ });
12375
+ return content.replace(fmMatch[0], `---
12376
+ ${updatedFrontmatter}
12377
+ ---`);
12081
12378
  }
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.
12379
+ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTemplate, modelConfig) {
12380
+ if (strategy === "skip") {
12381
+ return { src: srcPath, dest: destPath, action: "skipped", dryRun };
12382
+ }
12383
+ let content;
12384
+ try {
12385
+ content = await readFile4(srcPath, "utf-8");
12386
+ } catch (e) {
12387
+ return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
12388
+ }
12389
+ const incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath) : content;
12390
+ let finalContent = incomingContent;
12391
+ let action = "written";
12392
+ if (strategy === "append" && !force) {
12393
+ let existing = "";
12394
+ try {
12395
+ existing = await readFile4(destPath, "utf-8");
12396
+ } catch {}
12397
+ if (existing) {
12398
+ finalContent = existing + `
12097
12399
 
12098
- ## Context
12099
- ${spec.outputContract}
12100
- `;
12101
- }
12400
+ ---
12102
12401
 
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");
12402
+ ` + incomingContent;
12403
+ }
12404
+ action = "appended";
12405
+ } else if (strategy === "merge-json" && !force) {
12406
+ let existing = {};
12407
+ try {
12408
+ existing = JSON.parse(await readFile4(destPath, "utf-8"));
12409
+ } catch {}
12410
+ try {
12411
+ const incoming = JSON.parse(incomingContent);
12412
+ finalContent = JSON.stringify(mergeDeep(existing, incoming), null, 2);
12413
+ } catch (e) {
12414
+ return { src: srcPath, dest: destPath, action: "error", error: `JSON merge failed: ${e}` };
12415
+ }
12416
+ action = "merged";
12417
+ } else if (strategy === "merge-managed") {
12418
+ let existing = null;
12419
+ try {
12420
+ existing = await readFile4(destPath, "utf-8");
12421
+ } catch {}
12422
+ try {
12423
+ const merged = mergeManagedBlock(existing, incomingContent);
12424
+ finalContent = merged.content;
12425
+ action = merged.action;
12426
+ } catch (e) {
12427
+ return { src: srcPath, dest: destPath, action: "error", error: `Managed merge failed: ${e}` };
12114
12428
  }
12115
- return generateClaudeFiles(this.spec);
12116
12429
  }
12117
- async isAvailable() {
12118
- return true;
12430
+ if (dryRun) {
12431
+ return { src: srcPath, dest: destPath, action, dryRun: true };
12432
+ }
12433
+ try {
12434
+ await mkdir4(dirname3(destPath), { recursive: true });
12435
+ await writeFile4(destPath, finalContent, "utf-8");
12436
+ return { src: srcPath, dest: destPath, action };
12437
+ } catch (e) {
12438
+ return { src: srcPath, dest: destPath, action: "error", error: String(e) };
12119
12439
  }
12120
12440
  }
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
- });
12441
+ async function copyFromManifest(manifest, presetsDir, baseDir, isGlobal, dryRun, force, modelConfig = null) {
12442
+ const results = [];
12443
+ for (const entry of manifest.entries) {
12444
+ const strategy = isGlobal && entry.globalStrategy ? entry.globalStrategy : entry.strategy;
12445
+ const files = await resolveEntryFiles(entry, presetsDir, baseDir);
12446
+ const isTemplate = entry.template === true;
12447
+ for (const { src, dest } of files) {
12448
+ results.push(await applySingleFile(src, dest, strategy, force, dryRun, isTemplate, modelConfig));
12449
+ }
12141
12450
  }
12142
- files.push({
12143
- path: ".codex/skills/council/SKILL.md",
12144
- content: generateCodexSkill(spec),
12145
- overwrite: true
12146
- });
12147
- return files;
12451
+ return results;
12148
12452
  }
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
12453
 
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
- `;
12454
+ // src/core/presets/manifest-loader.ts
12455
+ import { readFile as readFile5 } from "node:fs/promises";
12456
+ import { join as join3 } from "node:path";
12457
+ var MANIFESTS_DIR = join3(SRC_PRESETS_DIR, "manifests");
12458
+ var MODELS_DIR = join3(SRC_PRESETS_DIR, "models");
12459
+ var PRESETS_DIR = ROOT_PRESETS_DIR;
12460
+ async function loadManifest(target) {
12461
+ const manifestPath = join3(MANIFESTS_DIR, `${target}.yml`);
12462
+ const content = await readFile5(manifestPath, "utf-8");
12463
+ const data = $parse(content);
12464
+ return InstallManifestSchema.parse(data);
12173
12465
  }
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
- `;
12466
+ async function loadModelConfig(target) {
12467
+ const modelPath = join3(MODELS_DIR, `${target}.yml`);
12468
+ const content = await readFile5(modelPath, "utf-8");
12469
+ const data = $parse(content);
12470
+ return ModelConfigSchema.parse(data);
12188
12471
  }
12189
12472
 
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");
12473
+ // src/core/presets/preset-loader.ts
12474
+ import { readFile as readFile6 } from "node:fs/promises";
12475
+ import { resolve as resolve6 } from "node:path";
12476
+ async function loadPreset(name, projectRoot = process.cwd()) {
12477
+ const candidates = [
12478
+ resolve6(projectRoot, ".codeconductor", "presets", `${name}.yml`),
12479
+ resolve6(SRC_PRESETS_DIR, name, `${name}.yml`)
12480
+ ];
12481
+ for (const presetPath of candidates) {
12482
+ try {
12483
+ const content = await readFile6(presetPath, "utf-8");
12484
+ const data = $parse(content);
12485
+ if (!data)
12486
+ continue;
12487
+ const validated = validateCouncilSpec(data);
12488
+ return ok(validated);
12489
+ } catch (error) {
12490
+ if (error instanceof ValidationError) {
12491
+ return err(error);
12492
+ }
12493
+ const code = error.code;
12494
+ if (code !== "ENOENT") {
12495
+ return err(new ValidationError(`Invalid preset at ${presetPath}: ${String(error)}`));
12496
+ }
12201
12497
  }
12202
- return generateCodexFiles(this.spec);
12203
12498
  }
12204
- async isAvailable() {
12205
- return true;
12499
+ return err(new ValidationError(`Preset not found: ${name}. Run \`codeconductor init\` first.`));
12500
+ }
12501
+ async function loadCouncilPreset(projectRoot) {
12502
+ return loadPreset("council", projectRoot);
12503
+ }
12504
+
12505
+ // src/core/runner/runner-target.ts
12506
+ var RUNNER_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor", "all"];
12507
+ var INDIVIDUAL_TARGETS = ["opencode", "claude", "codex", "gemini", "cursor"];
12508
+ function isRunnerTarget(value) {
12509
+ return RUNNER_TARGETS.includes(value);
12510
+ }
12511
+ function parseRunnerTarget(value) {
12512
+ if (!isRunnerTarget(value)) {
12513
+ throw new Error(`Invalid runner target: ${value}. Valid targets: ${RUNNER_TARGETS.join(", ")}`);
12206
12514
  }
12515
+ return value;
12207
12516
  }
12208
- function createCodexInstaller(spec) {
12209
- const installer = new CodexInstaller;
12210
- installer.setSpec(spec);
12211
- return installer;
12517
+ function getIndividualTargets(target) {
12518
+ if (target === "all") {
12519
+ return [...INDIVIDUAL_TARGETS];
12520
+ }
12521
+ return [target];
12212
12522
  }
12213
12523
 
12214
12524
  // src/commands/install.command.ts
@@ -12250,7 +12560,7 @@ async function installCommand(options) {
12250
12560
  const generatedFiles = await installer.generate();
12251
12561
  const resolvedFiles = generatedFiles.map((f) => ({
12252
12562
  ...f,
12253
- path: resolve6(baseDir, f.path)
12563
+ path: resolve7(baseDir, f.path)
12254
12564
  }));
12255
12565
  const results = await writeGeneratedFiles(resolvedFiles, writeOptions);
12256
12566
  for (const result of results) {
@@ -12350,184 +12660,6 @@ async function installPresetCommand(options) {
12350
12660
  }
12351
12661
  }
12352
12662
 
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
12663
  // src/commands/update.command.ts
12532
12664
  async function updateCommand(options) {
12533
12665
  const { dryRun, force, output, projectRoot } = options;
@@ -12652,59 +12784,6 @@ async function updateCommand(options) {
12652
12784
  };
12653
12785
  }
12654
12786
  }
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
12787
 
12709
12788
  // src/cli/router.ts
12710
12789
  function parseArgs(args) {
@@ -12770,7 +12849,7 @@ function getVersion() {
12770
12849
  function getHelp() {
12771
12850
  return `CodeConductor CLI v${package_default.version}
12772
12851
 
12773
- Usage: codeconductor <command> [options]
12852
+ Usage: npx cc-codeconductor <command> [options]
12774
12853
 
12775
12854
  Commands:
12776
12855
  init Initialize CodeConductor in a project
@@ -12789,20 +12868,20 @@ Options:
12789
12868
  --output, -o Output mode: human or json
12790
12869
 
12791
12870
  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
12871
+ npx cc-codeconductor init
12872
+ npx cc-codeconductor init --global
12873
+ npx cc-codeconductor detect
12874
+ npx cc-codeconductor install preset --target opencode
12875
+ npx cc-codeconductor install preset --target claude
12876
+ npx cc-codeconductor install preset --target codex
12877
+ npx cc-codeconductor install preset --target all
12878
+ npx cc-codeconductor install preset --target claude --global
12879
+ npx cc-codeconductor install council --target opencode
12880
+ npx cc-codeconductor install council --target claude
12881
+ npx cc-codeconductor install council --target codex
12882
+ npx cc-codeconductor install council --target all
12883
+ npx cc-codeconductor doctor
12884
+ npx cc-codeconductor update --dry-run
12806
12885
  `;
12807
12886
  }
12808
12887
  async function routeCommand(args, projectRoot) {
@@ -12909,7 +12988,13 @@ async function runCli(args) {
12909
12988
  });
12910
12989
  } else if ("fileResults" in data) {
12911
12990
  const fileResults = data.fileResults;
12912
- const actionIcon = { written: "✓", appended: "→", merged: "~", skipped: "∅", error: "✗" };
12991
+ const actionIcon = {
12992
+ written: "✓",
12993
+ appended: "→",
12994
+ merged: "~",
12995
+ skipped: "∅",
12996
+ error: "✗"
12997
+ };
12913
12998
  fileResults.forEach((r) => {
12914
12999
  if (r.action === "skipped")
12915
13000
  return;