@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/cli.js +2010 -1686
- package/dist/index.js +757 -86
- package/package.json +9 -9
- package/templates/base/groupy_prompt.md +22 -19
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
|
|
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: {
|
|
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
|
|
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: '${
|
|
2608
|
+
return { output: `Error: File not found: '${rawPath}'`, isError: true };
|
|
2584
2609
|
}
|
|
2585
2610
|
try {
|
|
2586
2611
|
const content = readFileSync3(filePath, "utf8");
|
|
2587
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
5454
|
-
import { resolve as resolve12, dirname as dirname6, join as
|
|
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 (!
|
|
6157
|
+
if (!existsSync15(fullPath))
|
|
5490
6158
|
return;
|
|
5491
6159
|
this.loadedConfigFiles.add(fullPath);
|
|
5492
6160
|
try {
|
|
5493
|
-
const content =
|
|
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 (!
|
|
6412
|
+
if (!existsSync15(dir)) {
|
|
5745
6413
|
mkdirSync7(dir, { recursive: true });
|
|
5746
6414
|
}
|
|
5747
6415
|
let existing = { mcpServers: {} };
|
|
5748
|
-
if (
|
|
6416
|
+
if (existsSync15(fullPath)) {
|
|
5749
6417
|
try {
|
|
5750
|
-
const content =
|
|
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 (!
|
|
6431
|
+
if (!existsSync15(fullPath))
|
|
5764
6432
|
return false;
|
|
5765
6433
|
try {
|
|
5766
|
-
const content =
|
|
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 =
|
|
5794
|
-
if (
|
|
6461
|
+
const workspaceConfig = join9(cwd, ".mcp.json");
|
|
6462
|
+
if (existsSync15(workspaceConfig))
|
|
5795
6463
|
return workspaceConfig;
|
|
5796
|
-
const altConfig =
|
|
5797
|
-
if (
|
|
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
|
|
5820
|
-
import { join as
|
|
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 &&
|
|
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
|
-
|
|
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
|
-
|
|
6507
|
+
join10(process.env.LOCALAPPDATA || "", "BraveSoftware\\Brave-Browser\\Application\\brave.exe")
|
|
5840
6508
|
];
|
|
5841
6509
|
for (const path of candidates) {
|
|
5842
|
-
if (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 (
|
|
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 (
|
|
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 ||
|
|
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 &&
|
|
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
|
|
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 =
|
|
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
|
|
8331
|
-
import { resolve as resolve18, join as
|
|
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 (!
|
|
9084
|
+
if (!existsSync18(fullPath))
|
|
8417
9085
|
return;
|
|
8418
|
-
const entries =
|
|
9086
|
+
const entries = readdirSync8(fullPath);
|
|
8419
9087
|
for (const entry of entries) {
|
|
8420
9088
|
if (entry.endsWith(".json")) {
|
|
8421
9089
|
try {
|
|
8422
|
-
const content =
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
9148
|
-
import { resolve as resolve20, join as
|
|
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 (
|
|
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) =>
|
|
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 =
|
|
9911
|
+
const entries = readdirSync9(root, { withFileTypes: true });
|
|
9244
9912
|
for (const entry of entries) {
|
|
9245
9913
|
if (entry.isDirectory()) {
|
|
9246
|
-
const skillDir =
|
|
9247
|
-
const skillFilePath =
|
|
9248
|
-
if (
|
|
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 =
|
|
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 =
|
|
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
|
|
9371
|
-
import { resolve as resolve21, join as
|
|
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 (
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
9411
|
-
if (!
|
|
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
|
|
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 =
|
|
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 =
|
|
9474
|
-
if (!
|
|
10141
|
+
let targetPath = join13(memoryDir, topicNameOrFile);
|
|
10142
|
+
if (!existsSync22(targetPath)) {
|
|
9475
10143
|
if (!topicNameOrFile.endsWith(".md")) {
|
|
9476
|
-
targetPath =
|
|
10144
|
+
targetPath = join13(memoryDir, `${topicNameOrFile}.md`);
|
|
9477
10145
|
}
|
|
9478
10146
|
}
|
|
9479
|
-
if (!
|
|
9480
|
-
const files =
|
|
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 =
|
|
10151
|
+
targetPath = join13(memoryDir, match);
|
|
9484
10152
|
} else {
|
|
9485
10153
|
return null;
|
|
9486
10154
|
}
|
|
9487
10155
|
}
|
|
9488
10156
|
try {
|
|
9489
|
-
const raw =
|
|
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 =
|
|
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 =
|
|
9545
|
-
const files =
|
|
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 =
|
|
9550
|
-
const parsed = this.parseTopicFile(
|
|
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 (!
|
|
10244
|
+
if (!existsSync22(indexPath))
|
|
9577
10245
|
return "";
|
|
9578
10246
|
try {
|
|
9579
|
-
const raw =
|
|
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 (!
|
|
10260
|
+
if (!existsSync22(memoryDir))
|
|
9593
10261
|
return [];
|
|
9594
|
-
const files =
|
|
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 =
|
|
9599
|
-
list.push(this.parseTopicFile(
|
|
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
|
|
9777
|
-
import { existsSync as
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
9836
|
-
if (
|
|
10503
|
+
const metaPath = join14(wt.path, "groupy-thread.json");
|
|
10504
|
+
if (existsSync23(metaPath)) {
|
|
9837
10505
|
try {
|
|
9838
|
-
const raw = JSON.parse(
|
|
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
|
};
|