@pikaa-ai/pikaa 0.3.27 → 0.3.28

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
@@ -2567,29 +2567,116 @@ var shellTool = createShellTool();
2567
2567
  // src/tools/handlers/file-ops.ts
2568
2568
  import { readdirSync as readdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync8, statSync as statSync3, mkdirSync as mkdirSync6 } from "fs";
2569
2569
  import { resolve as resolve6, dirname as dirname4 } from "path";
2570
+ var DEFAULT_MAX_UNPAGINATED_LINES = 250;
2570
2571
  var readFileTool = {
2571
2572
  name: "read_file",
2572
- description: "Read the full text content of a file.",
2573
+ description: "Read file content with surgical line-range support. Supports start_line and end_line to inspect specific sections of large files without exhausting token context.",
2573
2574
  parameters: {
2574
2575
  type: "object",
2575
2576
  properties: {
2576
- path: { type: "string", description: "Relative or absolute path to the file." }
2577
+ path: {
2578
+ type: "string",
2579
+ description: "Relative or absolute path to the file."
2580
+ },
2581
+ start_line: {
2582
+ type: "number",
2583
+ description: "Optional 1-indexed line number to start reading from (e.g. 120)."
2584
+ },
2585
+ end_line: {
2586
+ type: "number",
2587
+ description: "Optional 1-indexed line number to end reading at, inclusive (e.g. 180)."
2588
+ },
2589
+ offset: {
2590
+ type: "number",
2591
+ description: "Alias for start_line (1-indexed)."
2592
+ },
2593
+ limit: {
2594
+ type: "number",
2595
+ description: "Maximum number of lines to read."
2596
+ },
2597
+ line_numbers: {
2598
+ type: "boolean",
2599
+ description: "Whether to include line number prefixes ('<line>: <content>'). Defaults to true for range reads."
2600
+ }
2577
2601
  },
2578
2602
  required: ["path"]
2579
2603
  },
2580
2604
  async execute(args, ctx) {
2581
- const filePath = resolve6(ctx.cwd, String(args.path || ""));
2605
+ const rawPath = String(args.path || "");
2606
+ const filePath = resolve6(ctx.cwd, rawPath);
2582
2607
  if (!existsSync8(filePath)) {
2583
- return { output: `Error: File not found: '${args.path}'`, isError: true };
2608
+ return { output: `Error: File not found: '${rawPath}'`, isError: true };
2584
2609
  }
2585
2610
  try {
2586
2611
  const content = readFileSync3(filePath, "utf8");
2587
- return { output: content };
2612
+ const lines = content.split(/\r?\n/);
2613
+ const totalLines = lines.length;
2614
+ const hasRange = args.start_line !== undefined || args.end_line !== undefined || args.startLine !== undefined || args.endLine !== undefined || args.offset !== undefined || args.limit !== undefined;
2615
+ if (!hasRange) {
2616
+ if (args.line_numbers === true) {
2617
+ const formatted = lines.map((l, idx) => `${idx + 1}: ${l}`).join(`
2618
+ `);
2619
+ return { output: formatted };
2620
+ }
2621
+ if (totalLines <= DEFAULT_MAX_UNPAGINATED_LINES) {
2622
+ return { output: content };
2623
+ }
2624
+ const truncated = lines.slice(0, DEFAULT_MAX_UNPAGINATED_LINES);
2625
+ const formatted = truncated.map((l, idx) => `${idx + 1}: ${l}`).join(`
2626
+ `);
2627
+ return {
2628
+ output: `[Showing lines 1 to ${DEFAULT_MAX_UNPAGINATED_LINES} of ${totalLines} in '${rawPath}']
2629
+ ${formatted}
2630
+
2631
+ [Truncated: ${totalLines - DEFAULT_MAX_UNPAGINATED_LINES} more lines. Use start_line=${DEFAULT_MAX_UNPAGINATED_LINES + 1} to continue reading.]`
2632
+ };
2633
+ }
2634
+ const startArg = args.start_line ?? args.startLine ?? args.offset;
2635
+ const start = Math.max(1, typeof startArg === "number" ? Math.floor(startArg) : 1);
2636
+ let end;
2637
+ const endArg = args.end_line ?? args.endLine;
2638
+ if (typeof endArg === "number") {
2639
+ end = Math.min(totalLines, Math.floor(endArg));
2640
+ } else if (typeof args.limit === "number") {
2641
+ end = Math.min(totalLines, start + Math.floor(args.limit) - 1);
2642
+ } else {
2643
+ end = Math.min(totalLines, start + DEFAULT_MAX_UNPAGINATED_LINES - 1);
2644
+ }
2645
+ if (start > totalLines) {
2646
+ return {
2647
+ output: `Error: start_line (${start}) exceeds total lines in file (${totalLines}).`,
2648
+ isError: true
2649
+ };
2650
+ }
2651
+ if (end < start) {
2652
+ return {
2653
+ output: `Error: end_line (${end}) cannot be less than start_line (${start}).`,
2654
+ isError: true
2655
+ };
2656
+ }
2657
+ const sliced = lines.slice(start - 1, end);
2658
+ const withNums = args.line_numbers !== false;
2659
+ const rendered = withNums ? sliced.map((l, idx) => `${start + idx}: ${l}`).join(`
2660
+ `) : sliced.join(`
2661
+ `);
2662
+ let notice = `[Showing lines ${start} to ${end} of ${totalLines} in '${rawPath}']
2663
+ ${rendered}`;
2664
+ if (end < totalLines) {
2665
+ notice += `
2666
+
2667
+ [File has ${totalLines} lines. To read further, use start_line=${end + 1}.]`;
2668
+ }
2669
+ return { output: notice };
2588
2670
  } catch (err) {
2589
2671
  return { output: `Failed to read file: ${err instanceof Error ? err.message : String(err)}`, isError: true };
2590
2672
  }
2591
2673
  }
2592
2674
  };
2675
+ var viewFileTool = {
2676
+ ...readFileTool,
2677
+ name: "view_file",
2678
+ description: "View file content with surgical line-range support. Alias for read_file matching Antigravity & Claude Code conventions."
2679
+ };
2593
2680
  var listDirTool = {
2594
2681
  name: "list_dir",
2595
2682
  description: "List contents of a directory with file names and types.",
@@ -3103,7 +3190,13 @@ function createFileSearchTools(engine = new FileSearchEngine) {
3103
3190
  `) };
3104
3191
  }
3105
3192
  };
3106
- return [grepSearchTool, findFilesTool];
3193
+ const findByNameTool = {
3194
+ name: "find_by_name",
3195
+ description: "Search for files and directories across the workspace matching a name or glob pattern. Alias for find_files matching Antigravity & Claude Code conventions.",
3196
+ parameters: findFilesTool.parameters,
3197
+ execute: findFilesTool.execute
3198
+ };
3199
+ return [grepSearchTool, findFilesTool, findByNameTool];
3107
3200
  }
3108
3201
 
3109
3202
  // src/code-mode/tools-proxy.ts
@@ -3622,6 +3715,7 @@ function createDefaultTools(options = {}) {
3622
3715
  router2.register(applyPatchTool);
3623
3716
  router2.register(shellTool);
3624
3717
  router2.register(readFileTool);
3718
+ router2.register(viewFileTool);
3625
3719
  router2.register(writeFileTool);
3626
3720
  router2.register(listDirTool);
3627
3721
  router2.register(requestUserInputTool);
@@ -4268,7 +4362,499 @@ class TurnContext {
4268
4362
  this.abortController.abort(new Error(reason));
4269
4363
  }
4270
4364
  }
4365
+ // src/verification/verifier.ts
4366
+ import { spawnSync } from "child_process";
4367
+ import { existsSync as existsSync14, readFileSync as readFileSync9 } from "fs";
4368
+ import { join as join8 } from "path";
4369
+
4370
+ // src/init/project-analyzer.ts
4371
+ import { existsSync as existsSync13, readFileSync as readFileSync8, readdirSync as readdirSync6 } from "fs";
4372
+ import { join as join7, basename } from "path";
4271
4373
 
4374
+ class ProjectAnalyzer {
4375
+ cwd;
4376
+ constructor(cwd = process.cwd()) {
4377
+ this.cwd = cwd;
4378
+ }
4379
+ analyze() {
4380
+ const readmeInfo = this.extractReadmeMetadata();
4381
+ const projectName = readmeInfo.title || this.detectProjectName();
4382
+ const languages = this.detectLanguages();
4383
+ const packageManager = this.detectPackageManager();
4384
+ const frameworks = [];
4385
+ const infrastructure = [];
4386
+ const commands = {};
4387
+ const architectureNotes = [];
4388
+ const codeConventions = [];
4389
+ let description = readmeInfo.description;
4390
+ const pkgPath = join7(this.cwd, "package.json");
4391
+ if (existsSync13(pkgPath)) {
4392
+ try {
4393
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
4394
+ if (!description && pkg.description)
4395
+ description = pkg.description;
4396
+ const pm = packageManager || "npm";
4397
+ const runPrefix = pm === "bun" || pm === "yarn" || pm === "pnpm" ? `${pm} run` : "npm run";
4398
+ const testPrefix = pm === "bun" ? "bun test" : pm === "pnpm" ? "pnpm test" : pm === "yarn" ? "yarn test" : "npm test";
4399
+ if (pkg.scripts) {
4400
+ if (pkg.scripts.dev)
4401
+ commands.dev = `${runPrefix} dev`;
4402
+ else if (pkg.scripts.start)
4403
+ commands.dev = `${runPrefix} start`;
4404
+ if (pkg.scripts.build)
4405
+ commands.build = `${runPrefix} build`;
4406
+ if (pkg.scripts.test)
4407
+ commands.test = pkg.scripts.test === "bun test" ? "bun test" : testPrefix;
4408
+ if (pkg.scripts.typecheck)
4409
+ commands.typecheck = `${runPrefix} typecheck`;
4410
+ else if (pkg.scripts.check)
4411
+ commands.typecheck = `${runPrefix} check`;
4412
+ if (pkg.scripts.lint)
4413
+ commands.lint = `${runPrefix} lint`;
4414
+ if (pkg.scripts.format)
4415
+ commands.format = `${runPrefix} format`;
4416
+ }
4417
+ const allDeps = {
4418
+ ...pkg.dependencies || {},
4419
+ ...pkg.devDependencies || {}
4420
+ };
4421
+ if (allDeps.next)
4422
+ frameworks.push("Next.js");
4423
+ if (allDeps.react)
4424
+ frameworks.push("React");
4425
+ if (allDeps.vue)
4426
+ frameworks.push("Vue.js");
4427
+ if (allDeps.svelte || allDeps["@sveltejs/kit"])
4428
+ frameworks.push("Svelte");
4429
+ if (allDeps.astro)
4430
+ frameworks.push("Astro");
4431
+ if (allDeps.vite)
4432
+ frameworks.push("Vite");
4433
+ if (allDeps.express)
4434
+ frameworks.push("Express");
4435
+ if (allDeps.hono)
4436
+ frameworks.push("Hono");
4437
+ if (allDeps.fastify)
4438
+ frameworks.push("Fastify");
4439
+ if (allDeps["@nestjs/core"])
4440
+ frameworks.push("NestJS");
4441
+ if (allDeps.tailwindcss)
4442
+ frameworks.push("TailwindCSS");
4443
+ if (allDeps["lucide-react"] || allDeps.lucide)
4444
+ frameworks.push("Lucide Icons");
4445
+ if (allDeps.zustand)
4446
+ frameworks.push("Zustand");
4447
+ if (allDeps["@tanstack/react-query"])
4448
+ frameworks.push("TanStack Query");
4449
+ if (allDeps.oxlint)
4450
+ frameworks.push("Oxlint");
4451
+ if (allDeps.eslint)
4452
+ frameworks.push("ESLint");
4453
+ if (allDeps.vitest)
4454
+ frameworks.push("Vitest");
4455
+ if (allDeps.jest)
4456
+ frameworks.push("Jest");
4457
+ if (allDeps.playwright || allDeps["@playwright/test"])
4458
+ frameworks.push("Playwright");
4459
+ if (pkg.type === "module") {
4460
+ codeConventions.push("Use ES modules (`import/export`), not CommonJS (`require`).");
4461
+ }
4462
+ } catch {}
4463
+ }
4464
+ const tsconfigPath = join7(this.cwd, "tsconfig.json");
4465
+ if (existsSync13(tsconfigPath)) {
4466
+ try {
4467
+ const tsconfig = JSON.parse(readFileSync8(tsconfigPath, "utf8"));
4468
+ if (tsconfig.compilerOptions?.strict) {
4469
+ codeConventions.push("TypeScript strict mode enabled.");
4470
+ }
4471
+ if (!commands.typecheck) {
4472
+ commands.typecheck = "tsc --noEmit";
4473
+ }
4474
+ } catch {}
4475
+ }
4476
+ const cargoPath = join7(this.cwd, "Cargo.toml");
4477
+ if (existsSync13(cargoPath)) {
4478
+ try {
4479
+ commands.dev = commands.dev || "cargo run";
4480
+ commands.build = commands.build || "cargo build";
4481
+ commands.test = commands.test || "cargo test";
4482
+ commands.lint = commands.lint || "cargo clippy";
4483
+ frameworks.push("Rust Cargo");
4484
+ } catch {}
4485
+ }
4486
+ const goModPath = join7(this.cwd, "go.mod");
4487
+ if (existsSync13(goModPath)) {
4488
+ try {
4489
+ commands.dev = commands.dev || "go run .";
4490
+ commands.build = commands.build || "go build ./...";
4491
+ commands.test = commands.test || "go test ./...";
4492
+ commands.lint = commands.lint || "golangci-lint run";
4493
+ frameworks.push("Go Modules");
4494
+ } catch {}
4495
+ }
4496
+ const pyprojectPath = join7(this.cwd, "pyproject.toml");
4497
+ const requirementsPath = join7(this.cwd, "requirements.txt");
4498
+ if (existsSync13(pyprojectPath) || existsSync13(requirementsPath)) {
4499
+ commands.test = commands.test || "pytest";
4500
+ commands.lint = commands.lint || "ruff check .";
4501
+ if (existsSync13(join7(this.cwd, "uv.lock"))) {
4502
+ frameworks.push("uv");
4503
+ commands.test = "uv run pytest";
4504
+ } else if (existsSync13(join7(this.cwd, "poetry.lock"))) {
4505
+ frameworks.push("Poetry");
4506
+ commands.test = "poetry run pytest";
4507
+ }
4508
+ }
4509
+ if (existsSync13(join7(this.cwd, "Dockerfile"))) {
4510
+ infrastructure.push("Docker");
4511
+ const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
4512
+ commands.dockerBuild = `docker build -t ${sanitizedName || "app"} .`;
4513
+ }
4514
+ if (existsSync13(join7(this.cwd, "nginx.conf"))) {
4515
+ infrastructure.push("Nginx");
4516
+ }
4517
+ if (existsSync13(join7(this.cwd, "src/api.ts")) || existsSync13(join7(this.cwd, "src/api"))) {
4518
+ architectureNotes.push("Backend API endpoints and network client logic are centralized in `src/api`.");
4519
+ }
4520
+ if (existsSync13(join7(this.cwd, "src/components"))) {
4521
+ architectureNotes.push("Reusable UI presentation components live in `src/components/`.");
4522
+ }
4523
+ if (existsSync13(join7(this.cwd, "src/types.ts")) || existsSync13(join7(this.cwd, "src/types"))) {
4524
+ architectureNotes.push("Shared TypeScript data models and interfaces are defined in `src/types`.");
4525
+ }
4526
+ if (existsSync13(join7(this.cwd, ".env.example"))) {
4527
+ architectureNotes.push("Environment configuration template is in `.env.example`.");
4528
+ }
4529
+ if (commands.typecheck || commands.lint || commands.test) {
4530
+ const checks = [];
4531
+ if (commands.typecheck)
4532
+ checks.push(`typecheck (\`${commands.typecheck}\`)`);
4533
+ if (commands.lint)
4534
+ checks.push(`lint (\`${commands.lint}\`)`);
4535
+ if (commands.test)
4536
+ checks.push(`tests (\`${commands.test}\`)`);
4537
+ codeConventions.push(`Run ${checks.join(" and ")} before concluding any major code edits.`);
4538
+ }
4539
+ const instructionFiles = ["AGENTS.md", "CLAUDE.md", ".agents.md", "AGENTS.override.md"];
4540
+ let hasExistingInstructions = false;
4541
+ let existingInstructionFile;
4542
+ for (const f of instructionFiles) {
4543
+ if (existsSync13(join7(this.cwd, f))) {
4544
+ hasExistingInstructions = true;
4545
+ existingInstructionFile = f;
4546
+ break;
4547
+ }
4548
+ }
4549
+ return {
4550
+ projectName,
4551
+ description,
4552
+ languages,
4553
+ packageManager,
4554
+ frameworks,
4555
+ infrastructure,
4556
+ commands,
4557
+ architectureNotes,
4558
+ codeConventions,
4559
+ hasExistingInstructions,
4560
+ existingInstructionFile
4561
+ };
4562
+ }
4563
+ generateAgentsMarkdown(analysis) {
4564
+ const lines = [];
4565
+ lines.push(`# ${analysis.projectName}`);
4566
+ lines.push("");
4567
+ if (analysis.description) {
4568
+ lines.push(`> ${analysis.description}`);
4569
+ lines.push("");
4570
+ }
4571
+ lines.push("## Commands");
4572
+ lines.push("");
4573
+ if (Object.keys(analysis.commands).length > 0) {
4574
+ if (analysis.commands.dev)
4575
+ lines.push(`- **Dev Server**: \`${analysis.commands.dev}\``);
4576
+ if (analysis.commands.build)
4577
+ lines.push(`- **Build**: \`${analysis.commands.build}\``);
4578
+ if (analysis.commands.test)
4579
+ lines.push(`- **Test**: \`${analysis.commands.test}\``);
4580
+ if (analysis.commands.typecheck)
4581
+ lines.push(`- **Typecheck**: \`${analysis.commands.typecheck}\``);
4582
+ if (analysis.commands.lint)
4583
+ lines.push(`- **Lint**: \`${analysis.commands.lint}\``);
4584
+ if (analysis.commands.format)
4585
+ lines.push(`- **Format**: \`${analysis.commands.format}\``);
4586
+ if (analysis.commands.dockerBuild)
4587
+ lines.push(`- **Docker Build**: \`${analysis.commands.dockerBuild}\``);
4588
+ } else {
4589
+ lines.push("- *No standard build/test commands detected.*");
4590
+ }
4591
+ lines.push("");
4592
+ lines.push("## Architecture & Stack");
4593
+ lines.push("");
4594
+ const stackItems = [];
4595
+ if (analysis.languages.length > 0)
4596
+ stackItems.push(analysis.languages.join(", "));
4597
+ if (analysis.frameworks.length > 0)
4598
+ stackItems.push(analysis.frameworks.join(", "));
4599
+ if (analysis.infrastructure.length > 0)
4600
+ stackItems.push(analysis.infrastructure.join(", "));
4601
+ if (stackItems.length > 0) {
4602
+ lines.push(`- **Core Stack**: ${stackItems.join(" \u2022 ")}`);
4603
+ }
4604
+ for (const note of analysis.architectureNotes) {
4605
+ lines.push(`- ${note}`);
4606
+ }
4607
+ lines.push("");
4608
+ lines.push("## Workflow & Code Guidelines");
4609
+ lines.push("");
4610
+ if (analysis.codeConventions.length > 0) {
4611
+ for (const conv of analysis.codeConventions) {
4612
+ lines.push(`- ${conv}`);
4613
+ }
4614
+ }
4615
+ lines.push("- Prefer targeted edits over whole-file rewrites.");
4616
+ lines.push("- When fixing errors, address the root cause rather than suppressing compiler warnings.");
4617
+ lines.push("");
4618
+ return lines.join(`
4619
+ `);
4620
+ }
4621
+ extractReadmeMetadata() {
4622
+ const readmeFiles = ["README.md", "readme.md", "README.MD"];
4623
+ for (const file of readmeFiles) {
4624
+ const fullPath = join7(this.cwd, file);
4625
+ if (existsSync13(fullPath)) {
4626
+ try {
4627
+ const content = readFileSync8(fullPath, "utf8");
4628
+ const lines = content.split(`
4629
+ `);
4630
+ let title;
4631
+ let description;
4632
+ for (const line of lines) {
4633
+ const trimmed = line.trim();
4634
+ if (!title && trimmed.startsWith("# ")) {
4635
+ title = trimmed.replace(/^#\s+/, "").trim();
4636
+ continue;
4637
+ }
4638
+ if (title && !description && trimmed.length > 0 && !trimmed.startsWith("#") && !trimmed.startsWith("```") && !trimmed.startsWith("[")) {
4639
+ description = trimmed;
4640
+ break;
4641
+ }
4642
+ }
4643
+ return { title, description };
4644
+ } catch {}
4645
+ }
4646
+ }
4647
+ return {};
4648
+ }
4649
+ detectProjectName() {
4650
+ const pkgPath = join7(this.cwd, "package.json");
4651
+ if (existsSync13(pkgPath)) {
4652
+ try {
4653
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
4654
+ if (pkg.name && pkg.name !== "frontend" && pkg.name !== "backend" && pkg.name !== "app") {
4655
+ return pkg.name.startsWith("@") ? pkg.name.split("/")[1] || pkg.name : pkg.name;
4656
+ }
4657
+ } catch {}
4658
+ }
4659
+ const cargoPath = join7(this.cwd, "Cargo.toml");
4660
+ if (existsSync13(cargoPath)) {
4661
+ try {
4662
+ const match = readFileSync8(cargoPath, "utf8").match(/name\s*=\s*"([^"]+)"/);
4663
+ if (match?.[1])
4664
+ return match[1];
4665
+ } catch {}
4666
+ }
4667
+ const goModPath = join7(this.cwd, "go.mod");
4668
+ if (existsSync13(goModPath)) {
4669
+ try {
4670
+ const match = readFileSync8(goModPath, "utf8").match(/module\s+([^\s]+)/);
4671
+ if (match?.[1])
4672
+ return basename(match[1]);
4673
+ } catch {}
4674
+ }
4675
+ return basename(this.cwd);
4676
+ }
4677
+ detectLanguages() {
4678
+ const langs = new Set;
4679
+ if (existsSync13(join7(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
4680
+ langs.add("TypeScript");
4681
+ }
4682
+ if (existsSync13(join7(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
4683
+ langs.add("JavaScript");
4684
+ }
4685
+ if (existsSync13(join7(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
4686
+ langs.add("Rust");
4687
+ }
4688
+ if (existsSync13(join7(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
4689
+ langs.add("Go");
4690
+ }
4691
+ if (existsSync13(join7(this.cwd, "pyproject.toml")) || existsSync13(join7(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
4692
+ langs.add("Python");
4693
+ }
4694
+ if (existsSync13(join7(this.cwd, "pom.xml")) || existsSync13(join7(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
4695
+ langs.add("Java");
4696
+ }
4697
+ if (existsSync13(join7(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
4698
+ langs.add("C/C++");
4699
+ }
4700
+ return Array.from(langs);
4701
+ }
4702
+ detectPackageManager() {
4703
+ if (existsSync13(join7(this.cwd, "bun.lockb")) || existsSync13(join7(this.cwd, "bun.lock")))
4704
+ return "bun";
4705
+ if (existsSync13(join7(this.cwd, "pnpm-lock.yaml")))
4706
+ return "pnpm";
4707
+ if (existsSync13(join7(this.cwd, "yarn.lock")))
4708
+ return "yarn";
4709
+ if (existsSync13(join7(this.cwd, "package-lock.json")))
4710
+ return "npm";
4711
+ if (existsSync13(join7(this.cwd, "Cargo.lock")) || existsSync13(join7(this.cwd, "Cargo.toml")))
4712
+ return "cargo";
4713
+ if (existsSync13(join7(this.cwd, "uv.lock")))
4714
+ return "uv";
4715
+ if (existsSync13(join7(this.cwd, "poetry.lock")))
4716
+ return "poetry";
4717
+ if (existsSync13(join7(this.cwd, "go.sum")) || existsSync13(join7(this.cwd, "go.mod")))
4718
+ return "go";
4719
+ if (existsSync13(join7(this.cwd, "package.json")))
4720
+ return "npm";
4721
+ return;
4722
+ }
4723
+ hasFileWithExtension(...exts) {
4724
+ try {
4725
+ const entries = readdirSync6(this.cwd);
4726
+ return entries.some((e) => exts.some((ext) => e.endsWith(ext)));
4727
+ } catch {
4728
+ return false;
4729
+ }
4730
+ }
4731
+ }
4732
+
4733
+ // src/verification/verifier.ts
4734
+ class AutoVerifier {
4735
+ cwd;
4736
+ customCommand;
4737
+ timeoutMs;
4738
+ constructor(options) {
4739
+ this.cwd = options.cwd;
4740
+ this.customCommand = options.customCommand;
4741
+ this.timeoutMs = options.timeoutMs ?? 30000;
4742
+ }
4743
+ resolveVerificationCommand() {
4744
+ if (this.customCommand && this.customCommand.trim()) {
4745
+ return this.customCommand.trim();
4746
+ }
4747
+ try {
4748
+ const analyzer = new ProjectAnalyzer(this.cwd);
4749
+ const analysis = analyzer.analyze();
4750
+ if (analysis.commands.typecheck) {
4751
+ return analysis.commands.typecheck;
4752
+ }
4753
+ if (analysis.commands.lint) {
4754
+ return analysis.commands.lint;
4755
+ }
4756
+ if (analysis.commands.test) {
4757
+ return analysis.commands.test;
4758
+ }
4759
+ } catch {}
4760
+ const pkgPath = join8(this.cwd, "package.json");
4761
+ if (existsSync14(pkgPath)) {
4762
+ try {
4763
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf8"));
4764
+ if (pkg.scripts) {
4765
+ if (pkg.scripts.typecheck)
4766
+ return "npm run typecheck";
4767
+ if (pkg.scripts.check)
4768
+ return "npm run check";
4769
+ if (pkg.scripts.test)
4770
+ return "npm test";
4771
+ }
4772
+ } catch {}
4773
+ }
4774
+ if (existsSync14(join8(this.cwd, "tsconfig.json"))) {
4775
+ return "npx tsc --noEmit";
4776
+ }
4777
+ if (existsSync14(join8(this.cwd, "Cargo.toml"))) {
4778
+ return "cargo check";
4779
+ }
4780
+ if (existsSync14(join8(this.cwd, "go.mod"))) {
4781
+ return "go vet ./...";
4782
+ }
4783
+ if (existsSync14(join8(this.cwd, "pyproject.toml")) || existsSync14(join8(this.cwd, "setup.py"))) {
4784
+ if (existsSync14(join8(this.cwd, "mypy.ini")) || existsSync14(join8(this.cwd, ".mypy.ini"))) {
4785
+ return "mypy .";
4786
+ }
4787
+ }
4788
+ return null;
4789
+ }
4790
+ verify(modifiedFiles = []) {
4791
+ const command = this.resolveVerificationCommand();
4792
+ const startTime = performance.now();
4793
+ if (!command) {
4794
+ return {
4795
+ command: "none",
4796
+ success: true,
4797
+ exitCode: 0,
4798
+ output: "No automated verification command configured or detected for this workspace.",
4799
+ durationMs: 0,
4800
+ reason: "NO_VERIFIER_DETECTED"
4801
+ };
4802
+ }
4803
+ try {
4804
+ const isWindows = process.platform === "win32";
4805
+ const proc = spawnSync(command, {
4806
+ cwd: this.cwd,
4807
+ shell: true,
4808
+ encoding: "utf8",
4809
+ timeout: this.timeoutMs,
4810
+ maxBuffer: 10 * 1024 * 1024,
4811
+ env: {
4812
+ ...process.env,
4813
+ CI: "true",
4814
+ FORCE_COLOR: "0"
4815
+ }
4816
+ });
4817
+ const durationMs = Math.round(performance.now() - startTime);
4818
+ const stdout = proc.stdout ? String(proc.stdout) : "";
4819
+ const stderr = proc.stderr ? String(proc.stderr) : "";
4820
+ let combined = (stdout + `
4821
+ ` + stderr).trim();
4822
+ if (combined.length > 3000) {
4823
+ const lines = combined.split(`
4824
+ `);
4825
+ if (lines.length > 60) {
4826
+ const head = lines.slice(0, 30).join(`
4827
+ `);
4828
+ const tail = lines.slice(-25).join(`
4829
+ `);
4830
+ combined = `${head}
4831
+
4832
+ ... [${lines.length - 55} lines truncated for context efficiency] ...
4833
+
4834
+ ${tail}`;
4835
+ }
4836
+ }
4837
+ const exitCode = proc.status ?? (proc.error ? 1 : 0);
4838
+ const success = exitCode === 0;
4839
+ return {
4840
+ command,
4841
+ success,
4842
+ exitCode,
4843
+ output: combined || (success ? "Verification succeeded cleanly." : "Command failed with empty output."),
4844
+ durationMs
4845
+ };
4846
+ } catch (err) {
4847
+ const durationMs = Math.round(performance.now() - startTime);
4848
+ return {
4849
+ command,
4850
+ success: false,
4851
+ exitCode: 1,
4852
+ output: `Verification execution error: ${err.message || String(err)}`,
4853
+ durationMs
4854
+ };
4855
+ }
4856
+ }
4857
+ }
4272
4858
  // src/session/turn.ts
4273
4859
  async function runTurn(session, turnContext, input) {
4274
4860
  const { turnId, signal } = turnContext;
@@ -4319,6 +4905,9 @@ async function runTurn(session, turnContext, input) {
4319
4905
  let accumulatedOutputTokens = 0;
4320
4906
  let accumulatedCachedTokens = 0;
4321
4907
  const clientSession = session.modelClient.newSession();
4908
+ const modifiedFiles = new Set;
4909
+ let selfHealingAttempts = 0;
4910
+ let hasRunVerification = false;
4322
4911
  try {
4323
4912
  while (iteration < turnContext.maxIterations) {
4324
4913
  if (signal.aborted) {
@@ -4453,6 +5042,15 @@ async function runTurn(session, turnContext, input) {
4453
5042
  isError: toolResult.isError,
4454
5043
  createdAt: Date.now()
4455
5044
  };
5045
+ if (!toolResult.isError) {
5046
+ if (toolCall.name === "apply_patch" || toolCall.name === "write_file") {
5047
+ const p = String(toolCall.arguments?.path || "");
5048
+ if (p) {
5049
+ modifiedFiles.add(p);
5050
+ hasRunVerification = false;
5051
+ }
5052
+ }
5053
+ }
4456
5054
  session.addHistoryItem(functionOutputItem);
4457
5055
  session.emitEvent({
4458
5056
  type: "ToolCallFinished",
@@ -4469,6 +5067,70 @@ async function runTurn(session, turnContext, input) {
4469
5067
  }
4470
5068
  continue;
4471
5069
  }
5070
+ if (session.autoVerification && modifiedFiles.size > 0 && !hasRunVerification && iteration < turnContext.maxIterations) {
5071
+ const verifier = new AutoVerifier({
5072
+ cwd,
5073
+ customCommand: session.autoVerificationCommand
5074
+ });
5075
+ const command = verifier.resolveVerificationCommand();
5076
+ if (command) {
5077
+ session.emitEvent({
5078
+ type: "VerificationStarted",
5079
+ turnId,
5080
+ command,
5081
+ modifiedFiles: Array.from(modifiedFiles)
5082
+ });
5083
+ const vResult = verifier.verify(Array.from(modifiedFiles));
5084
+ session.emitEvent({
5085
+ type: "VerificationCompleted",
5086
+ turnId,
5087
+ command: vResult.command,
5088
+ success: vResult.success,
5089
+ output: vResult.output,
5090
+ durationMs: vResult.durationMs
5091
+ });
5092
+ if (!vResult.success) {
5093
+ if (selfHealingAttempts < session.maxSelfHealingAttempts) {
5094
+ selfHealingAttempts++;
5095
+ session.emitEvent({
5096
+ type: "SelfHealingStarted",
5097
+ turnId,
5098
+ attempt: selfHealingAttempts,
5099
+ maxAttempts: session.maxSelfHealingAttempts,
5100
+ command: vResult.command,
5101
+ error: vResult.output
5102
+ });
5103
+ const feedbackMsg = `[Automated Self-Verification Failure]
5104
+ Verification command '${vResult.command}' failed with exit code ${vResult.exitCode}.
5105
+
5106
+ Error trace / compiler output:
5107
+ ${vResult.output}
5108
+
5109
+ Modified file(s) in this turn: ${Array.from(modifiedFiles).join(", ")}
5110
+
5111
+ Self-Healing Directive (Attempt ${selfHealingAttempts} of ${session.maxSelfHealingAttempts}):
5112
+ 1. Review the error trace above carefully and locate the exact root cause.
5113
+ 2. Formulate and apply the necessary surgical fix using 'apply_patch' or 'write_file'.
5114
+ 3. Do NOT conclude the turn or report to the user until this error is resolved and verification passes cleanly.`;
5115
+ session.addHistoryItem({
5116
+ id: `msg_heal_${Date.now()}`,
5117
+ type: "user_message",
5118
+ content: feedbackMsg,
5119
+ createdAt: Date.now()
5120
+ });
5121
+ continue;
5122
+ } else {
5123
+ session.emitEvent({
5124
+ type: "Warning",
5125
+ message: `Auto-verification failed after ${selfHealingAttempts} self-healing attempts for command: ${vResult.command}`
5126
+ });
5127
+ hasRunVerification = true;
5128
+ }
5129
+ } else {
5130
+ hasRunVerification = true;
5131
+ }
5132
+ }
5133
+ }
4472
5134
  if (!currentAgentText.trim() && toolCallRequests.length === 0) {
4473
5135
  if (iteration === 1 && iteration < turnContext.maxIterations) {
4474
5136
  session.addHistoryItem({
@@ -4594,6 +5256,9 @@ class Session {
4594
5256
  mcpManager;
4595
5257
  execPolicy;
4596
5258
  collaborationMode = "default";
5259
+ autoVerification;
5260
+ autoVerificationCommand;
5261
+ maxSelfHealingAttempts;
4597
5262
  get permissionMode() {
4598
5263
  return this.execPolicy.getMode();
4599
5264
  }
@@ -4626,6 +5291,9 @@ class Session {
4626
5291
  this.mcpManager = options.mcpManager;
4627
5292
  this.execPolicy = options.execPolicy || new ExecPolicy;
4628
5293
  this.collaborationMode = options.collaborationMode || "default";
5294
+ this.autoVerification = options.autoVerification ?? (process.env.PIKAA_AUTO_VERIFY !== "0" && process.env.PIKAA_AUTO_VERIFY !== "false");
5295
+ this.autoVerificationCommand = options.autoVerificationCommand;
5296
+ this.maxSelfHealingAttempts = options.maxSelfHealingAttempts ?? 3;
4629
5297
  this.history = options.initialHistory ? [...options.initialHistory] : [];
4630
5298
  if (options.onEvent) {
4631
5299
  this.eventListeners.push(options.onEvent);
@@ -4873,7 +5541,7 @@ class ThreadManager {
4873
5541
  }
4874
5542
  }
4875
5543
  // src/mcp/process-killer.ts
4876
- import { spawnSync } from "child_process";
5544
+ import { spawnSync as spawnSync2 } from "child_process";
4877
5545
 
4878
5546
  class GlobalProcessRegistry {
4879
5547
  static trackedProcesses = new Map;
@@ -4883,7 +5551,7 @@ class GlobalProcessRegistry {
4883
5551
  return;
4884
5552
  if (process.platform === "win32") {
4885
5553
  try {
4886
- spawnSync("taskkill", ["/pid", String(pid), "/T", "/F"], {
5554
+ spawnSync2("taskkill", ["/pid", String(pid), "/T", "/F"], {
4887
5555
  stdio: "ignore",
4888
5556
  windowsHide: true
4889
5557
  });
@@ -5450,8 +6118,8 @@ class McpClient {
5450
6118
  }
5451
6119
  }
5452
6120
  // src/mcp/manager.ts
5453
- import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7 } from "fs";
5454
- import { resolve as resolve12, dirname as dirname6, join as join7 } from "path";
6121
+ import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7 } from "fs";
6122
+ import { resolve as resolve12, dirname as dirname6, join as join9 } from "path";
5455
6123
  class McpManager {
5456
6124
  clients = new Map;
5457
6125
  serverConfigs = new Map;
@@ -5486,11 +6154,11 @@ class McpManager {
5486
6154
  }
5487
6155
  async loadConfigFile(filePath) {
5488
6156
  const fullPath = resolve12(filePath);
5489
- if (!existsSync13(fullPath))
6157
+ if (!existsSync15(fullPath))
5490
6158
  return;
5491
6159
  this.loadedConfigFiles.add(fullPath);
5492
6160
  try {
5493
- const content = readFileSync8(fullPath, "utf8");
6161
+ const content = readFileSync10(fullPath, "utf8");
5494
6162
  const parsed = JSON.parse(content);
5495
6163
  if (parsed.mcpServers) {
5496
6164
  await this.loadConfig(parsed);
@@ -5741,13 +6409,13 @@ class McpManager {
5741
6409
  saveServerToConfigFile(filePath, name, config) {
5742
6410
  const fullPath = resolve12(filePath);
5743
6411
  const dir = dirname6(fullPath);
5744
- if (!existsSync13(dir)) {
6412
+ if (!existsSync15(dir)) {
5745
6413
  mkdirSync7(dir, { recursive: true });
5746
6414
  }
5747
6415
  let existing = { mcpServers: {} };
5748
- if (existsSync13(fullPath)) {
6416
+ if (existsSync15(fullPath)) {
5749
6417
  try {
5750
- const content = readFileSync8(fullPath, "utf8");
6418
+ const content = readFileSync10(fullPath, "utf8");
5751
6419
  existing = JSON.parse(content);
5752
6420
  if (!existing.mcpServers)
5753
6421
  existing.mcpServers = {};
@@ -5760,10 +6428,10 @@ class McpManager {
5760
6428
  }
5761
6429
  removeServerFromConfigFile(filePath, name) {
5762
6430
  const fullPath = resolve12(filePath);
5763
- if (!existsSync13(fullPath))
6431
+ if (!existsSync15(fullPath))
5764
6432
  return false;
5765
6433
  try {
5766
- const content = readFileSync8(fullPath, "utf8");
6434
+ const content = readFileSync10(fullPath, "utf8");
5767
6435
  const existing = JSON.parse(content);
5768
6436
  if (existing.mcpServers && existing.mcpServers[name]) {
5769
6437
  delete existing.mcpServers[name];
@@ -5790,11 +6458,11 @@ class McpManager {
5790
6458
  }
5791
6459
  }
5792
6460
  getDefaultConfigFile(cwd = process.cwd()) {
5793
- const workspaceConfig = join7(cwd, ".mcp.json");
5794
- if (existsSync13(workspaceConfig))
6461
+ const workspaceConfig = join9(cwd, ".mcp.json");
6462
+ if (existsSync15(workspaceConfig))
5795
6463
  return workspaceConfig;
5796
- const altConfig = join7(cwd, "mcp_config.json");
5797
- if (existsSync13(altConfig))
6464
+ const altConfig = join9(cwd, "mcp_config.json");
6465
+ if (existsSync15(altConfig))
5798
6466
  return altConfig;
5799
6467
  return workspaceConfig;
5800
6468
  }
@@ -5816,8 +6484,8 @@ class McpManager {
5816
6484
  import { resolve as resolve14 } from "path";
5817
6485
 
5818
6486
  // src/mcp/servers/chrome-devtools/launcher.ts
5819
- import { existsSync as existsSync14, mkdirSync as mkdirSync8, rmSync as rmSync2 } from "fs";
5820
- import { join as join8 } from "path";
6487
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, rmSync as rmSync2 } from "fs";
6488
+ import { join as join10 } from "path";
5821
6489
  import { tmpdir as tmpdir2 } from "os";
5822
6490
  class BrowserLauncher {
5823
6491
  proc = null;
@@ -5825,21 +6493,21 @@ class BrowserLauncher {
5825
6493
  wsDebuggerUrl = null;
5826
6494
  port = 0;
5827
6495
  static findBrowserExecutable() {
5828
- if (process.env.CHROME_PATH && existsSync14(process.env.CHROME_PATH)) {
6496
+ if (process.env.CHROME_PATH && existsSync16(process.env.CHROME_PATH)) {
5829
6497
  return process.env.CHROME_PATH;
5830
6498
  }
5831
6499
  if (process.platform === "win32") {
5832
6500
  const candidates = [
5833
6501
  "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
5834
6502
  "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
5835
- join8(process.env.LOCALAPPDATA || "", "Google\\Chrome\\Application\\chrome.exe"),
6503
+ join10(process.env.LOCALAPPDATA || "", "Google\\Chrome\\Application\\chrome.exe"),
5836
6504
  "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
5837
6505
  "C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
5838
6506
  "C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe",
5839
- join8(process.env.LOCALAPPDATA || "", "BraveSoftware\\Brave-Browser\\Application\\brave.exe")
6507
+ join10(process.env.LOCALAPPDATA || "", "BraveSoftware\\Brave-Browser\\Application\\brave.exe")
5840
6508
  ];
5841
6509
  for (const path of candidates) {
5842
- if (path && existsSync14(path))
6510
+ if (path && existsSync16(path))
5843
6511
  return path;
5844
6512
  }
5845
6513
  } else if (process.platform === "darwin") {
@@ -5850,7 +6518,7 @@ class BrowserLauncher {
5850
6518
  "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
5851
6519
  ];
5852
6520
  for (const path of candidates) {
5853
- if (existsSync14(path))
6521
+ if (existsSync16(path))
5854
6522
  return path;
5855
6523
  }
5856
6524
  } else {
@@ -5863,7 +6531,7 @@ class BrowserLauncher {
5863
6531
  "/usr/bin/microsoft-edge"
5864
6532
  ];
5865
6533
  for (const path of candidates) {
5866
- if (existsSync14(path))
6534
+ if (existsSync16(path))
5867
6535
  return path;
5868
6536
  }
5869
6537
  }
@@ -5875,7 +6543,7 @@ class BrowserLauncher {
5875
6543
  throw new Error("No supported browser (Google Chrome, Chromium, MS Edge, Brave) found on this machine. Please install Chrome or specify CHROME_PATH.");
5876
6544
  }
5877
6545
  this.port = options.port || 9200 + Math.floor(Math.random() * 500);
5878
- this.tempUserDataDir = options.userDataDir || join8(tmpdir2(), `groupy_chrome_${Date.now()}_${Math.random().toString(36).slice(2)}`);
6546
+ this.tempUserDataDir = options.userDataDir || join10(tmpdir2(), `groupy_chrome_${Date.now()}_${Math.random().toString(36).slice(2)}`);
5879
6547
  mkdirSync8(this.tempUserDataDir, { recursive: true });
5880
6548
  const isHeadless = options.headless ?? true;
5881
6549
  const launchArgs = [
@@ -5953,7 +6621,7 @@ class BrowserLauncher {
5953
6621
  }
5954
6622
  this.proc = null;
5955
6623
  }
5956
- if (this.tempUserDataDir && existsSync14(this.tempUserDataDir)) {
6624
+ if (this.tempUserDataDir && existsSync16(this.tempUserDataDir)) {
5957
6625
  try {
5958
6626
  rmSync2(this.tempUserDataDir, { recursive: true, force: true });
5959
6627
  } catch {}
@@ -7793,7 +8461,7 @@ import { resolve as resolve17 } from "path";
7793
8461
  // src/mcp/servers/sqlite/db-engine.ts
7794
8462
  import { Database as Database2 } from "bun:sqlite";
7795
8463
  import { resolve as resolve16, isAbsolute } from "path";
7796
- import { readdirSync as readdirSync6 } from "fs";
8464
+ import { readdirSync as readdirSync7 } from "fs";
7797
8465
 
7798
8466
  class SqliteEngine {
7799
8467
  connections = new Map;
@@ -7823,7 +8491,7 @@ class SqliteEngine {
7823
8491
  }
7824
8492
  autoDiscoverDatabase() {
7825
8493
  try {
7826
- const files = readdirSync6(process.cwd());
8494
+ const files = readdirSync7(process.cwd());
7827
8495
  const dbFile = files.find((f) => f.endsWith(".sqlite") || f.endsWith(".sqlite3") || f.endsWith(".db"));
7828
8496
  return dbFile ? resolve16(process.cwd(), dbFile) : null;
7829
8497
  } catch {
@@ -8327,8 +8995,8 @@ function verifyTaskAction(assertion, payload) {
8327
8995
  }
8328
8996
  }
8329
8997
  // src/agents/roles.ts
8330
- import { existsSync as existsSync16, readdirSync as readdirSync7, readFileSync as readFileSync9 } from "fs";
8331
- import { resolve as resolve18, join as join9 } from "path";
8998
+ import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync11 } from "fs";
8999
+ import { resolve as resolve18, join as join11 } from "path";
8332
9000
 
8333
9001
  class AgentRoleRegistry {
8334
9002
  roles = new Map;
@@ -8413,13 +9081,13 @@ class AgentRoleRegistry {
8413
9081
  }
8414
9082
  loadRolesFromDir(dirPath) {
8415
9083
  const fullPath = resolve18(dirPath);
8416
- if (!existsSync16(fullPath))
9084
+ if (!existsSync18(fullPath))
8417
9085
  return;
8418
- const entries = readdirSync7(fullPath);
9086
+ const entries = readdirSync8(fullPath);
8419
9087
  for (const entry of entries) {
8420
9088
  if (entry.endsWith(".json")) {
8421
9089
  try {
8422
- const content = readFileSync9(join9(fullPath, entry), "utf8");
9090
+ const content = readFileSync11(join11(fullPath, entry), "utf8");
8423
9091
  const parsed = JSON.parse(content);
8424
9092
  if (parsed.name && parsed.systemPrompt) {
8425
9093
  this.registerRole(parsed);
@@ -8449,7 +9117,7 @@ class AgentRoleRegistry {
8449
9117
  // src/agents/graph-store.ts
8450
9118
  import { Database as Database3 } from "bun:sqlite";
8451
9119
  import { resolve as resolve19 } from "path";
8452
- import { existsSync as existsSync17, mkdirSync as mkdirSync10 } from "fs";
9120
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10 } from "fs";
8453
9121
  class AgentGraphStore {
8454
9122
  db;
8455
9123
  constructor(dbPathOrDb) {
@@ -8459,7 +9127,7 @@ class AgentGraphStore {
8459
9127
  const dbPath = dbPathOrDb || getAgentGraphDbPath();
8460
9128
  if (dbPath !== ":memory:") {
8461
9129
  const dir = resolve19(dbPath, "..");
8462
- if (!existsSync17(dir)) {
9130
+ if (!existsSync19(dir)) {
8463
9131
  mkdirSync10(dir, { recursive: true });
8464
9132
  }
8465
9133
  }
@@ -8882,7 +9550,7 @@ function registerMultiAgentTools(router, spawner) {
8882
9550
  }
8883
9551
  // src/storage/sqlite-store.ts
8884
9552
  import { Database as Database4 } from "bun:sqlite";
8885
- import { existsSync as existsSync18, mkdirSync as mkdirSync11 } from "fs";
9553
+ import { existsSync as existsSync20, mkdirSync as mkdirSync11 } from "fs";
8886
9554
  import { dirname as dirname8 } from "path";
8887
9555
  class SqliteThreadStore {
8888
9556
  db;
@@ -8890,7 +9558,7 @@ class SqliteThreadStore {
8890
9558
  const effectivePath = dbPath || this.getDefaultDbPath();
8891
9559
  if (effectivePath !== ":memory:") {
8892
9560
  const dir = dirname8(effectivePath);
8893
- if (!existsSync18(dir)) {
9561
+ if (!existsSync20(dir)) {
8894
9562
  mkdirSync11(dir, { recursive: true });
8895
9563
  }
8896
9564
  }
@@ -9144,8 +9812,8 @@ class SessionPersistenceManager {
9144
9812
  }
9145
9813
  }
9146
9814
  // src/skills/loader.ts
9147
- import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync10 } from "fs";
9148
- import { resolve as resolve20, join as join10 } from "path";
9815
+ import { existsSync as existsSync21, readdirSync as readdirSync9, readFileSync as readFileSync12 } from "fs";
9816
+ import { resolve as resolve20, join as join12 } from "path";
9149
9817
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/skills";
9150
9818
 
9151
9819
  class SkillsLoader {
@@ -9215,7 +9883,7 @@ class SkillsLoader {
9215
9883
  resolve20(cwd, "skills")
9216
9884
  ];
9217
9885
  for (const cand of candidates) {
9218
- if (existsSync19(cand) && !roots.includes(cand)) {
9886
+ if (existsSync21(cand) && !roots.includes(cand)) {
9219
9887
  roots.push(cand);
9220
9888
  }
9221
9889
  }
@@ -9224,7 +9892,7 @@ class SkillsLoader {
9224
9892
  roots.push(getGlobalSkillsDir());
9225
9893
  }
9226
9894
  roots.push(...this.customRoots.map((r) => resolve20(r)));
9227
- return roots.filter((r) => existsSync19(r));
9895
+ return roots.filter((r) => existsSync21(r));
9228
9896
  }
9229
9897
  discoverSkills(cwd, options) {
9230
9898
  return this.listSkills(cwd, options);
@@ -9240,12 +9908,12 @@ class SkillsLoader {
9240
9908
  const discovered = new Map;
9241
9909
  for (const root of roots) {
9242
9910
  try {
9243
- const entries = readdirSync8(root, { withFileTypes: true });
9911
+ const entries = readdirSync9(root, { withFileTypes: true });
9244
9912
  for (const entry of entries) {
9245
9913
  if (entry.isDirectory()) {
9246
- const skillDir = join10(root, entry.name);
9247
- const skillFilePath = join10(skillDir, "SKILL.md");
9248
- if (existsSync19(skillFilePath)) {
9914
+ const skillDir = join12(root, entry.name);
9915
+ const skillFilePath = join12(skillDir, "SKILL.md");
9916
+ if (existsSync21(skillFilePath)) {
9249
9917
  const meta = this.parseSkillFrontmatter(skillFilePath, entry.name, root, cwd);
9250
9918
  if (meta && !discovered.has(meta.name)) {
9251
9919
  meta.enabled = !this.isSkillDisabled(meta.name);
@@ -9274,7 +9942,7 @@ class SkillsLoader {
9274
9942
  if (!meta)
9275
9943
  return null;
9276
9944
  try {
9277
- const raw = readFileSync10(meta.path, "utf8");
9945
+ const raw = readFileSync12(meta.path, "utf8");
9278
9946
  const { body } = this.extractFrontmatterAndBody(raw);
9279
9947
  return {
9280
9948
  ...meta,
@@ -9286,7 +9954,7 @@ class SkillsLoader {
9286
9954
  }
9287
9955
  parseSkillFrontmatter(filePath, dirName, root, cwd) {
9288
9956
  try {
9289
- const raw = readFileSync10(filePath, "utf8");
9957
+ const raw = readFileSync12(filePath, "utf8");
9290
9958
  const { attributes } = this.extractFrontmatterAndBody(raw);
9291
9959
  let scope = "global";
9292
9960
  const normPath = filePath.toLowerCase().replace(/\\/g, "/");
@@ -9367,8 +10035,8 @@ When tackling complex specialized tasks that match any of these skills, autonomo
9367
10035
  }
9368
10036
  }
9369
10037
  // src/memories/store.ts
9370
- import { existsSync as existsSync20, readFileSync as readFileSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync12, readdirSync as readdirSync9 } from "fs";
9371
- import { resolve as resolve21, join as join11, basename, dirname as dirname9 } from "path";
10038
+ import { existsSync as existsSync22, readFileSync as readFileSync13, writeFileSync as writeFileSync6, mkdirSync as mkdirSync12, readdirSync as readdirSync10 } from "fs";
10039
+ import { resolve as resolve21, join as join13, basename as basename2, dirname as dirname9 } from "path";
9372
10040
  import { createHash } from "crypto";
9373
10041
  class MemoryStore {
9374
10042
  globalPath;
@@ -9380,7 +10048,7 @@ class MemoryStore {
9380
10048
  findProjectRoot(cwd) {
9381
10049
  let current = resolve21(cwd);
9382
10050
  while (true) {
9383
- if (existsSync20(join11(current, ".git"))) {
10051
+ if (existsSync22(join13(current, ".git"))) {
9384
10052
  return current;
9385
10053
  }
9386
10054
  const parent = dirname9(current);
@@ -9392,14 +10060,14 @@ class MemoryStore {
9392
10060
  }
9393
10061
  getProjectSlug(cwd) {
9394
10062
  const root = this.findProjectRoot(cwd);
9395
- const folderName = basename(root).toLowerCase().replace(/[^a-z0-9_-]/g, "-") || "project";
10063
+ const folderName = basename2(root).toLowerCase().replace(/[^a-z0-9_-]/g, "-") || "project";
9396
10064
  const hash = createHash("sha256").update(resolve21(root)).digest("hex").slice(0, 6);
9397
10065
  return `${folderName}-${hash}`;
9398
10066
  }
9399
10067
  getProjectMemoryDir(cwd) {
9400
10068
  if (this.customWorkspacePath) {
9401
10069
  const dir = resolve21(this.customWorkspacePath);
9402
- if (!existsSync20(dir)) {
10070
+ if (!existsSync22(dir)) {
9403
10071
  try {
9404
10072
  mkdirSync12(dir, { recursive: true });
9405
10073
  } catch {}
@@ -9407,8 +10075,8 @@ class MemoryStore {
9407
10075
  return dir;
9408
10076
  }
9409
10077
  const slug = this.getProjectSlug(cwd);
9410
- const dir = join11(getProjectsDir(), slug, "memory");
9411
- if (!existsSync20(dir)) {
10078
+ const dir = join13(getProjectsDir(), slug, "memory");
10079
+ if (!existsSync22(dir)) {
9412
10080
  try {
9413
10081
  mkdirSync12(dir, { recursive: true });
9414
10082
  } catch {}
@@ -9416,7 +10084,7 @@ class MemoryStore {
9416
10084
  return dir;
9417
10085
  }
9418
10086
  getMemoryIndexPath(cwd) {
9419
- return join11(this.getProjectMemoryDir(cwd), "MEMORY.md");
10087
+ return join13(this.getProjectMemoryDir(cwd), "MEMORY.md");
9420
10088
  }
9421
10089
  normalizeCategory(raw) {
9422
10090
  const cat = raw.toLowerCase().trim();
@@ -9435,7 +10103,7 @@ class MemoryStore {
9435
10103
  const sanitizedName = params.name.toLowerCase().trim().replace(/[^a-z0-9_-]/g, "_").replace(/^_+|_+$/g, "") || `note_${Date.now()}`;
9436
10104
  const memoryDir = this.getProjectMemoryDir(params.cwd);
9437
10105
  const fileName = `${type}_${sanitizedName}.md`;
9438
- const filePath = join11(memoryDir, fileName);
10106
+ const filePath = join13(memoryDir, fileName);
9439
10107
  const nowIso = new Date().toISOString();
9440
10108
  const cleanContent = params.content.trim();
9441
10109
  const desc = (params.description || cleanContent.split(`
@@ -9470,23 +10138,23 @@ class MemoryStore {
9470
10138
  }
9471
10139
  readTopicMemory(topicNameOrFile, cwd) {
9472
10140
  const memoryDir = this.getProjectMemoryDir(cwd);
9473
- let targetPath = join11(memoryDir, topicNameOrFile);
9474
- if (!existsSync20(targetPath)) {
10141
+ let targetPath = join13(memoryDir, topicNameOrFile);
10142
+ if (!existsSync22(targetPath)) {
9475
10143
  if (!topicNameOrFile.endsWith(".md")) {
9476
- targetPath = join11(memoryDir, `${topicNameOrFile}.md`);
10144
+ targetPath = join13(memoryDir, `${topicNameOrFile}.md`);
9477
10145
  }
9478
10146
  }
9479
- if (!existsSync20(targetPath)) {
9480
- const files = readdirSync9(memoryDir);
10147
+ if (!existsSync22(targetPath)) {
10148
+ const files = readdirSync10(memoryDir);
9481
10149
  const match = files.find((f) => f.includes(topicNameOrFile));
9482
10150
  if (match) {
9483
- targetPath = join11(memoryDir, match);
10151
+ targetPath = join13(memoryDir, match);
9484
10152
  } else {
9485
10153
  return null;
9486
10154
  }
9487
10155
  }
9488
10156
  try {
9489
- const raw = readFileSync11(targetPath, "utf8");
10157
+ const raw = readFileSync13(targetPath, "utf8");
9490
10158
  return this.parseTopicFile(raw, targetPath);
9491
10159
  } catch {
9492
10160
  return null;
@@ -9497,7 +10165,7 @@ class MemoryStore {
9497
10165
  `);
9498
10166
  let inFm = false;
9499
10167
  let type = "project";
9500
- let name = basename(filePath, ".md");
10168
+ let name = basename2(filePath, ".md");
9501
10169
  let description;
9502
10170
  let modified = new Date().toISOString();
9503
10171
  const bodyLines = [];
@@ -9541,13 +10209,13 @@ class MemoryStore {
9541
10209
  }
9542
10210
  syncMemoryIndex(cwd) {
9543
10211
  const memoryDir = this.getProjectMemoryDir(cwd);
9544
- const indexPath = join11(memoryDir, "MEMORY.md");
9545
- const files = existsSync20(memoryDir) ? readdirSync9(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
10212
+ const indexPath = join13(memoryDir, "MEMORY.md");
10213
+ const files = existsSync22(memoryDir) ? readdirSync10(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
9546
10214
  const items = [];
9547
10215
  for (const f of files) {
9548
10216
  try {
9549
- const full = join11(memoryDir, f);
9550
- const parsed = this.parseTopicFile(readFileSync11(full, "utf8"), full);
10217
+ const full = join13(memoryDir, f);
10218
+ const parsed = this.parseTopicFile(readFileSync13(full, "utf8"), full);
9551
10219
  items.push({
9552
10220
  type: parsed.type,
9553
10221
  name: parsed.name,
@@ -9573,10 +10241,10 @@ class MemoryStore {
9573
10241
  }
9574
10242
  loadMemoryIndex(cwd) {
9575
10243
  const indexPath = this.getMemoryIndexPath(cwd);
9576
- if (!existsSync20(indexPath))
10244
+ if (!existsSync22(indexPath))
9577
10245
  return "";
9578
10246
  try {
9579
- const raw = readFileSync11(indexPath, "utf8");
10247
+ const raw = readFileSync13(indexPath, "utf8");
9580
10248
  const byteLimit = 25 * 1024;
9581
10249
  const sliced = raw.length > byteLimit ? raw.slice(0, byteLimit) : raw;
9582
10250
  const lines = sliced.split(`
@@ -9589,14 +10257,14 @@ class MemoryStore {
9589
10257
  }
9590
10258
  listProjectMemories(cwd) {
9591
10259
  const memoryDir = this.getProjectMemoryDir(cwd);
9592
- if (!existsSync20(memoryDir))
10260
+ if (!existsSync22(memoryDir))
9593
10261
  return [];
9594
- const files = readdirSync9(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
10262
+ const files = readdirSync10(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
9595
10263
  const list = [];
9596
10264
  for (const f of files) {
9597
10265
  try {
9598
- const full = join11(memoryDir, f);
9599
- list.push(this.parseTopicFile(readFileSync11(full, "utf8"), full));
10266
+ const full = join13(memoryDir, f);
10267
+ list.push(this.parseTopicFile(readFileSync13(full, "utf8"), full));
9600
10268
  } catch {}
9601
10269
  }
9602
10270
  return list;
@@ -9773,8 +10441,8 @@ async function removeWorktreeGit(repoRoot, worktreePath, deleteBranch = false) {
9773
10441
  return { success: true };
9774
10442
  }
9775
10443
  // src/worktree/manager.ts
9776
- import { resolve as resolve23, join as join12 } from "path";
9777
- import { existsSync as existsSync21, mkdirSync as mkdirSync13, writeFileSync as writeFileSync7, readFileSync as readFileSync12 } from "fs";
10444
+ import { resolve as resolve23, join as join14 } from "path";
10445
+ import { existsSync as existsSync23, mkdirSync as mkdirSync13, writeFileSync as writeFileSync7, readFileSync as readFileSync14 } from "fs";
9778
10446
  var DEFAULT_WORKTREE_KEEP_COUNT = 15;
9779
10447
 
9780
10448
  class WorktreeManager {
@@ -9798,7 +10466,7 @@ class WorktreeManager {
9798
10466
  const branchName = options.branch || `groupy/${taskId}`;
9799
10467
  const targetDir = options.worktreePath || (this.baseStorageDir ? resolve23(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve23(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
9800
10468
  const worktreeParent = resolve23(targetDir, "..");
9801
- if (!existsSync21(worktreeParent)) {
10469
+ if (!existsSync23(worktreeParent)) {
9802
10470
  mkdirSync13(worktreeParent, { recursive: true });
9803
10471
  }
9804
10472
  const baseBranch = options.baseBranch || await getCurrentBranch(repoRoot);
@@ -9806,7 +10474,7 @@ class WorktreeManager {
9806
10474
  if (!result.success) {
9807
10475
  throw new Error(`Failed to create git worktree: ${result.error}`);
9808
10476
  }
9809
- const metaPath = join12(targetDir, "groupy-thread.json");
10477
+ const metaPath = join14(targetDir, "groupy-thread.json");
9810
10478
  try {
9811
10479
  writeFileSync7(metaPath, JSON.stringify({
9812
10480
  version: 1,
@@ -9832,10 +10500,10 @@ class WorktreeManager {
9832
10500
  return [];
9833
10501
  const worktrees = await listWorktreesGit(repoRoot);
9834
10502
  return worktrees.map((wt) => {
9835
- const metaPath = join12(wt.path, "groupy-thread.json");
9836
- if (existsSync21(metaPath)) {
10503
+ const metaPath = join14(wt.path, "groupy-thread.json");
10504
+ if (existsSync23(metaPath)) {
9837
10505
  try {
9838
- const raw = JSON.parse(readFileSync12(metaPath, "utf8"));
10506
+ const raw = JSON.parse(readFileSync14(metaPath, "utf8"));
9839
10507
  return { ...wt, threadId: raw.ownerThreadId || raw.threadId };
9840
10508
  } catch {}
9841
10509
  }
@@ -11544,6 +12212,7 @@ export {
11544
12212
  AgentRoleRegistry,
11545
12213
  AgentSpawner,
11546
12214
  AuthClient,
12215
+ AutoVerifier,
11547
12216
  BrowserLauncher,
11548
12217
  CHROME_DEVTOOLS_MCP_SERVER_PATH,
11549
12218
  CLAUDE_GLYPHS,
@@ -11565,6 +12234,7 @@ export {
11565
12234
  CredentialsStore,
11566
12235
  DEFAULT_AUTO_COMPACT_THRESHOLD_TOKENS,
11567
12236
  DEFAULT_MAX_CONTEXT_TOKENS,
12237
+ DEFAULT_MAX_UNPAGINATED_LINES,
11568
12238
  DEFAULT_WORKTREE_KEEP_COUNT,
11569
12239
  DefaultModelClientSession,
11570
12240
  DomSnapshotEngine,
@@ -11660,5 +12330,6 @@ export {
11660
12330
  submissionLoop,
11661
12331
  updatePlanTool,
11662
12332
  verifyTaskAction,
12333
+ viewFileTool,
11663
12334
  writeFileTool
11664
12335
  };