jinzd-ai-cli 0.4.103 → 0.4.105
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/README.zh-CN.md +1 -1
- package/dist/{agent-client-6GX6QQDU.js → agent-client-25TIQ6AP.js} +1 -0
- package/dist/{auth-MSUWO6SE.js → auth-SC6KHHI3.js} +1 -0
- package/dist/{batch-LNTG2IRQ.js → batch-NPK4USGH.js} +3 -2
- package/dist/{chat-index-W2UZ34ZI.js → chat-index-7OHUKJY5.js} +1 -0
- package/dist/{chat-index-QKFH7ZP6.js → chat-index-ADG2GPCC.js} +1 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/{chunk-SN56X6RE.js → chunk-B6NUQVYK.js} +1 -1
- package/dist/{chunk-VOWVIR2U.js → chunk-F7XJ67XB.js} +30 -112
- package/dist/chunk-HOSJZMQS.js +97 -0
- package/dist/{chunk-OVYOYUP7.js → chunk-LVX667WL.js} +89 -36
- package/dist/{chunk-JHPSWYO3.js → chunk-LX5FXZVP.js} +7 -4
- package/dist/chunk-PDX44BCA.js +11 -0
- package/dist/{chunk-RZWWODW7.js → chunk-RFKT3T5S.js} +199 -74
- package/dist/{chunk-DGXUO7D4.js → chunk-VOF6OTZB.js} +89 -39
- package/dist/constants-HK5BB5EZ.js +78 -0
- package/dist/electron-server.js +289 -129
- package/dist/{file-checkpoint-CGH6OJVI.js → file-checkpoint-UHSMHCRU.js} +1 -0
- package/dist/{file-checkpoint-NKBHGC7L.js → file-checkpoint-ZN7KE3TN.js} +1 -0
- package/dist/git-context-7KIP4X2V.js +12 -0
- package/dist/{hub-4YGZ4XHN.js → hub-5VFGLTHY.js} +3 -2
- package/dist/{hub-server-BYXNQGDY.js → hub-server-AUMVPNU6.js} +1 -0
- package/dist/index.js +98 -47
- package/dist/{indexer-C7QYYHSZ.js → indexer-XGY7XGJM.js} +1 -0
- package/dist/{indexer-O5FCGFBJ.js → indexer-Z6AQTGBK.js} +1 -0
- package/dist/project-trust-EBGHD7LE.js +67 -0
- package/dist/project-trust-IFM7FXEV.js +68 -0
- package/dist/{run-tests-SN74WT4Z.js → run-tests-IMVI43CZ.js} +2 -1
- package/dist/{run-tests-3YOJEN2Q.js → run-tests-VQ3YZB75.js} +3 -2
- package/dist/{semantic-3KJPAUW6.js → semantic-FR2ZSQLY.js} +1 -0
- package/dist/{semantic-YDRPPVWK.js → semantic-UFKVYKFE.js} +3 -2
- package/dist/{server-BG4WR6RF.js → server-XDBIWNRW.js} +9 -8
- package/dist/{server-TNPDHGQT.js → server-ZVY3CKTJ.js} +65 -28
- package/dist/{store-S24SPPDZ.js → store-JDEW743P.js} +1 -0
- package/dist/{store-247B3TAU.js → store-Q7NMUCPP.js} +1 -0
- package/dist/{task-orchestrator-MUIH3XBY.js → task-orchestrator-UEZOFXQX.js} +9 -8
- package/dist/{vector-store-NDUFLNGN.js → vector-store-AK6J3RIA.js} +1 -0
- package/dist/{vector-store-QARQ2P6D.js → vector-store-MCQ77OOJ.js} +1 -0
- package/package.json +1 -1
- package/dist/{chunk-KJLJPUY2.js → chunk-3BICTI5M.js} +3 -3
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
- **PWA Support** — Install Web UI as a desktop/mobile app, accessible over LAN
|
|
46
46
|
- **Hierarchical Context** — 3-layer context files (global / project / subdirectory) auto-injected
|
|
47
47
|
- **Headless Mode** — `ai-cli -p "prompt"` for CI/CD pipelines and scripting
|
|
48
|
-
- **
|
|
48
|
+
- **44 REPL Commands** — Session management, checkpointing, code review, security review/scan, rewind, scaffolding, cross-session history search, chat-memory recall, smart model routing (`/route`), and more
|
|
49
49
|
- **GitHub Actions CI/CD** — Automated testing on Node 20/22 + npm publish on release tags
|
|
50
50
|
- **Cross-Platform** — Windows, macOS, Linux
|
|
51
51
|
|
package/README.zh-CN.md
CHANGED
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
- **PWA 支持** — Web UI 可安装为桌面/移动应用,支持局域网访问
|
|
38
38
|
- **三层级上下文** — 全局 / 项目 / 子目录上下文文件自动注入
|
|
39
39
|
- **无头模式** — `aicli -p "提示词"` 用于 CI/CD 管道和脚本
|
|
40
|
-
- **
|
|
40
|
+
- **44 个 REPL 命令** — 会话管理、检查点、代码审查、安全审查/扫描、对话回退、脚手架、聊天记忆召回、智能模型路由(`/route`)等
|
|
41
41
|
- **GitHub Actions CI/CD** — Node 20/22 自动测试 + Release tag 自动发布 npm
|
|
42
42
|
- **跨平台** — Windows、macOS、Linux
|
|
43
43
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B6NUQVYK.js";
|
|
5
5
|
import "./chunk-2ZD3YTVM.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-LX5FXZVP.js";
|
|
7
|
+
import "./chunk-PDX44BCA.js";
|
|
7
8
|
|
|
8
9
|
// src/cli/batch.ts
|
|
9
10
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
__require
|
|
10
|
+
};
|
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
schemaToJsonSchema,
|
|
4
4
|
truncateForPersist
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RFKT3T5S.js";
|
|
6
6
|
import {
|
|
7
7
|
AuthError,
|
|
8
8
|
ProviderError,
|
|
9
9
|
ProviderNotFoundError,
|
|
10
10
|
RateLimitError
|
|
11
11
|
} from "./chunk-2ZD3YTVM.js";
|
|
12
|
-
import {
|
|
13
|
-
redactJson
|
|
14
|
-
} from "./chunk-ANYYM4CF.js";
|
|
15
12
|
import {
|
|
16
13
|
APP_NAME,
|
|
17
14
|
CONFIG_DIR_NAME,
|
|
@@ -21,7 +18,10 @@ import {
|
|
|
21
18
|
MCP_PROTOCOL_VERSION,
|
|
22
19
|
MCP_TOOL_PREFIX,
|
|
23
20
|
VERSION
|
|
24
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-LX5FXZVP.js";
|
|
22
|
+
import {
|
|
23
|
+
redactJson
|
|
24
|
+
} from "./chunk-ANYYM4CF.js";
|
|
25
25
|
|
|
26
26
|
// src/providers/claude.ts
|
|
27
27
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -1686,7 +1686,18 @@ function extractClaimedFilePaths(content) {
|
|
|
1686
1686
|
const enRe = /(?:saved|written|created)\s+(?:to|as|at)\s+[`'”]?([^\s`'”\n,]+?\.\w{1,6})/gi;
|
|
1687
1687
|
while ((m = enRe.exec(content)) !== null) add(m[1]);
|
|
1688
1688
|
const checkRe = /✅[^\n`]*?[`'”]?([^\s`'”\n,,。]+?\.\w{1,6})/g;
|
|
1689
|
-
while ((m = checkRe.exec(content)) !== null)
|
|
1689
|
+
while ((m = checkRe.exec(content)) !== null) {
|
|
1690
|
+
let pos = m.index;
|
|
1691
|
+
let linesBack = 0;
|
|
1692
|
+
while (linesBack < 9 && pos > 0) {
|
|
1693
|
+
pos--;
|
|
1694
|
+
if (content[pos] === "\n") linesBack++;
|
|
1695
|
+
}
|
|
1696
|
+
const windowStart = pos === 0 ? 0 : pos + 1;
|
|
1697
|
+
const lineEndIdx = content.indexOf("\n", m.index + m[0].length);
|
|
1698
|
+
const window = content.slice(windowStart, lineEndIdx === -1 ? void 0 : lineEndIdx);
|
|
1699
|
+
if (actionLineRe.test(window)) add(m[1]);
|
|
1700
|
+
}
|
|
1690
1701
|
return Array.from(paths);
|
|
1691
1702
|
}
|
|
1692
1703
|
function extractWrittenFilePaths(extraMessages) {
|
|
@@ -3083,96 +3094,6 @@ var SessionManager = class {
|
|
|
3083
3094
|
}
|
|
3084
3095
|
};
|
|
3085
3096
|
|
|
3086
|
-
// src/tools/git-context.ts
|
|
3087
|
-
import { execSync } from "child_process";
|
|
3088
|
-
import { existsSync as existsSync2 } from "fs";
|
|
3089
|
-
import { join as join2 } from "path";
|
|
3090
|
-
function runGit(cmd, cwd) {
|
|
3091
|
-
try {
|
|
3092
|
-
return execSync(`git ${cmd}`, {
|
|
3093
|
-
cwd,
|
|
3094
|
-
encoding: "utf-8",
|
|
3095
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
3096
|
-
timeout: 5e3
|
|
3097
|
-
}).trim();
|
|
3098
|
-
} catch {
|
|
3099
|
-
return null;
|
|
3100
|
-
}
|
|
3101
|
-
}
|
|
3102
|
-
function getGitRoot(cwd = process.cwd()) {
|
|
3103
|
-
return runGit("rev-parse --show-toplevel", cwd);
|
|
3104
|
-
}
|
|
3105
|
-
function getGitContext(cwd = process.cwd()) {
|
|
3106
|
-
if (!existsSync2(join2(cwd, ".git"))) {
|
|
3107
|
-
const result = runGit("rev-parse --git-dir", cwd);
|
|
3108
|
-
if (!result) return null;
|
|
3109
|
-
}
|
|
3110
|
-
const branch = runGit("rev-parse --abbrev-ref HEAD", cwd);
|
|
3111
|
-
if (!branch) return null;
|
|
3112
|
-
const statusOutput = runGit("status --porcelain", cwd) ?? "";
|
|
3113
|
-
const statusLines = statusOutput ? statusOutput.split("\n").filter(Boolean) : [];
|
|
3114
|
-
const stagedFiles = [];
|
|
3115
|
-
const changedFiles = [];
|
|
3116
|
-
for (const line of statusLines) {
|
|
3117
|
-
const xy = line.slice(0, 2);
|
|
3118
|
-
const file = line.slice(3).trim();
|
|
3119
|
-
const indexStatus = xy[0];
|
|
3120
|
-
const workStatus = xy[1];
|
|
3121
|
-
if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
|
|
3122
|
-
stagedFiles.push(`${indexStatus} ${file}`);
|
|
3123
|
-
}
|
|
3124
|
-
if (workStatus && workStatus !== " ") {
|
|
3125
|
-
changedFiles.push(`${workStatus} ${file}`);
|
|
3126
|
-
}
|
|
3127
|
-
}
|
|
3128
|
-
const logOutput = runGit("log --oneline -3", cwd) ?? "";
|
|
3129
|
-
const recentCommits = logOutput ? logOutput.split("\n").filter(Boolean) : [];
|
|
3130
|
-
const unpushedOutput = runGit("log @{u}..HEAD --oneline", cwd);
|
|
3131
|
-
const hasUnpushed = unpushedOutput !== null && unpushedOutput.trim().length > 0;
|
|
3132
|
-
return {
|
|
3133
|
-
branch,
|
|
3134
|
-
changedFiles,
|
|
3135
|
-
stagedFiles,
|
|
3136
|
-
recentCommits,
|
|
3137
|
-
hasUnpushed
|
|
3138
|
-
};
|
|
3139
|
-
}
|
|
3140
|
-
function formatGitContextForPrompt(ctx) {
|
|
3141
|
-
const lines = ["# Git Repository Status", ""];
|
|
3142
|
-
lines.push(`- **Branch**: \`${ctx.branch}\``);
|
|
3143
|
-
if (ctx.stagedFiles.length > 0) {
|
|
3144
|
-
lines.push(`- **Staged** (${ctx.stagedFiles.length} files):`);
|
|
3145
|
-
for (const f of ctx.stagedFiles.slice(0, 10)) {
|
|
3146
|
-
lines.push(` - ${f}`);
|
|
3147
|
-
}
|
|
3148
|
-
if (ctx.stagedFiles.length > 10) {
|
|
3149
|
-
lines.push(` - ... and ${ctx.stagedFiles.length - 10} more`);
|
|
3150
|
-
}
|
|
3151
|
-
}
|
|
3152
|
-
if (ctx.changedFiles.length > 0) {
|
|
3153
|
-
lines.push(`- **Modified** (${ctx.changedFiles.length} files):`);
|
|
3154
|
-
for (const f of ctx.changedFiles.slice(0, 10)) {
|
|
3155
|
-
lines.push(` - ${f}`);
|
|
3156
|
-
}
|
|
3157
|
-
if (ctx.changedFiles.length > 10) {
|
|
3158
|
-
lines.push(` - ... and ${ctx.changedFiles.length - 10} more`);
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
if (ctx.stagedFiles.length === 0 && ctx.changedFiles.length === 0) {
|
|
3162
|
-
lines.push("- **Working tree**: clean");
|
|
3163
|
-
}
|
|
3164
|
-
if (ctx.recentCommits.length > 0) {
|
|
3165
|
-
lines.push("- **Recent commits**:");
|
|
3166
|
-
for (const c of ctx.recentCommits) {
|
|
3167
|
-
lines.push(` - ${c}`);
|
|
3168
|
-
}
|
|
3169
|
-
}
|
|
3170
|
-
if (ctx.hasUnpushed) {
|
|
3171
|
-
lines.push("- \u26A0\uFE0F Has unpushed commits");
|
|
3172
|
-
}
|
|
3173
|
-
return lines.join("\n");
|
|
3174
|
-
}
|
|
3175
|
-
|
|
3176
3097
|
// src/mcp/client.ts
|
|
3177
3098
|
import { spawn } from "child_process";
|
|
3178
3099
|
var McpClient = class {
|
|
@@ -3683,8 +3604,8 @@ var McpManager = class {
|
|
|
3683
3604
|
};
|
|
3684
3605
|
|
|
3685
3606
|
// src/skills/manager.ts
|
|
3686
|
-
import { existsSync as
|
|
3687
|
-
import { join as
|
|
3607
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, mkdirSync as mkdirSync2, statSync } from "fs";
|
|
3608
|
+
import { join as join2 } from "path";
|
|
3688
3609
|
|
|
3689
3610
|
// src/skills/types.ts
|
|
3690
3611
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -3752,7 +3673,7 @@ var SkillManager = class {
|
|
|
3752
3673
|
/** 发现并加载 skillsDir 下所有 .md 文件,返回加载数量 */
|
|
3753
3674
|
loadSkills() {
|
|
3754
3675
|
this.skills.clear();
|
|
3755
|
-
if (!
|
|
3676
|
+
if (!existsSync2(this.skillsDir)) {
|
|
3756
3677
|
try {
|
|
3757
3678
|
mkdirSync2(this.skillsDir, { recursive: true });
|
|
3758
3679
|
} catch {
|
|
@@ -3767,14 +3688,14 @@ var SkillManager = class {
|
|
|
3767
3688
|
}
|
|
3768
3689
|
for (const entry of entries) {
|
|
3769
3690
|
let filePath;
|
|
3770
|
-
const fullPath =
|
|
3691
|
+
const fullPath = join2(this.skillsDir, entry);
|
|
3771
3692
|
if (entry.endsWith(".md")) {
|
|
3772
3693
|
filePath = fullPath;
|
|
3773
3694
|
} else {
|
|
3774
3695
|
try {
|
|
3775
3696
|
if (statSync(fullPath).isDirectory()) {
|
|
3776
|
-
const skillMd =
|
|
3777
|
-
if (
|
|
3697
|
+
const skillMd = join2(fullPath, "SKILL.md");
|
|
3698
|
+
if (existsSync2(skillMd)) {
|
|
3778
3699
|
filePath = skillMd;
|
|
3779
3700
|
} else {
|
|
3780
3701
|
continue;
|
|
@@ -4028,8 +3949,8 @@ function autoTrimSessionIfNeeded(session, sizeLimit = SESSION_SIZE_LIMIT) {
|
|
|
4028
3949
|
}
|
|
4029
3950
|
|
|
4030
3951
|
// src/repl/dev-state.ts
|
|
4031
|
-
import { existsSync as
|
|
4032
|
-
import { join as
|
|
3952
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3 } from "fs";
|
|
3953
|
+
import { join as join3 } from "path";
|
|
4033
3954
|
import { homedir } from "os";
|
|
4034
3955
|
var DEV_STATE_MAX_CHARS = 6e3;
|
|
4035
3956
|
var SNAPSHOT_PROMPT = `You are about to be replaced by a different AI model. Please generate a structured development state snapshot so the next model can continue seamlessly.
|
|
@@ -4083,11 +4004,11 @@ function sessionHasMeaningfulContent(messages) {
|
|
|
4083
4004
|
return hasUser && hasAssistant;
|
|
4084
4005
|
}
|
|
4085
4006
|
function getDevStatePath() {
|
|
4086
|
-
return
|
|
4007
|
+
return join3(homedir(), CONFIG_DIR_NAME, DEV_STATE_FILE_NAME);
|
|
4087
4008
|
}
|
|
4088
4009
|
function saveDevState(content) {
|
|
4089
|
-
const configDir =
|
|
4090
|
-
if (!
|
|
4010
|
+
const configDir = join3(homedir(), CONFIG_DIR_NAME);
|
|
4011
|
+
if (!existsSync3(configDir)) {
|
|
4091
4012
|
mkdirSync3(configDir, { recursive: true });
|
|
4092
4013
|
}
|
|
4093
4014
|
let trimmed = content.trim();
|
|
@@ -4103,13 +4024,13 @@ function saveDevState(content) {
|
|
|
4103
4024
|
}
|
|
4104
4025
|
function loadDevState() {
|
|
4105
4026
|
const path = getDevStatePath();
|
|
4106
|
-
if (!
|
|
4027
|
+
if (!existsSync3(path)) return null;
|
|
4107
4028
|
const content = readFileSync3(path, "utf-8").trim();
|
|
4108
4029
|
return content || null;
|
|
4109
4030
|
}
|
|
4110
4031
|
function clearDevState() {
|
|
4111
4032
|
const path = getDevStatePath();
|
|
4112
|
-
if (
|
|
4033
|
+
if (existsSync3(path)) {
|
|
4113
4034
|
try {
|
|
4114
4035
|
unlinkSync2(path);
|
|
4115
4036
|
} catch {
|
|
@@ -4129,9 +4050,6 @@ export {
|
|
|
4129
4050
|
ProviderRegistry,
|
|
4130
4051
|
getContentText,
|
|
4131
4052
|
SessionManager,
|
|
4132
|
-
getGitRoot,
|
|
4133
|
-
getGitContext,
|
|
4134
|
-
formatGitContextForPrompt,
|
|
4135
4053
|
getPricing,
|
|
4136
4054
|
computeCost,
|
|
4137
4055
|
formatCost,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/tools/git-context.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
function runGit(cmd, cwd) {
|
|
8
|
+
try {
|
|
9
|
+
return execSync(`git ${cmd}`, {
|
|
10
|
+
cwd,
|
|
11
|
+
encoding: "utf-8",
|
|
12
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
13
|
+
timeout: 5e3
|
|
14
|
+
}).trim();
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function getGitRoot(cwd = process.cwd()) {
|
|
20
|
+
return runGit("rev-parse --show-toplevel", cwd);
|
|
21
|
+
}
|
|
22
|
+
function getGitContext(cwd = process.cwd()) {
|
|
23
|
+
if (!existsSync(join(cwd, ".git"))) {
|
|
24
|
+
const result = runGit("rev-parse --git-dir", cwd);
|
|
25
|
+
if (!result) return null;
|
|
26
|
+
}
|
|
27
|
+
const branch = runGit("rev-parse --abbrev-ref HEAD", cwd);
|
|
28
|
+
if (!branch) return null;
|
|
29
|
+
const statusOutput = runGit("status --porcelain", cwd) ?? "";
|
|
30
|
+
const statusLines = statusOutput ? statusOutput.split("\n").filter(Boolean) : [];
|
|
31
|
+
const stagedFiles = [];
|
|
32
|
+
const changedFiles = [];
|
|
33
|
+
for (const line of statusLines) {
|
|
34
|
+
const xy = line.slice(0, 2);
|
|
35
|
+
const file = line.slice(3).trim();
|
|
36
|
+
const indexStatus = xy[0];
|
|
37
|
+
const workStatus = xy[1];
|
|
38
|
+
if (indexStatus && indexStatus !== " " && indexStatus !== "?") {
|
|
39
|
+
stagedFiles.push(`${indexStatus} ${file}`);
|
|
40
|
+
}
|
|
41
|
+
if (workStatus && workStatus !== " ") {
|
|
42
|
+
changedFiles.push(`${workStatus} ${file}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const logOutput = runGit("log --oneline -3", cwd) ?? "";
|
|
46
|
+
const recentCommits = logOutput ? logOutput.split("\n").filter(Boolean) : [];
|
|
47
|
+
const unpushedOutput = runGit("log @{u}..HEAD --oneline", cwd);
|
|
48
|
+
const hasUnpushed = unpushedOutput !== null && unpushedOutput.trim().length > 0;
|
|
49
|
+
return {
|
|
50
|
+
branch,
|
|
51
|
+
changedFiles,
|
|
52
|
+
stagedFiles,
|
|
53
|
+
recentCommits,
|
|
54
|
+
hasUnpushed
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function formatGitContextForPrompt(ctx) {
|
|
58
|
+
const lines = ["# Git Repository Status", ""];
|
|
59
|
+
lines.push(`- **Branch**: \`${ctx.branch}\``);
|
|
60
|
+
if (ctx.stagedFiles.length > 0) {
|
|
61
|
+
lines.push(`- **Staged** (${ctx.stagedFiles.length} files):`);
|
|
62
|
+
for (const f of ctx.stagedFiles.slice(0, 10)) {
|
|
63
|
+
lines.push(` - ${f}`);
|
|
64
|
+
}
|
|
65
|
+
if (ctx.stagedFiles.length > 10) {
|
|
66
|
+
lines.push(` - ... and ${ctx.stagedFiles.length - 10} more`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (ctx.changedFiles.length > 0) {
|
|
70
|
+
lines.push(`- **Modified** (${ctx.changedFiles.length} files):`);
|
|
71
|
+
for (const f of ctx.changedFiles.slice(0, 10)) {
|
|
72
|
+
lines.push(` - ${f}`);
|
|
73
|
+
}
|
|
74
|
+
if (ctx.changedFiles.length > 10) {
|
|
75
|
+
lines.push(` - ... and ${ctx.changedFiles.length - 10} more`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (ctx.stagedFiles.length === 0 && ctx.changedFiles.length === 0) {
|
|
79
|
+
lines.push("- **Working tree**: clean");
|
|
80
|
+
}
|
|
81
|
+
if (ctx.recentCommits.length > 0) {
|
|
82
|
+
lines.push("- **Recent commits**:");
|
|
83
|
+
for (const c of ctx.recentCommits) {
|
|
84
|
+
lines.push(` - ${c}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (ctx.hasUnpushed) {
|
|
88
|
+
lines.push("- \u26A0\uFE0F Has unpushed commits");
|
|
89
|
+
}
|
|
90
|
+
return lines.join("\n");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
getGitRoot,
|
|
95
|
+
getGitContext,
|
|
96
|
+
formatGitContextForPrompt
|
|
97
|
+
};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
TEST_TIMEOUT
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LX5FXZVP.js";
|
|
5
5
|
|
|
6
6
|
// src/tools/builtin/run-tests.ts
|
|
7
|
-
import { execSync } from "child_process";
|
|
7
|
+
import { execSync, spawnSync } from "child_process";
|
|
8
8
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
9
9
|
import { join } from "path";
|
|
10
10
|
import { platform } from "os";
|
|
11
11
|
import chalk from "chalk";
|
|
12
|
+
var FILTER_WHITELIST = /^[\w.\-/:#\s*?|^()\[\]@+]{1,200}$/;
|
|
12
13
|
var IS_WINDOWS = platform() === "win32";
|
|
13
14
|
function detectNodeTestFramework(cwd, pkg) {
|
|
14
15
|
const devDeps = pkg.devDependencies ?? {};
|
|
@@ -238,12 +239,62 @@ function renderColorReport(summary, framework) {
|
|
|
238
239
|
}
|
|
239
240
|
console.log();
|
|
240
241
|
}
|
|
242
|
+
function needsWindowsShell(file) {
|
|
243
|
+
if (!IS_WINDOWS) return false;
|
|
244
|
+
const lower = file.toLowerCase();
|
|
245
|
+
return lower.endsWith(".cmd") || lower.endsWith(".bat") || lower === "mvn" || lower === "gradle" || lower === "npm" || lower === "npx";
|
|
246
|
+
}
|
|
247
|
+
function buildArgv(detected, filter) {
|
|
248
|
+
switch (detected.type) {
|
|
249
|
+
case "java": {
|
|
250
|
+
const parts2 = detected.command.split(/\s+/);
|
|
251
|
+
const file = parts2[0];
|
|
252
|
+
const rest = parts2.slice(1);
|
|
253
|
+
const args = [...rest];
|
|
254
|
+
if (filter) {
|
|
255
|
+
args.push(detected.framework.startsWith("Maven") ? `-Dtest=${filter}` : `--tests=${filter}`);
|
|
256
|
+
}
|
|
257
|
+
return { file, args };
|
|
258
|
+
}
|
|
259
|
+
case "python":
|
|
260
|
+
return { file: "pytest", args: filter ? ["-v", "-k", filter] : ["-v"] };
|
|
261
|
+
case "rust":
|
|
262
|
+
return { file: "cargo", args: filter ? ["test", filter] : ["test"] };
|
|
263
|
+
case "go":
|
|
264
|
+
return filter ? { file: "go", args: ["test", "./...", "-run", filter] } : { file: "go", args: ["test", "./..."] };
|
|
265
|
+
case "node": {
|
|
266
|
+
if (detected.framework === "vitest") {
|
|
267
|
+
return { file: "npx", args: filter ? ["vitest", "run", "-t", filter] : ["vitest", "run"] };
|
|
268
|
+
}
|
|
269
|
+
if (detected.framework === "jest") {
|
|
270
|
+
return { file: "npx", args: filter ? ["jest", "-t", filter] : ["jest"] };
|
|
271
|
+
}
|
|
272
|
+
if (detected.framework === "mocha") {
|
|
273
|
+
return { file: "npx", args: filter ? ["mocha", "--grep", filter] : ["mocha"] };
|
|
274
|
+
}
|
|
275
|
+
if (detected.framework === "playwright") {
|
|
276
|
+
return { file: "npx", args: filter ? ["playwright", "test", "--grep", filter] : ["playwright", "test"] };
|
|
277
|
+
}
|
|
278
|
+
if (detected.framework === "ava") {
|
|
279
|
+
return { file: "npx", args: filter ? ["ava", "--match", filter] : ["ava"] };
|
|
280
|
+
}
|
|
281
|
+
return filter ? { file: "npm", args: ["test", "--", "--grep", filter] } : { file: "npm", args: ["test"] };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const parts = detected.command.split(/\s+/);
|
|
285
|
+
return { file: parts[0], args: parts.slice(1) };
|
|
286
|
+
}
|
|
241
287
|
async function executeTests(args) {
|
|
242
288
|
const cwd = process.cwd();
|
|
243
289
|
const customCmd = args["command"] ? String(args["command"]).trim() : "";
|
|
244
290
|
const filter = args["filter"] ? String(args["filter"]).trim() : "";
|
|
291
|
+
if (filter && !FILTER_WHITELIST.test(filter)) {
|
|
292
|
+
return `Error: filter contains characters that are not allowed in test names. Allowed: word chars, dots, dashes, slashes, colons, parens, spaces, and basic glob/regex meta (* ? | ^). Rejected: quotes, semicolons, backticks, redirects, and shell metacharacters.
|
|
293
|
+
Got: ${filter.slice(0, 60)}${filter.length > 60 ? "..." : ""}`;
|
|
294
|
+
}
|
|
245
295
|
let command;
|
|
246
296
|
let framework;
|
|
297
|
+
let argv = null;
|
|
247
298
|
if (customCmd) {
|
|
248
299
|
command = customCmd;
|
|
249
300
|
framework = "custom";
|
|
@@ -254,50 +305,52 @@ async function executeTests(args) {
|
|
|
254
305
|
}
|
|
255
306
|
command = detected.command;
|
|
256
307
|
framework = detected.framework;
|
|
257
|
-
|
|
258
|
-
if (detected.type === "java" && command.includes("mvn")) {
|
|
259
|
-
command += ` -Dtest="${filter}"`;
|
|
260
|
-
} else if (detected.type === "python") {
|
|
261
|
-
command += ` -k "${filter}"`;
|
|
262
|
-
} else if (detected.type === "rust") {
|
|
263
|
-
command += ` ${filter}`;
|
|
264
|
-
} else if (detected.type === "go") {
|
|
265
|
-
command = `go test ./... -run "${filter}"`;
|
|
266
|
-
} else if (detected.type === "node") {
|
|
267
|
-
if (detected.framework === "vitest") {
|
|
268
|
-
command += ` -t "${filter}"`;
|
|
269
|
-
} else if (detected.framework === "jest") {
|
|
270
|
-
command += ` -t "${filter}"`;
|
|
271
|
-
} else if (detected.framework === "mocha") {
|
|
272
|
-
command += ` --grep "${filter}"`;
|
|
273
|
-
} else if (detected.framework === "playwright") {
|
|
274
|
-
command += ` --grep "${filter}"`;
|
|
275
|
-
} else {
|
|
276
|
-
command += ` -- --grep "${filter}"`;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
308
|
+
argv = buildArgv(detected, filter);
|
|
280
309
|
}
|
|
281
310
|
let output;
|
|
282
311
|
let exitCode = 0;
|
|
283
|
-
|
|
284
|
-
const
|
|
312
|
+
if (argv) {
|
|
313
|
+
const r = spawnSync(argv.file, argv.args, {
|
|
285
314
|
cwd,
|
|
286
315
|
timeout: TEST_TIMEOUT,
|
|
287
|
-
encoding: "buffer",
|
|
288
316
|
stdio: ["pipe", "pipe", "pipe"],
|
|
317
|
+
// shell:false is the default for spawnSync; on Windows .cmd/.bat
|
|
318
|
+
// requires shell:true, so we opt-in only for known wrapper names.
|
|
319
|
+
shell: needsWindowsShell(argv.file),
|
|
289
320
|
env: {
|
|
290
321
|
...process.env,
|
|
291
322
|
...IS_WINDOWS ? {} : { FORCE_COLOR: "0" }
|
|
292
|
-
}
|
|
323
|
+
},
|
|
324
|
+
encoding: "utf-8",
|
|
325
|
+
maxBuffer: 64 * 1024 * 1024
|
|
293
326
|
});
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
327
|
+
if (r.error) {
|
|
328
|
+
output = `Failed to launch test runner: ${r.error.message}`;
|
|
329
|
+
exitCode = 1;
|
|
330
|
+
} else {
|
|
331
|
+
exitCode = r.status ?? 1;
|
|
332
|
+
output = (r.stdout ?? "") + (r.stderr ? "\n" + r.stderr : "");
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
try {
|
|
336
|
+
const buf = execSync(command, {
|
|
337
|
+
cwd,
|
|
338
|
+
timeout: TEST_TIMEOUT,
|
|
339
|
+
encoding: "buffer",
|
|
340
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
341
|
+
env: {
|
|
342
|
+
...process.env,
|
|
343
|
+
...IS_WINDOWS ? {} : { FORCE_COLOR: "0" }
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
output = buf.toString("utf-8");
|
|
347
|
+
} catch (err) {
|
|
348
|
+
const e = err;
|
|
349
|
+
exitCode = e.status ?? 1;
|
|
350
|
+
const stdout = e.stdout?.toString("utf-8") ?? "";
|
|
351
|
+
const stderr = e.stderr?.toString("utf-8") ?? "";
|
|
352
|
+
output = stdout + (stderr ? "\n" + stderr : "");
|
|
353
|
+
}
|
|
301
354
|
}
|
|
302
355
|
let summary = {
|
|
303
356
|
tests: 0,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/core/constants.ts
|
|
4
|
-
var VERSION = "0.4.
|
|
4
|
+
var VERSION = "0.4.105";
|
|
5
5
|
var APP_NAME = "ai-cli";
|
|
6
6
|
var CONFIG_DIR_NAME = ".aicli";
|
|
7
7
|
var CONFIG_FILE_NAME = "config.json";
|
|
@@ -14,6 +14,8 @@ var MEMORY_FILE_NAME = "memory.md";
|
|
|
14
14
|
var MEMORY_MAX_CHARS = 1e4;
|
|
15
15
|
var DEV_STATE_FILE_NAME = "dev-state.md";
|
|
16
16
|
var DEFAULT_MAX_TOKENS = 8192;
|
|
17
|
+
var DEFAULT_TEMPERATURE = 0.7;
|
|
18
|
+
var MAX_HISTORY_DAYS = 30;
|
|
17
19
|
var DEFAULT_MAX_TOOL_ROUNDS = 200;
|
|
18
20
|
var DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP = 5e5;
|
|
19
21
|
var MCP_TOOL_PREFIX = "mcp__";
|
|
@@ -68,24 +70,22 @@ Once planning is complete, clearly inform the user: type \`/plan execute\` to be
|
|
|
68
70
|
var SUBAGENT_DEFAULT_MAX_ROUNDS = 15;
|
|
69
71
|
var SUBAGENT_MAX_ROUNDS_LIMIT = 30;
|
|
70
72
|
var SUBAGENT_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
|
|
71
|
-
"bash",
|
|
72
73
|
"read_file",
|
|
73
74
|
"write_file",
|
|
74
75
|
"edit_file",
|
|
75
76
|
"list_dir",
|
|
76
77
|
"grep_files",
|
|
77
78
|
"glob_files",
|
|
78
|
-
"run_interactive",
|
|
79
79
|
"web_fetch",
|
|
80
80
|
"google_search",
|
|
81
81
|
"write_todos",
|
|
82
|
-
"run_tests",
|
|
83
82
|
"find_symbol",
|
|
84
83
|
"get_outline",
|
|
85
84
|
"find_references",
|
|
86
85
|
"search_code"
|
|
87
86
|
]);
|
|
88
87
|
var CONTEXT_PRESSURE_THRESHOLD = 0.8;
|
|
88
|
+
var CONTEXT_WARNING_THRESHOLD = 0.6;
|
|
89
89
|
var TEST_TIMEOUT = 3e5;
|
|
90
90
|
var AGENTIC_BEHAVIOR_GUIDELINE = `# Important Behavioral Guidelines
|
|
91
91
|
|
|
@@ -146,6 +146,8 @@ export {
|
|
|
146
146
|
MEMORY_MAX_CHARS,
|
|
147
147
|
DEV_STATE_FILE_NAME,
|
|
148
148
|
DEFAULT_MAX_TOKENS,
|
|
149
|
+
DEFAULT_TEMPERATURE,
|
|
150
|
+
MAX_HISTORY_DAYS,
|
|
149
151
|
DEFAULT_MAX_TOOL_ROUNDS,
|
|
150
152
|
DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
|
|
151
153
|
MCP_TOOL_PREFIX,
|
|
@@ -159,6 +161,7 @@ export {
|
|
|
159
161
|
SUBAGENT_MAX_ROUNDS_LIMIT,
|
|
160
162
|
SUBAGENT_ALLOWED_TOOLS,
|
|
161
163
|
CONTEXT_PRESSURE_THRESHOLD,
|
|
164
|
+
CONTEXT_WARNING_THRESHOLD,
|
|
162
165
|
TEST_TIMEOUT,
|
|
163
166
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
164
167
|
buildUserIdentityPrompt,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
__require
|
|
11
|
+
};
|