youmd 0.6.19 → 0.6.21
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/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +337 -31
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/skill.js +2 -2
- package/dist/commands/skill.js.map +1 -1
- package/dist/index.js +79 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/ascii.d.ts +4 -0
- package/dist/lib/ascii.d.ts.map +1 -1
- package/dist/lib/ascii.js +12 -4
- package/dist/lib/ascii.js.map +1 -1
- package/dist/lib/first-run.d.ts +14 -0
- package/dist/lib/first-run.d.ts.map +1 -0
- package/dist/lib/first-run.js +97 -0
- package/dist/lib/first-run.js.map +1 -0
- package/dist/lib/onboarding.js +1 -1
- package/dist/lib/onboarding.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/you.js +1 -1
- package/dist/you.js.map +1 -1
- package/package.json +1 -1
- package/dist/__tests__/api.test.d.ts +0 -2
- package/dist/__tests__/api.test.d.ts.map +0 -1
- package/dist/__tests__/api.test.js +0 -87
- package/dist/__tests__/api.test.js.map +0 -1
- package/dist/__tests__/compiler.test.d.ts +0 -2
- package/dist/__tests__/compiler.test.d.ts.map +0 -1
- package/dist/__tests__/compiler.test.js +0 -127
- package/dist/__tests__/compiler.test.js.map +0 -1
- package/dist/__tests__/config.test.d.ts +0 -2
- package/dist/__tests__/config.test.d.ts.map +0 -1
- package/dist/__tests__/config.test.js +0 -79
- package/dist/__tests__/config.test.js.map +0 -1
- package/dist/__tests__/decompile.test.d.ts +0 -2
- package/dist/__tests__/decompile.test.d.ts.map +0 -1
- package/dist/__tests__/decompile.test.js +0 -102
- package/dist/__tests__/decompile.test.js.map +0 -1
- package/dist/__tests__/hash.test.d.ts +0 -2
- package/dist/__tests__/hash.test.d.ts.map +0 -1
- package/dist/__tests__/hash.test.js +0 -44
- package/dist/__tests__/hash.test.js.map +0 -1
- package/dist/__tests__/integration.test.d.ts +0 -2
- package/dist/__tests__/integration.test.d.ts.map +0 -1
- package/dist/__tests__/integration.test.js +0 -284
- package/dist/__tests__/integration.test.js.map +0 -1
- package/dist/__tests__/skill-renderer.test.d.ts +0 -2
- package/dist/__tests__/skill-renderer.test.d.ts.map +0 -1
- package/dist/__tests__/skill-renderer.test.js +0 -68
- package/dist/__tests__/skill-renderer.test.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAi7DA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA2jBjD"}
|
package/dist/commands/chat.js
CHANGED
|
@@ -55,7 +55,7 @@ const update_1 = require("../lib/update");
|
|
|
55
55
|
const config_2 = require("../lib/config");
|
|
56
56
|
const CONVEX_SITE_URL = (0, config_2.getConvexSiteUrl)();
|
|
57
57
|
const STREAM_URL = `${CONVEX_SITE_URL}/api/v1/chat/stream`;
|
|
58
|
-
const CURRENT_VERSION = "0.6.
|
|
58
|
+
const CURRENT_VERSION = "0.6.21";
|
|
59
59
|
function delay(ms) {
|
|
60
60
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
61
61
|
}
|
|
@@ -1330,6 +1330,310 @@ function formatProjectBootstrapToolResult(project, result) {
|
|
|
1330
1330
|
`recommended_next_move: read ${project.name}'s project-context and turn the rough docs into a sharper current-state + TODO pass.`,
|
|
1331
1331
|
].join("\n");
|
|
1332
1332
|
}
|
|
1333
|
+
function isLocalToolLoopCandidate(input) {
|
|
1334
|
+
if (isStartThereIntent(input) || isLocalRecentProjectsIntent(input))
|
|
1335
|
+
return true;
|
|
1336
|
+
const lower = input.toLowerCase();
|
|
1337
|
+
return [
|
|
1338
|
+
"local",
|
|
1339
|
+
"workspace",
|
|
1340
|
+
"workspaces",
|
|
1341
|
+
"filesystem",
|
|
1342
|
+
"file system",
|
|
1343
|
+
"recent projects",
|
|
1344
|
+
"recent work",
|
|
1345
|
+
"working on lately",
|
|
1346
|
+
"project-context",
|
|
1347
|
+
"project context",
|
|
1348
|
+
"agent entrypoint",
|
|
1349
|
+
"agent instructions",
|
|
1350
|
+
"scaffold",
|
|
1351
|
+
"bootstrap",
|
|
1352
|
+
"start there",
|
|
1353
|
+
"sync identity",
|
|
1354
|
+
"sync my identity",
|
|
1355
|
+
"publish identity",
|
|
1356
|
+
"publish my identity",
|
|
1357
|
+
"push my identity",
|
|
1358
|
+
].some((phrase) => lower.includes(phrase));
|
|
1359
|
+
}
|
|
1360
|
+
function userAskedForMutation(input) {
|
|
1361
|
+
const lower = input.toLowerCase();
|
|
1362
|
+
return [
|
|
1363
|
+
"start there",
|
|
1364
|
+
"do it",
|
|
1365
|
+
"go ahead",
|
|
1366
|
+
"scaffold",
|
|
1367
|
+
"bootstrap",
|
|
1368
|
+
"create",
|
|
1369
|
+
"write",
|
|
1370
|
+
"update",
|
|
1371
|
+
"fix",
|
|
1372
|
+
"tighten",
|
|
1373
|
+
"sync",
|
|
1374
|
+
"publish",
|
|
1375
|
+
"push",
|
|
1376
|
+
"upload",
|
|
1377
|
+
].some((phrase) => lower.includes(phrase));
|
|
1378
|
+
}
|
|
1379
|
+
function safeJsonObject(text) {
|
|
1380
|
+
const trimmed = text.trim();
|
|
1381
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
1382
|
+
const candidate = fenced?.[1]?.trim() || trimmed.match(/\{[\s\S]*\}/)?.[0];
|
|
1383
|
+
if (!candidate)
|
|
1384
|
+
return null;
|
|
1385
|
+
try {
|
|
1386
|
+
const parsed = JSON.parse(candidate);
|
|
1387
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
1388
|
+
? parsed
|
|
1389
|
+
: null;
|
|
1390
|
+
}
|
|
1391
|
+
catch {
|
|
1392
|
+
return null;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
function normalizeLocalHostToolCall(value) {
|
|
1396
|
+
if (!value)
|
|
1397
|
+
return null;
|
|
1398
|
+
const tool = value.tool;
|
|
1399
|
+
if (tool !== "discover_projects" &&
|
|
1400
|
+
tool !== "read_project_context" &&
|
|
1401
|
+
tool !== "write_project_context" &&
|
|
1402
|
+
tool !== "sync_identity" &&
|
|
1403
|
+
tool !== "respond") {
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
const mode = value.mode === "publish" || value.mode === "bootstrap" || value.mode === "status"
|
|
1407
|
+
? value.mode
|
|
1408
|
+
: undefined;
|
|
1409
|
+
return {
|
|
1410
|
+
tool,
|
|
1411
|
+
project: typeof value.project === "string" ? value.project : undefined,
|
|
1412
|
+
mode,
|
|
1413
|
+
reason: typeof value.reason === "string" ? value.reason : undefined,
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
function inferLocalHostToolCall(input, launchInvestigation) {
|
|
1417
|
+
const lower = input.toLowerCase();
|
|
1418
|
+
if (isStartThereIntent(input)) {
|
|
1419
|
+
return { tool: "write_project_context", project: launchInvestigation.strongestProject?.name, mode: "bootstrap" };
|
|
1420
|
+
}
|
|
1421
|
+
if (lower.includes("sync") || lower.includes("publish") || lower.includes("push my identity")) {
|
|
1422
|
+
return { tool: "sync_identity", mode: lower.includes("publish") || lower.includes("push") || lower.includes("sync") ? "publish" : "status" };
|
|
1423
|
+
}
|
|
1424
|
+
if (lower.includes("read") || lower.includes("show") || lower.includes("inspect") || lower.includes("context")) {
|
|
1425
|
+
return { tool: "read_project_context", project: launchInvestigation.strongestProject?.name };
|
|
1426
|
+
}
|
|
1427
|
+
if (lower.includes("scaffold") || lower.includes("bootstrap") || lower.includes("create") || lower.includes("write") || lower.includes("update")) {
|
|
1428
|
+
return { tool: "write_project_context", project: launchInvestigation.strongestProject?.name, mode: "bootstrap" };
|
|
1429
|
+
}
|
|
1430
|
+
return { tool: "discover_projects" };
|
|
1431
|
+
}
|
|
1432
|
+
function collectKnownProjects(launchInvestigation) {
|
|
1433
|
+
const projects = [
|
|
1434
|
+
launchInvestigation.strongestProject,
|
|
1435
|
+
...scanRecentWorkspaceProjects(12),
|
|
1436
|
+
...(0, project_1.getRecentProjectInsights)(process.cwd(), 12),
|
|
1437
|
+
].filter((item) => !!item);
|
|
1438
|
+
const seen = new Set();
|
|
1439
|
+
return projects.filter((project) => {
|
|
1440
|
+
const key = fs.existsSync(project.projectDir)
|
|
1441
|
+
? fs.realpathSync.native(project.projectDir)
|
|
1442
|
+
: path.resolve(project.projectDir);
|
|
1443
|
+
if (seen.has(key))
|
|
1444
|
+
return false;
|
|
1445
|
+
seen.add(key);
|
|
1446
|
+
return true;
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
function resolveProjectForTool(projectRef, launchInvestigation) {
|
|
1450
|
+
const projects = collectKnownProjects(launchInvestigation);
|
|
1451
|
+
if (!projectRef && launchInvestigation.strongestProject)
|
|
1452
|
+
return launchInvestigation.strongestProject;
|
|
1453
|
+
if (!projectRef)
|
|
1454
|
+
return projects[0] || null;
|
|
1455
|
+
const normalized = projectRef.toLowerCase().trim();
|
|
1456
|
+
const byPath = projects.find((project) => path.resolve(project.projectDir) === path.resolve(projectRef));
|
|
1457
|
+
if (byPath)
|
|
1458
|
+
return byPath;
|
|
1459
|
+
return projects.find((project) => project.name.toLowerCase() === normalized ||
|
|
1460
|
+
project.slug.toLowerCase() === normalized ||
|
|
1461
|
+
project.projectDir.toLowerCase().includes(normalized)) || null;
|
|
1462
|
+
}
|
|
1463
|
+
function readSnippet(filePath, maxChars = 1800) {
|
|
1464
|
+
if (!fs.existsSync(filePath))
|
|
1465
|
+
return null;
|
|
1466
|
+
try {
|
|
1467
|
+
const content = fs.readFileSync(filePath, "utf-8").trim();
|
|
1468
|
+
if (!content)
|
|
1469
|
+
return null;
|
|
1470
|
+
return content.length > maxChars ? `${content.slice(0, maxChars)}\n...` : content;
|
|
1471
|
+
}
|
|
1472
|
+
catch {
|
|
1473
|
+
return null;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
function formatProjectReadToolResult(project) {
|
|
1477
|
+
const files = [
|
|
1478
|
+
"AGENTS.md",
|
|
1479
|
+
"CLAUDE.md",
|
|
1480
|
+
"package.json",
|
|
1481
|
+
"pyproject.toml",
|
|
1482
|
+
"Cargo.toml",
|
|
1483
|
+
"go.mod",
|
|
1484
|
+
"project-context/CURRENT_STATE.md",
|
|
1485
|
+
"project-context/TODO.md",
|
|
1486
|
+
"project-context/PRD.md",
|
|
1487
|
+
"project-context/ARCHITECTURE.md",
|
|
1488
|
+
".you/project-context/CURRENT_STATE.md",
|
|
1489
|
+
".you/project-context/TODO.md",
|
|
1490
|
+
];
|
|
1491
|
+
const snippets = files
|
|
1492
|
+
.map((relativePath) => {
|
|
1493
|
+
const snippet = readSnippet(path.join(project.projectDir, relativePath));
|
|
1494
|
+
return snippet ? `file: ${relativePath}\n${snippet}` : null;
|
|
1495
|
+
})
|
|
1496
|
+
.filter((item) => !!item)
|
|
1497
|
+
.slice(0, 5);
|
|
1498
|
+
const managedContext = (0, project_1.readProjectContext)(project.projectDir);
|
|
1499
|
+
return [
|
|
1500
|
+
"tool: read_project_context",
|
|
1501
|
+
"status: ok",
|
|
1502
|
+
`project: ${project.name}`,
|
|
1503
|
+
`project_dir: ${project.projectDir}`,
|
|
1504
|
+
`markers: ${(0, project_1.getProjectMarkerSignals)(project.projectDir).join(", ") || "none"}`,
|
|
1505
|
+
managedContext ? `managed_project_context: ${managedContext.meta.name}` : "managed_project_context: none",
|
|
1506
|
+
"",
|
|
1507
|
+
snippets.length > 0 ? snippets.join("\n\n---\n\n") : "no readable project-context or agent entrypoint files found yet.",
|
|
1508
|
+
"",
|
|
1509
|
+
`recommended_next_move: write a sharper current-state + TODO pass for ${project.name} if the user wants this project tightened.`,
|
|
1510
|
+
].join("\n");
|
|
1511
|
+
}
|
|
1512
|
+
async function formatIdentitySyncToolResult(bundleDir, publish) {
|
|
1513
|
+
const result = (0, compiler_1.compileBundle)(bundleDir);
|
|
1514
|
+
(0, compiler_1.writeBundle)(bundleDir, result);
|
|
1515
|
+
const lines = [
|
|
1516
|
+
"tool: sync_identity",
|
|
1517
|
+
"status: ok",
|
|
1518
|
+
`bundle_dir: ${bundleDir}`,
|
|
1519
|
+
`compiled_version: ${result.stats.version}`,
|
|
1520
|
+
`sections: ${result.stats.filledSections}/${result.stats.totalSections}`,
|
|
1521
|
+
];
|
|
1522
|
+
if (!publish) {
|
|
1523
|
+
lines.push("remote_sync: not requested; local bundle compiled only.");
|
|
1524
|
+
lines.push("recommended_next_move: publish if you want this local identity bundle pushed to you.md.");
|
|
1525
|
+
return lines.join("\n");
|
|
1526
|
+
}
|
|
1527
|
+
if (!(0, config_1.isAuthenticated)()) {
|
|
1528
|
+
return [
|
|
1529
|
+
...lines,
|
|
1530
|
+
"remote_sync: blocked; not authenticated.",
|
|
1531
|
+
"recommended_next_move: run youmd login, then ask me to sync identity again.",
|
|
1532
|
+
].join("\n");
|
|
1533
|
+
}
|
|
1534
|
+
const youJson = JSON.parse(fs.readFileSync(path.join(bundleDir, "you.json"), "utf-8"));
|
|
1535
|
+
const youMd = fs.readFileSync(path.join(bundleDir, "you.md"), "utf-8");
|
|
1536
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(bundleDir, "manifest.json"), "utf-8"));
|
|
1537
|
+
const uploadRes = await (0, api_1.uploadBundle)({ manifest, youJson, youMd });
|
|
1538
|
+
if (!uploadRes.ok) {
|
|
1539
|
+
return [
|
|
1540
|
+
...lines,
|
|
1541
|
+
`remote_sync: upload failed with status ${uploadRes.status}.`,
|
|
1542
|
+
"recommended_next_move: inspect auth/API state before retrying identity sync.",
|
|
1543
|
+
].join("\n");
|
|
1544
|
+
}
|
|
1545
|
+
const publishRes = await (0, api_1.publishLatest)();
|
|
1546
|
+
if (!publishRes.ok) {
|
|
1547
|
+
return [
|
|
1548
|
+
...lines,
|
|
1549
|
+
`remote_sync: publish failed with status ${publishRes.status}.`,
|
|
1550
|
+
"recommended_next_move: inspect publish API state before retrying identity sync.",
|
|
1551
|
+
].join("\n");
|
|
1552
|
+
}
|
|
1553
|
+
lines.push(`remote_sync: published v${publishRes.data.version} as ${publishRes.data.username}.`);
|
|
1554
|
+
lines.push(`url: ${publishRes.data.url || `https://you.md/${publishRes.data.username}`}`);
|
|
1555
|
+
lines.push("recommended_next_move: test the updated identity from another agent surface.");
|
|
1556
|
+
return lines.join("\n");
|
|
1557
|
+
}
|
|
1558
|
+
async function chooseLocalHostTool(args) {
|
|
1559
|
+
const projects = collectKnownProjects(args.launchInvestigation)
|
|
1560
|
+
.slice(0, 8)
|
|
1561
|
+
.map((project) => `${project.name} | ${project.projectDir} | ${project.signals.slice(0, 4).join(", ")}`)
|
|
1562
|
+
.join("\n");
|
|
1563
|
+
const prompt = [
|
|
1564
|
+
"You are the local host tool router for the you.md CLI. Return ONLY compact JSON. No prose.",
|
|
1565
|
+
"Available tools:",
|
|
1566
|
+
"- discover_projects: scan local filesystem markers for recent project work.",
|
|
1567
|
+
"- read_project_context: read AGENTS/CLAUDE/package/project-context files for a project.",
|
|
1568
|
+
"- write_project_context: bootstrap/tighten project context files for a project. Use only when the user asks to start, scaffold, update, write, fix, or tighten.",
|
|
1569
|
+
"- sync_identity: compile identity locally; use mode publish only when the user asks to sync/publish/push identity.",
|
|
1570
|
+
"- respond: no local tool needed.",
|
|
1571
|
+
"",
|
|
1572
|
+
"JSON shape: {\"tool\":\"discover_projects|read_project_context|write_project_context|sync_identity|respond\",\"project\":\"optional project name/path\",\"mode\":\"status|publish|bootstrap\",\"reason\":\"short\"}",
|
|
1573
|
+
"",
|
|
1574
|
+
"Known projects:",
|
|
1575
|
+
projects || "none yet",
|
|
1576
|
+
"",
|
|
1577
|
+
`User request: ${args.userInput}`,
|
|
1578
|
+
].join("\n");
|
|
1579
|
+
try {
|
|
1580
|
+
const raw = await (0, onboarding_1.callLLM)(args.apiKey, [
|
|
1581
|
+
{ role: "system", content: "Return only valid JSON for local tool routing." },
|
|
1582
|
+
{ role: "user", content: prompt },
|
|
1583
|
+
]);
|
|
1584
|
+
return normalizeLocalHostToolCall(safeJsonObject(raw)) || inferLocalHostToolCall(args.userInput, args.launchInvestigation);
|
|
1585
|
+
}
|
|
1586
|
+
catch {
|
|
1587
|
+
return inferLocalHostToolCall(args.userInput, args.launchInvestigation);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
async function executeLocalHostTool(toolCall, args) {
|
|
1591
|
+
if (toolCall.tool === "discover_projects") {
|
|
1592
|
+
const insights = scanRecentWorkspaceProjects(10);
|
|
1593
|
+
args.launchInvestigation.strongestProject = insights[0] || args.launchInvestigation.strongestProject;
|
|
1594
|
+
args.launchInvestigation.strongestMove = insights[0]?.summary || args.launchInvestigation.strongestMove;
|
|
1595
|
+
return formatLocalRecentProjectsToolResult(insights);
|
|
1596
|
+
}
|
|
1597
|
+
if (toolCall.tool === "read_project_context") {
|
|
1598
|
+
const project = resolveProjectForTool(toolCall.project, args.launchInvestigation);
|
|
1599
|
+
if (!project) {
|
|
1600
|
+
return "tool: read_project_context\nstatus: blocked\nresult: no real local project target is known yet.\nrecommended_next_move: run discover_projects first.";
|
|
1601
|
+
}
|
|
1602
|
+
args.launchInvestigation.strongestProject = project;
|
|
1603
|
+
return formatProjectReadToolResult(project);
|
|
1604
|
+
}
|
|
1605
|
+
if (toolCall.tool === "write_project_context") {
|
|
1606
|
+
const project = resolveProjectForTool(toolCall.project, args.launchInvestigation);
|
|
1607
|
+
if (!project) {
|
|
1608
|
+
return "tool: write_project_context\nstatus: blocked\nresult: no real local project target is known yet.\nrecommended_next_move: run discover_projects first.";
|
|
1609
|
+
}
|
|
1610
|
+
if (!userAskedForMutation(args.userInput)) {
|
|
1611
|
+
return [
|
|
1612
|
+
"tool: write_project_context",
|
|
1613
|
+
"status: blocked",
|
|
1614
|
+
`project: ${project.name}`,
|
|
1615
|
+
"result: user did not explicitly ask for a filesystem mutation.",
|
|
1616
|
+
`recommended_next_move: say "start there" to bootstrap ${project.name}.`,
|
|
1617
|
+
].join("\n");
|
|
1618
|
+
}
|
|
1619
|
+
const previousCwd = process.cwd();
|
|
1620
|
+
let result;
|
|
1621
|
+
try {
|
|
1622
|
+
process.chdir(project.projectDir);
|
|
1623
|
+
result = (0, skills_1.initProject)({ mode: "additive" });
|
|
1624
|
+
}
|
|
1625
|
+
finally {
|
|
1626
|
+
process.chdir(previousCwd);
|
|
1627
|
+
}
|
|
1628
|
+
args.launchInvestigation.strongestProject = project;
|
|
1629
|
+
return formatProjectBootstrapToolResult(project, result);
|
|
1630
|
+
}
|
|
1631
|
+
if (toolCall.tool === "sync_identity") {
|
|
1632
|
+
const publish = toolCall.mode === "publish" && userAskedForMutation(args.userInput);
|
|
1633
|
+
return await formatIdentitySyncToolResult(args.bundleDir, publish);
|
|
1634
|
+
}
|
|
1635
|
+
return "tool: respond\nstatus: skipped\nresult: no local host tool needed.";
|
|
1636
|
+
}
|
|
1333
1637
|
async function handleLocalChatIntent(args) {
|
|
1334
1638
|
const runToolResultThroughModel = async (toolResult, spinnerLabel) => {
|
|
1335
1639
|
args.messages.push({ role: "user", content: args.userInput });
|
|
@@ -1345,6 +1649,8 @@ async function handleLocalChatIntent(args) {
|
|
|
1345
1649
|
"Do not ask the user what the local project is if the tool result already names it.",
|
|
1346
1650
|
"State what the local host actually found, make one concrete recommendation, and end with the exact phrase `next strongest move: ...`.",
|
|
1347
1651
|
"For workspace scans, reuse recommended_next_move as the final next strongest move. The supported follow-up command is `start there`; do not invent commands like `open PROJECT`.",
|
|
1652
|
+
"For read_project_context results, summarize what you actually saw and recommend the next concrete local tool action.",
|
|
1653
|
+
"For sync_identity results, state whether local compile or remote publish happened.",
|
|
1348
1654
|
"Do not end with a question. Keep it under 8 lines. No generic help-desk closer.",
|
|
1349
1655
|
].join("\n"),
|
|
1350
1656
|
});
|
|
@@ -1354,41 +1660,40 @@ async function handleLocalChatIntent(args) {
|
|
|
1354
1660
|
printAgentMessage((0, onboarding_1.parseUpdatesFromResponse)(result.text).display);
|
|
1355
1661
|
}
|
|
1356
1662
|
};
|
|
1357
|
-
if (
|
|
1358
|
-
const
|
|
1663
|
+
if (isLocalToolLoopCandidate(args.userInput)) {
|
|
1664
|
+
const routeSpinner = new render_1.BrailleSpinner("choosing local tool");
|
|
1665
|
+
routeSpinner.start();
|
|
1666
|
+
const toolCall = await chooseLocalHostTool(args);
|
|
1667
|
+
routeSpinner.stop(`selected ${toolCall.tool}`);
|
|
1668
|
+
if (toolCall.tool === "respond")
|
|
1669
|
+
return false;
|
|
1670
|
+
const labelByTool = {
|
|
1671
|
+
discover_projects: "discovering local projects",
|
|
1672
|
+
read_project_context: "reading project context",
|
|
1673
|
+
write_project_context: "writing project context",
|
|
1674
|
+
sync_identity: "syncing identity context",
|
|
1675
|
+
respond: "thinking",
|
|
1676
|
+
};
|
|
1677
|
+
const spinner = new render_1.BrailleSpinner(labelByTool[toolCall.tool]);
|
|
1359
1678
|
spinner.start();
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1679
|
+
try {
|
|
1680
|
+
const toolResult = await executeLocalHostTool(toolCall, args);
|
|
1681
|
+
const statusLine = toolResult.match(/^status: (.+)$/m)?.[1] || "done";
|
|
1682
|
+
spinner.stop(statusLine);
|
|
1683
|
+
await runToolResultThroughModel(toolResult, `summarizing ${toolCall.tool}`);
|
|
1684
|
+
}
|
|
1685
|
+
catch (err) {
|
|
1686
|
+
const message = err instanceof Error ? err.message : "local tool failed";
|
|
1687
|
+
spinner.fail(message);
|
|
1688
|
+
const response = [
|
|
1689
|
+
`local tool failed: ${message}`,
|
|
1690
|
+
"",
|
|
1691
|
+
"next strongest move: retry with a read-only local scan so i can recover from real filesystem state.",
|
|
1692
|
+
].join("\n");
|
|
1373
1693
|
args.messages.push({ role: "user", content: args.userInput });
|
|
1374
1694
|
args.messages.push({ role: "assistant", content: response });
|
|
1375
1695
|
printAgentMessage(response);
|
|
1376
|
-
return true;
|
|
1377
|
-
}
|
|
1378
|
-
const spinner = new render_1.BrailleSpinner(`opening ${project.name} from local disk`);
|
|
1379
|
-
spinner.start();
|
|
1380
|
-
await delay(500);
|
|
1381
|
-
const previousCwd = process.cwd();
|
|
1382
|
-
let result;
|
|
1383
|
-
try {
|
|
1384
|
-
process.chdir(project.projectDir);
|
|
1385
|
-
result = (0, skills_1.initProject)({ mode: "additive" });
|
|
1386
|
-
}
|
|
1387
|
-
finally {
|
|
1388
|
-
process.chdir(previousCwd);
|
|
1389
1696
|
}
|
|
1390
|
-
spinner.stop(`${project.name} updated`);
|
|
1391
|
-
await runToolResultThroughModel(formatProjectBootstrapToolResult(project, result), `summarizing ${project.name} changes`);
|
|
1392
1697
|
return true;
|
|
1393
1698
|
}
|
|
1394
1699
|
return false;
|
|
@@ -1707,6 +2012,7 @@ async function chatCommand() {
|
|
|
1707
2012
|
messages,
|
|
1708
2013
|
launchInvestigation,
|
|
1709
2014
|
apiKey,
|
|
2015
|
+
bundleDir,
|
|
1710
2016
|
});
|
|
1711
2017
|
if (handledLocally)
|
|
1712
2018
|
continue;
|