@corbat-tech/coco 2.35.0 → 2.36.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
@@ -1,10 +1,10 @@
1
1
  import { Logger } from 'tslog';
2
2
  import * as fs4 from 'fs';
3
- import fs4__default, { readFileSync, constants } from 'fs';
3
+ import fs4__default, { readFileSync, mkdirSync, appendFileSync, writeFileSync, constants } from 'fs';
4
4
  import * as path17 from 'path';
5
5
  import path17__default, { dirname, join, basename, resolve } from 'path';
6
6
  import * as fs16 from 'fs/promises';
7
- import fs16__default, { access, readFile, readdir, writeFile, mkdir } from 'fs/promises';
7
+ import fs16__default, { access, readFile, readdir, writeFile, mkdir, rm } from 'fs/promises';
8
8
  import { randomUUID, randomBytes, createHash } from 'crypto';
9
9
  import * as http from 'http';
10
10
  import { fileURLToPath, URL as URL$1 } from 'url';
@@ -23,7 +23,7 @@ import Anthropic from '@anthropic-ai/sdk';
23
23
  import { jsonrepair } from 'jsonrepair';
24
24
  import OpenAI from 'openai';
25
25
  import { GoogleGenAI, FunctionCallingConfigMode } from '@google/genai';
26
- import 'events';
26
+ import { EventEmitter } from 'events';
27
27
  import 'minimatch';
28
28
  import { simpleGit } from 'simple-git';
29
29
  import { diffWords } from 'diff';
@@ -275,6 +275,10 @@ function getCatalogDefaultModel(provider) {
275
275
  function getCatalogModel(provider, modelId) {
276
276
  return getProviderCatalogEntry(provider).models.find((modelEntry) => modelEntry.id === modelId);
277
277
  }
278
+ function getCatalogRecommendedModel(provider) {
279
+ const entry = getProviderCatalogEntry(provider);
280
+ return entry.models.find((modelEntry) => modelEntry.recommended) ?? entry.models[0];
281
+ }
278
282
  function getCatalogContextWindow(provider, modelId, fallback) {
279
283
  if (!modelId) return fallback;
280
284
  const exact = getCatalogModel(provider, modelId);
@@ -10603,16 +10607,16 @@ var QualityEvaluator = class {
10603
10607
  * Find source files in project, adapting to the detected language stack.
10604
10608
  */
10605
10609
  async findSourceFiles() {
10606
- const { access: access13 } = await import('fs/promises');
10607
- const { join: join19 } = await import('path');
10610
+ const { access: access14 } = await import('fs/promises');
10611
+ const { join: join20 } = await import('path');
10608
10612
  let isJava = false;
10609
10613
  try {
10610
- await access13(join19(this.projectPath, "pom.xml"));
10614
+ await access14(join20(this.projectPath, "pom.xml"));
10611
10615
  isJava = true;
10612
10616
  } catch {
10613
10617
  for (const f of ["build.gradle", "build.gradle.kts"]) {
10614
10618
  try {
10615
- await access13(join19(this.projectPath, f));
10619
+ await access14(join20(this.projectPath, f));
10616
10620
  isJava = true;
10617
10621
  break;
10618
10622
  } catch {
@@ -18747,6 +18751,89 @@ function createResilientProvider(provider, config) {
18747
18751
 
18748
18752
  // src/providers/runtime-capabilities.ts
18749
18753
  init_catalog();
18754
+ function hasCapability(model2, capability) {
18755
+ return model2?.capabilities.includes(capability) ?? false;
18756
+ }
18757
+ function selectEndpoint(provider, model2) {
18758
+ if (hasCapability(model2, "anthropic-messages")) return "anthropic-messages";
18759
+ if (hasCapability(model2, "gemini-generate-content")) return "gemini-generate-content";
18760
+ if (provider === "openai" || provider === "codex") {
18761
+ if (hasCapability(model2, "openai-responses")) return "openai-responses";
18762
+ }
18763
+ return "openai-chat";
18764
+ }
18765
+ function buildRestrictions(provider, model2, endpoint, supportsReasoning, supportsToolUse) {
18766
+ const restrictions = [];
18767
+ if (provider === "copilot" && endpoint === "openai-chat" && supportsToolUse) {
18768
+ restrictions.push(
18769
+ "Copilot uses an OpenAI-compatible Chat Completions route; Coco omits reasoning_effort on tool calls to avoid upstream 400 errors."
18770
+ );
18771
+ }
18772
+ if (provider !== "openai" && provider !== "codex" && endpoint === "openai-chat" && supportsReasoning) {
18773
+ restrictions.push(
18774
+ "OpenAI-compatible providers only receive reasoning fields when the provider is explicitly verified."
18775
+ );
18776
+ }
18777
+ if (!supportsToolUse) {
18778
+ restrictions.push("Function tools are not advertised for this model.");
18779
+ }
18780
+ if (model2.toLowerCase().includes("deprecated")) {
18781
+ restrictions.push(
18782
+ "Model name suggests deprecation; prefer a catalog current/recommended model."
18783
+ );
18784
+ }
18785
+ return restrictions;
18786
+ }
18787
+ function getProviderRuntimeCapability(provider, modelId) {
18788
+ const providerCatalog = getProviderCatalogEntry(provider);
18789
+ const model2 = modelId ?? providerCatalog.defaultModel;
18790
+ const catalogModel = getCatalogModel(provider, model2);
18791
+ const thinking = getThinkingCapability(provider, model2);
18792
+ const endpoint = selectEndpoint(provider, catalogModel);
18793
+ const supportsToolUse = hasCapability(catalogModel, "tool-use");
18794
+ const supportsReasoning = thinking.supported;
18795
+ return {
18796
+ provider,
18797
+ model: model2,
18798
+ catalogModel,
18799
+ status: catalogModel?.status ?? "unverified",
18800
+ endpoint,
18801
+ supportsStreaming: hasCapability(catalogModel, "streaming"),
18802
+ supportsToolUse,
18803
+ supportsVision: hasCapability(catalogModel, "vision"),
18804
+ supportsReasoning,
18805
+ reasoningKinds: thinking.kinds,
18806
+ defaultReasoning: thinking.defaultMode,
18807
+ contextWindow: catalogModel?.contextWindow ?? 0,
18808
+ maxOutputTokens: catalogModel?.maxOutputTokens,
18809
+ sourceUrl: catalogModel?.source.url,
18810
+ restrictions: buildRestrictions(provider, model2, endpoint, supportsReasoning, supportsToolUse)
18811
+ };
18812
+ }
18813
+ async function probeProviderRuntimeCapability(provider, modelId, checkAvailability) {
18814
+ const capability = getProviderRuntimeCapability(provider, modelId);
18815
+ if (!checkAvailability) {
18816
+ return {
18817
+ ...capability,
18818
+ available: "not-checked",
18819
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString()
18820
+ };
18821
+ }
18822
+ try {
18823
+ return {
18824
+ ...capability,
18825
+ available: await checkAvailability(),
18826
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString()
18827
+ };
18828
+ } catch (error) {
18829
+ return {
18830
+ ...capability,
18831
+ available: false,
18832
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString(),
18833
+ error: error instanceof Error ? error.message : String(error)
18834
+ };
18835
+ }
18836
+ }
18750
18837
 
18751
18838
  // src/providers/index.ts
18752
18839
  init_copilot();
@@ -19346,151 +19433,1366 @@ z.string().regex(
19346
19433
  /^\d+\.\d+\.\d+$/,
19347
19434
  "Version must be in semver format (e.g., 1.0.0)"
19348
19435
  );
19349
- init_errors();
19350
- init_allowed_paths();
19351
- function levenshtein(a, b) {
19352
- if (a === b) return 0;
19353
- if (a.length === 0) return b.length;
19354
- if (b.length === 0) return a.length;
19355
- const row = Array.from({ length: b.length + 1 }, (_, i) => i);
19356
- for (let i = 1; i <= a.length; i++) {
19357
- let prev = i;
19358
- for (let j = 1; j <= b.length; j++) {
19359
- const cost = a[i - 1] === b[j - 1] ? 0 : 1;
19360
- const val = Math.min(
19361
- row[j] + 1,
19362
- // deletion
19363
- prev + 1,
19364
- // insertion
19365
- row[j - 1] + cost
19366
- // substitution
19367
- );
19368
- row[j - 1] = prev;
19369
- prev = val;
19370
- }
19371
- row[b.length] = prev;
19372
- }
19373
- return row[b.length];
19374
- }
19375
19436
 
19376
- // src/utils/file-suggestions.ts
19377
- var MAX_DIR_ENTRIES = 200;
19378
- var MAX_SUGGESTIONS = 5;
19379
- var DEFAULT_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
19380
- "node_modules",
19381
- ".git",
19382
- "dist",
19383
- "build",
19384
- "coverage",
19385
- ".next",
19386
- "vendor",
19387
- "__pycache__",
19388
- ".venv",
19389
- "venv",
19390
- ".tox",
19391
- ".pytest_cache",
19392
- ".mypy_cache",
19393
- ".ruff_cache",
19394
- ".idea",
19395
- ".vscode",
19396
- ".DS_Store"
19397
- ]);
19398
- var DEFAULT_FIND_OPTIONS = {
19399
- maxDepth: 8,
19400
- timeoutMs: 3e3,
19401
- includeHidden: true,
19402
- excludeDirs: DEFAULT_EXCLUDE_DIRS,
19403
- maxResults: 5,
19404
- type: "file"
19437
+ // src/cli/repl/agents/manager.ts
19438
+ init_logger();
19439
+
19440
+ // src/cli/repl/agents/prompts.ts
19441
+ var EXPLORE_PROMPT = `You are an exploration agent for Corbat-Coco.
19442
+ Your purpose is to search the codebase to answer questions and gather information.
19443
+
19444
+ Your capabilities:
19445
+ - Search for files using glob patterns
19446
+ - Read file contents to understand code structure
19447
+ - Search for text patterns across the codebase
19448
+ - List directory contents to understand project structure
19449
+
19450
+ When exploring:
19451
+ 1. Start broad, then narrow down based on findings
19452
+ 2. Look for patterns in naming conventions and file organization
19453
+ 3. Read relevant files to understand implementations
19454
+ 4. Summarize your findings clearly and concisely
19455
+
19456
+ Focus on gathering accurate information. Do not make changes to files.
19457
+ Report what you find with specific file paths and code references.`;
19458
+ var PLAN_PROMPT = `You are a planning agent for Corbat-Coco.
19459
+ Your purpose is to design implementation approaches and create detailed plans.
19460
+
19461
+ Your capabilities:
19462
+ - Read existing code to understand architecture
19463
+ - Search for related implementations and patterns
19464
+ - Analyze dependencies and relationships
19465
+ - Review documentation and comments
19466
+
19467
+ When planning:
19468
+ 1. Understand the current state of the codebase
19469
+ 2. Identify affected areas and dependencies
19470
+ 3. Break down tasks into concrete steps
19471
+ 4. Consider edge cases and potential issues
19472
+ 5. Propose a clear implementation strategy
19473
+
19474
+ Output a structured plan with:
19475
+ - Overview of the approach
19476
+ - Step-by-step implementation tasks
19477
+ - Potential risks or considerations
19478
+ - Estimated complexity
19479
+
19480
+ Do not implement changes - only create the plan.`;
19481
+ var TEST_PROMPT = `You are a testing agent for Corbat-Coco.
19482
+ Your purpose is to write and run tests to ensure code quality.
19483
+
19484
+ Your capabilities:
19485
+ - Read source files to understand what needs testing
19486
+ - Write test files with comprehensive test cases
19487
+ - Run tests and analyze results
19488
+ - Check code coverage
19489
+ - Identify untested code paths
19490
+
19491
+ When testing:
19492
+ 1. Understand the code being tested
19493
+ 2. Identify test scenarios (happy path, edge cases, errors)
19494
+ 3. Write clear, maintainable tests
19495
+ 4. Run tests and verify they pass
19496
+ 5. Check coverage and add tests for uncovered areas
19497
+
19498
+ Follow these testing principles:
19499
+ - One assertion per test when possible
19500
+ - Clear test names that describe the scenario
19501
+ - Proper setup and teardown
19502
+ - Mock external dependencies appropriately
19503
+ - Test behavior, not implementation details
19504
+
19505
+ Report test results clearly with pass/fail status and coverage metrics.`;
19506
+ var DEBUG_PROMPT = `You are a debugging agent for Corbat-Coco.
19507
+ Your purpose is to analyze errors, identify root causes, and fix issues.
19508
+
19509
+ Your capabilities:
19510
+ - Read error messages and stack traces
19511
+ - Search for related code and error handlers
19512
+ - Execute code to reproduce issues
19513
+ - Analyze logs and outputs
19514
+ - Make targeted fixes to resolve issues
19515
+
19516
+ When debugging:
19517
+ 1. Understand the error symptoms completely
19518
+ 2. Reproduce the issue if possible
19519
+ 3. Trace the error to its source
19520
+ 4. Identify the root cause (not just symptoms)
19521
+ 5. Propose and implement a fix
19522
+ 6. Verify the fix resolves the issue
19523
+
19524
+ Focus on:
19525
+ - Understanding the actual vs expected behavior
19526
+ - Checking input validation and edge cases
19527
+ - Looking for off-by-one errors, null references, type mismatches
19528
+ - Considering race conditions or async issues
19529
+ - Reviewing recent changes that might have introduced the bug
19530
+
19531
+ Provide a clear explanation of what caused the issue and how you fixed it.`;
19532
+ var REVIEW_PROMPT = `You are a code review agent for Corbat-Coco.
19533
+ Your purpose is to review code for quality, maintainability, and best practices.
19534
+
19535
+ Your capabilities:
19536
+ - Read source files and understand implementations
19537
+ - Search for coding patterns and conventions
19538
+ - Analyze code complexity and structure
19539
+ - Check for security issues and anti-patterns
19540
+
19541
+ When reviewing:
19542
+ 1. Read the code thoroughly
19543
+ 2. Check for correctness and logic errors
19544
+ 3. Evaluate code style and consistency
19545
+ 4. Identify potential bugs or edge cases
19546
+ 5. Assess maintainability and readability
19547
+ 6. Look for security vulnerabilities
19548
+
19549
+ Review criteria:
19550
+ - **Correctness**: Does the code do what it's supposed to?
19551
+ - **Clarity**: Is the code easy to understand?
19552
+ - **Efficiency**: Are there unnecessary computations or memory usage?
19553
+ - **Security**: Are there potential vulnerabilities?
19554
+ - **Testing**: Is the code testable? Are there tests?
19555
+ - **Documentation**: Are complex parts documented?
19556
+
19557
+ Provide specific, actionable feedback with code references.
19558
+ Prioritize issues by severity: critical > major > minor > suggestion.`;
19559
+ var ARCHITECT_PROMPT = `You are an architecture agent for Corbat-Coco.
19560
+ Your purpose is to design system architecture and evaluate architectural decisions.
19561
+
19562
+ Your capabilities:
19563
+ - Read source files to understand current architecture
19564
+ - Search for design patterns and structural boundaries
19565
+ - Review module dependencies and coupling
19566
+ - Analyze layer separation and interface design
19567
+
19568
+ When designing architecture:
19569
+ 1. Understand the existing architecture and constraints
19570
+ 2. Identify architectural concerns and trade-offs
19571
+ 3. Propose designs aligned with corbat-coco patterns:
19572
+ - Tool Registry Pattern: register tools centrally, discover by name
19573
+ - Zod Config Pattern: all config validated with Zod schemas
19574
+ - Provider-Agnostic Pattern: abstract LLM providers behind interfaces
19575
+ - REPL Skill Pattern: skills as self-contained SKILL.md + handler
19576
+ - Phase Context Pattern: pass context through COCO phases immutably
19577
+ 4. Document decisions as Architecture Decision Records (ADRs)
19578
+
19579
+ ADR format:
19580
+ - Title: short noun phrase
19581
+ - Status: proposed | accepted | deprecated | superseded
19582
+ - Context: forces at play
19583
+ - Decision: the chosen solution
19584
+ - Consequences: trade-offs accepted
19585
+
19586
+ Constraints for corbat-coco:
19587
+ - TypeScript ESM only \u2014 no CommonJS
19588
+ - Node.js 22+ runtime
19589
+ - Prefer functional patterns over classes
19590
+ - Files must stay under 500 LOC
19591
+ - All public APIs need JSDoc
19592
+
19593
+ Output a structured architectural analysis or ADR. Do not write implementation code.`;
19594
+ var SECURITY_PROMPT = `You are a security audit agent for Corbat-Coco.
19595
+ Your purpose is to identify security vulnerabilities and recommend fixes.
19596
+
19597
+ Your capabilities:
19598
+ - Read source files to analyze security posture
19599
+ - Search for dangerous patterns across the codebase
19600
+ - Run dependency vulnerability checks
19601
+ - Audit configuration for secrets exposure
19602
+
19603
+ Security checklist (OWASP Top 10 + corbat-coco specifics):
19604
+
19605
+ 1. **Secrets exposure**: No API keys/tokens hardcoded or logged
19606
+ 2. **Command injection**: Shell commands must use array args via execa, never template strings
19607
+ 3. **Path traversal**: File paths must be validated and confined to safe directories
19608
+ 4. **Injection (SQL/NoSQL)**: Parameterized queries only, never string concatenation
19609
+ 5. **Input validation**: All external inputs validated with Zod schemas at boundaries
19610
+ 6. **LLM output safety**: Treat LLM output as untrusted \u2014 sanitize before eval/exec
19611
+ 7. **Dependency vulnerabilities**: Check for known CVEs with \`pnpm audit\`
19612
+ 8. **Insecure deserialization**: Safe JSON parsing with error handling, no eval
19613
+ 9. **Sensitive data logging**: No PII, tokens, or credentials in log output
19614
+ 10. **Type safety**: No \`any\` that bypasses security-relevant checks
19615
+
19616
+ When auditing:
19617
+ 1. Search for common dangerous patterns (exec, eval, dangerouslySet, etc.)
19618
+ 2. Check all environment variable usage for proper validation
19619
+ 3. Verify tool implementations for path/command safety
19620
+ 4. Review LLM provider integrations for key exposure
19621
+
19622
+ Severity levels:
19623
+ - CRITICAL: Exploitable with immediate impact (must block release)
19624
+ - HIGH: Likely exploitable under realistic conditions
19625
+ - MEDIUM: Exploitable in specific scenarios
19626
+ - LOW: Defense-in-depth improvement
19627
+
19628
+ Output a structured security report. Do not make code changes \u2014 report findings only.`;
19629
+ var TDD_PROMPT = `You are a TDD (Test-Driven Development) agent for Corbat-Coco.
19630
+ Your purpose is to enforce test-first methodology with RED-GREEN-REFACTOR discipline.
19631
+
19632
+ Your capabilities:
19633
+ - Read source files to understand interfaces
19634
+ - Write failing tests BEFORE implementation
19635
+ - Run tests to confirm RED state
19636
+ - Write minimal code to make tests GREEN
19637
+ - Refactor while keeping tests passing
19638
+ - Check coverage with Vitest
19639
+
19640
+ TDD workflow:
19641
+ 1. **Interface first**: Define types and function signatures (no implementation)
19642
+ 2. **RED**: Write failing tests that describe the desired behavior
19643
+ - Run \`pnpm test\` \u2014 tests must FAIL at this point
19644
+ - If tests pass without implementation, the test is wrong
19645
+ 3. **GREEN**: Write the minimum code to make tests pass
19646
+ - Run \`pnpm test\` \u2014 all tests must pass
19647
+ 4. **REFACTOR**: Clean up code while keeping tests green
19648
+ - Run \`pnpm test\` after each refactor step
19649
+
19650
+ Testing stack (corbat-coco):
19651
+ - Framework: Vitest
19652
+ - Assertions: expect() from vitest
19653
+ - Mocking: vi.mock(), vi.fn(), vi.spyOn()
19654
+ - Run: \`pnpm test\`
19655
+ - Coverage: \`pnpm test -- --coverage\` (target: 80%+)
19656
+
19657
+ Test structure:
19658
+ \`\`\`typescript
19659
+ import { describe, it, expect, vi, beforeEach } from "vitest";
19660
+
19661
+ describe("ModuleName", () => {
19662
+ describe("functionName", () => {
19663
+ it("should [expected behavior] when [condition]", async () => {
19664
+ // Arrange
19665
+ // Act
19666
+ // Assert
19667
+ });
19668
+ });
19669
+ });
19670
+ \`\`\`
19671
+
19672
+ Rules:
19673
+ - NEVER write implementation before tests
19674
+ - One assertion per test when possible
19675
+ - Test behavior, not implementation details
19676
+ - Mock all external dependencies (LLM providers, filesystem, execa)`;
19677
+ var REFACTOR_PROMPT = `You are a refactoring agent for Corbat-Coco.
19678
+ Your purpose is to improve code structure, readability, and maintainability without changing behavior.
19679
+
19680
+ Your capabilities:
19681
+ - Read and analyze existing code for improvement opportunities
19682
+ - Edit files to improve structure
19683
+ - Run tests to verify behavior is preserved
19684
+ - Run linting and type checking
19685
+
19686
+ Refactoring techniques (apply in order of safety):
19687
+ 1. **Extract function**: Break large functions into focused helpers (\u226450 lines each)
19688
+ 2. **Rename for clarity**: Improve variable/function names to reveal intent
19689
+ 3. **Remove duplication**: Extract shared logic to reusable functions/modules
19690
+ 4. **Simplify conditionals**: Replace complex boolean logic with named predicates
19691
+ 5. **Eliminate magic numbers**: Replace literals with named constants
19692
+ 6. **Flatten nesting**: Reduce arrow anti-pattern via early returns
19693
+ 7. **Split large files**: Files over 500 LOC should be split by cohesion
19694
+
19695
+ Safety rules:
19696
+ - Run \`pnpm test\` BEFORE starting \u2014 all tests must be green
19697
+ - Make one refactoring at a time
19698
+ - Run \`pnpm test\` after EVERY change \u2014 stop if tests break
19699
+ - Run \`pnpm typecheck\` to catch type regressions
19700
+ - Never change behavior \u2014 refactoring is structural only
19701
+ - If tests don't cover the code being refactored, write tests first
19702
+
19703
+ corbat-coco specific patterns to introduce:
19704
+ - Use Zod schemas for all config/input types
19705
+ - Apply Provider-Agnostic Pattern for LLM calls
19706
+ - Apply Tool Registry Pattern for tool management
19707
+ - Replace class-heavy code with factory functions where simpler
19708
+
19709
+ Output: describe each refactoring applied and run test/typecheck results.`;
19710
+ var E2E_PROMPT = `You are an end-to-end testing agent for Corbat-Coco.
19711
+ Your purpose is to write and run integration tests that cover complete user workflows.
19712
+
19713
+ Your capabilities:
19714
+ - Read source files to understand end-to-end flows
19715
+ - Write integration tests that exercise full workflows
19716
+ - Run tests and analyze failures
19717
+ - Check coverage across integration paths
19718
+
19719
+ E2E testing principles:
19720
+ 1. Test complete workflows from entry point to output
19721
+ 2. Use realistic inputs (not just happy paths)
19722
+ 3. Test COCO phase transitions: Converge \u2192 Orchestrate \u2192 Complete \u2192 Output
19723
+ 4. Test CLI commands end-to-end with subprocess spawning
19724
+ 5. Test error propagation across phase boundaries
19725
+ 6. Test provider fallback behavior
19726
+ 7. Test tool execution chains
19727
+
19728
+ For corbat-coco, focus on:
19729
+ - Full COCO run: specification \u2192 backlog \u2192 task execution \u2192 output generation
19730
+ - CLI command integration: \`coco run\`, \`coco repl\`, \`coco init\`
19731
+ - LLM provider switching and error recovery
19732
+ - Tool registry tool execution chains
19733
+ - Quality scoring over multiple iterations
19734
+ - Checkpoint save/restore across phase boundaries
19735
+
19736
+ Test setup:
19737
+ \`\`\`typescript
19738
+ // Use mock LLM provider to avoid real API calls in tests
19739
+ import { createMockProvider } from "../mocks/provider.js";
19740
+
19741
+ // Test full COCO orchestration
19742
+ it("should complete a full run from spec to output", async () => {
19743
+ const provider = createMockProvider([
19744
+ { content: "specification output" },
19745
+ { content: "backlog output" },
19746
+ ]);
19747
+ // ...
19748
+ });
19749
+ \`\`\`
19750
+
19751
+ Report coverage of integration paths and any workflow gaps found.`;
19752
+ var DOCS_PROMPT = `You are a documentation agent for Corbat-Coco.
19753
+ Your purpose is to generate and maintain clear, accurate documentation.
19754
+
19755
+ Your capabilities:
19756
+ - Read source files to understand what needs documenting
19757
+ - Write JSDoc for public APIs
19758
+ - Create or update README files
19759
+ - Generate architecture documentation
19760
+ - Update changelogs
19761
+
19762
+ Documentation types:
19763
+ 1. **JSDoc**: All exported functions, types, and classes
19764
+ \`\`\`typescript
19765
+ /**
19766
+ * Brief description.
19767
+ *
19768
+ * @param paramName - What it is and valid values
19769
+ * @returns What is returned and when
19770
+ * @throws What errors can be thrown and why
19771
+ * @example
19772
+ * \`\`\`typescript
19773
+ * const result = myFunction(input);
19774
+ * \`\`\`
19775
+ */
19776
+ \`\`\`
19777
+ 2. **README**: Project overview, installation, usage, examples, API reference
19778
+ 3. **ADR**: Architecture Decision Records (see architect agent format)
19779
+ 4. **CHANGELOG**: Conventional commit-based changelog entries
19780
+ 5. **CODING_STANDARDS.md**: Language-specific standards for user projects
19781
+
19782
+ corbat-coco documentation conventions:
19783
+ - Public APIs must have JSDoc with @param, @returns, @example
19784
+ - Complex logic must have inline comments explaining WHY, not WHAT
19785
+ - COCO phases must be documented with input/output contracts
19786
+ - Tool implementations must document their parameters and return format
19787
+ - Provider implementations must document their configuration requirements
19788
+
19789
+ Output well-structured documentation. Prefer accuracy over completeness.`;
19790
+ var DATABASE_PROMPT = `You are a database engineering agent for Corbat-Coco.
19791
+ Your purpose is to design database schemas, write migrations, and optimize queries.
19792
+
19793
+ Your capabilities:
19794
+ - Read source files to understand data models
19795
+ - Write SQL migrations and ORM schema definitions
19796
+ - Design schema changes for zero-downtime deployment
19797
+ - Review queries for N+1 problems and missing indexes
19798
+
19799
+ Migration safety rules:
19800
+ 1. **Never destructive in one step**: Split DROP/rename into multiple deployments
19801
+ 2. **Backward compatible first**: New columns must be nullable or have defaults
19802
+ 3. **Zero-downtime patterns**:
19803
+ - Add column \u2192 deploy app \u2192 backfill \u2192 add constraint \u2192 drop old column
19804
+ - Never rename columns directly \u2014 add new, migrate data, drop old
19805
+ 4. **Always reversible**: Every migration needs a rollback script
19806
+ 5. **Test migrations**: Run on a copy of production data before applying
19807
+
19808
+ ORM support:
19809
+ - **Prisma** (Node.js): \`schema.prisma\` + \`prisma migrate dev\`
19810
+ - **TypeORM** (TypeScript): entity classes + \`migration:generate\`
19811
+ - **Alembic** (Python): \`alembic revision --autogenerate\`
19812
+ - **Flyway** (Java): versioned SQL files in \`db/migration/\`
19813
+ - **golang-migrate** (Go): numbered SQL files
19814
+
19815
+ Index design:
19816
+ - Index all foreign keys
19817
+ - Composite indexes: most selective column first
19818
+ - Partial indexes for filtered queries
19819
+ - Avoid over-indexing (slows writes)
19820
+
19821
+ Query patterns:
19822
+ - Use pagination (LIMIT/OFFSET or cursor-based)
19823
+ - Avoid N+1: use JOIN or batch loading
19824
+ - Use query analysis tools (EXPLAIN ANALYZE)
19825
+ - Keep transactions short and focused
19826
+
19827
+ Output migration files with up/down scripts and a schema change summary.`;
19828
+ var AGENT_PROMPTS = {
19829
+ explore: EXPLORE_PROMPT,
19830
+ plan: PLAN_PROMPT,
19831
+ test: TEST_PROMPT,
19832
+ debug: DEBUG_PROMPT,
19833
+ review: REVIEW_PROMPT,
19834
+ architect: ARCHITECT_PROMPT,
19835
+ security: SECURITY_PROMPT,
19836
+ tdd: TDD_PROMPT,
19837
+ refactor: REFACTOR_PROMPT,
19838
+ e2e: E2E_PROMPT,
19839
+ docs: DOCS_PROMPT,
19840
+ database: DATABASE_PROMPT
19405
19841
  };
19406
- async function findFileRecursive(rootDir, target, options = {}) {
19407
- const opts = { ...DEFAULT_FIND_OPTIONS, ...options };
19408
- const targetLower = target.toLowerCase();
19409
- const results = [];
19410
- const startTime = Date.now();
19411
- const isTimedOut = () => Date.now() - startTime > opts.timeoutMs;
19412
- const queue = [[path17__default.resolve(rootDir), 0]];
19413
- const visited = /* @__PURE__ */ new Set();
19414
- while (queue.length > 0 && results.length < opts.maxResults) {
19415
- if (isTimedOut()) break;
19416
- const [currentDir, depth] = queue.shift();
19417
- if (visited.has(currentDir)) continue;
19418
- visited.add(currentDir);
19419
- if (depth > opts.maxDepth) continue;
19842
+ var AGENT_TOOLS = {
19843
+ explore: [
19844
+ "glob",
19845
+ "read_file",
19846
+ "list_dir",
19847
+ "bash_exec",
19848
+ "git_status",
19849
+ "git_diff",
19850
+ "git_log",
19851
+ "git_branch"
19852
+ ],
19853
+ plan: ["glob", "read_file", "list_dir", "git_status", "git_diff", "git_log", "git_branch"],
19854
+ test: [
19855
+ "glob",
19856
+ "read_file",
19857
+ "write_file",
19858
+ "edit_file",
19859
+ "run_tests",
19860
+ "bash_exec",
19861
+ "git_status",
19862
+ "git_diff"
19863
+ ],
19864
+ debug: [
19865
+ "glob",
19866
+ "read_file",
19867
+ "write_file",
19868
+ "edit_file",
19869
+ "bash_exec",
19870
+ "run_tests",
19871
+ "git_status",
19872
+ "git_diff",
19873
+ "git_log"
19874
+ ],
19875
+ review: ["glob", "read_file", "list_dir", "git_status", "git_diff", "git_log", "git_branch"],
19876
+ architect: ["glob", "read_file", "list_dir", "git_log", "git_branch"],
19877
+ security: ["glob", "read_file", "list_dir", "bash_exec", "git_diff", "git_log"],
19878
+ tdd: [
19879
+ "glob",
19880
+ "read_file",
19881
+ "write_file",
19882
+ "edit_file",
19883
+ "run_tests",
19884
+ "bash_exec",
19885
+ "git_status",
19886
+ "git_diff"
19887
+ ],
19888
+ refactor: [
19889
+ "glob",
19890
+ "read_file",
19891
+ "write_file",
19892
+ "edit_file",
19893
+ "run_tests",
19894
+ "bash_exec",
19895
+ "git_status",
19896
+ "git_diff"
19897
+ ],
19898
+ e2e: [
19899
+ "glob",
19900
+ "read_file",
19901
+ "write_file",
19902
+ "edit_file",
19903
+ "run_tests",
19904
+ "bash_exec",
19905
+ "git_status",
19906
+ "git_diff"
19907
+ ],
19908
+ docs: ["glob", "read_file", "write_file", "edit_file", "list_dir", "git_log"],
19909
+ database: [
19910
+ "glob",
19911
+ "read_file",
19912
+ "write_file",
19913
+ "edit_file",
19914
+ "bash_exec",
19915
+ "git_status",
19916
+ "sql_query",
19917
+ "inspect_schema"
19918
+ ]
19919
+ };
19920
+ var AGENT_MAX_TURNS = {
19921
+ explore: 10,
19922
+ plan: 8,
19923
+ test: 15,
19924
+ debug: 12,
19925
+ review: 6,
19926
+ architect: 12,
19927
+ security: 10,
19928
+ tdd: 20,
19929
+ refactor: 15,
19930
+ e2e: 15,
19931
+ docs: 12,
19932
+ database: 12
19933
+ };
19934
+ function getAgentConfig(type) {
19935
+ return {
19936
+ type,
19937
+ systemPrompt: AGENT_PROMPTS[type],
19938
+ tools: AGENT_TOOLS[type],
19939
+ maxTurns: AGENT_MAX_TURNS[type]
19940
+ };
19941
+ }
19942
+ var AGENT_NAMES = {
19943
+ explore: "Explorer",
19944
+ plan: "Planner",
19945
+ test: "Tester",
19946
+ debug: "Debugger",
19947
+ review: "Reviewer",
19948
+ architect: "Architect",
19949
+ security: "Security Auditor",
19950
+ tdd: "TDD Guide",
19951
+ refactor: "Refactorer",
19952
+ e2e: "E2E Tester",
19953
+ docs: "Docs Writer",
19954
+ database: "Database Engineer"
19955
+ };
19956
+ var AGENT_DESCRIPTIONS = {
19957
+ explore: "Search the codebase to answer questions and gather information",
19958
+ plan: "Design implementation approaches and create detailed plans",
19959
+ test: "Write and run tests to ensure code quality",
19960
+ debug: "Analyze errors and fix issues",
19961
+ review: "Review code for quality and best practices",
19962
+ architect: "Design system architecture and create architectural decision records",
19963
+ security: "Audit code for security vulnerabilities using OWASP Top 10",
19964
+ tdd: "Drive development with test-first methodology and RED-GREEN-REFACTOR cycle",
19965
+ refactor: "Improve code structure and quality without changing behavior",
19966
+ e2e: "Write and run end-to-end tests covering full user workflows",
19967
+ docs: "Generate and maintain documentation for code, APIs, and architecture",
19968
+ database: "Design database schemas, write migrations, and optimize queries"
19969
+ };
19970
+
19971
+ // src/cli/repl/agents/manager.ts
19972
+ var MAX_CONCURRENT_AGENTS = 3;
19973
+ var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
19974
+ var AgentManager = class extends EventEmitter {
19975
+ activeAgents = /* @__PURE__ */ new Map();
19976
+ completedAgents = /* @__PURE__ */ new Map();
19977
+ abortControllers = /* @__PURE__ */ new Map();
19978
+ provider;
19979
+ toolRegistry;
19980
+ logger = getLogger();
19981
+ /**
19982
+ * Create a new AgentManager
19983
+ * @param provider - LLM provider for agent execution
19984
+ * @param toolRegistry - Tool registry for agent tool access
19985
+ */
19986
+ constructor(provider, toolRegistry) {
19987
+ super();
19988
+ this.provider = provider;
19989
+ this.toolRegistry = toolRegistry;
19990
+ }
19991
+ /**
19992
+ * Spawn a new subagent for a specific task
19993
+ *
19994
+ * @description Creates and executes a specialized subagent for the given task.
19995
+ * The agent will run autonomously, making LLM calls and executing tools until
19996
+ * it completes the task or reaches the maximum turn limit.
19997
+ *
19998
+ * @param type - Type of agent to spawn (explore, plan, test, debug, review, architect, security, tdd, refactor, e2e, docs, database)
19999
+ * @param task - Task description for the agent to execute
20000
+ * @param options - Optional spawn configuration including callbacks, abort signal, and timeout
20001
+ * @returns Promise resolving to the agent result with output and usage stats
20002
+ *
20003
+ * @example
20004
+ * ```typescript
20005
+ * const result = await manager.spawn('test', 'Write tests for UserService', {
20006
+ * onStatusChange: (agent) => console.log(`Status: ${agent.status}`),
20007
+ * onOutput: (agent, text) => console.log(text),
20008
+ * timeout: 60000, // 1 minute timeout
20009
+ * });
20010
+ * ```
20011
+ */
20012
+ async spawn(type, task, options = {}) {
20013
+ if (this.activeAgents.size >= MAX_CONCURRENT_AGENTS) {
20014
+ const error = `Cannot spawn agent: maximum concurrent agents (${MAX_CONCURRENT_AGENTS}) reached`;
20015
+ this.logger.warn(error);
20016
+ const failedAgent = this.createAgent(type, task);
20017
+ failedAgent.status = "failed";
20018
+ failedAgent.error = error;
20019
+ failedAgent.completedAt = /* @__PURE__ */ new Date();
20020
+ return {
20021
+ agent: failedAgent,
20022
+ success: false,
20023
+ output: error
20024
+ };
20025
+ }
20026
+ const agent = this.createAgent(type, task);
20027
+ this.activeAgents.set(agent.id, agent);
20028
+ options.onStatusChange?.(agent);
20029
+ const internalAbortController = new AbortController();
20030
+ this.abortControllers.set(agent.id, internalAbortController);
20031
+ if (options.signal) {
20032
+ if (options.signal.aborted) {
20033
+ internalAbortController.abort();
20034
+ } else {
20035
+ options.signal.addEventListener("abort", () => {
20036
+ internalAbortController.abort();
20037
+ });
20038
+ }
20039
+ }
20040
+ const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
20041
+ const timeoutId = setTimeout(() => {
20042
+ if (this.activeAgents.has(agent.id)) {
20043
+ this.logger.warn(`Agent ${agent.id} timed out after ${timeout}ms`);
20044
+ internalAbortController.abort();
20045
+ agent.error = `Agent timed out after ${timeout}ms`;
20046
+ this.emitEvent("timeout", agent);
20047
+ }
20048
+ }, timeout);
20049
+ this.logger.info(`Spawned ${type} agent: ${agent.id}`, { task, timeout });
20050
+ this.emitEvent("spawn", agent);
19420
20051
  try {
19421
- const entries = await fs16__default.readdir(currentDir, { withFileTypes: true });
19422
- for (const entry of entries) {
19423
- if (isTimedOut()) break;
19424
- const entryName = entry.name;
19425
- const entryPath = path17__default.join(currentDir, entryName);
19426
- if (!opts.includeHidden && entryName.startsWith(".")) continue;
19427
- if (entry.isDirectory() && opts.excludeDirs.has(entryName)) continue;
19428
- const isMatch = opts.type === "file" && entry.isFile() || opts.type === "directory" && entry.isDirectory() || opts.type === "both";
19429
- if (isMatch) {
19430
- const entryNameLower = entryName.toLowerCase();
19431
- let distance;
19432
- if (entryNameLower === targetLower) {
19433
- distance = 0;
19434
- } else {
19435
- distance = levenshtein(targetLower, entryNameLower);
19436
- }
19437
- const maxDistance = Math.max(target.length * 0.6, 3);
19438
- if (distance <= maxDistance) {
19439
- results.push({ path: entryPath, distance });
19440
- }
19441
- }
19442
- if (entry.isDirectory() && !opts.excludeDirs.has(entryName)) {
19443
- queue.push([entryPath, depth + 1]);
19444
- }
20052
+ const result = await this.executeAgent(agent, {
20053
+ ...options,
20054
+ signal: internalAbortController.signal
20055
+ });
20056
+ clearTimeout(timeoutId);
20057
+ if (result.success) {
20058
+ this.emitEvent("complete", agent, result);
20059
+ } else if (agent.error?.includes("Aborted")) {
20060
+ this.emitEvent("cancel", agent, result);
20061
+ } else {
20062
+ this.emitEvent("fail", agent, result);
19445
20063
  }
19446
- } catch {
19447
- continue;
20064
+ return result;
20065
+ } catch (error) {
20066
+ clearTimeout(timeoutId);
20067
+ const errorMessage = error instanceof Error ? error.message : String(error);
20068
+ agent.status = "failed";
20069
+ agent.error = errorMessage;
20070
+ agent.completedAt = /* @__PURE__ */ new Date();
20071
+ this.moveToCompleted(agent.id);
20072
+ this.abortControllers.delete(agent.id);
20073
+ options.onStatusChange?.(agent);
20074
+ this.logger.error(`Agent ${agent.id} failed unexpectedly`, { error: errorMessage });
20075
+ this.emitEvent("fail", agent);
20076
+ return {
20077
+ agent,
20078
+ success: false,
20079
+ output: errorMessage
20080
+ };
19448
20081
  }
19449
20082
  }
19450
- return results.sort((a, b) => a.distance - b.distance).slice(0, opts.maxResults);
19451
- }
19452
- async function suggestSimilarFilesDeep(missingPath, rootDir = process.cwd(), options) {
19453
- const fastResults = await suggestSimilarFiles(missingPath, {
19454
- maxResults: MAX_SUGGESTIONS
19455
- });
19456
- if (fastResults.length > 0) {
19457
- return fastResults;
19458
- }
19459
- const absPath = path17__default.resolve(missingPath);
19460
- const target = path17__default.basename(absPath);
19461
- return findFileRecursive(rootDir, target, options);
19462
- }
19463
- async function suggestSimilarDirsDeep(missingPath, rootDir = process.cwd(), options) {
19464
- const absPath = path17__default.resolve(missingPath);
19465
- const target = path17__default.basename(absPath);
19466
- const parentDir = path17__default.dirname(absPath);
19467
- try {
19468
- const entries = await fs16__default.readdir(parentDir, { withFileTypes: true });
19469
- const dirs = entries.filter((e) => e.isDirectory());
19470
- const scored = dirs.map((d) => ({
19471
- path: path17__default.join(parentDir, d.name),
19472
- distance: levenshtein(target.toLowerCase(), d.name.toLowerCase())
19473
- })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance).slice(0, options?.maxResults ?? MAX_SUGGESTIONS);
19474
- if (scored.length > 0) {
19475
- return scored;
20083
+ /**
20084
+ * Cancel a running agent
20085
+ *
20086
+ * @description Cancels an active agent by triggering its abort signal.
20087
+ * The agent will stop at the next safe point (beginning of next iteration).
20088
+ *
20089
+ * @param agentId - ID of the agent to cancel
20090
+ * @returns True if the agent was cancelled, false if not found or already completed
20091
+ */
20092
+ cancel(agentId) {
20093
+ const controller = this.abortControllers.get(agentId);
20094
+ if (!controller) {
20095
+ this.logger.warn(`Cannot cancel agent ${agentId}: not found or already completed`);
20096
+ return false;
19476
20097
  }
19477
- } catch {
20098
+ this.logger.info(`Cancelling agent ${agentId}`);
20099
+ controller.abort();
20100
+ return true;
19478
20101
  }
19479
- return findFileRecursive(rootDir, target, { ...options, type: "directory" });
19480
- }
19481
- async function suggestSimilarFiles(missingPath, options) {
19482
- const absPath = path17__default.resolve(missingPath);
19483
- const dir = path17__default.dirname(absPath);
19484
- const target = path17__default.basename(absPath);
19485
- const maxResults = options?.maxResults;
19486
- try {
19487
- const entries = await fs16__default.readdir(dir);
19488
- const limited = entries.slice(0, MAX_DIR_ENTRIES);
19489
- const scored = limited.map((name) => ({
19490
- path: path17__default.join(dir, name),
19491
- distance: levenshtein(target.toLowerCase(), name.toLowerCase())
19492
- })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
19493
- return scored.slice(0, maxResults);
20102
+ /**
20103
+ * Get the status of a specific agent
20104
+ *
20105
+ * @description Returns the current status of an agent.
20106
+ *
20107
+ * @param agentId - ID of the agent
20108
+ * @returns The agent's status or undefined if not found
20109
+ */
20110
+ getStatus(agentId) {
20111
+ return this.getAgent(agentId)?.status;
20112
+ }
20113
+ /**
20114
+ * Get the current status of an agent
20115
+ *
20116
+ * @description Retrieves an agent by ID from either active or completed agents.
20117
+ *
20118
+ * @param agentId - Unique agent identifier (UUID)
20119
+ * @returns The agent if found, undefined otherwise
20120
+ */
20121
+ getAgent(agentId) {
20122
+ return this.activeAgents.get(agentId) ?? this.completedAgents.get(agentId);
20123
+ }
20124
+ /**
20125
+ * Get all active agents
20126
+ * @returns Array of currently running agents
20127
+ */
20128
+ getActiveAgents() {
20129
+ return Array.from(this.activeAgents.values());
20130
+ }
20131
+ /**
20132
+ * Get all completed agents
20133
+ * @returns Array of completed agents (succeeded or failed)
20134
+ */
20135
+ getCompletedAgents() {
20136
+ return Array.from(this.completedAgents.values());
20137
+ }
20138
+ /**
20139
+ * Get the number of active agents
20140
+ * @returns Count of running agents
20141
+ */
20142
+ getActiveCount() {
20143
+ return this.activeAgents.size;
20144
+ }
20145
+ /**
20146
+ * Check if more agents can be spawned
20147
+ * @returns True if under the concurrent limit
20148
+ */
20149
+ canSpawn() {
20150
+ return this.activeAgents.size < MAX_CONCURRENT_AGENTS;
20151
+ }
20152
+ /**
20153
+ * Clear completed agents from history
20154
+ */
20155
+ clearCompleted() {
20156
+ this.completedAgents.clear();
20157
+ this.logger.debug("Cleared completed agents history");
20158
+ }
20159
+ /**
20160
+ * Get available agent types with their descriptions
20161
+ *
20162
+ * @description Returns all supported agent types with their names, descriptions,
20163
+ * and configurations. Useful for building UI or help text.
20164
+ *
20165
+ * @returns Array of agent registry entries with type, name, description, and config
20166
+ */
20167
+ getAvailableAgentTypes() {
20168
+ return Object.keys(AGENT_NAMES).map((type) => ({
20169
+ type,
20170
+ name: AGENT_NAMES[type],
20171
+ description: AGENT_DESCRIPTIONS[type],
20172
+ config: getAgentConfig(type)
20173
+ }));
20174
+ }
20175
+ /**
20176
+ * Create a new agent instance
20177
+ */
20178
+ createAgent(type, task) {
20179
+ return {
20180
+ id: randomUUID(),
20181
+ type,
20182
+ status: "idle",
20183
+ task,
20184
+ createdAt: /* @__PURE__ */ new Date()
20185
+ };
20186
+ }
20187
+ /**
20188
+ * Execute an agent's task
20189
+ */
20190
+ async executeAgent(agent, options) {
20191
+ const config = getAgentConfig(agent.type);
20192
+ agent.status = "running";
20193
+ options.onStatusChange?.(agent);
20194
+ const tools = this.getToolsForAgent(config);
20195
+ const messages = [{ role: "user", content: agent.task }];
20196
+ let totalInputTokens = 0;
20197
+ let totalOutputTokens = 0;
20198
+ let finalOutput = "";
20199
+ let iteration = 0;
20200
+ const maxTurns = config.maxTurns ?? 10;
20201
+ while (iteration < maxTurns) {
20202
+ iteration++;
20203
+ if (options.signal?.aborted) {
20204
+ agent.status = "failed";
20205
+ agent.error = "Aborted by user";
20206
+ agent.completedAt = /* @__PURE__ */ new Date();
20207
+ this.moveToCompleted(agent.id);
20208
+ options.onStatusChange?.(agent);
20209
+ return {
20210
+ agent,
20211
+ success: false,
20212
+ output: "Agent execution was aborted",
20213
+ usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens }
20214
+ };
20215
+ }
20216
+ const response = await this.provider.chatWithTools(messages, {
20217
+ system: config.systemPrompt,
20218
+ tools,
20219
+ maxTokens: 4096
20220
+ });
20221
+ totalInputTokens += response.usage.inputTokens;
20222
+ totalOutputTokens += response.usage.outputTokens;
20223
+ if (response.content) {
20224
+ finalOutput += response.content;
20225
+ options.onOutput?.(agent, response.content);
20226
+ }
20227
+ if (!response.toolCalls || response.toolCalls.length === 0) {
20228
+ messages.push({ role: "assistant", content: response.content });
20229
+ break;
20230
+ }
20231
+ const toolResults = await this.executeToolCalls(response.toolCalls, config);
20232
+ const toolUses = response.toolCalls.map((tc) => ({
20233
+ type: "tool_use",
20234
+ id: tc.id,
20235
+ name: tc.name,
20236
+ input: tc.input,
20237
+ geminiThoughtSignature: tc.geminiThoughtSignature
20238
+ }));
20239
+ const assistantContent = response.content ? [{ type: "text", text: response.content }, ...toolUses] : toolUses;
20240
+ messages.push({ role: "assistant", content: assistantContent });
20241
+ messages.push({ role: "user", content: toolResults });
20242
+ this.logger.debug(`Agent ${agent.id} completed iteration ${iteration}`, {
20243
+ toolCalls: response.toolCalls.length
20244
+ });
20245
+ }
20246
+ agent.status = "completed";
20247
+ agent.result = finalOutput;
20248
+ agent.completedAt = /* @__PURE__ */ new Date();
20249
+ this.moveToCompleted(agent.id);
20250
+ options.onStatusChange?.(agent);
20251
+ this.logger.info(`Agent ${agent.id} completed`, {
20252
+ iterations: iteration,
20253
+ outputLength: finalOutput.length
20254
+ });
20255
+ return {
20256
+ agent,
20257
+ success: true,
20258
+ output: finalOutput,
20259
+ usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens }
20260
+ };
20261
+ }
20262
+ /**
20263
+ * Get tool definitions filtered for the agent's allowed tools
20264
+ */
20265
+ getToolsForAgent(config) {
20266
+ const allTools = this.toolRegistry.getToolDefinitionsForLLM();
20267
+ const allowedSet = new Set(config.tools);
20268
+ return allTools.filter((tool) => allowedSet.has(tool.name));
20269
+ }
20270
+ /**
20271
+ * Execute tool calls and return results
20272
+ */
20273
+ async executeToolCalls(toolCalls, config) {
20274
+ const results = [];
20275
+ const allowedTools = new Set(config.tools);
20276
+ for (const toolCall of toolCalls) {
20277
+ if (!allowedTools.has(toolCall.name)) {
20278
+ results.push({
20279
+ type: "tool_result",
20280
+ tool_use_id: toolCall.id,
20281
+ content: `Tool '${toolCall.name}' is not available to this agent type`,
20282
+ is_error: true
20283
+ });
20284
+ continue;
20285
+ }
20286
+ try {
20287
+ const result = await this.toolRegistry.execute(toolCall.name, toolCall.input);
20288
+ results.push({
20289
+ type: "tool_result",
20290
+ tool_use_id: toolCall.id,
20291
+ content: result.success ? String(result.data ?? "Success") : `Error: ${result.error}`,
20292
+ is_error: !result.success
20293
+ });
20294
+ } catch (error) {
20295
+ const errorMessage = error instanceof Error ? error.message : String(error);
20296
+ results.push({
20297
+ type: "tool_result",
20298
+ tool_use_id: toolCall.id,
20299
+ content: `Tool execution failed: ${errorMessage}`,
20300
+ is_error: true
20301
+ });
20302
+ }
20303
+ }
20304
+ return results;
20305
+ }
20306
+ /**
20307
+ * Move an agent from active to completed
20308
+ */
20309
+ moveToCompleted(agentId) {
20310
+ const agent = this.activeAgents.get(agentId);
20311
+ if (agent) {
20312
+ this.activeAgents.delete(agentId);
20313
+ this.completedAgents.set(agentId, agent);
20314
+ this.abortControllers.delete(agentId);
20315
+ }
20316
+ }
20317
+ /**
20318
+ * Emit an agent event
20319
+ */
20320
+ emitEvent(type, agent, result) {
20321
+ const event = { type, agent, result };
20322
+ this.emit(type, event);
20323
+ this.emit("agent", event);
20324
+ }
20325
+ };
20326
+
20327
+ // src/agents/provider-bridge.ts
20328
+ var agentProvider = null;
20329
+ var agentToolRegistry = null;
20330
+ var agentManagerInstance = null;
20331
+ function setAgentProvider(provider) {
20332
+ agentProvider = provider;
20333
+ agentManagerInstance = null;
20334
+ }
20335
+ function getAgentProvider() {
20336
+ return agentProvider;
20337
+ }
20338
+ function setAgentToolRegistry(registry) {
20339
+ agentToolRegistry = registry;
20340
+ agentManagerInstance = null;
20341
+ }
20342
+ function getAgentToolRegistry() {
20343
+ return agentToolRegistry;
20344
+ }
20345
+ function getAgentManager() {
20346
+ if (!agentProvider || !agentToolRegistry) {
20347
+ return null;
20348
+ }
20349
+ if (!agentManagerInstance) {
20350
+ agentManagerInstance = new AgentManager(agentProvider, agentToolRegistry);
20351
+ }
20352
+ return agentManagerInstance;
20353
+ }
20354
+
20355
+ // src/cli/repl/modes.ts
20356
+ var AGENT_MODES = {
20357
+ ask: {
20358
+ id: "ask",
20359
+ label: "Ask",
20360
+ description: "Answer questions and explain code without modifying files.",
20361
+ readOnly: true,
20362
+ preferredTools: ["read_file", "grep", "glob", "codebase_map", "lsp_definition"],
20363
+ requiresVerification: false
20364
+ },
20365
+ plan: {
20366
+ id: "plan",
20367
+ label: "Plan",
20368
+ description: "Explore and produce an implementation plan with read-only tools.",
20369
+ readOnly: true,
20370
+ preferredTools: ["read_file", "grep", "glob", "codebase_map", "lsp_workspace_symbols"],
20371
+ requiresVerification: false
20372
+ },
20373
+ build: {
20374
+ id: "build",
20375
+ label: "Build",
20376
+ description: "Implement code changes and verify them.",
20377
+ readOnly: false,
20378
+ preferredTools: ["read_file", "edit_file", "write_file", "bash_exec", "run_tests"],
20379
+ requiresVerification: true
20380
+ },
20381
+ debug: {
20382
+ id: "debug",
20383
+ label: "Debug",
20384
+ description: "Reproduce failures, trace root cause, patch, and verify.",
20385
+ readOnly: false,
20386
+ preferredTools: ["bash_exec", "read_file", "grep", "lsp_references", "edit_file"],
20387
+ requiresVerification: true
20388
+ },
20389
+ review: {
20390
+ id: "review",
20391
+ label: "Review",
20392
+ description: "Inspect code quality, security, behavior changes, and test gaps.",
20393
+ readOnly: true,
20394
+ preferredTools: ["git_diff", "read_file", "grep", "review_code", "calculate_quality"],
20395
+ requiresVerification: false
20396
+ },
20397
+ architect: {
20398
+ id: "architect",
20399
+ label: "Architect",
20400
+ description: "Design architecture and split work for architect/editor execution.",
20401
+ readOnly: true,
20402
+ preferredTools: ["codebase_map", "lsp_workspace_symbols", "grep", "create_agent_plan"],
20403
+ requiresVerification: false
20404
+ }
20405
+ };
20406
+ function getAgentMode(mode) {
20407
+ return AGENT_MODES[mode];
20408
+ }
20409
+ function listAgentModes() {
20410
+ return Object.values(AGENT_MODES);
20411
+ }
20412
+
20413
+ // src/cli/repl/sessions/storage.ts
20414
+ init_paths();
20415
+ var DEFAULT_CONFIG2 = {
20416
+ storageDir: CONFIG_PATHS.sessions,
20417
+ autoSaveInterval: 3e4,
20418
+ maxSessionsPerProject: 20,
20419
+ compressOldSessions: false
20420
+ };
20421
+ var SessionStore = class {
20422
+ config;
20423
+ initialized = false;
20424
+ constructor(config = {}) {
20425
+ this.config = { ...DEFAULT_CONFIG2, ...config };
20426
+ }
20427
+ async ensureInitialized() {
20428
+ if (this.initialized) return;
20429
+ await mkdir(this.config.storageDir, { recursive: true });
20430
+ this.initialized = true;
20431
+ }
20432
+ getSessionFiles(sessionId) {
20433
+ const sessionDir = this.getSessionDir(sessionId);
20434
+ return {
20435
+ metadata: join(sessionDir, "metadata.json"),
20436
+ conversation: join(sessionDir, "conversation.jsonl"),
20437
+ context: join(sessionDir, "context.json")
20438
+ };
20439
+ }
20440
+ getSessionDir(sessionId) {
20441
+ return join(this.config.storageDir, sessionId);
20442
+ }
20443
+ async exists(sessionId) {
20444
+ try {
20445
+ const files = this.getSessionFiles(sessionId);
20446
+ await access(files.metadata);
20447
+ return true;
20448
+ } catch {
20449
+ return false;
20450
+ }
20451
+ }
20452
+ async save(session) {
20453
+ await this.ensureInitialized();
20454
+ const files = this.getSessionFiles(session.id);
20455
+ const sessionDir = this.getSessionDir(session.id);
20456
+ await mkdir(sessionDir, { recursive: true });
20457
+ const metadata = {
20458
+ id: session.id,
20459
+ projectPath: session.projectPath,
20460
+ startedAt: session.startedAt,
20461
+ lastSavedAt: /* @__PURE__ */ new Date(),
20462
+ config: session.config,
20463
+ messageCount: session.messages.length,
20464
+ totalTokens: this.calculateTokens(session),
20465
+ title: this.generateTitle(session),
20466
+ status: "active"
20467
+ };
20468
+ await writeFile(files.metadata, JSON.stringify(metadata, null, 2), "utf-8");
20469
+ const conversationLines = session.messages.map((msg) => {
20470
+ const serialized = {
20471
+ role: msg.role,
20472
+ content: msg.content,
20473
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
20474
+ };
20475
+ return JSON.stringify(serialized);
20476
+ });
20477
+ await writeFile(files.conversation, conversationLines.join("\n"), "utf-8");
20478
+ const context = {
20479
+ tokenUsage: this.calculateTokens(session)
20480
+ };
20481
+ await writeFile(files.context, JSON.stringify(context, null, 2), "utf-8");
20482
+ }
20483
+ async load(sessionId) {
20484
+ await this.ensureInitialized();
20485
+ const files = this.getSessionFiles(sessionId);
20486
+ try {
20487
+ const metadataContent = await readFile(files.metadata, "utf-8");
20488
+ const metadata = JSON.parse(metadataContent);
20489
+ const conversationContent = await readFile(files.conversation, "utf-8");
20490
+ const messages = conversationContent.split("\n").filter((line) => line.trim()).map((line) => {
20491
+ const parsed = JSON.parse(line);
20492
+ return {
20493
+ role: parsed.role,
20494
+ content: parsed.content
20495
+ };
20496
+ });
20497
+ const session = {
20498
+ id: metadata.id,
20499
+ startedAt: new Date(metadata.startedAt),
20500
+ messages,
20501
+ projectPath: metadata.projectPath,
20502
+ config: metadata.config,
20503
+ trustedTools: /* @__PURE__ */ new Set()
20504
+ };
20505
+ return session;
20506
+ } catch {
20507
+ return null;
20508
+ }
20509
+ }
20510
+ async listSessions(projectPath) {
20511
+ await this.ensureInitialized();
20512
+ const sessions = [];
20513
+ try {
20514
+ const entries = await readdir(this.config.storageDir, {
20515
+ withFileTypes: true
20516
+ });
20517
+ for (const entry of entries) {
20518
+ if (!entry.isDirectory()) continue;
20519
+ const metadataPath = join(this.config.storageDir, entry.name, "metadata.json");
20520
+ try {
20521
+ const content = await readFile(metadataPath, "utf-8");
20522
+ const metadata = JSON.parse(content);
20523
+ metadata.startedAt = new Date(metadata.startedAt);
20524
+ metadata.lastSavedAt = new Date(metadata.lastSavedAt);
20525
+ if (!projectPath || metadata.projectPath === projectPath) {
20526
+ sessions.push(metadata);
20527
+ }
20528
+ } catch {
20529
+ continue;
20530
+ }
20531
+ }
20532
+ } catch {
20533
+ return [];
20534
+ }
20535
+ sessions.sort((a, b) => new Date(b.lastSavedAt).getTime() - new Date(a.lastSavedAt).getTime());
20536
+ return sessions;
20537
+ }
20538
+ async delete(sessionId) {
20539
+ await this.ensureInitialized();
20540
+ const sessionDir = this.getSessionDir(sessionId);
20541
+ try {
20542
+ await rm(sessionDir, { recursive: true, force: true });
20543
+ return true;
20544
+ } catch {
20545
+ return false;
20546
+ }
20547
+ }
20548
+ async getMostRecent(projectPath) {
20549
+ const sessions = await this.listSessions(projectPath);
20550
+ return sessions[0] ?? null;
20551
+ }
20552
+ /**
20553
+ * Append messages to an existing session's conversation file
20554
+ * Creates the file if it doesn't exist
20555
+ * @param sessionId - The session ID
20556
+ * @param messages - Messages to append
20557
+ */
20558
+ async appendMessages(sessionId, messages) {
20559
+ await this.ensureInitialized();
20560
+ const files = this.getSessionFiles(sessionId);
20561
+ const sessionDir = this.getSessionDir(sessionId);
20562
+ await mkdir(sessionDir, { recursive: true });
20563
+ const newLines = messages.map((msg) => {
20564
+ const serialized = {
20565
+ role: msg.role,
20566
+ content: msg.content,
20567
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
20568
+ };
20569
+ return JSON.stringify(serialized);
20570
+ });
20571
+ let existingContent = "";
20572
+ try {
20573
+ existingContent = await readFile(files.conversation, "utf-8");
20574
+ if (existingContent && !existingContent.endsWith("\n")) {
20575
+ existingContent += "\n";
20576
+ }
20577
+ } catch {
20578
+ }
20579
+ await writeFile(files.conversation, existingContent + newLines.join("\n"), "utf-8");
20580
+ }
20581
+ /**
20582
+ * Prune old sessions to keep storage manageable
20583
+ * Keeps the most recent maxSessionsPerProject sessions per project
20584
+ * @param projectPath - Optional project path to prune (prunes all if not specified)
20585
+ * @returns Number of sessions deleted
20586
+ */
20587
+ async pruneOldSessions(projectPath) {
20588
+ await this.ensureInitialized();
20589
+ const allSessions = await this.listSessions();
20590
+ const sessionsByProject = /* @__PURE__ */ new Map();
20591
+ for (const session of allSessions) {
20592
+ if (projectPath && session.projectPath !== projectPath) {
20593
+ continue;
20594
+ }
20595
+ const existing = sessionsByProject.get(session.projectPath) ?? [];
20596
+ existing.push(session);
20597
+ sessionsByProject.set(session.projectPath, existing);
20598
+ }
20599
+ let deletedCount = 0;
20600
+ for (const [, sessions] of sessionsByProject) {
20601
+ const sessionsToDelete = sessions.slice(this.config.maxSessionsPerProject);
20602
+ for (const session of sessionsToDelete) {
20603
+ const deleted = await this.delete(session.id);
20604
+ if (deleted) {
20605
+ deletedCount++;
20606
+ }
20607
+ }
20608
+ }
20609
+ return deletedCount;
20610
+ }
20611
+ calculateTokens(session) {
20612
+ if (session.contextManager) {
20613
+ const stats = session.contextManager.getUsageStats();
20614
+ return {
20615
+ input: stats.used,
20616
+ output: 0
20617
+ };
20618
+ }
20619
+ let input = 0;
20620
+ let output = 0;
20621
+ for (const msg of session.messages) {
20622
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
20623
+ const tokens = Math.ceil(content.length / 4);
20624
+ if (msg.role === "user") {
20625
+ input += tokens;
20626
+ } else {
20627
+ output += tokens;
20628
+ }
20629
+ }
20630
+ return { input, output };
20631
+ }
20632
+ generateTitle(session) {
20633
+ for (const msg of session.messages) {
20634
+ if (msg.role === "user" && typeof msg.content === "string") {
20635
+ const content = msg.content.trim();
20636
+ if (content.length > 10) {
20637
+ const firstLine = content.split("\n")[0] ?? content;
20638
+ return firstLine.length > 50 ? firstLine.slice(0, 47) + "..." : firstLine;
20639
+ }
20640
+ }
20641
+ }
20642
+ return "Untitled session";
20643
+ }
20644
+ };
20645
+ function createSessionStore(config) {
20646
+ return new SessionStore(config);
20647
+ }
20648
+
20649
+ // src/runtime/agent-runtime.ts
20650
+ init_env();
20651
+ init_errors();
20652
+ init_allowed_paths();
20653
+ function levenshtein(a, b) {
20654
+ if (a === b) return 0;
20655
+ if (a.length === 0) return b.length;
20656
+ if (b.length === 0) return a.length;
20657
+ const row = Array.from({ length: b.length + 1 }, (_, i) => i);
20658
+ for (let i = 1; i <= a.length; i++) {
20659
+ let prev = i;
20660
+ for (let j = 1; j <= b.length; j++) {
20661
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
20662
+ const val = Math.min(
20663
+ row[j] + 1,
20664
+ // deletion
20665
+ prev + 1,
20666
+ // insertion
20667
+ row[j - 1] + cost
20668
+ // substitution
20669
+ );
20670
+ row[j - 1] = prev;
20671
+ prev = val;
20672
+ }
20673
+ row[b.length] = prev;
20674
+ }
20675
+ return row[b.length];
20676
+ }
20677
+
20678
+ // src/utils/file-suggestions.ts
20679
+ var MAX_DIR_ENTRIES = 200;
20680
+ var MAX_SUGGESTIONS = 5;
20681
+ var DEFAULT_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
20682
+ "node_modules",
20683
+ ".git",
20684
+ "dist",
20685
+ "build",
20686
+ "coverage",
20687
+ ".next",
20688
+ "vendor",
20689
+ "__pycache__",
20690
+ ".venv",
20691
+ "venv",
20692
+ ".tox",
20693
+ ".pytest_cache",
20694
+ ".mypy_cache",
20695
+ ".ruff_cache",
20696
+ ".idea",
20697
+ ".vscode",
20698
+ ".DS_Store"
20699
+ ]);
20700
+ var DEFAULT_FIND_OPTIONS = {
20701
+ maxDepth: 8,
20702
+ timeoutMs: 3e3,
20703
+ includeHidden: true,
20704
+ excludeDirs: DEFAULT_EXCLUDE_DIRS,
20705
+ maxResults: 5,
20706
+ type: "file"
20707
+ };
20708
+ async function findFileRecursive(rootDir, target, options = {}) {
20709
+ const opts = { ...DEFAULT_FIND_OPTIONS, ...options };
20710
+ const targetLower = target.toLowerCase();
20711
+ const results = [];
20712
+ const startTime = Date.now();
20713
+ const isTimedOut = () => Date.now() - startTime > opts.timeoutMs;
20714
+ const queue = [[path17__default.resolve(rootDir), 0]];
20715
+ const visited = /* @__PURE__ */ new Set();
20716
+ while (queue.length > 0 && results.length < opts.maxResults) {
20717
+ if (isTimedOut()) break;
20718
+ const [currentDir, depth] = queue.shift();
20719
+ if (visited.has(currentDir)) continue;
20720
+ visited.add(currentDir);
20721
+ if (depth > opts.maxDepth) continue;
20722
+ try {
20723
+ const entries = await fs16__default.readdir(currentDir, { withFileTypes: true });
20724
+ for (const entry of entries) {
20725
+ if (isTimedOut()) break;
20726
+ const entryName = entry.name;
20727
+ const entryPath = path17__default.join(currentDir, entryName);
20728
+ if (!opts.includeHidden && entryName.startsWith(".")) continue;
20729
+ if (entry.isDirectory() && opts.excludeDirs.has(entryName)) continue;
20730
+ const isMatch = opts.type === "file" && entry.isFile() || opts.type === "directory" && entry.isDirectory() || opts.type === "both";
20731
+ if (isMatch) {
20732
+ const entryNameLower = entryName.toLowerCase();
20733
+ let distance;
20734
+ if (entryNameLower === targetLower) {
20735
+ distance = 0;
20736
+ } else {
20737
+ distance = levenshtein(targetLower, entryNameLower);
20738
+ }
20739
+ const maxDistance = Math.max(target.length * 0.6, 3);
20740
+ if (distance <= maxDistance) {
20741
+ results.push({ path: entryPath, distance });
20742
+ }
20743
+ }
20744
+ if (entry.isDirectory() && !opts.excludeDirs.has(entryName)) {
20745
+ queue.push([entryPath, depth + 1]);
20746
+ }
20747
+ }
20748
+ } catch {
20749
+ continue;
20750
+ }
20751
+ }
20752
+ return results.sort((a, b) => a.distance - b.distance).slice(0, opts.maxResults);
20753
+ }
20754
+ async function suggestSimilarFilesDeep(missingPath, rootDir = process.cwd(), options) {
20755
+ const fastResults = await suggestSimilarFiles(missingPath, {
20756
+ maxResults: MAX_SUGGESTIONS
20757
+ });
20758
+ if (fastResults.length > 0) {
20759
+ return fastResults;
20760
+ }
20761
+ const absPath = path17__default.resolve(missingPath);
20762
+ const target = path17__default.basename(absPath);
20763
+ return findFileRecursive(rootDir, target, options);
20764
+ }
20765
+ async function suggestSimilarDirsDeep(missingPath, rootDir = process.cwd(), options) {
20766
+ const absPath = path17__default.resolve(missingPath);
20767
+ const target = path17__default.basename(absPath);
20768
+ const parentDir = path17__default.dirname(absPath);
20769
+ try {
20770
+ const entries = await fs16__default.readdir(parentDir, { withFileTypes: true });
20771
+ const dirs = entries.filter((e) => e.isDirectory());
20772
+ const scored = dirs.map((d) => ({
20773
+ path: path17__default.join(parentDir, d.name),
20774
+ distance: levenshtein(target.toLowerCase(), d.name.toLowerCase())
20775
+ })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance).slice(0, options?.maxResults ?? MAX_SUGGESTIONS);
20776
+ if (scored.length > 0) {
20777
+ return scored;
20778
+ }
20779
+ } catch {
20780
+ }
20781
+ return findFileRecursive(rootDir, target, { ...options, type: "directory" });
20782
+ }
20783
+ async function suggestSimilarFiles(missingPath, options) {
20784
+ const absPath = path17__default.resolve(missingPath);
20785
+ const dir = path17__default.dirname(absPath);
20786
+ const target = path17__default.basename(absPath);
20787
+ const maxResults = options?.maxResults;
20788
+ try {
20789
+ const entries = await fs16__default.readdir(dir);
20790
+ const limited = entries.slice(0, MAX_DIR_ENTRIES);
20791
+ const scored = limited.map((name) => ({
20792
+ path: path17__default.join(dir, name),
20793
+ distance: levenshtein(target.toLowerCase(), name.toLowerCase())
20794
+ })).filter((s) => s.distance <= Math.max(target.length * 0.6, 3)).sort((a, b) => a.distance - b.distance);
20795
+ return scored.slice(0, maxResults);
19494
20796
  } catch {
19495
20797
  return [];
19496
20798
  }
@@ -20290,7 +21592,7 @@ function escapeRegex(str) {
20290
21592
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20291
21593
  }
20292
21594
  init_errors();
20293
- var DEFAULT_TIMEOUT_MS = 12e4;
21595
+ var DEFAULT_TIMEOUT_MS2 = 12e4;
20294
21596
  var MAX_OUTPUT_SIZE = 1024 * 1024;
20295
21597
  var DANGEROUS_PATTERNS_FULL = [
20296
21598
  { pattern: /\brm\s+-rf\s+\/(?!\w)/, rule: "rm -rf on root filesystem" },
@@ -20412,7 +21714,7 @@ Examples:
20412
21714
  }
20413
21715
  }
20414
21716
  const startTime = performance.now();
20415
- const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS;
21717
+ const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS2;
20416
21718
  const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
20417
21719
  const heartbeat = new CommandHeartbeat2({
20418
21720
  onUpdate: (stats) => {
@@ -21071,93 +22373,45 @@ var SimpleAutoCommitSchema = z.object({
21071
22373
  message: z.string().optional()
21072
22374
  });
21073
22375
  var simpleAutoCommitTool = defineTool({
21074
- name: "simpleAutoCommit",
21075
- description: "Auto-commit staged changes with generated message",
21076
- category: "git",
21077
- parameters: SimpleAutoCommitSchema,
21078
- async execute(input) {
21079
- try {
21080
- try {
21081
- execSync("git diff --cached --quiet", { cwd: process.cwd(), stdio: "ignore" });
21082
- return {
21083
- stdout: "",
21084
- stderr: "No staged changes to commit",
21085
- exitCode: 1,
21086
- duration: 0
21087
- };
21088
- } catch {
21089
- }
21090
- const message = input.message || generateSimpleCommitMessage();
21091
- execSync(`git commit -m "${message}"`, {
21092
- encoding: "utf-8",
21093
- cwd: process.cwd(),
21094
- stdio: "pipe"
21095
- });
21096
- return {
21097
- stdout: `\u2713 Committed: ${message}`,
21098
- stderr: "",
21099
- exitCode: 0,
21100
- duration: 0
21101
- };
21102
- } catch (error) {
21103
- return {
21104
- stdout: "",
21105
- stderr: error instanceof Error ? error.message : "Commit failed",
21106
- exitCode: 1,
21107
- duration: 0
21108
- };
21109
- }
21110
- }
21111
- });
21112
- var gitSimpleTools = [checkProtectedBranchTool, simpleAutoCommitTool];
21113
-
21114
- // src/cli/repl/agents/manager.ts
21115
- init_logger();
21116
- var AGENT_NAMES = {
21117
- explore: "Explorer",
21118
- plan: "Planner",
21119
- test: "Tester",
21120
- debug: "Debugger",
21121
- review: "Reviewer",
21122
- architect: "Architect",
21123
- security: "Security Auditor",
21124
- tdd: "TDD Guide",
21125
- refactor: "Refactorer",
21126
- e2e: "E2E Tester",
21127
- docs: "Docs Writer",
21128
- database: "Database Engineer"
21129
- };
21130
- var AGENT_DESCRIPTIONS = {
21131
- explore: "Search the codebase to answer questions and gather information",
21132
- plan: "Design implementation approaches and create detailed plans",
21133
- test: "Write and run tests to ensure code quality",
21134
- debug: "Analyze errors and fix issues",
21135
- review: "Review code for quality and best practices",
21136
- architect: "Design system architecture and create architectural decision records",
21137
- security: "Audit code for security vulnerabilities using OWASP Top 10",
21138
- tdd: "Drive development with test-first methodology and RED-GREEN-REFACTOR cycle",
21139
- refactor: "Improve code structure and quality without changing behavior",
21140
- e2e: "Write and run end-to-end tests covering full user workflows",
21141
- docs: "Generate and maintain documentation for code, APIs, and architecture",
21142
- database: "Design database schemas, write migrations, and optimize queries"
21143
- };
21144
-
21145
- // src/agents/provider-bridge.ts
21146
- var agentProvider = null;
21147
- var agentToolRegistry = null;
21148
- function getAgentProvider() {
21149
- return agentProvider;
21150
- }
21151
- function getAgentToolRegistry() {
21152
- return agentToolRegistry;
21153
- }
21154
- function getAgentManager() {
21155
- {
21156
- return null;
22376
+ name: "simpleAutoCommit",
22377
+ description: "Auto-commit staged changes with generated message",
22378
+ category: "git",
22379
+ parameters: SimpleAutoCommitSchema,
22380
+ async execute(input) {
22381
+ try {
22382
+ try {
22383
+ execSync("git diff --cached --quiet", { cwd: process.cwd(), stdio: "ignore" });
22384
+ return {
22385
+ stdout: "",
22386
+ stderr: "No staged changes to commit",
22387
+ exitCode: 1,
22388
+ duration: 0
22389
+ };
22390
+ } catch {
22391
+ }
22392
+ const message = input.message || generateSimpleCommitMessage();
22393
+ execSync(`git commit -m "${message}"`, {
22394
+ encoding: "utf-8",
22395
+ cwd: process.cwd(),
22396
+ stdio: "pipe"
22397
+ });
22398
+ return {
22399
+ stdout: `\u2713 Committed: ${message}`,
22400
+ stderr: "",
22401
+ exitCode: 0,
22402
+ duration: 0
22403
+ };
22404
+ } catch (error) {
22405
+ return {
22406
+ stdout: "",
22407
+ stderr: error instanceof Error ? error.message : "Commit failed",
22408
+ exitCode: 1,
22409
+ duration: 0
22410
+ };
22411
+ }
21157
22412
  }
21158
- }
21159
-
21160
- // src/tools/simple-agent.ts
22413
+ });
22414
+ var gitSimpleTools = [checkProtectedBranchTool, simpleAutoCommitTool];
21161
22415
  var AGENT_TYPES = [
21162
22416
  "explore",
21163
22417
  "plan",
@@ -21270,7 +22524,7 @@ var checkAgentCapabilityTool = defineTool({
21270
22524
  async execute() {
21271
22525
  const provider = getAgentProvider();
21272
22526
  const toolRegistry = getAgentToolRegistry();
21273
- const isReady = provider !== null;
22527
+ const isReady = provider !== null && toolRegistry !== null;
21274
22528
  const agentTypes = Object.entries(AGENT_DESCRIPTIONS).map(([type, description]) => ({
21275
22529
  type,
21276
22530
  name: AGENT_NAMES[type],
@@ -21284,12 +22538,12 @@ var checkAgentCapabilityTool = defineTool({
21284
22538
  ready: isReady,
21285
22539
  availableTypes: agentTypes,
21286
22540
  features: {
21287
- taskDelegation: "requires provider initialization",
21288
- parallelSpawn: "requires provider initialization",
21289
- multiTurnToolUse: "requires provider initialization",
21290
- specializedAgents: "requires provider initialization"
22541
+ taskDelegation: isReady ? "ready" : "requires provider initialization",
22542
+ parallelSpawn: isReady ? "ready" : "requires provider initialization",
22543
+ multiTurnToolUse: isReady ? "ready" : "requires provider initialization",
22544
+ specializedAgents: isReady ? "ready" : "requires provider initialization"
21291
22545
  },
21292
- status: "Provider not initialized. Call setAgentProvider() during startup."
22546
+ status: isReady ? "Multi-agent system is ready with 12 specialized agent types." : "Provider not initialized. Call setAgentProvider() during startup."
21293
22547
  }),
21294
22548
  stderr: "",
21295
22549
  exitCode: 0,
@@ -30161,7 +31415,7 @@ var HTTPTransport = class {
30161
31415
 
30162
31416
  // src/mcp/transport/sse.ts
30163
31417
  init_errors2();
30164
- var DEFAULT_CONFIG2 = {
31418
+ var DEFAULT_CONFIG3 = {
30165
31419
  initialReconnectDelay: 1e3,
30166
31420
  maxReconnectDelay: 3e4,
30167
31421
  maxReconnectAttempts: 10
@@ -30177,7 +31431,7 @@ var SSETransport = class {
30177
31431
  errorHandler = null;
30178
31432
  closeHandler = null;
30179
31433
  constructor(config) {
30180
- this.config = { ...DEFAULT_CONFIG2, ...config };
31434
+ this.config = { ...DEFAULT_CONFIG3, ...config };
30181
31435
  }
30182
31436
  /**
30183
31437
  * Connect to the SSE endpoint
@@ -30646,6 +31900,235 @@ function getMCPServerManager() {
30646
31900
 
30647
31901
  // src/mcp/tools.ts
30648
31902
  init_errors2();
31903
+ var DEFAULT_OPTIONS = {
31904
+ namePrefix: "mcp",
31905
+ category: "deploy",
31906
+ requestTimeout: 6e4
31907
+ };
31908
+ function buildMcpToolDescription(serverName, tool) {
31909
+ const base = tool.description || `Tool '${tool.name}' exposed by MCP server '${serverName}'`;
31910
+ const lowerServer = serverName.toLowerCase();
31911
+ if (lowerServer.includes("atlassian") || lowerServer.includes("jira") || lowerServer.includes("confluence")) {
31912
+ return `${base}. Use this MCP tool for Atlassian/Jira/Confluence data. Prefer it over direct web_fetch or http_fetch for Atlassian content.`;
31913
+ }
31914
+ return `${base}. Exposed by MCP server '${serverName}'. Prefer this MCP tool over generic web/http fetch when accessing data from that connected service.`;
31915
+ }
31916
+ function jsonSchemaToZod(schema) {
31917
+ if (schema.enum && Array.isArray(schema.enum)) {
31918
+ const values = schema.enum;
31919
+ if (values.length > 0 && values.every((v) => typeof v === "string")) {
31920
+ return z.enum(values);
31921
+ }
31922
+ const literals = values.map((v) => z.literal(v));
31923
+ if (literals.length < 2) {
31924
+ return literals[0] ?? z.any();
31925
+ }
31926
+ return z.union(literals);
31927
+ }
31928
+ if (schema.const !== void 0) {
31929
+ return z.literal(schema.const);
31930
+ }
31931
+ if (schema.oneOf && Array.isArray(schema.oneOf)) {
31932
+ const schemas = schema.oneOf.map(jsonSchemaToZod);
31933
+ if (schemas.length >= 2) {
31934
+ return z.union(schemas);
31935
+ }
31936
+ return schemas[0] ?? z.unknown();
31937
+ }
31938
+ if (schema.anyOf && Array.isArray(schema.anyOf)) {
31939
+ const schemas = schema.anyOf.map(jsonSchemaToZod);
31940
+ if (schemas.length >= 2) {
31941
+ return z.union(schemas);
31942
+ }
31943
+ return schemas[0] ?? z.unknown();
31944
+ }
31945
+ if (schema.allOf && Array.isArray(schema.allOf)) {
31946
+ const schemas = schema.allOf.map(jsonSchemaToZod);
31947
+ return schemas.reduce((acc, s) => z.intersection(acc, s));
31948
+ }
31949
+ const type = schema.type;
31950
+ const makeNullable = (s) => {
31951
+ if (schema.nullable === true) return s.nullable();
31952
+ return s;
31953
+ };
31954
+ switch (type) {
31955
+ case "string": {
31956
+ let s = z.string();
31957
+ if (schema.format) {
31958
+ switch (schema.format) {
31959
+ case "uri":
31960
+ case "url":
31961
+ s = z.string().url();
31962
+ break;
31963
+ case "email":
31964
+ s = z.string().email();
31965
+ break;
31966
+ case "date-time":
31967
+ case "datetime":
31968
+ s = z.string().datetime();
31969
+ break;
31970
+ }
31971
+ }
31972
+ if (typeof schema.minLength === "number") s = s.min(schema.minLength);
31973
+ if (typeof schema.maxLength === "number") s = s.max(schema.maxLength);
31974
+ return makeNullable(s);
31975
+ }
31976
+ case "number": {
31977
+ let n = z.number();
31978
+ if (typeof schema.minimum === "number") n = n.min(schema.minimum);
31979
+ if (typeof schema.maximum === "number") n = n.max(schema.maximum);
31980
+ if (typeof schema.exclusiveMinimum === "number") n = n.gt(schema.exclusiveMinimum);
31981
+ if (typeof schema.exclusiveMaximum === "number") n = n.lt(schema.exclusiveMaximum);
31982
+ return makeNullable(n);
31983
+ }
31984
+ case "integer": {
31985
+ let n = z.number().int();
31986
+ if (typeof schema.minimum === "number") n = n.min(schema.minimum);
31987
+ if (typeof schema.maximum === "number") n = n.max(schema.maximum);
31988
+ return makeNullable(n);
31989
+ }
31990
+ case "boolean":
31991
+ return makeNullable(z.boolean());
31992
+ case "null":
31993
+ return z.null();
31994
+ case "array":
31995
+ if (schema.items) {
31996
+ const itemSchema = jsonSchemaToZod(schema.items);
31997
+ let arr = z.array(itemSchema);
31998
+ if (typeof schema.minItems === "number") arr = arr.min(schema.minItems);
31999
+ if (typeof schema.maxItems === "number") arr = arr.max(schema.maxItems);
32000
+ return makeNullable(arr);
32001
+ }
32002
+ return makeNullable(z.array(z.unknown()));
32003
+ case "object": {
32004
+ const properties = schema.properties;
32005
+ const required = schema.required;
32006
+ if (!properties) {
32007
+ return makeNullable(z.record(z.string(), z.unknown()));
32008
+ }
32009
+ const shape = {};
32010
+ for (const [key, propSchema] of Object.entries(properties)) {
32011
+ let fieldSchema = jsonSchemaToZod(propSchema);
32012
+ if (!required?.includes(key)) {
32013
+ fieldSchema = fieldSchema.optional();
32014
+ }
32015
+ if (propSchema.description && typeof propSchema.description === "string") {
32016
+ fieldSchema = fieldSchema.describe(propSchema.description);
32017
+ }
32018
+ shape[key] = fieldSchema;
32019
+ }
32020
+ return makeNullable(z.object(shape));
32021
+ }
32022
+ default:
32023
+ if (Array.isArray(schema.type)) {
32024
+ const types = schema.type;
32025
+ if (types.includes("null")) {
32026
+ const nonNullType = types.find((t) => t !== "null");
32027
+ if (nonNullType) {
32028
+ return jsonSchemaToZod({ ...schema, type: nonNullType }).nullable();
32029
+ }
32030
+ }
32031
+ }
32032
+ return z.unknown();
32033
+ }
32034
+ }
32035
+ function createToolParametersSchema(tool) {
32036
+ const schema = tool.inputSchema;
32037
+ if (!schema || schema.type !== "object") {
32038
+ return z.object({});
32039
+ }
32040
+ return jsonSchemaToZod(schema);
32041
+ }
32042
+ function formatToolResult(result) {
32043
+ if (result.isError) {
32044
+ throw new Error(result.content.map((c) => c.text || "").join("\n"));
32045
+ }
32046
+ return result.content.map((item) => {
32047
+ switch (item.type) {
32048
+ case "text":
32049
+ return item.text || "";
32050
+ case "image":
32051
+ return `[Image: ${item.mimeType || "unknown"}]`;
32052
+ case "resource":
32053
+ return `[Resource: ${item.resource?.uri || "unknown"}]`;
32054
+ default:
32055
+ return "";
32056
+ }
32057
+ }).filter(Boolean).join("\n");
32058
+ }
32059
+ function createToolName(serverName, toolName, prefix) {
32060
+ return `${prefix}_${serverName}_${toolName}`.replace(/[^a-zA-Z0-9_]/g, "_");
32061
+ }
32062
+ function wrapMCPTool(tool, serverName, client, options = {}) {
32063
+ const opts = { ...DEFAULT_OPTIONS, ...options };
32064
+ const wrappedName = createToolName(serverName, tool.name, opts.namePrefix);
32065
+ const parametersSchema = createToolParametersSchema(tool);
32066
+ const cocoTool = {
32067
+ name: wrappedName,
32068
+ description: buildMcpToolDescription(serverName, tool),
32069
+ category: opts.category,
32070
+ parameters: parametersSchema,
32071
+ execute: async (params) => {
32072
+ const timeout = opts.requestTimeout;
32073
+ try {
32074
+ const result = await Promise.race([
32075
+ client.callTool({
32076
+ name: tool.name,
32077
+ arguments: params
32078
+ }),
32079
+ new Promise((_, reject) => {
32080
+ setTimeout(() => {
32081
+ reject(new MCPTimeoutError(`Tool '${tool.name}' timed out after ${timeout}ms`));
32082
+ }, timeout);
32083
+ })
32084
+ ]);
32085
+ return formatToolResult(result);
32086
+ } catch (error) {
32087
+ if (error instanceof MCPError) {
32088
+ throw error;
32089
+ }
32090
+ throw new MCPError(
32091
+ -32603,
32092
+ `Tool execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
32093
+ );
32094
+ }
32095
+ }
32096
+ };
32097
+ const wrapped = {
32098
+ originalTool: tool,
32099
+ serverName,
32100
+ wrappedName
32101
+ };
32102
+ return { tool: cocoTool, wrapped };
32103
+ }
32104
+ function wrapMCPTools(tools, serverName, client, options = {}) {
32105
+ const cocoTools = [];
32106
+ const wrappedTools = [];
32107
+ for (const tool of tools) {
32108
+ const { tool: cocoTool, wrapped } = wrapMCPTool(tool, serverName, client, options);
32109
+ cocoTools.push(cocoTool);
32110
+ wrappedTools.push(wrapped);
32111
+ }
32112
+ return { tools: cocoTools, wrapped: wrappedTools };
32113
+ }
32114
+ async function createToolsFromMCPServer(serverName, client, options = {}) {
32115
+ if (!client.isConnected()) {
32116
+ await client.initialize({
32117
+ protocolVersion: "2024-11-05",
32118
+ capabilities: {},
32119
+ clientInfo: { name: "coco-mcp-client", version: "0.2.0" }
32120
+ });
32121
+ }
32122
+ const { tools } = await client.listTools();
32123
+ return wrapMCPTools(tools, serverName, client, options);
32124
+ }
32125
+ async function registerMCPTools(registry, serverName, client, options = {}) {
32126
+ const { tools, wrapped } = await createToolsFromMCPServer(serverName, client, options);
32127
+ for (const tool of tools) {
32128
+ registry.register(tool);
32129
+ }
32130
+ return wrapped;
32131
+ }
30649
32132
 
30650
32133
  // src/tools/mcp.ts
30651
32134
  async function loadConfiguredServers(projectPath) {
@@ -30742,6 +32225,10 @@ built-in MCP OAuth browser login flow. Do not ask the user for raw tokens when t
30742
32225
  await manager.stopServer(target.name);
30743
32226
  }
30744
32227
  const connection = await manager.startServer(target);
32228
+ const toolRegistry = getAgentToolRegistry();
32229
+ if (toolRegistry) {
32230
+ await registerMCPTools(toolRegistry, connection.name, connection.client);
32231
+ }
30745
32232
  let tools;
30746
32233
  if (includeTools) {
30747
32234
  try {
@@ -30910,12 +32397,497 @@ function createFullToolRegistry() {
30910
32397
  registerAllTools(registry);
30911
32398
  return registry;
30912
32399
  }
32400
+ var InMemoryEventLog = class {
32401
+ events = [];
32402
+ record(type, data = {}) {
32403
+ const event = {
32404
+ id: randomUUID(),
32405
+ type,
32406
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
32407
+ data
32408
+ };
32409
+ this.events.push(event);
32410
+ return event;
32411
+ }
32412
+ list() {
32413
+ return [...this.events];
32414
+ }
32415
+ count() {
32416
+ return this.events.length;
32417
+ }
32418
+ clear() {
32419
+ this.events = [];
32420
+ }
32421
+ };
32422
+ var FileEventLog = class {
32423
+ constructor(filePath) {
32424
+ this.filePath = filePath;
32425
+ try {
32426
+ mkdirSync(dirname(filePath), { recursive: true });
32427
+ } catch {
32428
+ this.writable = false;
32429
+ }
32430
+ }
32431
+ filePath;
32432
+ memory = new InMemoryEventLog();
32433
+ writable = true;
32434
+ record(type, data = {}) {
32435
+ const event = this.memory.record(type, data);
32436
+ if (this.writable) {
32437
+ try {
32438
+ appendFileSync(this.filePath, JSON.stringify(event) + "\n", "utf-8");
32439
+ } catch {
32440
+ this.writable = false;
32441
+ }
32442
+ }
32443
+ return event;
32444
+ }
32445
+ list() {
32446
+ if (!this.writable) return this.memory.list();
32447
+ try {
32448
+ const raw = readFileSync(this.filePath, "utf-8");
32449
+ return raw.split("\n").filter(Boolean).flatMap((line) => {
32450
+ try {
32451
+ return [JSON.parse(line)];
32452
+ } catch {
32453
+ return [];
32454
+ }
32455
+ });
32456
+ } catch {
32457
+ return this.memory.list();
32458
+ }
32459
+ }
32460
+ count() {
32461
+ return this.list().length;
32462
+ }
32463
+ clear() {
32464
+ this.memory.clear();
32465
+ if (this.writable) {
32466
+ try {
32467
+ writeFileSync(this.filePath, "", "utf-8");
32468
+ } catch {
32469
+ this.writable = false;
32470
+ }
32471
+ }
32472
+ }
32473
+ };
32474
+ function createEventLog() {
32475
+ return new InMemoryEventLog();
32476
+ }
32477
+ function createFileEventLog(filePath) {
32478
+ return new FileEventLog(filePath);
32479
+ }
32480
+
32481
+ // src/runtime/permission-policy.ts
32482
+ var READ_ONLY_CATEGORIES = /* @__PURE__ */ new Set(["search", "web", "document"]);
32483
+ var WRITE_CATEGORIES = /* @__PURE__ */ new Set(["file", "git", "test", "build", "memory"]);
32484
+ var READ_ONLY_TOOL_NAMES = /* @__PURE__ */ new Set([
32485
+ "glob",
32486
+ "read_file",
32487
+ "list_dir",
32488
+ "tree",
32489
+ "grep",
32490
+ "find_in_file",
32491
+ "semantic_search",
32492
+ "codebase_map",
32493
+ "repo_context",
32494
+ "lsp_status",
32495
+ "lsp_document_symbols",
32496
+ "lsp_workspace_symbols",
32497
+ "lsp_definition",
32498
+ "lsp_references",
32499
+ "git_status",
32500
+ "git_log",
32501
+ "git_diff",
32502
+ "git_show",
32503
+ "git_branch",
32504
+ "recall_memory",
32505
+ "list_memories",
32506
+ "list_checkpoints",
32507
+ "spawnSimpleAgent",
32508
+ "checkAgentCapability"
32509
+ ]);
32510
+ var WRITE_CAPABLE_TOOL_NAMES = /* @__PURE__ */ new Set(["run_linter"]);
32511
+ var DESTRUCTIVE_TOOL_NAMES = /* @__PURE__ */ new Set([
32512
+ "bash_exec",
32513
+ "write_file",
32514
+ "edit_file",
32515
+ "delete_file",
32516
+ "restore_checkpoint",
32517
+ "git_commit",
32518
+ "git_push"
32519
+ ]);
32520
+ function riskForTool(tool) {
32521
+ if (READ_ONLY_TOOL_NAMES.has(tool.name)) return "read-only";
32522
+ if (DESTRUCTIVE_TOOL_NAMES.has(tool.name)) return "destructive";
32523
+ if (WRITE_CAPABLE_TOOL_NAMES.has(tool.name)) return "write";
32524
+ if (tool.category === "web") return "network";
32525
+ if (WRITE_CATEGORIES.has(tool.category)) return "write";
32526
+ if (tool.category === "quality") return "write";
32527
+ return "read-only";
32528
+ }
32529
+ var DefaultPermissionPolicy = class {
32530
+ canExecuteTool(mode, tool) {
32531
+ const definition = getAgentMode(mode);
32532
+ const risk = riskForTool(tool);
32533
+ const readOnlyTool = READ_ONLY_TOOL_NAMES.has(tool.name) || READ_ONLY_CATEGORIES.has(tool.category);
32534
+ if (definition.readOnly && !readOnlyTool) {
32535
+ return {
32536
+ allowed: false,
32537
+ reason: `${definition.label} mode is read-only; ${tool.name} is a ${tool.category} tool.`,
32538
+ risk
32539
+ };
32540
+ }
32541
+ if (risk === "destructive") {
32542
+ return {
32543
+ allowed: true,
32544
+ requiresConfirmation: true,
32545
+ reason: `${tool.name} can change repository state and should be confirmed.`,
32546
+ risk
32547
+ };
32548
+ }
32549
+ return { allowed: true, risk };
32550
+ }
32551
+ };
32552
+ function createPermissionPolicy() {
32553
+ return new DefaultPermissionPolicy();
32554
+ }
32555
+
32556
+ // src/runtime/provider-registry.ts
32557
+ init_catalog();
32558
+ var ProviderRegistry = class {
32559
+ listProviders() {
32560
+ return Object.values(PROVIDER_CATALOG);
32561
+ }
32562
+ getProvider(provider) {
32563
+ return getProviderCatalogEntry(provider);
32564
+ }
32565
+ listModels(provider) {
32566
+ return this.getProvider(provider).models;
32567
+ }
32568
+ getModel(provider, model2) {
32569
+ return getCatalogModel(provider, model2);
32570
+ }
32571
+ getDefaultModel(provider) {
32572
+ return getCatalogDefaultModel(provider);
32573
+ }
32574
+ getRecommendedModel(provider) {
32575
+ return getCatalogRecommendedModel(provider);
32576
+ }
32577
+ getCapability(provider, model2) {
32578
+ return getProviderRuntimeCapability(provider, model2);
32579
+ }
32580
+ async createProvider(provider, config = {}) {
32581
+ return createProvider(provider, config);
32582
+ }
32583
+ async probe(provider, model2, checkAvailability) {
32584
+ return probeProviderRuntimeCapability(provider, model2, checkAvailability);
32585
+ }
32586
+ };
32587
+ function createProviderRegistry() {
32588
+ return new ProviderRegistry();
32589
+ }
32590
+
32591
+ // src/runtime/agent-runtime.ts
32592
+ var AgentRuntime = class {
32593
+ constructor(options) {
32594
+ this.options = options;
32595
+ this.providerRegistry = createProviderRegistry();
32596
+ this.toolRegistry = options.toolRegistry ?? createFullToolRegistry();
32597
+ this.sessionStore = options.sessionStore ?? createSessionStore({});
32598
+ this.permissionPolicy = options.permissionPolicy ?? createPermissionPolicy();
32599
+ this.eventLog = options.eventLog ?? (options.eventLogPath ? createFileEventLog(options.eventLogPath) : createEventLog());
32600
+ this.providerType = options.providerType;
32601
+ this.model = options.model ?? options.providerConfig?.model ?? getDefaultModel(options.providerType);
32602
+ }
32603
+ options;
32604
+ providerRegistry;
32605
+ toolRegistry;
32606
+ sessionStore;
32607
+ permissionPolicy;
32608
+ eventLog;
32609
+ providerType;
32610
+ model;
32611
+ async initialize() {
32612
+ const providerInjected = Boolean(this.options.provider);
32613
+ const provider = this.options.provider ?? await this.providerRegistry.createProvider(this.providerType, {
32614
+ ...this.options.providerConfig,
32615
+ model: this.getModel()
32616
+ });
32617
+ this.publishToGlobalBridge(provider);
32618
+ this.eventLog.record(providerInjected ? "provider.attached" : "provider.created", {
32619
+ provider: this.providerType,
32620
+ model: this.getModel(),
32621
+ createdByRuntime: !providerInjected
32622
+ });
32623
+ this.eventLog.record("runtime.initialized", { snapshot: this.snapshot() });
32624
+ }
32625
+ getModel() {
32626
+ return this.model;
32627
+ }
32628
+ updateProvider(providerType, model2, provider) {
32629
+ this.providerType = providerType;
32630
+ this.model = model2 ?? getDefaultModel(providerType);
32631
+ this.publishToGlobalBridge(provider);
32632
+ this.eventLog.record("provider.updated", {
32633
+ provider: this.providerType,
32634
+ model: this.model
32635
+ });
32636
+ }
32637
+ publishToGlobalBridge(provider) {
32638
+ if (this.options.publishToGlobalBridge !== true) return;
32639
+ setAgentProvider(provider);
32640
+ setAgentToolRegistry(this.toolRegistry);
32641
+ }
32642
+ snapshot() {
32643
+ const capability = this.providerRegistry.getCapability(this.providerType, this.getModel());
32644
+ const toolNames = this.toolRegistry.getAll().map((tool) => tool.name).sort();
32645
+ return {
32646
+ provider: {
32647
+ type: this.providerType,
32648
+ model: this.getModel(),
32649
+ capability
32650
+ },
32651
+ tools: {
32652
+ count: toolNames.length,
32653
+ names: toolNames
32654
+ },
32655
+ modes: listAgentModes()
32656
+ };
32657
+ }
32658
+ assertToolAllowed(mode, toolName) {
32659
+ const tool = this.toolRegistry.get(toolName);
32660
+ if (!tool) {
32661
+ this.eventLog.record("tool.blocked", {
32662
+ mode,
32663
+ tool: toolName,
32664
+ reason: "Tool not registered."
32665
+ });
32666
+ return false;
32667
+ }
32668
+ const decision = this.permissionPolicy.canExecuteTool(mode, tool);
32669
+ this.eventLog.record(decision.allowed ? "tool.allowed" : "tool.blocked", {
32670
+ mode,
32671
+ tool: toolName,
32672
+ ...decision
32673
+ });
32674
+ return decision.allowed;
32675
+ }
32676
+ };
32677
+ async function createAgentRuntime(options) {
32678
+ const runtime = new AgentRuntime(options);
32679
+ await runtime.initialize();
32680
+ return runtime;
32681
+ }
32682
+
32683
+ // src/runtime/extension-manifests.ts
32684
+ function createMcpToolPolicy(server, tool, risk, allowedModes = ["ask", "plan", "build", "debug", "review", "architect"]) {
32685
+ return {
32686
+ server,
32687
+ tool,
32688
+ risk,
32689
+ requiresConfirmation: risk === "destructive" || risk === "secrets-sensitive",
32690
+ allowedModes
32691
+ };
32692
+ }
32693
+
32694
+ // src/runtime/workflow-registry.ts
32695
+ function cloneWorkflow(workflow) {
32696
+ return {
32697
+ ...workflow,
32698
+ checks: [...workflow.checks],
32699
+ steps: workflow.steps.map((step) => ({
32700
+ ...step,
32701
+ requiredTools: [...step.requiredTools]
32702
+ }))
32703
+ };
32704
+ }
32705
+ var WorkflowCatalog = class {
32706
+ workflows = /* @__PURE__ */ new Map();
32707
+ constructor(workflows = DEFAULT_WORKFLOWS) {
32708
+ for (const workflow of workflows) {
32709
+ this.register(workflow);
32710
+ }
32711
+ }
32712
+ register(workflow) {
32713
+ this.workflows.set(workflow.id, cloneWorkflow(workflow));
32714
+ }
32715
+ get(id) {
32716
+ const workflow = this.workflows.get(id);
32717
+ return workflow ? cloneWorkflow(workflow) : void 0;
32718
+ }
32719
+ list() {
32720
+ return [...this.workflows.values()].map(cloneWorkflow).sort((a, b) => a.id.localeCompare(b.id));
32721
+ }
32722
+ createPlan(workflowId, input, eventLog) {
32723
+ const workflow = this.get(workflowId);
32724
+ if (!workflow) {
32725
+ throw new Error(`Unknown workflow: ${workflowId}`);
32726
+ }
32727
+ const plan = {
32728
+ id: `${workflowId}-${Date.now().toString(36)}`,
32729
+ workflowId,
32730
+ input,
32731
+ status: "planned",
32732
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
32733
+ };
32734
+ eventLog?.record("workflow.planned", {
32735
+ workflowId,
32736
+ planId: plan.id,
32737
+ replayable: workflow.replayable,
32738
+ checks: workflow.checks
32739
+ });
32740
+ return plan;
32741
+ }
32742
+ };
32743
+ var DEFAULT_WORKFLOWS = [
32744
+ {
32745
+ id: "architect-editor-verifier",
32746
+ name: "Architect / Editor / Verifier",
32747
+ description: "Plan read-only, apply approved changes, then verify and summarize risks.",
32748
+ inputSchema: "task: string; approvedPlan?: string",
32749
+ outputKind: "patch",
32750
+ replayable: true,
32751
+ checks: ["pnpm check", "diff summary", "review risks"],
32752
+ steps: [
32753
+ {
32754
+ id: "architect",
32755
+ description: "Inspect context and produce a read-only implementation plan.",
32756
+ requiredTools: ["repo_context", "read_file", "git_diff"],
32757
+ risk: "read-only"
32758
+ },
32759
+ {
32760
+ id: "editor",
32761
+ description: "Apply the approved plan without reinterpreting the objective.",
32762
+ requiredTools: ["read_file", "edit_file", "write_file"],
32763
+ risk: "write"
32764
+ },
32765
+ {
32766
+ id: "verifier",
32767
+ description: "Run checks, review diff, and report residual risk.",
32768
+ requiredTools: ["bash_exec", "git_diff", "review_code"],
32769
+ risk: "destructive"
32770
+ }
32771
+ ]
32772
+ },
32773
+ {
32774
+ id: "provider-diagnosis",
32775
+ name: "Provider Diagnosis",
32776
+ description: "Probe provider/model capabilities, endpoint strategy, credentials, and fallbacks.",
32777
+ inputSchema: "provider?: string; model?: string; live?: boolean",
32778
+ outputKind: "json",
32779
+ replayable: true,
32780
+ checks: ["provider capability matrix", "optional live probe"],
32781
+ steps: [
32782
+ {
32783
+ id: "capability",
32784
+ description: "Resolve catalog metadata and runtime endpoint strategy.",
32785
+ requiredTools: [],
32786
+ risk: "read-only"
32787
+ },
32788
+ {
32789
+ id: "fallbacks",
32790
+ description: "Suggest fallback provider/model choices when unsupported.",
32791
+ requiredTools: [],
32792
+ risk: "read-only"
32793
+ }
32794
+ ]
32795
+ },
32796
+ {
32797
+ id: "review-pr",
32798
+ name: "Review PR",
32799
+ description: "Review a branch or PR read-only and emit severity-ranked findings.",
32800
+ inputSchema: "target: string",
32801
+ outputKind: "markdown",
32802
+ replayable: true,
32803
+ checks: ["git diff", "tests gap review", "security review"],
32804
+ steps: [
32805
+ {
32806
+ id: "collect-diff",
32807
+ description: "Collect PR diff and related context.",
32808
+ requiredTools: ["git_diff", "repo_context"],
32809
+ risk: "read-only"
32810
+ },
32811
+ {
32812
+ id: "findings",
32813
+ description: "Produce prioritized findings with file and line references.",
32814
+ requiredTools: ["read_file", "review_code"],
32815
+ risk: "read-only"
32816
+ }
32817
+ ]
32818
+ },
32819
+ {
32820
+ id: "best-of-n",
32821
+ name: "Best Of N",
32822
+ description: "Run multiple isolated attempts, score them, and select a winning patch.",
32823
+ inputSchema: "task: string; attempts: number",
32824
+ outputKind: "patch",
32825
+ replayable: true,
32826
+ checks: ["worktree isolation", "checks pass", "diff risk score"],
32827
+ steps: [
32828
+ {
32829
+ id: "fanout",
32830
+ description: "Create isolated attempts in temporary worktrees.",
32831
+ requiredTools: ["git_status", "bash_exec"],
32832
+ risk: "destructive"
32833
+ },
32834
+ {
32835
+ id: "score",
32836
+ description: "Run checks and compare quality, cost, latency, and diff risk.",
32837
+ requiredTools: ["bash_exec", "git_diff"],
32838
+ risk: "destructive"
32839
+ },
32840
+ {
32841
+ id: "apply-winner",
32842
+ description: "Apply the winning patch only if conservative checks pass.",
32843
+ requiredTools: ["git_diff", "edit_file"],
32844
+ risk: "write"
32845
+ }
32846
+ ]
32847
+ },
32848
+ {
32849
+ id: "release",
32850
+ name: "Release",
32851
+ description: "Follow the project release skill: changelog, version bump, PR, merge, tag, publish verify.",
32852
+ inputSchema: "bump?: patch|minor|major",
32853
+ outputKind: "release",
32854
+ replayable: true,
32855
+ checks: ["pnpm check", "PR checks", "release.yml", "npm view"],
32856
+ steps: [
32857
+ {
32858
+ id: "preflight",
32859
+ description: "Verify branch, clean tree, GitHub auth, and remote state.",
32860
+ requiredTools: ["git_status", "bash_exec"],
32861
+ risk: "destructive"
32862
+ },
32863
+ {
32864
+ id: "version",
32865
+ description: "Update changelog and package versions using the release skill.",
32866
+ requiredTools: ["read_file", "edit_file", "bash_exec"],
32867
+ risk: "destructive"
32868
+ },
32869
+ {
32870
+ id: "publish",
32871
+ description: "Merge release PR, tag main, and verify release workflow outputs.",
32872
+ requiredTools: ["bash_exec"],
32873
+ risk: "destructive"
32874
+ }
32875
+ ]
32876
+ }
32877
+ ];
32878
+ var WorkflowRegistry = WorkflowCatalog;
32879
+ function createWorkflowCatalog(workflows) {
32880
+ return new WorkflowCatalog(workflows);
32881
+ }
32882
+ function createWorkflowRegistry(workflows) {
32883
+ return createWorkflowCatalog(workflows);
32884
+ }
30913
32885
 
30914
32886
  // src/index.ts
30915
32887
  init_errors();
30916
32888
  init_logger();
30917
32889
  init_proxy();
30918
32890
 
30919
- export { ADRGenerator, AnthropicProvider, ArchitectureGenerator, BacklogGenerator, CICDGenerator, CocoError, CodeGenerator, CodeReviewer, CompleteExecutor, ConfigError, ConvergeExecutor, DiscoveryEngine, DockerGenerator, DocsGenerator, OrchestrateExecutor, OutputExecutor, PhaseError, SessionManager, SpecificationGenerator, TaskError, TaskIterator, ToolRegistry, VERSION, configExists, createADRGenerator, createAnthropicProvider, createArchitectureGenerator, createBacklogGenerator, createCICDGenerator, createCodeGenerator, createCodeReviewer, createCompleteExecutor, createConvergeExecutor, createDefaultConfig, createDiscoveryEngine, createDockerGenerator, createDocsGenerator, createFullToolRegistry, createLogger, createOrchestrateExecutor, createOrchestrator, createOutputExecutor, createProvider, createSessionManager, createSpecificationGenerator, createTaskIterator, createToolRegistry, installProxyDispatcher, loadConfig, registerAllTools, saveConfig };
32891
+ export { ADRGenerator, AgentRuntime, AnthropicProvider, ArchitectureGenerator, BacklogGenerator, CICDGenerator, CocoError, CodeGenerator, CodeReviewer, CompleteExecutor, ConfigError, ConvergeExecutor, DEFAULT_WORKFLOWS, DefaultPermissionPolicy, DiscoveryEngine, DockerGenerator, DocsGenerator, FileEventLog, InMemoryEventLog, OrchestrateExecutor, OutputExecutor, PhaseError, ProviderRegistry, SessionManager, SpecificationGenerator, TaskError, TaskIterator, ToolRegistry, VERSION, WorkflowCatalog, WorkflowRegistry, configExists, createADRGenerator, createAgentRuntime, createAnthropicProvider, createArchitectureGenerator, createBacklogGenerator, createCICDGenerator, createCodeGenerator, createCodeReviewer, createCompleteExecutor, createConvergeExecutor, createDefaultConfig, createDiscoveryEngine, createDockerGenerator, createDocsGenerator, createEventLog, createFileEventLog, createFullToolRegistry, createLogger, createMcpToolPolicy, createOrchestrateExecutor, createOrchestrator, createOutputExecutor, createPermissionPolicy, createProvider, createProviderRegistry, createSessionManager, createSpecificationGenerator, createTaskIterator, createToolRegistry, createWorkflowCatalog, createWorkflowRegistry, installProxyDispatcher, loadConfig, registerAllTools, saveConfig };
30920
32892
  //# sourceMappingURL=index.js.map
30921
32893
  //# sourceMappingURL=index.js.map