cc-codeconductor 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +100 -6
  2. package/dist/index.js +674 -65
  3. package/package.json +1 -1
  4. package/presets/agy/AGENTS.md +24 -0
  5. package/presets/agy/workflows/cc-pipeline.md +69 -0
  6. package/presets/codex/AGENTS.md +34 -0
  7. package/presets/laravel-tall/agents/architect.md +8 -0
  8. package/presets/laravel-tall/agents/implementer.md +12 -0
  9. package/presets/laravel-tall/laravel-tall.yml +38 -0
  10. package/presets/opencode/agents/architect.md +154 -61
  11. package/presets/opencode/agents/docs.md +126 -40
  12. package/presets/opencode/agents/implementer.md +100 -38
  13. package/presets/opencode/agents/orchestrator.md +41 -60
  14. package/presets/opencode/agents/repo-explorer.md +1 -1
  15. package/presets/opencode/agents/reviewer.md +142 -74
  16. package/presets/opencode/agents/task-coach.md +111 -59
  17. package/presets/opencode/prompts/v0.4.0/architect.md +221 -0
  18. package/presets/opencode/prompts/v0.4.0/complexity-auditor.md +89 -0
  19. package/presets/opencode/prompts/v0.4.0/docs.md +189 -0
  20. package/presets/opencode/prompts/v0.4.0/implementer.md +162 -0
  21. package/presets/opencode/prompts/v0.4.0/orchestrator.md +348 -0
  22. package/presets/opencode/prompts/v0.4.0/repo-explorer.md +110 -0
  23. package/presets/opencode/prompts/v0.4.0/reviewer.md +225 -0
  24. package/presets/opencode/prompts/v0.4.0/task-coach.md +155 -0
  25. package/presets/opencode/prompts/v0.4.0/tester.md +251 -0
  26. package/presets/opencode/skills/auth-token-inspector/SKILL.md +31 -0
  27. package/presets/opencode/skills/drizzle-schema-architect/SKILL.md +51 -0
  28. package/presets/opencode/skills/fastapi-pydantic-strict/SKILL.md +44 -0
  29. package/presets/opencode/skills/jpa-nplusone-detector/SKILL.md +46 -0
  30. package/presets/opencode/skills/livewire-alpine-bridge/SKILL.md +36 -0
  31. package/presets/opencode/skills/seo-analytics-injector/SKILL.md +44 -0
  32. package/presets/opencode/skills/spring-auth-auditor/SKILL.md +30 -0
  33. package/presets/opencode/skills/tailwind-responsive-auditor/SKILL.md +30 -0
  34. package/presets/opencode/skills/tdd-mutation-tester/SKILL.md +28 -0
  35. package/presets/python-data-api/agents/architect.md +8 -0
  36. package/presets/python-data-api/agents/implementer.md +9 -0
  37. package/presets/python-data-api/python-data-api.yml +37 -0
  38. package/presets/spring-kotlin-jpa/agents/architect.md +8 -0
  39. package/presets/spring-kotlin-jpa/agents/implementer.md +9 -0
  40. package/presets/spring-kotlin-jpa/spring-kotlin-jpa.yml +38 -0
  41. package/presets/ts-next-drizzle/agents/architect.md +8 -0
  42. package/presets/ts-next-drizzle/agents/implementer.md +10 -0
  43. package/presets/ts-next-drizzle/ts-next-drizzle.yml +41 -0
  44. package/src/presets/manifests/agy.yml +2 -2
  45. package/src/presets/manifests/claude.yml +2 -2
  46. package/src/presets/manifests/codex.yml +2 -2
  47. package/src/presets/manifests/cursor.yml +2 -2
  48. package/src/presets/manifests/gemini.yml +2 -2
  49. package/src/presets/manifests/opencode.yml +2 -2
  50. package/src/presets/models/agy.yml +7 -0
  51. package/src/presets/models/claude.yml +6 -0
  52. package/src/presets/models/codex.yml +6 -0
  53. package/src/presets/models/cursor.yml +6 -0
  54. package/src/presets/models/gemini.yml +6 -0
  55. package/src/presets/models/opencode.yml +6 -0
package/dist/index.js CHANGED
@@ -11126,7 +11126,7 @@ function validateCouncilSpec(data) {
11126
11126
  function validateConfig(data) {
11127
11127
  return CodeConductorConfigSchema.parse(data);
11128
11128
  }
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;
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, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema;
11130
11130
  var init_schemas = __esm(() => {
11131
11131
  init_zod();
11132
11132
  CouncilAgentSpecSchema = exports_external.object({
@@ -11167,7 +11167,11 @@ var init_schemas = __esm(() => {
11167
11167
  LoopConfigSchema = exports_external.object({
11168
11168
  enabled: exports_external.boolean().optional().default(true),
11169
11169
  maxIterations: exports_external.number().int().positive().optional().default(3),
11170
- maxTokenBudget: exports_external.number().nonnegative().optional().default(0)
11170
+ maxTokenBudget: exports_external.number().nonnegative().optional().default(0),
11171
+ compaction: exports_external.object({
11172
+ enabled: exports_external.boolean().optional().default(true),
11173
+ historyFile: exports_external.string().optional()
11174
+ }).optional()
11171
11175
  });
11172
11176
  CodeConductorConfigSchema = exports_external.object({
11173
11177
  version: exports_external.string(),
@@ -11309,6 +11313,34 @@ var init_schemas = __esm(() => {
11309
11313
  environment: exports_external.string().optional(),
11310
11314
  release: exports_external.string().optional()
11311
11315
  });
11316
+ MemoryPointerSchema = exports_external.object({
11317
+ topic_key: exports_external.string().min(1).max(128),
11318
+ id: exports_external.number().int().positive(),
11319
+ file: exports_external.string().min(1),
11320
+ summary: exports_external.string().min(1).max(200),
11321
+ timestamp: exports_external.string().datetime(),
11322
+ tags: exports_external.array(exports_external.string().max(64)).max(10).optional().default([])
11323
+ });
11324
+ MemoryIndexSchema = exports_external.object({
11325
+ version: exports_external.literal(1),
11326
+ pointers: exports_external.array(MemoryPointerSchema)
11327
+ });
11328
+ GoalTaskSchema = exports_external.object({
11329
+ id: exports_external.string(),
11330
+ title: exports_external.string(),
11331
+ type: exports_external.enum(["feature", "fix", "refactor", "test", "docs"]),
11332
+ risk: exports_external.enum(["low", "medium", "high"]),
11333
+ status: exports_external.enum(["pending", "in-progress", "done", "blocked"]),
11334
+ depends_on: exports_external.array(exports_external.string()).optional().default([]),
11335
+ acceptance_criteria: exports_external.array(exports_external.string()),
11336
+ blocked_reason: exports_external.string().optional(),
11337
+ context_scope: exports_external.enum(["isolated", "continuation", "full"]).optional().default("isolated")
11338
+ });
11339
+ GoalGraphSchema = exports_external.object({
11340
+ objective: exports_external.string(),
11341
+ tasks: exports_external.array(GoalTaskSchema),
11342
+ created_at: exports_external.string()
11343
+ });
11312
11344
  });
11313
11345
 
11314
11346
  // src/core/presets/package-paths.ts
@@ -12134,7 +12166,7 @@ init_errors();
12134
12166
  // package.json
12135
12167
  var package_default = {
12136
12168
  name: "cc-codeconductor",
12137
- version: "0.4.1",
12169
+ version: "0.4.3",
12138
12170
  description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
12139
12171
  keywords: [
12140
12172
  "ai",
@@ -13056,7 +13088,8 @@ function renderTemplate(content, modelConfig, filePath, locale = "en") {
13056
13088
  "tester",
13057
13089
  "reviewer",
13058
13090
  "docs",
13059
- "repo-explorer"
13091
+ "repo-explorer",
13092
+ "goal-planner"
13060
13093
  ];
13061
13094
  let result = templatedContent;
13062
13095
  for (const role of knownRoles) {
@@ -13144,8 +13177,20 @@ function injectMcpServers(jsonString, filePath) {
13144
13177
  if (isClaudeSettings || isAgyMcpConfig) {
13145
13178
  obj.mcpServers = mergeDeep(obj.mcpServers || {}, mcpServers);
13146
13179
  } else if (isOpencodeJsonc) {
13147
- obj.mcp = obj.mcp || {};
13148
- obj.mcp.servers = mergeDeep(obj.mcp.servers || {}, mcpServers);
13180
+ const opencodeMcp = {};
13181
+ for (const [key, server] of Object.entries(mcpServers)) {
13182
+ const commandArray = typeof server.command === "string" ? [server.command, ...server.args || []] : server.command;
13183
+ const formattedServer = {
13184
+ type: "local",
13185
+ command: commandArray,
13186
+ enabled: true
13187
+ };
13188
+ if (server.env) {
13189
+ formattedServer.environment = server.env;
13190
+ }
13191
+ opencodeMcp[key] = formattedServer;
13192
+ }
13193
+ obj.mcp = mergeDeep(obj.mcp || {}, opencodeMcp);
13149
13194
  }
13150
13195
  return JSON.stringify(obj, null, 2);
13151
13196
  } catch {
@@ -13887,7 +13932,7 @@ async function writeConfig(projectRoot, config = {}, force = false) {
13887
13932
  init_package_paths();
13888
13933
 
13889
13934
  // src/core/presets/preset-resolver.ts
13890
- var CURRENT_PRESET_VERSION = "v0.3.0";
13935
+ var CURRENT_PRESET_VERSION = "v0.4.0";
13891
13936
  function resolvePreset(target, profile) {
13892
13937
  const stack = resolveStack(profile);
13893
13938
  const isMonorepo = profile.signals.some((s) => [
@@ -13950,17 +13995,17 @@ function resolveStack(profile) {
13950
13995
  function resolveAssets(target) {
13951
13996
  switch (target) {
13952
13997
  case "opencode":
13953
- return ["opencode.jsonc", "agents", "commands", "prompts/v0.3.0", "skills"];
13998
+ return ["opencode.jsonc", "agents", "commands", "prompts/v0.4.0", "skills"];
13954
13999
  case "claude":
13955
- return ["CLAUDE.md", "settings.json", "commands", "skills", "agents", "prompts/v0.3.0"];
14000
+ return ["CLAUDE.md", "settings.json", "commands", "skills", "agents", "prompts/v0.4.0"];
13956
14001
  case "codex":
13957
- return ["AGENTS.md", "skills", "prompts/v0.3.0"];
14002
+ return ["AGENTS.md", "skills", "prompts/v0.4.0"];
13958
14003
  case "gemini":
13959
- return ["agents", "prompts/v0.3.0"];
14004
+ return ["agents", "prompts/v0.4.0"];
13960
14005
  case "cursor":
13961
- return ["agents", "prompts/v0.3.0"];
14006
+ return ["agents", "prompts/v0.4.0"];
13962
14007
  case "agy":
13963
- return ["AGENTS.md", "rules", "workflows", "skills", "prompts/v0.3.0"];
14008
+ return ["AGENTS.md", "rules", "workflows", "skills", "prompts/v0.4.0"];
13964
14009
  }
13965
14010
  }
13966
14011
 
@@ -15080,6 +15125,522 @@ async function debtHarvestCommand(options) {
15080
15125
  }
15081
15126
  }
15082
15127
 
15128
+ // src/core/goal/goal-planner.ts
15129
+ var TEMPLATES = [
15130
+ {
15131
+ keywords: ["login", "auth", "authentication", "signin", "sign-in"],
15132
+ tasks: [
15133
+ {
15134
+ id: "auth-schema",
15135
+ title: "Define auth data model and DB schema",
15136
+ type: "feature",
15137
+ risk: "high",
15138
+ status: "pending",
15139
+ context_scope: "isolated",
15140
+ depends_on: [],
15141
+ acceptance_criteria: [
15142
+ "User entity with email, password_hash, created_at fields",
15143
+ "Migration script creates users table",
15144
+ "Schema supports password reset token"
15145
+ ]
15146
+ },
15147
+ {
15148
+ id: "auth-api",
15149
+ title: "Define auth API contract",
15150
+ type: "feature",
15151
+ risk: "medium",
15152
+ status: "pending",
15153
+ context_scope: "isolated",
15154
+ depends_on: ["auth-schema"],
15155
+ acceptance_criteria: [
15156
+ "POST /auth/register endpoint defined",
15157
+ "POST /auth/login endpoint defined",
15158
+ "POST /auth/logout endpoint defined",
15159
+ "OpenAPI spec updated"
15160
+ ]
15161
+ },
15162
+ {
15163
+ id: "auth-impl",
15164
+ title: "Implement auth endpoints",
15165
+ type: "feature",
15166
+ risk: "high",
15167
+ status: "pending",
15168
+ context_scope: "isolated",
15169
+ depends_on: ["auth-api"],
15170
+ acceptance_criteria: [
15171
+ "Register endpoint hashes password and stores user",
15172
+ "Login endpoint validates credentials and returns session",
15173
+ "Logout endpoint invalidates session",
15174
+ "Input validation on all endpoints"
15175
+ ]
15176
+ },
15177
+ {
15178
+ id: "auth-tests",
15179
+ title: "Write auth tests",
15180
+ type: "test",
15181
+ risk: "medium",
15182
+ status: "pending",
15183
+ context_scope: "isolated",
15184
+ depends_on: ["auth-impl"],
15185
+ acceptance_criteria: [
15186
+ "Unit tests for password hashing",
15187
+ "Integration tests for register/login/logout flow",
15188
+ "Edge cases: duplicate email, invalid password, expired token"
15189
+ ]
15190
+ }
15191
+ ]
15192
+ },
15193
+ {
15194
+ keywords: ["crud", "resource", "api", "endpoint", "rest"],
15195
+ tasks: [
15196
+ {
15197
+ id: "crud-model",
15198
+ title: "Define data model and schema",
15199
+ type: "feature",
15200
+ risk: "medium",
15201
+ status: "pending",
15202
+ context_scope: "isolated",
15203
+ depends_on: [],
15204
+ acceptance_criteria: [
15205
+ "Entity definition with required fields",
15206
+ "Database migration script",
15207
+ "Schema follows project conventions"
15208
+ ]
15209
+ },
15210
+ {
15211
+ id: "crud-service",
15212
+ title: "Implement repository/service layer",
15213
+ type: "feature",
15214
+ risk: "medium",
15215
+ status: "pending",
15216
+ context_scope: "isolated",
15217
+ depends_on: ["crud-model"],
15218
+ acceptance_criteria: [
15219
+ "Create, Read (list + by id), Update, Delete operations",
15220
+ "Input validation",
15221
+ "Error handling for not-found and conflict"
15222
+ ]
15223
+ },
15224
+ {
15225
+ id: "crud-api",
15226
+ title: "Implement API endpoints",
15227
+ type: "feature",
15228
+ risk: "medium",
15229
+ status: "pending",
15230
+ context_scope: "isolated",
15231
+ depends_on: ["crud-service"],
15232
+ acceptance_criteria: [
15233
+ "REST endpoints for all CRUD operations",
15234
+ "Proper HTTP status codes",
15235
+ "Request/response schemas"
15236
+ ]
15237
+ },
15238
+ {
15239
+ id: "crud-tests",
15240
+ title: "Write tests",
15241
+ type: "test",
15242
+ risk: "low",
15243
+ status: "pending",
15244
+ context_scope: "isolated",
15245
+ depends_on: ["crud-api"],
15246
+ acceptance_criteria: [
15247
+ "Unit tests for service layer",
15248
+ "Integration tests for API endpoints",
15249
+ "Edge cases covered"
15250
+ ]
15251
+ }
15252
+ ]
15253
+ },
15254
+ {
15255
+ keywords: ["search", "filter", "query", "full-text", "fts"],
15256
+ tasks: [
15257
+ {
15258
+ id: "search-schema",
15259
+ title: "Design search schema and indexing strategy",
15260
+ type: "feature",
15261
+ risk: "medium",
15262
+ status: "pending",
15263
+ context_scope: "isolated",
15264
+ depends_on: [],
15265
+ acceptance_criteria: [
15266
+ "Search index definition",
15267
+ "Indexing strategy documented",
15268
+ "Performance requirements defined"
15269
+ ]
15270
+ },
15271
+ {
15272
+ id: "search-service",
15273
+ title: "Implement search service",
15274
+ type: "feature",
15275
+ risk: "medium",
15276
+ status: "pending",
15277
+ context_scope: "isolated",
15278
+ depends_on: ["search-schema"],
15279
+ acceptance_criteria: [
15280
+ "Full-text search with ranking",
15281
+ "Filter support",
15282
+ "Pagination"
15283
+ ]
15284
+ },
15285
+ {
15286
+ id: "search-api",
15287
+ title: "Implement search API",
15288
+ type: "feature",
15289
+ risk: "low",
15290
+ status: "pending",
15291
+ context_scope: "isolated",
15292
+ depends_on: ["search-service"],
15293
+ acceptance_criteria: [
15294
+ "GET /search endpoint with query params",
15295
+ "Response includes results + metadata",
15296
+ "Rate limiting applied"
15297
+ ]
15298
+ },
15299
+ {
15300
+ id: "search-tests",
15301
+ title: "Write search tests",
15302
+ type: "test",
15303
+ risk: "low",
15304
+ status: "pending",
15305
+ context_scope: "isolated",
15306
+ depends_on: ["search-api"],
15307
+ acceptance_criteria: [
15308
+ "Unit tests for search logic",
15309
+ "Integration tests with sample data",
15310
+ "Performance test for large datasets"
15311
+ ]
15312
+ }
15313
+ ]
15314
+ },
15315
+ {
15316
+ keywords: ["notification", "email", "alert", "push", "notify"],
15317
+ tasks: [
15318
+ {
15319
+ id: "notif-model",
15320
+ title: "Design notification data model",
15321
+ type: "feature",
15322
+ risk: "medium",
15323
+ status: "pending",
15324
+ context_scope: "isolated",
15325
+ depends_on: [],
15326
+ acceptance_criteria: [
15327
+ "Notification entity with type, payload, status, user_id",
15328
+ "Database migration",
15329
+ "Enum for notification types"
15330
+ ]
15331
+ },
15332
+ {
15333
+ id: "notif-service",
15334
+ title: "Implement notification service",
15335
+ type: "feature",
15336
+ risk: "medium",
15337
+ status: "pending",
15338
+ context_scope: "isolated",
15339
+ depends_on: ["notif-model"],
15340
+ acceptance_criteria: [
15341
+ "Send notification by type",
15342
+ "Queue-based delivery",
15343
+ "Retry logic for failed deliveries"
15344
+ ]
15345
+ },
15346
+ {
15347
+ id: "notif-api",
15348
+ title: "Implement notification API",
15349
+ type: "feature",
15350
+ risk: "low",
15351
+ status: "pending",
15352
+ context_scope: "isolated",
15353
+ depends_on: ["notif-service"],
15354
+ acceptance_criteria: [
15355
+ "POST /notifications endpoint",
15356
+ "GET /notifications endpoint with read/unread filter",
15357
+ "PATCH /notifications/:id/read endpoint"
15358
+ ]
15359
+ },
15360
+ {
15361
+ id: "notif-tests",
15362
+ title: "Write notification tests",
15363
+ type: "test",
15364
+ risk: "low",
15365
+ status: "pending",
15366
+ context_scope: "isolated",
15367
+ depends_on: ["notif-api"],
15368
+ acceptance_criteria: [
15369
+ "Unit tests for notification logic",
15370
+ "Integration tests for API",
15371
+ "Edge cases: invalid type, missing user"
15372
+ ]
15373
+ }
15374
+ ]
15375
+ },
15376
+ {
15377
+ keywords: ["migration", "migrate", "schema change", "database change", "column"],
15378
+ tasks: [
15379
+ {
15380
+ id: "migrate-script",
15381
+ title: "Write migration script",
15382
+ type: "feature",
15383
+ risk: "high",
15384
+ status: "pending",
15385
+ context_scope: "isolated",
15386
+ depends_on: [],
15387
+ acceptance_criteria: [
15388
+ "Migration is reversible",
15389
+ "Handles existing data gracefully",
15390
+ "Rollback script included"
15391
+ ]
15392
+ },
15393
+ {
15394
+ id: "migrate-model",
15395
+ title: "Update domain model",
15396
+ type: "refactor",
15397
+ risk: "medium",
15398
+ status: "pending",
15399
+ context_scope: "isolated",
15400
+ depends_on: ["migrate-script"],
15401
+ acceptance_criteria: [
15402
+ "Domain model reflects new schema",
15403
+ "Existing code paths updated",
15404
+ "Backward compatibility maintained"
15405
+ ]
15406
+ },
15407
+ {
15408
+ id: "migrate-dal",
15409
+ title: "Update data access layer",
15410
+ type: "refactor",
15411
+ risk: "medium",
15412
+ status: "pending",
15413
+ context_scope: "isolated",
15414
+ depends_on: ["migrate-model"],
15415
+ acceptance_criteria: [
15416
+ "Repository/service reads/writes new schema",
15417
+ "No broken queries",
15418
+ "Performance not degraded"
15419
+ ]
15420
+ },
15421
+ {
15422
+ id: "migrate-tests",
15423
+ title: "Write migration tests",
15424
+ type: "test",
15425
+ risk: "high",
15426
+ status: "pending",
15427
+ context_scope: "isolated",
15428
+ depends_on: ["migrate-dal"],
15429
+ acceptance_criteria: [
15430
+ "Migration up/down test",
15431
+ "Data integrity test",
15432
+ "Integration tests with new schema"
15433
+ ]
15434
+ }
15435
+ ]
15436
+ }
15437
+ ];
15438
+ var GENERIC_TEMPLATE = [
15439
+ {
15440
+ id: "plan-scope",
15441
+ title: "Clarify requirements and scope",
15442
+ type: "feature",
15443
+ risk: "low",
15444
+ status: "pending",
15445
+ context_scope: "isolated",
15446
+ depends_on: [],
15447
+ acceptance_criteria: ["Objective is well-defined", "Scope boundaries identified"]
15448
+ },
15449
+ {
15450
+ id: "plan-design",
15451
+ title: "Design technical approach",
15452
+ type: "feature",
15453
+ risk: "medium",
15454
+ status: "pending",
15455
+ context_scope: "isolated",
15456
+ depends_on: ["plan-scope"],
15457
+ acceptance_criteria: ["Architecture decisions documented", "API contracts defined"]
15458
+ },
15459
+ {
15460
+ id: "plan-impl",
15461
+ title: "Implement solution",
15462
+ type: "feature",
15463
+ risk: "medium",
15464
+ status: "pending",
15465
+ context_scope: "isolated",
15466
+ depends_on: ["plan-design"],
15467
+ acceptance_criteria: ["Core functionality implemented", "Follows project conventions"]
15468
+ },
15469
+ {
15470
+ id: "plan-tests",
15471
+ title: "Write tests and verify",
15472
+ type: "test",
15473
+ risk: "low",
15474
+ status: "pending",
15475
+ context_scope: "isolated",
15476
+ depends_on: ["plan-impl"],
15477
+ acceptance_criteria: ["Unit tests pass", "Integration tests pass", "No regressions"]
15478
+ }
15479
+ ];
15480
+ function matchTemplate(objective) {
15481
+ const lower = objective.toLowerCase();
15482
+ for (const template of TEMPLATES) {
15483
+ const matched = template.keywords.some((kw) => lower.includes(kw));
15484
+ if (matched)
15485
+ return template.tasks;
15486
+ }
15487
+ return GENERIC_TEMPLATE;
15488
+ }
15489
+ function planGoal(objective) {
15490
+ const tasks = matchTemplate(objective);
15491
+ return {
15492
+ objective,
15493
+ tasks: tasks.map((t) => ({ ...t, status: "pending" })),
15494
+ created_at: new Date().toISOString()
15495
+ };
15496
+ }
15497
+
15498
+ // src/core/goal/goal-state.ts
15499
+ init_dist();
15500
+ init_schemas();
15501
+ import { mkdir as mkdir7, readFile as readFile10, writeFile as writeFile6 } from "node:fs/promises";
15502
+ import { resolve as resolve10 } from "node:path";
15503
+ function validateGoalGraph(graph) {
15504
+ const ids = new Set(graph.tasks.map((t) => t.id));
15505
+ if (ids.size !== graph.tasks.length) {
15506
+ return err(new Error("Duplicate task IDs in goal graph"));
15507
+ }
15508
+ for (const task of graph.tasks) {
15509
+ for (const dep of task.depends_on ?? []) {
15510
+ if (!ids.has(dep)) {
15511
+ return err(new Error(`Task "${task.id}" depends on unknown task "${dep}"`));
15512
+ }
15513
+ }
15514
+ }
15515
+ const visited = new Set;
15516
+ const inStack = new Set;
15517
+ function dfs(taskId) {
15518
+ if (inStack.has(taskId))
15519
+ return true;
15520
+ if (visited.has(taskId))
15521
+ return false;
15522
+ visited.add(taskId);
15523
+ inStack.add(taskId);
15524
+ const task = graph.tasks.find((t) => t.id === taskId);
15525
+ if (task) {
15526
+ for (const dep of task.depends_on ?? []) {
15527
+ if (dfs(dep))
15528
+ return true;
15529
+ }
15530
+ }
15531
+ inStack.delete(taskId);
15532
+ return false;
15533
+ }
15534
+ for (const task of graph.tasks) {
15535
+ if (dfs(task.id)) {
15536
+ return err(new Error(`Cycle detected involving task "${task.id}"`));
15537
+ }
15538
+ }
15539
+ return ok(graph);
15540
+ }
15541
+ async function writeGoal(projectRoot, graph) {
15542
+ const validation = validateGoalGraph(graph);
15543
+ if (!validation.success)
15544
+ return validation;
15545
+ const dir = resolve10(projectRoot, ".codeconductor");
15546
+ await mkdir7(dir, { recursive: true });
15547
+ const yaml = $stringify(graph);
15548
+ await writeFile6(resolve10(dir, "current-goal.yml"), yaml, "utf-8");
15549
+ return ok(undefined);
15550
+ }
15551
+
15552
+ // src/commands/goal.command.ts
15553
+ function renderDependencyTree(tasks) {
15554
+ const lines = [];
15555
+ const taskMap = new Map(tasks.map((t) => [t.id, t]));
15556
+ const roots = tasks.filter((t) => !t.depends_on || t.depends_on.length === 0);
15557
+ function render(taskId, prefix, isLast) {
15558
+ const task = taskMap.get(taskId);
15559
+ if (!task)
15560
+ return;
15561
+ const connector = isLast ? "└── " : "├── ";
15562
+ const label = `${task.id}: ${task.title}`;
15563
+ lines.push(`${prefix}${connector}${label}`);
15564
+ const children = tasks.filter((t) => t.depends_on && t.depends_on.includes(taskId));
15565
+ const childPrefix = prefix + (isLast ? " " : "│ ");
15566
+ children.forEach((child, i) => {
15567
+ render(child.id, childPrefix, i === children.length - 1);
15568
+ });
15569
+ }
15570
+ roots.forEach((root, i) => {
15571
+ render(root.id, "", i === roots.length - 1);
15572
+ });
15573
+ return lines.join(`
15574
+ `);
15575
+ }
15576
+ async function goalCommand(options) {
15577
+ const { objective, projectRoot, output } = options;
15578
+ if (!objective || objective.trim().length === 0) {
15579
+ return {
15580
+ code: 1,
15581
+ data: {
15582
+ success: false,
15583
+ command: "goal",
15584
+ errors: ['Objective is required. Usage: codeconductor goal "<objective>"']
15585
+ }
15586
+ };
15587
+ }
15588
+ try {
15589
+ const graph = planGoal(objective);
15590
+ const writeResult = await writeGoal(projectRoot, graph);
15591
+ if (!writeResult.success) {
15592
+ return {
15593
+ code: 1,
15594
+ data: {
15595
+ success: false,
15596
+ command: "goal",
15597
+ errors: [`Failed to write goal: ${writeResult.error.message}`]
15598
+ }
15599
+ };
15600
+ }
15601
+ if (output === "json") {
15602
+ return {
15603
+ code: 0,
15604
+ data: {
15605
+ success: true,
15606
+ command: "goal",
15607
+ objective: graph.objective,
15608
+ tasks: graph.tasks,
15609
+ file: ".codeconductor/current-goal.yml"
15610
+ }
15611
+ };
15612
+ }
15613
+ const tree = renderDependencyTree(graph.tasks);
15614
+ const lines = [
15615
+ `Objective: ${graph.objective}`,
15616
+ "",
15617
+ "Task dependency graph:",
15618
+ tree,
15619
+ "",
15620
+ `Tasks: ${graph.tasks.length} total`,
15621
+ `File: .codeconductor/current-goal.yml`
15622
+ ];
15623
+ return {
15624
+ code: 0,
15625
+ data: {
15626
+ success: true,
15627
+ command: "goal",
15628
+ output: lines.join(`
15629
+ `)
15630
+ }
15631
+ };
15632
+ } catch (e) {
15633
+ return {
15634
+ code: 1,
15635
+ data: {
15636
+ success: false,
15637
+ command: "goal",
15638
+ errors: [e instanceof Error ? e.message : String(e)]
15639
+ }
15640
+ };
15641
+ }
15642
+ }
15643
+
15083
15644
  // src/commands/help.command.ts
15084
15645
  import { readdir as readdir3 } from "node:fs/promises";
15085
15646
  import { join as join8 } from "node:path";
@@ -15159,6 +15720,14 @@ function renderHuman(inventory, defaultTarget) {
15159
15720
  lines.push(` - ${wf}`);
15160
15721
  }
15161
15722
  }
15723
+ lines.push("");
15724
+ lines.push("Stack-specific presets (v0.4.0):");
15725
+ lines.push(" ts-next-drizzle Next.js / Astro, Tailwind, Drizzle ORM, Bun, Postgres");
15726
+ lines.push(" spring-kotlin-jpa Spring Boot, Kotlin/Java, Gradle, JPA, Hibernate");
15727
+ lines.push(" laravel-tall Laravel, Blade, Livewire, Alpine.js");
15728
+ lines.push(" python-data-api Python, FastAPI, Django, uv");
15729
+ lines.push(" Browse: presets/<preset-name>/agents/ — copy into the target runner.");
15730
+ lines.push(" Registry: src/core/presets/preset-registry.ts (listPresets / getPreset).");
15162
15731
  return lines.join(`
15163
15732
  `);
15164
15733
  }
@@ -15207,8 +15776,8 @@ async function helpCommand(options) {
15207
15776
  }
15208
15777
 
15209
15778
  // src/commands/seo-audit.command.ts
15210
- import { writeFile as writeFile6, mkdir as mkdir7 } from "node:fs/promises";
15211
- import { dirname as dirname4, resolve as resolve10 } from "node:path";
15779
+ import { writeFile as writeFile7, mkdir as mkdir8 } from "node:fs/promises";
15780
+ import { dirname as dirname4, resolve as resolve11 } from "node:path";
15212
15781
 
15213
15782
  // src/infrastructure/http/safe-fetch.ts
15214
15783
  import { lookup } from "node:dns/promises";
@@ -15295,7 +15864,7 @@ async function safeFetch(urlString, options = {}) {
15295
15864
  }
15296
15865
  }
15297
15866
  async function delay(ms) {
15298
- return new Promise((resolve10) => setTimeout(resolve10, ms));
15867
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
15299
15868
  }
15300
15869
 
15301
15870
  // src/infrastructure/parsers/sitemap-parser.ts
@@ -16499,14 +17068,14 @@ async function seoAuditCommand(options) {
16499
17068
  formattedOutput = formatCli(report);
16500
17069
  }
16501
17070
  if (output) {
16502
- const outputPath = resolve10(options.projectRoot, output);
16503
- await mkdir7(dirname4(outputPath), { recursive: true });
16504
- await writeFile6(outputPath, formattedOutput, "utf-8");
17071
+ const outputPath = resolve11(options.projectRoot, output);
17072
+ await mkdir8(dirname4(outputPath), { recursive: true });
17073
+ await writeFile7(outputPath, formattedOutput, "utf-8");
16505
17074
  } else if (format === "markdown") {
16506
17075
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
16507
- const defaultPath = resolve10(options.projectRoot, "seo-reports", `audit-report-${timestamp}.md`);
16508
- await mkdir7(dirname4(defaultPath), { recursive: true });
16509
- await writeFile6(defaultPath, formattedOutput, "utf-8");
17076
+ const defaultPath = resolve11(options.projectRoot, "seo-reports", `audit-report-${timestamp}.md`);
17077
+ await mkdir8(dirname4(defaultPath), { recursive: true });
17078
+ await writeFile7(defaultPath, formattedOutput, "utf-8");
16510
17079
  process.stderr.write(`Report saved to: ${defaultPath}
16511
17080
  `);
16512
17081
  }
@@ -16518,7 +17087,7 @@ async function seoAuditCommand(options) {
16518
17087
  command: "seo audit",
16519
17088
  report,
16520
17089
  output: formattedOutput,
16521
- outputFile: output ? resolve10(options.projectRoot, output) : format === "markdown" ? resolve10(options.projectRoot, "seo-reports") : undefined
17090
+ outputFile: output ? resolve11(options.projectRoot, output) : format === "markdown" ? resolve11(options.projectRoot, "seo-reports") : undefined
16522
17091
  }
16523
17092
  };
16524
17093
  } catch (error) {
@@ -16534,8 +17103,8 @@ async function seoAuditCommand(options) {
16534
17103
  }
16535
17104
 
16536
17105
  // src/commands/seo-llms.command.ts
16537
- import { writeFile as writeFile7, mkdir as mkdir8 } from "node:fs/promises";
16538
- import { dirname as dirname5, resolve as resolve11 } from "node:path";
17106
+ import { writeFile as writeFile8, mkdir as mkdir9 } from "node:fs/promises";
17107
+ import { dirname as dirname5, resolve as resolve12 } from "node:path";
16539
17108
 
16540
17109
  // src/domain/seo/llms-generator.ts
16541
17110
  function extractTitle2(html) {
@@ -16678,9 +17247,9 @@ async function seoLlmsCommand(options) {
16678
17247
  }
16679
17248
  }) : await generateLlmsTxtFromUrl(url);
16680
17249
  process.stderr.write("\r" + " ".repeat(80) + "\r");
16681
- const outputPath = output ? resolve11(options.projectRoot, output) : resolve11(options.projectRoot, "llms.txt");
16682
- await mkdir8(dirname5(outputPath), { recursive: true });
16683
- await writeFile7(outputPath, result.content, "utf-8");
17250
+ const outputPath = output ? resolve12(options.projectRoot, output) : resolve12(options.projectRoot, "llms.txt");
17251
+ await mkdir9(dirname5(outputPath), { recursive: true });
17252
+ await writeFile8(outputPath, result.content, "utf-8");
16684
17253
  process.stderr.write(`Generated: ${outputPath} (${result.entries.length} entries)
16685
17254
  `);
16686
17255
  return {
@@ -16707,8 +17276,8 @@ async function seoLlmsCommand(options) {
16707
17276
 
16708
17277
  // src/commands/update.command.ts
16709
17278
  import { homedir as homedir9 } from "node:os";
16710
- import { resolve as resolve12, dirname as dirname6 } from "node:path";
16711
- import { mkdir as mkdir9, readFile as readFile10, writeFile as writeFile8, stat as stat3 } from "node:fs/promises";
17279
+ import { resolve as resolve13, dirname as dirname6 } from "node:path";
17280
+ import { mkdir as mkdir10, readFile as readFile11, writeFile as writeFile9, stat as stat3 } from "node:fs/promises";
16712
17281
  init_package_paths();
16713
17282
  async function updateCommand(options) {
16714
17283
  const { dryRun, force, global: isGlobal, output, projectRoot } = options;
@@ -16774,23 +17343,23 @@ async function updateCommand(options) {
16774
17343
  }
16775
17344
  const updated = [];
16776
17345
  if (updateResults.council) {
16777
- const localCouncil = resolve12(basePath, ".codeconductor", "presets", "council.yml");
16778
- const bundledCouncil = resolve12(SRC_PRESETS_DIR, "council", "council.yml");
17346
+ const localCouncil = resolve13(basePath, ".codeconductor", "presets", "council.yml");
17347
+ const bundledCouncil = resolve13(SRC_PRESETS_DIR, "council", "council.yml");
16779
17348
  try {
16780
- const content = await readFile10(bundledCouncil, "utf-8");
16781
- await mkdir9(dirname6(localCouncil), { recursive: true });
16782
- await writeFile8(localCouncil, content, "utf-8");
17349
+ const content = await readFile11(bundledCouncil, "utf-8");
17350
+ await mkdir10(dirname6(localCouncil), { recursive: true });
17351
+ await writeFile9(localCouncil, content, "utf-8");
16783
17352
  updated.push(localCouncil);
16784
17353
  } catch (e) {
16785
17354
  throw new Error(`Failed to update council.yml: ${e}`);
16786
17355
  }
16787
17356
  }
16788
17357
  if (updateResults.policy) {
16789
- const localPolicy = resolve12(basePath, ".codeconductor", "presets", "policy.yml");
17358
+ const localPolicy = resolve13(basePath, ".codeconductor", "presets", "policy.yml");
16790
17359
  try {
16791
- const content = await readFile10(POLICY_PATH, "utf-8");
16792
- await mkdir9(dirname6(localPolicy), { recursive: true });
16793
- await writeFile8(localPolicy, content, "utf-8");
17360
+ const content = await readFile11(POLICY_PATH, "utf-8");
17361
+ await mkdir10(dirname6(localPolicy), { recursive: true });
17362
+ await writeFile9(localPolicy, content, "utf-8");
16794
17363
  updated.push(localPolicy);
16795
17364
  } catch (e) {
16796
17365
  throw new Error(`Failed to update policy.yml: ${e}`);
@@ -16842,12 +17411,12 @@ async function updateCommand(options) {
16842
17411
  let targetPath = f.path;
16843
17412
  let targetBase = basePath;
16844
17413
  if ((targetName === "agy" || targetName === "gemini") && isGlobal) {
16845
- targetBase = resolve12(homedir9(), ".gemini", "config");
17414
+ targetBase = resolve13(homedir9(), ".gemini", "config");
16846
17415
  targetPath = targetPath.replace(/^\.agents\/?/, "");
16847
17416
  }
16848
17417
  return {
16849
17418
  ...f,
16850
- path: resolve12(targetBase, targetPath)
17419
+ path: resolve13(targetBase, targetPath)
16851
17420
  };
16852
17421
  });
16853
17422
  const filesToWrite = resolvedFiles.filter((f) => t.files.includes(f.path));
@@ -16875,19 +17444,19 @@ async function updateCommand(options) {
16875
17444
  for (const s of updateResults.skills) {
16876
17445
  newSkillsLock[s.id] = s.latestVersion;
16877
17446
  }
16878
- let lockDest = resolve12(basePath, ".codeconductor", "skills-lock.json");
17447
+ let lockDest = resolve13(basePath, ".codeconductor", "skills-lock.json");
16879
17448
  try {
16880
- const statAgents = await stat3(resolve12(basePath, ".agents"));
17449
+ const statAgents = await stat3(resolve13(basePath, ".agents"));
16881
17450
  if (statAgents.isDirectory()) {
16882
- const statCodeConductor = await stat3(resolve12(basePath, ".codeconductor")).catch(() => null);
17451
+ const statCodeConductor = await stat3(resolve13(basePath, ".codeconductor")).catch(() => null);
16883
17452
  if (!statCodeConductor) {
16884
- lockDest = resolve12(basePath, ".agents", "skills-lock.json");
17453
+ lockDest = resolve13(basePath, ".agents", "skills-lock.json");
16885
17454
  }
16886
17455
  }
16887
17456
  } catch {}
16888
17457
  try {
16889
- await mkdir9(dirname6(lockDest), { recursive: true });
16890
- await writeFile8(lockDest, JSON.stringify(newSkillsLock, null, 2), "utf-8");
17458
+ await mkdir10(dirname6(lockDest), { recursive: true });
17459
+ await writeFile9(lockDest, JSON.stringify(newSkillsLock, null, 2), "utf-8");
16891
17460
  updated.push(lockDest);
16892
17461
  } catch (e) {
16893
17462
  throw new Error(`Failed to write skills-lock.json: ${e}`);
@@ -16988,26 +17557,47 @@ Usage: npx cc-codeconductor <command> [options]
16988
17557
 
16989
17558
  Commands:
16990
17559
  init Initialize CodeConductor in a project
16991
- detect Detect project stack and recommended presets
16992
- install council Install generated council spec files to runner targets
16993
- install preset Install full preset (agents, prompts, skills, commands)
16994
- install lsp Install and configure LSP servers for AI coding tools
16995
- seo audit Run SEO audit on a URL or sitemap
16996
- seo llms Generate llms.txt from a URL or sitemap
16997
- doctor Validate configuration and generated files
16998
- update Update installed presets
16999
- help / cc-help Show preset inventory (skills, subagents, commands)
17000
- debt-harvest / harvest Scan source files for deferred debt items
17560
+ detect Detect project stack and recommended presets
17561
+ install council Install generated council spec files to runner targets
17562
+ install preset Install full preset (agents, prompts, skills, commands)
17563
+ install lsp Install and configure LSP servers for AI coding tools
17564
+ seo audit Run SEO audit on a URL or sitemap
17565
+ seo llms Generate llms.txt from a URL or sitemap
17566
+ doctor Validate configuration and generated files
17567
+ update Update installed presets
17568
+ help / cc-help Show preset inventory (skills, subagents, commands)
17569
+ debt-harvest / harvest Scan source files for deferred debt items
17570
+ goal / cc-goal Plan goal into task graph with dependencies
17001
17571
 
17002
17572
  Options:
17003
- --help, -h Show this help message
17004
- --version, -v Show package version
17005
- --dry-run Show what would happen without writing files
17006
- --force Allow overwriting existing files
17007
- --global Install to home directory (~/.claude, ~/.opencode, etc.)
17008
- --output, -o Output mode: human or json
17009
- --lang Comma-separated list of languages (e.g., typescript,php,python)
17010
- --locale Instruction language for agent files: en (default) | es
17573
+ --help, -h Show this help message
17574
+ --version, -v Show package version
17575
+ --dry-run Show what would happen without writing files
17576
+ --force Allow overwriting existing files
17577
+ --global Install to home directory (~/.claude, ~/.opencode, etc.)
17578
+ --output, -o Output mode: human or json
17579
+ --lang Comma-separated list of languages (e.g., typescript,php,python)
17580
+ --locale Instruction language for agent files: en (default) | es
17581
+ --target Runner target: opencode, claude, codex, gemini, cursor, agy, all
17582
+
17583
+ Stack-specific presets (v0.4.0, registered in preset-registry):
17584
+ ts-next-drizzle Next.js / Astro, Tailwind, Drizzle ORM, Bun, Postgres
17585
+ spring-kotlin-jpa Spring Boot, Kotlin/Java, Gradle, JPA, Hibernate
17586
+ laravel-tall Laravel, Blade, Livewire, Alpine.js
17587
+ python-data-api Python, FastAPI, Django, uv
17588
+ Listed programmatically via listPresets() in src/core/presets/preset-registry.ts.
17589
+ Stack detection wires matching specialized skills automatically when
17590
+ init/detect identifies the stack.
17591
+
17592
+ Workflow Loop Core (v0.4.0):
17593
+ runWorkflowPipeline() in src/core/pipeline/workflow-loop.ts runs the
17594
+ 8-phase loop (intake -> structure -> design -> test -> implement ->
17595
+ validate -> council -> compact) with wall-clock, files-modified and
17596
+ lines-changed guardrails and STOP gates after Design and Council.
17597
+
17598
+ Council consensus v0.4.0:
17599
+ Per-agent confidence thresholds (< 0.6 or average < 0.7 escalate)
17600
+ and a complianceVeto channel that overrides majority like securityVeto.
17011
17601
 
17012
17602
  Examples:
17013
17603
  npx cc-codeconductor init
@@ -17036,6 +17626,16 @@ Examples:
17036
17626
  npx cc-codeconductor seo audit --sitemap https://example.com/sitemap.xml --format markdown
17037
17627
  npx cc-codeconductor seo llms --sitemap https://example.com/sitemap.xml
17038
17628
  npx cc-codeconductor seo llms --url https://example.com --output llms.txt
17629
+ npx cc-codeconductor goal "Add user authentication"
17630
+ npx cc-codeconductor cc-goal "Implement CRUD for invoices"
17631
+ npx cc-codeconductor help --target opencode
17632
+ npx cc-codeconductor help --target claude --output json
17633
+
17634
+ Docs: https://github.com/lgzarturo/codeconductor/tree/main/docs
17635
+ docs/v0.4.0-release-notes.md v0.4.0 feature breakdown
17636
+ docs/routing-policy.md Risk-based routing (v0.4.0)
17637
+ docs/prompt-versioning.md Agent contract versions
17638
+ docs/usage-cli.md Detailed CLI reference
17039
17639
  `;
17040
17640
  }
17041
17641
  async function routeCommand(args, projectRoot) {
@@ -17123,6 +17723,13 @@ async function routeCommand(args, projectRoot) {
17123
17723
  dir: options.dir,
17124
17724
  output: flags.output
17125
17725
  });
17726
+ case "goal":
17727
+ case "cc-goal":
17728
+ return goalCommand({
17729
+ objective: subcommand || options.objective || "",
17730
+ projectRoot,
17731
+ output: flags.output
17732
+ });
17126
17733
  case "seo": {
17127
17734
  if (subcommand === "audit") {
17128
17735
  return seoAuditCommand({
@@ -17187,6 +17794,8 @@ async function runCli(args) {
17187
17794
  const data = result.data;
17188
17795
  if ("help" in data) {
17189
17796
  console.log(data.help);
17797
+ } else if ("message" in data && typeof data.message === "string") {
17798
+ console.log(data.message);
17190
17799
  } else {
17191
17800
  if ("errors" in data && Array.isArray(data.errors) && data.errors.length > 0) {
17192
17801
  data.errors.forEach((e) => console.error(e));