cc-codeconductor 0.2.4 → 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.
- package/README.md +24 -24
- package/dist/index.js +1150 -1065
- package/package.json +4 -2
- package/policy.yml +22 -2
- package/presets/claude/skills/api-versioning/SKILL.md +8 -2
- package/presets/claude/skills/django-orm/SKILL.md +7 -1
- package/presets/claude/skills/django-testing/SKILL.md +7 -1
- package/presets/claude/skills/jpa-postgres/SKILL.md +8 -2
- package/presets/claude/skills/python/SKILL.md +7 -1
- package/presets/claude/skills/python-django-stack/SKILL.md +7 -1
- package/presets/claude/skills/python-fastapi-stack/SKILL.md +7 -1
- package/presets/claude/skills/spring-boot-feature/SKILL.md +8 -2
- package/presets/claude/skills/spring-boot-kotlin/SKILL.md +8 -2
- package/presets/claude/skills/sqlalchemy/SKILL.md +7 -1
- package/presets/claude/skills/testing-strategy/SKILL.md +8 -2
- package/presets/codex/skills/api-versioning/SKILL.md +8 -2
- package/presets/codex/skills/django-orm/SKILL.md +7 -1
- package/presets/codex/skills/django-testing/SKILL.md +7 -1
- package/presets/codex/skills/jpa-postgres/SKILL.md +8 -2
- package/presets/codex/skills/python/SKILL.md +7 -1
- package/presets/codex/skills/python-django-stack/SKILL.md +7 -1
- package/presets/codex/skills/python-fastapi-stack/SKILL.md +7 -1
- package/presets/codex/skills/spring-boot-feature/SKILL.md +8 -2
- package/presets/codex/skills/spring-boot-kotlin/SKILL.md +8 -2
- package/presets/codex/skills/sqlalchemy/SKILL.md +7 -1
- package/presets/codex/skills/testing-strategy/SKILL.md +8 -2
- package/presets/opencode/agents/architect.md +3 -0
- package/presets/opencode/agents/docs.md +3 -0
- package/presets/opencode/agents/implementer.md +7 -4
- package/presets/opencode/agents/orchestrator.md +3 -0
- package/presets/opencode/agents/repo-explorer.md +3 -0
- package/presets/opencode/agents/reviewer.md +3 -0
- package/presets/opencode/agents/task-coach.md +3 -0
- package/presets/opencode/agents/tester.md +3 -0
- package/presets/opencode/skills/api-versioning/SKILL.md +8 -2
- package/presets/opencode/skills/astro/SKILL.md +7 -1
- package/presets/opencode/skills/code-review/SKILL.md +7 -1
- package/presets/opencode/skills/django-orm/SKILL.md +7 -1
- package/presets/opencode/skills/django-testing/SKILL.md +7 -1
- package/presets/opencode/skills/django-uv/SKILL.md +7 -1
- package/presets/opencode/skills/jpa-postgres/SKILL.md +8 -2
- package/presets/opencode/skills/nextjs-typescript/SKILL.md +7 -1
- package/presets/opencode/skills/python/SKILL.md +7 -1
- package/presets/opencode/skills/python-django-stack/SKILL.md +7 -1
- package/presets/opencode/skills/python-fastapi-stack/SKILL.md +7 -1
- package/presets/opencode/skills/security/SKILL.md +7 -1
- package/presets/opencode/skills/spring-boot-feature/SKILL.md +8 -2
- package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +8 -2
- package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +7 -1
- package/presets/opencode/skills/sqlalchemy/SKILL.md +7 -1
- package/presets/opencode/skills/testing-tdd/SKILL.md +7 -1
- package/src/presets/manifests/cursor.yml +9 -0
- package/src/presets/manifests/gemini.yml +9 -0
- package/src/presets/models/claude.yml +60 -1
- package/src/presets/models/codex.yml +60 -1
- package/src/presets/models/cursor.yml +96 -0
- package/src/presets/models/gemini.yml +96 -0
- 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
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
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/
|
|
7567
|
-
import {
|
|
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/
|
|
7571
|
-
import { readFile
|
|
7572
|
-
import { resolve
|
|
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([
|
|
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([
|
|
11593
|
-
|
|
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/
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
11627
|
-
|
|
11628
|
-
|
|
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
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
11650
|
-
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
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/
|
|
11670
|
-
import { readFile as
|
|
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/
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
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/
|
|
11709
|
-
async function
|
|
11710
|
-
const
|
|
11711
|
-
const
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11723
|
-
|
|
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
|
|
11726
|
-
if (
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
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
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11737
|
-
|
|
11738
|
-
|
|
11739
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11752
|
-
|
|
11753
|
-
|
|
11754
|
-
|
|
11755
|
-
|
|
11756
|
-
|
|
11757
|
-
|
|
11758
|
-
|
|
11759
|
-
}
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
|
|
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
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
|
|
11814
|
-
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
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
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
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
|
-
|
|
11836
|
-
|
|
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
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
}
|
|
11843
|
-
|
|
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
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
|
|
11853
|
-
|
|
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/
|
|
11860
|
-
|
|
11861
|
-
|
|
11862
|
-
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
|
|
11870
|
-
|
|
11871
|
-
|
|
11872
|
-
|
|
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
|
-
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
|
|
11878
|
-
}
|
|
11879
|
-
|
|
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 (
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
|
|
11885
|
-
|
|
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
|
-
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
}
|
|
11797
|
+
command: "init",
|
|
11798
|
+
errors: [result.error.message]
|
|
11799
|
+
}
|
|
11800
|
+
};
|
|
11891
11801
|
}
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
|
|
11902
|
-
|
|
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
|
-
|
|
11905
|
-
|
|
11906
|
-
|
|
11826
|
+
command: "init",
|
|
11827
|
+
errors: [String(error)]
|
|
11828
|
+
}
|
|
11829
|
+
};
|
|
11907
11830
|
}
|
|
11908
|
-
return results;
|
|
11909
11831
|
}
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
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
|
|
11918
|
-
|
|
11919
|
-
|
|
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
|
|
11865
|
+
return copied;
|
|
11922
11866
|
}
|
|
11923
|
-
function
|
|
11924
|
-
|
|
11925
|
-
|
|
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
|
-
${
|
|
11965
|
+
${spec.outputContract}
|
|
11966
|
+
`;
|
|
11967
|
+
}
|
|
11968
11968
|
|
|
11969
|
-
|
|
11970
|
-
|
|
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
|
-
|
|
11973
|
-
|
|
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
|
-
##
|
|
11977
|
-
|
|
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
|
-
|
|
12056
|
-
|
|
12351
|
+
if (modelConfig.tools) {
|
|
12352
|
+
result = substituteToolNames(result, modelConfig);
|
|
12057
12353
|
}
|
|
12354
|
+
return result;
|
|
12058
12355
|
}
|
|
12059
|
-
function
|
|
12060
|
-
|
|
12061
|
-
|
|
12062
|
-
|
|
12063
|
-
|
|
12064
|
-
|
|
12065
|
-
|
|
12066
|
-
|
|
12067
|
-
const
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
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
|
-
|
|
12373
|
+
return `tools: ${mappedNames.join(", ")}`;
|
|
12374
|
+
});
|
|
12375
|
+
return content.replace(fmMatch[0], `---
|
|
12376
|
+
${updatedFrontmatter}
|
|
12377
|
+
---`);
|
|
12081
12378
|
}
|
|
12082
|
-
function
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
|
|
12095
|
-
|
|
12096
|
-
|
|
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
|
-
|
|
12099
|
-
${spec.outputContract}
|
|
12100
|
-
`;
|
|
12101
|
-
}
|
|
12400
|
+
---
|
|
12102
12401
|
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
|
|
12107
|
-
|
|
12108
|
-
|
|
12109
|
-
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
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
|
-
|
|
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
|
|
12122
|
-
const
|
|
12123
|
-
|
|
12124
|
-
|
|
12125
|
-
|
|
12126
|
-
|
|
12127
|
-
|
|
12128
|
-
|
|
12129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
function
|
|
12167
|
-
const
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
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
|
|
12175
|
-
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
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/
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
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
|
-
|
|
12205
|
-
|
|
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
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
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:
|
|
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.3",
|
|
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 = {
|
|
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;
|