@cryer/star-cli 0.2.1 → 0.2.2
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/{chunk-MJ2O7NWI.js → chunk-GN236FD4.js} +157 -11
- package/dist/chunk-GN236FD4.js.map +1 -0
- package/dist/{loop-R3RQCNFT.js → loop-ADVWZA62.js} +2 -2
- package/dist/main.js +571 -128
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-MJ2O7NWI.js.map +0 -1
- /package/dist/{loop-R3RQCNFT.js.map → loop-ADVWZA62.js.map} +0 -0
|
@@ -433,6 +433,12 @@ function matchAllowRule(rule, req) {
|
|
|
433
433
|
return target !== void 0 && globMatch(rule.pattern, target);
|
|
434
434
|
}
|
|
435
435
|
function isAllowedByRules(rules, req) {
|
|
436
|
+
return matchesAnyRule(rules, req);
|
|
437
|
+
}
|
|
438
|
+
function isDeniedByRules(rules, req) {
|
|
439
|
+
return matchesAnyRule(rules, req);
|
|
440
|
+
}
|
|
441
|
+
function matchesAnyRule(rules, req) {
|
|
436
442
|
return rules.some((raw) => {
|
|
437
443
|
const rule = parseAllowRule(raw);
|
|
438
444
|
return rule !== null && matchAllowRule(rule, req);
|
|
@@ -484,7 +490,7 @@ function isSensitivePath(p) {
|
|
|
484
490
|
}
|
|
485
491
|
return base === ".env" || base.startsWith(".env.") || base === "id_rsa" || base.endsWith(".pem");
|
|
486
492
|
}
|
|
487
|
-
function checkPermission(mode, req, ctx, allowRules = []) {
|
|
493
|
+
function checkPermission(mode, req, ctx, allowRules = [], denyRules = []) {
|
|
488
494
|
if (mode === "yolo") {
|
|
489
495
|
return "allow";
|
|
490
496
|
}
|
|
@@ -504,6 +510,9 @@ function checkPermission(mode, req, ctx, allowRules = []) {
|
|
|
504
510
|
return "deny";
|
|
505
511
|
}
|
|
506
512
|
}
|
|
513
|
+
if (isDeniedByRules(denyRules, req)) {
|
|
514
|
+
return "deny";
|
|
515
|
+
}
|
|
507
516
|
if (mode === "auto") {
|
|
508
517
|
return "allow";
|
|
509
518
|
}
|
|
@@ -745,6 +754,9 @@ function globalConfigPath() {
|
|
|
745
754
|
function sessionsDir() {
|
|
746
755
|
return path4.join(starHome(), "sessions");
|
|
747
756
|
}
|
|
757
|
+
function userMemoryPath() {
|
|
758
|
+
return path4.join(starHome(), "MEMORY.md");
|
|
759
|
+
}
|
|
748
760
|
function projectConfigPath(cwd) {
|
|
749
761
|
return path4.join(cwd, ".star", "config.toml");
|
|
750
762
|
}
|
|
@@ -1197,10 +1209,11 @@ import path9 from "path";
|
|
|
1197
1209
|
import { z as z4 } from "zod";
|
|
1198
1210
|
|
|
1199
1211
|
// src/tools/fs/util.ts
|
|
1212
|
+
import { readFileSync as readFileSync3, statSync as statSync3 } from "fs";
|
|
1200
1213
|
import { readdir, stat } from "fs/promises";
|
|
1201
1214
|
import path8 from "path";
|
|
1202
1215
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist"]);
|
|
1203
|
-
async function walkFiles(root, skipDirs = SKIP_DIRS) {
|
|
1216
|
+
async function walkFiles(root, skipDirs = SKIP_DIRS, options = {}) {
|
|
1204
1217
|
const out = [];
|
|
1205
1218
|
async function walk(dir, relBase) {
|
|
1206
1219
|
const entries = await readdir(dir, { withFileTypes: true }).catch(() => null);
|
|
@@ -1211,10 +1224,14 @@ async function walkFiles(root, skipDirs = SKIP_DIRS) {
|
|
|
1211
1224
|
const abs = path8.join(dir, entry.name);
|
|
1212
1225
|
const rel = relBase ? `${relBase}/${entry.name}` : entry.name;
|
|
1213
1226
|
if (entry.isDirectory()) {
|
|
1214
|
-
if (
|
|
1215
|
-
|
|
1227
|
+
if (skipDirs.has(entry.name) || options.ignore?.(abs, true)) {
|
|
1228
|
+
continue;
|
|
1216
1229
|
}
|
|
1230
|
+
await walk(abs, rel);
|
|
1217
1231
|
} else if (entry.isFile()) {
|
|
1232
|
+
if (options.ignore?.(abs, false)) {
|
|
1233
|
+
continue;
|
|
1234
|
+
}
|
|
1218
1235
|
try {
|
|
1219
1236
|
const st = await stat(abs);
|
|
1220
1237
|
out.push({ abs, rel, mtimeMs: st.mtimeMs });
|
|
@@ -1263,6 +1280,69 @@ function matchesGlob(pattern, relPath) {
|
|
|
1263
1280
|
}
|
|
1264
1281
|
return globToRegExp(normalized).test(relPath);
|
|
1265
1282
|
}
|
|
1283
|
+
var IGNORE_FILE = ".starignore";
|
|
1284
|
+
var ignoreCache = /* @__PURE__ */ new Map();
|
|
1285
|
+
function parseIgnorePatterns(body) {
|
|
1286
|
+
const out = [];
|
|
1287
|
+
for (const raw of body.split("\n")) {
|
|
1288
|
+
const line = raw.trim();
|
|
1289
|
+
if (!line || line.startsWith("#")) {
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
if (line.endsWith("/")) {
|
|
1293
|
+
const pattern = line.slice(0, -1);
|
|
1294
|
+
if (pattern) {
|
|
1295
|
+
out.push({ pattern, dirOnly: true });
|
|
1296
|
+
}
|
|
1297
|
+
} else {
|
|
1298
|
+
out.push({ pattern: line, dirOnly: false });
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return out;
|
|
1302
|
+
}
|
|
1303
|
+
function loadIgnorePatterns(dir) {
|
|
1304
|
+
const file = path8.join(dir, IGNORE_FILE);
|
|
1305
|
+
let mtimeMs;
|
|
1306
|
+
try {
|
|
1307
|
+
mtimeMs = statSync3(file).mtimeMs;
|
|
1308
|
+
} catch {
|
|
1309
|
+
return [];
|
|
1310
|
+
}
|
|
1311
|
+
const hit = ignoreCache.get(file);
|
|
1312
|
+
if (hit && hit.mtimeMs === mtimeMs) {
|
|
1313
|
+
return hit.patterns;
|
|
1314
|
+
}
|
|
1315
|
+
let patterns = [];
|
|
1316
|
+
try {
|
|
1317
|
+
patterns = parseIgnorePatterns(readFileSync3(file, "utf8"));
|
|
1318
|
+
} catch {
|
|
1319
|
+
patterns = [];
|
|
1320
|
+
}
|
|
1321
|
+
ignoreCache.set(file, { mtimeMs, patterns });
|
|
1322
|
+
return patterns;
|
|
1323
|
+
}
|
|
1324
|
+
function createIgnorePredicate(cwd) {
|
|
1325
|
+
const patterns = loadIgnorePatterns(cwd);
|
|
1326
|
+
if (patterns.length === 0) {
|
|
1327
|
+
return void 0;
|
|
1328
|
+
}
|
|
1329
|
+
const root = path8.resolve(cwd);
|
|
1330
|
+
return (absPath, isDir) => {
|
|
1331
|
+
const rel = path8.relative(root, absPath).split(path8.sep).join("/");
|
|
1332
|
+
if (!rel || rel.startsWith("..")) {
|
|
1333
|
+
return false;
|
|
1334
|
+
}
|
|
1335
|
+
for (const { pattern, dirOnly } of patterns) {
|
|
1336
|
+
if (dirOnly && !isDir) {
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
if (matchesGlob(pattern, rel)) {
|
|
1340
|
+
return true;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
return false;
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1266
1346
|
function isSensitivePath2(filePath) {
|
|
1267
1347
|
const base = path8.basename(filePath);
|
|
1268
1348
|
if (base === ".env.example" || base === ".env.sample" || base === ".env.template") {
|
|
@@ -1292,7 +1372,7 @@ var globTool = {
|
|
|
1292
1372
|
parameters: schema3,
|
|
1293
1373
|
async execute(args, ctx) {
|
|
1294
1374
|
const root = path9.resolve(ctx.cwd, args.path ?? ".");
|
|
1295
|
-
const files = await walkFiles(root);
|
|
1375
|
+
const files = await walkFiles(root, void 0, { ignore: createIgnorePredicate(ctx.cwd) });
|
|
1296
1376
|
const matched = files.filter((f) => matchesGlob(args.pattern, f.rel)).sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, MAX_RESULTS);
|
|
1297
1377
|
if (matched.length === 0) {
|
|
1298
1378
|
return { content: `No files matched pattern: ${args.pattern}` };
|
|
@@ -1331,7 +1411,7 @@ var grepTool = {
|
|
|
1331
1411
|
if (st.isFile()) {
|
|
1332
1412
|
files = [{ abs: root, rel: path10.basename(root), mtimeMs: st.mtimeMs }];
|
|
1333
1413
|
} else {
|
|
1334
|
-
files = await walkFiles(root);
|
|
1414
|
+
files = await walkFiles(root, void 0, { ignore: createIgnorePredicate(ctx.cwd) });
|
|
1335
1415
|
}
|
|
1336
1416
|
} catch {
|
|
1337
1417
|
return { content: `Path not found: ${args.path ?? "."}`, isError: true };
|
|
@@ -1959,7 +2039,7 @@ function createSubagentTool(deps) {
|
|
|
1959
2039
|
description: z12.string().optional().describe("Short label for the subtask, shown in the result header.")
|
|
1960
2040
|
}),
|
|
1961
2041
|
async execute(args, ctx) {
|
|
1962
|
-
const { AgentLoop: AgentLoop2 } = await import("./loop-
|
|
2042
|
+
const { AgentLoop: AgentLoop2 } = await import("./loop-ADVWZA62.js");
|
|
1963
2043
|
const child = new AgentLoop2({
|
|
1964
2044
|
model: deps.model,
|
|
1965
2045
|
registry: createDefaultRegistry(),
|
|
@@ -2016,6 +2096,64 @@ ${report || "(subagent produced no text output)"}` };
|
|
|
2016
2096
|
};
|
|
2017
2097
|
}
|
|
2018
2098
|
|
|
2099
|
+
// src/agent/user-memory.ts
|
|
2100
|
+
import { appendFileSync, mkdirSync, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
2101
|
+
import path14 from "path";
|
|
2102
|
+
import { z as z13 } from "zod";
|
|
2103
|
+
var USER_MEMORY_MAX_CHARS = 32 * 1024;
|
|
2104
|
+
var REMEMBER_TEXT_MAX_CHARS = 500;
|
|
2105
|
+
var cache3 = /* @__PURE__ */ new Map();
|
|
2106
|
+
function readUserMemory(home = starHome()) {
|
|
2107
|
+
const file = path14.join(home, "MEMORY.md");
|
|
2108
|
+
let mtimeMs;
|
|
2109
|
+
try {
|
|
2110
|
+
mtimeMs = statSync4(file).mtimeMs;
|
|
2111
|
+
} catch {
|
|
2112
|
+
return null;
|
|
2113
|
+
}
|
|
2114
|
+
const hit = cache3.get(file);
|
|
2115
|
+
if (hit && hit.mtimeMs === mtimeMs) return hit.block;
|
|
2116
|
+
let block = null;
|
|
2117
|
+
try {
|
|
2118
|
+
let body = readFileSync4(file, "utf8").trim();
|
|
2119
|
+
if (body) {
|
|
2120
|
+
if (body.length > USER_MEMORY_MAX_CHARS) {
|
|
2121
|
+
body = `${body.slice(0, USER_MEMORY_MAX_CHARS)}
|
|
2122
|
+
|
|
2123
|
+
[MEMORY.md truncated to ${USER_MEMORY_MAX_CHARS} characters]`;
|
|
2124
|
+
}
|
|
2125
|
+
block = [
|
|
2126
|
+
"# User memory (MEMORY.md)",
|
|
2127
|
+
"The following notes were saved by the user across sessions. Respect them unless they conflict with the current request.",
|
|
2128
|
+
body
|
|
2129
|
+
].join("\n");
|
|
2130
|
+
}
|
|
2131
|
+
} catch {
|
|
2132
|
+
block = null;
|
|
2133
|
+
}
|
|
2134
|
+
cache3.set(file, { mtimeMs, block });
|
|
2135
|
+
return block;
|
|
2136
|
+
}
|
|
2137
|
+
function appendUserMemory(text, home = starHome()) {
|
|
2138
|
+
mkdirSync(home, { recursive: true });
|
|
2139
|
+
appendFileSync(path14.join(home, "MEMORY.md"), `- ${text}
|
|
2140
|
+
`, "utf8");
|
|
2141
|
+
}
|
|
2142
|
+
function createRememberTool(deps = {}) {
|
|
2143
|
+
return {
|
|
2144
|
+
name: "remember",
|
|
2145
|
+
description: `Save a note to the user's long-term memory (${userMemoryPath()}), injected into every future session. ONLY use when the user explicitly asks to remember or save something. Never use autonomously.`,
|
|
2146
|
+
permission: "write",
|
|
2147
|
+
parameters: z13.object({
|
|
2148
|
+
text: z13.string().max(REMEMBER_TEXT_MAX_CHARS).describe("The note to save, as a single concise sentence.")
|
|
2149
|
+
}),
|
|
2150
|
+
async execute(args) {
|
|
2151
|
+
appendUserMemory(args.text, deps.home);
|
|
2152
|
+
return { content: `Saved to user memory: ${args.text}` };
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2019
2157
|
// src/agent/loop.ts
|
|
2020
2158
|
var PLAN_MODE_PROMPT = "\n\nYou are currently in PLAN MODE. Research the task using only the read-only tools available to you, then present a concrete, step-by-step implementation plan as your final answer. You must not modify files, run shell commands, or otherwise change the system \u2014 write/exec tools are unavailable in this mode. Do not ask the user to run commands for you; note manual steps in the plan instead. The user will review your plan and approve it before any execution begins.";
|
|
2021
2159
|
var AgentLoop = class {
|
|
@@ -2041,6 +2179,7 @@ var AgentLoop = class {
|
|
|
2041
2179
|
this.bindSnapshotHooks(opts.sessionStore);
|
|
2042
2180
|
}
|
|
2043
2181
|
opts.registry?.register(createSkillTool({ cwd: opts.cwd }));
|
|
2182
|
+
opts.registry?.register(createRememberTool());
|
|
2044
2183
|
const depth = opts.subagentDepth ?? 0;
|
|
2045
2184
|
if (opts.registry && depth < MAX_SUBAGENT_DEPTH) {
|
|
2046
2185
|
opts.registry.register(
|
|
@@ -2285,8 +2424,9 @@ var AgentLoop = class {
|
|
|
2285
2424
|
// /model switch) replaced the history with one that has none. Git context
|
|
2286
2425
|
// (branch, dirty count, recent commits) is refreshed here too, so the model
|
|
2287
2426
|
// always sees the current repo state; any git failure is silently skipped.
|
|
2288
|
-
// Project memory (AGENTS.md in the cwd)
|
|
2289
|
-
//
|
|
2427
|
+
// Project memory (AGENTS.md in the cwd) and user memory (~/.star-cli/
|
|
2428
|
+
// MEMORY.md) are appended as delimited blocks; both cache by mtime, so this
|
|
2429
|
+
// stays cheap per turn. The
|
|
2290
2430
|
// skills listing is refreshed here as well, so skills added mid-session
|
|
2291
2431
|
// show up in the next turn's system prompt.
|
|
2292
2432
|
syncSystemMessage() {
|
|
@@ -2297,6 +2437,8 @@ var AgentLoop = class {
|
|
|
2297
2437
|
if (plan) parts.push(PLAN_MODE_PROMPT.trim());
|
|
2298
2438
|
const memory = readProjectMemory(this.opts.cwd);
|
|
2299
2439
|
if (memory) parts.push(memory);
|
|
2440
|
+
const userMemory = readUserMemory();
|
|
2441
|
+
if (userMemory) parts.push(userMemory);
|
|
2300
2442
|
const skills = discoverSkills(this.opts.cwd);
|
|
2301
2443
|
if (skills.length > 0) parts.push(formatSkillsBlock(skills));
|
|
2302
2444
|
const git = getGitSummary(this.opts.cwd);
|
|
@@ -2378,7 +2520,8 @@ ${summary}`
|
|
|
2378
2520
|
config.permissionMode,
|
|
2379
2521
|
{ toolName: call.name, args: call.args, level: tool.permission },
|
|
2380
2522
|
{ cwd },
|
|
2381
|
-
config.permissions.allow
|
|
2523
|
+
config.permissions.allow,
|
|
2524
|
+
config.permissions.deny
|
|
2382
2525
|
);
|
|
2383
2526
|
if (decision === "deny") {
|
|
2384
2527
|
return { content: `Permission denied for tool "${call.name}".`, isError: true };
|
|
@@ -2450,12 +2593,14 @@ export {
|
|
|
2450
2593
|
starHome,
|
|
2451
2594
|
globalConfigPath,
|
|
2452
2595
|
sessionsDir,
|
|
2596
|
+
userMemoryPath,
|
|
2453
2597
|
projectConfigPath,
|
|
2454
2598
|
discoverSkills,
|
|
2455
2599
|
defaultTaskManager,
|
|
2456
2600
|
resolveShell,
|
|
2457
2601
|
bashTool,
|
|
2458
2602
|
SKIP_DIRS,
|
|
2603
|
+
createIgnorePredicate,
|
|
2459
2604
|
isSensitivePath2 as isSensitivePath,
|
|
2460
2605
|
formatTaskList,
|
|
2461
2606
|
formatTaskStarted,
|
|
@@ -2463,7 +2608,8 @@ export {
|
|
|
2463
2608
|
TodoStore,
|
|
2464
2609
|
formatTodos,
|
|
2465
2610
|
createDefaultRegistry,
|
|
2611
|
+
appendUserMemory,
|
|
2466
2612
|
PLAN_MODE_PROMPT,
|
|
2467
2613
|
AgentLoop
|
|
2468
2614
|
};
|
|
2469
|
-
//# sourceMappingURL=chunk-
|
|
2615
|
+
//# sourceMappingURL=chunk-GN236FD4.js.map
|