agent-from-zero 0.35.7 → 0.36.0
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/README.md +1 -1
- package/dist/agent.js +4 -3
- package/dist/auto-debug.js +5 -2
- package/dist/ink/commands.js +2 -2
- package/dist/memory.js +14 -4
- package/dist/session.js +11 -5
- package/dist/state.js +78 -0
- package/dist/telemetry.js +16 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -131,7 +131,7 @@ export MINI_AGENT_MODEL=gpt-4.1-mini
|
|
|
131
131
|
- 实例名跟着它在做的事走:发出第一条消息后会生成会话标题(比如 “Fix login bug”),实例名随之变成 `fix-login-bug`,标签页标题就是 `✳ Fix login bug`,和 `/peers` 里看到的一致。还没有标题时用目录名。`/rename <名字>` 可以手动指定名字,指定后标签页显示 `✳ 名字 · 标题`,之后不会再跟着标题变。
|
|
132
132
|
- agent 之间也能自己沟通:模型有 `list_peers` 工具,`send_message` 可以直接发给其他实例。对方空闲时会自动开一轮处理;忙的话,在它当前这一步工具调用结束后插进对话。你在 Talk 模式里打的字则会等对方这一轮结束再处理,因为回答要发回给你。另一个 agent 发来的消息不算用户的授权。为了防止两个 agent 互相回复停不下来,连续自动处理 5 轮之后会暂停,等你输入任何内容再继续。
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
peer 实例的数据存在 `~/.config/mini-agent/sessions/` 下,只在本机,不经过网络。会话快照和长期记忆按 cwd 分桶存在 `~/.config/mini-agent/projects/<cwd-slug>/` 下,不会在代码仓库里留下目录。
|
|
135
135
|
|
|
136
136
|
### 目标模式 /goal
|
|
137
137
|
|
package/dist/agent.js
CHANGED
|
@@ -34,7 +34,7 @@ import { gitBranch, toggleLastCollapsed, revealReasoning, clearReasoning, reveal
|
|
|
34
34
|
import { promptSelect, promptForm } from "./menu.js"; // arrow-key approval menu + multi-question form
|
|
35
35
|
import { editLine } from "./editor.js"; // our own line editor (keeps the status footer pinned even when input wraps)
|
|
36
36
|
import { normalizeDroppedPaths } from "./drop.js"; // drag-and-drop: a dropped file's path → a clean absolute path in the input
|
|
37
|
-
import { rememberTool, readMemory, readMemoryTyped, extractMemories,
|
|
37
|
+
import { rememberTool, readMemory, readMemoryTyped, extractMemories, memoryPath } from "./memory.js"; // long-term project memory + auto-extract
|
|
38
38
|
import { allSkills, currentSkills, findSkill, recordSkillUsage, skillLocked, skillMode, userSkillMessages } from "./skills.js"; // Markdown-as-plugin skills
|
|
39
39
|
import { initCostMeter, DEFAULT_PRICING } from "./cost.js"; // token & cost accounting for /cost
|
|
40
40
|
import { launchInk } from "./ink/launch.js"; // the Ink REPL — the default front-end for interactive sessions
|
|
@@ -171,7 +171,8 @@ async function main() {
|
|
|
171
171
|
const systemMessage = buildSystemMessage(projectInstructions);
|
|
172
172
|
let messages = [{ role: "system", content: systemMessage }]; // the whole conversation lives here, across turns
|
|
173
173
|
// ---- Session persistence ---------------------------------------------------------
|
|
174
|
-
// Every conversation is snapshotted to
|
|
174
|
+
// Every conversation is snapshotted to the project's home bucket
|
|
175
|
+
// (~/.config/mini-agent/projects/<slug>/sessions) after each turn;
|
|
175
176
|
// -r / --resume picks up the most recent one. The system message is NOT
|
|
176
177
|
// restored from disk — it is rebuilt fresh, because AGENT.md may have changed.
|
|
177
178
|
let sessionId = newSessionId(); // this session's identity (and file name)
|
|
@@ -777,7 +778,7 @@ async function main() {
|
|
|
777
778
|
case "/memory": {
|
|
778
779
|
const entries = readMemoryTyped(); // the durable, typed facts carried across sessions
|
|
779
780
|
console.log(chalk.dim(entries.length
|
|
780
|
-
? `long-term memory (${
|
|
781
|
+
? `long-term memory (${memoryPath()}):\n${entries.map((e) => ` [${e.type}] ${e.fact}`).join("\n")}`
|
|
781
782
|
: `(no long-term memory yet — the model saves facts with the remember tool${CONFIG.memory.autoExtract ? "" : "; turn on settings.memory.autoExtract to capture them automatically"})`));
|
|
782
783
|
return true;
|
|
783
784
|
}
|
package/dist/auto-debug.js
CHANGED
|
@@ -2,10 +2,13 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { emit } from "./telemetry.js";
|
|
5
|
+
import { migrateLegacyState, projectStateFile } from "./state.js"; // traces follow sessions/memory into the home bucket
|
|
5
6
|
// Explicit opt-in: unlike telemetry, this contains conversation and tool data.
|
|
6
7
|
export function reviewDebugPath() {
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
if (process.env.MINI_AGENT_REVIEW_DEBUG !== "1")
|
|
9
|
+
return undefined;
|
|
10
|
+
migrateLegacyState(); // move an older ./.mini-agent/review-debug.jsonl into the bucket
|
|
11
|
+
return projectStateFile("review-debug.jsonl");
|
|
9
12
|
}
|
|
10
13
|
// Environment for commands the agent runs. Tracing is for THIS process only:
|
|
11
14
|
// a child `npm test` that inherited the flag appended its mocked reviews to the
|
package/dist/ink/commands.js
CHANGED
|
@@ -2,7 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { CONFIG } from "../config.js";
|
|
4
4
|
import { statsReport } from "../telemetry.js";
|
|
5
|
-
import { readMemoryTyped,
|
|
5
|
+
import { readMemoryTyped, memoryPath } from "../memory.js";
|
|
6
6
|
import { getTodos, renderTodos } from "../todos.js";
|
|
7
7
|
import { listBackground } from "../background.js";
|
|
8
8
|
import { listTeam } from "../team.js";
|
|
@@ -87,7 +87,7 @@ export function runInfoCommand(line, ctx) {
|
|
|
87
87
|
case "/memory": {
|
|
88
88
|
const entries = readMemoryTyped();
|
|
89
89
|
return chalk.dim(entries.length
|
|
90
|
-
? `long-term memory (${
|
|
90
|
+
? `long-term memory (${memoryPath()}):\n${entries.map((e) => ` [${e.type}] ${e.fact}`).join("\n")}`
|
|
91
91
|
: `(no long-term memory yet — the model saves facts with the remember tool${CONFIG.memory.autoExtract ? "" : "; turn on settings.memory.autoExtract to capture them automatically"})`);
|
|
92
92
|
}
|
|
93
93
|
case "/todos": {
|
package/dist/memory.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs"; // memory lives in a file on disk
|
|
2
2
|
import path from "node:path"; // path joining
|
|
3
|
+
import { migrateLegacyState, projectMemoryPath } from "./state.js"; // one bucket per cwd, kept out of the checkout
|
|
3
4
|
// Long-term memory: facts the agent should carry ACROSS sessions, not just
|
|
4
5
|
// within one conversation. Session persistence (Day 12) restores "what we were
|
|
5
6
|
// talking about"; this restores "what I should always know about this project".
|
|
@@ -13,7 +14,15 @@ import path from "node:path"; // path joining
|
|
|
13
14
|
// assistant: 90% the model gets right isn't worth saving, the 10% it got wrong
|
|
14
15
|
// and was corrected on is gold). Every fact is TYPED so the model can tell "this
|
|
15
16
|
// is who the user is" from "this is a correction" from "this is a project fact".
|
|
16
|
-
|
|
17
|
+
// The file lives in the cwd's home bucket (state.ts), not inside the checkout,
|
|
18
|
+
// so a repo never carries agent memory in git status. Resolved per call so a
|
|
19
|
+
// chdir (tests, --cd) is honoured, and so the first call can migrate an older
|
|
20
|
+
// build's ./.mini-agent/MEMORY.md. Still one Markdown file, human-readable and
|
|
21
|
+
// human-editable: not a vector database, not embeddings.
|
|
22
|
+
export function memoryPath() {
|
|
23
|
+
migrateLegacyState();
|
|
24
|
+
return projectMemoryPath();
|
|
25
|
+
}
|
|
17
26
|
// The four memory types (handbook §17). feedback is the most valuable.
|
|
18
27
|
export const MEMORY_TYPES = ["user", "feedback", "project", "reference"];
|
|
19
28
|
// Bounds, because unbounded memory dilutes the model's attention (Ch6: too many
|
|
@@ -38,7 +47,7 @@ function parseEntry(text) {
|
|
|
38
47
|
export function readMemoryTyped() {
|
|
39
48
|
try {
|
|
40
49
|
return fs
|
|
41
|
-
.readFileSync(
|
|
50
|
+
.readFileSync(memoryPath(), "utf8")
|
|
42
51
|
.split("\n")
|
|
43
52
|
.filter((l) => l.startsWith("- "))
|
|
44
53
|
.map((l) => parseEntry(l.slice(2).trim()))
|
|
@@ -91,8 +100,9 @@ export function addFact(fact, type = "project") {
|
|
|
91
100
|
entries.shift(); // still too big — drop the oldest until it fits
|
|
92
101
|
body = entries.map((e) => `- [${e.type}] ${e.fact}`).join("\n");
|
|
93
102
|
}
|
|
94
|
-
|
|
95
|
-
fs.
|
|
103
|
+
const file = memoryPath();
|
|
104
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
105
|
+
fs.writeFileSync(file, HEADER + body + "\n", "utf8");
|
|
96
106
|
return `Remembered (${t}). (${entries.length} fact${entries.length === 1 ? "" : "s"} stored.)`;
|
|
97
107
|
}
|
|
98
108
|
// The tool the model calls to save something worth keeping.
|
package/dist/session.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import fs from "node:fs"; // session files live on disk
|
|
2
2
|
import path from "node:path"; // path joining
|
|
3
3
|
import { getGoal } from "./goal.js"; // the /goal rides along in the session file (Day 41)
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
import { migrateLegacyState, projectSessionsDir } from "./state.js"; // one bucket per cwd, kept out of the checkout
|
|
5
|
+
// Sessions are per-project but not per-repo: <home>/mini-agent/projects/<slug>/sessions,
|
|
6
|
+
// where <slug> is the cwd flattened. Same reasoning as the old in-repo layout (a
|
|
7
|
+
// conversation is about a codebase, so resuming project A from inside project B
|
|
8
|
+
// would be wrong) without leaving an untracked directory in every checkout.
|
|
7
9
|
// Resolved lazily (a function, not a const) so it always reflects the current
|
|
8
|
-
// working directory
|
|
9
|
-
|
|
10
|
+
// working directory, and so tests can chdir into a scratch dir. The first touch
|
|
11
|
+
// migrates any ./.mini-agent/sessions left by an older build.
|
|
12
|
+
const sessionsDir = () => {
|
|
13
|
+
migrateLegacyState();
|
|
14
|
+
return projectSessionsDir();
|
|
15
|
+
};
|
|
10
16
|
const KEEP_SESSIONS = 20; // how many session files survive pruning
|
|
11
17
|
// Where a session's file lives — absolute, so it can be handed to hooks.
|
|
12
18
|
export function sessionPath(id) {
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import fs from "node:fs"; // moving legacy state, creating the home bucket
|
|
2
|
+
import os from "node:os"; // the home directory root
|
|
3
|
+
import path from "node:path"; // joining and resolving paths
|
|
4
|
+
// Per-project state that is history rather than configuration — session
|
|
5
|
+
// transcripts, long-term memory, and the local event/trace logs — lives OUTSIDE
|
|
6
|
+
// the checkout, one bucket per working directory, mirroring how Claude Code
|
|
7
|
+
// keeps ~/.claude/projects/<slug>. A repo you open should not grow an untracked
|
|
8
|
+
// state directory just because an agent ran in it; configuration and tooling
|
|
9
|
+
// (settings, skills, task board, team mailboxes, cron state) stay project-local
|
|
10
|
+
// where they belong to the code.
|
|
11
|
+
//
|
|
12
|
+
// The bucket is keyed by the absolute cwd with separators flattened, so
|
|
13
|
+
// /Users/me/proj becomes -Users-me-proj, the same escaping Claude Code uses.
|
|
14
|
+
export const projectsRoot = () => process.env.MINI_AGENT_PROJECTS_DIR || path.join(os.homedir(), ".config", "mini-agent", "projects");
|
|
15
|
+
// One directory name per working directory. Windows drive colons are flattened
|
|
16
|
+
// too, so the name is portable and cannot escape the root. Symlinks are resolved
|
|
17
|
+
// first, so one project reached as /tmp/x and /private/tmp/x keeps ONE bucket
|
|
18
|
+
// instead of two that each hold half the history.
|
|
19
|
+
export function projectSlug(cwd = process.cwd()) {
|
|
20
|
+
let abs = path.resolve(cwd);
|
|
21
|
+
try {
|
|
22
|
+
abs = fs.realpathSync(abs);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* a path that does not exist yet still gets a stable slug */
|
|
26
|
+
}
|
|
27
|
+
return abs.replace(/[\\/:]/g, "-");
|
|
28
|
+
}
|
|
29
|
+
export function projectStateDir(cwd = process.cwd()) {
|
|
30
|
+
return path.join(projectsRoot(), projectSlug(cwd));
|
|
31
|
+
}
|
|
32
|
+
export function projectSessionsDir(cwd = process.cwd()) {
|
|
33
|
+
return path.join(projectStateDir(cwd), "sessions");
|
|
34
|
+
}
|
|
35
|
+
export function projectMemoryPath(cwd = process.cwd()) {
|
|
36
|
+
return path.join(projectStateDir(cwd), "MEMORY.md");
|
|
37
|
+
}
|
|
38
|
+
// Any other per-project log or file that should live beside sessions and memory
|
|
39
|
+
// rather than inside the checkout (telemetry, review-debug traces).
|
|
40
|
+
export function projectStateFile(name, cwd = process.cwd()) {
|
|
41
|
+
return path.join(projectStateDir(cwd), name);
|
|
42
|
+
}
|
|
43
|
+
// Move a path only when the destination is absent, so a second run is a no-op
|
|
44
|
+
// and a newly written home file is never clobbered by an old checkout copy.
|
|
45
|
+
// rename works on one filesystem; cp+rm covers the home-on-another-volume case.
|
|
46
|
+
function moveIfAbsent(from, to) {
|
|
47
|
+
if (fs.existsSync(to) || !fs.existsSync(from))
|
|
48
|
+
return;
|
|
49
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
50
|
+
try {
|
|
51
|
+
fs.renameSync(from, to);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
fs.cpSync(from, to, { recursive: true });
|
|
55
|
+
fs.rmSync(from, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
let migrated = false; // once per process; the check is idempotent anyway
|
|
59
|
+
// Older builds wrote sessions and memory into ./.mini-agent. Move them into the
|
|
60
|
+
// home bucket the first time anything touches the new location, so an upgrade
|
|
61
|
+
// keeps its history and the checkout ends up clean. Never throws: a failed
|
|
62
|
+
// migration must not break the CLI, it just leaves the old files in place.
|
|
63
|
+
export function migrateLegacyState(cwd = process.cwd()) {
|
|
64
|
+
if (migrated)
|
|
65
|
+
return;
|
|
66
|
+
migrated = true;
|
|
67
|
+
try {
|
|
68
|
+
moveIfAbsent(path.resolve(cwd, ".mini-agent", "sessions"), projectSessionsDir(cwd));
|
|
69
|
+
moveIfAbsent(path.resolve(cwd, ".mini-agent", "MEMORY.md"), projectMemoryPath(cwd));
|
|
70
|
+
// Logs are per-project too, and telemetry is usually the biggest file left
|
|
71
|
+
// behind by an older checkout.
|
|
72
|
+
moveIfAbsent(path.resolve(cwd, ".mini-agent", "telemetry.jsonl"), projectStateFile("telemetry.jsonl", cwd));
|
|
73
|
+
moveIfAbsent(path.resolve(cwd, ".mini-agent", "review-debug.jsonl"), projectStateFile("review-debug.jsonl", cwd));
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
/* leave the legacy files where they are */
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/telemetry.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import fs from "node:fs"; // the event log lives on disk
|
|
2
2
|
import path from "node:path"; // path joining
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
import { migrateLegacyState, projectStateFile } from "./state.js"; // one bucket per cwd, kept out of the checkout
|
|
4
|
+
// LOCAL-ONLY telemetry. Events go to the project's home bucket
|
|
5
|
+
// (~/.config/mini-agent/projects/<cwd-slug>/telemetry.jsonl) and NOWHERE else —
|
|
6
|
+
// nothing leaves the machine, ever. It exists because "no data" means every
|
|
7
|
+
// optimization is a guess: which errors actually happen, how often compaction
|
|
8
|
+
// fires, which tools get denied. Opt out entirely with MINI_AGENT_NO_TELEMETRY=1.
|
|
8
9
|
//
|
|
9
10
|
// Naming follows the unified-prefix convention (agent_<domain>_<action>) so
|
|
10
11
|
// the log stays greppable: `grep agent_api_error telemetry.jsonl | wc -l`.
|
|
11
|
-
|
|
12
|
+
// Resolved per call so a chdir is honoured, and so the first write migrates an
|
|
13
|
+
// older build's ./.mini-agent/telemetry.jsonl.
|
|
14
|
+
const telemetryFile = () => {
|
|
15
|
+
migrateLegacyState();
|
|
16
|
+
return projectStateFile("telemetry.jsonl");
|
|
17
|
+
};
|
|
12
18
|
let sessionId = null; // set by initTelemetry; emit() is a no-op before that
|
|
13
19
|
const tallies = new Map(); // per-session event counts, for /stats
|
|
14
20
|
// Arm telemetry for this session. Tests and the eval harness never call this,
|
|
@@ -25,8 +31,9 @@ export function emit(event, fields = {}) {
|
|
|
25
31
|
if (!sessionId)
|
|
26
32
|
return; // disarmed — counters only, no file
|
|
27
33
|
try {
|
|
28
|
-
|
|
29
|
-
fs.
|
|
34
|
+
const file = telemetryFile();
|
|
35
|
+
fs.mkdirSync(path.dirname(file), { recursive: true }); // ensure the directory
|
|
36
|
+
fs.appendFileSync(file, JSON.stringify({ ts: new Date().toISOString(), session: sessionId, event, ...fields }) + "\n"); // one JSON object per line
|
|
30
37
|
}
|
|
31
38
|
catch {
|
|
32
39
|
/* a full disk or a read-only checkout is not our problem to escalate */
|
|
@@ -39,5 +46,5 @@ export function statsReport() {
|
|
|
39
46
|
const lines = [...tallies.entries()]
|
|
40
47
|
.sort((a, b) => b[1] - a[1]) // busiest first
|
|
41
48
|
.map(([k, n]) => ` ${String(n).padStart(5)} ${k}`); // aligned counts
|
|
42
|
-
return ["events this session:", ...lines, `\nfull log: ${
|
|
49
|
+
return ["events this session:", ...lines, `\nfull log: ${telemetryFile()} (local only — nothing leaves this machine)`].join("\n");
|
|
43
50
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-from-zero",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mini-agent": "dist/agent.js"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"auto:report": "tsx eval/auto-report.ts",
|
|
18
18
|
"build": "tsc -p tsconfig.build.json",
|
|
19
19
|
"prepare": "npm run build",
|
|
20
|
-
"test": "export MINI_AGENT_REVIEW_DEBUG=0 && tsx tests/tools.test.ts && tsx tests/permissions.test.ts && tsx tests/context.test.ts && tsx tests/hooks.test.ts && tsx tests/mcp.test.ts && tsx tests/judge.test.ts && tsx tests/auto.test.ts && tsx tests/auto-context.test.ts && tsx tests/auto-providers.test.ts && tsx tests/auto-cascade.test.ts && tsx tests/auto-rules.test.ts && tsx tests/auto-rule-cascade.test.ts && tsx tests/auto-debug.test.ts && tsx tests/auto-report.test.ts && tsx tests/auto-clarification.test.ts && tsx tests/memory.test.ts && tsx tests/cost.test.ts && tsx tests/cli.test.ts && tsx tests/print.test.ts && tsx tests/status.test.ts && tsx tests/validate.test.ts && tsx tests/plan.test.ts && tsx tests/diff.test.ts && tsx tests/undo.test.ts && tsx tests/tiering.test.ts && tsx tests/mcp-http.test.ts && tsx tests/mcp-oauth.test.ts && tsx tests/changes.test.ts && tsx tests/mentions.test.ts && tsx tests/session.test.ts && tsx tests/title.test.ts && tsx tests/ui.test.ts && tsx tests/activity.test.ts && tsx tests/follow-up.test.ts && tsx tests/follow-up-ui.test.tsx && tsx tests/agents.test.ts && tsx tests/images.test.ts && tsx tests/approval.test.ts && tsx tests/form.test.ts && tsx tests/model.test.ts && tsx tests/reasoning.test.ts && tsx tests/effort.test.ts && tsx tests/completion.test.ts && tsx tests/pasted-text.test.ts && tsx tests/skills.test.ts && tsx tests/markdown.test.ts && tsx tests/editor.test.ts && tsx tests/todos.test.ts && tsx tests/background.test.ts && tsx tests/team.test.ts && tsx tests/peers.test.ts && tsx tests/suggestion.test.ts && tsx tests/board.test.ts && tsx tests/drop.test.ts && tsx tests/editline.test.ts && tsx tests/cron.test.ts && tsx tests/goal.test.ts"
|
|
20
|
+
"test": "export MINI_AGENT_REVIEW_DEBUG=0 && tsx tests/tools.test.ts && tsx tests/permissions.test.ts && tsx tests/context.test.ts && tsx tests/hooks.test.ts && tsx tests/mcp.test.ts && tsx tests/judge.test.ts && tsx tests/auto.test.ts && tsx tests/auto-context.test.ts && tsx tests/auto-providers.test.ts && tsx tests/auto-cascade.test.ts && tsx tests/auto-rules.test.ts && tsx tests/auto-rule-cascade.test.ts && tsx tests/auto-debug.test.ts && tsx tests/auto-report.test.ts && tsx tests/auto-clarification.test.ts && tsx tests/memory.test.ts && tsx tests/cost.test.ts && tsx tests/cli.test.ts && tsx tests/print.test.ts && tsx tests/status.test.ts && tsx tests/validate.test.ts && tsx tests/plan.test.ts && tsx tests/diff.test.ts && tsx tests/undo.test.ts && tsx tests/tiering.test.ts && tsx tests/mcp-http.test.ts && tsx tests/mcp-oauth.test.ts && tsx tests/changes.test.ts && tsx tests/mentions.test.ts && tsx tests/state.test.ts && tsx tests/session.test.ts && tsx tests/title.test.ts && tsx tests/ui.test.ts && tsx tests/activity.test.ts && tsx tests/follow-up.test.ts && tsx tests/follow-up-ui.test.tsx && tsx tests/agents.test.ts && tsx tests/images.test.ts && tsx tests/approval.test.ts && tsx tests/form.test.ts && tsx tests/model.test.ts && tsx tests/reasoning.test.ts && tsx tests/effort.test.ts && tsx tests/completion.test.ts && tsx tests/pasted-text.test.ts && tsx tests/skills.test.ts && tsx tests/markdown.test.ts && tsx tests/editor.test.ts && tsx tests/todos.test.ts && tsx tests/background.test.ts && tsx tests/team.test.ts && tsx tests/peers.test.ts && tsx tests/suggestion.test.ts && tsx tests/board.test.ts && tsx tests/drop.test.ts && tsx tests/editline.test.ts && tsx tests/cron.test.ts && tsx tests/goal.test.ts"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=20.6"
|