@mtreeai/msapling-cli 2.3.6-beta.48 → 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 +655 -115
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5397,17 +5397,17 @@ async function backupFile(absPath) {
|
|
|
5397
5397
|
try {
|
|
5398
5398
|
const { readFile: readFile30, writeFile: writeFile19, mkdir: mkdir10 } = await import("fs/promises");
|
|
5399
5399
|
const { homedir: homedir25 } = await import("os");
|
|
5400
|
-
const { join:
|
|
5400
|
+
const { join: join42 } = await import("path");
|
|
5401
5401
|
const filename = absPath.split(/[\\/]/).pop() ?? "file";
|
|
5402
5402
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5403
5403
|
const suffix = randomBytes7(4).toString("hex");
|
|
5404
|
-
const backupPath =
|
|
5404
|
+
const backupPath = join42(
|
|
5405
5405
|
homedir25(),
|
|
5406
5406
|
".msapling",
|
|
5407
5407
|
"backups",
|
|
5408
5408
|
`${filename}.backup-${stamp}-${suffix}.bak`
|
|
5409
5409
|
);
|
|
5410
|
-
await mkdir10(
|
|
5410
|
+
await mkdir10(join42(homedir25(), ".msapling", "backups"), { recursive: true });
|
|
5411
5411
|
const content = await readFile30(absPath, "utf8");
|
|
5412
5412
|
await writeFile19(backupPath, content, "utf8");
|
|
5413
5413
|
return backupPath;
|
|
@@ -6017,10 +6017,10 @@ var init_Sandbox = __esm({
|
|
|
6017
6017
|
* to a minimal platform default so subprocess spawn never sees an empty PATH.
|
|
6018
6018
|
*/
|
|
6019
6019
|
static curatePath(rawPath) {
|
|
6020
|
-
const
|
|
6021
|
-
const entries = (rawPath ?? "").split(
|
|
6020
|
+
const sep5 = process.platform === "win32" ? ";" : ":";
|
|
6021
|
+
const entries = (rawPath ?? "").split(sep5);
|
|
6022
6022
|
const kept = entries.filter((e) => _Sandbox.isSafePathEntry(e));
|
|
6023
|
-
if (kept.length > 0) return kept.join(
|
|
6023
|
+
if (kept.length > 0) return kept.join(sep5);
|
|
6024
6024
|
return process.platform === "win32" ? "C:\\Windows\\System32;C:\\Windows" : "/usr/local/bin:/usr/bin:/bin";
|
|
6025
6025
|
}
|
|
6026
6026
|
getRestrictedEnv(pathOverride) {
|
|
@@ -6594,6 +6594,124 @@ var init_ResourceGovernor = __esm({
|
|
|
6594
6594
|
}
|
|
6595
6595
|
});
|
|
6596
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
|
+
|
|
6597
6715
|
// ../core/src/agent/ToolExecutor.ts
|
|
6598
6716
|
import { readFile as readFile10 } from "fs/promises";
|
|
6599
6717
|
var APPROVAL_GATED, ToolExecutor;
|
|
@@ -6628,6 +6746,7 @@ var init_ToolExecutor = __esm({
|
|
|
6628
6746
|
init_ShadowService();
|
|
6629
6747
|
init_Hooks();
|
|
6630
6748
|
init_ResourceGovernor();
|
|
6749
|
+
init_SessionStats();
|
|
6631
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"]);
|
|
6632
6751
|
ToolExecutor = class {
|
|
6633
6752
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -6941,8 +7060,10 @@ ${blocker.stderr || "(empty)"}`,
|
|
|
6941
7060
|
reason: `${toolName} requested by agent in '${this.mode}' mode`
|
|
6942
7061
|
});
|
|
6943
7062
|
if (decision === "no") {
|
|
7063
|
+
getSessionStats().recordApproval("denied");
|
|
6944
7064
|
return { content: `User denied ${toolName} approval.`, isError: true };
|
|
6945
7065
|
}
|
|
7066
|
+
getSessionStats().recordApproval("approved");
|
|
6946
7067
|
if (decision === "always") {
|
|
6947
7068
|
if (this.trustStore) {
|
|
6948
7069
|
this.trustStore.add(cmdKey).catch(() => {
|
|
@@ -6951,6 +7072,8 @@ ${blocker.stderr || "(empty)"}`,
|
|
|
6951
7072
|
this.sessionTrust.add(cmdKey);
|
|
6952
7073
|
}
|
|
6953
7074
|
}
|
|
7075
|
+
} else {
|
|
7076
|
+
getSessionStats().recordApproval("autoApproved");
|
|
6954
7077
|
}
|
|
6955
7078
|
}
|
|
6956
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") {
|
|
@@ -6982,10 +7105,20 @@ Please approve the diff in the UI to sync this change locally.`
|
|
|
6982
7105
|
const governor = await getGlobalGovernor();
|
|
6983
7106
|
await governor.acquireTool();
|
|
6984
7107
|
let result;
|
|
7108
|
+
const startedAt = Date.now();
|
|
7109
|
+
let threw = false;
|
|
6985
7110
|
try {
|
|
6986
7111
|
result = await tool.execute(args2, projectRoot);
|
|
7112
|
+
} catch (e) {
|
|
7113
|
+
threw = true;
|
|
7114
|
+
throw e;
|
|
6987
7115
|
} finally {
|
|
6988
7116
|
governor.releaseTool();
|
|
7117
|
+
getSessionStats().recordTool(
|
|
7118
|
+
toolName,
|
|
7119
|
+
Date.now() - startedAt,
|
|
7120
|
+
threw || !!(result && result.isError)
|
|
7121
|
+
);
|
|
6989
7122
|
}
|
|
6990
7123
|
if (this.hooks) {
|
|
6991
7124
|
this.hooks.fire({
|
|
@@ -7194,13 +7327,14 @@ async function loadProjectConfig(cwd = process.cwd()) {
|
|
|
7194
7327
|
const [user, project] = await Promise.all([findUserConfig(), findProjectConfig(cwd)]);
|
|
7195
7328
|
return { user, project, combined: buildCombined(user, project) };
|
|
7196
7329
|
}
|
|
7197
|
-
var FILENAMES, TRUNCATE_AT;
|
|
7330
|
+
var FILENAMES, TRUNCATE_AT, PROJECT_CONFIG_FILENAMES;
|
|
7198
7331
|
var init_ProjectConfig = __esm({
|
|
7199
7332
|
"../core/src/ProjectConfig.ts"() {
|
|
7200
7333
|
"use strict";
|
|
7201
7334
|
init_esm_shims();
|
|
7202
7335
|
FILENAMES = ["MSAPLING.md", "CLAUDE.md", "GEMINI.md", "AGENTS.md"];
|
|
7203
7336
|
TRUNCATE_AT = 32e3;
|
|
7337
|
+
PROJECT_CONFIG_FILENAMES = FILENAMES;
|
|
7204
7338
|
}
|
|
7205
7339
|
});
|
|
7206
7340
|
|
|
@@ -9217,8 +9351,10 @@ __export(src_exports2, {
|
|
|
9217
9351
|
MultiEditFileTool: () => MultiEditFileTool,
|
|
9218
9352
|
NotebookEditTool: () => NotebookEditTool,
|
|
9219
9353
|
NotebookReadTool: () => NotebookReadTool,
|
|
9354
|
+
PROJECT_CONFIG_FILENAMES: () => PROJECT_CONFIG_FILENAMES,
|
|
9220
9355
|
PatchFileTool: () => PatchFileTool,
|
|
9221
9356
|
ReadBackgroundShellTool: () => ReadBackgroundShellTool,
|
|
9357
|
+
SessionStats: () => SessionStats,
|
|
9222
9358
|
StorageManager: () => StorageManager,
|
|
9223
9359
|
SwarmManager: () => SwarmManager,
|
|
9224
9360
|
TOOL_NAME_ALIASES: () => TOOL_NAME_ALIASES,
|
|
@@ -9230,6 +9366,7 @@ __export(src_exports2, {
|
|
|
9230
9366
|
WebFetchTool: () => WebFetchTool,
|
|
9231
9367
|
WebSearchTool: () => WebSearchTool,
|
|
9232
9368
|
WriteFileTool: () => WriteFileTool,
|
|
9369
|
+
_resetSessionStatsSingleton: () => _resetSessionStatsSingleton,
|
|
9233
9370
|
_setBackupDirOverride: () => _setBackupDirOverride,
|
|
9234
9371
|
backupDir: () => backupDir,
|
|
9235
9372
|
buildCompactionPrompt: () => buildCompactionPrompt,
|
|
@@ -9243,6 +9380,7 @@ __export(src_exports2, {
|
|
|
9243
9380
|
formatNotebookHeader: () => formatNotebookHeader,
|
|
9244
9381
|
formatTodos: () => formatTodos,
|
|
9245
9382
|
getOrCreateJournalKey: () => getOrCreateJournalKey,
|
|
9383
|
+
getSessionStats: () => getSessionStats,
|
|
9246
9384
|
initJournalEncryption: () => initJournalEncryption,
|
|
9247
9385
|
listCheckpoints: () => listCheckpoints,
|
|
9248
9386
|
loadNamedAgents: () => loadNamedAgents,
|
|
@@ -9254,6 +9392,7 @@ __export(src_exports2, {
|
|
|
9254
9392
|
parseAgentFile: () => parseAgentFile,
|
|
9255
9393
|
parseToolsValue: () => parseToolsValue,
|
|
9256
9394
|
recordBackup: () => recordBackup,
|
|
9395
|
+
renderSessionStats: () => renderSessionStats,
|
|
9257
9396
|
restoreCheckpoint: () => restoreCheckpoint,
|
|
9258
9397
|
takeSnapshot: () => takeSnapshot
|
|
9259
9398
|
});
|
|
@@ -9272,6 +9411,7 @@ var init_src3 = __esm({
|
|
|
9272
9411
|
init_Storage();
|
|
9273
9412
|
init_journalCrypto();
|
|
9274
9413
|
init_Mutex();
|
|
9414
|
+
init_SessionStats();
|
|
9275
9415
|
init_ProjectConfig();
|
|
9276
9416
|
init_Settings();
|
|
9277
9417
|
init_client();
|
|
@@ -11347,40 +11487,405 @@ var init_compact = __esm({
|
|
|
11347
11487
|
}
|
|
11348
11488
|
});
|
|
11349
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
|
+
|
|
11350
11840
|
// src/commands/init.ts
|
|
11351
|
-
import { join as
|
|
11352
|
-
import { existsSync as
|
|
11841
|
+
import { join as join28 } from "path";
|
|
11842
|
+
import { existsSync as existsSync24 } from "fs";
|
|
11353
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
|
+
}
|
|
11354
11852
|
var initCommand;
|
|
11355
11853
|
var init_init = __esm({
|
|
11356
11854
|
"src/commands/init.ts"() {
|
|
11357
11855
|
"use strict";
|
|
11358
11856
|
init_esm_shims();
|
|
11857
|
+
init_src3();
|
|
11858
|
+
init_codebaseScan();
|
|
11359
11859
|
initCommand = {
|
|
11360
11860
|
name: "init",
|
|
11361
|
-
|
|
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)",
|
|
11362
11863
|
category: "project",
|
|
11363
11864
|
handler: async (args2, context) => {
|
|
11364
11865
|
try {
|
|
11365
11866
|
const cwd = process.cwd();
|
|
11366
|
-
const
|
|
11367
|
-
|
|
11368
|
-
|
|
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
|
+
);
|
|
11369
11874
|
return;
|
|
11370
11875
|
}
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11376
|
-
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
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
|
+
);
|
|
11384
11889
|
} catch (e) {
|
|
11385
11890
|
context.addMessage("error", `Failed to initialize project: ${e.message}`);
|
|
11386
11891
|
}
|
|
@@ -11390,7 +11895,7 @@ var init_init = __esm({
|
|
|
11390
11895
|
});
|
|
11391
11896
|
|
|
11392
11897
|
// src/commands/review.ts
|
|
11393
|
-
import { existsSync as
|
|
11898
|
+
import { existsSync as existsSync25 } from "fs";
|
|
11394
11899
|
import { readFile as readFile21 } from "fs/promises";
|
|
11395
11900
|
var reviewCommand;
|
|
11396
11901
|
var init_review = __esm({
|
|
@@ -11410,7 +11915,7 @@ var init_review = __esm({
|
|
|
11410
11915
|
}
|
|
11411
11916
|
let content = "";
|
|
11412
11917
|
try {
|
|
11413
|
-
if (
|
|
11918
|
+
if (existsSync25(target)) {
|
|
11414
11919
|
content = await readFile21(target, "utf8");
|
|
11415
11920
|
} else {
|
|
11416
11921
|
content = `Review target: ${target}`;
|
|
@@ -11504,15 +12009,15 @@ var init_swarm = __esm({
|
|
|
11504
12009
|
|
|
11505
12010
|
// src/commands/recipe.ts
|
|
11506
12011
|
import { parse as parseYaml } from "yaml";
|
|
11507
|
-
import { existsSync as
|
|
12012
|
+
import { existsSync as existsSync26 } from "fs";
|
|
11508
12013
|
import { readFile as readFile22 } from "fs/promises";
|
|
11509
|
-
import { join as
|
|
12014
|
+
import { join as join29 } from "path";
|
|
11510
12015
|
function findRecipe(name, cwd) {
|
|
11511
12016
|
for (const dir of RECIPE_DIRS) {
|
|
11512
12017
|
for (const suffix of NAME_SUFFIXES) {
|
|
11513
12018
|
for (const ext of FILE_EXTS) {
|
|
11514
|
-
const p =
|
|
11515
|
-
if (
|
|
12019
|
+
const p = join29(cwd, dir, `${name}${suffix}${ext}`);
|
|
12020
|
+
if (existsSync26(p)) return p;
|
|
11516
12021
|
}
|
|
11517
12022
|
}
|
|
11518
12023
|
}
|
|
@@ -11625,13 +12130,13 @@ ${rendered}` : rendered;
|
|
|
11625
12130
|
});
|
|
11626
12131
|
|
|
11627
12132
|
// src/commands/skill.ts
|
|
11628
|
-
import { existsSync as
|
|
12133
|
+
import { existsSync as existsSync27, readdirSync as readdirSync4, statSync as statSync8 } from "fs";
|
|
11629
12134
|
import { readFile as readFile23 } from "fs/promises";
|
|
11630
|
-
import { join as
|
|
12135
|
+
import { join as join30, resolve as resolve16 } from "path";
|
|
11631
12136
|
function findSkillsRoot(cwd) {
|
|
11632
12137
|
for (const candidate of SKILLS_DIRS) {
|
|
11633
12138
|
const full = resolve16(cwd, candidate);
|
|
11634
|
-
if (
|
|
12139
|
+
if (existsSync27(full) && statSync8(full).isDirectory()) return full;
|
|
11635
12140
|
}
|
|
11636
12141
|
return null;
|
|
11637
12142
|
}
|
|
@@ -11639,28 +12144,28 @@ function listAllSkills(root) {
|
|
|
11639
12144
|
const out = [];
|
|
11640
12145
|
let domains;
|
|
11641
12146
|
try {
|
|
11642
|
-
domains =
|
|
12147
|
+
domains = readdirSync4(root);
|
|
11643
12148
|
} catch {
|
|
11644
12149
|
return out;
|
|
11645
12150
|
}
|
|
11646
12151
|
for (const domain of domains) {
|
|
11647
|
-
const dir =
|
|
12152
|
+
const dir = join30(root, domain);
|
|
11648
12153
|
let s;
|
|
11649
12154
|
try {
|
|
11650
|
-
s =
|
|
12155
|
+
s = statSync8(dir);
|
|
11651
12156
|
} catch {
|
|
11652
12157
|
continue;
|
|
11653
12158
|
}
|
|
11654
12159
|
if (!s.isDirectory()) continue;
|
|
11655
12160
|
let files;
|
|
11656
12161
|
try {
|
|
11657
|
-
files =
|
|
12162
|
+
files = readdirSync4(dir);
|
|
11658
12163
|
} catch {
|
|
11659
12164
|
continue;
|
|
11660
12165
|
}
|
|
11661
12166
|
for (const f of files) {
|
|
11662
12167
|
if (!f.endsWith(".md")) continue;
|
|
11663
|
-
out.push({ domain, name: f.slice(0, -3), path:
|
|
12168
|
+
out.push({ domain, name: f.slice(0, -3), path: join30(dir, f) });
|
|
11664
12169
|
}
|
|
11665
12170
|
}
|
|
11666
12171
|
return out.sort(
|
|
@@ -11750,7 +12255,7 @@ ${prompt4}`;
|
|
|
11750
12255
|
|
|
11751
12256
|
// src/commands/benchmark.ts
|
|
11752
12257
|
import { homedir as homedir16 } from "os";
|
|
11753
|
-
import { join as
|
|
12258
|
+
import { join as join31 } from "path";
|
|
11754
12259
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
11755
12260
|
import * as fs2 from "fs";
|
|
11756
12261
|
function parseArgs(args2) {
|
|
@@ -11767,7 +12272,7 @@ function parseArgs(args2) {
|
|
|
11767
12272
|
}
|
|
11768
12273
|
function formatTable(results) {
|
|
11769
12274
|
const header = `${"Model".padEnd(40)} ${"TTFT(ms)".padStart(9)} ${"TPS".padStart(7)} ${"Tokens".padStart(8)} ${"Cost($)".padStart(9)}`;
|
|
11770
|
-
const
|
|
12275
|
+
const sep5 = "-".repeat(header.length);
|
|
11771
12276
|
const rows = results.map((r) => {
|
|
11772
12277
|
const model = r.model.slice(0, 39).padEnd(40);
|
|
11773
12278
|
const ttft = r.ttft_ms != null ? r.ttft_ms.toFixed(0).padStart(9) : " - ";
|
|
@@ -11777,7 +12282,7 @@ function formatTable(results) {
|
|
|
11777
12282
|
const err = r.error ? ` \u26A0 ${r.error}` : "";
|
|
11778
12283
|
return `${model} ${ttft} ${tps} ${tok} ${cost}${err}`;
|
|
11779
12284
|
});
|
|
11780
|
-
return [
|
|
12285
|
+
return [sep5, header, sep5, ...rows, sep5].join("\n");
|
|
11781
12286
|
}
|
|
11782
12287
|
var DEFAULT_PROMPTS, benchmarkCommand;
|
|
11783
12288
|
var init_benchmark = __esm({
|
|
@@ -11870,10 +12375,10 @@ HW at start: ${hw.cores}-core ${hw.platform} | CPU ${hw.cpuPct}% | RAM ${hw.ramP
|
|
|
11870
12375
|
`[HW at run time: CPU ${hwAtEnd.cpuPct}% / RAM ${hwAtEnd.ramPct}% | ${hw.ramGiB} GiB RAM, ${hw.cores} cores]`
|
|
11871
12376
|
);
|
|
11872
12377
|
try {
|
|
11873
|
-
const dir =
|
|
12378
|
+
const dir = join31(homedir16(), ".msapling", "benchmarks");
|
|
11874
12379
|
mkdirSync5(dir, { recursive: true });
|
|
11875
12380
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
11876
|
-
const file =
|
|
12381
|
+
const file = join31(dir, `${ts}.json`);
|
|
11877
12382
|
const run = {
|
|
11878
12383
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11879
12384
|
rounds,
|
|
@@ -12119,22 +12624,22 @@ var init_theme = __esm({
|
|
|
12119
12624
|
});
|
|
12120
12625
|
|
|
12121
12626
|
// src/commands/theme.ts
|
|
12122
|
-
import { join as
|
|
12627
|
+
import { join as join32 } from "path";
|
|
12123
12628
|
import { homedir as homedir17 } from "os";
|
|
12124
|
-
import { existsSync as
|
|
12629
|
+
import { existsSync as existsSync28 } from "fs";
|
|
12125
12630
|
import { readFile as readFile24, writeFile as writeFile14 } from "fs/promises";
|
|
12126
12631
|
async function persistTheme(storage, themeName) {
|
|
12127
|
-
const settingsPath =
|
|
12632
|
+
const settingsPath = join32(homedir17(), ".msapling", "settings.json");
|
|
12128
12633
|
let existing = {};
|
|
12129
12634
|
try {
|
|
12130
|
-
if (
|
|
12635
|
+
if (existsSync28(settingsPath)) {
|
|
12131
12636
|
const text = await readFile24(settingsPath, "utf8");
|
|
12132
12637
|
if (text.trim()) existing = JSON.parse(text);
|
|
12133
12638
|
}
|
|
12134
12639
|
} catch {
|
|
12135
12640
|
}
|
|
12136
12641
|
existing["theme"] = themeName;
|
|
12137
|
-
ensureConfigDir(
|
|
12642
|
+
ensureConfigDir(join32(homedir17(), ".msapling"));
|
|
12138
12643
|
await writeFile14(settingsPath, JSON.stringify(existing, null, 2), "utf8");
|
|
12139
12644
|
}
|
|
12140
12645
|
var VALID_THEMES, themeCommand;
|
|
@@ -12206,7 +12711,7 @@ var init_version = __esm({
|
|
|
12206
12711
|
description: "Show version information for CLI and core packages",
|
|
12207
12712
|
category: "debug",
|
|
12208
12713
|
handler: async (_args, context) => {
|
|
12209
|
-
const cliVersion = true ? "2.3.6-beta.
|
|
12714
|
+
const cliVersion = true ? "2.3.6-beta.49" : "(dev)";
|
|
12210
12715
|
const coreVersion = true ? "2.3.6-beta.43" : "(dev)";
|
|
12211
12716
|
const runtime = process.version;
|
|
12212
12717
|
context.addMessage("system", "MSapling Version Info");
|
|
@@ -12215,7 +12720,7 @@ var init_version = __esm({
|
|
|
12215
12720
|
context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
|
|
12216
12721
|
context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
|
|
12217
12722
|
try {
|
|
12218
|
-
const ts = "2026-06-20T08:
|
|
12723
|
+
const ts = "2026-06-20T08:28:18.242Z";
|
|
12219
12724
|
if (ts && ts !== "__BUILD_TIMESTAMP__") {
|
|
12220
12725
|
context.addMessage("system", row2("Build Timestamp", ts));
|
|
12221
12726
|
}
|
|
@@ -12228,14 +12733,14 @@ var init_version = __esm({
|
|
|
12228
12733
|
});
|
|
12229
12734
|
|
|
12230
12735
|
// src/commands/feedback.ts
|
|
12231
|
-
import { join as
|
|
12232
|
-
import { existsSync as
|
|
12736
|
+
import { join as join33 } from "path";
|
|
12737
|
+
import { existsSync as existsSync29 } from "fs";
|
|
12233
12738
|
import { readFile as readFile25 } from "fs/promises";
|
|
12234
12739
|
async function readCliVersion() {
|
|
12235
12740
|
try {
|
|
12236
12741
|
const baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
12237
|
-
const pkgPath =
|
|
12238
|
-
if (!
|
|
12742
|
+
const pkgPath = join33(baseDir, "..", "..", "package.json");
|
|
12743
|
+
if (!existsSync29(pkgPath)) return "unknown";
|
|
12239
12744
|
const text = await readFile25(pkgPath, "utf8");
|
|
12240
12745
|
const json = JSON.parse(text);
|
|
12241
12746
|
return json.version ?? "unknown";
|
|
@@ -12277,7 +12782,7 @@ var init_feedback = __esm({
|
|
|
12277
12782
|
|
|
12278
12783
|
// src/commands/export.ts
|
|
12279
12784
|
import { homedir as homedir18 } from "os";
|
|
12280
|
-
import { join as
|
|
12785
|
+
import { join as join34 } from "path";
|
|
12281
12786
|
import { writeFile as writeFile15, mkdir as mkdir8 } from "fs/promises";
|
|
12282
12787
|
function formatTimestamp(date) {
|
|
12283
12788
|
return date.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
@@ -12327,10 +12832,10 @@ var init_export = __esm({
|
|
|
12327
12832
|
let outputPath;
|
|
12328
12833
|
let content;
|
|
12329
12834
|
if (arg === "" || arg === "json") {
|
|
12330
|
-
outputPath =
|
|
12835
|
+
outputPath = join34(homedir18(), `msapling-export-${timestamp}.json`);
|
|
12331
12836
|
content = buildJsonExport(history);
|
|
12332
12837
|
} else if (arg === "markdown" || arg === "md") {
|
|
12333
|
-
outputPath =
|
|
12838
|
+
outputPath = join34(homedir18(), `msapling-export-${timestamp}.md`);
|
|
12334
12839
|
content = buildMarkdownExport(history);
|
|
12335
12840
|
} else {
|
|
12336
12841
|
outputPath = arg;
|
|
@@ -12342,7 +12847,7 @@ var init_export = __esm({
|
|
|
12342
12847
|
}
|
|
12343
12848
|
}
|
|
12344
12849
|
try {
|
|
12345
|
-
const dir =
|
|
12850
|
+
const dir = join34(outputPath, "..");
|
|
12346
12851
|
await mkdir8(dir, { recursive: true });
|
|
12347
12852
|
await writeFile15(outputPath, content, "utf8");
|
|
12348
12853
|
context.addMessage("system", `Exported to: ${outputPath}`);
|
|
@@ -12538,15 +13043,15 @@ var init_plan = __esm({
|
|
|
12538
13043
|
|
|
12539
13044
|
// src/commands/note.ts
|
|
12540
13045
|
import { homedir as homedir19 } from "os";
|
|
12541
|
-
import { join as
|
|
12542
|
-
import { existsSync as
|
|
13046
|
+
import { join as join35 } from "path";
|
|
13047
|
+
import { existsSync as existsSync30 } from "fs";
|
|
12543
13048
|
import { readFile as readFile26, writeFile as writeFile16 } from "fs/promises";
|
|
12544
13049
|
function getNotesFilePath() {
|
|
12545
|
-
return
|
|
13050
|
+
return join35(homedir19(), ".msapling", "notes.json");
|
|
12546
13051
|
}
|
|
12547
13052
|
async function readNotes(filePath = getNotesFilePath()) {
|
|
12548
13053
|
try {
|
|
12549
|
-
if (!
|
|
13054
|
+
if (!existsSync30(filePath)) return [];
|
|
12550
13055
|
const raw = await readFile26(filePath, "utf8");
|
|
12551
13056
|
const parsed = JSON.parse(raw);
|
|
12552
13057
|
if (!Array.isArray(parsed)) return [];
|
|
@@ -12556,7 +13061,7 @@ async function readNotes(filePath = getNotesFilePath()) {
|
|
|
12556
13061
|
}
|
|
12557
13062
|
}
|
|
12558
13063
|
async function writeNotes(notes, filePath = getNotesFilePath()) {
|
|
12559
|
-
const dir =
|
|
13064
|
+
const dir = join35(homedir19(), ".msapling");
|
|
12560
13065
|
ensureConfigDir(dir);
|
|
12561
13066
|
await writeFile16(filePath, JSON.stringify(notes, null, 2), "utf8");
|
|
12562
13067
|
}
|
|
@@ -12703,16 +13208,16 @@ var init_todo = __esm({
|
|
|
12703
13208
|
|
|
12704
13209
|
// src/commands/outputStyle.ts
|
|
12705
13210
|
import { homedir as homedir20 } from "os";
|
|
12706
|
-
import { join as
|
|
12707
|
-
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";
|
|
12708
13213
|
function resolveHome() {
|
|
12709
13214
|
return process.env.HOME || process.env.USERPROFILE || homedir20();
|
|
12710
13215
|
}
|
|
12711
13216
|
function stylesDir() {
|
|
12712
|
-
return
|
|
13217
|
+
return join36(resolveHome(), ".msapling", "output-styles");
|
|
12713
13218
|
}
|
|
12714
13219
|
function activeFile() {
|
|
12715
|
-
return
|
|
13220
|
+
return join36(stylesDir(), ".active");
|
|
12716
13221
|
}
|
|
12717
13222
|
function parseStyleFile(text) {
|
|
12718
13223
|
const fm = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
|
@@ -12733,16 +13238,16 @@ function parseStyleFile(text) {
|
|
|
12733
13238
|
}
|
|
12734
13239
|
function listUserStyles() {
|
|
12735
13240
|
const dir = stylesDir();
|
|
12736
|
-
if (!
|
|
13241
|
+
if (!existsSync31(dir)) return [];
|
|
12737
13242
|
const out = [];
|
|
12738
|
-
for (const entry of
|
|
13243
|
+
for (const entry of readdirSync5(dir)) {
|
|
12739
13244
|
if (extname3(entry).toLowerCase() !== ".md") continue;
|
|
12740
|
-
const full =
|
|
13245
|
+
const full = join36(dir, entry);
|
|
12741
13246
|
try {
|
|
12742
|
-
const text =
|
|
13247
|
+
const text = readFileSync5(full, "utf8");
|
|
12743
13248
|
const { description, body } = parseStyleFile(text);
|
|
12744
13249
|
out.push({
|
|
12745
|
-
name:
|
|
13250
|
+
name: basename4(entry, ".md"),
|
|
12746
13251
|
description,
|
|
12747
13252
|
body,
|
|
12748
13253
|
source: "user",
|
|
@@ -12765,15 +13270,15 @@ function findStyle(name) {
|
|
|
12765
13270
|
function getActiveStyleName() {
|
|
12766
13271
|
try {
|
|
12767
13272
|
const f = activeFile();
|
|
12768
|
-
if (!
|
|
12769
|
-
return
|
|
13273
|
+
if (!existsSync31(f)) return "default";
|
|
13274
|
+
return readFileSync5(f, "utf8").trim() || "default";
|
|
12770
13275
|
} catch {
|
|
12771
13276
|
return "default";
|
|
12772
13277
|
}
|
|
12773
13278
|
}
|
|
12774
13279
|
function setActiveStyleName(name) {
|
|
12775
13280
|
const dir = stylesDir();
|
|
12776
|
-
if (!
|
|
13281
|
+
if (!existsSync31(dir)) mkdirSync6(dir, { recursive: true });
|
|
12777
13282
|
writeFileSync5(activeFile(), `${name}
|
|
12778
13283
|
`, "utf8");
|
|
12779
13284
|
}
|
|
@@ -12786,8 +13291,8 @@ function createUserStyle(name, description, body) {
|
|
|
12786
13291
|
throw new Error(`Invalid style name "${name}" \u2014 use letters, digits, _ and - only.`);
|
|
12787
13292
|
}
|
|
12788
13293
|
const dir = stylesDir();
|
|
12789
|
-
if (!
|
|
12790
|
-
const target =
|
|
13294
|
+
if (!existsSync31(dir)) mkdirSync6(dir, { recursive: true });
|
|
13295
|
+
const target = join36(dir, `${name}.md`);
|
|
12791
13296
|
const frontmatter = `---
|
|
12792
13297
|
description: ${description.replace(/\n/g, " ")}
|
|
12793
13298
|
---
|
|
@@ -14204,6 +14709,32 @@ var init_agents = __esm({
|
|
|
14204
14709
|
}
|
|
14205
14710
|
});
|
|
14206
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
|
+
|
|
14207
14738
|
// src/commands/index.ts
|
|
14208
14739
|
var commands_exports = {};
|
|
14209
14740
|
__export(commands_exports, {
|
|
@@ -14278,6 +14809,7 @@ var init_commands = __esm({
|
|
|
14278
14809
|
init_rewind();
|
|
14279
14810
|
init_bashes();
|
|
14280
14811
|
init_agents();
|
|
14812
|
+
init_stats();
|
|
14281
14813
|
commands = [
|
|
14282
14814
|
loginCommand,
|
|
14283
14815
|
logoutCommand,
|
|
@@ -14344,7 +14876,8 @@ var init_commands = __esm({
|
|
|
14344
14876
|
remoteAgentCommand,
|
|
14345
14877
|
rewindCommand,
|
|
14346
14878
|
bashesCommand,
|
|
14347
|
-
agentsCommand
|
|
14879
|
+
agentsCommand,
|
|
14880
|
+
statsCommand
|
|
14348
14881
|
];
|
|
14349
14882
|
}
|
|
14350
14883
|
});
|
|
@@ -14414,15 +14947,15 @@ var exec_exports = {};
|
|
|
14414
14947
|
__export(exec_exports, {
|
|
14415
14948
|
runExec: () => runExec
|
|
14416
14949
|
});
|
|
14417
|
-
import { existsSync as
|
|
14950
|
+
import { existsSync as existsSync34 } from "fs";
|
|
14418
14951
|
import { readFile as readFile29 } from "fs/promises";
|
|
14419
14952
|
import { homedir as homedir23 } from "os";
|
|
14420
|
-
import { join as
|
|
14953
|
+
import { join as join38 } from "path";
|
|
14421
14954
|
async function loadPersistedSettings() {
|
|
14422
14955
|
const out = { mode: "default", theme: null };
|
|
14423
14956
|
try {
|
|
14424
|
-
const p =
|
|
14425
|
-
if (!
|
|
14957
|
+
const p = join38(homedir23(), ".msapling", "settings.json");
|
|
14958
|
+
if (!existsSync34(p)) return out;
|
|
14426
14959
|
const raw = JSON.parse(await readFile29(p, "utf8"));
|
|
14427
14960
|
const parsed = parseApprovalMode(raw, Date.now());
|
|
14428
14961
|
if (parsed.kind === "ok") out.mode = parsed.mode;
|
|
@@ -15281,8 +15814,8 @@ __export(doctor_exports, {
|
|
|
15281
15814
|
runDoctor: () => runDoctor
|
|
15282
15815
|
});
|
|
15283
15816
|
import { homedir as homedir24, platform as platform4, tmpdir } from "os";
|
|
15284
|
-
import { join as
|
|
15285
|
-
import { existsSync as
|
|
15817
|
+
import { join as join39 } from "path";
|
|
15818
|
+
import { existsSync as existsSync35, statSync as statSync9 } from "fs";
|
|
15286
15819
|
import { readdir as readdir3, mkdir as mkdir9, rm as rm3 } from "fs/promises";
|
|
15287
15820
|
import { exec as exec2 } from "child_process";
|
|
15288
15821
|
import { promisify } from "util";
|
|
@@ -15305,8 +15838,8 @@ async function checkNodeVersion() {
|
|
|
15305
15838
|
};
|
|
15306
15839
|
}
|
|
15307
15840
|
async function checkConfigDir() {
|
|
15308
|
-
const configDir =
|
|
15309
|
-
if (!
|
|
15841
|
+
const configDir = join39(homedir24(), ".msapling");
|
|
15842
|
+
if (!existsSync35(configDir)) {
|
|
15310
15843
|
return {
|
|
15311
15844
|
name: "Config directory",
|
|
15312
15845
|
status: "WARN",
|
|
@@ -15314,7 +15847,7 @@ async function checkConfigDir() {
|
|
|
15314
15847
|
remediation: `mkdir -p "${configDir}" && chmod 700 "${configDir}"`
|
|
15315
15848
|
};
|
|
15316
15849
|
}
|
|
15317
|
-
const stats =
|
|
15850
|
+
const stats = statSync9(configDir);
|
|
15318
15851
|
if (!stats.isDirectory()) {
|
|
15319
15852
|
return {
|
|
15320
15853
|
name: "Config directory",
|
|
@@ -15375,7 +15908,7 @@ async function checkPathConflicts() {
|
|
|
15375
15908
|
const timedOutDirs = [];
|
|
15376
15909
|
const DIR_TIMEOUT_MS = 1500;
|
|
15377
15910
|
for (const dir of paths) {
|
|
15378
|
-
if (!dir || !
|
|
15911
|
+
if (!dir || !existsSync35(dir)) continue;
|
|
15379
15912
|
try {
|
|
15380
15913
|
const files = await Promise.race([
|
|
15381
15914
|
readdir3(dir),
|
|
@@ -15388,7 +15921,7 @@ async function checkPathConflicts() {
|
|
|
15388
15921
|
]);
|
|
15389
15922
|
for (const file of files) {
|
|
15390
15923
|
if (file === "msapling" || file === "msapling.exe" || file === "msapling.py") {
|
|
15391
|
-
const fullPath =
|
|
15924
|
+
const fullPath = join39(dir, file);
|
|
15392
15925
|
conflicts.push(fullPath);
|
|
15393
15926
|
}
|
|
15394
15927
|
}
|
|
@@ -15506,9 +16039,9 @@ async function checkTokenValidity() {
|
|
|
15506
16039
|
}
|
|
15507
16040
|
async function checkOsSpecific() {
|
|
15508
16041
|
if (platform4() === "win32") {
|
|
15509
|
-
const testDir =
|
|
16042
|
+
const testDir = join39(tmpdir(), `msapling-longpath-test-${Date.now()}`);
|
|
15510
16043
|
const longDirName = "A".repeat(260);
|
|
15511
|
-
const testPath =
|
|
16044
|
+
const testPath = join39(testDir, longDirName);
|
|
15512
16045
|
try {
|
|
15513
16046
|
await mkdir9(testDir, { recursive: true });
|
|
15514
16047
|
try {
|
|
@@ -17185,8 +17718,8 @@ var init_libesm = __esm({
|
|
|
17185
17718
|
});
|
|
17186
17719
|
|
|
17187
17720
|
// ../core/src/mcp/catalog.ts
|
|
17188
|
-
import { readdirSync as
|
|
17189
|
-
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";
|
|
17190
17723
|
function buildFileTree(root, maxFiles) {
|
|
17191
17724
|
const SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", "build", "dist", ".venv", "venv", ".next", "__pycache__", ".dart_tool", ".bun", "target"]);
|
|
17192
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"]);
|
|
@@ -17196,17 +17729,17 @@ function buildFileTree(root, maxFiles) {
|
|
|
17196
17729
|
const dir = queue.shift();
|
|
17197
17730
|
let entries;
|
|
17198
17731
|
try {
|
|
17199
|
-
entries =
|
|
17732
|
+
entries = readdirSync6(dir);
|
|
17200
17733
|
} catch {
|
|
17201
17734
|
continue;
|
|
17202
17735
|
}
|
|
17203
17736
|
for (const name of entries) {
|
|
17204
17737
|
if (out.length >= maxFiles) break;
|
|
17205
17738
|
if (SKIP_DIRS2.has(name)) continue;
|
|
17206
|
-
const full =
|
|
17739
|
+
const full = join40(dir, name);
|
|
17207
17740
|
let s;
|
|
17208
17741
|
try {
|
|
17209
|
-
s =
|
|
17742
|
+
s = statSync10(full);
|
|
17210
17743
|
} catch {
|
|
17211
17744
|
continue;
|
|
17212
17745
|
}
|
|
@@ -17227,12 +17760,12 @@ function readFilesAsContext(root, files, maxKB) {
|
|
|
17227
17760
|
for (const f of files) {
|
|
17228
17761
|
let body;
|
|
17229
17762
|
try {
|
|
17230
|
-
body =
|
|
17763
|
+
body = readFileSync6(f, "utf8");
|
|
17231
17764
|
} catch {
|
|
17232
17765
|
continue;
|
|
17233
17766
|
}
|
|
17234
17767
|
if (body.length > cap) body = body.slice(0, cap) + "\n[...truncated]";
|
|
17235
|
-
const rel =
|
|
17768
|
+
const rel = relative17(root, f).replace(/\\/g, "/");
|
|
17236
17769
|
parts.push(`### ${rel}
|
|
17237
17770
|
|
|
17238
17771
|
\`\`\`
|
|
@@ -18009,7 +18542,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
18009
18542
|
var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
|
|
18010
18543
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
18011
18544
|
"\u25CF MSapling CLI v",
|
|
18012
|
-
"2.3.6-beta.
|
|
18545
|
+
"2.3.6-beta.49"
|
|
18013
18546
|
] }),
|
|
18014
18547
|
/* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
|
|
18015
18548
|
] });
|
|
@@ -18412,6 +18945,7 @@ function createIdleAwarePoll(pollingIntervalRef, lastActivityRef, client, setUse
|
|
|
18412
18945
|
|
|
18413
18946
|
// src/state/commandHandler.ts
|
|
18414
18947
|
init_esm_shims();
|
|
18948
|
+
init_src3();
|
|
18415
18949
|
init_commands();
|
|
18416
18950
|
init_plan();
|
|
18417
18951
|
import { spawn as spawn10 } from "child_process";
|
|
@@ -18419,14 +18953,14 @@ import { spawn as spawn10 } from "child_process";
|
|
|
18419
18953
|
// src/state/persistentState.ts
|
|
18420
18954
|
init_esm_shims();
|
|
18421
18955
|
import { homedir as homedir22 } from "os";
|
|
18422
|
-
import { join as
|
|
18423
|
-
import { existsSync as
|
|
18956
|
+
import { join as join37, dirname as dirname5 } from "path";
|
|
18957
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync7 } from "fs";
|
|
18424
18958
|
import { readFile as readFile27, writeFile as writeFile17, rename as rename3 } from "fs/promises";
|
|
18425
18959
|
import { randomBytes as randomBytes15 } from "crypto";
|
|
18426
|
-
var STATE_PATH =
|
|
18960
|
+
var STATE_PATH = join37(homedir22(), ".msapling", "state.json");
|
|
18427
18961
|
async function loadPersistentState(statePath = STATE_PATH) {
|
|
18428
18962
|
try {
|
|
18429
|
-
if (!
|
|
18963
|
+
if (!existsSync32(statePath)) return { version: 1 };
|
|
18430
18964
|
const text = await readFile27(statePath, "utf8");
|
|
18431
18965
|
const parsed = JSON.parse(text);
|
|
18432
18966
|
if (parsed.version !== 1) return { version: 1 };
|
|
@@ -18442,7 +18976,7 @@ async function loadPersistentState(statePath = STATE_PATH) {
|
|
|
18442
18976
|
async function savePersistentState(state, statePath = STATE_PATH) {
|
|
18443
18977
|
try {
|
|
18444
18978
|
const dir = dirname5(statePath);
|
|
18445
|
-
if (!
|
|
18979
|
+
if (!existsSync32(dir)) mkdirSync7(dir, { recursive: true });
|
|
18446
18980
|
const existing = await loadPersistentState(statePath);
|
|
18447
18981
|
const merged = {
|
|
18448
18982
|
version: 1,
|
|
@@ -18511,6 +19045,11 @@ ${prompt4}` : prompt4;
|
|
|
18511
19045
|
if (usage !== null) {
|
|
18512
19046
|
ctx.setLastCost(usage.cost_usd);
|
|
18513
19047
|
ctx.setSessionCost((prev) => prev + usage.cost_usd);
|
|
19048
|
+
getSessionStats().recordUsage(
|
|
19049
|
+
usage.cost_usd,
|
|
19050
|
+
usage.prompt_tokens,
|
|
19051
|
+
usage.completion_tokens
|
|
19052
|
+
);
|
|
18514
19053
|
}
|
|
18515
19054
|
ctx.setContextBudgetSnap(snapshotBudget(ctx.agent.getContextBudget()));
|
|
18516
19055
|
} catch (e) {
|
|
@@ -18589,9 +19128,9 @@ ${prompt4}` : prompt4;
|
|
|
18589
19128
|
for (const mention of fileMentions) {
|
|
18590
19129
|
const filePath = mention.slice(1);
|
|
18591
19130
|
try {
|
|
18592
|
-
const { existsSync:
|
|
19131
|
+
const { existsSync: existsSync36 } = await import("fs");
|
|
18593
19132
|
const { readFile: readFile30 } = await import("fs/promises");
|
|
18594
|
-
if (
|
|
19133
|
+
if (existsSync36(filePath)) {
|
|
18595
19134
|
const content = await readFile30(filePath, "utf8");
|
|
18596
19135
|
const MAX_LEN = 32768;
|
|
18597
19136
|
const truncated = content.length > MAX_LEN ? content.slice(0, MAX_LEN) + "\n...[TRUNCATED]" : content;
|
|
@@ -18617,6 +19156,7 @@ ${finalCmd}`;
|
|
|
18617
19156
|
if (usage !== null) {
|
|
18618
19157
|
ctx.setLastCost(usage.cost_usd);
|
|
18619
19158
|
ctx.setSessionCost((prev) => prev + usage.cost_usd);
|
|
19159
|
+
getSessionStats().recordUsage(usage.cost_usd, usage.prompt_tokens, usage.completion_tokens);
|
|
18620
19160
|
}
|
|
18621
19161
|
ctx.setContextBudgetSnap(snapshotBudget(ctx.agent.getContextBudget()));
|
|
18622
19162
|
} catch (e) {
|
|
@@ -18653,7 +19193,7 @@ init_src3();
|
|
|
18653
19193
|
init_src();
|
|
18654
19194
|
init_parseApprovalMode();
|
|
18655
19195
|
import { readFile as readFile28 } from "fs/promises";
|
|
18656
|
-
import { existsSync as
|
|
19196
|
+
import { existsSync as existsSync33 } from "fs";
|
|
18657
19197
|
async function initSession(ctx) {
|
|
18658
19198
|
try {
|
|
18659
19199
|
const journalEncrypted = await initJournalEncryption();
|
|
@@ -18679,9 +19219,9 @@ async function initSession(ctx) {
|
|
|
18679
19219
|
}
|
|
18680
19220
|
try {
|
|
18681
19221
|
const { homedir: homedir25 } = await import("os");
|
|
18682
|
-
const { join:
|
|
18683
|
-
const userSettingsPath =
|
|
18684
|
-
if (
|
|
19222
|
+
const { join: join42 } = await import("path");
|
|
19223
|
+
const userSettingsPath = join42(homedir25(), ".msapling", "settings.json");
|
|
19224
|
+
if (existsSync33(userSettingsPath)) {
|
|
18685
19225
|
const userText = await readFile28(userSettingsPath, "utf8");
|
|
18686
19226
|
let parsed;
|
|
18687
19227
|
try {
|
|
@@ -19098,14 +19638,14 @@ var App = ({ compact: compact2 = false, continueSession: continueSession2 = fals
|
|
|
19098
19638
|
|
|
19099
19639
|
// src/runtime/bootstrap.ts
|
|
19100
19640
|
init_esm_shims();
|
|
19101
|
-
import { readFileSync as
|
|
19641
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
19102
19642
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
19103
|
-
import { dirname as dirname6, join as
|
|
19643
|
+
import { dirname as dirname6, join as join41 } from "path";
|
|
19104
19644
|
function readCliVersion2() {
|
|
19105
19645
|
const here = dirname6(fileURLToPath2(import.meta.url));
|
|
19106
19646
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
19107
19647
|
try {
|
|
19108
|
-
const pkg = JSON.parse(
|
|
19648
|
+
const pkg = JSON.parse(readFileSync7(join41(here, rel), "utf8"));
|
|
19109
19649
|
if (pkg.name && pkg.version) {
|
|
19110
19650
|
return { name: pkg.name, version: pkg.version };
|
|
19111
19651
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mtreeai/msapling-cli",
|
|
3
|
-
"version": "2.3.6-beta.
|
|
3
|
+
"version": "2.3.6-beta.49",
|
|
4
4
|
"description": "MSapling CLI — React/Ink terminal client for the MSapling backend (chat, projects, MDrive, agent tools). Proprietary; redistribution prohibited.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "MSapling Team",
|