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