cc-codeconductor 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36,14 +36,15 @@ function getExitCode(error) {
36
36
  }
37
37
  return ExitCode.VALIDATION_ERROR;
38
38
  }
39
- var ExitCode, CliError, ValidationError, UnsafeOperationError;
39
+ var ExitCode, CliError, ValidationError, UnsafeOperationError, CredentialGuardError;
40
40
  var init_errors = __esm(() => {
41
41
  ExitCode = {
42
42
  SUCCESS: 0,
43
43
  VALIDATION_ERROR: 1,
44
44
  UNSAFE_OPERATION: 2,
45
45
  UNSUPPORTED_PROJECT: 3,
46
- CONFIG_CONFLICT: 4
46
+ CONFIG_CONFLICT: 4,
47
+ CREDENTIAL_LEAK: 5
47
48
  };
48
49
  CliError = class CliError extends Error {
49
50
  code;
@@ -67,14 +68,24 @@ var init_errors = __esm(() => {
67
68
  this.name = "UnsafeOperationError";
68
69
  }
69
70
  };
71
+ CredentialGuardError = class CredentialGuardError extends CliError {
72
+ matches;
73
+ constructor(message, matches, details) {
74
+ super(message, ExitCode.CREDENTIAL_LEAK, details);
75
+ this.matches = matches;
76
+ this.name = "CredentialGuardError";
77
+ }
78
+ };
70
79
  });
71
80
 
72
81
  // src/core/filesystem/safety.ts
73
82
  var exports_safety = {};
74
83
  __export(exports_safety, {
75
84
  validateWritePath: () => validateWritePath,
85
+ scanForCredentials: () => scanForCredentials,
76
86
  isWritable: () => isWritable,
77
87
  isProtectedPath: () => isProtectedPath,
88
+ isCredentialContent: () => isCredentialContent,
78
89
  fileExists: () => fileExists
79
90
  });
80
91
  import { constants } from "node:fs";
@@ -103,6 +114,42 @@ async function isWritable(dir) {
103
114
  return false;
104
115
  }
105
116
  }
117
+ function buildCredentialRegexes(patterns) {
118
+ return patterns.map((keyword) => new RegExp(`(?:${keyword})\\s*[:=]\\s*[^\\s]{8,}`, "i"));
119
+ }
120
+ function scanForCredentials(filePath, content, secretPatterns) {
121
+ const regexes = buildCredentialRegexes(secretPatterns);
122
+ const lines = content.split(`
123
+ `);
124
+ const matches = [];
125
+ for (let i = 0;i < lines.length; i++) {
126
+ const line = lines[i];
127
+ for (const regex of regexes) {
128
+ const m = regex.exec(line);
129
+ if (m) {
130
+ matches.push({
131
+ filePath,
132
+ line: i + 1,
133
+ pattern: regex.source,
134
+ matched: m[0]
135
+ });
136
+ }
137
+ }
138
+ }
139
+ return matches;
140
+ }
141
+ function isCredentialContent(content, secretPatterns) {
142
+ const regexes = buildCredentialRegexes(secretPatterns);
143
+ const lines = content.split(`
144
+ `);
145
+ for (const line of lines) {
146
+ for (const regex of regexes) {
147
+ if (regex.test(line))
148
+ return true;
149
+ }
150
+ }
151
+ return false;
152
+ }
106
153
  var PROTECTED_PATHS;
107
154
  var init_safety = __esm(() => {
108
155
  PROTECTED_PATHS = [".git", ".env", ".env.local", ".env.production", "secrets", "credentials"];
@@ -11079,7 +11126,7 @@ function validateCouncilSpec(data) {
11079
11126
  function validateConfig(data) {
11080
11127
  return CodeConductorConfigSchema.parse(data);
11081
11128
  }
11082
- var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema;
11129
+ var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema;
11083
11130
  var init_schemas = __esm(() => {
11084
11131
  init_zod();
11085
11132
  CouncilAgentSpecSchema = exports_external.object({
@@ -11112,6 +11159,16 @@ var init_schemas = __esm(() => {
11112
11159
  signals: exports_external.array(exports_external.string()),
11113
11160
  confidence: exports_external.enum(["low", "medium", "high"])
11114
11161
  });
11162
+ CompileCheckConfigSchema = exports_external.object({
11163
+ enabled: exports_external.boolean(),
11164
+ command: exports_external.string().optional(),
11165
+ timeoutMs: exports_external.number().positive().optional()
11166
+ });
11167
+ LoopConfigSchema = exports_external.object({
11168
+ enabled: exports_external.boolean().optional().default(true),
11169
+ maxIterations: exports_external.number().int().positive().optional().default(3),
11170
+ maxTokenBudget: exports_external.number().nonnegative().optional().default(0)
11171
+ });
11115
11172
  CodeConductorConfigSchema = exports_external.object({
11116
11173
  version: exports_external.string(),
11117
11174
  project: exports_external.object({
@@ -11131,8 +11188,10 @@ var init_schemas = __esm(() => {
11131
11188
  }),
11132
11189
  safety: exports_external.object({
11133
11190
  destructiveCommands: exports_external.array(exports_external.string()),
11134
- secretPatterns: exports_external.array(exports_external.string())
11135
- })
11191
+ secretPatterns: exports_external.array(exports_external.string()),
11192
+ compileCheck: CompileCheckConfigSchema.optional()
11193
+ }),
11194
+ loop: LoopConfigSchema.optional()
11136
11195
  });
11137
11196
  RunnerTargetSchema = exports_external.enum([
11138
11197
  "opencode",
@@ -11176,6 +11235,80 @@ var init_schemas = __esm(() => {
11176
11235
  tools: exports_external.record(exports_external.string(), ToolProviderNamesSchema).optional(),
11177
11236
  permissions: PermissionProviderNamesSchema.optional()
11178
11237
  });
11238
+ ContractTargetSchema = exports_external.enum([
11239
+ "claude",
11240
+ "opencode",
11241
+ "codex",
11242
+ "gemini",
11243
+ "cursor",
11244
+ "agy"
11245
+ ]);
11246
+ ContractFormatSchema = exports_external.object({
11247
+ target: ContractTargetSchema,
11248
+ options: exports_external.record(exports_external.unknown()).optional()
11249
+ });
11250
+ AgentContractSchema = exports_external.object({
11251
+ council: CouncilSpecSchema,
11252
+ targets: exports_external.array(ContractFormatSchema),
11253
+ contractVersion: exports_external.string(),
11254
+ renderHints: exports_external.record(ContractTargetSchema, exports_external.record(exports_external.unknown())).optional()
11255
+ });
11256
+ CouncilFindingSchema = exports_external.object({
11257
+ category: exports_external.string(),
11258
+ severity: exports_external.enum(["info", "warning", "critical"]),
11259
+ message: exports_external.string(),
11260
+ agentId: exports_external.string()
11261
+ });
11262
+ CouncilVerdictInputSchema = exports_external.object({
11263
+ agentId: exports_external.string(),
11264
+ agentRole: exports_external.string(),
11265
+ status: exports_external.enum(["APPROVED", "REJECTED", "ABSTAIN"]),
11266
+ securityVeto: exports_external.boolean(),
11267
+ findings: exports_external.array(CouncilFindingSchema),
11268
+ summary: exports_external.string()
11269
+ });
11270
+ ConsensusConfigSchema = exports_external.object({
11271
+ algorithm: exports_external.enum(["majority", "unanimous"]),
11272
+ allowSecurityVeto: exports_external.boolean()
11273
+ });
11274
+ CouncilVerdictSchema = exports_external.object({
11275
+ status: exports_external.enum(["APPROVED", "REJECTED", "ESCALATED"]),
11276
+ totalAgents: exports_external.number(),
11277
+ approvedCount: exports_external.number(),
11278
+ rejectedCount: exports_external.number(),
11279
+ abstainedCount: exports_external.number(),
11280
+ vetoApplied: exports_external.boolean(),
11281
+ vetoByAgentId: exports_external.string().optional(),
11282
+ findings: exports_external.array(CouncilFindingSchema),
11283
+ summary: exports_external.string(),
11284
+ individualVerdicts: exports_external.array(CouncilVerdictInputSchema)
11285
+ });
11286
+ ClaudeAgentFileSchema = exports_external.object({
11287
+ path: exports_external.string().startsWith(".claude/"),
11288
+ content: exports_external.string(),
11289
+ overwrite: exports_external.boolean()
11290
+ });
11291
+ OpenCodeAgentFileSchema = exports_external.object({
11292
+ path: exports_external.string().startsWith(".opencode/"),
11293
+ content: exports_external.string(),
11294
+ overwrite: exports_external.boolean()
11295
+ });
11296
+ SentryStackFrameSchema = exports_external.object({
11297
+ filename: exports_external.string(),
11298
+ function: exports_external.string(),
11299
+ lineNo: exports_external.number(),
11300
+ colNo: exports_external.number().optional(),
11301
+ context: exports_external.array(exports_external.string())
11302
+ });
11303
+ SentryWebhookSchema = exports_external.object({
11304
+ issueId: exports_external.string(),
11305
+ title: exports_external.string(),
11306
+ culprit: exports_external.string(),
11307
+ filename: exports_external.string().optional(),
11308
+ stackTrace: exports_external.array(SentryStackFrameSchema),
11309
+ environment: exports_external.string().optional(),
11310
+ release: exports_external.string().optional()
11311
+ });
11179
11312
  });
11180
11313
 
11181
11314
  // src/core/presets/package-paths.ts
@@ -11865,6 +11998,59 @@ var init_agy_installer = __esm(() => {
11865
11998
  init_agy_council_generator();
11866
11999
  });
11867
12000
 
12001
+ // src/core/filesystem/credential-guard.ts
12002
+ import { readFile as readFile7 } from "node:fs/promises";
12003
+ import { resolve as resolve6 } from "node:path";
12004
+ async function loadPolicyPatterns() {
12005
+ try {
12006
+ const content = await readFile7(POLICY_PATH2, "utf-8");
12007
+ const parsed = $parse(content);
12008
+ if (Array.isArray(parsed.secretPatterns) && parsed.secretPatterns.length > 0) {
12009
+ return parsed.secretPatterns;
12010
+ }
12011
+ } catch {}
12012
+ return [];
12013
+ }
12014
+ function mergePatterns(...arrays) {
12015
+ const seen = new Set;
12016
+ const result = [];
12017
+ for (const arr of arrays) {
12018
+ for (const p of arr) {
12019
+ if (!seen.has(p)) {
12020
+ seen.add(p);
12021
+ result.push(p);
12022
+ }
12023
+ }
12024
+ }
12025
+ return result;
12026
+ }
12027
+ async function loadCredentialPatterns(config) {
12028
+ const policyPatterns = await loadPolicyPatterns();
12029
+ const configPatterns = config?.safety?.secretPatterns;
12030
+ if (configPatterns && configPatterns.length > 0) {
12031
+ return mergePatterns(configPatterns, policyPatterns, DEFAULT_SECRET_PATTERNS);
12032
+ }
12033
+ if (policyPatterns.length > 0) {
12034
+ return mergePatterns(policyPatterns, DEFAULT_SECRET_PATTERNS);
12035
+ }
12036
+ return DEFAULT_SECRET_PATTERNS;
12037
+ }
12038
+ var DEFAULT_SECRET_PATTERNS, POLICY_PATH2;
12039
+ var init_credential_guard = __esm(() => {
12040
+ init_dist();
12041
+ DEFAULT_SECRET_PATTERNS = [
12042
+ "password",
12043
+ "secret",
12044
+ "api_key",
12045
+ "token",
12046
+ "api[_-]?key",
12047
+ "access[_-]?token",
12048
+ "auth[_-]?token",
12049
+ "private[_-]?key"
12050
+ ];
12051
+ POLICY_PATH2 = resolve6(import.meta.dir, "..", "..", "..", "policy.yml");
12052
+ });
12053
+
11868
12054
  // src/core/filesystem/file-writer.ts
11869
12055
  var exports_file_writer = {};
11870
12056
  __export(exports_file_writer, {
@@ -11875,6 +12061,13 @@ import { access as access4, mkdir as mkdir4, writeFile as writeFile4 } from "nod
11875
12061
  import { dirname as dirname3 } from "node:path";
11876
12062
  async function writeGeneratedFiles(files, options) {
11877
12063
  const results = [];
12064
+ const secretPatterns = await loadCredentialPatterns(options.config);
12065
+ const allMatches = files.flatMap((file) => {
12066
+ return scanForCredentials(file.path, file.content, secretPatterns);
12067
+ });
12068
+ if (allMatches.length > 0) {
12069
+ throw new CredentialGuardError(`Credential leak detected in ${allMatches.length} file(s). No files written.`, allMatches);
12070
+ }
11878
12071
  for (const file of files) {
11879
12072
  if (!validateWritePath(file.path)) {
11880
12073
  results.push({
@@ -11933,6 +12126,7 @@ async function writeSingleFile(path, content, options) {
11933
12126
  }
11934
12127
  var init_file_writer = __esm(() => {
11935
12128
  init_errors();
12129
+ init_credential_guard();
11936
12130
  init_safety();
11937
12131
  });
11938
12132
 
@@ -11941,7 +12135,7 @@ init_errors();
11941
12135
  // package.json
11942
12136
  var package_default = {
11943
12137
  name: "cc-codeconductor",
11944
- version: "0.3.3",
12138
+ version: "0.4.0",
11945
12139
  description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
11946
12140
  keywords: [
11947
12141
  "ai",
@@ -13604,17 +13798,18 @@ async function doctorCommand(options) {
13604
13798
  }
13605
13799
 
13606
13800
  // src/commands/init.command.ts
13607
- import { access as access3, mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "node:fs/promises";
13801
+ import { access as access3, mkdir as mkdir3, readFile as readFile8, writeFile as writeFile3 } from "node:fs/promises";
13608
13802
  import { homedir as homedir5 } from "node:os";
13609
- import { basename, resolve as resolve7 } from "node:path";
13803
+ import { basename, resolve as resolve8 } from "node:path";
13610
13804
 
13611
13805
  // src/core/config/config-writer.ts
13612
13806
  init_dist();
13613
13807
  init_errors();
13614
13808
  import { access as access2, mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
13615
- import { resolve as resolve6 } from "node:path";
13809
+ import { resolve as resolve7 } from "node:path";
13616
13810
 
13617
13811
  // src/core/config/codeconductor-config.ts
13812
+ init_credential_guard();
13618
13813
  var DEFAULT_CONFIG = {
13619
13814
  version: "0.2.0",
13620
13815
  project: {
@@ -13633,7 +13828,17 @@ var DEFAULT_CONFIG = {
13633
13828
  },
13634
13829
  safety: {
13635
13830
  destructiveCommands: ["rm -rf", "drop table", "delete from"],
13636
- secretPatterns: ["password", "secret", "api_key", "token"]
13831
+ secretPatterns: DEFAULT_SECRET_PATTERNS,
13832
+ compileCheck: {
13833
+ enabled: true,
13834
+ command: "tsc --noEmit",
13835
+ timeoutMs: 120000
13836
+ }
13837
+ },
13838
+ loop: {
13839
+ enabled: true,
13840
+ maxIterations: 3,
13841
+ maxTokenBudget: 0
13637
13842
  }
13638
13843
  };
13639
13844
 
@@ -13642,9 +13847,9 @@ var CONFIG_DIR = ".codeconductor";
13642
13847
  var CONFIG_FILE2 = "config.yml";
13643
13848
  async function writeConfig(projectRoot, config = {}, force = false) {
13644
13849
  try {
13645
- const configDir = resolve6(projectRoot, CONFIG_DIR);
13850
+ const configDir = resolve7(projectRoot, CONFIG_DIR);
13646
13851
  await mkdir2(configDir, { recursive: true });
13647
- const configPath = resolve6(configDir, CONFIG_FILE2);
13852
+ const configPath = resolve7(configDir, CONFIG_FILE2);
13648
13853
  if (!force) {
13649
13854
  try {
13650
13855
  await access2(configPath);
@@ -13870,7 +14075,7 @@ async function initCommand(options) {
13870
14075
  }
13871
14076
  async function resolvePresetsToCopy() {
13872
14077
  const sources = [];
13873
- const bundledCouncil = resolve7(SRC_PRESETS_DIR, "council", "council.yml");
14078
+ const bundledCouncil = resolve8(SRC_PRESETS_DIR, "council", "council.yml");
13874
14079
  if (await fileExists2(bundledCouncil)) {
13875
14080
  sources.push({ name: "council.yml", sourcePath: bundledCouncil });
13876
14081
  }
@@ -13881,16 +14086,16 @@ async function resolvePresetsToCopy() {
13881
14086
  return sources;
13882
14087
  }
13883
14088
  async function copyPresets(baseDir, presets, force) {
13884
- const presetsDir = resolve7(baseDir, ".codeconductor", "presets");
14089
+ const presetsDir = resolve8(baseDir, ".codeconductor", "presets");
13885
14090
  await mkdir3(presetsDir, { recursive: true });
13886
14091
  const copied = [];
13887
14092
  for (const preset of presets) {
13888
- const destPath = resolve7(presetsDir, preset.name);
14093
+ const destPath = resolve8(presetsDir, preset.name);
13889
14094
  if (!force && await fileExists2(destPath)) {
13890
14095
  continue;
13891
14096
  }
13892
14097
  try {
13893
- const content = await readFile7(preset.sourcePath, "utf-8");
14098
+ const content = await readFile8(preset.sourcePath, "utf-8");
13894
14099
  await writeFile3(destPath, content, "utf-8");
13895
14100
  copied.push(`.codeconductor/presets/${preset.name}`);
13896
14101
  } catch (err2) {
@@ -13918,7 +14123,7 @@ init_claude_installer();
13918
14123
  init_codex_installer();
13919
14124
  init_opencode_installer();
13920
14125
  import { homedir as homedir6 } from "node:os";
13921
- import { resolve as resolve8 } from "node:path";
14126
+ import { resolve as resolve9 } from "node:path";
13922
14127
  init_file_writer();
13923
14128
  init_preset_loader();
13924
14129
 
@@ -13959,8 +14164,10 @@ async function installCommand(options) {
13959
14164
  }
13960
14165
  };
13961
14166
  }
14167
+ const configResult = await loadConfig(projectRoot);
14168
+ const config = configResult.success ? configResult.data : undefined;
13962
14169
  const spec = presetResult.data;
13963
- const writeOptions = { dryRun, force };
14170
+ const writeOptions = { dryRun, force, config };
13964
14171
  const allFiles = [];
13965
14172
  for (const t of targets) {
13966
14173
  let installer;
@@ -13986,12 +14193,12 @@ async function installCommand(options) {
13986
14193
  let targetPath = f.path;
13987
14194
  let targetBase = baseDir;
13988
14195
  if ((t === "agy" || t === "gemini") && isGlobal) {
13989
- targetBase = resolve8(homedir6(), ".gemini", "config");
14196
+ targetBase = resolve9(homedir6(), ".gemini", "config");
13990
14197
  targetPath = targetPath.replace(/^\.agents\/?/, "");
13991
14198
  }
13992
14199
  return {
13993
14200
  ...f,
13994
- path: resolve8(targetBase, targetPath)
14201
+ path: resolve9(targetBase, targetPath)
13995
14202
  };
13996
14203
  });
13997
14204
  const results = await writeGeneratedFiles(resolvedFiles, writeOptions);
@@ -14102,7 +14309,7 @@ async function installPresetCommand(options) {
14102
14309
 
14103
14310
  // src/commands/install-lsp.command.ts
14104
14311
  import { homedir as homedir8 } from "node:os";
14105
- import { resolve as resolve9 } from "node:path";
14312
+ import { resolve as resolve10 } from "node:path";
14106
14313
 
14107
14314
  // src/core/lsp/lsp-config-utils.ts
14108
14315
  function getLanguageServerConfig(lspIds) {
@@ -14627,7 +14834,9 @@ async function installLspCommand(options) {
14627
14834
  }
14628
14835
  const installer = createLspInstaller();
14629
14836
  const installReport = await installer.installAll(lsps, { dryRun });
14630
- const writeOptions = { dryRun, force };
14837
+ const configResult = await loadConfig(projectRoot);
14838
+ const config = configResult.success ? configResult.data : undefined;
14839
+ const writeOptions = { dryRun, force, config };
14631
14840
  const allConfigResults = [];
14632
14841
  for (const t of targets) {
14633
14842
  const generator = getLspConfigGenerator(t);
@@ -14637,7 +14846,7 @@ async function installLspCommand(options) {
14637
14846
  const generatedFiles = generator.generate(installReport.results);
14638
14847
  const resolvedFiles = generatedFiles.map((f) => ({
14639
14848
  ...f,
14640
- path: resolve9(baseDir, f.path)
14849
+ path: resolve10(baseDir, f.path)
14641
14850
  }));
14642
14851
  const results = await writeGeneratedFiles(resolvedFiles, writeOptions);
14643
14852
  for (const result of results) {
@@ -14729,9 +14938,278 @@ function getLspConfigGenerator(target) {
14729
14938
  }
14730
14939
  }
14731
14940
 
14941
+ // src/commands/debt-harvest.command.ts
14942
+ import { readdir as readdir2, readFile as readFile9, writeFile as writeFile5, mkdir as mkdir6 } from "node:fs/promises";
14943
+ import { join as join7, relative as relative2, extname } from "node:path";
14944
+ var DEFER_REGEX = /\/\/\s*defer\s*[-:]\s*(.+?)(?:\s*--(\w+))?\s*$/gm;
14945
+ var SOURCE_EXTENSIONS = new Set([
14946
+ ".ts",
14947
+ ".tsx",
14948
+ ".js",
14949
+ ".jsx",
14950
+ ".mjs",
14951
+ ".cjs",
14952
+ ".go",
14953
+ ".rs",
14954
+ ".java",
14955
+ ".kt",
14956
+ ".swift",
14957
+ ".cs",
14958
+ ".php",
14959
+ ".scala",
14960
+ ".dart",
14961
+ ".c",
14962
+ ".cpp",
14963
+ ".h",
14964
+ ".hpp"
14965
+ ]);
14966
+ async function walkDir(dir, extensions) {
14967
+ const results = [];
14968
+ try {
14969
+ const entries = await readdir2(dir, { withFileTypes: true });
14970
+ for (const entry of entries) {
14971
+ const fullPath = join7(dir, entry.name);
14972
+ if (entry.isDirectory()) {
14973
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
14974
+ continue;
14975
+ results.push(...await walkDir(fullPath, extensions));
14976
+ } else if (extensions.has(extname(entry.name))) {
14977
+ results.push(fullPath);
14978
+ }
14979
+ }
14980
+ } catch {}
14981
+ return results;
14982
+ }
14983
+ function extractDefers(content, filePath) {
14984
+ const entries = [];
14985
+ const lines = content.split(`
14986
+ `);
14987
+ for (let i = 0;i < lines.length; i++) {
14988
+ const line = lines[i];
14989
+ DEFER_REGEX.lastIndex = 0;
14990
+ const match = DEFER_REGEX.exec(line);
14991
+ if (match) {
14992
+ entries.push({
14993
+ file: filePath,
14994
+ line: i + 1,
14995
+ reason: match[1].trim(),
14996
+ tag: match[2]
14997
+ });
14998
+ }
14999
+ }
15000
+ return entries;
15001
+ }
15002
+ function renderLedger(entries) {
15003
+ const grouped = new Map;
15004
+ for (const entry of entries) {
15005
+ const tag = entry.tag ?? "unclassified";
15006
+ const group = grouped.get(tag) ?? [];
15007
+ group.push(entry);
15008
+ grouped.set(tag, group);
15009
+ }
15010
+ const lines = [
15011
+ "<!-- CODECONDUCTOR:BEGIN managed -->",
15012
+ "",
15013
+ "# Debt Ledger",
15014
+ "",
15015
+ "> Auto-generated by `codeconductor debt-harvest`. Do not edit manually.",
15016
+ ""
15017
+ ];
15018
+ const sortedTags = [...grouped.keys()].sort();
15019
+ for (const tag of sortedTags) {
15020
+ const tagEntries = grouped.get(tag);
15021
+ lines.push(`## ${tag}`);
15022
+ lines.push("");
15023
+ lines.push("| File | Line | Reason |");
15024
+ lines.push("| ---- | ---- | ------ |");
15025
+ for (const entry of tagEntries) {
15026
+ lines.push(`| ${entry.file} | ${entry.line} | ${entry.reason} |`);
15027
+ }
15028
+ lines.push("");
15029
+ }
15030
+ lines.push("<!-- CODECONDUCTOR:END managed -->");
15031
+ lines.push("");
15032
+ return lines.join(`
15033
+ `);
15034
+ }
15035
+ async function debtHarvestCommand(options) {
15036
+ const { projectRoot, dir = "src", output } = options;
15037
+ try {
15038
+ const targetDir = join7(projectRoot, dir);
15039
+ const files = await walkDir(targetDir, SOURCE_EXTENSIONS);
15040
+ const allEntries = [];
15041
+ for (const file of files) {
15042
+ const content = await readFile9(file, "utf-8");
15043
+ const entries = extractDefers(content, relative2(projectRoot, file));
15044
+ allEntries.push(...entries);
15045
+ }
15046
+ const ledgerDir = join7(projectRoot, ".codeconductor");
15047
+ await mkdir6(ledgerDir, { recursive: true });
15048
+ const ledgerPath = join7(ledgerDir, "debt-ledger.md");
15049
+ const ledgerContent = renderLedger(allEntries);
15050
+ await writeFile5(ledgerPath, ledgerContent, "utf-8");
15051
+ if (output === "json") {
15052
+ return {
15053
+ code: 0,
15054
+ data: {
15055
+ success: true,
15056
+ command: "debt-harvest",
15057
+ entries: allEntries,
15058
+ entryCount: allEntries.length,
15059
+ ledger: ledgerPath
15060
+ }
15061
+ };
15062
+ }
15063
+ return {
15064
+ code: 0,
15065
+ data: {
15066
+ success: true,
15067
+ command: "debt-harvest",
15068
+ message: `Found ${allEntries.length} deferred item(s) across ${files.length} file(s). Ledger written to ${ledgerPath}`,
15069
+ entries: allEntries
15070
+ }
15071
+ };
15072
+ } catch (error) {
15073
+ return {
15074
+ code: 1,
15075
+ data: {
15076
+ success: false,
15077
+ command: "debt-harvest",
15078
+ errors: [String(error)]
15079
+ }
15080
+ };
15081
+ }
15082
+ }
15083
+
15084
+ // src/commands/help.command.ts
15085
+ import { readdir as readdir3 } from "node:fs/promises";
15086
+ import { join as join8 } from "node:path";
15087
+ async function scanPresetDir(projectRoot, target) {
15088
+ const inventory = {
15089
+ target,
15090
+ skills: [],
15091
+ commands: [],
15092
+ agents: [],
15093
+ workflows: []
15094
+ };
15095
+ const presetDir = join8(projectRoot, "presets", target);
15096
+ try {
15097
+ const entries = await readdir3(presetDir, { withFileTypes: true });
15098
+ for (const entry of entries) {
15099
+ if (entry.isDirectory()) {
15100
+ const subDir = join8(presetDir, entry.name);
15101
+ const subEntries = await readdir3(subDir, { withFileTypes: true });
15102
+ for (const sub of subEntries) {
15103
+ if (sub.isFile()) {
15104
+ const itemName = sub.name.replace(/\.[^.]+$/, "");
15105
+ if (entry.name === "skills")
15106
+ inventory.skills.push(itemName);
15107
+ else if (entry.name === "commands")
15108
+ inventory.commands.push(itemName);
15109
+ else if (entry.name === "agents")
15110
+ inventory.agents.push(itemName);
15111
+ else if (entry.name === "workflows")
15112
+ inventory.workflows.push(itemName);
15113
+ } else if (sub.isDirectory()) {
15114
+ if (entry.name === "skills")
15115
+ inventory.skills.push(sub.name);
15116
+ else if (entry.name === "agents")
15117
+ inventory.agents.push(sub.name);
15118
+ }
15119
+ }
15120
+ }
15121
+ }
15122
+ } catch {}
15123
+ return inventory;
15124
+ }
15125
+ function renderHuman(inventory, defaultTarget) {
15126
+ const lines = [
15127
+ `CodeConductor Help — ${inventory.target}${inventory.target === defaultTarget ? " (active)" : ""}`,
15128
+ ""
15129
+ ];
15130
+ lines.push(`Skills (${inventory.skills.length}):`);
15131
+ if (inventory.skills.length === 0) {
15132
+ lines.push(" (none)");
15133
+ } else {
15134
+ for (const skill of inventory.skills.sort()) {
15135
+ lines.push(` - ${skill}`);
15136
+ }
15137
+ }
15138
+ lines.push("");
15139
+ lines.push(`Subagents (${inventory.agents.length}):`);
15140
+ if (inventory.agents.length === 0) {
15141
+ lines.push(" (none)");
15142
+ } else {
15143
+ for (const agent of inventory.agents.sort()) {
15144
+ lines.push(` - ${agent}`);
15145
+ }
15146
+ }
15147
+ lines.push("");
15148
+ lines.push(`Commands (${inventory.commands.length}):`);
15149
+ if (inventory.commands.length === 0) {
15150
+ lines.push(" (none)");
15151
+ } else {
15152
+ for (const cmd of inventory.commands.sort()) {
15153
+ lines.push(` - ${cmd}`);
15154
+ }
15155
+ }
15156
+ if (inventory.workflows.length > 0) {
15157
+ lines.push("");
15158
+ lines.push(`Workflows (${inventory.workflows.length}):`);
15159
+ for (const wf of inventory.workflows.sort()) {
15160
+ lines.push(` - ${wf}`);
15161
+ }
15162
+ }
15163
+ return lines.join(`
15164
+ `);
15165
+ }
15166
+ async function helpCommand(options) {
15167
+ const { projectRoot, target: overrideTarget, output } = options;
15168
+ try {
15169
+ let defaultTarget = "opencode";
15170
+ try {
15171
+ const configResult = await loadConfig(projectRoot);
15172
+ if (configResult.success) {
15173
+ defaultTarget = configResult.data.defaults.target;
15174
+ }
15175
+ } catch {}
15176
+ const target = overrideTarget ?? defaultTarget;
15177
+ const inventory = await scanPresetDir(projectRoot, target);
15178
+ if (output === "json") {
15179
+ return {
15180
+ code: 0,
15181
+ data: {
15182
+ success: true,
15183
+ command: "help",
15184
+ inventory,
15185
+ defaultTarget
15186
+ }
15187
+ };
15188
+ }
15189
+ return {
15190
+ code: 0,
15191
+ data: {
15192
+ success: true,
15193
+ command: "help",
15194
+ message: renderHuman(inventory, defaultTarget),
15195
+ inventory
15196
+ }
15197
+ };
15198
+ } catch (error) {
15199
+ return {
15200
+ code: 1,
15201
+ data: {
15202
+ success: false,
15203
+ command: "help",
15204
+ errors: [String(error)]
15205
+ }
15206
+ };
15207
+ }
15208
+ }
15209
+
14732
15210
  // src/commands/seo-audit.command.ts
14733
- import { writeFile as writeFile5, mkdir as mkdir6 } from "node:fs/promises";
14734
- import { dirname as dirname4, resolve as resolve10 } from "node:path";
15211
+ import { writeFile as writeFile6, mkdir as mkdir7 } from "node:fs/promises";
15212
+ import { dirname as dirname4, resolve as resolve11 } from "node:path";
14735
15213
 
14736
15214
  // src/infrastructure/http/safe-fetch.ts
14737
15215
  import { lookup } from "node:dns/promises";
@@ -14818,7 +15296,7 @@ async function safeFetch(urlString, options = {}) {
14818
15296
  }
14819
15297
  }
14820
15298
  async function delay(ms) {
14821
- return new Promise((resolve10) => setTimeout(resolve10, ms));
15299
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
14822
15300
  }
14823
15301
 
14824
15302
  // src/infrastructure/parsers/sitemap-parser.ts
@@ -16022,14 +16500,14 @@ async function seoAuditCommand(options) {
16022
16500
  formattedOutput = formatCli(report);
16023
16501
  }
16024
16502
  if (output) {
16025
- const outputPath = resolve10(options.projectRoot, output);
16026
- await mkdir6(dirname4(outputPath), { recursive: true });
16027
- await writeFile5(outputPath, formattedOutput, "utf-8");
16503
+ const outputPath = resolve11(options.projectRoot, output);
16504
+ await mkdir7(dirname4(outputPath), { recursive: true });
16505
+ await writeFile6(outputPath, formattedOutput, "utf-8");
16028
16506
  } else if (format === "markdown") {
16029
16507
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
16030
- const defaultPath = resolve10(options.projectRoot, "seo-reports", `audit-report-${timestamp}.md`);
16031
- await mkdir6(dirname4(defaultPath), { recursive: true });
16032
- await writeFile5(defaultPath, formattedOutput, "utf-8");
16508
+ const defaultPath = resolve11(options.projectRoot, "seo-reports", `audit-report-${timestamp}.md`);
16509
+ await mkdir7(dirname4(defaultPath), { recursive: true });
16510
+ await writeFile6(defaultPath, formattedOutput, "utf-8");
16033
16511
  process.stderr.write(`Report saved to: ${defaultPath}
16034
16512
  `);
16035
16513
  }
@@ -16041,7 +16519,7 @@ async function seoAuditCommand(options) {
16041
16519
  command: "seo audit",
16042
16520
  report,
16043
16521
  output: formattedOutput,
16044
- outputFile: output ? resolve10(options.projectRoot, output) : format === "markdown" ? resolve10(options.projectRoot, "seo-reports") : undefined
16522
+ outputFile: output ? resolve11(options.projectRoot, output) : format === "markdown" ? resolve11(options.projectRoot, "seo-reports") : undefined
16045
16523
  }
16046
16524
  };
16047
16525
  } catch (error) {
@@ -16057,8 +16535,8 @@ async function seoAuditCommand(options) {
16057
16535
  }
16058
16536
 
16059
16537
  // src/commands/seo-llms.command.ts
16060
- import { writeFile as writeFile6, mkdir as mkdir7 } from "node:fs/promises";
16061
- import { dirname as dirname5, resolve as resolve11 } from "node:path";
16538
+ import { writeFile as writeFile7, mkdir as mkdir8 } from "node:fs/promises";
16539
+ import { dirname as dirname5, resolve as resolve12 } from "node:path";
16062
16540
 
16063
16541
  // src/domain/seo/llms-generator.ts
16064
16542
  function extractTitle2(html) {
@@ -16201,9 +16679,9 @@ async function seoLlmsCommand(options) {
16201
16679
  }
16202
16680
  }) : await generateLlmsTxtFromUrl(url);
16203
16681
  process.stderr.write("\r" + " ".repeat(80) + "\r");
16204
- const outputPath = output ? resolve11(options.projectRoot, output) : resolve11(options.projectRoot, "llms.txt");
16205
- await mkdir7(dirname5(outputPath), { recursive: true });
16206
- await writeFile6(outputPath, result.content, "utf-8");
16682
+ const outputPath = output ? resolve12(options.projectRoot, output) : resolve12(options.projectRoot, "llms.txt");
16683
+ await mkdir8(dirname5(outputPath), { recursive: true });
16684
+ await writeFile7(outputPath, result.content, "utf-8");
16207
16685
  process.stderr.write(`Generated: ${outputPath} (${result.entries.length} entries)
16208
16686
  `);
16209
16687
  return {
@@ -16230,8 +16708,8 @@ async function seoLlmsCommand(options) {
16230
16708
 
16231
16709
  // src/commands/update.command.ts
16232
16710
  import { homedir as homedir9 } from "node:os";
16233
- import { resolve as resolve12, dirname as dirname6 } from "node:path";
16234
- import { mkdir as mkdir8, readFile as readFile8, writeFile as writeFile7, stat as stat3 } from "node:fs/promises";
16711
+ import { resolve as resolve13, dirname as dirname6 } from "node:path";
16712
+ import { mkdir as mkdir9, readFile as readFile10, writeFile as writeFile8, stat as stat3 } from "node:fs/promises";
16235
16713
  init_package_paths();
16236
16714
  async function updateCommand(options) {
16237
16715
  const { dryRun, force, global: isGlobal, output, projectRoot } = options;
@@ -16297,23 +16775,23 @@ async function updateCommand(options) {
16297
16775
  }
16298
16776
  const updated = [];
16299
16777
  if (updateResults.council) {
16300
- const localCouncil = resolve12(basePath, ".codeconductor", "presets", "council.yml");
16301
- const bundledCouncil = resolve12(SRC_PRESETS_DIR, "council", "council.yml");
16778
+ const localCouncil = resolve13(basePath, ".codeconductor", "presets", "council.yml");
16779
+ const bundledCouncil = resolve13(SRC_PRESETS_DIR, "council", "council.yml");
16302
16780
  try {
16303
- const content = await readFile8(bundledCouncil, "utf-8");
16304
- await mkdir8(dirname6(localCouncil), { recursive: true });
16305
- await writeFile7(localCouncil, content, "utf-8");
16781
+ const content = await readFile10(bundledCouncil, "utf-8");
16782
+ await mkdir9(dirname6(localCouncil), { recursive: true });
16783
+ await writeFile8(localCouncil, content, "utf-8");
16306
16784
  updated.push(localCouncil);
16307
16785
  } catch (e) {
16308
16786
  throw new Error(`Failed to update council.yml: ${e}`);
16309
16787
  }
16310
16788
  }
16311
16789
  if (updateResults.policy) {
16312
- const localPolicy = resolve12(basePath, ".codeconductor", "presets", "policy.yml");
16790
+ const localPolicy = resolve13(basePath, ".codeconductor", "presets", "policy.yml");
16313
16791
  try {
16314
- const content = await readFile8(POLICY_PATH, "utf-8");
16315
- await mkdir8(dirname6(localPolicy), { recursive: true });
16316
- await writeFile7(localPolicy, content, "utf-8");
16792
+ const content = await readFile10(POLICY_PATH, "utf-8");
16793
+ await mkdir9(dirname6(localPolicy), { recursive: true });
16794
+ await writeFile8(localPolicy, content, "utf-8");
16317
16795
  updated.push(localPolicy);
16318
16796
  } catch (e) {
16319
16797
  throw new Error(`Failed to update policy.yml: ${e}`);
@@ -16365,17 +16843,17 @@ async function updateCommand(options) {
16365
16843
  let targetPath = f.path;
16366
16844
  let targetBase = basePath;
16367
16845
  if ((targetName === "agy" || targetName === "gemini") && isGlobal) {
16368
- targetBase = resolve12(homedir9(), ".gemini", "config");
16846
+ targetBase = resolve13(homedir9(), ".gemini", "config");
16369
16847
  targetPath = targetPath.replace(/^\.agents\/?/, "");
16370
16848
  }
16371
16849
  return {
16372
16850
  ...f,
16373
- path: resolve12(targetBase, targetPath)
16851
+ path: resolve13(targetBase, targetPath)
16374
16852
  };
16375
16853
  });
16376
16854
  const filesToWrite = resolvedFiles.filter((f) => t.files.includes(f.path));
16377
16855
  if (filesToWrite.length > 0) {
16378
- const writeResults = await writeGeneratedFiles2(filesToWrite, { dryRun: false, force: true });
16856
+ const writeResults = await writeGeneratedFiles2(filesToWrite, { dryRun: false, force: true, config });
16379
16857
  for (const wr of writeResults) {
16380
16858
  if (wr.success) {
16381
16859
  updated.push(wr.path);
@@ -16398,19 +16876,19 @@ async function updateCommand(options) {
16398
16876
  for (const s of updateResults.skills) {
16399
16877
  newSkillsLock[s.id] = s.latestVersion;
16400
16878
  }
16401
- let lockDest = resolve12(basePath, ".codeconductor", "skills-lock.json");
16879
+ let lockDest = resolve13(basePath, ".codeconductor", "skills-lock.json");
16402
16880
  try {
16403
- const statAgents = await stat3(resolve12(basePath, ".agents"));
16881
+ const statAgents = await stat3(resolve13(basePath, ".agents"));
16404
16882
  if (statAgents.isDirectory()) {
16405
- const statCodeConductor = await stat3(resolve12(basePath, ".codeconductor")).catch(() => null);
16883
+ const statCodeConductor = await stat3(resolve13(basePath, ".codeconductor")).catch(() => null);
16406
16884
  if (!statCodeConductor) {
16407
- lockDest = resolve12(basePath, ".agents", "skills-lock.json");
16885
+ lockDest = resolve13(basePath, ".agents", "skills-lock.json");
16408
16886
  }
16409
16887
  }
16410
16888
  } catch {}
16411
16889
  try {
16412
- await mkdir8(dirname6(lockDest), { recursive: true });
16413
- await writeFile7(lockDest, JSON.stringify(newSkillsLock, null, 2), "utf-8");
16890
+ await mkdir9(dirname6(lockDest), { recursive: true });
16891
+ await writeFile8(lockDest, JSON.stringify(newSkillsLock, null, 2), "utf-8");
16414
16892
  updated.push(lockDest);
16415
16893
  } catch (e) {
16416
16894
  throw new Error(`Failed to write skills-lock.json: ${e}`);
@@ -16519,6 +16997,8 @@ Commands:
16519
16997
  seo llms Generate llms.txt from a URL or sitemap
16520
16998
  doctor Validate configuration and generated files
16521
16999
  update Update installed presets
17000
+ help / cc-help Show preset inventory (skills, subagents, commands)
17001
+ debt-harvest / harvest Scan source files for deferred debt items
16522
17002
 
16523
17003
  Options:
16524
17004
  --help, -h Show this help message
@@ -16562,8 +17042,6 @@ Examples:
16562
17042
  async function routeCommand(args, projectRoot) {
16563
17043
  const { command, subcommand, options, flags } = args;
16564
17044
  switch (command) {
16565
- case "help":
16566
- return { code: 0, data: { help: getHelp() } };
16567
17045
  case "init":
16568
17046
  return initCommand({
16569
17047
  projectRoot,
@@ -16632,6 +17110,20 @@ async function routeCommand(args, projectRoot) {
16632
17110
  global: options.global === true || options.global === "true",
16633
17111
  output: flags.output
16634
17112
  });
17113
+ case "help":
17114
+ case "cc-help":
17115
+ return helpCommand({
17116
+ projectRoot,
17117
+ target: options.target,
17118
+ output: flags.output
17119
+ });
17120
+ case "debt-harvest":
17121
+ case "harvest":
17122
+ return debtHarvestCommand({
17123
+ projectRoot,
17124
+ dir: options.dir,
17125
+ output: flags.output
17126
+ });
16635
17127
  case "seo": {
16636
17128
  if (subcommand === "audit") {
16637
17129
  return seoAuditCommand({