@mtreeai/msapling-cli 2.3.6-beta.47 → 2.3.6-beta.49
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 +754 -122
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1032,6 +1032,47 @@ var init_src = __esm({
|
|
|
1032
1032
|
body: JSON.stringify({ query, max_results: maxResults })
|
|
1033
1033
|
});
|
|
1034
1034
|
}
|
|
1035
|
+
/**
|
|
1036
|
+
* CLI-LOOP-01: cached backend capability map (from GET /chat/health). `null`
|
|
1037
|
+
* until first probed. Cached for the client's lifetime — capabilities are a
|
|
1038
|
+
* deploy-time property of the backend, so a single probe per session suffices.
|
|
1039
|
+
*/
|
|
1040
|
+
_capabilities = null;
|
|
1041
|
+
_capabilitiesProbe = null;
|
|
1042
|
+
/**
|
|
1043
|
+
* CLI-LOOP-01: fetch (and cache) the backend capability map from
|
|
1044
|
+
* GET /chat/health. Returns an empty object when the backend is older / the
|
|
1045
|
+
* probe fails, so callers degrade to legacy behavior rather than throwing.
|
|
1046
|
+
*
|
|
1047
|
+
* Concurrent callers share a single in-flight probe.
|
|
1048
|
+
*/
|
|
1049
|
+
async getCapabilities(force = false) {
|
|
1050
|
+
if (this._capabilities && !force) return this._capabilities;
|
|
1051
|
+
if (this._capabilitiesProbe && !force) return this._capabilitiesProbe;
|
|
1052
|
+
this._capabilitiesProbe = (async () => {
|
|
1053
|
+
try {
|
|
1054
|
+
const data = await this.request("/api/chat/health");
|
|
1055
|
+
const caps = data && typeof data === "object" && data.capabilities && typeof data.capabilities === "object" ? data.capabilities : {};
|
|
1056
|
+
this._capabilities = caps;
|
|
1057
|
+
return caps;
|
|
1058
|
+
} catch {
|
|
1059
|
+
this._capabilities = {};
|
|
1060
|
+
return this._capabilities;
|
|
1061
|
+
} finally {
|
|
1062
|
+
this._capabilitiesProbe = null;
|
|
1063
|
+
}
|
|
1064
|
+
})();
|
|
1065
|
+
return this._capabilitiesProbe;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* CLI-LOOP-01: convenience — does the backend accept the additive structured
|
|
1069
|
+
* `tool_results` array (native parallel multi-tool-use)? When false the Agent
|
|
1070
|
+
* loop uses the legacy `[TOOL_RESULT]`-per-prompt path.
|
|
1071
|
+
*/
|
|
1072
|
+
async supportsStructuredToolResults() {
|
|
1073
|
+
const caps = await this.getCapabilities();
|
|
1074
|
+
return caps.structured_tool_results === true;
|
|
1075
|
+
}
|
|
1035
1076
|
async getHistory(chatId) {
|
|
1036
1077
|
const data = await this.request(`/api/projects/chat/${chatId}/history`);
|
|
1037
1078
|
return data.messages.map((m) => ({
|
|
@@ -5356,17 +5397,17 @@ async function backupFile(absPath) {
|
|
|
5356
5397
|
try {
|
|
5357
5398
|
const { readFile: readFile30, writeFile: writeFile19, mkdir: mkdir10 } = await import("fs/promises");
|
|
5358
5399
|
const { homedir: homedir25 } = await import("os");
|
|
5359
|
-
const { join:
|
|
5400
|
+
const { join: join42 } = await import("path");
|
|
5360
5401
|
const filename = absPath.split(/[\\/]/).pop() ?? "file";
|
|
5361
5402
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5362
5403
|
const suffix = randomBytes7(4).toString("hex");
|
|
5363
|
-
const backupPath =
|
|
5404
|
+
const backupPath = join42(
|
|
5364
5405
|
homedir25(),
|
|
5365
5406
|
".msapling",
|
|
5366
5407
|
"backups",
|
|
5367
5408
|
`${filename}.backup-${stamp}-${suffix}.bak`
|
|
5368
5409
|
);
|
|
5369
|
-
await mkdir10(
|
|
5410
|
+
await mkdir10(join42(homedir25(), ".msapling", "backups"), { recursive: true });
|
|
5370
5411
|
const content = await readFile30(absPath, "utf8");
|
|
5371
5412
|
await writeFile19(backupPath, content, "utf8");
|
|
5372
5413
|
return backupPath;
|
|
@@ -5976,10 +6017,10 @@ var init_Sandbox = __esm({
|
|
|
5976
6017
|
* to a minimal platform default so subprocess spawn never sees an empty PATH.
|
|
5977
6018
|
*/
|
|
5978
6019
|
static curatePath(rawPath) {
|
|
5979
|
-
const
|
|
5980
|
-
const entries = (rawPath ?? "").split(
|
|
6020
|
+
const sep5 = process.platform === "win32" ? ";" : ":";
|
|
6021
|
+
const entries = (rawPath ?? "").split(sep5);
|
|
5981
6022
|
const kept = entries.filter((e) => _Sandbox.isSafePathEntry(e));
|
|
5982
|
-
if (kept.length > 0) return kept.join(
|
|
6023
|
+
if (kept.length > 0) return kept.join(sep5);
|
|
5983
6024
|
return process.platform === "win32" ? "C:\\Windows\\System32;C:\\Windows" : "/usr/local/bin:/usr/bin:/bin";
|
|
5984
6025
|
}
|
|
5985
6026
|
getRestrictedEnv(pathOverride) {
|
|
@@ -6553,6 +6594,124 @@ var init_ResourceGovernor = __esm({
|
|
|
6553
6594
|
}
|
|
6554
6595
|
});
|
|
6555
6596
|
|
|
6597
|
+
// ../core/src/SessionStats.ts
|
|
6598
|
+
function getSessionStats() {
|
|
6599
|
+
if (!singleton) singleton = new SessionStats();
|
|
6600
|
+
return singleton;
|
|
6601
|
+
}
|
|
6602
|
+
function _resetSessionStatsSingleton() {
|
|
6603
|
+
singleton = null;
|
|
6604
|
+
}
|
|
6605
|
+
function fmtDuration(ms) {
|
|
6606
|
+
const s = Math.floor(ms / 1e3);
|
|
6607
|
+
const h = Math.floor(s / 3600);
|
|
6608
|
+
const m = Math.floor(s % 3600 / 60);
|
|
6609
|
+
const sec = s % 60;
|
|
6610
|
+
const parts = [];
|
|
6611
|
+
if (h) parts.push(`${h}h`);
|
|
6612
|
+
if (m || h) parts.push(`${m}m`);
|
|
6613
|
+
parts.push(`${sec}s`);
|
|
6614
|
+
return parts.join(" ");
|
|
6615
|
+
}
|
|
6616
|
+
function renderSessionStats(snap) {
|
|
6617
|
+
const lines = [];
|
|
6618
|
+
lines.push("=== Session Stats ===");
|
|
6619
|
+
lines.push(` Duration: ${fmtDuration(snap.wallMs)}`);
|
|
6620
|
+
const toolNames = Object.keys(snap.tools).sort();
|
|
6621
|
+
lines.push("");
|
|
6622
|
+
lines.push(` Tool calls: ${snap.totalToolCalls}`);
|
|
6623
|
+
if (toolNames.length === 0) {
|
|
6624
|
+
lines.push(" (no tools invoked yet)");
|
|
6625
|
+
} else {
|
|
6626
|
+
for (const name of toolNames) {
|
|
6627
|
+
const t = snap.tools[name];
|
|
6628
|
+
const avg = t.count > 0 ? t.totalMs / t.count : 0;
|
|
6629
|
+
const errStr = t.errors > 0 ? `, ${t.errors} err` : "";
|
|
6630
|
+
lines.push(
|
|
6631
|
+
` - ${name}: ${t.count}\xD7 (avg ${avg.toFixed(0)}ms, total ${t.totalMs.toFixed(0)}ms${errStr})`
|
|
6632
|
+
);
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
const a = snap.approvals;
|
|
6636
|
+
if (a.approved || a.denied || a.autoApproved) {
|
|
6637
|
+
lines.push("");
|
|
6638
|
+
lines.push(" Approvals:");
|
|
6639
|
+
lines.push(` - approved: ${a.approved}`);
|
|
6640
|
+
lines.push(` - denied: ${a.denied}`);
|
|
6641
|
+
lines.push(` - auto (trusted):${a.autoApproved}`);
|
|
6642
|
+
}
|
|
6643
|
+
const u = snap.usage;
|
|
6644
|
+
const totalTokens = u.promptTokens + u.completionTokens;
|
|
6645
|
+
lines.push("");
|
|
6646
|
+
lines.push(" Usage:");
|
|
6647
|
+
lines.push(` - turns: ${u.turns}`);
|
|
6648
|
+
lines.push(` - prompt tokens: ${u.promptTokens.toLocaleString()}`);
|
|
6649
|
+
lines.push(` - output tokens: ${u.completionTokens.toLocaleString()}`);
|
|
6650
|
+
lines.push(` - total tokens: ${totalTokens.toLocaleString()}`);
|
|
6651
|
+
lines.push(` - cost (USD): $${u.costUsd.toFixed(4)}`);
|
|
6652
|
+
if (u.turns > 0) {
|
|
6653
|
+
lines.push(` - avg cost/turn: $${(u.costUsd / u.turns).toFixed(4)}`);
|
|
6654
|
+
}
|
|
6655
|
+
return lines;
|
|
6656
|
+
}
|
|
6657
|
+
var SessionStats, singleton;
|
|
6658
|
+
var init_SessionStats = __esm({
|
|
6659
|
+
"../core/src/SessionStats.ts"() {
|
|
6660
|
+
"use strict";
|
|
6661
|
+
init_esm_shims();
|
|
6662
|
+
SessionStats = class {
|
|
6663
|
+
startedAt = Date.now();
|
|
6664
|
+
tools = /* @__PURE__ */ new Map();
|
|
6665
|
+
usage = { costUsd: 0, promptTokens: 0, completionTokens: 0, turns: 0 };
|
|
6666
|
+
approvals = { approved: 0, denied: 0, autoApproved: 0 };
|
|
6667
|
+
/** Record a single tool invocation's outcome + latency. */
|
|
6668
|
+
recordTool(name, latencyMs, isError) {
|
|
6669
|
+
const cur = this.tools.get(name) ?? { count: 0, errors: 0, totalMs: 0 };
|
|
6670
|
+
cur.count += 1;
|
|
6671
|
+
if (isError) cur.errors += 1;
|
|
6672
|
+
cur.totalMs += Math.max(0, latencyMs);
|
|
6673
|
+
this.tools.set(name, cur);
|
|
6674
|
+
}
|
|
6675
|
+
/** Record an approval-gate outcome for a tool call. */
|
|
6676
|
+
recordApproval(kind) {
|
|
6677
|
+
this.approvals[kind] += 1;
|
|
6678
|
+
}
|
|
6679
|
+
/** Record backend usage from a completed agent turn. */
|
|
6680
|
+
recordUsage(costUsd, promptTokens, completionTokens) {
|
|
6681
|
+
if (Number.isFinite(costUsd)) this.usage.costUsd += costUsd;
|
|
6682
|
+
if (Number.isFinite(promptTokens)) this.usage.promptTokens += promptTokens;
|
|
6683
|
+
if (Number.isFinite(completionTokens)) this.usage.completionTokens += completionTokens;
|
|
6684
|
+
this.usage.turns += 1;
|
|
6685
|
+
}
|
|
6686
|
+
/** Immutable snapshot for rendering. */
|
|
6687
|
+
snapshot() {
|
|
6688
|
+
const tools = {};
|
|
6689
|
+
let totalToolCalls = 0;
|
|
6690
|
+
for (const [name, s] of this.tools) {
|
|
6691
|
+
tools[name] = { ...s };
|
|
6692
|
+
totalToolCalls += s.count;
|
|
6693
|
+
}
|
|
6694
|
+
return {
|
|
6695
|
+
startedAt: this.startedAt,
|
|
6696
|
+
wallMs: Date.now() - this.startedAt,
|
|
6697
|
+
tools,
|
|
6698
|
+
totalToolCalls,
|
|
6699
|
+
usage: { ...this.usage },
|
|
6700
|
+
approvals: { ...this.approvals }
|
|
6701
|
+
};
|
|
6702
|
+
}
|
|
6703
|
+
/** Reset everything (used by tests and a potential /stats reset). */
|
|
6704
|
+
reset() {
|
|
6705
|
+
this.startedAt = Date.now();
|
|
6706
|
+
this.tools.clear();
|
|
6707
|
+
this.usage = { costUsd: 0, promptTokens: 0, completionTokens: 0, turns: 0 };
|
|
6708
|
+
this.approvals = { approved: 0, denied: 0, autoApproved: 0 };
|
|
6709
|
+
}
|
|
6710
|
+
};
|
|
6711
|
+
singleton = null;
|
|
6712
|
+
}
|
|
6713
|
+
});
|
|
6714
|
+
|
|
6556
6715
|
// ../core/src/agent/ToolExecutor.ts
|
|
6557
6716
|
import { readFile as readFile10 } from "fs/promises";
|
|
6558
6717
|
var APPROVAL_GATED, ToolExecutor;
|
|
@@ -6587,6 +6746,7 @@ var init_ToolExecutor = __esm({
|
|
|
6587
6746
|
init_ShadowService();
|
|
6588
6747
|
init_Hooks();
|
|
6589
6748
|
init_ResourceGovernor();
|
|
6749
|
+
init_SessionStats();
|
|
6590
6750
|
APPROVAL_GATED = /* @__PURE__ */ new Set(["run_command", "bash_command", "bash_background", "edit_file", "write_file", "patch_file", "multi_edit_file", "notebook_edit_cell", "move_file", "delete_file", "open_sub_shell"]);
|
|
6591
6751
|
ToolExecutor = class {
|
|
6592
6752
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -6900,8 +7060,10 @@ ${blocker.stderr || "(empty)"}`,
|
|
|
6900
7060
|
reason: `${toolName} requested by agent in '${this.mode}' mode`
|
|
6901
7061
|
});
|
|
6902
7062
|
if (decision === "no") {
|
|
7063
|
+
getSessionStats().recordApproval("denied");
|
|
6903
7064
|
return { content: `User denied ${toolName} approval.`, isError: true };
|
|
6904
7065
|
}
|
|
7066
|
+
getSessionStats().recordApproval("approved");
|
|
6905
7067
|
if (decision === "always") {
|
|
6906
7068
|
if (this.trustStore) {
|
|
6907
7069
|
this.trustStore.add(cmdKey).catch(() => {
|
|
@@ -6910,6 +7072,8 @@ ${blocker.stderr || "(empty)"}`,
|
|
|
6910
7072
|
this.sessionTrust.add(cmdKey);
|
|
6911
7073
|
}
|
|
6912
7074
|
}
|
|
7075
|
+
} else {
|
|
7076
|
+
getSessionStats().recordApproval("autoApproved");
|
|
6913
7077
|
}
|
|
6914
7078
|
}
|
|
6915
7079
|
if (toolName === "run_command" || toolName === "bash_command" || toolName === "bash_background" || toolName === "edit_file" || toolName === "write_file" || toolName === "patch_file" || toolName === "multi_edit_file" || toolName === "notebook_edit_cell" || toolName === "move_file" || toolName === "delete_file") {
|
|
@@ -6941,10 +7105,20 @@ Please approve the diff in the UI to sync this change locally.`
|
|
|
6941
7105
|
const governor = await getGlobalGovernor();
|
|
6942
7106
|
await governor.acquireTool();
|
|
6943
7107
|
let result;
|
|
7108
|
+
const startedAt = Date.now();
|
|
7109
|
+
let threw = false;
|
|
6944
7110
|
try {
|
|
6945
7111
|
result = await tool.execute(args2, projectRoot);
|
|
7112
|
+
} catch (e) {
|
|
7113
|
+
threw = true;
|
|
7114
|
+
throw e;
|
|
6946
7115
|
} finally {
|
|
6947
7116
|
governor.releaseTool();
|
|
7117
|
+
getSessionStats().recordTool(
|
|
7118
|
+
toolName,
|
|
7119
|
+
Date.now() - startedAt,
|
|
7120
|
+
threw || !!(result && result.isError)
|
|
7121
|
+
);
|
|
6948
7122
|
}
|
|
6949
7123
|
if (this.hooks) {
|
|
6950
7124
|
this.hooks.fire({
|
|
@@ -7153,13 +7327,14 @@ async function loadProjectConfig(cwd = process.cwd()) {
|
|
|
7153
7327
|
const [user, project] = await Promise.all([findUserConfig(), findProjectConfig(cwd)]);
|
|
7154
7328
|
return { user, project, combined: buildCombined(user, project) };
|
|
7155
7329
|
}
|
|
7156
|
-
var FILENAMES, TRUNCATE_AT;
|
|
7330
|
+
var FILENAMES, TRUNCATE_AT, PROJECT_CONFIG_FILENAMES;
|
|
7157
7331
|
var init_ProjectConfig = __esm({
|
|
7158
7332
|
"../core/src/ProjectConfig.ts"() {
|
|
7159
7333
|
"use strict";
|
|
7160
7334
|
init_esm_shims();
|
|
7161
7335
|
FILENAMES = ["MSAPLING.md", "CLAUDE.md", "GEMINI.md", "AGENTS.md"];
|
|
7162
7336
|
TRUNCATE_AT = 32e3;
|
|
7337
|
+
PROJECT_CONFIG_FILENAMES = FILENAMES;
|
|
7163
7338
|
}
|
|
7164
7339
|
});
|
|
7165
7340
|
|
|
@@ -7407,8 +7582,15 @@ var init_Agent = __esm({
|
|
|
7407
7582
|
}
|
|
7408
7583
|
const config = await this.getProjectConfig();
|
|
7409
7584
|
const MAX_WORKER_TURN_DEPTH = 25;
|
|
7585
|
+
let structuredToolResults = false;
|
|
7586
|
+
try {
|
|
7587
|
+
structuredToolResults = await this.client.supportsStructuredToolResults();
|
|
7588
|
+
} catch {
|
|
7589
|
+
structuredToolResults = false;
|
|
7590
|
+
}
|
|
7410
7591
|
const queue = [prompt4];
|
|
7411
7592
|
let rounds = 0;
|
|
7593
|
+
let toolCallSeq = 0;
|
|
7412
7594
|
let streamUsage = null;
|
|
7413
7595
|
while (queue.length > 0 && rounds < MAX_WORKER_TURN_DEPTH) {
|
|
7414
7596
|
if (this.contextBudget.needsCompaction() && queue.length > 0) {
|
|
@@ -7452,14 +7634,25 @@ var init_Agent = __esm({
|
|
|
7452
7634
|
this.contextBudget.reset();
|
|
7453
7635
|
if (compactionSummary) {
|
|
7454
7636
|
const next = queue[0];
|
|
7455
|
-
|
|
7637
|
+
if (typeof next === "string") {
|
|
7638
|
+
queue[0] = `[CONTEXT_SUMMARY]: ${compactionSummary}
|
|
7456
7639
|
|
|
7457
7640
|
${next}`;
|
|
7641
|
+
} else {
|
|
7642
|
+
queue[0] = {
|
|
7643
|
+
...next,
|
|
7644
|
+
prompt: `[CONTEXT_SUMMARY]: ${compactionSummary}
|
|
7645
|
+
|
|
7646
|
+
${next.prompt}`
|
|
7647
|
+
};
|
|
7648
|
+
}
|
|
7458
7649
|
}
|
|
7459
7650
|
continue;
|
|
7460
7651
|
}
|
|
7461
|
-
const
|
|
7652
|
+
const currentItem = queue.shift();
|
|
7462
7653
|
rounds++;
|
|
7654
|
+
const currentPrompt = typeof currentItem === "string" ? currentItem : currentItem.prompt;
|
|
7655
|
+
const currentToolResults = typeof currentItem === "string" ? void 0 : currentItem.toolResults;
|
|
7463
7656
|
const stream = this.chatWithFallback(
|
|
7464
7657
|
{
|
|
7465
7658
|
chat_id: chatId,
|
|
@@ -7468,11 +7661,16 @@ ${next}`;
|
|
|
7468
7661
|
tools: this.executor.getToolSchemas(),
|
|
7469
7662
|
project_root: this.projectRoot,
|
|
7470
7663
|
mode: this.executor.getMode(),
|
|
7664
|
+
// CLI-LOOP-01: carry the concurrently-executed tool_results from the
|
|
7665
|
+
// previous turn as a structured batch. Only set in the capability-
|
|
7666
|
+
// enabled path; absent => backend sees the legacy single-prompt turn.
|
|
7667
|
+
...currentToolResults && currentToolResults.length > 0 ? { tool_results: currentToolResults } : {},
|
|
7471
7668
|
...config.combined ? { project_context: config.combined } : {}
|
|
7472
7669
|
},
|
|
7473
7670
|
chatId,
|
|
7474
7671
|
currentPrompt
|
|
7475
7672
|
);
|
|
7673
|
+
const pendingToolCalls = [];
|
|
7476
7674
|
for await (const chunk of stream) {
|
|
7477
7675
|
if (chunk.content) {
|
|
7478
7676
|
onContent(chunk.content);
|
|
@@ -7489,11 +7687,39 @@ ${next}`;
|
|
|
7489
7687
|
});
|
|
7490
7688
|
}
|
|
7491
7689
|
if (chunk.tool_use) {
|
|
7492
|
-
const
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7690
|
+
const id = chunk.tool_use.id ?? `call_${rounds}_${toolCallSeq++}`;
|
|
7691
|
+
pendingToolCalls.push({ id, name: chunk.tool_use.name, args: chunk.tool_use.args });
|
|
7692
|
+
}
|
|
7693
|
+
}
|
|
7694
|
+
if (pendingToolCalls.length === 0) {
|
|
7695
|
+
continue;
|
|
7696
|
+
}
|
|
7697
|
+
if (structuredToolResults) {
|
|
7698
|
+
const toolResults = await Promise.all(
|
|
7699
|
+
pendingToolCalls.map(async (call) => {
|
|
7700
|
+
try {
|
|
7701
|
+
const result = await this.executor.execute(call.name, call.args, this.projectRoot);
|
|
7702
|
+
const redactedContent = SafetyGuard.redact(result.content);
|
|
7703
|
+
return {
|
|
7704
|
+
tool_use_id: call.id,
|
|
7705
|
+
name: call.name,
|
|
7706
|
+
content: redactedContent,
|
|
7707
|
+
is_error: !!result.isError
|
|
7708
|
+
};
|
|
7709
|
+
} catch (e) {
|
|
7710
|
+
return {
|
|
7711
|
+
tool_use_id: call.id,
|
|
7712
|
+
name: call.name,
|
|
7713
|
+
content: SafetyGuard.redact(`Tool execution failed: ${e?.message ?? String(e)}`),
|
|
7714
|
+
is_error: true
|
|
7715
|
+
};
|
|
7716
|
+
}
|
|
7717
|
+
})
|
|
7718
|
+
);
|
|
7719
|
+
queue.push({ prompt: "", toolResults });
|
|
7720
|
+
} else {
|
|
7721
|
+
for (const call of pendingToolCalls) {
|
|
7722
|
+
const result = await this.executor.execute(call.name, call.args, this.projectRoot);
|
|
7497
7723
|
const redactedContent = SafetyGuard.redact(result.content);
|
|
7498
7724
|
const redactedResult = { ...result, content: redactedContent };
|
|
7499
7725
|
queue.push(`[TOOL_RESULT]: ${JSON.stringify(redactedResult)}`);
|
|
@@ -9125,8 +9351,10 @@ __export(src_exports2, {
|
|
|
9125
9351
|
MultiEditFileTool: () => MultiEditFileTool,
|
|
9126
9352
|
NotebookEditTool: () => NotebookEditTool,
|
|
9127
9353
|
NotebookReadTool: () => NotebookReadTool,
|
|
9354
|
+
PROJECT_CONFIG_FILENAMES: () => PROJECT_CONFIG_FILENAMES,
|
|
9128
9355
|
PatchFileTool: () => PatchFileTool,
|
|
9129
9356
|
ReadBackgroundShellTool: () => ReadBackgroundShellTool,
|
|
9357
|
+
SessionStats: () => SessionStats,
|
|
9130
9358
|
StorageManager: () => StorageManager,
|
|
9131
9359
|
SwarmManager: () => SwarmManager,
|
|
9132
9360
|
TOOL_NAME_ALIASES: () => TOOL_NAME_ALIASES,
|
|
@@ -9138,6 +9366,7 @@ __export(src_exports2, {
|
|
|
9138
9366
|
WebFetchTool: () => WebFetchTool,
|
|
9139
9367
|
WebSearchTool: () => WebSearchTool,
|
|
9140
9368
|
WriteFileTool: () => WriteFileTool,
|
|
9369
|
+
_resetSessionStatsSingleton: () => _resetSessionStatsSingleton,
|
|
9141
9370
|
_setBackupDirOverride: () => _setBackupDirOverride,
|
|
9142
9371
|
backupDir: () => backupDir,
|
|
9143
9372
|
buildCompactionPrompt: () => buildCompactionPrompt,
|
|
@@ -9151,6 +9380,7 @@ __export(src_exports2, {
|
|
|
9151
9380
|
formatNotebookHeader: () => formatNotebookHeader,
|
|
9152
9381
|
formatTodos: () => formatTodos,
|
|
9153
9382
|
getOrCreateJournalKey: () => getOrCreateJournalKey,
|
|
9383
|
+
getSessionStats: () => getSessionStats,
|
|
9154
9384
|
initJournalEncryption: () => initJournalEncryption,
|
|
9155
9385
|
listCheckpoints: () => listCheckpoints,
|
|
9156
9386
|
loadNamedAgents: () => loadNamedAgents,
|
|
@@ -9162,6 +9392,7 @@ __export(src_exports2, {
|
|
|
9162
9392
|
parseAgentFile: () => parseAgentFile,
|
|
9163
9393
|
parseToolsValue: () => parseToolsValue,
|
|
9164
9394
|
recordBackup: () => recordBackup,
|
|
9395
|
+
renderSessionStats: () => renderSessionStats,
|
|
9165
9396
|
restoreCheckpoint: () => restoreCheckpoint,
|
|
9166
9397
|
takeSnapshot: () => takeSnapshot
|
|
9167
9398
|
});
|
|
@@ -9180,6 +9411,7 @@ var init_src3 = __esm({
|
|
|
9180
9411
|
init_Storage();
|
|
9181
9412
|
init_journalCrypto();
|
|
9182
9413
|
init_Mutex();
|
|
9414
|
+
init_SessionStats();
|
|
9183
9415
|
init_ProjectConfig();
|
|
9184
9416
|
init_Settings();
|
|
9185
9417
|
init_client();
|
|
@@ -11255,40 +11487,405 @@ var init_compact = __esm({
|
|
|
11255
11487
|
}
|
|
11256
11488
|
});
|
|
11257
11489
|
|
|
11490
|
+
// src/runtime/codebaseScan.ts
|
|
11491
|
+
import { existsSync as existsSync23, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync7 } from "fs";
|
|
11492
|
+
import { join as join27, relative as relative16, sep as sep4, basename as basename3 } from "path";
|
|
11493
|
+
function parseGitignore(content) {
|
|
11494
|
+
const patterns = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l && !l.startsWith("#") && !l.startsWith("!"));
|
|
11495
|
+
const matchers = patterns.map((raw) => {
|
|
11496
|
+
let p = raw;
|
|
11497
|
+
const dirOnly = p.endsWith("/");
|
|
11498
|
+
if (dirOnly) p = p.slice(0, -1);
|
|
11499
|
+
const anchored = p.startsWith("/");
|
|
11500
|
+
if (anchored) p = p.slice(1);
|
|
11501
|
+
const re = new RegExp(
|
|
11502
|
+
"^" + p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, "[^/]*").replace(//g, ".*").replace(/\?/g, "[^/]") + "$"
|
|
11503
|
+
);
|
|
11504
|
+
return { re, dirOnly, anchored, base: p };
|
|
11505
|
+
});
|
|
11506
|
+
return (relPath, isDir) => {
|
|
11507
|
+
const posix = relPath.split(sep4).join("/");
|
|
11508
|
+
for (const m of matchers) {
|
|
11509
|
+
if (m.dirOnly && !isDir) continue;
|
|
11510
|
+
if (m.anchored) {
|
|
11511
|
+
if (m.re.test(posix)) return true;
|
|
11512
|
+
} else {
|
|
11513
|
+
if (m.re.test(posix)) return true;
|
|
11514
|
+
if (m.re.test(basename3(posix))) return true;
|
|
11515
|
+
const segs = posix.split("/");
|
|
11516
|
+
for (let i = 0; i < segs.length; i++) {
|
|
11517
|
+
if (m.re.test(segs[i])) return true;
|
|
11518
|
+
}
|
|
11519
|
+
}
|
|
11520
|
+
}
|
|
11521
|
+
return false;
|
|
11522
|
+
};
|
|
11523
|
+
}
|
|
11524
|
+
function loadIgnore(root) {
|
|
11525
|
+
try {
|
|
11526
|
+
const gi = join27(root, ".gitignore");
|
|
11527
|
+
if (existsSync23(gi)) {
|
|
11528
|
+
return parseGitignore(readFileSync4(gi, "utf8"));
|
|
11529
|
+
}
|
|
11530
|
+
} catch {
|
|
11531
|
+
}
|
|
11532
|
+
return () => false;
|
|
11533
|
+
}
|
|
11534
|
+
function readJsonSafe(path2) {
|
|
11535
|
+
try {
|
|
11536
|
+
return JSON.parse(readFileSync4(path2, "utf8"));
|
|
11537
|
+
} catch {
|
|
11538
|
+
return null;
|
|
11539
|
+
}
|
|
11540
|
+
}
|
|
11541
|
+
function detectStacks(root) {
|
|
11542
|
+
const found = [];
|
|
11543
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11544
|
+
for (const probe of STACK_PROBES) {
|
|
11545
|
+
const hit = probe.detect(root);
|
|
11546
|
+
if (hit && !seen.has(hit.name)) {
|
|
11547
|
+
seen.add(hit.name);
|
|
11548
|
+
found.push(hit);
|
|
11549
|
+
}
|
|
11550
|
+
}
|
|
11551
|
+
return found;
|
|
11552
|
+
}
|
|
11553
|
+
function inferCommands(root, stacks) {
|
|
11554
|
+
const cmds = { extra: {} };
|
|
11555
|
+
for (const stack of stacks) {
|
|
11556
|
+
if (stack.manifest === "package.json") {
|
|
11557
|
+
const pkg = readJsonSafe(join27(root, "package.json"));
|
|
11558
|
+
const scripts = pkg?.scripts ?? {};
|
|
11559
|
+
const pm = stack.packageManager ?? "npm";
|
|
11560
|
+
const runScript = (name) => pm === "npm" ? `npm run ${name}` : `${pm} run ${name}`;
|
|
11561
|
+
if (scripts.build) cmds.build = runScript("build");
|
|
11562
|
+
if (scripts.test) cmds.test = pm === "npm" ? "npm test" : `${pm} test`;
|
|
11563
|
+
if (scripts.start) cmds.run = pm === "npm" ? "npm start" : `${pm} start`;
|
|
11564
|
+
else if (scripts.dev) cmds.run = runScript("dev");
|
|
11565
|
+
if (scripts.lint) cmds.lint = runScript("lint");
|
|
11566
|
+
for (const name of ["typecheck", "format", "e2e", "coverage"]) {
|
|
11567
|
+
if (scripts[name]) cmds.extra[name] = runScript(name);
|
|
11568
|
+
}
|
|
11569
|
+
} else if (stack.name === "Python") {
|
|
11570
|
+
cmds.test = cmds.test ?? "pytest";
|
|
11571
|
+
if (stack.packageManager === "poetry") cmds.run = cmds.run ?? "poetry run python -m <module>";
|
|
11572
|
+
} else if (stack.name === "Go") {
|
|
11573
|
+
cmds.build = cmds.build ?? "go build ./...";
|
|
11574
|
+
cmds.test = cmds.test ?? "go test ./...";
|
|
11575
|
+
cmds.run = cmds.run ?? "go run .";
|
|
11576
|
+
} else if (stack.name === "Rust") {
|
|
11577
|
+
cmds.build = cmds.build ?? "cargo build";
|
|
11578
|
+
cmds.test = cmds.test ?? "cargo test";
|
|
11579
|
+
cmds.run = cmds.run ?? "cargo run";
|
|
11580
|
+
} else if (stack.name === "Java / Maven") {
|
|
11581
|
+
cmds.build = cmds.build ?? "mvn package";
|
|
11582
|
+
cmds.test = cmds.test ?? "mvn test";
|
|
11583
|
+
} else if (stack.name === "Java / Kotlin (Gradle)") {
|
|
11584
|
+
cmds.build = cmds.build ?? "gradle build";
|
|
11585
|
+
cmds.test = cmds.test ?? "gradle test";
|
|
11586
|
+
} else if (stack.name === "Ruby") {
|
|
11587
|
+
cmds.test = cmds.test ?? "bundle exec rspec";
|
|
11588
|
+
} else if (stack.name === "PHP") {
|
|
11589
|
+
cmds.test = cmds.test ?? "composer test";
|
|
11590
|
+
}
|
|
11591
|
+
}
|
|
11592
|
+
return cmds;
|
|
11593
|
+
}
|
|
11594
|
+
function detectEntryPoints(root, stacks) {
|
|
11595
|
+
const out = [];
|
|
11596
|
+
for (const stack of stacks) {
|
|
11597
|
+
if (stack.manifest === "package.json") {
|
|
11598
|
+
const pkg = readJsonSafe(join27(root, "package.json"));
|
|
11599
|
+
if (pkg?.main && existsSync23(join27(root, pkg.main))) out.push(pkg.main);
|
|
11600
|
+
if (typeof pkg?.bin === "string" && existsSync23(join27(root, pkg.bin))) out.push(pkg.bin);
|
|
11601
|
+
else if (pkg?.bin && typeof pkg.bin === "object") {
|
|
11602
|
+
for (const v of Object.values(pkg.bin)) {
|
|
11603
|
+
if (typeof v === "string" && existsSync23(join27(root, v))) out.push(v);
|
|
11604
|
+
}
|
|
11605
|
+
}
|
|
11606
|
+
}
|
|
11607
|
+
}
|
|
11608
|
+
for (const c of ENTRY_CANDIDATES) {
|
|
11609
|
+
if (existsSync23(join27(root, c)) && !out.includes(c)) out.push(c);
|
|
11610
|
+
}
|
|
11611
|
+
return out.slice(0, 8);
|
|
11612
|
+
}
|
|
11613
|
+
function scanCodebase(root, opts = {}) {
|
|
11614
|
+
const maxFiles = opts.maxFiles ?? DEFAULT_MAX_FILES;
|
|
11615
|
+
const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
11616
|
+
const ignored = loadIgnore(root);
|
|
11617
|
+
let fileCount = 0;
|
|
11618
|
+
let dirCount = 0;
|
|
11619
|
+
let truncated = false;
|
|
11620
|
+
const keyDirs = /* @__PURE__ */ new Set();
|
|
11621
|
+
const walk = (dir, depth) => {
|
|
11622
|
+
if (truncated || depth > maxDepth) return;
|
|
11623
|
+
let entries;
|
|
11624
|
+
try {
|
|
11625
|
+
entries = readdirSync3(dir);
|
|
11626
|
+
} catch {
|
|
11627
|
+
return;
|
|
11628
|
+
}
|
|
11629
|
+
for (const name of entries) {
|
|
11630
|
+
if (fileCount >= maxFiles) {
|
|
11631
|
+
truncated = true;
|
|
11632
|
+
return;
|
|
11633
|
+
}
|
|
11634
|
+
const abs = join27(dir, name);
|
|
11635
|
+
let isDir = false;
|
|
11636
|
+
try {
|
|
11637
|
+
isDir = statSync7(abs).isDirectory();
|
|
11638
|
+
} catch {
|
|
11639
|
+
continue;
|
|
11640
|
+
}
|
|
11641
|
+
const rel = relative16(root, abs);
|
|
11642
|
+
if (isDir) {
|
|
11643
|
+
if (ALWAYS_IGNORE_DIRS.has(name)) continue;
|
|
11644
|
+
if (name.startsWith(".") && depth >= 1) continue;
|
|
11645
|
+
if (ignored(rel, true)) continue;
|
|
11646
|
+
dirCount++;
|
|
11647
|
+
if (depth === 0) keyDirs.add(rel.split(sep4).join("/"));
|
|
11648
|
+
walk(abs, depth + 1);
|
|
11649
|
+
} else {
|
|
11650
|
+
if (ignored(rel, false)) continue;
|
|
11651
|
+
fileCount++;
|
|
11652
|
+
}
|
|
11653
|
+
}
|
|
11654
|
+
};
|
|
11655
|
+
walk(root, 0);
|
|
11656
|
+
const stacks = detectStacks(root);
|
|
11657
|
+
const commands2 = inferCommands(root, stacks);
|
|
11658
|
+
const entryPoints = detectEntryPoints(root, stacks);
|
|
11659
|
+
return {
|
|
11660
|
+
root,
|
|
11661
|
+
stacks,
|
|
11662
|
+
keyDirs: Array.from(keyDirs).sort(),
|
|
11663
|
+
entryPoints,
|
|
11664
|
+
commands: commands2,
|
|
11665
|
+
fileCount,
|
|
11666
|
+
dirCount,
|
|
11667
|
+
truncated
|
|
11668
|
+
};
|
|
11669
|
+
}
|
|
11670
|
+
function renderMemoryFile(scan, title = "Project Memory") {
|
|
11671
|
+
const lines = [];
|
|
11672
|
+
lines.push(`# ${title}`);
|
|
11673
|
+
lines.push("");
|
|
11674
|
+
lines.push(
|
|
11675
|
+
"> Auto-generated by `/init` (deep codebase scan). Edit freely \u2014 re-running `/init` refreshes the scanned sections but preserves this file if you have customized it."
|
|
11676
|
+
);
|
|
11677
|
+
lines.push("");
|
|
11678
|
+
lines.push("## Stack");
|
|
11679
|
+
if (scan.stacks.length === 0) {
|
|
11680
|
+
lines.push("- No recognized manifest found (no package.json / pyproject.toml / go.mod / Cargo.toml / etc.).");
|
|
11681
|
+
} else {
|
|
11682
|
+
for (const s of scan.stacks) {
|
|
11683
|
+
const pm = s.packageManager ? ` (${s.packageManager})` : "";
|
|
11684
|
+
lines.push(`- **${s.name}**${pm} \u2014 detected via \`${s.manifest}\``);
|
|
11685
|
+
}
|
|
11686
|
+
}
|
|
11687
|
+
lines.push("");
|
|
11688
|
+
lines.push("## Commands");
|
|
11689
|
+
const c = scan.commands;
|
|
11690
|
+
const cmdRows = [
|
|
11691
|
+
["Build", c.build],
|
|
11692
|
+
["Test", c.test],
|
|
11693
|
+
["Run", c.run],
|
|
11694
|
+
["Lint", c.lint]
|
|
11695
|
+
];
|
|
11696
|
+
const anyCmd = cmdRows.some(([, v]) => v) || Object.keys(c.extra).length > 0;
|
|
11697
|
+
if (!anyCmd) {
|
|
11698
|
+
lines.push("- No build/test/run commands could be inferred from the manifests.");
|
|
11699
|
+
} else {
|
|
11700
|
+
for (const [label, val] of cmdRows) {
|
|
11701
|
+
if (val) lines.push(`- **${label}:** \`${val}\``);
|
|
11702
|
+
}
|
|
11703
|
+
for (const [name, val] of Object.entries(c.extra)) {
|
|
11704
|
+
lines.push(`- **${name}:** \`${val}\``);
|
|
11705
|
+
}
|
|
11706
|
+
}
|
|
11707
|
+
lines.push("");
|
|
11708
|
+
lines.push("## Structure");
|
|
11709
|
+
lines.push(
|
|
11710
|
+
`- ${scan.dirCount} directories, ${scan.fileCount} files scanned${scan.truncated ? " (scan capped \u2014 large repo)" : ""}.`
|
|
11711
|
+
);
|
|
11712
|
+
if (scan.keyDirs.length > 0) {
|
|
11713
|
+
lines.push("- Key directories:");
|
|
11714
|
+
for (const d of scan.keyDirs) lines.push(` - \`${d}/\``);
|
|
11715
|
+
}
|
|
11716
|
+
lines.push("");
|
|
11717
|
+
lines.push("## Entry points");
|
|
11718
|
+
if (scan.entryPoints.length === 0) {
|
|
11719
|
+
lines.push("- No obvious entry point detected.");
|
|
11720
|
+
} else {
|
|
11721
|
+
for (const e of scan.entryPoints) lines.push(`- \`${e}\``);
|
|
11722
|
+
}
|
|
11723
|
+
lines.push("");
|
|
11724
|
+
lines.push("## Guidelines");
|
|
11725
|
+
lines.push("- Follow the existing code style and conventions in this repository.");
|
|
11726
|
+
lines.push("- Run the test command above before considering a change complete.");
|
|
11727
|
+
lines.push("");
|
|
11728
|
+
return lines.join("\n");
|
|
11729
|
+
}
|
|
11730
|
+
var DEFAULT_MAX_FILES, DEFAULT_MAX_DEPTH, ALWAYS_IGNORE_DIRS, STACK_PROBES, ENTRY_CANDIDATES;
|
|
11731
|
+
var init_codebaseScan = __esm({
|
|
11732
|
+
"src/runtime/codebaseScan.ts"() {
|
|
11733
|
+
"use strict";
|
|
11734
|
+
init_esm_shims();
|
|
11735
|
+
DEFAULT_MAX_FILES = 5e3;
|
|
11736
|
+
DEFAULT_MAX_DEPTH = 6;
|
|
11737
|
+
ALWAYS_IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
11738
|
+
"node_modules",
|
|
11739
|
+
".git",
|
|
11740
|
+
".hg",
|
|
11741
|
+
".svn",
|
|
11742
|
+
"dist",
|
|
11743
|
+
"build",
|
|
11744
|
+
"out",
|
|
11745
|
+
"target",
|
|
11746
|
+
".next",
|
|
11747
|
+
".nuxt",
|
|
11748
|
+
".cache",
|
|
11749
|
+
"coverage",
|
|
11750
|
+
".venv",
|
|
11751
|
+
"venv",
|
|
11752
|
+
"__pycache__",
|
|
11753
|
+
".idea",
|
|
11754
|
+
".vscode",
|
|
11755
|
+
"vendor",
|
|
11756
|
+
".bun",
|
|
11757
|
+
".turbo"
|
|
11758
|
+
]);
|
|
11759
|
+
STACK_PROBES = [
|
|
11760
|
+
{
|
|
11761
|
+
manifest: "package.json",
|
|
11762
|
+
detect: (root) => {
|
|
11763
|
+
const path2 = join27(root, "package.json");
|
|
11764
|
+
if (!existsSync23(path2)) return null;
|
|
11765
|
+
const pkg = readJsonSafe(path2);
|
|
11766
|
+
let pm = "npm";
|
|
11767
|
+
if (existsSync23(join27(root, "bun.lock")) || existsSync23(join27(root, "bun.lockb"))) pm = "bun";
|
|
11768
|
+
else if (existsSync23(join27(root, "pnpm-lock.yaml"))) pm = "pnpm";
|
|
11769
|
+
else if (existsSync23(join27(root, "yarn.lock"))) pm = "yarn";
|
|
11770
|
+
const hasTs = existsSync23(join27(root, "tsconfig.json")) || !!(pkg?.devDependencies?.typescript || pkg?.dependencies?.typescript);
|
|
11771
|
+
return {
|
|
11772
|
+
name: hasTs ? "Node.js / TypeScript" : "Node.js / JavaScript",
|
|
11773
|
+
manifest: "package.json",
|
|
11774
|
+
packageManager: pm
|
|
11775
|
+
};
|
|
11776
|
+
}
|
|
11777
|
+
},
|
|
11778
|
+
{
|
|
11779
|
+
manifest: "pyproject.toml",
|
|
11780
|
+
detect: (root) => {
|
|
11781
|
+
if (!existsSync23(join27(root, "pyproject.toml"))) return null;
|
|
11782
|
+
let pm = "pip";
|
|
11783
|
+
if (existsSync23(join27(root, "poetry.lock"))) pm = "poetry";
|
|
11784
|
+
else if (existsSync23(join27(root, "uv.lock"))) pm = "uv";
|
|
11785
|
+
return { name: "Python", manifest: "pyproject.toml", packageManager: pm };
|
|
11786
|
+
}
|
|
11787
|
+
},
|
|
11788
|
+
{
|
|
11789
|
+
manifest: "requirements.txt",
|
|
11790
|
+
detect: (root) => {
|
|
11791
|
+
if (!existsSync23(join27(root, "requirements.txt"))) return null;
|
|
11792
|
+
return { name: "Python", manifest: "requirements.txt", packageManager: "pip" };
|
|
11793
|
+
}
|
|
11794
|
+
},
|
|
11795
|
+
{
|
|
11796
|
+
manifest: "go.mod",
|
|
11797
|
+
detect: (root) => existsSync23(join27(root, "go.mod")) ? { name: "Go", manifest: "go.mod", packageManager: "go" } : null
|
|
11798
|
+
},
|
|
11799
|
+
{
|
|
11800
|
+
manifest: "Cargo.toml",
|
|
11801
|
+
detect: (root) => existsSync23(join27(root, "Cargo.toml")) ? { name: "Rust", manifest: "Cargo.toml", packageManager: "cargo" } : null
|
|
11802
|
+
},
|
|
11803
|
+
{
|
|
11804
|
+
manifest: "pom.xml",
|
|
11805
|
+
detect: (root) => existsSync23(join27(root, "pom.xml")) ? { name: "Java / Maven", manifest: "pom.xml", packageManager: "maven" } : null
|
|
11806
|
+
},
|
|
11807
|
+
{
|
|
11808
|
+
manifest: "build.gradle",
|
|
11809
|
+
detect: (root) => existsSync23(join27(root, "build.gradle")) || existsSync23(join27(root, "build.gradle.kts")) ? { name: "Java / Kotlin (Gradle)", manifest: "build.gradle", packageManager: "gradle" } : null
|
|
11810
|
+
},
|
|
11811
|
+
{
|
|
11812
|
+
manifest: "Gemfile",
|
|
11813
|
+
detect: (root) => existsSync23(join27(root, "Gemfile")) ? { name: "Ruby", manifest: "Gemfile", packageManager: "bundler" } : null
|
|
11814
|
+
},
|
|
11815
|
+
{
|
|
11816
|
+
manifest: "composer.json",
|
|
11817
|
+
detect: (root) => existsSync23(join27(root, "composer.json")) ? { name: "PHP", manifest: "composer.json", packageManager: "composer" } : null
|
|
11818
|
+
}
|
|
11819
|
+
];
|
|
11820
|
+
ENTRY_CANDIDATES = [
|
|
11821
|
+
"src/index.ts",
|
|
11822
|
+
"src/index.tsx",
|
|
11823
|
+
"src/index.js",
|
|
11824
|
+
"src/main.ts",
|
|
11825
|
+
"src/main.tsx",
|
|
11826
|
+
"src/main.py",
|
|
11827
|
+
"src/main.rs",
|
|
11828
|
+
"index.ts",
|
|
11829
|
+
"index.js",
|
|
11830
|
+
"main.py",
|
|
11831
|
+
"main.go",
|
|
11832
|
+
"app.py",
|
|
11833
|
+
"manage.py",
|
|
11834
|
+
"cmd/main.go",
|
|
11835
|
+
"src/main/java"
|
|
11836
|
+
];
|
|
11837
|
+
}
|
|
11838
|
+
});
|
|
11839
|
+
|
|
11258
11840
|
// src/commands/init.ts
|
|
11259
|
-
import { join as
|
|
11260
|
-
import { existsSync as
|
|
11841
|
+
import { join as join28 } from "path";
|
|
11842
|
+
import { existsSync as existsSync24 } from "fs";
|
|
11261
11843
|
import { writeFile as writeFile13 } from "fs/promises";
|
|
11844
|
+
function resolveTargetFile(cwd) {
|
|
11845
|
+
for (const filename2 of PROJECT_CONFIG_FILENAMES) {
|
|
11846
|
+
const path2 = join28(cwd, filename2);
|
|
11847
|
+
if (existsSync24(path2)) return { path: path2, filename: filename2, existed: true };
|
|
11848
|
+
}
|
|
11849
|
+
const filename = PROJECT_CONFIG_FILENAMES[0];
|
|
11850
|
+
return { path: join28(cwd, filename), filename, existed: false };
|
|
11851
|
+
}
|
|
11262
11852
|
var initCommand;
|
|
11263
11853
|
var init_init = __esm({
|
|
11264
11854
|
"src/commands/init.ts"() {
|
|
11265
11855
|
"use strict";
|
|
11266
11856
|
init_esm_shims();
|
|
11857
|
+
init_src3();
|
|
11858
|
+
init_codebaseScan();
|
|
11267
11859
|
initCommand = {
|
|
11268
11860
|
name: "init",
|
|
11269
|
-
|
|
11861
|
+
args: "[--force]",
|
|
11862
|
+
description: "Scan the codebase (stack, commands, structure, entry points) and write/refresh a project-memory file (MSAPLING.md/CLAUDE.md/\u2026)",
|
|
11270
11863
|
category: "project",
|
|
11271
11864
|
handler: async (args2, context) => {
|
|
11272
11865
|
try {
|
|
11273
11866
|
const cwd = process.cwd();
|
|
11274
|
-
const
|
|
11275
|
-
|
|
11276
|
-
|
|
11867
|
+
const force = args2.includes("--force");
|
|
11868
|
+
const target = resolveTargetFile(cwd);
|
|
11869
|
+
if (target.existed && !force) {
|
|
11870
|
+
context.addMessage(
|
|
11871
|
+
"error",
|
|
11872
|
+
`${target.filename} already exists. Re-run \`/init --force\` to refresh it from a fresh codebase scan.`
|
|
11873
|
+
);
|
|
11277
11874
|
return;
|
|
11278
11875
|
}
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11876
|
+
context.addMessage("system", `Scanning codebase at ${cwd} ...`);
|
|
11877
|
+
const scan = scanCodebase(cwd);
|
|
11878
|
+
const title = target.filename === "MSAPLING.md" ? "MSapling Project Memory" : "Project Memory";
|
|
11879
|
+
const content = renderMemoryFile(scan, title);
|
|
11880
|
+
await writeFile13(target.path, content, "utf8");
|
|
11881
|
+
const stackSummary = scan.stacks.length > 0 ? scan.stacks.map((s) => s.name).join(", ") : "unknown stack";
|
|
11882
|
+
context.addMessage(
|
|
11883
|
+
"system",
|
|
11884
|
+
`${target.existed ? "Refreshed" : "Created"} ${target.filename} at ${target.path}
|
|
11885
|
+
Stack: ${stackSummary}
|
|
11886
|
+
Scanned: ${scan.dirCount} dirs, ${scan.fileCount} files${scan.truncated ? " (capped)" : ""}
|
|
11887
|
+
Entry points: ${scan.entryPoints.length > 0 ? scan.entryPoints.join(", ") : "none detected"}`
|
|
11888
|
+
);
|
|
11292
11889
|
} catch (e) {
|
|
11293
11890
|
context.addMessage("error", `Failed to initialize project: ${e.message}`);
|
|
11294
11891
|
}
|
|
@@ -11298,7 +11895,7 @@ var init_init = __esm({
|
|
|
11298
11895
|
});
|
|
11299
11896
|
|
|
11300
11897
|
// src/commands/review.ts
|
|
11301
|
-
import { existsSync as
|
|
11898
|
+
import { existsSync as existsSync25 } from "fs";
|
|
11302
11899
|
import { readFile as readFile21 } from "fs/promises";
|
|
11303
11900
|
var reviewCommand;
|
|
11304
11901
|
var init_review = __esm({
|
|
@@ -11318,7 +11915,7 @@ var init_review = __esm({
|
|
|
11318
11915
|
}
|
|
11319
11916
|
let content = "";
|
|
11320
11917
|
try {
|
|
11321
|
-
if (
|
|
11918
|
+
if (existsSync25(target)) {
|
|
11322
11919
|
content = await readFile21(target, "utf8");
|
|
11323
11920
|
} else {
|
|
11324
11921
|
content = `Review target: ${target}`;
|
|
@@ -11412,15 +12009,15 @@ var init_swarm = __esm({
|
|
|
11412
12009
|
|
|
11413
12010
|
// src/commands/recipe.ts
|
|
11414
12011
|
import { parse as parseYaml } from "yaml";
|
|
11415
|
-
import { existsSync as
|
|
12012
|
+
import { existsSync as existsSync26 } from "fs";
|
|
11416
12013
|
import { readFile as readFile22 } from "fs/promises";
|
|
11417
|
-
import { join as
|
|
12014
|
+
import { join as join29 } from "path";
|
|
11418
12015
|
function findRecipe(name, cwd) {
|
|
11419
12016
|
for (const dir of RECIPE_DIRS) {
|
|
11420
12017
|
for (const suffix of NAME_SUFFIXES) {
|
|
11421
12018
|
for (const ext of FILE_EXTS) {
|
|
11422
|
-
const p =
|
|
11423
|
-
if (
|
|
12019
|
+
const p = join29(cwd, dir, `${name}${suffix}${ext}`);
|
|
12020
|
+
if (existsSync26(p)) return p;
|
|
11424
12021
|
}
|
|
11425
12022
|
}
|
|
11426
12023
|
}
|
|
@@ -11533,13 +12130,13 @@ ${rendered}` : rendered;
|
|
|
11533
12130
|
});
|
|
11534
12131
|
|
|
11535
12132
|
// src/commands/skill.ts
|
|
11536
|
-
import { existsSync as
|
|
12133
|
+
import { existsSync as existsSync27, readdirSync as readdirSync4, statSync as statSync8 } from "fs";
|
|
11537
12134
|
import { readFile as readFile23 } from "fs/promises";
|
|
11538
|
-
import { join as
|
|
12135
|
+
import { join as join30, resolve as resolve16 } from "path";
|
|
11539
12136
|
function findSkillsRoot(cwd) {
|
|
11540
12137
|
for (const candidate of SKILLS_DIRS) {
|
|
11541
12138
|
const full = resolve16(cwd, candidate);
|
|
11542
|
-
if (
|
|
12139
|
+
if (existsSync27(full) && statSync8(full).isDirectory()) return full;
|
|
11543
12140
|
}
|
|
11544
12141
|
return null;
|
|
11545
12142
|
}
|
|
@@ -11547,28 +12144,28 @@ function listAllSkills(root) {
|
|
|
11547
12144
|
const out = [];
|
|
11548
12145
|
let domains;
|
|
11549
12146
|
try {
|
|
11550
|
-
domains =
|
|
12147
|
+
domains = readdirSync4(root);
|
|
11551
12148
|
} catch {
|
|
11552
12149
|
return out;
|
|
11553
12150
|
}
|
|
11554
12151
|
for (const domain of domains) {
|
|
11555
|
-
const dir =
|
|
12152
|
+
const dir = join30(root, domain);
|
|
11556
12153
|
let s;
|
|
11557
12154
|
try {
|
|
11558
|
-
s =
|
|
12155
|
+
s = statSync8(dir);
|
|
11559
12156
|
} catch {
|
|
11560
12157
|
continue;
|
|
11561
12158
|
}
|
|
11562
12159
|
if (!s.isDirectory()) continue;
|
|
11563
12160
|
let files;
|
|
11564
12161
|
try {
|
|
11565
|
-
files =
|
|
12162
|
+
files = readdirSync4(dir);
|
|
11566
12163
|
} catch {
|
|
11567
12164
|
continue;
|
|
11568
12165
|
}
|
|
11569
12166
|
for (const f of files) {
|
|
11570
12167
|
if (!f.endsWith(".md")) continue;
|
|
11571
|
-
out.push({ domain, name: f.slice(0, -3), path:
|
|
12168
|
+
out.push({ domain, name: f.slice(0, -3), path: join30(dir, f) });
|
|
11572
12169
|
}
|
|
11573
12170
|
}
|
|
11574
12171
|
return out.sort(
|
|
@@ -11658,7 +12255,7 @@ ${prompt4}`;
|
|
|
11658
12255
|
|
|
11659
12256
|
// src/commands/benchmark.ts
|
|
11660
12257
|
import { homedir as homedir16 } from "os";
|
|
11661
|
-
import { join as
|
|
12258
|
+
import { join as join31 } from "path";
|
|
11662
12259
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11663
12260
|
import * as fs2 from "fs";
|
|
11664
12261
|
function parseArgs(args2) {
|
|
@@ -11675,7 +12272,7 @@ function parseArgs(args2) {
|
|
|
11675
12272
|
}
|
|
11676
12273
|
function formatTable(results) {
|
|
11677
12274
|
const header = `${"Model".padEnd(40)} ${"TTFT(ms)".padStart(9)} ${"TPS".padStart(7)} ${"Tokens".padStart(8)} ${"Cost($)".padStart(9)}`;
|
|
11678
|
-
const
|
|
12275
|
+
const sep5 = "-".repeat(header.length);
|
|
11679
12276
|
const rows = results.map((r) => {
|
|
11680
12277
|
const model = r.model.slice(0, 39).padEnd(40);
|
|
11681
12278
|
const ttft = r.ttft_ms != null ? r.ttft_ms.toFixed(0).padStart(9) : " - ";
|
|
@@ -11685,7 +12282,7 @@ function formatTable(results) {
|
|
|
11685
12282
|
const err = r.error ? ` \u26A0 ${r.error}` : "";
|
|
11686
12283
|
return `${model} ${ttft} ${tps} ${tok} ${cost}${err}`;
|
|
11687
12284
|
});
|
|
11688
|
-
return [
|
|
12285
|
+
return [sep5, header, sep5, ...rows, sep5].join("\n");
|
|
11689
12286
|
}
|
|
11690
12287
|
var DEFAULT_PROMPTS, benchmarkCommand;
|
|
11691
12288
|
var init_benchmark = __esm({
|
|
@@ -11778,10 +12375,10 @@ HW at start: ${hw.cores}-core ${hw.platform} | CPU ${hw.cpuPct}% | RAM ${hw.ramP
|
|
|
11778
12375
|
`[HW at run time: CPU ${hwAtEnd.cpuPct}% / RAM ${hwAtEnd.ramPct}% | ${hw.ramGiB} GiB RAM, ${hw.cores} cores]`
|
|
11779
12376
|
);
|
|
11780
12377
|
try {
|
|
11781
|
-
const dir =
|
|
12378
|
+
const dir = join31(homedir16(), ".msapling", "benchmarks");
|
|
11782
12379
|
mkdirSync5(dir, { recursive: true });
|
|
11783
12380
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
11784
|
-
const file =
|
|
12381
|
+
const file = join31(dir, `${ts}.json`);
|
|
11785
12382
|
const run = {
|
|
11786
12383
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11787
12384
|
rounds,
|
|
@@ -12027,22 +12624,22 @@ var init_theme = __esm({
|
|
|
12027
12624
|
});
|
|
12028
12625
|
|
|
12029
12626
|
// src/commands/theme.ts
|
|
12030
|
-
import { join as
|
|
12627
|
+
import { join as join32 } from "path";
|
|
12031
12628
|
import { homedir as homedir17 } from "os";
|
|
12032
|
-
import { existsSync as
|
|
12629
|
+
import { existsSync as existsSync28 } from "fs";
|
|
12033
12630
|
import { readFile as readFile24, writeFile as writeFile14 } from "fs/promises";
|
|
12034
12631
|
async function persistTheme(storage, themeName) {
|
|
12035
|
-
const settingsPath =
|
|
12632
|
+
const settingsPath = join32(homedir17(), ".msapling", "settings.json");
|
|
12036
12633
|
let existing = {};
|
|
12037
12634
|
try {
|
|
12038
|
-
if (
|
|
12635
|
+
if (existsSync28(settingsPath)) {
|
|
12039
12636
|
const text = await readFile24(settingsPath, "utf8");
|
|
12040
12637
|
if (text.trim()) existing = JSON.parse(text);
|
|
12041
12638
|
}
|
|
12042
12639
|
} catch {
|
|
12043
12640
|
}
|
|
12044
12641
|
existing["theme"] = themeName;
|
|
12045
|
-
ensureConfigDir(
|
|
12642
|
+
ensureConfigDir(join32(homedir17(), ".msapling"));
|
|
12046
12643
|
await writeFile14(settingsPath, JSON.stringify(existing, null, 2), "utf8");
|
|
12047
12644
|
}
|
|
12048
12645
|
var VALID_THEMES, themeCommand;
|
|
@@ -12114,7 +12711,7 @@ var init_version = __esm({
|
|
|
12114
12711
|
description: "Show version information for CLI and core packages",
|
|
12115
12712
|
category: "debug",
|
|
12116
12713
|
handler: async (_args, context) => {
|
|
12117
|
-
const cliVersion = true ? "2.3.6-beta.
|
|
12714
|
+
const cliVersion = true ? "2.3.6-beta.49" : "(dev)";
|
|
12118
12715
|
const coreVersion = true ? "2.3.6-beta.43" : "(dev)";
|
|
12119
12716
|
const runtime = process.version;
|
|
12120
12717
|
context.addMessage("system", "MSapling Version Info");
|
|
@@ -12123,7 +12720,7 @@ var init_version = __esm({
|
|
|
12123
12720
|
context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
|
|
12124
12721
|
context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
|
|
12125
12722
|
try {
|
|
12126
|
-
const ts = "2026-06-20T08:
|
|
12723
|
+
const ts = "2026-06-20T08:28:18.242Z";
|
|
12127
12724
|
if (ts && ts !== "__BUILD_TIMESTAMP__") {
|
|
12128
12725
|
context.addMessage("system", row2("Build Timestamp", ts));
|
|
12129
12726
|
}
|
|
@@ -12136,14 +12733,14 @@ var init_version = __esm({
|
|
|
12136
12733
|
});
|
|
12137
12734
|
|
|
12138
12735
|
// src/commands/feedback.ts
|
|
12139
|
-
import { join as
|
|
12140
|
-
import { existsSync as
|
|
12736
|
+
import { join as join33 } from "path";
|
|
12737
|
+
import { existsSync as existsSync29 } from "fs";
|
|
12141
12738
|
import { readFile as readFile25 } from "fs/promises";
|
|
12142
12739
|
async function readCliVersion() {
|
|
12143
12740
|
try {
|
|
12144
12741
|
const baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
12145
|
-
const pkgPath =
|
|
12146
|
-
if (!
|
|
12742
|
+
const pkgPath = join33(baseDir, "..", "..", "package.json");
|
|
12743
|
+
if (!existsSync29(pkgPath)) return "unknown";
|
|
12147
12744
|
const text = await readFile25(pkgPath, "utf8");
|
|
12148
12745
|
const json = JSON.parse(text);
|
|
12149
12746
|
return json.version ?? "unknown";
|
|
@@ -12185,7 +12782,7 @@ var init_feedback = __esm({
|
|
|
12185
12782
|
|
|
12186
12783
|
// src/commands/export.ts
|
|
12187
12784
|
import { homedir as homedir18 } from "os";
|
|
12188
|
-
import { join as
|
|
12785
|
+
import { join as join34 } from "path";
|
|
12189
12786
|
import { writeFile as writeFile15, mkdir as mkdir8 } from "fs/promises";
|
|
12190
12787
|
function formatTimestamp(date) {
|
|
12191
12788
|
return date.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
@@ -12235,10 +12832,10 @@ var init_export = __esm({
|
|
|
12235
12832
|
let outputPath;
|
|
12236
12833
|
let content;
|
|
12237
12834
|
if (arg === "" || arg === "json") {
|
|
12238
|
-
outputPath =
|
|
12835
|
+
outputPath = join34(homedir18(), `msapling-export-${timestamp}.json`);
|
|
12239
12836
|
content = buildJsonExport(history);
|
|
12240
12837
|
} else if (arg === "markdown" || arg === "md") {
|
|
12241
|
-
outputPath =
|
|
12838
|
+
outputPath = join34(homedir18(), `msapling-export-${timestamp}.md`);
|
|
12242
12839
|
content = buildMarkdownExport(history);
|
|
12243
12840
|
} else {
|
|
12244
12841
|
outputPath = arg;
|
|
@@ -12250,7 +12847,7 @@ var init_export = __esm({
|
|
|
12250
12847
|
}
|
|
12251
12848
|
}
|
|
12252
12849
|
try {
|
|
12253
|
-
const dir =
|
|
12850
|
+
const dir = join34(outputPath, "..");
|
|
12254
12851
|
await mkdir8(dir, { recursive: true });
|
|
12255
12852
|
await writeFile15(outputPath, content, "utf8");
|
|
12256
12853
|
context.addMessage("system", `Exported to: ${outputPath}`);
|
|
@@ -12446,15 +13043,15 @@ var init_plan = __esm({
|
|
|
12446
13043
|
|
|
12447
13044
|
// src/commands/note.ts
|
|
12448
13045
|
import { homedir as homedir19 } from "os";
|
|
12449
|
-
import { join as
|
|
12450
|
-
import { existsSync as
|
|
13046
|
+
import { join as join35 } from "path";
|
|
13047
|
+
import { existsSync as existsSync30 } from "fs";
|
|
12451
13048
|
import { readFile as readFile26, writeFile as writeFile16 } from "fs/promises";
|
|
12452
13049
|
function getNotesFilePath() {
|
|
12453
|
-
return
|
|
13050
|
+
return join35(homedir19(), ".msapling", "notes.json");
|
|
12454
13051
|
}
|
|
12455
13052
|
async function readNotes(filePath = getNotesFilePath()) {
|
|
12456
13053
|
try {
|
|
12457
|
-
if (!
|
|
13054
|
+
if (!existsSync30(filePath)) return [];
|
|
12458
13055
|
const raw = await readFile26(filePath, "utf8");
|
|
12459
13056
|
const parsed = JSON.parse(raw);
|
|
12460
13057
|
if (!Array.isArray(parsed)) return [];
|
|
@@ -12464,7 +13061,7 @@ async function readNotes(filePath = getNotesFilePath()) {
|
|
|
12464
13061
|
}
|
|
12465
13062
|
}
|
|
12466
13063
|
async function writeNotes(notes, filePath = getNotesFilePath()) {
|
|
12467
|
-
const dir =
|
|
13064
|
+
const dir = join35(homedir19(), ".msapling");
|
|
12468
13065
|
ensureConfigDir(dir);
|
|
12469
13066
|
await writeFile16(filePath, JSON.stringify(notes, null, 2), "utf8");
|
|
12470
13067
|
}
|
|
@@ -12611,16 +13208,16 @@ var init_todo = __esm({
|
|
|
12611
13208
|
|
|
12612
13209
|
// src/commands/outputStyle.ts
|
|
12613
13210
|
import { homedir as homedir20 } from "os";
|
|
12614
|
-
import { join as
|
|
12615
|
-
import { existsSync as
|
|
13211
|
+
import { join as join36, basename as basename4, extname as extname3 } from "path";
|
|
13212
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync6, readdirSync as readdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
12616
13213
|
function resolveHome() {
|
|
12617
13214
|
return process.env.HOME || process.env.USERPROFILE || homedir20();
|
|
12618
13215
|
}
|
|
12619
13216
|
function stylesDir() {
|
|
12620
|
-
return
|
|
13217
|
+
return join36(resolveHome(), ".msapling", "output-styles");
|
|
12621
13218
|
}
|
|
12622
13219
|
function activeFile() {
|
|
12623
|
-
return
|
|
13220
|
+
return join36(stylesDir(), ".active");
|
|
12624
13221
|
}
|
|
12625
13222
|
function parseStyleFile(text) {
|
|
12626
13223
|
const fm = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
|
@@ -12641,16 +13238,16 @@ function parseStyleFile(text) {
|
|
|
12641
13238
|
}
|
|
12642
13239
|
function listUserStyles() {
|
|
12643
13240
|
const dir = stylesDir();
|
|
12644
|
-
if (!
|
|
13241
|
+
if (!existsSync31(dir)) return [];
|
|
12645
13242
|
const out = [];
|
|
12646
|
-
for (const entry of
|
|
13243
|
+
for (const entry of readdirSync5(dir)) {
|
|
12647
13244
|
if (extname3(entry).toLowerCase() !== ".md") continue;
|
|
12648
|
-
const full =
|
|
13245
|
+
const full = join36(dir, entry);
|
|
12649
13246
|
try {
|
|
12650
|
-
const text =
|
|
13247
|
+
const text = readFileSync5(full, "utf8");
|
|
12651
13248
|
const { description, body } = parseStyleFile(text);
|
|
12652
13249
|
out.push({
|
|
12653
|
-
name:
|
|
13250
|
+
name: basename4(entry, ".md"),
|
|
12654
13251
|
description,
|
|
12655
13252
|
body,
|
|
12656
13253
|
source: "user",
|
|
@@ -12673,15 +13270,15 @@ function findStyle(name) {
|
|
|
12673
13270
|
function getActiveStyleName() {
|
|
12674
13271
|
try {
|
|
12675
13272
|
const f = activeFile();
|
|
12676
|
-
if (!
|
|
12677
|
-
return
|
|
13273
|
+
if (!existsSync31(f)) return "default";
|
|
13274
|
+
return readFileSync5(f, "utf8").trim() || "default";
|
|
12678
13275
|
} catch {
|
|
12679
13276
|
return "default";
|
|
12680
13277
|
}
|
|
12681
13278
|
}
|
|
12682
13279
|
function setActiveStyleName(name) {
|
|
12683
13280
|
const dir = stylesDir();
|
|
12684
|
-
if (!
|
|
13281
|
+
if (!existsSync31(dir)) mkdirSync6(dir, { recursive: true });
|
|
12685
13282
|
writeFileSync5(activeFile(), `${name}
|
|
12686
13283
|
`, "utf8");
|
|
12687
13284
|
}
|
|
@@ -12694,8 +13291,8 @@ function createUserStyle(name, description, body) {
|
|
|
12694
13291
|
throw new Error(`Invalid style name "${name}" \u2014 use letters, digits, _ and - only.`);
|
|
12695
13292
|
}
|
|
12696
13293
|
const dir = stylesDir();
|
|
12697
|
-
if (!
|
|
12698
|
-
const target =
|
|
13294
|
+
if (!existsSync31(dir)) mkdirSync6(dir, { recursive: true });
|
|
13295
|
+
const target = join36(dir, `${name}.md`);
|
|
12699
13296
|
const frontmatter = `---
|
|
12700
13297
|
description: ${description.replace(/\n/g, " ")}
|
|
12701
13298
|
---
|
|
@@ -14112,6 +14709,32 @@ var init_agents = __esm({
|
|
|
14112
14709
|
}
|
|
14113
14710
|
});
|
|
14114
14711
|
|
|
14712
|
+
// src/commands/stats.ts
|
|
14713
|
+
var statsCommand;
|
|
14714
|
+
var init_stats = __esm({
|
|
14715
|
+
"src/commands/stats.ts"() {
|
|
14716
|
+
"use strict";
|
|
14717
|
+
init_esm_shims();
|
|
14718
|
+
init_src3();
|
|
14719
|
+
statsCommand = {
|
|
14720
|
+
name: "stats",
|
|
14721
|
+
args: "[--json]",
|
|
14722
|
+
description: "Show this session's tool, token, cost, and approval stats",
|
|
14723
|
+
category: "debug",
|
|
14724
|
+
handler: (args2, context) => {
|
|
14725
|
+
const snap = getSessionStats().snapshot();
|
|
14726
|
+
if (args2.includes("--json")) {
|
|
14727
|
+
context.addMessage("system", JSON.stringify(snap, null, 2));
|
|
14728
|
+
return;
|
|
14729
|
+
}
|
|
14730
|
+
for (const line of renderSessionStats(snap)) {
|
|
14731
|
+
context.addMessage("system", line);
|
|
14732
|
+
}
|
|
14733
|
+
}
|
|
14734
|
+
};
|
|
14735
|
+
}
|
|
14736
|
+
});
|
|
14737
|
+
|
|
14115
14738
|
// src/commands/index.ts
|
|
14116
14739
|
var commands_exports = {};
|
|
14117
14740
|
__export(commands_exports, {
|
|
@@ -14186,6 +14809,7 @@ var init_commands = __esm({
|
|
|
14186
14809
|
init_rewind();
|
|
14187
14810
|
init_bashes();
|
|
14188
14811
|
init_agents();
|
|
14812
|
+
init_stats();
|
|
14189
14813
|
commands = [
|
|
14190
14814
|
loginCommand,
|
|
14191
14815
|
logoutCommand,
|
|
@@ -14252,7 +14876,8 @@ var init_commands = __esm({
|
|
|
14252
14876
|
remoteAgentCommand,
|
|
14253
14877
|
rewindCommand,
|
|
14254
14878
|
bashesCommand,
|
|
14255
|
-
agentsCommand
|
|
14879
|
+
agentsCommand,
|
|
14880
|
+
statsCommand
|
|
14256
14881
|
];
|
|
14257
14882
|
}
|
|
14258
14883
|
});
|
|
@@ -14322,15 +14947,15 @@ var exec_exports = {};
|
|
|
14322
14947
|
__export(exec_exports, {
|
|
14323
14948
|
runExec: () => runExec
|
|
14324
14949
|
});
|
|
14325
|
-
import { existsSync as
|
|
14950
|
+
import { existsSync as existsSync34 } from "fs";
|
|
14326
14951
|
import { readFile as readFile29 } from "fs/promises";
|
|
14327
14952
|
import { homedir as homedir23 } from "os";
|
|
14328
|
-
import { join as
|
|
14953
|
+
import { join as join38 } from "path";
|
|
14329
14954
|
async function loadPersistedSettings() {
|
|
14330
14955
|
const out = { mode: "default", theme: null };
|
|
14331
14956
|
try {
|
|
14332
|
-
const p =
|
|
14333
|
-
if (!
|
|
14957
|
+
const p = join38(homedir23(), ".msapling", "settings.json");
|
|
14958
|
+
if (!existsSync34(p)) return out;
|
|
14334
14959
|
const raw = JSON.parse(await readFile29(p, "utf8"));
|
|
14335
14960
|
const parsed = parseApprovalMode(raw, Date.now());
|
|
14336
14961
|
if (parsed.kind === "ok") out.mode = parsed.mode;
|
|
@@ -15189,8 +15814,8 @@ __export(doctor_exports, {
|
|
|
15189
15814
|
runDoctor: () => runDoctor
|
|
15190
15815
|
});
|
|
15191
15816
|
import { homedir as homedir24, platform as platform4, tmpdir } from "os";
|
|
15192
|
-
import { join as
|
|
15193
|
-
import { existsSync as
|
|
15817
|
+
import { join as join39 } from "path";
|
|
15818
|
+
import { existsSync as existsSync35, statSync as statSync9 } from "fs";
|
|
15194
15819
|
import { readdir as readdir3, mkdir as mkdir9, rm as rm3 } from "fs/promises";
|
|
15195
15820
|
import { exec as exec2 } from "child_process";
|
|
15196
15821
|
import { promisify } from "util";
|
|
@@ -15213,8 +15838,8 @@ async function checkNodeVersion() {
|
|
|
15213
15838
|
};
|
|
15214
15839
|
}
|
|
15215
15840
|
async function checkConfigDir() {
|
|
15216
|
-
const configDir =
|
|
15217
|
-
if (!
|
|
15841
|
+
const configDir = join39(homedir24(), ".msapling");
|
|
15842
|
+
if (!existsSync35(configDir)) {
|
|
15218
15843
|
return {
|
|
15219
15844
|
name: "Config directory",
|
|
15220
15845
|
status: "WARN",
|
|
@@ -15222,7 +15847,7 @@ async function checkConfigDir() {
|
|
|
15222
15847
|
remediation: `mkdir -p "${configDir}" && chmod 700 "${configDir}"`
|
|
15223
15848
|
};
|
|
15224
15849
|
}
|
|
15225
|
-
const stats =
|
|
15850
|
+
const stats = statSync9(configDir);
|
|
15226
15851
|
if (!stats.isDirectory()) {
|
|
15227
15852
|
return {
|
|
15228
15853
|
name: "Config directory",
|
|
@@ -15283,7 +15908,7 @@ async function checkPathConflicts() {
|
|
|
15283
15908
|
const timedOutDirs = [];
|
|
15284
15909
|
const DIR_TIMEOUT_MS = 1500;
|
|
15285
15910
|
for (const dir of paths) {
|
|
15286
|
-
if (!dir || !
|
|
15911
|
+
if (!dir || !existsSync35(dir)) continue;
|
|
15287
15912
|
try {
|
|
15288
15913
|
const files = await Promise.race([
|
|
15289
15914
|
readdir3(dir),
|
|
@@ -15296,7 +15921,7 @@ async function checkPathConflicts() {
|
|
|
15296
15921
|
]);
|
|
15297
15922
|
for (const file of files) {
|
|
15298
15923
|
if (file === "msapling" || file === "msapling.exe" || file === "msapling.py") {
|
|
15299
|
-
const fullPath =
|
|
15924
|
+
const fullPath = join39(dir, file);
|
|
15300
15925
|
conflicts.push(fullPath);
|
|
15301
15926
|
}
|
|
15302
15927
|
}
|
|
@@ -15414,9 +16039,9 @@ async function checkTokenValidity() {
|
|
|
15414
16039
|
}
|
|
15415
16040
|
async function checkOsSpecific() {
|
|
15416
16041
|
if (platform4() === "win32") {
|
|
15417
|
-
const testDir =
|
|
16042
|
+
const testDir = join39(tmpdir(), `msapling-longpath-test-${Date.now()}`);
|
|
15418
16043
|
const longDirName = "A".repeat(260);
|
|
15419
|
-
const testPath =
|
|
16044
|
+
const testPath = join39(testDir, longDirName);
|
|
15420
16045
|
try {
|
|
15421
16046
|
await mkdir9(testDir, { recursive: true });
|
|
15422
16047
|
try {
|
|
@@ -17093,8 +17718,8 @@ var init_libesm = __esm({
|
|
|
17093
17718
|
});
|
|
17094
17719
|
|
|
17095
17720
|
// ../core/src/mcp/catalog.ts
|
|
17096
|
-
import { readdirSync as
|
|
17097
|
-
import { join as
|
|
17721
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync6, statSync as statSync10 } from "fs";
|
|
17722
|
+
import { join as join40, relative as relative17 } from "path";
|
|
17098
17723
|
function buildFileTree(root, maxFiles) {
|
|
17099
17724
|
const SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", "build", "dist", ".venv", "venv", ".next", "__pycache__", ".dart_tool", ".bun", "target"]);
|
|
17100
17725
|
const SOURCE_EXT = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".go", ".rs", ".java", ".kt", ".swift", ".dart", ".rb", ".cs", ".cpp", ".c", ".h", ".md", ".json", ".yaml", ".yml", ".toml", ".sh", ".sql"]);
|
|
@@ -17104,17 +17729,17 @@ function buildFileTree(root, maxFiles) {
|
|
|
17104
17729
|
const dir = queue.shift();
|
|
17105
17730
|
let entries;
|
|
17106
17731
|
try {
|
|
17107
|
-
entries =
|
|
17732
|
+
entries = readdirSync6(dir);
|
|
17108
17733
|
} catch {
|
|
17109
17734
|
continue;
|
|
17110
17735
|
}
|
|
17111
17736
|
for (const name of entries) {
|
|
17112
17737
|
if (out.length >= maxFiles) break;
|
|
17113
17738
|
if (SKIP_DIRS2.has(name)) continue;
|
|
17114
|
-
const full =
|
|
17739
|
+
const full = join40(dir, name);
|
|
17115
17740
|
let s;
|
|
17116
17741
|
try {
|
|
17117
|
-
s =
|
|
17742
|
+
s = statSync10(full);
|
|
17118
17743
|
} catch {
|
|
17119
17744
|
continue;
|
|
17120
17745
|
}
|
|
@@ -17135,12 +17760,12 @@ function readFilesAsContext(root, files, maxKB) {
|
|
|
17135
17760
|
for (const f of files) {
|
|
17136
17761
|
let body;
|
|
17137
17762
|
try {
|
|
17138
|
-
body =
|
|
17763
|
+
body = readFileSync6(f, "utf8");
|
|
17139
17764
|
} catch {
|
|
17140
17765
|
continue;
|
|
17141
17766
|
}
|
|
17142
17767
|
if (body.length > cap) body = body.slice(0, cap) + "\n[...truncated]";
|
|
17143
|
-
const rel =
|
|
17768
|
+
const rel = relative17(root, f).replace(/\\/g, "/");
|
|
17144
17769
|
parts.push(`### ${rel}
|
|
17145
17770
|
|
|
17146
17771
|
\`\`\`
|
|
@@ -17917,7 +18542,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
17917
18542
|
var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
|
|
17918
18543
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
17919
18544
|
"\u25CF MSapling CLI v",
|
|
17920
|
-
"2.3.6-beta.
|
|
18545
|
+
"2.3.6-beta.49"
|
|
17921
18546
|
] }),
|
|
17922
18547
|
/* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
|
|
17923
18548
|
] });
|
|
@@ -18320,6 +18945,7 @@ function createIdleAwarePoll(pollingIntervalRef, lastActivityRef, client, setUse
|
|
|
18320
18945
|
|
|
18321
18946
|
// src/state/commandHandler.ts
|
|
18322
18947
|
init_esm_shims();
|
|
18948
|
+
init_src3();
|
|
18323
18949
|
init_commands();
|
|
18324
18950
|
init_plan();
|
|
18325
18951
|
import { spawn as spawn10 } from "child_process";
|
|
@@ -18327,14 +18953,14 @@ import { spawn as spawn10 } from "child_process";
|
|
|
18327
18953
|
// src/state/persistentState.ts
|
|
18328
18954
|
init_esm_shims();
|
|
18329
18955
|
import { homedir as homedir22 } from "os";
|
|
18330
|
-
import { join as
|
|
18331
|
-
import { existsSync as
|
|
18956
|
+
import { join as join37, dirname as dirname5 } from "path";
|
|
18957
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync7 } from "fs";
|
|
18332
18958
|
import { readFile as readFile27, writeFile as writeFile17, rename as rename3 } from "fs/promises";
|
|
18333
18959
|
import { randomBytes as randomBytes15 } from "crypto";
|
|
18334
|
-
var STATE_PATH =
|
|
18960
|
+
var STATE_PATH = join37(homedir22(), ".msapling", "state.json");
|
|
18335
18961
|
async function loadPersistentState(statePath = STATE_PATH) {
|
|
18336
18962
|
try {
|
|
18337
|
-
if (!
|
|
18963
|
+
if (!existsSync32(statePath)) return { version: 1 };
|
|
18338
18964
|
const text = await readFile27(statePath, "utf8");
|
|
18339
18965
|
const parsed = JSON.parse(text);
|
|
18340
18966
|
if (parsed.version !== 1) return { version: 1 };
|
|
@@ -18350,7 +18976,7 @@ async function loadPersistentState(statePath = STATE_PATH) {
|
|
|
18350
18976
|
async function savePersistentState(state, statePath = STATE_PATH) {
|
|
18351
18977
|
try {
|
|
18352
18978
|
const dir = dirname5(statePath);
|
|
18353
|
-
if (!
|
|
18979
|
+
if (!existsSync32(dir)) mkdirSync7(dir, { recursive: true });
|
|
18354
18980
|
const existing = await loadPersistentState(statePath);
|
|
18355
18981
|
const merged = {
|
|
18356
18982
|
version: 1,
|
|
@@ -18419,6 +19045,11 @@ ${prompt4}` : prompt4;
|
|
|
18419
19045
|
if (usage !== null) {
|
|
18420
19046
|
ctx.setLastCost(usage.cost_usd);
|
|
18421
19047
|
ctx.setSessionCost((prev) => prev + usage.cost_usd);
|
|
19048
|
+
getSessionStats().recordUsage(
|
|
19049
|
+
usage.cost_usd,
|
|
19050
|
+
usage.prompt_tokens,
|
|
19051
|
+
usage.completion_tokens
|
|
19052
|
+
);
|
|
18422
19053
|
}
|
|
18423
19054
|
ctx.setContextBudgetSnap(snapshotBudget(ctx.agent.getContextBudget()));
|
|
18424
19055
|
} catch (e) {
|
|
@@ -18497,9 +19128,9 @@ ${prompt4}` : prompt4;
|
|
|
18497
19128
|
for (const mention of fileMentions) {
|
|
18498
19129
|
const filePath = mention.slice(1);
|
|
18499
19130
|
try {
|
|
18500
|
-
const { existsSync:
|
|
19131
|
+
const { existsSync: existsSync36 } = await import("fs");
|
|
18501
19132
|
const { readFile: readFile30 } = await import("fs/promises");
|
|
18502
|
-
if (
|
|
19133
|
+
if (existsSync36(filePath)) {
|
|
18503
19134
|
const content = await readFile30(filePath, "utf8");
|
|
18504
19135
|
const MAX_LEN = 32768;
|
|
18505
19136
|
const truncated = content.length > MAX_LEN ? content.slice(0, MAX_LEN) + "\n...[TRUNCATED]" : content;
|
|
@@ -18525,6 +19156,7 @@ ${finalCmd}`;
|
|
|
18525
19156
|
if (usage !== null) {
|
|
18526
19157
|
ctx.setLastCost(usage.cost_usd);
|
|
18527
19158
|
ctx.setSessionCost((prev) => prev + usage.cost_usd);
|
|
19159
|
+
getSessionStats().recordUsage(usage.cost_usd, usage.prompt_tokens, usage.completion_tokens);
|
|
18528
19160
|
}
|
|
18529
19161
|
ctx.setContextBudgetSnap(snapshotBudget(ctx.agent.getContextBudget()));
|
|
18530
19162
|
} catch (e) {
|
|
@@ -18561,7 +19193,7 @@ init_src3();
|
|
|
18561
19193
|
init_src();
|
|
18562
19194
|
init_parseApprovalMode();
|
|
18563
19195
|
import { readFile as readFile28 } from "fs/promises";
|
|
18564
|
-
import { existsSync as
|
|
19196
|
+
import { existsSync as existsSync33 } from "fs";
|
|
18565
19197
|
async function initSession(ctx) {
|
|
18566
19198
|
try {
|
|
18567
19199
|
const journalEncrypted = await initJournalEncryption();
|
|
@@ -18587,9 +19219,9 @@ async function initSession(ctx) {
|
|
|
18587
19219
|
}
|
|
18588
19220
|
try {
|
|
18589
19221
|
const { homedir: homedir25 } = await import("os");
|
|
18590
|
-
const { join:
|
|
18591
|
-
const userSettingsPath =
|
|
18592
|
-
if (
|
|
19222
|
+
const { join: join42 } = await import("path");
|
|
19223
|
+
const userSettingsPath = join42(homedir25(), ".msapling", "settings.json");
|
|
19224
|
+
if (existsSync33(userSettingsPath)) {
|
|
18593
19225
|
const userText = await readFile28(userSettingsPath, "utf8");
|
|
18594
19226
|
let parsed;
|
|
18595
19227
|
try {
|
|
@@ -19006,14 +19638,14 @@ var App = ({ compact: compact2 = false, continueSession: continueSession2 = fals
|
|
|
19006
19638
|
|
|
19007
19639
|
// src/runtime/bootstrap.ts
|
|
19008
19640
|
init_esm_shims();
|
|
19009
|
-
import { readFileSync as
|
|
19641
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
19010
19642
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19011
|
-
import { dirname as dirname6, join as
|
|
19643
|
+
import { dirname as dirname6, join as join41 } from "path";
|
|
19012
19644
|
function readCliVersion2() {
|
|
19013
19645
|
const here = dirname6(fileURLToPath2(import.meta.url));
|
|
19014
19646
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
19015
19647
|
try {
|
|
19016
|
-
const pkg = JSON.parse(
|
|
19648
|
+
const pkg = JSON.parse(readFileSync7(join41(here, rel), "utf8"));
|
|
19017
19649
|
if (pkg.name && pkg.version) {
|
|
19018
19650
|
return { name: pkg.name, version: pkg.version };
|
|
19019
19651
|
}
|