@4pm/cli 0.8.0 → 0.8.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/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
__require,
|
|
12
12
|
__toESM,
|
|
13
13
|
wrapper_default
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-I7WQWC33.js";
|
|
15
15
|
|
|
16
16
|
// ../../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
|
|
17
17
|
var require_react_production_min = __commonJS({
|
|
@@ -1995,14 +1995,14 @@ var require_react_development = __commonJS({
|
|
|
1995
1995
|
var thenableResult = result;
|
|
1996
1996
|
var wasAwaited = false;
|
|
1997
1997
|
var thenable = {
|
|
1998
|
-
then: function(
|
|
1998
|
+
then: function(resolve8, reject) {
|
|
1999
1999
|
wasAwaited = true;
|
|
2000
2000
|
thenableResult.then(function(returnValue2) {
|
|
2001
2001
|
popActScope(prevActScopeDepth);
|
|
2002
2002
|
if (actScopeDepth === 0) {
|
|
2003
|
-
recursivelyFlushAsyncActWork(returnValue2,
|
|
2003
|
+
recursivelyFlushAsyncActWork(returnValue2, resolve8, reject);
|
|
2004
2004
|
} else {
|
|
2005
|
-
|
|
2005
|
+
resolve8(returnValue2);
|
|
2006
2006
|
}
|
|
2007
2007
|
}, function(error2) {
|
|
2008
2008
|
popActScope(prevActScopeDepth);
|
|
@@ -2032,20 +2032,20 @@ var require_react_development = __commonJS({
|
|
|
2032
2032
|
ReactCurrentActQueue.current = null;
|
|
2033
2033
|
}
|
|
2034
2034
|
var _thenable = {
|
|
2035
|
-
then: function(
|
|
2035
|
+
then: function(resolve8, reject) {
|
|
2036
2036
|
if (ReactCurrentActQueue.current === null) {
|
|
2037
2037
|
ReactCurrentActQueue.current = [];
|
|
2038
|
-
recursivelyFlushAsyncActWork(returnValue,
|
|
2038
|
+
recursivelyFlushAsyncActWork(returnValue, resolve8, reject);
|
|
2039
2039
|
} else {
|
|
2040
|
-
|
|
2040
|
+
resolve8(returnValue);
|
|
2041
2041
|
}
|
|
2042
2042
|
}
|
|
2043
2043
|
};
|
|
2044
2044
|
return _thenable;
|
|
2045
2045
|
} else {
|
|
2046
2046
|
var _thenable2 = {
|
|
2047
|
-
then: function(
|
|
2048
|
-
|
|
2047
|
+
then: function(resolve8, reject) {
|
|
2048
|
+
resolve8(returnValue);
|
|
2049
2049
|
}
|
|
2050
2050
|
};
|
|
2051
2051
|
return _thenable2;
|
|
@@ -2061,7 +2061,7 @@ var require_react_development = __commonJS({
|
|
|
2061
2061
|
actScopeDepth = prevActScopeDepth;
|
|
2062
2062
|
}
|
|
2063
2063
|
}
|
|
2064
|
-
function recursivelyFlushAsyncActWork(returnValue,
|
|
2064
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve8, reject) {
|
|
2065
2065
|
{
|
|
2066
2066
|
var queue = ReactCurrentActQueue.current;
|
|
2067
2067
|
if (queue !== null) {
|
|
@@ -2070,16 +2070,16 @@ var require_react_development = __commonJS({
|
|
|
2070
2070
|
enqueueTask(function() {
|
|
2071
2071
|
if (queue.length === 0) {
|
|
2072
2072
|
ReactCurrentActQueue.current = null;
|
|
2073
|
-
|
|
2073
|
+
resolve8(returnValue);
|
|
2074
2074
|
} else {
|
|
2075
|
-
recursivelyFlushAsyncActWork(returnValue,
|
|
2075
|
+
recursivelyFlushAsyncActWork(returnValue, resolve8, reject);
|
|
2076
2076
|
}
|
|
2077
2077
|
});
|
|
2078
2078
|
} catch (error2) {
|
|
2079
2079
|
reject(error2);
|
|
2080
2080
|
}
|
|
2081
2081
|
} else {
|
|
2082
|
-
|
|
2082
|
+
resolve8(returnValue);
|
|
2083
2083
|
}
|
|
2084
2084
|
}
|
|
2085
2085
|
}
|
|
@@ -24691,8 +24691,8 @@ async function runLink(serverUrl, explicitProfile) {
|
|
|
24691
24691
|
}
|
|
24692
24692
|
|
|
24693
24693
|
// src/commands/start.ts
|
|
24694
|
-
import { spawn as
|
|
24695
|
-
import { join as
|
|
24694
|
+
import { spawn as spawn6 } from "child_process";
|
|
24695
|
+
import { join as join23 } from "path";
|
|
24696
24696
|
|
|
24697
24697
|
// src/core/update.ts
|
|
24698
24698
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -24863,7 +24863,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
24863
24863
|
|
|
24864
24864
|
// src/core/ws-client.ts
|
|
24865
24865
|
import { randomUUID } from "crypto";
|
|
24866
|
-
import { basename as
|
|
24866
|
+
import { basename as basename4, join as join19 } from "path";
|
|
24867
24867
|
|
|
24868
24868
|
// ../../22-packages/11-shared/51-utils/src/helpers.ts
|
|
24869
24869
|
function backoffJitterMs(attempt, baseMs = 500, maxMs = 1e4) {
|
|
@@ -24972,6 +24972,12 @@ var PermissionCode = {
|
|
|
24972
24972
|
PROJECT_GIT_WRITE: "project.git_write",
|
|
24973
24973
|
// Autonomous mode (ADR-0152): settings/start-pause/VERIFY-approve/post-USER_TODO
|
|
24974
24974
|
PROJECT_AUTONOMOUS: "project.autonomous",
|
|
24975
|
+
// Subagents & skills management (ADR-0153): create/edit/delete .claude/agents + skills
|
|
24976
|
+
PROJECT_AGENTS: "project.agents",
|
|
24977
|
+
// Security & placeholder management (ADR-0154): AI_SECURITY/AI_PLACEHOLDER docs + write-only secrets
|
|
24978
|
+
PROJECT_SECRETS: "project.secrets",
|
|
24979
|
+
// RAG capability install (ADR-0156): install/manage the worker's RAG engine
|
|
24980
|
+
PROJECT_RAG: "project.rag",
|
|
24975
24981
|
// permission
|
|
24976
24982
|
PERMISSION_LIST: "permission.list",
|
|
24977
24983
|
PERMISSION_READ: "permission.read",
|
|
@@ -25074,6 +25080,9 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25074
25080
|
P.PROJECT_GIT,
|
|
25075
25081
|
P.PROJECT_GIT_WRITE,
|
|
25076
25082
|
P.PROJECT_AUTONOMOUS,
|
|
25083
|
+
P.PROJECT_AGENTS,
|
|
25084
|
+
P.PROJECT_SECRETS,
|
|
25085
|
+
P.PROJECT_RAG,
|
|
25077
25086
|
P.PERMISSION_LIST,
|
|
25078
25087
|
P.PERMISSION_READ,
|
|
25079
25088
|
P.MACHINE_PAIR,
|
|
@@ -25099,6 +25108,12 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25099
25108
|
P.PROJECT_MEMO_READ,
|
|
25100
25109
|
P.PROJECT_MEMO_WRITE,
|
|
25101
25110
|
P.PROJECT_GIT,
|
|
25111
|
+
// manage subagents/skills (dev artifacts) — ADR-0153
|
|
25112
|
+
P.PROJECT_AGENTS,
|
|
25113
|
+
// manage security/placeholder + set (write-only) secrets — ADR-0154
|
|
25114
|
+
P.PROJECT_SECRETS,
|
|
25115
|
+
// install/manage the worker RAG engine — ADR-0156
|
|
25116
|
+
P.PROJECT_RAG,
|
|
25102
25117
|
P.INTEGRATION_READ
|
|
25103
25118
|
],
|
|
25104
25119
|
[Role.QA]: [
|
|
@@ -25133,6 +25148,8 @@ var ROLE_DEFAULT_PERMISSIONS = {
|
|
|
25133
25148
|
P.PROJECT_GIT_WRITE,
|
|
25134
25149
|
// autonomous mode control on its own physic project (ADR-0152)
|
|
25135
25150
|
P.PROJECT_AUTONOMOUS,
|
|
25151
|
+
// manage its own subagents/skills (ADR-0153)
|
|
25152
|
+
P.PROJECT_AGENTS,
|
|
25136
25153
|
// view one's own worker/cli + configure the physic project
|
|
25137
25154
|
P.MACHINE_LIST,
|
|
25138
25155
|
P.MACHINE_PHYSIC_MANAGE,
|
|
@@ -25163,7 +25180,10 @@ var PROJECT_SCOPE_CHECKED_CODES = [
|
|
|
25163
25180
|
P.PROJECT_AI_ASSIST,
|
|
25164
25181
|
P.PROJECT_GIT,
|
|
25165
25182
|
P.PROJECT_GIT_WRITE,
|
|
25166
|
-
P.PROJECT_AUTONOMOUS
|
|
25183
|
+
P.PROJECT_AUTONOMOUS,
|
|
25184
|
+
P.PROJECT_AGENTS,
|
|
25185
|
+
P.PROJECT_SECRETS,
|
|
25186
|
+
P.PROJECT_RAG
|
|
25167
25187
|
];
|
|
25168
25188
|
var SELF_SERVICE_CODES = [
|
|
25169
25189
|
P.AUTH_LOGOUT,
|
|
@@ -25206,6 +25226,9 @@ var TEAM_GRANTABLE_CODES = [
|
|
|
25206
25226
|
P.PROJECT_GIT,
|
|
25207
25227
|
P.PROJECT_GIT_WRITE,
|
|
25208
25228
|
P.PROJECT_AUTONOMOUS,
|
|
25229
|
+
P.PROJECT_AGENTS,
|
|
25230
|
+
P.PROJECT_SECRETS,
|
|
25231
|
+
P.PROJECT_RAG,
|
|
25209
25232
|
P.COMMAND_EXECUTE,
|
|
25210
25233
|
P.COMMAND_READ,
|
|
25211
25234
|
P.MACHINE_LIST,
|
|
@@ -25230,6 +25253,9 @@ var PROJECT_GRANTABLE_CODES = [
|
|
|
25230
25253
|
P.PROJECT_GIT,
|
|
25231
25254
|
P.PROJECT_GIT_WRITE,
|
|
25232
25255
|
P.PROJECT_AUTONOMOUS,
|
|
25256
|
+
P.PROJECT_AGENTS,
|
|
25257
|
+
P.PROJECT_SECRETS,
|
|
25258
|
+
P.PROJECT_RAG,
|
|
25233
25259
|
P.COMMAND_EXECUTE,
|
|
25234
25260
|
P.COMMAND_READ,
|
|
25235
25261
|
P.MACHINE_PHYSIC_MANAGE
|
|
@@ -29342,7 +29368,7 @@ var pinnedFilterSchema = external_exports.enum(["true", "false"]).transform((v)
|
|
|
29342
29368
|
var deletedFilterSchema = external_exports.enum(["true", "false"]).transform((v) => v === "true").optional();
|
|
29343
29369
|
|
|
29344
29370
|
// ../../22-packages/11-shared/41-validation/src/schemas.ts
|
|
29345
|
-
var passwordSchema = external_exports.string().min(8, "password: at least 8 characters").max(
|
|
29371
|
+
var passwordSchema = external_exports.string().min(8, "password: at least 8 characters").max(32, "password: at most 32 characters").regex(/[a-zA-Z]/, "password: needs at least 1 letter").regex(/[0-9]/, "password: needs at least 1 digit");
|
|
29346
29372
|
var usernameSchema = external_exports.string().min(3).max(32).regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/, "username: invalid characters");
|
|
29347
29373
|
var emailSchema = external_exports.string().email().max(254).toLowerCase();
|
|
29348
29374
|
var phoneSchema = external_exports.string().regex(/^\+?[0-9]{8,15}$/, "phone: invalid format");
|
|
@@ -29371,6 +29397,9 @@ var registerRequestSchema = external_exports.object({
|
|
|
29371
29397
|
var mailTokenRequestSchema = external_exports.object({
|
|
29372
29398
|
token: hexTokenSchema
|
|
29373
29399
|
});
|
|
29400
|
+
var resendVerifyRequestSchema = external_exports.object({
|
|
29401
|
+
email: emailSchema
|
|
29402
|
+
});
|
|
29374
29403
|
var loginRequestSchema = external_exports.object({
|
|
29375
29404
|
identifier: external_exports.string().min(1).max(254),
|
|
29376
29405
|
password: external_exports.string().min(1).max(72),
|
|
@@ -29762,6 +29791,38 @@ var autonomousWriteRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
|
29762
29791
|
var autonomousLogsQuerySchema = external_exports.object({
|
|
29763
29792
|
date: external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional()
|
|
29764
29793
|
});
|
|
29794
|
+
var agentNameSchema = external_exports.string().min(1).max(64).regex(/^[\w.-]+$/);
|
|
29795
|
+
var agentReadQuerySchema = external_exports.object({
|
|
29796
|
+
kind: external_exports.enum(["subagent", "skill"]),
|
|
29797
|
+
name: agentNameSchema
|
|
29798
|
+
});
|
|
29799
|
+
var ragInstallRequestSchema = external_exports.object({
|
|
29800
|
+
model: external_exports.string().min(1).max(200)
|
|
29801
|
+
});
|
|
29802
|
+
var ragQueryQuerySchema = external_exports.object({
|
|
29803
|
+
q: external_exports.string().min(1).max(2e3),
|
|
29804
|
+
k: external_exports.coerce.number().int().min(1).max(20).optional()
|
|
29805
|
+
});
|
|
29806
|
+
var graphBuildQuerySchema = external_exports.object({
|
|
29807
|
+
mode: external_exports.enum(["docs", "code"])
|
|
29808
|
+
});
|
|
29809
|
+
var secretKeySchema = external_exports.string().min(1).max(128).regex(/^[\w.-]+$/);
|
|
29810
|
+
var secretsWriteRequestSchema = external_exports.discriminatedUnion("kind", [
|
|
29811
|
+
external_exports.object({ kind: external_exports.literal("security"), content: external_exports.string().max(128 * 1024) }),
|
|
29812
|
+
external_exports.object({ kind: external_exports.literal("placeholder"), content: external_exports.string().max(128 * 1024) }),
|
|
29813
|
+
external_exports.object({ kind: external_exports.literal("secret"), key: secretKeySchema, value: external_exports.string().max(8192) }),
|
|
29814
|
+
external_exports.object({ kind: external_exports.literal("secretDelete"), key: secretKeySchema })
|
|
29815
|
+
]);
|
|
29816
|
+
var agentWriteRequestSchema = external_exports.object({
|
|
29817
|
+
kind: external_exports.enum(["subagent", "skill"]),
|
|
29818
|
+
name: agentNameSchema,
|
|
29819
|
+
action: external_exports.enum(["write", "delete"]),
|
|
29820
|
+
content: external_exports.string().max(128 * 1024).optional()
|
|
29821
|
+
}).superRefine((v, ctx) => {
|
|
29822
|
+
if (v.action === "write" && (v.content === void 0 || v.content.length === 0)) {
|
|
29823
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["content"], message: "content is required for write" });
|
|
29824
|
+
}
|
|
29825
|
+
});
|
|
29765
29826
|
|
|
29766
29827
|
// ../../22-packages/11-shared/31-dto/src/command.ts
|
|
29767
29828
|
var COMMAND_MAX_LEN = 8e3;
|
|
@@ -30021,6 +30082,21 @@ var WsChannels = {
|
|
|
30021
30082
|
AUTONOMOUS_READ: "autonomous.read",
|
|
30022
30083
|
AUTONOMOUS_WRITE: "autonomous.write",
|
|
30023
30084
|
AUTONOMOUS_LOGS: "autonomous.logs",
|
|
30085
|
+
// subagents & skills management (ADR-0153): list/read/write .claude/agents + .claude/skills
|
|
30086
|
+
AGENTS_LIST: "agents.list",
|
|
30087
|
+
AGENTS_READ: "agents.read",
|
|
30088
|
+
AGENTS_WRITE: "agents.write",
|
|
30089
|
+
// security & placeholder management (ADR-0154): AI_SECURITY/AI_PLACEHOLDER + write-only secrets
|
|
30090
|
+
SECRETS_READ: "secrets.read",
|
|
30091
|
+
SECRETS_WRITE: "secrets.write",
|
|
30092
|
+
// docs & code dependency graph (ADR-0155): build a {nodes,edges,orphans} graph per mode
|
|
30093
|
+
GRAPH_BUILD: "graph.build",
|
|
30094
|
+
// RAG capability + install (ADR-0156): probe the worker + install a worker-tuned model
|
|
30095
|
+
RAG_STATUS: "rag.status",
|
|
30096
|
+
RAG_INSTALL: "rag.install",
|
|
30097
|
+
// RAG index + query (ADR-0157): reindex docs into sqlite-vec, semantic query
|
|
30098
|
+
RAG_REINDEX: "rag.reindex",
|
|
30099
|
+
RAG_QUERY: "rag.query",
|
|
30024
30100
|
// git history browse (read-through cli — ADR-0089): repos on the physic project,
|
|
30025
30101
|
// commit log, changed files of a commit, parent↔commit diff of one file.
|
|
30026
30102
|
GIT_REPOS: "git.repos",
|
|
@@ -30180,7 +30256,7 @@ var OutputBatcher = class {
|
|
|
30180
30256
|
};
|
|
30181
30257
|
|
|
30182
30258
|
// src/core/ws-client.ts
|
|
30183
|
-
import { existsSync as
|
|
30259
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, rmSync as rmSync4 } from "fs";
|
|
30184
30260
|
import { randomUUID as randomCommandId } from "crypto";
|
|
30185
30261
|
|
|
30186
30262
|
// src/core/update-scheduler.ts
|
|
@@ -30423,7 +30499,7 @@ async function runCommand(dispatch, emit) {
|
|
|
30423
30499
|
});
|
|
30424
30500
|
child.stdout?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
30425
30501
|
child.stderr?.on("data", (data) => batcher.push(data.toString("utf8")));
|
|
30426
|
-
await new Promise((
|
|
30502
|
+
await new Promise((resolve8) => {
|
|
30427
30503
|
child.on("close", (exitCode) => {
|
|
30428
30504
|
batcher.flush();
|
|
30429
30505
|
emit({
|
|
@@ -30433,7 +30509,7 @@ async function runCommand(dispatch, emit) {
|
|
|
30433
30509
|
done: true,
|
|
30434
30510
|
exitCode: exitCode ?? -1
|
|
30435
30511
|
});
|
|
30436
|
-
|
|
30512
|
+
resolve8();
|
|
30437
30513
|
});
|
|
30438
30514
|
child.on("error", (err) => {
|
|
30439
30515
|
emit({
|
|
@@ -30444,7 +30520,7 @@ async function runCommand(dispatch, emit) {
|
|
|
30444
30520
|
done: true,
|
|
30445
30521
|
exitCode: -1
|
|
30446
30522
|
});
|
|
30447
|
-
|
|
30523
|
+
resolve8();
|
|
30448
30524
|
});
|
|
30449
30525
|
});
|
|
30450
30526
|
}
|
|
@@ -30524,10 +30600,10 @@ function isSessionLimit(text) {
|
|
|
30524
30600
|
);
|
|
30525
30601
|
}
|
|
30526
30602
|
function runAttempt(commandId, cmd, args, cwd2, env3, onChunk) {
|
|
30527
|
-
return new Promise((
|
|
30603
|
+
return new Promise((resolve8) => {
|
|
30528
30604
|
void runCommand({ commandId, cmd, args, path: cwd2, env: env3 }, (out) => {
|
|
30529
30605
|
if (out.chunk) onChunk(out.chunk);
|
|
30530
|
-
if (out.done)
|
|
30606
|
+
if (out.done) resolve8(out.exitCode ?? -1);
|
|
30531
30607
|
});
|
|
30532
30608
|
});
|
|
30533
30609
|
}
|
|
@@ -30705,16 +30781,16 @@ async function fetchUsage(token) {
|
|
|
30705
30781
|
}
|
|
30706
30782
|
}
|
|
30707
30783
|
function runUsageCli(dir2, aiCli2) {
|
|
30708
|
-
return new Promise((
|
|
30784
|
+
return new Promise((resolve8) => {
|
|
30709
30785
|
let out = "";
|
|
30710
30786
|
let child = null;
|
|
30711
30787
|
const finish = () => {
|
|
30712
30788
|
clearTimeout(timer);
|
|
30713
|
-
|
|
30789
|
+
resolve8(out);
|
|
30714
30790
|
};
|
|
30715
30791
|
const timer = setTimeout(() => {
|
|
30716
30792
|
child?.kill();
|
|
30717
|
-
|
|
30793
|
+
resolve8(out);
|
|
30718
30794
|
}, 3e4);
|
|
30719
30795
|
timer.unref?.();
|
|
30720
30796
|
try {
|
|
@@ -31220,10 +31296,10 @@ async function crontabList() {
|
|
|
31220
31296
|
}
|
|
31221
31297
|
}
|
|
31222
31298
|
async function crontabSet(content) {
|
|
31223
|
-
return new Promise((
|
|
31299
|
+
return new Promise((resolve8) => {
|
|
31224
31300
|
const child = spawn4("crontab", ["-"], { stdio: ["pipe", "ignore", "ignore"] });
|
|
31225
|
-
child.on("error", () =>
|
|
31226
|
-
child.on("close", (code) =>
|
|
31301
|
+
child.on("error", () => resolve8(false));
|
|
31302
|
+
child.on("close", (code) => resolve8(code === 0));
|
|
31227
31303
|
child.stdin.end(content.endsWith("\n") ? content : content + "\n");
|
|
31228
31304
|
});
|
|
31229
31305
|
}
|
|
@@ -31331,24 +31407,552 @@ ${req.content.trim()}
|
|
|
31331
31407
|
}
|
|
31332
31408
|
}
|
|
31333
31409
|
|
|
31334
|
-
// src/core/
|
|
31335
|
-
import { readFile as readFile3 } from "fs/promises";
|
|
31336
|
-
import {
|
|
31337
|
-
|
|
31410
|
+
// src/core/agents.ts
|
|
31411
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir as readdir3, rm } from "fs/promises";
|
|
31412
|
+
import { join as join13 } from "path";
|
|
31413
|
+
var AGENTS_REL = ".claude/agents";
|
|
31414
|
+
var SKILLS_REL = ".claude/skills";
|
|
31415
|
+
var MEMORY_DIRS = /* @__PURE__ */ new Set(["short-memory", "long-memory"]);
|
|
31416
|
+
function safeName(name) {
|
|
31417
|
+
return name.replace(/[^\w.-]+/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
|
31418
|
+
}
|
|
31419
|
+
async function readText2(path, fallback = "") {
|
|
31420
|
+
try {
|
|
31421
|
+
return await readFile3(path, "utf8");
|
|
31422
|
+
} catch {
|
|
31423
|
+
return fallback;
|
|
31424
|
+
}
|
|
31425
|
+
}
|
|
31426
|
+
function frontmatter(text) {
|
|
31427
|
+
const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
31428
|
+
const body = m?.[1] ?? "";
|
|
31429
|
+
const get = (key) => body.match(new RegExp(`^${key}:\\s*(.+)$`, "m"))?.[1]?.trim().replace(/^["']|["']$/g, "") ?? "";
|
|
31430
|
+
return { name: get("name"), description: get("description") };
|
|
31431
|
+
}
|
|
31432
|
+
async function listAgents(root) {
|
|
31433
|
+
const subagents = [];
|
|
31434
|
+
const skills = [];
|
|
31435
|
+
try {
|
|
31436
|
+
for (const e of await readdir3(join13(root, AGENTS_REL), { withFileTypes: true })) {
|
|
31437
|
+
if (!e.isFile() || !e.name.endsWith(".md") || MEMORY_DIRS.has(e.name)) continue;
|
|
31438
|
+
const fm = frontmatter(await readText2(join13(root, AGENTS_REL, e.name)));
|
|
31439
|
+
subagents.push({ name: e.name.replace(/\.md$/, ""), description: fm.description });
|
|
31440
|
+
}
|
|
31441
|
+
} catch {
|
|
31442
|
+
}
|
|
31443
|
+
try {
|
|
31444
|
+
for (const e of await readdir3(join13(root, SKILLS_REL), { withFileTypes: true })) {
|
|
31445
|
+
if (!e.isDirectory()) continue;
|
|
31446
|
+
const fm = frontmatter(await readText2(join13(root, SKILLS_REL, e.name, "SKILL.md")));
|
|
31447
|
+
skills.push({ name: e.name, description: fm.description });
|
|
31448
|
+
}
|
|
31449
|
+
} catch {
|
|
31450
|
+
}
|
|
31451
|
+
subagents.sort((a, b) => a.name.localeCompare(b.name));
|
|
31452
|
+
skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
31453
|
+
return { subagents, skills };
|
|
31454
|
+
}
|
|
31455
|
+
function itemPath(root, kind, name) {
|
|
31456
|
+
const n = safeName(name);
|
|
31457
|
+
return kind === "subagent" ? join13(root, AGENTS_REL, `${n}.md`) : join13(root, SKILLS_REL, n, "SKILL.md");
|
|
31458
|
+
}
|
|
31459
|
+
async function readAgent(root, kind, name) {
|
|
31460
|
+
const content = await readText2(itemPath(root, kind, name));
|
|
31461
|
+
if (kind !== "subagent") return { content };
|
|
31462
|
+
const n = safeName(name);
|
|
31463
|
+
const [short, long] = await Promise.all([
|
|
31464
|
+
readText2(join13(root, AGENTS_REL, "short-memory", `${n}.md`)),
|
|
31465
|
+
readText2(join13(root, AGENTS_REL, "long-memory", `${n}.md`))
|
|
31466
|
+
]);
|
|
31467
|
+
return { content, memory: { short, long } };
|
|
31468
|
+
}
|
|
31469
|
+
async function writeAgent(root, req) {
|
|
31470
|
+
const n = safeName(req.name);
|
|
31471
|
+
if (!n) return { ok: false, error: "invalid name" };
|
|
31472
|
+
try {
|
|
31473
|
+
if (req.action === "delete") {
|
|
31474
|
+
if (req.kind === "subagent") await rm(itemPath(root, "subagent", n), { force: true });
|
|
31475
|
+
else await rm(join13(root, SKILLS_REL, n), { recursive: true, force: true });
|
|
31476
|
+
return { ok: true };
|
|
31477
|
+
}
|
|
31478
|
+
if (req.content === void 0) return { ok: false, error: "content is required" };
|
|
31479
|
+
const path = itemPath(root, req.kind, n);
|
|
31480
|
+
await mkdir3(join13(path, ".."), { recursive: true });
|
|
31481
|
+
await writeFile3(path, req.content, "utf8");
|
|
31482
|
+
return { ok: true };
|
|
31483
|
+
} catch (err) {
|
|
31484
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
31485
|
+
}
|
|
31486
|
+
}
|
|
31487
|
+
|
|
31488
|
+
// src/core/secrets.ts
|
|
31489
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
31490
|
+
import { join as join14 } from "path";
|
|
31491
|
+
var SECURITY_REL = "AI_SECURITY.md";
|
|
31492
|
+
var PLACEHOLDER_REL = "AI_PLACEHOLDER.md";
|
|
31493
|
+
var SECRETS_REL = "project.secrets.json";
|
|
31494
|
+
async function readText3(path, fallback = "") {
|
|
31495
|
+
try {
|
|
31496
|
+
return await readFile4(path, "utf8");
|
|
31497
|
+
} catch {
|
|
31498
|
+
return fallback;
|
|
31499
|
+
}
|
|
31500
|
+
}
|
|
31501
|
+
async function readSecretsObject(root) {
|
|
31502
|
+
try {
|
|
31503
|
+
const v = JSON.parse(await readFile4(join14(root, SECRETS_REL), "utf8"));
|
|
31504
|
+
return v && typeof v === "object" ? v : {};
|
|
31505
|
+
} catch {
|
|
31506
|
+
return {};
|
|
31507
|
+
}
|
|
31508
|
+
}
|
|
31509
|
+
function placeholderKeys(text) {
|
|
31510
|
+
const keys = [];
|
|
31511
|
+
for (const line of text.split("\n")) {
|
|
31512
|
+
const s = line.trim();
|
|
31513
|
+
if (!s.startsWith("|")) continue;
|
|
31514
|
+
if (/^\|[\s:|-]+\|?$/.test(s)) continue;
|
|
31515
|
+
const first = s.replace(/^\|/, "").split("|")[0]?.trim() ?? "";
|
|
31516
|
+
if (/^[\w.-]+$/.test(first) && first.toLowerCase() !== "key") keys.push(first);
|
|
31517
|
+
}
|
|
31518
|
+
return keys;
|
|
31519
|
+
}
|
|
31520
|
+
async function readSecrets(root) {
|
|
31521
|
+
const [security, placeholder, secrets] = await Promise.all([
|
|
31522
|
+
readText3(join14(root, SECURITY_REL)),
|
|
31523
|
+
readText3(join14(root, PLACEHOLDER_REL)),
|
|
31524
|
+
readSecretsObject(root)
|
|
31525
|
+
]);
|
|
31526
|
+
const names = /* @__PURE__ */ new Set([...placeholderKeys(placeholder), ...Object.keys(secrets)]);
|
|
31527
|
+
const keys = [...names].sort().map((name) => ({
|
|
31528
|
+
name,
|
|
31529
|
+
set: secrets[name] !== void 0 && String(secrets[name]).length > 0
|
|
31530
|
+
}));
|
|
31531
|
+
return { security, placeholder, keys };
|
|
31532
|
+
}
|
|
31533
|
+
async function writeSecrets(root, req) {
|
|
31534
|
+
try {
|
|
31535
|
+
if (req.kind === "security") {
|
|
31536
|
+
await writeFile4(join14(root, SECURITY_REL), req.content, "utf8");
|
|
31537
|
+
} else if (req.kind === "placeholder") {
|
|
31538
|
+
await writeFile4(join14(root, PLACEHOLDER_REL), req.content, "utf8");
|
|
31539
|
+
} else {
|
|
31540
|
+
const secrets = await readSecretsObject(root);
|
|
31541
|
+
if (req.kind === "secret") secrets[req.key] = req.value;
|
|
31542
|
+
else delete secrets[req.key];
|
|
31543
|
+
await writeFile4(join14(root, SECRETS_REL), JSON.stringify(secrets, null, 2) + "\n", "utf8");
|
|
31544
|
+
}
|
|
31545
|
+
return { ok: true };
|
|
31546
|
+
} catch (err) {
|
|
31547
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
31548
|
+
}
|
|
31549
|
+
}
|
|
31550
|
+
|
|
31551
|
+
// src/core/graph.ts
|
|
31552
|
+
import { readFile as readFile5, readdir as readdir4 } from "fs/promises";
|
|
31553
|
+
import { join as join15, relative, dirname as dirname4, resolve as resolve4, extname, basename as basename2 } from "path";
|
|
31554
|
+
var MAX_FILES = 2e3;
|
|
31555
|
+
var MAX_NODES = 3e3;
|
|
31556
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "out", "coverage", ".turbo"]);
|
|
31557
|
+
var CODE_EXT = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs", ".java", ".rb", ".php"]);
|
|
31558
|
+
async function walk(root, accept) {
|
|
31559
|
+
const out = [];
|
|
31560
|
+
const stack = [root];
|
|
31561
|
+
while (stack.length && out.length < MAX_FILES) {
|
|
31562
|
+
const dir2 = stack.pop();
|
|
31563
|
+
let entries2;
|
|
31564
|
+
try {
|
|
31565
|
+
entries2 = await readdir4(dir2, { withFileTypes: true });
|
|
31566
|
+
} catch {
|
|
31567
|
+
continue;
|
|
31568
|
+
}
|
|
31569
|
+
for (const e of entries2) {
|
|
31570
|
+
if (e.name.startsWith(".") && e.name !== ".claude") {
|
|
31571
|
+
if (e.isDirectory()) continue;
|
|
31572
|
+
}
|
|
31573
|
+
const full = join15(dir2, e.name);
|
|
31574
|
+
if (e.isDirectory()) {
|
|
31575
|
+
if (!SKIP_DIRS.has(e.name)) stack.push(full);
|
|
31576
|
+
} else if (accept(full)) {
|
|
31577
|
+
out.push(full);
|
|
31578
|
+
if (out.length >= MAX_FILES) break;
|
|
31579
|
+
}
|
|
31580
|
+
}
|
|
31581
|
+
}
|
|
31582
|
+
return out;
|
|
31583
|
+
}
|
|
31584
|
+
function orphansOf(nodes, edges) {
|
|
31585
|
+
const hasInbound = new Set(edges.map((e) => e.to));
|
|
31586
|
+
return nodes.filter((n) => !hasInbound.has(n.id)).map((n) => n.id);
|
|
31587
|
+
}
|
|
31588
|
+
async function buildDocs(root) {
|
|
31589
|
+
const files = await walk(root, (p) => extname(p) === ".md");
|
|
31590
|
+
const ids = new Set(files.map((f) => relative(root, f).split("\\").join("/")));
|
|
31591
|
+
const nodes = files.map((f) => {
|
|
31592
|
+
const rel = relative(root, f).split("\\").join("/");
|
|
31593
|
+
return { id: rel, label: basename2(rel), kind: "doc", file: rel };
|
|
31594
|
+
});
|
|
31595
|
+
const edges = [];
|
|
31596
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31597
|
+
for (const f of files) {
|
|
31598
|
+
const rel = relative(root, f).split("\\").join("/");
|
|
31599
|
+
let text = "";
|
|
31600
|
+
try {
|
|
31601
|
+
text = await readFile5(f, "utf8");
|
|
31602
|
+
} catch {
|
|
31603
|
+
continue;
|
|
31604
|
+
}
|
|
31605
|
+
for (const m of text.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
|
|
31606
|
+
const href = (m[1] ?? "").split("#")[0].trim();
|
|
31607
|
+
if (!href || !href.endsWith(".md") || href.startsWith("http")) continue;
|
|
31608
|
+
const target = relative(root, resolve4(dirname4(f), href)).split("\\").join("/");
|
|
31609
|
+
if (ids.has(target) && target !== rel) {
|
|
31610
|
+
const key = `${rel}->${target}`;
|
|
31611
|
+
if (!seen.has(key)) {
|
|
31612
|
+
seen.add(key);
|
|
31613
|
+
edges.push({ from: rel, to: target });
|
|
31614
|
+
}
|
|
31615
|
+
}
|
|
31616
|
+
}
|
|
31617
|
+
}
|
|
31618
|
+
return { nodes, edges, orphans: orphansOf(nodes, edges) };
|
|
31619
|
+
}
|
|
31620
|
+
var DEF_PATTERNS = [
|
|
31621
|
+
/\bfunction\s+([A-Za-z_$][\w$]*)/g,
|
|
31622
|
+
// JS/TS function decl
|
|
31623
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?(?:function\b|\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>)/g,
|
|
31624
|
+
// arrow/func expr
|
|
31625
|
+
/^\s*def\s+([A-Za-z_]\w*)/gm,
|
|
31626
|
+
// Python
|
|
31627
|
+
/\bfunc\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/g
|
|
31628
|
+
// Go
|
|
31629
|
+
];
|
|
31630
|
+
async function buildCode(root) {
|
|
31631
|
+
const files = await walk(root, (p) => CODE_EXT.has(extname(p)));
|
|
31632
|
+
const nodes = [];
|
|
31633
|
+
const nameToIds = /* @__PURE__ */ new Map();
|
|
31634
|
+
const fileDefs = /* @__PURE__ */ new Map();
|
|
31635
|
+
for (const f of files) {
|
|
31636
|
+
if (nodes.length >= MAX_NODES) break;
|
|
31637
|
+
const rel = relative(root, f).split("\\").join("/");
|
|
31638
|
+
let text = "";
|
|
31639
|
+
try {
|
|
31640
|
+
text = await readFile5(f, "utf8");
|
|
31641
|
+
} catch {
|
|
31642
|
+
continue;
|
|
31643
|
+
}
|
|
31644
|
+
const defs = [];
|
|
31645
|
+
for (const re of DEF_PATTERNS) {
|
|
31646
|
+
for (const m of text.matchAll(re)) {
|
|
31647
|
+
const name = m[1] ?? "";
|
|
31648
|
+
if (!name || name.length < 2) continue;
|
|
31649
|
+
const id = `${rel}#${name}`;
|
|
31650
|
+
if (nameToIds.has(name) && (nameToIds.get(name) ?? []).includes(id)) continue;
|
|
31651
|
+
if (nodes.length >= MAX_NODES) break;
|
|
31652
|
+
defs.push({ name, id, start: m.index ?? 0 });
|
|
31653
|
+
nodes.push({ id, label: name, kind: "fn", file: rel });
|
|
31654
|
+
nameToIds.set(name, [...nameToIds.get(name) ?? [], id]);
|
|
31655
|
+
}
|
|
31656
|
+
}
|
|
31657
|
+
defs.sort((a, b) => a.start - b.start);
|
|
31658
|
+
fileDefs.set(rel, { rel, text, defs });
|
|
31659
|
+
}
|
|
31660
|
+
const edges = [];
|
|
31661
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31662
|
+
for (const { text, defs } of fileDefs.values()) {
|
|
31663
|
+
for (let i = 0; i < defs.length; i++) {
|
|
31664
|
+
const def = defs[i];
|
|
31665
|
+
const end = i + 1 < defs.length ? defs[i + 1].start : text.length;
|
|
31666
|
+
const body = text.slice(def.start, end);
|
|
31667
|
+
for (const m of body.matchAll(/([A-Za-z_$][\w$]*)\s*\(/g)) {
|
|
31668
|
+
const callee = m[1] ?? "";
|
|
31669
|
+
if (callee === def.name) continue;
|
|
31670
|
+
for (const targetId of nameToIds.get(callee) ?? []) {
|
|
31671
|
+
if (targetId === def.id) continue;
|
|
31672
|
+
const key = `${def.id}->${targetId}`;
|
|
31673
|
+
if (!seen.has(key)) {
|
|
31674
|
+
seen.add(key);
|
|
31675
|
+
edges.push({ from: def.id, to: targetId });
|
|
31676
|
+
}
|
|
31677
|
+
}
|
|
31678
|
+
}
|
|
31679
|
+
}
|
|
31680
|
+
}
|
|
31681
|
+
return { nodes, edges, orphans: orphansOf(nodes, edges) };
|
|
31682
|
+
}
|
|
31683
|
+
async function buildGraph(root, mode) {
|
|
31684
|
+
try {
|
|
31685
|
+
return mode === "docs" ? await buildDocs(root) : await buildCode(root);
|
|
31686
|
+
} catch {
|
|
31687
|
+
return { nodes: [], edges: [], orphans: [] };
|
|
31688
|
+
}
|
|
31689
|
+
}
|
|
31690
|
+
|
|
31691
|
+
// src/core/rag.ts
|
|
31692
|
+
import { readFile as readFile6, writeFile as writeFile5, statfs, mkdir as mkdir4 } from "fs/promises";
|
|
31693
|
+
import { existsSync as existsSync7 } from "fs";
|
|
31694
|
+
import { execFile as execFile3, spawn as spawn5 } from "child_process";
|
|
31695
|
+
import { totalmem, cpus } from "os";
|
|
31696
|
+
import { join as join16 } from "path";
|
|
31338
31697
|
import { promisify as promisify3 } from "util";
|
|
31339
31698
|
var run3 = promisify3(execFile3);
|
|
31699
|
+
var RAG_REL = ".claude/rag";
|
|
31700
|
+
async function tryRun(cmd, args) {
|
|
31701
|
+
try {
|
|
31702
|
+
const { stdout } = await run3(cmd, args, { timeout: 8e3 });
|
|
31703
|
+
return { ok: true, out: stdout.trim() };
|
|
31704
|
+
} catch {
|
|
31705
|
+
return { ok: false, out: "" };
|
|
31706
|
+
}
|
|
31707
|
+
}
|
|
31708
|
+
async function detectPython() {
|
|
31709
|
+
for (const bin of ["python3", "python"]) {
|
|
31710
|
+
const r = await tryRun(bin, ["--version"]);
|
|
31711
|
+
if (r.ok) return { bin, found: true, version: r.out.replace(/^Python\s*/i, "") };
|
|
31712
|
+
}
|
|
31713
|
+
return { bin: "python3", found: false, version: "" };
|
|
31714
|
+
}
|
|
31715
|
+
async function installLog(root) {
|
|
31716
|
+
try {
|
|
31717
|
+
const text = await readFile6(join16(root, RAG_REL, "install.log"), "utf8");
|
|
31718
|
+
return text.split("\n").filter((l) => l.length > 0).slice(-200);
|
|
31719
|
+
} catch {
|
|
31720
|
+
return [];
|
|
31721
|
+
}
|
|
31722
|
+
}
|
|
31723
|
+
function optionsFor(m) {
|
|
31724
|
+
const opts = [
|
|
31725
|
+
{
|
|
31726
|
+
id: "small",
|
|
31727
|
+
label: "Small (bge-small, EN) \xB7 ~130 MB",
|
|
31728
|
+
model: "BAAI/bge-small-en-v1.5",
|
|
31729
|
+
sizeMB: 130,
|
|
31730
|
+
recommended: m.ramGB < 8,
|
|
31731
|
+
note: "Lightweight, low RAM."
|
|
31732
|
+
}
|
|
31733
|
+
];
|
|
31734
|
+
if (m.diskFreeGB > 1.5) {
|
|
31735
|
+
opts.push({
|
|
31736
|
+
id: "multilingual",
|
|
31737
|
+
label: "Multilingual (MiniLM, incl. Vietnamese) \xB7 ~470 MB",
|
|
31738
|
+
model: "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
|
|
31739
|
+
sizeMB: 470,
|
|
31740
|
+
recommended: m.ramGB >= 8 && m.diskFreeGB > 2,
|
|
31741
|
+
note: "Best for mixed VI/EN content."
|
|
31742
|
+
});
|
|
31743
|
+
}
|
|
31744
|
+
if (m.ramGB >= 8 && m.diskFreeGB > 2) {
|
|
31745
|
+
opts.push({
|
|
31746
|
+
id: "base",
|
|
31747
|
+
label: "Base (bge-base, EN) \xB7 ~440 MB",
|
|
31748
|
+
model: "BAAI/bge-base-en-v1.5",
|
|
31749
|
+
sizeMB: 440,
|
|
31750
|
+
recommended: false,
|
|
31751
|
+
note: "Higher quality EN, needs \u2265 8 GB RAM."
|
|
31752
|
+
});
|
|
31753
|
+
}
|
|
31754
|
+
return opts;
|
|
31755
|
+
}
|
|
31756
|
+
async function ragStatus(root) {
|
|
31757
|
+
const py = await detectPython();
|
|
31758
|
+
const [pip, fastembed, sqliteVec] = py.found ? await Promise.all([
|
|
31759
|
+
tryRun(py.bin, ["-m", "pip", "--version"]).then((r) => r.ok),
|
|
31760
|
+
tryRun(py.bin, ["-c", "import fastembed"]).then((r) => r.ok),
|
|
31761
|
+
tryRun(py.bin, ["-c", "import sqlite_vec"]).then((r) => r.ok)
|
|
31762
|
+
]) : [false, false, false];
|
|
31763
|
+
let diskFreeGB = 0;
|
|
31764
|
+
try {
|
|
31765
|
+
const s = await statfs(root);
|
|
31766
|
+
diskFreeGB = Math.round(s.bavail * s.bsize / 1e9 * 10) / 10;
|
|
31767
|
+
} catch {
|
|
31768
|
+
diskFreeGB = 0;
|
|
31769
|
+
}
|
|
31770
|
+
const gpu = (await tryRun("nvidia-smi", ["-L"])).ok;
|
|
31771
|
+
const machine = {
|
|
31772
|
+
ramGB: Math.round(totalmem() / 1e9 * 10) / 10,
|
|
31773
|
+
cpus: cpus().length,
|
|
31774
|
+
diskFreeGB,
|
|
31775
|
+
gpu
|
|
31776
|
+
};
|
|
31777
|
+
let modelName = "";
|
|
31778
|
+
try {
|
|
31779
|
+
modelName = (await readFile6(join16(root, RAG_REL, ".installed"), "utf8")).trim();
|
|
31780
|
+
} catch {
|
|
31781
|
+
modelName = "";
|
|
31782
|
+
}
|
|
31783
|
+
const modelPresent = modelName.length > 0;
|
|
31784
|
+
return {
|
|
31785
|
+
installed: fastembed && sqliteVec && modelPresent,
|
|
31786
|
+
installing: existsSync7(join16(root, RAG_REL, ".installing")),
|
|
31787
|
+
python: { found: py.found, version: py.version },
|
|
31788
|
+
deps: { pip, fastembed, sqliteVec },
|
|
31789
|
+
model: { present: modelPresent, name: modelName },
|
|
31790
|
+
machine,
|
|
31791
|
+
options: optionsFor(machine),
|
|
31792
|
+
installLog: await installLog(root),
|
|
31793
|
+
index: await readIndexMeta(root)
|
|
31794
|
+
};
|
|
31795
|
+
}
|
|
31796
|
+
async function readIndexMeta(root) {
|
|
31797
|
+
const indexing = existsSync7(join16(root, RAG_REL, ".indexing"));
|
|
31798
|
+
try {
|
|
31799
|
+
const m = JSON.parse(await readFile6(join16(root, RAG_REL, ".index.json"), "utf8"));
|
|
31800
|
+
return {
|
|
31801
|
+
present: true,
|
|
31802
|
+
indexing,
|
|
31803
|
+
chunks: typeof m.chunks === "number" ? m.chunks : 0,
|
|
31804
|
+
indexedAt: typeof m.indexedAt === "string" ? m.indexedAt : ""
|
|
31805
|
+
};
|
|
31806
|
+
} catch {
|
|
31807
|
+
return { present: false, indexing, chunks: 0, indexedAt: "" };
|
|
31808
|
+
}
|
|
31809
|
+
}
|
|
31810
|
+
var RAG_ENGINE_PY = `import sys, os, json, glob, sqlite3, datetime
|
|
31811
|
+
BASE = os.path.join(".claude", "rag")
|
|
31812
|
+
DB = os.path.join(BASE, "index.db")
|
|
31813
|
+
def model_name():
|
|
31814
|
+
return open(os.path.join(BASE, ".installed"), encoding="utf-8").read().strip()
|
|
31815
|
+
def connect():
|
|
31816
|
+
import sqlite_vec
|
|
31817
|
+
db = sqlite3.connect(DB)
|
|
31818
|
+
db.enable_load_extension(True)
|
|
31819
|
+
sqlite_vec.load(db)
|
|
31820
|
+
db.enable_load_extension(False)
|
|
31821
|
+
return db
|
|
31822
|
+
def embedder():
|
|
31823
|
+
from fastembed import TextEmbedding
|
|
31824
|
+
return TextEmbedding(model_name())
|
|
31825
|
+
def reindex():
|
|
31826
|
+
import sqlite_vec
|
|
31827
|
+
emb = embedder()
|
|
31828
|
+
chunks = []
|
|
31829
|
+
for p in glob.glob("**/*.md", recursive=True):
|
|
31830
|
+
parts = p.split(os.sep)
|
|
31831
|
+
if "node_modules" in parts or ".git" in parts: continue
|
|
31832
|
+
try: txt = open(p, encoding="utf-8").read()
|
|
31833
|
+
except Exception: continue
|
|
31834
|
+
for i in range(0, len(txt), 800):
|
|
31835
|
+
c = txt[i:i+1000].strip()
|
|
31836
|
+
if len(c) > 20: chunks.append((p, c))
|
|
31837
|
+
if len(chunks) >= 5000: break
|
|
31838
|
+
vectors = list(emb.embed([c for _, c in chunks])) if chunks else []
|
|
31839
|
+
dim = len(vectors[0]) if vectors else 384
|
|
31840
|
+
os.makedirs(BASE, exist_ok=True)
|
|
31841
|
+
db = connect()
|
|
31842
|
+
db.execute("DROP TABLE IF EXISTS chunks")
|
|
31843
|
+
db.execute("CREATE VIRTUAL TABLE chunks USING vec0(embedding float[%d], +path text, +snippet text)" % dim)
|
|
31844
|
+
for (p, c), v in zip(chunks, vectors):
|
|
31845
|
+
db.execute("INSERT INTO chunks(embedding, path, snippet) VALUES (?, ?, ?)",
|
|
31846
|
+
(sqlite_vec.serialize_float32(list(v)), p, c[:400]))
|
|
31847
|
+
db.commit(); db.close()
|
|
31848
|
+
json.dump({"chunks": len(chunks), "indexedAt": datetime.datetime.now().isoformat(timespec="seconds"), "model": model_name()},
|
|
31849
|
+
open(os.path.join(BASE, ".index.json"), "w", encoding="utf-8"))
|
|
31850
|
+
print("[reindex] DONE %d chunks" % len(chunks))
|
|
31851
|
+
def query(q, k):
|
|
31852
|
+
import sqlite_vec
|
|
31853
|
+
v = list(embedder().embed([q]))[0]
|
|
31854
|
+
db = connect()
|
|
31855
|
+
rows = db.execute("SELECT path, snippet, distance FROM chunks WHERE embedding MATCH ? ORDER BY distance LIMIT ?",
|
|
31856
|
+
(sqlite_vec.serialize_float32(list(v)), k)).fetchall()
|
|
31857
|
+
db.close()
|
|
31858
|
+
out = [{"path": r[0], "snippet": r[1], "score": round(1.0/(1.0+float(r[2])), 4)} for r in rows]
|
|
31859
|
+
print(json.dumps({"results": out}, ensure_ascii=False))
|
|
31860
|
+
if __name__ == "__main__":
|
|
31861
|
+
cmd = sys.argv[1] if len(sys.argv) > 1 else ""
|
|
31862
|
+
if cmd == "reindex": reindex()
|
|
31863
|
+
elif cmd == "query": query(sys.argv[2], int(sys.argv[3]) if len(sys.argv) > 3 else 8)
|
|
31864
|
+
`;
|
|
31865
|
+
async function ensureEngine(root) {
|
|
31866
|
+
await mkdir4(join16(root, RAG_REL), { recursive: true }).catch(() => void 0);
|
|
31867
|
+
await writeFile5(join16(root, RAG_REL, "rag_engine.py"), RAG_ENGINE_PY, "utf8");
|
|
31868
|
+
}
|
|
31869
|
+
async function ragReindex(root) {
|
|
31870
|
+
const py = await detectPython();
|
|
31871
|
+
if (!py.found) return { ok: false, started: false, error: "python3 not found" };
|
|
31872
|
+
if (!existsSync7(join16(root, RAG_REL, ".installed"))) return { ok: false, started: false, error: "RAG is not installed" };
|
|
31873
|
+
if (existsSync7(join16(root, RAG_REL, ".indexing"))) return { ok: true, started: true };
|
|
31874
|
+
await ensureEngine(root);
|
|
31875
|
+
const script = `
|
|
31876
|
+
set +e
|
|
31877
|
+
touch "${RAG_REL}/.indexing"
|
|
31878
|
+
{ echo "[reindex] $(date)"; "$RAG_PY" "${RAG_REL}/rag_engine.py" reindex 2>&1; } >> "${RAG_REL}/index.log" 2>&1
|
|
31879
|
+
rm -f "${RAG_REL}/.indexing"
|
|
31880
|
+
`;
|
|
31881
|
+
const child = spawn5("bash", ["-c", script], {
|
|
31882
|
+
cwd: root,
|
|
31883
|
+
env: { ...process.env, RAG_PY: py.bin },
|
|
31884
|
+
detached: true,
|
|
31885
|
+
stdio: "ignore"
|
|
31886
|
+
});
|
|
31887
|
+
child.unref();
|
|
31888
|
+
return { ok: true, started: true };
|
|
31889
|
+
}
|
|
31890
|
+
async function ragQuery(root, queryText, k = 8) {
|
|
31891
|
+
const py = await detectPython();
|
|
31892
|
+
if (!py.found) return { results: [], error: "python3 not found" };
|
|
31893
|
+
if (!existsSync7(join16(root, RAG_REL, "index.db"))) return { results: [], error: "no index \u2014 reindex first" };
|
|
31894
|
+
await ensureEngine(root);
|
|
31895
|
+
try {
|
|
31896
|
+
const { stdout } = await run3(
|
|
31897
|
+
py.bin,
|
|
31898
|
+
[join16(RAG_REL, "rag_engine.py"), "query", queryText, String(Math.min(Math.max(k, 1), 20))],
|
|
31899
|
+
{ cwd: root, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
|
|
31900
|
+
);
|
|
31901
|
+
const parsed = JSON.parse(stdout.trim() || '{"results":[]}');
|
|
31902
|
+
return { results: Array.isArray(parsed.results) ? parsed.results : [] };
|
|
31903
|
+
} catch (err) {
|
|
31904
|
+
return { results: [], error: err instanceof Error ? err.message : String(err) };
|
|
31905
|
+
}
|
|
31906
|
+
}
|
|
31907
|
+
async function ragInstall(root, model) {
|
|
31908
|
+
const py = await detectPython();
|
|
31909
|
+
if (!py.found) return { ok: false, started: false, error: "python3 not found on the worker" };
|
|
31910
|
+
if (existsSync7(join16(root, RAG_REL, ".installing"))) return { ok: true, started: true };
|
|
31911
|
+
try {
|
|
31912
|
+
await mkdir4(join16(root, RAG_REL), { recursive: true });
|
|
31913
|
+
} catch {
|
|
31914
|
+
}
|
|
31915
|
+
const script = `
|
|
31916
|
+
set +e
|
|
31917
|
+
touch "${RAG_REL}/.installing"
|
|
31918
|
+
rm -f "${RAG_REL}/.installed"
|
|
31919
|
+
{
|
|
31920
|
+
echo "[install] $(date) pip install fastembed sqlite-vec"
|
|
31921
|
+
"$RAG_PY" -m pip install --quiet --user fastembed sqlite-vec 2>&1
|
|
31922
|
+
echo "[install] warming model: $RAG_MODEL (first run downloads it)"
|
|
31923
|
+
"$RAG_PY" -c "from fastembed import TextEmbedding; TextEmbedding('$RAG_MODEL'); print('[install] model ready')" 2>&1
|
|
31924
|
+
if [ $? -eq 0 ]; then printf '%s' "$RAG_MODEL" > "${RAG_REL}/.installed"; echo "[install] DONE"; else echo "[install] FAILED"; fi
|
|
31925
|
+
} >> "${RAG_REL}/install.log" 2>&1
|
|
31926
|
+
rm -f "${RAG_REL}/.installing"
|
|
31927
|
+
`;
|
|
31928
|
+
const child = spawn5("bash", ["-c", script], {
|
|
31929
|
+
cwd: root,
|
|
31930
|
+
env: { ...process.env, RAG_PY: py.bin, RAG_MODEL: model },
|
|
31931
|
+
detached: true,
|
|
31932
|
+
stdio: "ignore"
|
|
31933
|
+
});
|
|
31934
|
+
child.unref();
|
|
31935
|
+
return { ok: true, started: true };
|
|
31936
|
+
}
|
|
31937
|
+
|
|
31938
|
+
// src/core/git-diff.ts
|
|
31939
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
31940
|
+
import { execFile as execFile4 } from "child_process";
|
|
31941
|
+
import { basename as basename3, dirname as dirname5, resolve as resolve5 } from "path";
|
|
31942
|
+
import { promisify as promisify4 } from "util";
|
|
31943
|
+
var run4 = promisify4(execFile4);
|
|
31340
31944
|
async function gitDiff(path) {
|
|
31341
|
-
const target =
|
|
31342
|
-
const dir2 =
|
|
31343
|
-
const base =
|
|
31945
|
+
const target = resolve5(path);
|
|
31946
|
+
const dir2 = dirname5(target);
|
|
31947
|
+
const base = basename3(target);
|
|
31344
31948
|
let newContent = "";
|
|
31345
31949
|
try {
|
|
31346
|
-
newContent = await
|
|
31950
|
+
newContent = await readFile7(target, "utf8");
|
|
31347
31951
|
} catch {
|
|
31348
31952
|
}
|
|
31349
31953
|
let oldContent = "";
|
|
31350
31954
|
try {
|
|
31351
|
-
const { stdout } = await
|
|
31955
|
+
const { stdout } = await run4("git", ["show", `HEAD:./${base}`], {
|
|
31352
31956
|
cwd: dir2,
|
|
31353
31957
|
timeout: 15e3,
|
|
31354
31958
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -31360,12 +31964,12 @@ async function gitDiff(path) {
|
|
|
31360
31964
|
}
|
|
31361
31965
|
|
|
31362
31966
|
// src/core/git-env.ts
|
|
31363
|
-
import { execFile as
|
|
31364
|
-
import { promisify as
|
|
31365
|
-
var
|
|
31967
|
+
import { execFile as execFile5 } from "child_process";
|
|
31968
|
+
import { promisify as promisify5 } from "util";
|
|
31969
|
+
var run5 = promisify5(execFile5);
|
|
31366
31970
|
async function hasBinary(bin, args = ["--version"]) {
|
|
31367
31971
|
try {
|
|
31368
|
-
await
|
|
31972
|
+
await run5(bin, args, { timeout: 5e3 });
|
|
31369
31973
|
return true;
|
|
31370
31974
|
} catch {
|
|
31371
31975
|
return false;
|
|
@@ -31378,7 +31982,7 @@ async function checkGitEnv(provider) {
|
|
|
31378
31982
|
let account = null;
|
|
31379
31983
|
if (installed) {
|
|
31380
31984
|
try {
|
|
31381
|
-
const { stdout, stderr } = await
|
|
31985
|
+
const { stdout, stderr } = await run5(bin, ["auth", "status"], { timeout: 8e3 });
|
|
31382
31986
|
const out = `${stdout}
|
|
31383
31987
|
${stderr}`;
|
|
31384
31988
|
authenticated = /logged in|logged into|✓/i.test(out);
|
|
@@ -31391,22 +31995,22 @@ ${stderr}`;
|
|
|
31391
31995
|
}
|
|
31392
31996
|
|
|
31393
31997
|
// src/core/git-history.ts
|
|
31394
|
-
import { readdir as
|
|
31395
|
-
import { execFile as
|
|
31396
|
-
import { resolve as
|
|
31397
|
-
import { promisify as
|
|
31398
|
-
var
|
|
31998
|
+
import { readdir as readdir5 } from "fs/promises";
|
|
31999
|
+
import { execFile as execFile6 } from "child_process";
|
|
32000
|
+
import { resolve as resolve6, sep as sep4 } from "path";
|
|
32001
|
+
import { promisify as promisify6 } from "util";
|
|
32002
|
+
var run6 = promisify6(execFile6);
|
|
31399
32003
|
var FS = "";
|
|
31400
32004
|
var RS = "";
|
|
31401
32005
|
function resolveRepo(root, repo) {
|
|
31402
32006
|
if (!root) return null;
|
|
31403
|
-
const base =
|
|
31404
|
-
const requested =
|
|
32007
|
+
const base = resolve6(root);
|
|
32008
|
+
const requested = resolve6(base, repo && repo.trim() ? repo : ".");
|
|
31405
32009
|
return requested === base || requested.startsWith(base + sep4) ? requested : base;
|
|
31406
32010
|
}
|
|
31407
32011
|
async function git(cwd2, args) {
|
|
31408
32012
|
try {
|
|
31409
|
-
const { stdout } = await
|
|
32013
|
+
const { stdout } = await run6("git", args, {
|
|
31410
32014
|
cwd: cwd2,
|
|
31411
32015
|
timeout: 15e3,
|
|
31412
32016
|
maxBuffer: 8 * 1024 * 1024
|
|
@@ -31418,7 +32022,7 @@ async function git(cwd2, args) {
|
|
|
31418
32022
|
}
|
|
31419
32023
|
async function gitRepos(root) {
|
|
31420
32024
|
if (!root) return { repos: [] };
|
|
31421
|
-
const base =
|
|
32025
|
+
const base = resolve6(root);
|
|
31422
32026
|
const found = /* @__PURE__ */ new Set();
|
|
31423
32027
|
const repos = [];
|
|
31424
32028
|
const add = async (dir2) => {
|
|
@@ -31432,16 +32036,16 @@ async function gitRepos(root) {
|
|
|
31432
32036
|
};
|
|
31433
32037
|
await add(base);
|
|
31434
32038
|
try {
|
|
31435
|
-
const lvl1 = await
|
|
32039
|
+
const lvl1 = await readdir5(base, { withFileTypes: true });
|
|
31436
32040
|
for (const d of lvl1) {
|
|
31437
32041
|
if (!d.isDirectory() || d.name === ".git" || d.name === "node_modules") continue;
|
|
31438
|
-
const p1 =
|
|
32042
|
+
const p1 = resolve6(base, d.name);
|
|
31439
32043
|
await add(p1);
|
|
31440
32044
|
try {
|
|
31441
|
-
const lvl2 = await
|
|
32045
|
+
const lvl2 = await readdir5(p1, { withFileTypes: true });
|
|
31442
32046
|
for (const d2 of lvl2) {
|
|
31443
32047
|
if (!d2.isDirectory() || d2.name === ".git" || d2.name === "node_modules") continue;
|
|
31444
|
-
await add(
|
|
32048
|
+
await add(resolve6(p1, d2.name));
|
|
31445
32049
|
}
|
|
31446
32050
|
} catch {
|
|
31447
32051
|
}
|
|
@@ -31508,24 +32112,24 @@ async function gitCommitDiff(root, repo, hash, path) {
|
|
|
31508
32112
|
}
|
|
31509
32113
|
|
|
31510
32114
|
// src/core/git-commit-identity.ts
|
|
31511
|
-
import { execFile as
|
|
31512
|
-
import { promisify as
|
|
31513
|
-
var
|
|
32115
|
+
import { execFile as execFile7 } from "child_process";
|
|
32116
|
+
import { promisify as promisify7 } from "util";
|
|
32117
|
+
var run7 = promisify7(execFile7);
|
|
31514
32118
|
async function setCommitAuthor(cwd2, machineUsername) {
|
|
31515
32119
|
if (!machineUsername) return;
|
|
31516
32120
|
try {
|
|
31517
|
-
await
|
|
32121
|
+
await run7("git", ["config", "--local", "user.name", machineUsername], { cwd: cwd2, timeout: 8e3 });
|
|
31518
32122
|
} catch {
|
|
31519
32123
|
}
|
|
31520
32124
|
}
|
|
31521
32125
|
|
|
31522
32126
|
// src/core/scaffold.ts
|
|
31523
|
-
import { cp, mkdir as
|
|
31524
|
-
import { execFile as
|
|
31525
|
-
import { dirname as
|
|
32127
|
+
import { cp, mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
32128
|
+
import { execFile as execFile8 } from "child_process";
|
|
32129
|
+
import { dirname as dirname6, join as join17, resolve as resolve7 } from "path";
|
|
31526
32130
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
31527
|
-
import { promisify as
|
|
31528
|
-
var
|
|
32131
|
+
import { promisify as promisify8 } from "util";
|
|
32132
|
+
var run8 = promisify8(execFile8);
|
|
31529
32133
|
function reposOf(spec) {
|
|
31530
32134
|
const repos = spec?.repos;
|
|
31531
32135
|
return Array.isArray(repos) ? repos : [];
|
|
@@ -31539,46 +32143,46 @@ async function provisionRepos(target, repos, emit, opts = {}) {
|
|
|
31539
32143
|
if (repo.primary) {
|
|
31540
32144
|
if (opts.clonePrimary && repo.op === "existing" && repo.url) {
|
|
31541
32145
|
emit("git", `Cloning ${repo.url}\u2026`);
|
|
31542
|
-
await
|
|
32146
|
+
await run8("git", ["clone", repo.url, target], { timeout: 12e4 });
|
|
31543
32147
|
continue;
|
|
31544
32148
|
}
|
|
31545
32149
|
emit("git", "Initializing the primary repo\u2026");
|
|
31546
|
-
await
|
|
32150
|
+
await run8("git", ["init"], { cwd: target, timeout: 2e4 });
|
|
31547
32151
|
if (repo.op === "existing" && repo.url) {
|
|
31548
|
-
await
|
|
32152
|
+
await run8("git", ["remote", "add", "origin", repo.url], { cwd: target, timeout: 2e4 }).catch(
|
|
31549
32153
|
() => void 0
|
|
31550
32154
|
);
|
|
31551
32155
|
}
|
|
31552
32156
|
continue;
|
|
31553
32157
|
}
|
|
31554
|
-
const dir2 =
|
|
32158
|
+
const dir2 = join17(target, subDirName(repo));
|
|
31555
32159
|
if (repo.op === "existing" && repo.url) {
|
|
31556
32160
|
emit("git", `Cloning sub-repo ${repo.url}\u2026`);
|
|
31557
|
-
await
|
|
32161
|
+
await run8("git", ["clone", repo.url, dir2], { timeout: 12e4 });
|
|
31558
32162
|
} else {
|
|
31559
32163
|
emit("git", `Initializing sub-repo ${subDirName(repo)}\u2026`);
|
|
31560
|
-
await
|
|
31561
|
-
await
|
|
32164
|
+
await mkdir5(dir2, { recursive: true });
|
|
32165
|
+
await run8("git", ["init"], { cwd: dir2, timeout: 2e4 });
|
|
31562
32166
|
}
|
|
31563
32167
|
}
|
|
31564
32168
|
}
|
|
31565
32169
|
function sampleDir() {
|
|
31566
32170
|
if (process.env.SCAFFOLD_SAMPLE_DIR) return process.env.SCAFFOLD_SAMPLE_DIR;
|
|
31567
|
-
const here =
|
|
31568
|
-
return
|
|
32171
|
+
const here = dirname6(fileURLToPath3(import.meta.url));
|
|
32172
|
+
return resolve7(here, "../../project-sample");
|
|
31569
32173
|
}
|
|
31570
32174
|
async function scaffoldProject(payload, profileDir2, onProgress) {
|
|
31571
32175
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
31572
32176
|
try {
|
|
31573
|
-
const root = process.env.SCAFFOLD_ROOT ?
|
|
31574
|
-
const target =
|
|
31575
|
-
await
|
|
32177
|
+
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
32178
|
+
const target = join17(root, payload.projectName);
|
|
32179
|
+
await mkdir5(target, { recursive: true });
|
|
31576
32180
|
emit("copy", "Copying the sample template\u2026");
|
|
31577
32181
|
await cp(sampleDir(), target, { recursive: true, force: false, errorOnExist: false });
|
|
31578
32182
|
if (payload.spec) {
|
|
31579
32183
|
emit("spec", "Writing project.spec.json\u2026");
|
|
31580
|
-
await
|
|
31581
|
-
|
|
32184
|
+
await writeFile6(
|
|
32185
|
+
join17(target, "project.spec.json"),
|
|
31582
32186
|
JSON.stringify(payload.spec, null, 2),
|
|
31583
32187
|
"utf8"
|
|
31584
32188
|
);
|
|
@@ -31596,7 +32200,7 @@ async function aiInit(target, spec, emit) {
|
|
|
31596
32200
|
emit("ai-init", "AI init: subagents, README, CLAUDE.md\u2026");
|
|
31597
32201
|
const subagents = Array.isArray(spec.subagents) ? spec.subagents : [];
|
|
31598
32202
|
if (subagents.length > 0) {
|
|
31599
|
-
await
|
|
32203
|
+
await mkdir5(join17(target, ".claude", "agents"), { recursive: true });
|
|
31600
32204
|
for (const sa of subagents) {
|
|
31601
32205
|
const name = (sa.name || "").trim().replace(/[^\w.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
31602
32206
|
if (!name) continue;
|
|
@@ -31606,17 +32210,17 @@ name: ${name}
|
|
|
31606
32210
|
|
|
31607
32211
|
${(sa.description || "").trim()}
|
|
31608
32212
|
`;
|
|
31609
|
-
await
|
|
32213
|
+
await writeFile6(join17(target, ".claude", "agents", `${name}.md`), body, "utf8");
|
|
31610
32214
|
}
|
|
31611
32215
|
}
|
|
31612
32216
|
const specJson = JSON.stringify(spec);
|
|
31613
32217
|
await generateFile(
|
|
31614
|
-
|
|
32218
|
+
join17(target, "README.md"),
|
|
31615
32219
|
`Write a concise README.md (Markdown only, no preamble) for this project from its spec JSON:
|
|
31616
32220
|
${specJson}`
|
|
31617
32221
|
);
|
|
31618
32222
|
await generateFile(
|
|
31619
|
-
|
|
32223
|
+
join17(target, "CLAUDE.md"),
|
|
31620
32224
|
`Write a CLAUDE.md (Markdown only, no preamble) with guidance/conventions for AI agents working in this project, derived from its spec JSON:
|
|
31621
32225
|
${specJson}`
|
|
31622
32226
|
);
|
|
@@ -31624,16 +32228,16 @@ ${specJson}`
|
|
|
31624
32228
|
async function generateFile(path, prompt) {
|
|
31625
32229
|
try {
|
|
31626
32230
|
const text = await aiGenerate(prompt);
|
|
31627
|
-
if (text.trim()) await
|
|
32231
|
+
if (text.trim()) await writeFile6(path, text.trim() + "\n", "utf8");
|
|
31628
32232
|
} catch {
|
|
31629
32233
|
}
|
|
31630
32234
|
}
|
|
31631
32235
|
async function addProject(payload, profileDir2, onProgress) {
|
|
31632
32236
|
const emit = (step, message) => onProgress?.({ projectId: payload.projectId, step, message });
|
|
31633
32237
|
try {
|
|
31634
|
-
const root = process.env.SCAFFOLD_ROOT ?
|
|
31635
|
-
const target =
|
|
31636
|
-
await
|
|
32238
|
+
const root = process.env.SCAFFOLD_ROOT ? resolve7(process.env.SCAFFOLD_ROOT) : resolve7(profileDir2);
|
|
32239
|
+
const target = join17(root, payload.projectName);
|
|
32240
|
+
await mkdir5(target, { recursive: true });
|
|
31637
32241
|
const repos = payload.repos ?? [];
|
|
31638
32242
|
if (repos.length > 0) await provisionRepos(target, repos, emit, { clonePrimary: true });
|
|
31639
32243
|
onProgress?.({ projectId: payload.projectId, step: "done", message: "Project added.", done: true });
|
|
@@ -31644,12 +32248,12 @@ async function addProject(payload, profileDir2, onProgress) {
|
|
|
31644
32248
|
}
|
|
31645
32249
|
|
|
31646
32250
|
// src/core/config-sync.ts
|
|
31647
|
-
import { existsSync as
|
|
31648
|
-
import { join as
|
|
32251
|
+
import { existsSync as existsSync8, readFileSync as readFileSync13 } from "fs";
|
|
32252
|
+
import { join as join18 } from "path";
|
|
31649
32253
|
var SERVER_MANAGED_KEYS = ["physicPath", "sessionSwitchPct", "perPromptTokenLimit"];
|
|
31650
32254
|
function readConfigText(profileDir2) {
|
|
31651
|
-
const path =
|
|
31652
|
-
if (
|
|
32255
|
+
const path = join18(profileDir2, "config.json");
|
|
32256
|
+
if (existsSync8(path)) {
|
|
31653
32257
|
try {
|
|
31654
32258
|
return readFileSync13(path, "utf8");
|
|
31655
32259
|
} catch {
|
|
@@ -31713,7 +32317,7 @@ var WsClient = class {
|
|
|
31713
32317
|
context.bus.onTranscriptUpdate(
|
|
31714
32318
|
(entry) => this.emitConsole({ kind: "update", entry: toDtoEntry(entry) })
|
|
31715
32319
|
);
|
|
31716
|
-
context.bus.onClear(() => this.
|
|
32320
|
+
context.bus.onClear(() => this.emitConsoleClear());
|
|
31717
32321
|
}
|
|
31718
32322
|
context;
|
|
31719
32323
|
socket = null;
|
|
@@ -31737,6 +32341,13 @@ var WsClient = class {
|
|
|
31737
32341
|
wakeReconnect = null;
|
|
31738
32342
|
/** When set, the next backoff sleep returns immediately (forced reconnect). */
|
|
31739
32343
|
forceReconnect = false;
|
|
32344
|
+
/**
|
|
32345
|
+
* The single "reconnecting…" transcript line for the current outage (approach B): created on
|
|
32346
|
+
* the first failed attempt and updated in place each retry, then finalized to the "connected"
|
|
32347
|
+
* line on the next hello_ack — so one outage adds one line, not an error + a wait line per
|
|
32348
|
+
* attempt. Null while connected / between outages.
|
|
32349
|
+
*/
|
|
32350
|
+
reconnectEntryId = null;
|
|
31740
32351
|
/** Org-configured reconnect cap (seconds) from the last ws_token — overrides env. */
|
|
31741
32352
|
orgReconnectMaxSec = null;
|
|
31742
32353
|
/** Env override for the reconnect cap (seconds); resolved once at construction. */
|
|
@@ -31805,14 +32416,14 @@ var WsClient = class {
|
|
|
31805
32416
|
*/
|
|
31806
32417
|
awaitConnected(timeoutMs) {
|
|
31807
32418
|
if (this.sessionKey && this.bus.status === "connected") return Promise.resolve(true);
|
|
31808
|
-
return new Promise((
|
|
32419
|
+
return new Promise((resolve8) => {
|
|
31809
32420
|
const waiter = () => {
|
|
31810
32421
|
clearTimeout(timer);
|
|
31811
|
-
|
|
32422
|
+
resolve8(true);
|
|
31812
32423
|
};
|
|
31813
32424
|
const timer = setTimeout(() => {
|
|
31814
32425
|
this.connectWaiters = this.connectWaiters.filter((w) => w !== waiter);
|
|
31815
|
-
|
|
32426
|
+
resolve8(false);
|
|
31816
32427
|
}, timeoutMs);
|
|
31817
32428
|
this.connectWaiters.push(waiter);
|
|
31818
32429
|
});
|
|
@@ -31839,16 +32450,16 @@ var WsClient = class {
|
|
|
31839
32450
|
this.forceReconnect = false;
|
|
31840
32451
|
return Promise.resolve();
|
|
31841
32452
|
}
|
|
31842
|
-
return new Promise((
|
|
32453
|
+
return new Promise((resolve8) => {
|
|
31843
32454
|
const timer = setTimeout(() => {
|
|
31844
32455
|
this.wakeReconnect = null;
|
|
31845
|
-
|
|
32456
|
+
resolve8();
|
|
31846
32457
|
}, ms);
|
|
31847
32458
|
this.wakeReconnect = () => {
|
|
31848
32459
|
clearTimeout(timer);
|
|
31849
32460
|
this.wakeReconnect = null;
|
|
31850
32461
|
this.forceReconnect = false;
|
|
31851
|
-
|
|
32462
|
+
resolve8();
|
|
31852
32463
|
};
|
|
31853
32464
|
});
|
|
31854
32465
|
}
|
|
@@ -31858,6 +32469,7 @@ var WsClient = class {
|
|
|
31858
32469
|
async run() {
|
|
31859
32470
|
let hashcodeInvalidStreak = 0;
|
|
31860
32471
|
for (let attempt = 0; !this.stopped; attempt++) {
|
|
32472
|
+
let reconnectDetail = "Disconnected";
|
|
31861
32473
|
try {
|
|
31862
32474
|
await this.connectOnce();
|
|
31863
32475
|
if (this.sessionEstablished) {
|
|
@@ -31875,17 +32487,14 @@ var WsClient = class {
|
|
|
31875
32487
|
this.logout(err.errorCode);
|
|
31876
32488
|
return;
|
|
31877
32489
|
}
|
|
31878
|
-
|
|
31879
|
-
`Token request rejected (HASHCODE_INVALID) \u2014 retrying (${hashcodeInvalidStreak}/${MAX_HASHCODE_INVALID_RETRIES}).`,
|
|
31880
|
-
"warn"
|
|
31881
|
-
);
|
|
32490
|
+
reconnectDetail = `Token rejected (HASHCODE_INVALID ${hashcodeInvalidStreak}/${MAX_HASHCODE_INVALID_RETRIES})`;
|
|
31882
32491
|
} else if (err.errorCode === "USER_PAUSED") {
|
|
31883
|
-
|
|
32492
|
+
reconnectDetail = "This account is paused \u2014 retrying until it is resumed";
|
|
31884
32493
|
} else {
|
|
31885
|
-
|
|
32494
|
+
reconnectDetail = `Connection error: ${err}`;
|
|
31886
32495
|
}
|
|
31887
32496
|
} else {
|
|
31888
|
-
|
|
32497
|
+
reconnectDetail = `Connection error: ${err}`;
|
|
31889
32498
|
}
|
|
31890
32499
|
}
|
|
31891
32500
|
if (this.stopped) return;
|
|
@@ -31896,7 +32505,7 @@ var WsClient = class {
|
|
|
31896
32505
|
);
|
|
31897
32506
|
logger.info("ws.reconnect", { attempt, waitMs: wait });
|
|
31898
32507
|
this.bus.setStatus("reconnecting");
|
|
31899
|
-
this.
|
|
32508
|
+
this.showReconnectStatus(reconnectDetail, Math.round(wait / 1e3), attempt + 1);
|
|
31900
32509
|
await this.interruptibleSleep(wait);
|
|
31901
32510
|
}
|
|
31902
32511
|
}
|
|
@@ -31904,7 +32513,7 @@ var WsClient = class {
|
|
|
31904
32513
|
* One WS session: token → connect → handshake → handle messages until close.
|
|
31905
32514
|
*/
|
|
31906
32515
|
connectOnce() {
|
|
31907
|
-
return new Promise((
|
|
32516
|
+
return new Promise((resolve8, reject) => {
|
|
31908
32517
|
void (async () => {
|
|
31909
32518
|
this.sessionEstablished = false;
|
|
31910
32519
|
this.bus.setStatus("connecting");
|
|
@@ -31973,7 +32582,7 @@ var WsClient = class {
|
|
|
31973
32582
|
if (code === CLOSE_DRAINING) {
|
|
31974
32583
|
this.bus.log("Server draining \u2014 will reconnect (backoff + jitter).");
|
|
31975
32584
|
}
|
|
31976
|
-
|
|
32585
|
+
resolve8();
|
|
31977
32586
|
});
|
|
31978
32587
|
socket.on("error", (err) => {
|
|
31979
32588
|
this.clearHeartbeat();
|
|
@@ -32002,7 +32611,7 @@ var WsClient = class {
|
|
|
32002
32611
|
logger.info("ws.connected", { version: CLI_VERSION });
|
|
32003
32612
|
this.stopConsoleSync();
|
|
32004
32613
|
this.bus.setStatus("connected");
|
|
32005
|
-
this.
|
|
32614
|
+
this.finishReconnect();
|
|
32006
32615
|
this.sendMachineStatus();
|
|
32007
32616
|
this.flushOfflineBuffer();
|
|
32008
32617
|
this.startHeartbeat();
|
|
@@ -32069,8 +32678,8 @@ var WsClient = class {
|
|
|
32069
32678
|
const oldRoot = this.physicFolderPath(sync.oldName);
|
|
32070
32679
|
if (oldRoot) void uninstallCron(oldRoot).catch(() => void 0);
|
|
32071
32680
|
}
|
|
32072
|
-
if (sync.oldName) rmSync4(
|
|
32073
|
-
mkdirSync4(
|
|
32681
|
+
if (sync.oldName) rmSync4(join19(this.context.profileDir, sync.oldName), { recursive: true, force: true });
|
|
32682
|
+
mkdirSync4(join19(this.context.profileDir, sync.newName), { recursive: true });
|
|
32074
32683
|
this.physicRoot = this.physicFolderPath(sync.newName);
|
|
32075
32684
|
this.bus.setProject(sync.newName);
|
|
32076
32685
|
this.bus.log(`Physic project folder synced \u2192 ${sync.newName}`);
|
|
@@ -32081,7 +32690,7 @@ var WsClient = class {
|
|
|
32081
32690
|
if (del.name) {
|
|
32082
32691
|
const delRoot = this.physicFolderPath(del.name);
|
|
32083
32692
|
if (delRoot) void uninstallCron(delRoot).catch(() => void 0);
|
|
32084
|
-
rmSync4(
|
|
32693
|
+
rmSync4(join19(this.context.profileDir, del.name), { recursive: true, force: true });
|
|
32085
32694
|
this.bus.log(`Physic project folder deleted (project removed) \u2192 ${del.name}`);
|
|
32086
32695
|
}
|
|
32087
32696
|
this.bus.setProject(null);
|
|
@@ -32178,6 +32787,88 @@ var WsClient = class {
|
|
|
32178
32787
|
}
|
|
32179
32788
|
break;
|
|
32180
32789
|
}
|
|
32790
|
+
case WsChannels.AGENTS_LIST:
|
|
32791
|
+
if (this.physicRoot) {
|
|
32792
|
+
void listAgents(this.physicRoot).then(
|
|
32793
|
+
(reply) => this.send(WsChannels.AGENTS_LIST, reply, message.id)
|
|
32794
|
+
);
|
|
32795
|
+
}
|
|
32796
|
+
break;
|
|
32797
|
+
case WsChannels.AGENTS_READ: {
|
|
32798
|
+
const req = payload;
|
|
32799
|
+
if (this.physicRoot) {
|
|
32800
|
+
void readAgent(this.physicRoot, req.kind, req.name).then(
|
|
32801
|
+
(reply) => this.send(WsChannels.AGENTS_READ, reply, message.id)
|
|
32802
|
+
);
|
|
32803
|
+
}
|
|
32804
|
+
break;
|
|
32805
|
+
}
|
|
32806
|
+
case WsChannels.AGENTS_WRITE: {
|
|
32807
|
+
const req = payload;
|
|
32808
|
+
if (this.physicRoot) {
|
|
32809
|
+
void writeAgent(this.physicRoot, req).then(
|
|
32810
|
+
(reply) => this.send(WsChannels.AGENTS_WRITE, reply, message.id)
|
|
32811
|
+
);
|
|
32812
|
+
}
|
|
32813
|
+
break;
|
|
32814
|
+
}
|
|
32815
|
+
case WsChannels.SECRETS_READ:
|
|
32816
|
+
if (this.physicRoot) {
|
|
32817
|
+
void readSecrets(this.physicRoot).then(
|
|
32818
|
+
(reply) => this.send(WsChannels.SECRETS_READ, reply, message.id)
|
|
32819
|
+
);
|
|
32820
|
+
}
|
|
32821
|
+
break;
|
|
32822
|
+
case WsChannels.SECRETS_WRITE: {
|
|
32823
|
+
const req = payload;
|
|
32824
|
+
if (this.physicRoot) {
|
|
32825
|
+
void writeSecrets(this.physicRoot, req).then(
|
|
32826
|
+
(reply) => this.send(WsChannels.SECRETS_WRITE, reply, message.id)
|
|
32827
|
+
);
|
|
32828
|
+
}
|
|
32829
|
+
break;
|
|
32830
|
+
}
|
|
32831
|
+
case WsChannels.GRAPH_BUILD: {
|
|
32832
|
+
const req = payload;
|
|
32833
|
+
if (this.physicRoot) {
|
|
32834
|
+
void buildGraph(this.physicRoot, req.mode).then(
|
|
32835
|
+
(reply) => this.send(WsChannels.GRAPH_BUILD, reply, message.id)
|
|
32836
|
+
);
|
|
32837
|
+
}
|
|
32838
|
+
break;
|
|
32839
|
+
}
|
|
32840
|
+
case WsChannels.RAG_STATUS:
|
|
32841
|
+
if (this.physicRoot) {
|
|
32842
|
+
void ragStatus(this.physicRoot).then(
|
|
32843
|
+
(reply) => this.send(WsChannels.RAG_STATUS, reply, message.id)
|
|
32844
|
+
);
|
|
32845
|
+
}
|
|
32846
|
+
break;
|
|
32847
|
+
case WsChannels.RAG_INSTALL: {
|
|
32848
|
+
const req = payload;
|
|
32849
|
+
if (this.physicRoot) {
|
|
32850
|
+
void ragInstall(this.physicRoot, req.model).then(
|
|
32851
|
+
(reply) => this.send(WsChannels.RAG_INSTALL, reply, message.id)
|
|
32852
|
+
);
|
|
32853
|
+
}
|
|
32854
|
+
break;
|
|
32855
|
+
}
|
|
32856
|
+
case WsChannels.RAG_REINDEX:
|
|
32857
|
+
if (this.physicRoot) {
|
|
32858
|
+
void ragReindex(this.physicRoot).then(
|
|
32859
|
+
(reply) => this.send(WsChannels.RAG_REINDEX, reply, message.id)
|
|
32860
|
+
);
|
|
32861
|
+
}
|
|
32862
|
+
break;
|
|
32863
|
+
case WsChannels.RAG_QUERY: {
|
|
32864
|
+
const req = payload;
|
|
32865
|
+
if (this.physicRoot) {
|
|
32866
|
+
void ragQuery(this.physicRoot, req.query, req.k).then(
|
|
32867
|
+
(reply) => this.send(WsChannels.RAG_QUERY, reply, message.id)
|
|
32868
|
+
);
|
|
32869
|
+
}
|
|
32870
|
+
break;
|
|
32871
|
+
}
|
|
32181
32872
|
case WsChannels.CONFIG_READ:
|
|
32182
32873
|
this.send(
|
|
32183
32874
|
WsChannels.CONFIG_READ,
|
|
@@ -32231,7 +32922,7 @@ var WsClient = class {
|
|
|
32231
32922
|
}
|
|
32232
32923
|
case WsChannels.LOG_READ: {
|
|
32233
32924
|
const limit = Math.min(Math.max(payload.limit ?? 200, 1), 2e3);
|
|
32234
|
-
const lines = readRecentLogLines(
|
|
32925
|
+
const lines = readRecentLogLines(join19(this.context.profileDir, "logs"), limit);
|
|
32235
32926
|
this.send(WsChannels.LOG_READ, { lines }, message.id);
|
|
32236
32927
|
break;
|
|
32237
32928
|
}
|
|
@@ -32571,6 +33262,46 @@ var WsClient = class {
|
|
|
32571
33262
|
if (!this.consoleWatching) return;
|
|
32572
33263
|
this.send(WsChannels.CONSOLE_SYNC, { ...ev, rev: ++this.consoleRev });
|
|
32573
33264
|
}
|
|
33265
|
+
/**
|
|
33266
|
+
* A `/clear` (or idle auto-clear) wiped the transcript (ADR-0150). Emit it as an absolute empty
|
|
33267
|
+
* **snapshot** rather than an incremental `clear` event: a snapshot is applied unconditionally on
|
|
33268
|
+
* the web (no `rev`-gap check), so the clear can't be silently dropped when the web's revision
|
|
33269
|
+
* drifted during a reconnect flap. No-op while unwatched — the next `console.watch` re-arm
|
|
33270
|
+
* snapshots the (now empty) buffer anyway. The history is already emptied before this fires, so
|
|
33271
|
+
* the snapshot carries no entries.
|
|
33272
|
+
*/
|
|
33273
|
+
emitConsoleClear() {
|
|
33274
|
+
if (!this.consoleWatching) return;
|
|
33275
|
+
this.sendConsoleSnapshot();
|
|
33276
|
+
}
|
|
33277
|
+
/**
|
|
33278
|
+
* Show the reconnect-backoff status as a SINGLE transcript line for the current outage
|
|
33279
|
+
* (approach B): create it on the first failed attempt, then update it in place on every retry —
|
|
33280
|
+
* so an outage adds one line instead of an error + a "reconnecting in Ns" line per attempt
|
|
33281
|
+
* (which flooded the TUI and, on the next snapshot, the web Console).
|
|
33282
|
+
*/
|
|
33283
|
+
showReconnectStatus(detail, waitSec, attempt) {
|
|
33284
|
+
const text = `\u27F3 ${detail} \u2014 reconnecting in ${waitSec}s (attempt ${attempt}, /reconnect to retry now)`;
|
|
33285
|
+
if (this.reconnectEntryId) {
|
|
33286
|
+
this.bus.updateEntry(this.reconnectEntryId, text, "warn");
|
|
33287
|
+
} else {
|
|
33288
|
+
this.reconnectEntryId = this.bus.push({ source: "system", kind: "log", text, level: "warn" });
|
|
33289
|
+
}
|
|
33290
|
+
}
|
|
33291
|
+
/**
|
|
33292
|
+
* A session became ready (hello_ack): finalize any in-flight reconnect line in place to the
|
|
33293
|
+
* "connected" line (so the outage's single line resolves cleanly), or — on a first connect with
|
|
33294
|
+
* no prior outage — push a fresh connected line.
|
|
33295
|
+
*/
|
|
33296
|
+
finishReconnect() {
|
|
33297
|
+
const msg = "\u2714 Connected to the server (session encryption ready).";
|
|
33298
|
+
if (this.reconnectEntryId) {
|
|
33299
|
+
this.bus.updateEntry(this.reconnectEntryId, msg, "info");
|
|
33300
|
+
this.reconnectEntryId = null;
|
|
33301
|
+
} else {
|
|
33302
|
+
this.bus.log(msg);
|
|
33303
|
+
}
|
|
33304
|
+
}
|
|
32574
33305
|
/** Send a full transcript snapshot (on attach + the periodic self-heal — ADR-0150). */
|
|
32575
33306
|
sendConsoleSnapshot() {
|
|
32576
33307
|
const entries2 = this.bus.snapshot().map(toDtoEntry);
|
|
@@ -32620,7 +33351,7 @@ var WsClient = class {
|
|
|
32620
33351
|
* Used for quota.check before spawning an AI cli (ADR-0020).
|
|
32621
33352
|
*/
|
|
32622
33353
|
request(channel, data) {
|
|
32623
|
-
return new Promise((
|
|
33354
|
+
return new Promise((resolve8, reject) => {
|
|
32624
33355
|
if (!this.socket || !this.sessionKey) {
|
|
32625
33356
|
reject(new Error("Not connected to the server."));
|
|
32626
33357
|
return;
|
|
@@ -32640,7 +33371,7 @@ var WsClient = class {
|
|
|
32640
33371
|
}, 15e3);
|
|
32641
33372
|
timer.unref?.();
|
|
32642
33373
|
this.pending.set(envelope.id, {
|
|
32643
|
-
resolve: (value) =>
|
|
33374
|
+
resolve: (value) => resolve8(value),
|
|
32644
33375
|
timer
|
|
32645
33376
|
});
|
|
32646
33377
|
this.socket.send(JSON.stringify(envelope));
|
|
@@ -32687,7 +33418,7 @@ var WsClient = class {
|
|
|
32687
33418
|
/** Create the physic folder at an absolute path if missing (best-effort). */
|
|
32688
33419
|
ensurePhysicFolderPath(folder) {
|
|
32689
33420
|
try {
|
|
32690
|
-
if (!
|
|
33421
|
+
if (!existsSync9(folder)) {
|
|
32691
33422
|
mkdirSync4(folder, { recursive: true });
|
|
32692
33423
|
this.bus.log(`Created physic project folder: ${folder}`);
|
|
32693
33424
|
}
|
|
@@ -32702,7 +33433,7 @@ var WsClient = class {
|
|
|
32702
33433
|
*/
|
|
32703
33434
|
physicFolderPath(projectName) {
|
|
32704
33435
|
const safe = projectName.replace(/[/\\]/g, "_").replace(/\.\./g, "_").trim();
|
|
32705
|
-
return safe ?
|
|
33436
|
+
return safe ? join19(this.context.profileDir, safe) : null;
|
|
32706
33437
|
}
|
|
32707
33438
|
/**
|
|
32708
33439
|
* Report machine.status: version + the state of the physic project being served
|
|
@@ -32716,7 +33447,7 @@ var WsClient = class {
|
|
|
32716
33447
|
projects: [],
|
|
32717
33448
|
version: CLI_VERSION,
|
|
32718
33449
|
physicPath,
|
|
32719
|
-
physicPathExists: physicPath ?
|
|
33450
|
+
physicPathExists: physicPath ? existsSync9(physicPath) : void 0,
|
|
32720
33451
|
// Real coarse state (ADR-0152): !paused and a recent tick (was hard-coded false).
|
|
32721
33452
|
autonomousRunning: physicPath ? isAutonomousRunning(physicPath) : false
|
|
32722
33453
|
});
|
|
@@ -32751,7 +33482,7 @@ var WsClient = class {
|
|
|
32751
33482
|
const working = getWorkingProfile(this.context.profileDir, aiCli2);
|
|
32752
33483
|
const configured = claudeHomeDirs(config);
|
|
32753
33484
|
const dirs = working ? [working, ...configured.filter((d) => d !== working)] : configured;
|
|
32754
|
-
const profile = working ?
|
|
33485
|
+
const profile = working ? basename4(working) : dirs[0] ? basename4(dirs[0]) : "default";
|
|
32755
33486
|
const api = await checkClaudeUsage(dirs, aiCli2);
|
|
32756
33487
|
const snapshot = api ?? {
|
|
32757
33488
|
plan: "?",
|
|
@@ -32788,7 +33519,7 @@ var WsClient = class {
|
|
|
32788
33519
|
*/
|
|
32789
33520
|
uploadLog() {
|
|
32790
33521
|
try {
|
|
32791
|
-
const upload = readLogUpload(
|
|
33522
|
+
const upload = readLogUpload(join19(this.context.profileDir, "logs"));
|
|
32792
33523
|
if (!upload) return;
|
|
32793
33524
|
const sig = `${upload.fileName}:${upload.totalBytes}`;
|
|
32794
33525
|
if (sig === this.lastLogSig) return;
|
|
@@ -32817,9 +33548,9 @@ var WsClient = class {
|
|
|
32817
33548
|
|
|
32818
33549
|
// src/core/instance-lock.ts
|
|
32819
33550
|
import { readFileSync as readFileSync14, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
32820
|
-
import { join as
|
|
33551
|
+
import { join as join20 } from "path";
|
|
32821
33552
|
function lockPath(profileDir2) {
|
|
32822
|
-
return
|
|
33553
|
+
return join20(profileDir2, "start.lock");
|
|
32823
33554
|
}
|
|
32824
33555
|
function isAlive(pid) {
|
|
32825
33556
|
try {
|
|
@@ -32912,14 +33643,15 @@ var SessionBus = class {
|
|
|
32912
33643
|
return [...this.history];
|
|
32913
33644
|
}
|
|
32914
33645
|
/**
|
|
32915
|
-
* Producer → consumers: replace the text of an already-pushed entry
|
|
32916
|
-
* grow a streaming `result` block in place
|
|
32917
|
-
*
|
|
33646
|
+
* Producer → consumers: replace the text (and optionally the level) of an already-pushed entry
|
|
33647
|
+
* (ADR-0108) — used to grow a streaming `result` block in place, or to update a single reconnect
|
|
33648
|
+
* status line without pushing a new line per attempt. No-op if the entry has aged out.
|
|
32918
33649
|
*/
|
|
32919
|
-
updateEntry(id, text) {
|
|
33650
|
+
updateEntry(id, text, level) {
|
|
32920
33651
|
const entry = this.history.find((e) => e.id === id);
|
|
32921
33652
|
if (!entry) return;
|
|
32922
33653
|
entry.text = text;
|
|
33654
|
+
if (level) entry.level = level;
|
|
32923
33655
|
this.emitter.emit("transcript-update", entry);
|
|
32924
33656
|
}
|
|
32925
33657
|
/** Subscribe to in-place entry updates (a `result` block growing — ADR-0108). */
|
|
@@ -36128,7 +36860,7 @@ var styles_default = styles2;
|
|
|
36128
36860
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/reconciler.js
|
|
36129
36861
|
if (process3.env["DEV"] === "true") {
|
|
36130
36862
|
try {
|
|
36131
|
-
await import("./devtools-
|
|
36863
|
+
await import("./devtools-ZL4ACXHE.js");
|
|
36132
36864
|
} catch (error) {
|
|
36133
36865
|
if (error.code === "ERR_MODULE_NOT_FOUND") {
|
|
36134
36866
|
console.warn(`
|
|
@@ -38346,8 +39078,8 @@ var Ink = class {
|
|
|
38346
39078
|
}
|
|
38347
39079
|
}
|
|
38348
39080
|
async waitUntilExit() {
|
|
38349
|
-
this.exitPromise ||= new Promise((
|
|
38350
|
-
this.resolveExitPromise =
|
|
39081
|
+
this.exitPromise ||= new Promise((resolve8, reject) => {
|
|
39082
|
+
this.resolveExitPromise = resolve8;
|
|
38351
39083
|
this.rejectExitPromise = reject;
|
|
38352
39084
|
});
|
|
38353
39085
|
return this.exitPromise;
|
|
@@ -38714,15 +39446,15 @@ var import_react21 = __toESM(require_react(), 1);
|
|
|
38714
39446
|
var import_react26 = __toESM(require_react(), 1);
|
|
38715
39447
|
|
|
38716
39448
|
// src/core/input-history.ts
|
|
38717
|
-
import { existsSync as
|
|
38718
|
-
import { join as
|
|
39449
|
+
import { existsSync as existsSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
|
|
39450
|
+
import { join as join21 } from "path";
|
|
38719
39451
|
var MAX_HISTORY = 200;
|
|
38720
39452
|
function historyPath(profileDir2) {
|
|
38721
|
-
return
|
|
39453
|
+
return join21(profileDir2, "input-history.json");
|
|
38722
39454
|
}
|
|
38723
39455
|
function loadInputHistory(profileDir2) {
|
|
38724
39456
|
const path = historyPath(profileDir2);
|
|
38725
|
-
if (!
|
|
39457
|
+
if (!existsSync11(path)) return [];
|
|
38726
39458
|
try {
|
|
38727
39459
|
const data = JSON.parse(readFileSync16(path, "utf8"));
|
|
38728
39460
|
return Array.isArray(data) ? data.filter((x) => typeof x === "string") : [];
|
|
@@ -38984,8 +39716,8 @@ function TranscriptLine({ line }) {
|
|
|
38984
39716
|
var import_react24 = __toESM(require_react(), 1);
|
|
38985
39717
|
|
|
38986
39718
|
// src/ui/slash-commands.ts
|
|
38987
|
-
import { existsSync as
|
|
38988
|
-
import { join as
|
|
39719
|
+
import { existsSync as existsSync12 } from "fs";
|
|
39720
|
+
import { join as join22 } from "path";
|
|
38989
39721
|
var CONFIG_KEYS = [
|
|
38990
39722
|
"aiCli",
|
|
38991
39723
|
"claudeHome",
|
|
@@ -38997,7 +39729,7 @@ var CONFIG_KEYS = [
|
|
|
38997
39729
|
"aiEnv"
|
|
38998
39730
|
];
|
|
38999
39731
|
function configPath(profileDir2) {
|
|
39000
|
-
return
|
|
39732
|
+
return join22(profileDir2, "config.json");
|
|
39001
39733
|
}
|
|
39002
39734
|
function parseConfigAssignment(key, valueTokens) {
|
|
39003
39735
|
const value = valueTokens.join(" ").trim();
|
|
@@ -39047,7 +39779,7 @@ function runConfig(ctx) {
|
|
|
39047
39779
|
ctx.print(`\u2714 ${verb} default config: ${path}`);
|
|
39048
39780
|
ctx.print("add claude profiles: /config set claudeHome .claude .claude-1 (tried in order)");
|
|
39049
39781
|
};
|
|
39050
|
-
if (
|
|
39782
|
+
if (existsSync12(path)) {
|
|
39051
39783
|
ctx.confirm(
|
|
39052
39784
|
`config exists: ${path} \u2014 replace it with defaults? (y/N)`,
|
|
39053
39785
|
() => writeDefaults("replaced with")
|
|
@@ -39132,7 +39864,7 @@ function runOutput(ctx) {
|
|
|
39132
39864
|
}
|
|
39133
39865
|
function runLogs(ctx) {
|
|
39134
39866
|
const n = Math.max(1, Math.min(200, Number(ctx.args[0]) || 20));
|
|
39135
|
-
const dir2 =
|
|
39867
|
+
const dir2 = join22(ctx.info.profileDir, "logs");
|
|
39136
39868
|
const lines = readRecentLogLines(dir2, n);
|
|
39137
39869
|
if (lines.length === 0) {
|
|
39138
39870
|
ctx.print("No log lines yet.");
|
|
@@ -39633,7 +40365,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
39633
40365
|
return;
|
|
39634
40366
|
}
|
|
39635
40367
|
logger.configure({
|
|
39636
|
-
dir:
|
|
40368
|
+
dir: join23(profileDir2, "logs"),
|
|
39637
40369
|
level: resolveLogLevel(),
|
|
39638
40370
|
base: { version: CLI_VERSION, profile: profileName, scope: credential.scope }
|
|
39639
40371
|
});
|
|
@@ -39653,7 +40385,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
39653
40385
|
if (result.action === "updated") {
|
|
39654
40386
|
logger.info("update.updated", { version: result.version });
|
|
39655
40387
|
console.log("Restarting with the new version\u2026");
|
|
39656
|
-
const child =
|
|
40388
|
+
const child = spawn6(process.execPath, process.argv.slice(1), {
|
|
39657
40389
|
stdio: "inherit",
|
|
39658
40390
|
env: { ...process.env, FOURPM_NO_UPDATE: "1" }
|
|
39659
40391
|
});
|
|
@@ -39691,7 +40423,7 @@ async function runStart(profileDir2, profileName) {
|
|
|
39691
40423
|
logger.error("ws.run.crash", { message: e.message, stack: e.stack });
|
|
39692
40424
|
if (client.isStopped) return;
|
|
39693
40425
|
bus.log(`Recovered from an internal error \u2014 reconnecting: ${e.message}`, "error");
|
|
39694
|
-
await new Promise((
|
|
40426
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
39695
40427
|
}
|
|
39696
40428
|
}
|
|
39697
40429
|
};
|
|
@@ -39818,10 +40550,10 @@ function Picker({
|
|
|
39818
40550
|
] });
|
|
39819
40551
|
}
|
|
39820
40552
|
function selectFromList(items, title) {
|
|
39821
|
-
return new Promise((
|
|
40553
|
+
return new Promise((resolve8) => {
|
|
39822
40554
|
let chosen = null;
|
|
39823
40555
|
const app = render_default(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Picker, { items, title, onDone: (v) => chosen = v }));
|
|
39824
|
-
void app.waitUntilExit().then(() =>
|
|
40556
|
+
void app.waitUntilExit().then(() => resolve8(chosen));
|
|
39825
40557
|
});
|
|
39826
40558
|
}
|
|
39827
40559
|
|