zelari-code 1.41.0 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/budget/llmCompact.js +97 -83
- package/dist/cli/budget/llmCompact.js.map +1 -1
- package/dist/cli/budget/requestMeter.js +138 -0
- package/dist/cli/budget/requestMeter.js.map +1 -0
- package/dist/cli/budget/requestSnapshotStore.js +55 -0
- package/dist/cli/budget/requestSnapshotStore.js.map +1 -0
- package/dist/cli/budget/tokenBudget.js +147 -15
- package/dist/cli/budget/tokenBudget.js.map +1 -1
- package/dist/cli/hooks/conversationContext.js +4 -0
- package/dist/cli/hooks/conversationContext.js.map +1 -1
- package/dist/cli/hooks/historyCompaction.js +76 -21
- package/dist/cli/hooks/historyCompaction.js.map +1 -1
- package/dist/cli/hooks/useChatTurn.js +90 -23
- package/dist/cli/hooks/useChatTurn.js.map +1 -1
- package/dist/cli/main.bundled.js +1555 -890
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/provider/openai-compatible.js +11 -1
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/runHeadless.js +15 -0
- package/dist/cli/runHeadless.js.map +1 -1
- package/dist/cli/toolRegistry.js +16 -0
- package/dist/cli/toolRegistry.js.map +1 -1
- package/dist/cli/tools/planTaskTools.js +236 -0
- package/dist/cli/tools/planTaskTools.js.map +1 -0
- package/dist/cli/workspace/planStore.js +213 -0
- package/dist/cli/workspace/planStore.js.map +1 -0
- package/dist/cli/workspace/planStore.test.js +295 -0
- package/dist/cli/workspace/planStore.test.js.map +1 -0
- package/dist/cli/workspace/stubs.js +14 -4
- package/dist/cli/workspace/stubs.js.map +1 -1
- package/dist/cli/workspace/workspaceSummary.js +7 -1
- package/dist/cli/workspace/workspaceSummary.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/main.bundled.js
CHANGED
|
@@ -1468,27 +1468,27 @@ function readStore() {
|
|
|
1468
1468
|
}
|
|
1469
1469
|
return { providers: {} };
|
|
1470
1470
|
}
|
|
1471
|
-
function writeStore(
|
|
1471
|
+
function writeStore(store4) {
|
|
1472
1472
|
const file2 = getKeyStorePath();
|
|
1473
1473
|
mkdirSync2(path3.dirname(file2), { recursive: true });
|
|
1474
|
-
writeFileSync2(file2, JSON.stringify(
|
|
1474
|
+
writeFileSync2(file2, JSON.stringify(store4, null, 2), { encoding: "utf-8", mode: 384 });
|
|
1475
1475
|
}
|
|
1476
1476
|
function setApiKey(providerId, key) {
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
writeStore(
|
|
1477
|
+
const store4 = readStore();
|
|
1478
|
+
store4.providers[providerId] = { apiKey: key };
|
|
1479
|
+
writeStore(store4);
|
|
1480
1480
|
}
|
|
1481
1481
|
function clearApiKey(providerId) {
|
|
1482
|
-
const
|
|
1483
|
-
delete
|
|
1484
|
-
writeStore(
|
|
1482
|
+
const store4 = readStore();
|
|
1483
|
+
delete store4.providers[providerId];
|
|
1484
|
+
writeStore(store4);
|
|
1485
1485
|
}
|
|
1486
1486
|
function getStoredApiKey(providerId) {
|
|
1487
|
-
const
|
|
1488
|
-
return
|
|
1487
|
+
const store4 = readStore();
|
|
1488
|
+
return store4.providers[providerId]?.apiKey ?? null;
|
|
1489
1489
|
}
|
|
1490
1490
|
function setOAuthToken(providerId, token) {
|
|
1491
|
-
const
|
|
1491
|
+
const store4 = readStore();
|
|
1492
1492
|
const entry = { apiKey: token.apiKey };
|
|
1493
1493
|
if (typeof token.expiresAt === "number" && Number.isFinite(token.expiresAt)) {
|
|
1494
1494
|
entry.expiresAt = token.expiresAt;
|
|
@@ -1502,12 +1502,12 @@ function setOAuthToken(providerId, token) {
|
|
|
1502
1502
|
if (typeof token.idToken === "string" && token.idToken.length > 0) {
|
|
1503
1503
|
entry.idToken = token.idToken;
|
|
1504
1504
|
}
|
|
1505
|
-
|
|
1506
|
-
writeStore(
|
|
1505
|
+
store4.providers[providerId] = entry;
|
|
1506
|
+
writeStore(store4);
|
|
1507
1507
|
}
|
|
1508
1508
|
function getOAuthToken(providerId) {
|
|
1509
|
-
const
|
|
1510
|
-
return
|
|
1509
|
+
const store4 = readStore();
|
|
1510
|
+
return store4.providers[providerId] ?? null;
|
|
1511
1511
|
}
|
|
1512
1512
|
function resolveApiKey(providerId) {
|
|
1513
1513
|
const spec = getProviderSpec(providerId);
|
|
@@ -1566,8 +1566,8 @@ async function forceRefreshOAuth(providerId, options = {}) {
|
|
|
1566
1566
|
function readStoreDirect() {
|
|
1567
1567
|
return readStore();
|
|
1568
1568
|
}
|
|
1569
|
-
function writeStoreDirect(
|
|
1570
|
-
writeStore(
|
|
1569
|
+
function writeStoreDirect(store4) {
|
|
1570
|
+
writeStore(store4);
|
|
1571
1571
|
}
|
|
1572
1572
|
function maskKey(key) {
|
|
1573
1573
|
if (key.length <= 12) return "****";
|
|
@@ -21063,6 +21063,12 @@ function isBrainMemberCostEvent(e) {
|
|
|
21063
21063
|
function isBrainCouncilModeEvent(e) {
|
|
21064
21064
|
return e.type === "council_mode";
|
|
21065
21065
|
}
|
|
21066
|
+
function isBrainTaskUpdateEvent(e) {
|
|
21067
|
+
return e.type === "task_update";
|
|
21068
|
+
}
|
|
21069
|
+
function isBrainTaskSnapshotEvent(e) {
|
|
21070
|
+
return e.type === "task_snapshot";
|
|
21071
|
+
}
|
|
21066
21072
|
function createBrainEvent(type, sessionId, data) {
|
|
21067
21073
|
return {
|
|
21068
21074
|
type,
|
|
@@ -21214,6 +21220,79 @@ var init_types = __esm({
|
|
|
21214
21220
|
}
|
|
21215
21221
|
});
|
|
21216
21222
|
|
|
21223
|
+
// packages/core/dist/core/requestSnapshot.js
|
|
21224
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
21225
|
+
function stableStringify(value) {
|
|
21226
|
+
if (value === null || typeof value !== "object")
|
|
21227
|
+
return JSON.stringify(value) ?? "null";
|
|
21228
|
+
if (Array.isArray(value)) {
|
|
21229
|
+
const items = value.map((v) => stableStringify(v));
|
|
21230
|
+
return `[${items.join(",")}]`;
|
|
21231
|
+
}
|
|
21232
|
+
const obj = value;
|
|
21233
|
+
const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
|
|
21234
|
+
const parts = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
|
|
21235
|
+
return `{${parts.join(",")}}`;
|
|
21236
|
+
}
|
|
21237
|
+
function sha256Hex(input) {
|
|
21238
|
+
return createHash2("sha256").update(input, "utf8").digest("hex").slice(0, 32);
|
|
21239
|
+
}
|
|
21240
|
+
function cloneMessages(messages) {
|
|
21241
|
+
return structuredClone(messages);
|
|
21242
|
+
}
|
|
21243
|
+
function canonicalTools(tools) {
|
|
21244
|
+
return [...tools].sort((a, b) => a.name.localeCompare(b.name));
|
|
21245
|
+
}
|
|
21246
|
+
function createRoutedRequestSnapshot(params) {
|
|
21247
|
+
let split = 0;
|
|
21248
|
+
while (split < params.messages.length && params.messages[split].role === "system") {
|
|
21249
|
+
split++;
|
|
21250
|
+
}
|
|
21251
|
+
const systemMessages = cloneMessages(params.messages.slice(0, split));
|
|
21252
|
+
const conversation = cloneMessages(params.messages.slice(split));
|
|
21253
|
+
const tools = canonicalTools(params.tools).map((t) => structuredClone(t));
|
|
21254
|
+
const header = stableStringify({
|
|
21255
|
+
provider: params.provider,
|
|
21256
|
+
model: params.model,
|
|
21257
|
+
systemMessages,
|
|
21258
|
+
tools
|
|
21259
|
+
});
|
|
21260
|
+
const request = stableStringify({
|
|
21261
|
+
provider: params.provider,
|
|
21262
|
+
model: params.model,
|
|
21263
|
+
systemMessages,
|
|
21264
|
+
tools,
|
|
21265
|
+
conversation
|
|
21266
|
+
});
|
|
21267
|
+
return {
|
|
21268
|
+
provider: params.provider,
|
|
21269
|
+
model: params.model,
|
|
21270
|
+
systemMessages,
|
|
21271
|
+
conversation,
|
|
21272
|
+
tools,
|
|
21273
|
+
headerFingerprint: sha256Hex(header),
|
|
21274
|
+
requestFingerprint: sha256Hex(request),
|
|
21275
|
+
createdAt: Date.now()
|
|
21276
|
+
};
|
|
21277
|
+
}
|
|
21278
|
+
function compareReplayPrefix(snapshot, messages) {
|
|
21279
|
+
const base = snapshot.conversation;
|
|
21280
|
+
const n = Math.min(base.length, messages.length);
|
|
21281
|
+
let matching = 0;
|
|
21282
|
+
for (let i = 0; i < n; i++) {
|
|
21283
|
+
if (stableStringify(base[i]) !== stableStringify(messages[i])) {
|
|
21284
|
+
return { exact: false, matchingMessages: matching, mismatchIndex: i };
|
|
21285
|
+
}
|
|
21286
|
+
matching++;
|
|
21287
|
+
}
|
|
21288
|
+
return { exact: true, matchingMessages: matching };
|
|
21289
|
+
}
|
|
21290
|
+
var init_requestSnapshot = __esm({
|
|
21291
|
+
"packages/core/dist/core/requestSnapshot.js"() {
|
|
21292
|
+
"use strict";
|
|
21293
|
+
}
|
|
21294
|
+
});
|
|
21295
|
+
|
|
21217
21296
|
// packages/core/dist/core/textLoopDetect.js
|
|
21218
21297
|
function isStatusTheaterUnit(unit) {
|
|
21219
21298
|
const u = normalizeLoopUnit(unit).toLowerCase();
|
|
@@ -21412,10 +21491,10 @@ var init_textLoopDetect = __esm({
|
|
|
21412
21491
|
|
|
21413
21492
|
// packages/core/dist/core/AgentHarness.js
|
|
21414
21493
|
function hashToolCall(toolName, args) {
|
|
21415
|
-
const canonical =
|
|
21494
|
+
const canonical = stableStringify2(args);
|
|
21416
21495
|
return `${toolName}::${canonical}`;
|
|
21417
21496
|
}
|
|
21418
|
-
function
|
|
21497
|
+
function stableStringify2(value) {
|
|
21419
21498
|
return JSON.stringify(value, (_k, v) => {
|
|
21420
21499
|
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
21421
21500
|
const sorted = {};
|
|
@@ -21648,6 +21727,7 @@ var init_AgentHarness = __esm({
|
|
|
21648
21727
|
"packages/core/dist/core/AgentHarness.js"() {
|
|
21649
21728
|
"use strict";
|
|
21650
21729
|
init_events();
|
|
21730
|
+
init_requestSnapshot();
|
|
21651
21731
|
init_textLoopDetect();
|
|
21652
21732
|
init_textLoopDetect();
|
|
21653
21733
|
AgentHarness = class {
|
|
@@ -22084,6 +22164,23 @@ ${shared2.content}`,
|
|
|
22084
22164
|
yield agentEnd;
|
|
22085
22165
|
this.activeController = null;
|
|
22086
22166
|
}
|
|
22167
|
+
/**
|
|
22168
|
+
* v1.36.0: capture a deterministic snapshot of the routed request just
|
|
22169
|
+
* before it goes out. Never throws into the request path.
|
|
22170
|
+
*/
|
|
22171
|
+
emitSnapshot(tools, generation) {
|
|
22172
|
+
if (!this.config.onRequestSnapshot)
|
|
22173
|
+
return;
|
|
22174
|
+
try {
|
|
22175
|
+
this.config.onRequestSnapshot(createRoutedRequestSnapshot({
|
|
22176
|
+
messages: this.config.messages,
|
|
22177
|
+
model: this.config.model,
|
|
22178
|
+
provider: this.config.provider,
|
|
22179
|
+
tools
|
|
22180
|
+
}), generation);
|
|
22181
|
+
} catch {
|
|
22182
|
+
}
|
|
22183
|
+
}
|
|
22087
22184
|
/**
|
|
22088
22185
|
* Run a single provider turn for the current message buffer.
|
|
22089
22186
|
* Streams from the provider, dispatches deltas to events, executes
|
|
@@ -22101,6 +22198,7 @@ ${shared2.content}`,
|
|
|
22101
22198
|
*/
|
|
22102
22199
|
async *runSingleTurn(messageId, finishRef, usageRef) {
|
|
22103
22200
|
try {
|
|
22201
|
+
this.emitSnapshot(this.config.tools);
|
|
22104
22202
|
const stream = this.config.providerStream({
|
|
22105
22203
|
messages: this.config.messages,
|
|
22106
22204
|
model: this.config.model,
|
|
@@ -22391,6 +22489,7 @@ ${cached2}`
|
|
|
22391
22489
|
const finishRef = { value: "stop" };
|
|
22392
22490
|
const usageRef = { value: null };
|
|
22393
22491
|
try {
|
|
22492
|
+
this.emitSnapshot([]);
|
|
22394
22493
|
const stream = this.config.providerStream({
|
|
22395
22494
|
messages: this.config.messages,
|
|
22396
22495
|
model: this.config.model,
|
|
@@ -22971,7 +23070,10 @@ __export(harness_exports, {
|
|
|
22971
23070
|
SessionJsonlWriter: () => SessionJsonlWriter,
|
|
22972
23071
|
TEXT_LOOP_RECOVERY_SYSTEM: () => TEXT_LOOP_RECOVERY_SYSTEM,
|
|
22973
23072
|
TEXT_LOOP_RECOVERY_USER_PROMPT: () => TEXT_LOOP_RECOVERY_USER_PROMPT,
|
|
23073
|
+
canonicalTools: () => canonicalTools,
|
|
22974
23074
|
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
23075
|
+
compareReplayPrefix: () => compareReplayPrefix,
|
|
23076
|
+
createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
|
|
22975
23077
|
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
22976
23078
|
detectAssistantTextLoopWindow: () => detectAssistantTextLoopWindow,
|
|
22977
23079
|
hashToolCall: () => hashToolCall,
|
|
@@ -22983,6 +23085,8 @@ __export(harness_exports, {
|
|
|
22983
23085
|
parseMinimaxStyleToolCalls: () => parseMinimaxStyleToolCalls,
|
|
22984
23086
|
parseTextToolCalls: () => parseTextToolCalls,
|
|
22985
23087
|
readSession: () => readSession,
|
|
23088
|
+
sha256Hex: () => sha256Hex,
|
|
23089
|
+
stableStringify: () => stableStringify,
|
|
22986
23090
|
toolMatches: () => toolMatches,
|
|
22987
23091
|
wrapLegacyStream: () => wrapLegacyStream
|
|
22988
23092
|
});
|
|
@@ -22991,6 +23095,7 @@ var init_harness = __esm({
|
|
|
22991
23095
|
"use strict";
|
|
22992
23096
|
init_AgentHarness();
|
|
22993
23097
|
init_providerStream();
|
|
23098
|
+
init_requestSnapshot();
|
|
22994
23099
|
init_sessionJsonl();
|
|
22995
23100
|
init_hooks();
|
|
22996
23101
|
}
|
|
@@ -27902,6 +28007,7 @@ __export(dist_exports, {
|
|
|
27902
28007
|
buildSystemPrompt: () => buildSystemPrompt,
|
|
27903
28008
|
buildSystemPromptSplit: () => buildSystemPromptSplit,
|
|
27904
28009
|
canRunParallel: () => canRunParallel,
|
|
28010
|
+
canonicalTools: () => canonicalTools,
|
|
27905
28011
|
captureFailure: () => captureFailure,
|
|
27906
28012
|
checkImplementationCompletion: () => checkImplementationCompletion,
|
|
27907
28013
|
checkImplementationDelivery: () => checkImplementationDelivery,
|
|
@@ -27913,6 +28019,7 @@ __export(dist_exports, {
|
|
|
27913
28019
|
clearCustomTools: () => clearCustomTools,
|
|
27914
28020
|
cliToolToEnhanced: () => cliToolToEnhanced,
|
|
27915
28021
|
collapseLoopedAssistantText: () => collapseLoopedAssistantText,
|
|
28022
|
+
compareReplayPrefix: () => compareReplayPrefix,
|
|
27916
28023
|
computeAgentSkills: () => computeAgentSkills,
|
|
27917
28024
|
computeAgentTools: () => computeAgentTools,
|
|
27918
28025
|
councilModeBanner: () => councilModeBanner,
|
|
@@ -27922,6 +28029,7 @@ __export(dist_exports, {
|
|
|
27922
28029
|
createBrainEvent: () => createBrainEvent,
|
|
27923
28030
|
createDefaultSystemPromptConfig: () => createDefaultSystemPromptConfig,
|
|
27924
28031
|
createGraph: () => createGraph,
|
|
28032
|
+
createRoutedRequestSnapshot: () => createRoutedRequestSnapshot,
|
|
27925
28033
|
defaultPersonaParse: () => defaultPersonaParse,
|
|
27926
28034
|
detectAssistantTextLoop: () => detectAssistantTextLoop,
|
|
27927
28035
|
detectAssistantTextLoopWindow: () => detectAssistantTextLoopWindow,
|
|
@@ -27971,6 +28079,8 @@ __export(dist_exports, {
|
|
|
27971
28079
|
isBrainMessageStartEvent: () => isBrainMessageStartEvent,
|
|
27972
28080
|
isBrainQueueUpdateEvent: () => isBrainQueueUpdateEvent,
|
|
27973
28081
|
isBrainSessionCompactedEvent: () => isBrainSessionCompactedEvent,
|
|
28082
|
+
isBrainTaskSnapshotEvent: () => isBrainTaskSnapshotEvent,
|
|
28083
|
+
isBrainTaskUpdateEvent: () => isBrainTaskUpdateEvent,
|
|
27974
28084
|
isBrainThinkingDeltaEvent: () => isBrainThinkingDeltaEvent,
|
|
27975
28085
|
isBrainToolExecutionEndEvent: () => isBrainToolExecutionEndEvent,
|
|
27976
28086
|
isBrainToolExecutionStartEvent: () => isBrainToolExecutionStartEvent,
|
|
@@ -28038,9 +28148,11 @@ __export(dist_exports, {
|
|
|
28038
28148
|
scrubProprietaryLeak: () => scrubProprietaryLeak,
|
|
28039
28149
|
selectParallelWave: () => selectParallelWave,
|
|
28040
28150
|
setWorkspaceStubs: () => setWorkspaceStubs,
|
|
28151
|
+
sha256Hex: () => sha256Hex,
|
|
28041
28152
|
shouldRetryMember: () => shouldRetryMember,
|
|
28042
28153
|
slugify: () => slugify2,
|
|
28043
28154
|
specificityFromAssumptions: () => specificityFromAssumptions,
|
|
28155
|
+
stableStringify: () => stableStringify,
|
|
28044
28156
|
stripClarificationProtocol: () => stripClarificationProtocol,
|
|
28045
28157
|
swapMembers: () => swapMembers,
|
|
28046
28158
|
systemMessagesFromSplit: () => systemMessagesFromSplit,
|
|
@@ -28403,6 +28515,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
28403
28515
|
if (cacheable) messageMappingCache.set(m, mapped);
|
|
28404
28516
|
return mapped;
|
|
28405
28517
|
});
|
|
28518
|
+
const generation = params.generation;
|
|
28406
28519
|
const body = {
|
|
28407
28520
|
// Use `params.model` (per-call override from AgentHarness, e.g. for
|
|
28408
28521
|
// `agentModels` config) rather than the closed-over `config.model`
|
|
@@ -28410,7 +28523,7 @@ function openaiCompatibleProvider(config2) {
|
|
|
28410
28523
|
model: params.model,
|
|
28411
28524
|
messages,
|
|
28412
28525
|
stream: true,
|
|
28413
|
-
temperature: 0.7,
|
|
28526
|
+
temperature: generation?.temperature ?? 0.7,
|
|
28414
28527
|
// Task G.4.2 — request the provider to send real token usage in
|
|
28415
28528
|
// the final chunk (gated by `stream_options.include_usage` on the
|
|
28416
28529
|
// OpenAI-compatible API). Providers that don't honor this (some
|
|
@@ -28418,6 +28531,9 @@ function openaiCompatibleProvider(config2) {
|
|
|
28418
28531
|
// the harness will fall back to the ~4-char/token approximation.
|
|
28419
28532
|
stream_options: { include_usage: true }
|
|
28420
28533
|
};
|
|
28534
|
+
if (typeof generation?.maxTokens === "number" && generation.maxTokens > 0) {
|
|
28535
|
+
body.max_tokens = generation.maxTokens;
|
|
28536
|
+
}
|
|
28421
28537
|
const thinkingSpec = config2.thinking ?? "auto";
|
|
28422
28538
|
if (config2.providerId === "deepseek" && thinkingSpec === "auto") {
|
|
28423
28539
|
const thinking = resolveDeepSeekThinking();
|
|
@@ -29312,7 +29428,7 @@ var init_resolveStream = __esm({
|
|
|
29312
29428
|
});
|
|
29313
29429
|
|
|
29314
29430
|
// packages/core/dist/core/tools/toolOutputSpill.js
|
|
29315
|
-
import { createHash as
|
|
29431
|
+
import { createHash as createHash3, randomBytes as randomBytes2 } from "node:crypto";
|
|
29316
29432
|
import { existsSync as existsSync12, mkdirSync as mkdirSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
29317
29433
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
29318
29434
|
import { join as join11 } from "node:path";
|
|
@@ -29342,7 +29458,7 @@ function spillToolOutput(fullText, meta3) {
|
|
|
29342
29458
|
if (!existsSync12(dir)) {
|
|
29343
29459
|
mkdirSync7(dir, { recursive: true });
|
|
29344
29460
|
}
|
|
29345
|
-
const hash3 =
|
|
29461
|
+
const hash3 = createHash3("sha256").update(fullText).digest("hex").slice(0, 12);
|
|
29346
29462
|
const stamp = Date.now().toString(36);
|
|
29347
29463
|
const rnd = randomBytes2(3).toString("hex");
|
|
29348
29464
|
const safeTool = (meta3?.toolName ?? "tool").replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 32);
|
|
@@ -30918,6 +31034,771 @@ var init_todoTools = __esm({
|
|
|
30918
31034
|
}
|
|
30919
31035
|
});
|
|
30920
31036
|
|
|
31037
|
+
// src/cli/workspace/paths.ts
|
|
31038
|
+
import {
|
|
31039
|
+
mkdirSync as mkdirSync10,
|
|
31040
|
+
writeFileSync as writeFileSync12,
|
|
31041
|
+
existsSync as existsSync17,
|
|
31042
|
+
accessSync,
|
|
31043
|
+
constants,
|
|
31044
|
+
realpathSync
|
|
31045
|
+
} from "node:fs";
|
|
31046
|
+
import { join as join13, basename } from "node:path";
|
|
31047
|
+
import { homedir as homedir5 } from "node:os";
|
|
31048
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
31049
|
+
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
31050
|
+
const candidates = [
|
|
31051
|
+
join13(projectRoot, ".zelari"),
|
|
31052
|
+
join13(homedir5(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
31053
|
+
];
|
|
31054
|
+
for (const candidate of candidates) {
|
|
31055
|
+
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
31056
|
+
ensureWorkspaceDir(candidate);
|
|
31057
|
+
return candidate;
|
|
31058
|
+
}
|
|
31059
|
+
}
|
|
31060
|
+
ensureWorkspaceDir(candidates[0]);
|
|
31061
|
+
return candidates[0];
|
|
31062
|
+
}
|
|
31063
|
+
function hashProject(projectPath) {
|
|
31064
|
+
return createHash4("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
31065
|
+
}
|
|
31066
|
+
function isWritableDir(dir) {
|
|
31067
|
+
try {
|
|
31068
|
+
if (!existsSync17(dir)) return false;
|
|
31069
|
+
accessSync(dir, constants.W_OK);
|
|
31070
|
+
return true;
|
|
31071
|
+
} catch {
|
|
31072
|
+
return false;
|
|
31073
|
+
}
|
|
31074
|
+
}
|
|
31075
|
+
function ensureWorkspaceDir(workspaceDir) {
|
|
31076
|
+
mkdirSync10(workspaceDir, { recursive: true });
|
|
31077
|
+
if (workspaceDir.endsWith("/.zelari") && existsSync17(join13(workspaceDir, "..", ".git"))) {
|
|
31078
|
+
const gitignorePath = join13(workspaceDir, ".gitignore");
|
|
31079
|
+
if (!existsSync17(gitignorePath)) {
|
|
31080
|
+
writeFileSync12(gitignorePath, "*\n!.gitignore\n");
|
|
31081
|
+
}
|
|
31082
|
+
}
|
|
31083
|
+
}
|
|
31084
|
+
function workspaceFile(rootDir, kind) {
|
|
31085
|
+
switch (kind) {
|
|
31086
|
+
case "plan":
|
|
31087
|
+
return join13(rootDir, "plan.md");
|
|
31088
|
+
case "risks":
|
|
31089
|
+
return join13(rootDir, "risks.md");
|
|
31090
|
+
case "index":
|
|
31091
|
+
return join13(rootDir, "workspace.json");
|
|
31092
|
+
}
|
|
31093
|
+
}
|
|
31094
|
+
function workspaceArtifact(rootDir, subdir, slug) {
|
|
31095
|
+
return join13(rootDir, subdir, `${slug}.md`);
|
|
31096
|
+
}
|
|
31097
|
+
function projectName(projectRoot = process.cwd()) {
|
|
31098
|
+
return basename(realpathSync(projectRoot));
|
|
31099
|
+
}
|
|
31100
|
+
var init_paths2 = __esm({
|
|
31101
|
+
"src/cli/workspace/paths.ts"() {
|
|
31102
|
+
"use strict";
|
|
31103
|
+
}
|
|
31104
|
+
});
|
|
31105
|
+
|
|
31106
|
+
// src/cli/workspace/storage.ts
|
|
31107
|
+
var storage_exports = {};
|
|
31108
|
+
__export(storage_exports, {
|
|
31109
|
+
Storage: () => Storage,
|
|
31110
|
+
parseFrontmatter: () => parseFrontmatter,
|
|
31111
|
+
parseYaml: () => parseYaml,
|
|
31112
|
+
serializeFrontmatter: () => serializeFrontmatter,
|
|
31113
|
+
serializeYaml: () => serializeYaml,
|
|
31114
|
+
workspaceMutex: () => workspaceMutex
|
|
31115
|
+
});
|
|
31116
|
+
import {
|
|
31117
|
+
readFileSync as readFileSync16,
|
|
31118
|
+
writeFileSync as writeFileSync13,
|
|
31119
|
+
existsSync as existsSync18,
|
|
31120
|
+
mkdirSync as mkdirSync11,
|
|
31121
|
+
readdirSync as readdirSync4,
|
|
31122
|
+
renameSync as renameSync2
|
|
31123
|
+
} from "node:fs";
|
|
31124
|
+
import { dirname as dirname2, join as join14 } from "node:path";
|
|
31125
|
+
function parseFrontmatter(md) {
|
|
31126
|
+
const m = FRONTMATTER_RE.exec(md);
|
|
31127
|
+
if (!m) return { meta: {}, body: md };
|
|
31128
|
+
const meta3 = parseYaml(m[1]);
|
|
31129
|
+
const body = m[2];
|
|
31130
|
+
return { meta: meta3, body };
|
|
31131
|
+
}
|
|
31132
|
+
function serializeFrontmatter(meta3, body) {
|
|
31133
|
+
const yamlStr = serializeYaml(meta3);
|
|
31134
|
+
return `---
|
|
31135
|
+
${yamlStr}
|
|
31136
|
+
---
|
|
31137
|
+
${body}`;
|
|
31138
|
+
}
|
|
31139
|
+
function parseYaml(input) {
|
|
31140
|
+
const lines = input.split(/\r?\n/);
|
|
31141
|
+
const ctx = { lines, i: 0 };
|
|
31142
|
+
return parseNode(ctx, 0);
|
|
31143
|
+
}
|
|
31144
|
+
function parseNode(ctx, indent) {
|
|
31145
|
+
while (ctx.i < ctx.lines.length) {
|
|
31146
|
+
const line2 = ctx.lines[ctx.i];
|
|
31147
|
+
if (line2.trim() === "" || line2.trim().startsWith("#")) {
|
|
31148
|
+
ctx.i++;
|
|
31149
|
+
continue;
|
|
31150
|
+
}
|
|
31151
|
+
break;
|
|
31152
|
+
}
|
|
31153
|
+
if (ctx.i >= ctx.lines.length) return null;
|
|
31154
|
+
const line = ctx.lines[ctx.i];
|
|
31155
|
+
const lineIndent = countIndent(line);
|
|
31156
|
+
if (/^\s*-\s+/.test(line)) {
|
|
31157
|
+
return parseBlockSequence(ctx, indent);
|
|
31158
|
+
}
|
|
31159
|
+
if (/^\s*\[.*\]\s*$/.test(line)) {
|
|
31160
|
+
const flow = line.trim().replace(/^\[/, "").replace(/\]$/, "");
|
|
31161
|
+
return parseFlowSequence(flow);
|
|
31162
|
+
}
|
|
31163
|
+
if (/^\s*\{.*\}\s*$/.test(line)) {
|
|
31164
|
+
const flow = line.trim().replace(/^\{/, "").replace(/\}$/, "");
|
|
31165
|
+
return parseFlowMap(flow);
|
|
31166
|
+
}
|
|
31167
|
+
return parseBlockMap(ctx, indent);
|
|
31168
|
+
}
|
|
31169
|
+
function parseBlockMap(ctx, indent) {
|
|
31170
|
+
const out = {};
|
|
31171
|
+
while (ctx.i < ctx.lines.length) {
|
|
31172
|
+
const line = ctx.lines[ctx.i];
|
|
31173
|
+
if (line.trim() === "" || line.trim().startsWith("#")) {
|
|
31174
|
+
ctx.i++;
|
|
31175
|
+
continue;
|
|
31176
|
+
}
|
|
31177
|
+
const lineIndent = countIndent(line);
|
|
31178
|
+
if (lineIndent < indent) break;
|
|
31179
|
+
if (lineIndent > indent) {
|
|
31180
|
+
ctx.i++;
|
|
31181
|
+
continue;
|
|
31182
|
+
}
|
|
31183
|
+
const m = /^([^:]+):\s*(.*)$/.exec(line);
|
|
31184
|
+
if (!m) {
|
|
31185
|
+
ctx.i++;
|
|
31186
|
+
continue;
|
|
31187
|
+
}
|
|
31188
|
+
const key = m[1].trim();
|
|
31189
|
+
const valuePart = m[2].trim();
|
|
31190
|
+
if (valuePart === "" || valuePart === "|" || valuePart === ">") {
|
|
31191
|
+
ctx.i++;
|
|
31192
|
+
const nested = parseNode(ctx, indent + 2);
|
|
31193
|
+
out[key] = nested;
|
|
31194
|
+
} else {
|
|
31195
|
+
if (valuePart.startsWith("[")) {
|
|
31196
|
+
out[key] = parseFlowSequence(stripFlow(valuePart, "[", "]"));
|
|
31197
|
+
} else if (valuePart.startsWith("{")) {
|
|
31198
|
+
out[key] = parseFlowMap(stripFlow(valuePart, "{", "}"));
|
|
31199
|
+
} else {
|
|
31200
|
+
out[key] = parseScalar(valuePart);
|
|
31201
|
+
}
|
|
31202
|
+
ctx.i++;
|
|
31203
|
+
}
|
|
31204
|
+
}
|
|
31205
|
+
return out;
|
|
31206
|
+
}
|
|
31207
|
+
function parseBlockSequence(ctx, indent) {
|
|
31208
|
+
const out = [];
|
|
31209
|
+
while (ctx.i < ctx.lines.length) {
|
|
31210
|
+
const line = ctx.lines[ctx.i];
|
|
31211
|
+
if (line.trim() === "") {
|
|
31212
|
+
ctx.i++;
|
|
31213
|
+
continue;
|
|
31214
|
+
}
|
|
31215
|
+
const lineIndent = countIndent(line);
|
|
31216
|
+
if (lineIndent < indent) break;
|
|
31217
|
+
if (lineIndent > indent) break;
|
|
31218
|
+
const m = /^-\s*(.*)$/.exec(line);
|
|
31219
|
+
if (!m) break;
|
|
31220
|
+
const rest = m[1];
|
|
31221
|
+
if (rest === "") {
|
|
31222
|
+
ctx.i++;
|
|
31223
|
+
out.push(parseNode(ctx, indent + 2));
|
|
31224
|
+
} else if (rest.startsWith("[") || rest.startsWith("{")) {
|
|
31225
|
+
let buffer = rest;
|
|
31226
|
+
let depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
31227
|
+
let depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
31228
|
+
while ((depthSq > 0 || depthCu > 0) && ctx.i + 1 < ctx.lines.length) {
|
|
31229
|
+
ctx.i++;
|
|
31230
|
+
const next = ctx.lines[ctx.i].trim();
|
|
31231
|
+
buffer += " " + next;
|
|
31232
|
+
depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
31233
|
+
depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
31234
|
+
}
|
|
31235
|
+
if (buffer.startsWith("[")) {
|
|
31236
|
+
out.push(parseFlowSequence(buffer.slice(1).replace(/\]$/, "")));
|
|
31237
|
+
} else {
|
|
31238
|
+
out.push(parseFlowMap(buffer.slice(1).replace(/\}$/, "")));
|
|
31239
|
+
}
|
|
31240
|
+
ctx.i++;
|
|
31241
|
+
} else if (rest.includes(":")) {
|
|
31242
|
+
ctx.i++;
|
|
31243
|
+
const mapCtx = { lines: [" ".repeat(indent + 2) + rest, ...ctx.lines.slice(ctx.i)], i: 0 };
|
|
31244
|
+
const val = parseBlockMap(mapCtx, indent + 2);
|
|
31245
|
+
ctx.i += mapCtx.i - 1;
|
|
31246
|
+
out.push(val);
|
|
31247
|
+
} else {
|
|
31248
|
+
out.push(parseScalar(rest));
|
|
31249
|
+
ctx.i++;
|
|
31250
|
+
}
|
|
31251
|
+
}
|
|
31252
|
+
return out;
|
|
31253
|
+
}
|
|
31254
|
+
function parseFlowSequence(input) {
|
|
31255
|
+
const parts = splitFlow(input);
|
|
31256
|
+
return parts.map((p3) => {
|
|
31257
|
+
const trimmed = p3.trim();
|
|
31258
|
+
if (trimmed.startsWith("{")) {
|
|
31259
|
+
return parseFlowMap(stripFlow(trimmed, "{", "}"));
|
|
31260
|
+
}
|
|
31261
|
+
return parseScalar(trimmed);
|
|
31262
|
+
});
|
|
31263
|
+
}
|
|
31264
|
+
function stripFlow(s, open, close) {
|
|
31265
|
+
let out = s.trim();
|
|
31266
|
+
if (out.startsWith(open)) out = out.slice(1);
|
|
31267
|
+
if (out.endsWith(close)) out = out.slice(0, -1);
|
|
31268
|
+
return out;
|
|
31269
|
+
}
|
|
31270
|
+
function parseFlowMap(input) {
|
|
31271
|
+
const parts = splitFlow(input);
|
|
31272
|
+
const out = {};
|
|
31273
|
+
for (const p3 of parts) {
|
|
31274
|
+
const colonIdx = p3.indexOf(":");
|
|
31275
|
+
if (colonIdx < 0) continue;
|
|
31276
|
+
const key = p3.slice(0, colonIdx).trim();
|
|
31277
|
+
const value = p3.slice(colonIdx + 1).trim();
|
|
31278
|
+
out[key] = parseScalar(value);
|
|
31279
|
+
}
|
|
31280
|
+
return out;
|
|
31281
|
+
}
|
|
31282
|
+
function splitFlow(input) {
|
|
31283
|
+
const out = [];
|
|
31284
|
+
let depthSq = 0, depthCu = 0, depthQu = 0;
|
|
31285
|
+
let buffer = "";
|
|
31286
|
+
for (let i = 0; i < input.length; i++) {
|
|
31287
|
+
const c = input[i];
|
|
31288
|
+
if (c === '"' || c === "'") {
|
|
31289
|
+
depthQu = depthQu === 0 ? depthQu + 1 : 0;
|
|
31290
|
+
buffer += c;
|
|
31291
|
+
} else if (depthQu === 0) {
|
|
31292
|
+
if (c === "[") depthSq++;
|
|
31293
|
+
else if (c === "]") depthSq--;
|
|
31294
|
+
else if (c === "{") depthCu++;
|
|
31295
|
+
else if (c === "}") depthCu--;
|
|
31296
|
+
else if (c === "," && depthSq === 0 && depthCu === 0) {
|
|
31297
|
+
out.push(buffer);
|
|
31298
|
+
buffer = "";
|
|
31299
|
+
continue;
|
|
31300
|
+
}
|
|
31301
|
+
buffer += c;
|
|
31302
|
+
} else {
|
|
31303
|
+
buffer += c;
|
|
31304
|
+
}
|
|
31305
|
+
}
|
|
31306
|
+
if (buffer.trim()) out.push(buffer);
|
|
31307
|
+
return out;
|
|
31308
|
+
}
|
|
31309
|
+
function parseScalar(s) {
|
|
31310
|
+
if (s === "" || s === "null" || s === "~") return null;
|
|
31311
|
+
if (VALID_SCALARS.test(s)) return s.toLowerCase() === "true";
|
|
31312
|
+
if (/^-?\d+(\.\d+)?$/.test(s)) return Number(s);
|
|
31313
|
+
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
31314
|
+
return s.slice(1, -1);
|
|
31315
|
+
}
|
|
31316
|
+
return s;
|
|
31317
|
+
}
|
|
31318
|
+
function serializeYaml(value, indent = 0) {
|
|
31319
|
+
if (value === null || value === void 0) return "";
|
|
31320
|
+
if (typeof value === "string") {
|
|
31321
|
+
if (/[:#\n\[\]\{\},&*!|>'"%@`]/.test(value)) {
|
|
31322
|
+
return JSON.stringify(value);
|
|
31323
|
+
}
|
|
31324
|
+
return value;
|
|
31325
|
+
}
|
|
31326
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
31327
|
+
if (Array.isArray(value)) {
|
|
31328
|
+
if (value.length === 0) return "[]";
|
|
31329
|
+
if (value.every((v) => v === null || typeof v !== "object")) {
|
|
31330
|
+
return `[${value.map(serializeScalarInline).join(", ")}]`;
|
|
31331
|
+
}
|
|
31332
|
+
return `[${value.map((v) => "{" + serializeInlineObject(v) + "}").join(", ")}]`;
|
|
31333
|
+
}
|
|
31334
|
+
if (typeof value === "object") {
|
|
31335
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0);
|
|
31336
|
+
return entries.map(([k, v]) => {
|
|
31337
|
+
if (v === null || v === void 0) return `${k}:`;
|
|
31338
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
31339
|
+
return `${k}: ${serializeScalarInline(v)}`;
|
|
31340
|
+
}
|
|
31341
|
+
if (Array.isArray(v)) {
|
|
31342
|
+
if (v.length === 0) return `${k}: []`;
|
|
31343
|
+
if (v.every((x) => x === null || typeof x !== "object")) {
|
|
31344
|
+
return `${k}: [${v.map(serializeScalarInline).join(", ")}]`;
|
|
31345
|
+
}
|
|
31346
|
+
return `${k}: [${v.map((x) => "{" + serializeInlineObject(x) + "}").join(", ")}]`;
|
|
31347
|
+
}
|
|
31348
|
+
return `${k}:
|
|
31349
|
+
${serializeYaml(v, indent + 2)}`;
|
|
31350
|
+
}).map((line) => `${" ".repeat(indent)}${line}`).join("\n");
|
|
31351
|
+
}
|
|
31352
|
+
return String(value);
|
|
31353
|
+
}
|
|
31354
|
+
function serializeScalarInline(v) {
|
|
31355
|
+
if (typeof v === "string" && /[:#\n\[\]\{\},&*!|>'"%@`]/.test(v)) return JSON.stringify(v);
|
|
31356
|
+
return String(v);
|
|
31357
|
+
}
|
|
31358
|
+
function serializeInlineObject(v) {
|
|
31359
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) {
|
|
31360
|
+
return serializeScalarInline(v);
|
|
31361
|
+
}
|
|
31362
|
+
const entries = Object.entries(v).filter(([, val]) => val !== void 0);
|
|
31363
|
+
return entries.map(([k, val]) => `${k}: ${serializeYaml(val)}`).join(", ");
|
|
31364
|
+
}
|
|
31365
|
+
function countIndent(line) {
|
|
31366
|
+
let i = 0;
|
|
31367
|
+
while (i < line.length && line[i] === " ") i++;
|
|
31368
|
+
return i;
|
|
31369
|
+
}
|
|
31370
|
+
var FRONTMATTER_RE, VALID_SCALARS, Storage, KeyedMutex, workspaceMutex;
|
|
31371
|
+
var init_storage = __esm({
|
|
31372
|
+
"src/cli/workspace/storage.ts"() {
|
|
31373
|
+
"use strict";
|
|
31374
|
+
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
31375
|
+
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
31376
|
+
Storage = class {
|
|
31377
|
+
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
31378
|
+
read(path53) {
|
|
31379
|
+
if (!existsSync18(path53)) {
|
|
31380
|
+
throw new Error(`File not found: ${path53}`);
|
|
31381
|
+
}
|
|
31382
|
+
const md = readFileSync16(path53, "utf8");
|
|
31383
|
+
return parseFrontmatter(md);
|
|
31384
|
+
}
|
|
31385
|
+
/** Read a Markdown file; returns null if not found. */
|
|
31386
|
+
readIfExists(path53) {
|
|
31387
|
+
if (!existsSync18(path53)) return null;
|
|
31388
|
+
return this.read(path53);
|
|
31389
|
+
}
|
|
31390
|
+
/**
|
|
31391
|
+
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
31392
|
+
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
31393
|
+
*/
|
|
31394
|
+
write(path53, meta3, body) {
|
|
31395
|
+
mkdirSync11(dirname2(path53), { recursive: true });
|
|
31396
|
+
const tmp = path53 + ".tmp-" + process.pid;
|
|
31397
|
+
const md = serializeFrontmatter(meta3, body);
|
|
31398
|
+
writeFileSync13(tmp, md, "utf8");
|
|
31399
|
+
renameSync2(tmp, path53);
|
|
31400
|
+
}
|
|
31401
|
+
/** List all .md files in a directory (non-recursive). */
|
|
31402
|
+
listMarkdown(dir) {
|
|
31403
|
+
if (!existsSync18(dir)) return [];
|
|
31404
|
+
return readdirSync4(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join14(dir, f));
|
|
31405
|
+
}
|
|
31406
|
+
};
|
|
31407
|
+
KeyedMutex = class {
|
|
31408
|
+
chains = /* @__PURE__ */ new Map();
|
|
31409
|
+
async run(key, fn) {
|
|
31410
|
+
const prev2 = this.chains.get(key) ?? Promise.resolve();
|
|
31411
|
+
let release = () => {
|
|
31412
|
+
};
|
|
31413
|
+
const next = new Promise((resolve3) => {
|
|
31414
|
+
release = resolve3;
|
|
31415
|
+
});
|
|
31416
|
+
const chained = prev2.then(() => next);
|
|
31417
|
+
this.chains.set(key, chained);
|
|
31418
|
+
await prev2;
|
|
31419
|
+
try {
|
|
31420
|
+
return await fn();
|
|
31421
|
+
} finally {
|
|
31422
|
+
release();
|
|
31423
|
+
if (this.chains.get(key) === chained) {
|
|
31424
|
+
this.chains.delete(key);
|
|
31425
|
+
}
|
|
31426
|
+
}
|
|
31427
|
+
}
|
|
31428
|
+
};
|
|
31429
|
+
workspaceMutex = new KeyedMutex();
|
|
31430
|
+
}
|
|
31431
|
+
});
|
|
31432
|
+
|
|
31433
|
+
// src/cli/workspace/planStore.ts
|
|
31434
|
+
import {
|
|
31435
|
+
copyFileSync,
|
|
31436
|
+
existsSync as existsSync19,
|
|
31437
|
+
mkdirSync as mkdirSync12,
|
|
31438
|
+
readFileSync as readFileSync17,
|
|
31439
|
+
renameSync as renameSync3,
|
|
31440
|
+
writeFileSync as writeFileSync14
|
|
31441
|
+
} from "node:fs";
|
|
31442
|
+
import { dirname as dirname3, join as join15 } from "node:path";
|
|
31443
|
+
async function withPlanStore(projectRoot, fn) {
|
|
31444
|
+
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
31445
|
+
return workspaceMutex.run(`${rootDir}:plan`, () => {
|
|
31446
|
+
const handle = loadHandle(rootDir);
|
|
31447
|
+
const out = fn(handle);
|
|
31448
|
+
saveHandle(rootDir, handle);
|
|
31449
|
+
return out;
|
|
31450
|
+
});
|
|
31451
|
+
}
|
|
31452
|
+
function nextPlanTaskId(store4) {
|
|
31453
|
+
const maxExisting = store4.tasks.reduce((max, t) => {
|
|
31454
|
+
const m = /^t(\d+)$/.exec(t.id);
|
|
31455
|
+
return m ? Math.max(max, parseInt(m[1], 10)) : max;
|
|
31456
|
+
}, 0);
|
|
31457
|
+
store4.counter = Math.max(store4.counter, maxExisting) + 1;
|
|
31458
|
+
return `t${store4.counter}`;
|
|
31459
|
+
}
|
|
31460
|
+
function writePlanTaskArtifact(rootDir, task) {
|
|
31461
|
+
const path53 = join15(rootDir, "plan-tasks", `${task.id}.md`);
|
|
31462
|
+
mkdirSync12(dirname3(path53), { recursive: true });
|
|
31463
|
+
const meta3 = {
|
|
31464
|
+
kind: "task",
|
|
31465
|
+
id: task.id,
|
|
31466
|
+
name: task.title,
|
|
31467
|
+
phaseId: task.phaseId,
|
|
31468
|
+
status: task.status,
|
|
31469
|
+
priority: task.priority ?? "medium",
|
|
31470
|
+
updatedAt: task.updatedAt
|
|
31471
|
+
};
|
|
31472
|
+
const body = [
|
|
31473
|
+
`# Task ${task.id}: ${task.title}`,
|
|
31474
|
+
"",
|
|
31475
|
+
`- Status: **${task.status}**`,
|
|
31476
|
+
`- Priority: ${task.priority ?? "medium"}`,
|
|
31477
|
+
task.phaseId ? `- Phase: ${task.phaseId}` : null,
|
|
31478
|
+
task.agent ? `- Agent: ${task.agent}` : null,
|
|
31479
|
+
"",
|
|
31480
|
+
task.notes?.trim() ? task.notes.trim() : "_(no notes)_",
|
|
31481
|
+
""
|
|
31482
|
+
].filter((l) => l !== null).join("\n");
|
|
31483
|
+
new Storage().write(path53, meta3, body);
|
|
31484
|
+
}
|
|
31485
|
+
function loadHandle(rootDir) {
|
|
31486
|
+
const jsonPath = join15(rootDir, "plan.json");
|
|
31487
|
+
if (!existsSync19(jsonPath)) {
|
|
31488
|
+
return { rootDir, tasks: [], counter: 0, rootFields: {} };
|
|
31489
|
+
}
|
|
31490
|
+
let parsed;
|
|
31491
|
+
try {
|
|
31492
|
+
const raw = JSON.parse(readFileSync17(jsonPath, "utf8"));
|
|
31493
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
31494
|
+
throw new Error("root is not a JSON object");
|
|
31495
|
+
}
|
|
31496
|
+
parsed = raw;
|
|
31497
|
+
} catch {
|
|
31498
|
+
throw new PlanStoreError(
|
|
31499
|
+
`PLAN_CORRUPT: ${jsonPath} is not valid JSON \u2014 refusing to overwrite a possibly hand-edited or crashed write. Fix or remove the file (a .bak may exist), then retry.`,
|
|
31500
|
+
"PLAN_CORRUPT"
|
|
31501
|
+
);
|
|
31502
|
+
}
|
|
31503
|
+
const {
|
|
31504
|
+
tasks: rawTasks,
|
|
31505
|
+
counter,
|
|
31506
|
+
schemaVersion: _schemaVersion,
|
|
31507
|
+
...rootFields
|
|
31508
|
+
} = parsed;
|
|
31509
|
+
const tasks = (Array.isArray(rawTasks) ? rawTasks : []).map(normalizeTask).filter((t) => typeof t.id === "string" && t.id.length > 0);
|
|
31510
|
+
const numericCounter = typeof counter === "number" && Number.isFinite(counter) && counter >= 0 ? Math.floor(counter) : 0;
|
|
31511
|
+
return { rootDir, tasks, counter: numericCounter, rootFields };
|
|
31512
|
+
}
|
|
31513
|
+
function saveHandle(rootDir, handle) {
|
|
31514
|
+
if (handle.tasks.length > PLAN_MAX_TASKS) {
|
|
31515
|
+
throw new PlanStoreError(
|
|
31516
|
+
`PLAN_TOO_MANY_TASKS: plan.json would exceed ${PLAN_MAX_TASKS} tasks (${handle.tasks.length}) \u2014 cancel or complete tasks first.`,
|
|
31517
|
+
"PLAN_TOO_MANY_TASKS"
|
|
31518
|
+
);
|
|
31519
|
+
}
|
|
31520
|
+
const jsonPath = join15(rootDir, "plan.json");
|
|
31521
|
+
mkdirSync12(rootDir, { recursive: true });
|
|
31522
|
+
if (existsSync19(jsonPath)) {
|
|
31523
|
+
copyFileSync(jsonPath, `${jsonPath}.bak`);
|
|
31524
|
+
}
|
|
31525
|
+
const file2 = {
|
|
31526
|
+
...handle.rootFields,
|
|
31527
|
+
schemaVersion: PLAN_SCHEMA_VERSION,
|
|
31528
|
+
counter: handle.counter,
|
|
31529
|
+
tasks: handle.tasks
|
|
31530
|
+
};
|
|
31531
|
+
const tmp = `${jsonPath}.tmp-${process.pid}`;
|
|
31532
|
+
writeFileSync14(tmp, JSON.stringify(file2, null, 2) + "\n", "utf8");
|
|
31533
|
+
renameSync3(tmp, jsonPath);
|
|
31534
|
+
}
|
|
31535
|
+
function normalizeTask(raw) {
|
|
31536
|
+
const t = raw !== null && typeof raw === "object" ? { ...raw } : {};
|
|
31537
|
+
if (typeof t.id === "string") {
|
|
31538
|
+
t.id = t.id.trim().slice(0, 64);
|
|
31539
|
+
}
|
|
31540
|
+
const titleSource = firstString(t.title) ?? firstString(t.name) ?? firstString(t.description) ?? "";
|
|
31541
|
+
t.title = titleSource.slice(0, PLAN_TITLE_MAX);
|
|
31542
|
+
if (t.title && !firstString(t.name)) {
|
|
31543
|
+
t.name = t.title;
|
|
31544
|
+
}
|
|
31545
|
+
t.status = normalizeStatus(t.status);
|
|
31546
|
+
if (typeof t.notes === "string") {
|
|
31547
|
+
t.notes = t.notes.slice(0, PLAN_NOTES_MAX);
|
|
31548
|
+
}
|
|
31549
|
+
if (typeof t.phaseId === "string") {
|
|
31550
|
+
t.phaseId = t.phaseId.slice(0, PLAN_TAG_MAX);
|
|
31551
|
+
}
|
|
31552
|
+
if (typeof t.agent === "string") {
|
|
31553
|
+
t.agent = t.agent.slice(0, PLAN_TAG_MAX);
|
|
31554
|
+
}
|
|
31555
|
+
return t;
|
|
31556
|
+
}
|
|
31557
|
+
function normalizeStatus(raw) {
|
|
31558
|
+
const s = typeof raw === "string" ? raw.trim().toLowerCase() : "";
|
|
31559
|
+
switch (s) {
|
|
31560
|
+
case "in_progress":
|
|
31561
|
+
case "in-progress":
|
|
31562
|
+
case "doing":
|
|
31563
|
+
case "started":
|
|
31564
|
+
case "active":
|
|
31565
|
+
return "in_progress";
|
|
31566
|
+
case "completed":
|
|
31567
|
+
case "complete":
|
|
31568
|
+
case "done":
|
|
31569
|
+
case "finished":
|
|
31570
|
+
return "completed";
|
|
31571
|
+
case "cancelled":
|
|
31572
|
+
case "canceled":
|
|
31573
|
+
case "closed":
|
|
31574
|
+
return "cancelled";
|
|
31575
|
+
case "blocked":
|
|
31576
|
+
case "on-hold":
|
|
31577
|
+
case "on hold":
|
|
31578
|
+
return "blocked";
|
|
31579
|
+
default:
|
|
31580
|
+
return "pending";
|
|
31581
|
+
}
|
|
31582
|
+
}
|
|
31583
|
+
function firstString(v) {
|
|
31584
|
+
return typeof v === "string" && v.trim().length > 0 ? v : null;
|
|
31585
|
+
}
|
|
31586
|
+
var PLAN_SCHEMA_VERSION, PLAN_MAX_TASKS, PLAN_TITLE_MAX, PLAN_NOTES_MAX, PLAN_TAG_MAX, PlanStoreError;
|
|
31587
|
+
var init_planStore = __esm({
|
|
31588
|
+
"src/cli/workspace/planStore.ts"() {
|
|
31589
|
+
"use strict";
|
|
31590
|
+
init_paths2();
|
|
31591
|
+
init_storage();
|
|
31592
|
+
PLAN_SCHEMA_VERSION = 1;
|
|
31593
|
+
PLAN_MAX_TASKS = 100;
|
|
31594
|
+
PLAN_TITLE_MAX = 200;
|
|
31595
|
+
PLAN_NOTES_MAX = 2e3;
|
|
31596
|
+
PLAN_TAG_MAX = 64;
|
|
31597
|
+
PlanStoreError = class extends Error {
|
|
31598
|
+
constructor(message, code) {
|
|
31599
|
+
super(message);
|
|
31600
|
+
this.code = code;
|
|
31601
|
+
this.name = "PlanStoreError";
|
|
31602
|
+
}
|
|
31603
|
+
};
|
|
31604
|
+
}
|
|
31605
|
+
});
|
|
31606
|
+
|
|
31607
|
+
// src/cli/tools/planTaskTools.ts
|
|
31608
|
+
function taskSummaryLine(t) {
|
|
31609
|
+
return `- ${t.id}: ${t.title} (${t.status}${t.priority ? `, ${t.priority}` : ""})`;
|
|
31610
|
+
}
|
|
31611
|
+
function toTaskPayload(t) {
|
|
31612
|
+
return {
|
|
31613
|
+
id: t.id,
|
|
31614
|
+
title: t.title,
|
|
31615
|
+
status: t.status,
|
|
31616
|
+
phaseId: t.phaseId,
|
|
31617
|
+
priority: t.priority
|
|
31618
|
+
};
|
|
31619
|
+
}
|
|
31620
|
+
function safeEmit(sink, event) {
|
|
31621
|
+
if (!sink) return;
|
|
31622
|
+
try {
|
|
31623
|
+
sink(event);
|
|
31624
|
+
} catch {
|
|
31625
|
+
}
|
|
31626
|
+
}
|
|
31627
|
+
function createPlanTaskTools(opts) {
|
|
31628
|
+
const projectRoot = opts.projectRoot;
|
|
31629
|
+
const onTaskEvent = opts.onTaskEvent;
|
|
31630
|
+
const taskCreate = {
|
|
31631
|
+
name: "task_create",
|
|
31632
|
+
description: "Create a durable workspace task in .zelari/plan.json (multi-session, shared with the Desktop Live Tasks panel). Use for project work that must survive this session. For volatile per-session tracking use todo_write instead. Returns the assigned id (t<N>).",
|
|
31633
|
+
permissions: ["write"],
|
|
31634
|
+
timeoutMs: 5e3,
|
|
31635
|
+
inputSchema: CreateSchema,
|
|
31636
|
+
execute: async (input) => {
|
|
31637
|
+
try {
|
|
31638
|
+
const res = await withPlanStore(projectRoot, (store4) => {
|
|
31639
|
+
if (store4.tasks.length >= PLAN_MAX_TASKS) {
|
|
31640
|
+
return typedErr(
|
|
31641
|
+
`PLAN_TOO_MANY_TASKS: plan.json already holds ${store4.tasks.length} tasks (max ${PLAN_MAX_TASKS}).`
|
|
31642
|
+
);
|
|
31643
|
+
}
|
|
31644
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
31645
|
+
const id = nextPlanTaskId(store4);
|
|
31646
|
+
const task = {
|
|
31647
|
+
id,
|
|
31648
|
+
title: input.title.trim().slice(0, PLAN_TITLE_MAX),
|
|
31649
|
+
// council readers (buildPlanSummary) render `name` — keep the
|
|
31650
|
+
// alias in sync from creation, not just on reload.
|
|
31651
|
+
name: input.title.trim().slice(0, PLAN_TITLE_MAX),
|
|
31652
|
+
status: "pending",
|
|
31653
|
+
priority: input.priority,
|
|
31654
|
+
phaseId: input.phaseId?.trim().slice(0, 64),
|
|
31655
|
+
notes: input.notes?.trim().slice(0, PLAN_NOTES_MAX),
|
|
31656
|
+
createdAt: now,
|
|
31657
|
+
updatedAt: now
|
|
31658
|
+
};
|
|
31659
|
+
store4.tasks.push(task);
|
|
31660
|
+
writePlanTaskArtifact(store4.rootDir, task);
|
|
31661
|
+
return typedOk({ id, task });
|
|
31662
|
+
});
|
|
31663
|
+
if (res.ok) {
|
|
31664
|
+
safeEmit(onTaskEvent, {
|
|
31665
|
+
type: "task_update",
|
|
31666
|
+
source: "workspace_plan",
|
|
31667
|
+
task: toTaskPayload(res.value.task)
|
|
31668
|
+
});
|
|
31669
|
+
}
|
|
31670
|
+
return res;
|
|
31671
|
+
} catch (err) {
|
|
31672
|
+
return typedErr(planStoreErrorMessage(err, "task_create"));
|
|
31673
|
+
}
|
|
31674
|
+
}
|
|
31675
|
+
};
|
|
31676
|
+
const taskUpdate = {
|
|
31677
|
+
name: "task_update",
|
|
31678
|
+
description: "Update a durable workspace task in .zelari/plan.json (status, title, priority, phaseId, notes, or appendNote). Accepts council-created ids too (task_list shows them). Errors with PLAN_TASK_NOT_FOUND on unknown ids.",
|
|
31679
|
+
permissions: ["write"],
|
|
31680
|
+
timeoutMs: 5e3,
|
|
31681
|
+
inputSchema: UpdateSchema,
|
|
31682
|
+
execute: async (input) => {
|
|
31683
|
+
try {
|
|
31684
|
+
const res = await withPlanStore(projectRoot, (store4) => {
|
|
31685
|
+
const task = store4.tasks.find((t) => t.id === input.id);
|
|
31686
|
+
if (!task) {
|
|
31687
|
+
return typedErr(
|
|
31688
|
+
`PLAN_TASK_NOT_FOUND: no task with id "${input.id}" in .zelari/plan.json (call task_list for current ids).`
|
|
31689
|
+
);
|
|
31690
|
+
}
|
|
31691
|
+
if (input.title !== void 0) {
|
|
31692
|
+
task.title = input.title.trim().slice(0, PLAN_TITLE_MAX);
|
|
31693
|
+
task.name = task.title;
|
|
31694
|
+
}
|
|
31695
|
+
if (input.status !== void 0) task.status = input.status;
|
|
31696
|
+
if (input.priority !== void 0) task.priority = input.priority;
|
|
31697
|
+
if (input.phaseId !== void 0) task.phaseId = input.phaseId.slice(0, 64);
|
|
31698
|
+
if (input.notes !== void 0) {
|
|
31699
|
+
task.notes = input.notes.slice(0, PLAN_NOTES_MAX);
|
|
31700
|
+
}
|
|
31701
|
+
if (input.appendNote !== void 0) {
|
|
31702
|
+
const prev2 = typeof task.notes === "string" ? task.notes : "";
|
|
31703
|
+
const merged = prev2 ? `${prev2}
|
|
31704
|
+
${input.appendNote}` : input.appendNote;
|
|
31705
|
+
task.notes = merged.slice(-PLAN_NOTES_MAX);
|
|
31706
|
+
}
|
|
31707
|
+
task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
31708
|
+
writePlanTaskArtifact(store4.rootDir, task);
|
|
31709
|
+
return typedOk({ task });
|
|
31710
|
+
});
|
|
31711
|
+
if (res.ok) {
|
|
31712
|
+
safeEmit(onTaskEvent, {
|
|
31713
|
+
type: "task_update",
|
|
31714
|
+
source: "workspace_plan",
|
|
31715
|
+
task: toTaskPayload(res.value.task)
|
|
31716
|
+
});
|
|
31717
|
+
}
|
|
31718
|
+
return res;
|
|
31719
|
+
} catch (err) {
|
|
31720
|
+
return typedErr(planStoreErrorMessage(err, "task_update"));
|
|
31721
|
+
}
|
|
31722
|
+
}
|
|
31723
|
+
};
|
|
31724
|
+
const taskList = {
|
|
31725
|
+
name: "task_list",
|
|
31726
|
+
description: "List durable workspace tasks from .zelari/plan.json (both t<N> agent tasks and council-created plan tasks). Optional status/phaseId filters. Use before task_update to discover ids.",
|
|
31727
|
+
permissions: ["read"],
|
|
31728
|
+
timeoutMs: 5e3,
|
|
31729
|
+
inputSchema: ListSchema,
|
|
31730
|
+
execute: async (input) => {
|
|
31731
|
+
try {
|
|
31732
|
+
let allPayloads = [];
|
|
31733
|
+
const res = await withPlanStore(projectRoot, (store4) => {
|
|
31734
|
+
allPayloads = store4.tasks.map(toTaskPayload);
|
|
31735
|
+
const filtered = store4.tasks.filter(
|
|
31736
|
+
(t) => (input.status === void 0 || t.status === input.status) && (input.phaseId === void 0 || t.phaseId === input.phaseId)
|
|
31737
|
+
);
|
|
31738
|
+
const done = store4.tasks.filter(
|
|
31739
|
+
(t) => t.status === "completed" || t.status === "cancelled"
|
|
31740
|
+
).length;
|
|
31741
|
+
const formatted = filtered.length === 0 ? "(no matching workspace tasks)" : filtered.map(taskSummaryLine).join("\n");
|
|
31742
|
+
return typedOk({
|
|
31743
|
+
tasks: filtered,
|
|
31744
|
+
total: store4.tasks.length,
|
|
31745
|
+
done,
|
|
31746
|
+
formatted: `${formatted}
|
|
31747
|
+
(done/total: ${done}/${store4.tasks.length})`
|
|
31748
|
+
});
|
|
31749
|
+
});
|
|
31750
|
+
if (res.ok) {
|
|
31751
|
+
safeEmit(onTaskEvent, {
|
|
31752
|
+
type: "task_snapshot",
|
|
31753
|
+
source: "workspace_plan",
|
|
31754
|
+
tasks: allPayloads
|
|
31755
|
+
});
|
|
31756
|
+
}
|
|
31757
|
+
return res;
|
|
31758
|
+
} catch (err) {
|
|
31759
|
+
return typedErr(planStoreErrorMessage(err, "task_list"));
|
|
31760
|
+
}
|
|
31761
|
+
}
|
|
31762
|
+
};
|
|
31763
|
+
return [taskCreate, taskUpdate, taskList];
|
|
31764
|
+
}
|
|
31765
|
+
function planStoreErrorMessage(err, tool) {
|
|
31766
|
+
if (err instanceof PlanStoreError) return err.message;
|
|
31767
|
+
return `[${tool}] ${err instanceof Error ? err.message : String(err)}`;
|
|
31768
|
+
}
|
|
31769
|
+
var StatusSchema2, PrioritySchema, CreateSchema, UpdateSchema, ListSchema;
|
|
31770
|
+
var init_planTaskTools = __esm({
|
|
31771
|
+
"src/cli/tools/planTaskTools.ts"() {
|
|
31772
|
+
"use strict";
|
|
31773
|
+
init_zod();
|
|
31774
|
+
init_toolTypes();
|
|
31775
|
+
init_planStore();
|
|
31776
|
+
StatusSchema2 = external_exports.enum(["pending", "in_progress", "completed", "cancelled", "blocked"]).describe(
|
|
31777
|
+
"blocked exists only here (session todos have no blocked); no rigid FSM \u2014 corrections like completed \u2192 in_progress are allowed"
|
|
31778
|
+
);
|
|
31779
|
+
PrioritySchema = external_exports.enum(["low", "medium", "high", "critical"]);
|
|
31780
|
+
CreateSchema = external_exports.object({
|
|
31781
|
+
title: external_exports.string().min(1).max(PLAN_TITLE_MAX).describe("Short task description"),
|
|
31782
|
+
priority: PrioritySchema.optional().describe("Default medium"),
|
|
31783
|
+
phaseId: external_exports.string().min(1).max(64).optional().describe("Existing plan phase id (see .zelari/plan.json phases)"),
|
|
31784
|
+
notes: external_exports.string().max(PLAN_NOTES_MAX).optional().describe("Optional context/acceptance notes")
|
|
31785
|
+
});
|
|
31786
|
+
UpdateSchema = external_exports.object({
|
|
31787
|
+
id: external_exports.string().min(1).max(64).describe("Task id from task_create/task_list"),
|
|
31788
|
+
status: StatusSchema2.optional(),
|
|
31789
|
+
title: external_exports.string().min(1).max(PLAN_TITLE_MAX).optional(),
|
|
31790
|
+
priority: PrioritySchema.optional(),
|
|
31791
|
+
phaseId: external_exports.string().min(1).max(64).optional(),
|
|
31792
|
+
notes: external_exports.string().max(PLAN_NOTES_MAX).optional(),
|
|
31793
|
+
appendNote: external_exports.string().max(PLAN_NOTES_MAX).optional().describe("Append to existing notes (kept within the size cap)")
|
|
31794
|
+
}).describe("At least one field besides id must be present");
|
|
31795
|
+
ListSchema = external_exports.object({
|
|
31796
|
+
status: StatusSchema2.optional().describe("Filter by status"),
|
|
31797
|
+
phaseId: external_exports.string().min(1).max(64).optional().describe("Filter by phase")
|
|
31798
|
+
});
|
|
31799
|
+
}
|
|
31800
|
+
});
|
|
31801
|
+
|
|
30921
31802
|
// src/cli/lsp/protocol.ts
|
|
30922
31803
|
function encodeMessage(message) {
|
|
30923
31804
|
const json2 = JSON.stringify(message);
|
|
@@ -31221,7 +32102,7 @@ var init_servers = __esm({
|
|
|
31221
32102
|
|
|
31222
32103
|
// src/cli/lsp/manager.ts
|
|
31223
32104
|
import { spawn as spawn4 } from "node:child_process";
|
|
31224
|
-
import { readFileSync as
|
|
32105
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
31225
32106
|
function processTransport(child) {
|
|
31226
32107
|
return {
|
|
31227
32108
|
send: (data) => {
|
|
@@ -31424,7 +32305,7 @@ var init_manager = __esm({
|
|
|
31424
32305
|
const uri = pathToUri(file2);
|
|
31425
32306
|
let text;
|
|
31426
32307
|
try {
|
|
31427
|
-
text =
|
|
32308
|
+
text = readFileSync18(file2, "utf8");
|
|
31428
32309
|
} catch {
|
|
31429
32310
|
text = "";
|
|
31430
32311
|
}
|
|
@@ -31746,13 +32627,13 @@ var init_store = __esm({
|
|
|
31746
32627
|
});
|
|
31747
32628
|
|
|
31748
32629
|
// src/cli/semantic/index.ts
|
|
31749
|
-
import { promises as fs12, existsSync as
|
|
31750
|
-
import { homedir as
|
|
32630
|
+
import { promises as fs12, existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
|
|
32631
|
+
import { homedir as homedir6 } from "node:os";
|
|
31751
32632
|
import path23 from "node:path";
|
|
31752
|
-
import { createHash as
|
|
32633
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
31753
32634
|
function getIndexPath(root) {
|
|
31754
|
-
const hash3 =
|
|
31755
|
-
return process.env.ZELARI_SEMANTIC_FILE ?? path23.join(
|
|
32635
|
+
const hash3 = createHash5("sha1").update(path23.resolve(root)).digest("hex").slice(0, 16);
|
|
32636
|
+
return process.env.ZELARI_SEMANTIC_FILE ?? path23.join(homedir6(), ".tmp", "zelari-code", "semantic", `${hash3}.json`);
|
|
31756
32637
|
}
|
|
31757
32638
|
async function collectSourceFiles(root, maxFiles = 1500) {
|
|
31758
32639
|
const out = [];
|
|
@@ -31830,9 +32711,9 @@ async function saveIndex(root, data) {
|
|
|
31830
32711
|
}
|
|
31831
32712
|
function loadIndex(root) {
|
|
31832
32713
|
const file2 = getIndexPath(root);
|
|
31833
|
-
if (!
|
|
32714
|
+
if (!existsSync20(file2)) return null;
|
|
31834
32715
|
try {
|
|
31835
|
-
const parsed = JSON.parse(
|
|
32716
|
+
const parsed = JSON.parse(readFileSync19(file2, "utf8"));
|
|
31836
32717
|
if (parsed && Array.isArray(parsed.chunks)) return parsed;
|
|
31837
32718
|
} catch {
|
|
31838
32719
|
}
|
|
@@ -32362,19 +33243,19 @@ __export(targets_exports, {
|
|
|
32362
33243
|
});
|
|
32363
33244
|
import {
|
|
32364
33245
|
chmodSync,
|
|
32365
|
-
existsSync as
|
|
32366
|
-
mkdirSync as
|
|
32367
|
-
readFileSync as
|
|
32368
|
-
writeFileSync as
|
|
33246
|
+
existsSync as existsSync21,
|
|
33247
|
+
mkdirSync as mkdirSync13,
|
|
33248
|
+
readFileSync as readFileSync20,
|
|
33249
|
+
writeFileSync as writeFileSync15
|
|
32369
33250
|
} from "node:fs";
|
|
32370
|
-
import { dirname as
|
|
32371
|
-
import { homedir as
|
|
33251
|
+
import { dirname as dirname4, join as join16 } from "node:path";
|
|
33252
|
+
import { homedir as homedir7 } from "node:os";
|
|
32372
33253
|
import { spawn as spawn5 } from "node:child_process";
|
|
32373
33254
|
function getSshTargetsPath() {
|
|
32374
|
-
return
|
|
33255
|
+
return join16(homedir7(), ".zelari-code", "ssh-targets.json");
|
|
32375
33256
|
}
|
|
32376
33257
|
function getSshSecretsPath() {
|
|
32377
|
-
return
|
|
33258
|
+
return join16(homedir7(), ".zelari-code", "ssh-secrets.json");
|
|
32378
33259
|
}
|
|
32379
33260
|
function normalizeAuth(auth) {
|
|
32380
33261
|
if (auth === "keyPath") return "keyPath";
|
|
@@ -32383,17 +33264,17 @@ function normalizeAuth(auth) {
|
|
|
32383
33264
|
}
|
|
32384
33265
|
function readSecrets() {
|
|
32385
33266
|
const path53 = getSshSecretsPath();
|
|
32386
|
-
if (!
|
|
33267
|
+
if (!existsSync21(path53)) return {};
|
|
32387
33268
|
try {
|
|
32388
|
-
return JSON.parse(
|
|
33269
|
+
return JSON.parse(readFileSync20(path53, "utf8"));
|
|
32389
33270
|
} catch {
|
|
32390
33271
|
return {};
|
|
32391
33272
|
}
|
|
32392
33273
|
}
|
|
32393
33274
|
function writeSecrets(data) {
|
|
32394
33275
|
const path53 = getSshSecretsPath();
|
|
32395
|
-
|
|
32396
|
-
|
|
33276
|
+
mkdirSync13(dirname4(path53), { recursive: true });
|
|
33277
|
+
writeFileSync15(path53, `${JSON.stringify(data, null, 2)}
|
|
32397
33278
|
`, "utf8");
|
|
32398
33279
|
try {
|
|
32399
33280
|
chmodSync(path53, 384);
|
|
@@ -32426,9 +33307,9 @@ function deleteSshPassword(id) {
|
|
|
32426
33307
|
}
|
|
32427
33308
|
function readStore2() {
|
|
32428
33309
|
const path53 = getSshTargetsPath();
|
|
32429
|
-
if (!
|
|
33310
|
+
if (!existsSync21(path53)) return [];
|
|
32430
33311
|
try {
|
|
32431
|
-
const parsed = JSON.parse(
|
|
33312
|
+
const parsed = JSON.parse(readFileSync20(path53, "utf8"));
|
|
32432
33313
|
const list = Array.isArray(parsed.targets) ? parsed.targets : [];
|
|
32433
33314
|
return list.filter(
|
|
32434
33315
|
(t) => t && typeof t.id === "string" && typeof t.host === "string" && typeof t.user === "string"
|
|
@@ -32444,9 +33325,9 @@ function readStore2() {
|
|
|
32444
33325
|
}
|
|
32445
33326
|
function writeStore2(targets) {
|
|
32446
33327
|
const path53 = getSshTargetsPath();
|
|
32447
|
-
|
|
33328
|
+
mkdirSync13(dirname4(path53), { recursive: true });
|
|
32448
33329
|
const clean = targets.map(({ hasPassword: _hp, ...t }) => t);
|
|
32449
|
-
|
|
33330
|
+
writeFileSync15(
|
|
32450
33331
|
path53,
|
|
32451
33332
|
`${JSON.stringify({ targets: clean }, null, 2)}
|
|
32452
33333
|
`,
|
|
@@ -32547,17 +33428,17 @@ function buildSshBaseArgs(target) {
|
|
|
32547
33428
|
return args;
|
|
32548
33429
|
}
|
|
32549
33430
|
function ensureAskpassHelper() {
|
|
32550
|
-
const dir =
|
|
32551
|
-
|
|
32552
|
-
const cjs =
|
|
32553
|
-
|
|
33431
|
+
const dir = join16(homedir7(), ".zelari-code", "ssh-helpers");
|
|
33432
|
+
mkdirSync13(dir, { recursive: true });
|
|
33433
|
+
const cjs = join16(dir, "askpass.cjs");
|
|
33434
|
+
writeFileSync15(
|
|
32554
33435
|
cjs,
|
|
32555
33436
|
"process.stdout.write(process.env.ZELARI_SSH_ASKPASS_PASS || '');\n",
|
|
32556
33437
|
"utf8"
|
|
32557
33438
|
);
|
|
32558
33439
|
if (process.platform === "win32") {
|
|
32559
|
-
const cmd =
|
|
32560
|
-
|
|
33440
|
+
const cmd = join16(dir, "askpass.cmd");
|
|
33441
|
+
writeFileSync15(
|
|
32561
33442
|
cmd,
|
|
32562
33443
|
`@echo off\r
|
|
32563
33444
|
node "%~dp0askpass.cjs"\r
|
|
@@ -32566,8 +33447,8 @@ node "%~dp0askpass.cjs"\r
|
|
|
32566
33447
|
);
|
|
32567
33448
|
return cmd;
|
|
32568
33449
|
}
|
|
32569
|
-
const sh =
|
|
32570
|
-
|
|
33450
|
+
const sh = join16(dir, "askpass.sh");
|
|
33451
|
+
writeFileSync15(
|
|
32571
33452
|
sh,
|
|
32572
33453
|
`#!/bin/sh
|
|
32573
33454
|
exec node "$(dirname "$0")/askpass.cjs"
|
|
@@ -32640,9 +33521,9 @@ function readSshPublicKey(keyOrPubPath) {
|
|
|
32640
33521
|
if (!raw) return { ok: false, error: "Empty path" };
|
|
32641
33522
|
const candidates = raw.endsWith(".pub") ? [raw] : [`${raw}.pub`, raw];
|
|
32642
33523
|
for (const p3 of candidates) {
|
|
32643
|
-
if (!
|
|
33524
|
+
if (!existsSync21(p3)) continue;
|
|
32644
33525
|
try {
|
|
32645
|
-
const content =
|
|
33526
|
+
const content = readFileSync20(p3, "utf8").trim();
|
|
32646
33527
|
if (!content) continue;
|
|
32647
33528
|
if (/BEGIN .*PRIVATE KEY/i.test(content)) {
|
|
32648
33529
|
return {
|
|
@@ -33212,11 +34093,11 @@ __export(folderTrust_exports, {
|
|
|
33212
34093
|
trustFolder: () => trustFolder,
|
|
33213
34094
|
untrustFolder: () => untrustFolder
|
|
33214
34095
|
});
|
|
33215
|
-
import { homedir as
|
|
33216
|
-
import { existsSync as
|
|
34096
|
+
import { homedir as homedir8 } from "node:os";
|
|
34097
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "node:fs";
|
|
33217
34098
|
import path28 from "node:path";
|
|
33218
34099
|
function trustStorePath() {
|
|
33219
|
-
return _overrideStorePath ?? path28.join(
|
|
34100
|
+
return _overrideStorePath ?? path28.join(homedir8(), ".zelari-code", "trust.json");
|
|
33220
34101
|
}
|
|
33221
34102
|
function normalize(p3) {
|
|
33222
34103
|
const resolved = path28.resolve(p3);
|
|
@@ -33224,7 +34105,7 @@ function normalize(p3) {
|
|
|
33224
34105
|
}
|
|
33225
34106
|
function readStore3() {
|
|
33226
34107
|
try {
|
|
33227
|
-
const raw =
|
|
34108
|
+
const raw = readFileSync21(trustStorePath(), "utf8");
|
|
33228
34109
|
const parsed = JSON.parse(raw);
|
|
33229
34110
|
if (parsed && Array.isArray(parsed.folders)) return parsed;
|
|
33230
34111
|
return DEFAULT_STORE;
|
|
@@ -33232,11 +34113,11 @@ function readStore3() {
|
|
|
33232
34113
|
return DEFAULT_STORE;
|
|
33233
34114
|
}
|
|
33234
34115
|
}
|
|
33235
|
-
function writeStore3(
|
|
34116
|
+
function writeStore3(store4) {
|
|
33236
34117
|
const p3 = trustStorePath();
|
|
33237
34118
|
try {
|
|
33238
|
-
|
|
33239
|
-
|
|
34119
|
+
mkdirSync14(path28.dirname(p3), { recursive: true });
|
|
34120
|
+
writeFileSync16(p3, JSON.stringify(store4, null, 2), "utf8");
|
|
33240
34121
|
} catch (err) {
|
|
33241
34122
|
throw new Error(
|
|
33242
34123
|
`failed to persist trust store ${p3}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -33260,21 +34141,21 @@ function isFolderTrusted(folderPath) {
|
|
|
33260
34141
|
return readStore3().folders.some((f) => normalize(f.path) === target);
|
|
33261
34142
|
}
|
|
33262
34143
|
function trustFolder(folderPath) {
|
|
33263
|
-
const
|
|
34144
|
+
const store4 = readStore3();
|
|
33264
34145
|
const normalized = path28.resolve(folderPath);
|
|
33265
|
-
if (!
|
|
33266
|
-
|
|
33267
|
-
writeStore3(
|
|
34146
|
+
if (!store4.folders.some((f) => normalize(f.path) === normalize(normalized))) {
|
|
34147
|
+
store4.folders.push({ path: normalized, trustedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
34148
|
+
writeStore3(store4);
|
|
33268
34149
|
}
|
|
33269
34150
|
return { ok: true, path: normalized };
|
|
33270
34151
|
}
|
|
33271
34152
|
function untrustFolder(folderPath) {
|
|
33272
|
-
const
|
|
34153
|
+
const store4 = readStore3();
|
|
33273
34154
|
const target = normalize(folderPath);
|
|
33274
|
-
const before =
|
|
33275
|
-
|
|
33276
|
-
if (
|
|
33277
|
-
writeStore3(
|
|
34155
|
+
const before = store4.folders.length;
|
|
34156
|
+
store4.folders = store4.folders.filter((f) => normalize(f.path) !== target);
|
|
34157
|
+
if (store4.folders.length === before) return { ok: true, removed: false };
|
|
34158
|
+
writeStore3(store4);
|
|
33278
34159
|
return { ok: true, removed: true };
|
|
33279
34160
|
}
|
|
33280
34161
|
function listTrustedFolders() {
|
|
@@ -33291,7 +34172,7 @@ function getTrustStorePath() {
|
|
|
33291
34172
|
return trustStorePath();
|
|
33292
34173
|
}
|
|
33293
34174
|
function hasTrustStore() {
|
|
33294
|
-
return
|
|
34175
|
+
return existsSync22(trustStorePath());
|
|
33295
34176
|
}
|
|
33296
34177
|
function _setTrustStorePathForTests(p3) {
|
|
33297
34178
|
_overrideStorePath = p3;
|
|
@@ -33306,21 +34187,21 @@ var init_folderTrust = __esm({
|
|
|
33306
34187
|
});
|
|
33307
34188
|
|
|
33308
34189
|
// src/cli/safety/lifecycleHooks.ts
|
|
33309
|
-
import { homedir as
|
|
33310
|
-
import { join as
|
|
33311
|
-
import { readdirSync as
|
|
34190
|
+
import { homedir as homedir9 } from "node:os";
|
|
34191
|
+
import { join as join17 } from "node:path";
|
|
34192
|
+
import { readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
|
|
33312
34193
|
function globalHooksDir() {
|
|
33313
|
-
return
|
|
34194
|
+
return join17(homedir9(), ".zelari-code", "hooks");
|
|
33314
34195
|
}
|
|
33315
34196
|
function projectHooksDir(projectRoot) {
|
|
33316
|
-
return
|
|
34197
|
+
return join17(projectRoot, ".zelari", "hooks");
|
|
33317
34198
|
}
|
|
33318
34199
|
function fingerprintHookDirs(dirs) {
|
|
33319
34200
|
const parts = [];
|
|
33320
34201
|
for (const dir of dirs) {
|
|
33321
34202
|
let names;
|
|
33322
34203
|
try {
|
|
33323
|
-
names =
|
|
34204
|
+
names = readdirSync5(dir).filter((f) => f.endsWith(".json")).sort();
|
|
33324
34205
|
} catch {
|
|
33325
34206
|
parts.push(`${dir}:missing`);
|
|
33326
34207
|
continue;
|
|
@@ -33334,7 +34215,7 @@ function fingerprintHookDirs(dirs) {
|
|
|
33334
34215
|
continue;
|
|
33335
34216
|
}
|
|
33336
34217
|
for (const name of names) {
|
|
33337
|
-
const full =
|
|
34218
|
+
const full = join17(dir, name);
|
|
33338
34219
|
try {
|
|
33339
34220
|
const st = statSync3(full);
|
|
33340
34221
|
parts.push(`${full}:${st.mtimeMs}:${st.size}`);
|
|
@@ -33375,7 +34256,7 @@ var init_lifecycleHooks = __esm({
|
|
|
33375
34256
|
});
|
|
33376
34257
|
|
|
33377
34258
|
// src/cli/toolResultCache.ts
|
|
33378
|
-
import { createHash as
|
|
34259
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
33379
34260
|
import { promises as fs14 } from "node:fs";
|
|
33380
34261
|
import path29 from "node:path";
|
|
33381
34262
|
function isToolCacheEnabled() {
|
|
@@ -33388,7 +34269,7 @@ function resolveToolCacheTtlMs() {
|
|
|
33388
34269
|
return Number.isFinite(n) && n >= 0 ? n : TOOL_CACHE_DEFAULT_TTL_MS;
|
|
33389
34270
|
}
|
|
33390
34271
|
function hashKey(parts) {
|
|
33391
|
-
return
|
|
34272
|
+
return createHash6("sha256").update(JSON.stringify(parts), "utf8").digest("hex");
|
|
33392
34273
|
}
|
|
33393
34274
|
function resultBytes(result) {
|
|
33394
34275
|
try {
|
|
@@ -33678,6 +34559,11 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
33678
34559
|
const todoRead = enableTodos ? withPerm(createTodoReadTool()) : null;
|
|
33679
34560
|
if (todoWrite) registry4.register(todoWrite);
|
|
33680
34561
|
if (todoRead) registry4.register(todoRead);
|
|
34562
|
+
const enablePlanTasks = options.enablePlanTasks !== false && options.readOnly !== true && (profile === "full" || options.planMode === true) && profile !== "explore" && profile !== "verify" && profile !== "general";
|
|
34563
|
+
const planTaskToolsWrapped = (enablePlanTasks ? createPlanTaskTools({ projectRoot: root, onTaskEvent: options.onTaskEvent }) : []).map((t) => withPerm(t));
|
|
34564
|
+
for (const t of planTaskToolsWrapped) {
|
|
34565
|
+
registry4.register(t);
|
|
34566
|
+
}
|
|
33681
34567
|
const summary = [
|
|
33682
34568
|
safeReadFile,
|
|
33683
34569
|
safeGrepContent,
|
|
@@ -33690,7 +34576,8 @@ function createBuiltinToolRegistry(options = {}) {
|
|
|
33690
34576
|
...askUserTool ? [askUserTool] : [],
|
|
33691
34577
|
...skillTool ? [skillTool] : [],
|
|
33692
34578
|
...todoWrite ? [todoWrite] : [],
|
|
33693
|
-
...todoRead ? [todoRead] : []
|
|
34579
|
+
...todoRead ? [todoRead] : [],
|
|
34580
|
+
...planTaskToolsWrapped.length > 0 ? planTaskToolsWrapped : []
|
|
33694
34581
|
];
|
|
33695
34582
|
const tools = summary.map((t) => ({
|
|
33696
34583
|
name: t.name,
|
|
@@ -34039,6 +34926,7 @@ var init_toolRegistry = __esm({
|
|
|
34039
34926
|
init_askUser();
|
|
34040
34927
|
init_skillTool();
|
|
34041
34928
|
init_todoTools();
|
|
34929
|
+
init_planTaskTools();
|
|
34042
34930
|
init_tools2();
|
|
34043
34931
|
init_manager();
|
|
34044
34932
|
init_tools3();
|
|
@@ -34069,7 +34957,7 @@ var init_toolRegistry = __esm({
|
|
|
34069
34957
|
});
|
|
34070
34958
|
|
|
34071
34959
|
// src/cli/state/fileStateStore.ts
|
|
34072
|
-
import { createHash as
|
|
34960
|
+
import { createHash as createHash7, randomUUID as randomUUID2 } from "node:crypto";
|
|
34073
34961
|
import { promises as fs15 } from "node:fs";
|
|
34074
34962
|
import * as path30 from "node:path";
|
|
34075
34963
|
function shortId() {
|
|
@@ -34112,16 +35000,16 @@ function isStateEnabled(env = process.env) {
|
|
|
34112
35000
|
}
|
|
34113
35001
|
async function getStateStore(projectRoot, env = process.env) {
|
|
34114
35002
|
if (!isStateEnabled(env)) return new NoopDurableStateStore();
|
|
34115
|
-
const
|
|
35003
|
+
const store4 = new FileDurableStateStore();
|
|
34116
35004
|
try {
|
|
34117
|
-
await
|
|
34118
|
-
return
|
|
35005
|
+
await store4.init(projectRoot);
|
|
35006
|
+
return store4;
|
|
34119
35007
|
} catch {
|
|
34120
35008
|
return new NoopDurableStateStore();
|
|
34121
35009
|
}
|
|
34122
35010
|
}
|
|
34123
35011
|
function hashStablePrompt(stable) {
|
|
34124
|
-
return
|
|
35012
|
+
return createHash7("sha256").update(stable, "utf8").digest("hex").slice(0, 16);
|
|
34125
35013
|
}
|
|
34126
35014
|
var DEFAULT_MATERIALIZE_CHARS, FileDurableStateStore, NoopDurableStateStore;
|
|
34127
35015
|
var init_fileStateStore = __esm({
|
|
@@ -34452,29 +35340,6 @@ function extractiveHistorySummary(dropped, opts) {
|
|
|
34452
35340
|
}
|
|
34453
35341
|
return out;
|
|
34454
35342
|
}
|
|
34455
|
-
function formatDroppedForLlm(dropped) {
|
|
34456
|
-
const lines = [];
|
|
34457
|
-
for (const m of dropped) {
|
|
34458
|
-
if (m.role === "user") {
|
|
34459
|
-
lines.push(`USER: ${oneLine(m.content, 400)}`);
|
|
34460
|
-
} else if (m.role === "assistant") {
|
|
34461
|
-
const tools = m.toolCalls?.map((t) => t.name).join(",") || "";
|
|
34462
|
-
const body2 = oneLine(m.content, 300);
|
|
34463
|
-
lines.push(
|
|
34464
|
-
tools ? `ASSISTANT(tools=${tools}): ${body2}` : `ASSISTANT: ${body2}`
|
|
34465
|
-
);
|
|
34466
|
-
} else if (m.role === "tool") {
|
|
34467
|
-
lines.push(`TOOL(${m.toolCallId ?? "?"}): ${oneLine(m.content, 160)}`);
|
|
34468
|
-
} else if (m.role === "system") {
|
|
34469
|
-
lines.push(`SYSTEM: ${oneLine(m.content, 200)}`);
|
|
34470
|
-
}
|
|
34471
|
-
}
|
|
34472
|
-
let body = lines.join("\n");
|
|
34473
|
-
if (body.length > MAX_LLM_INPUT_CHARS) {
|
|
34474
|
-
body = body.slice(body.length - MAX_LLM_INPUT_CHARS);
|
|
34475
|
-
}
|
|
34476
|
-
return body;
|
|
34477
|
-
}
|
|
34478
35343
|
function oneLine(s, max) {
|
|
34479
35344
|
const t = s.replace(/\s+/g, " ").trim();
|
|
34480
35345
|
if (t.length <= max) return t;
|
|
@@ -34500,12 +35365,11 @@ function collectPathsFromText(text, out) {
|
|
|
34500
35365
|
n += 1;
|
|
34501
35366
|
}
|
|
34502
35367
|
}
|
|
34503
|
-
var MAX_SUMMARY_CHARS
|
|
35368
|
+
var MAX_SUMMARY_CHARS;
|
|
34504
35369
|
var init_historySummary = __esm({
|
|
34505
35370
|
"src/cli/budget/historySummary.ts"() {
|
|
34506
35371
|
"use strict";
|
|
34507
35372
|
MAX_SUMMARY_CHARS = 3500;
|
|
34508
|
-
MAX_LLM_INPUT_CHARS = 24e3;
|
|
34509
35373
|
}
|
|
34510
35374
|
});
|
|
34511
35375
|
|
|
@@ -34515,92 +35379,87 @@ function isLlmCompactEnabled() {
|
|
|
34515
35379
|
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
34516
35380
|
return true;
|
|
34517
35381
|
}
|
|
34518
|
-
|
|
34519
|
-
|
|
34520
|
-
|
|
34521
|
-
|
|
34522
|
-
|
|
34523
|
-
|
|
34524
|
-
|
|
34525
|
-
|
|
35382
|
+
function compactModelOverride() {
|
|
35383
|
+
const v = process.env.ZELARI_COMPACT_MODEL?.trim();
|
|
35384
|
+
return v ? v : void 0;
|
|
35385
|
+
}
|
|
35386
|
+
async function llmSummarizeHistoryReplay(input) {
|
|
35387
|
+
const override = input.overrideModel ?? compactModelOverride();
|
|
35388
|
+
const model = override ?? input.model;
|
|
35389
|
+
const cacheReuseExpected = !override;
|
|
35390
|
+
if (!isLlmCompactEnabled()) return { summary: null, model, cacheReuseExpected };
|
|
35391
|
+
if (input.droppedMessages.length === 0) {
|
|
35392
|
+
return { summary: null, model, cacheReuseExpected };
|
|
34526
35393
|
}
|
|
34527
|
-
|
|
34528
|
-
|
|
35394
|
+
const messages = [
|
|
35395
|
+
...input.systemMessages,
|
|
35396
|
+
...input.droppedMessages,
|
|
35397
|
+
{
|
|
35398
|
+
role: "user",
|
|
35399
|
+
content: COMPACTION_INSTRUCTION
|
|
35400
|
+
}
|
|
35401
|
+
];
|
|
34529
35402
|
const controller = new AbortController();
|
|
34530
|
-
const timeout = setTimeout(() => controller.abort(),
|
|
35403
|
+
const timeout = setTimeout(() => controller.abort(), REPLAY_TIMEOUT_MS);
|
|
34531
35404
|
const onOuterAbort = () => controller.abort();
|
|
34532
35405
|
input.signal?.addEventListener("abort", onOuterAbort, { once: true });
|
|
34533
35406
|
try {
|
|
34534
|
-
|
|
34535
|
-
|
|
34536
|
-
|
|
35407
|
+
let text = "";
|
|
35408
|
+
let emittedToolCall = false;
|
|
35409
|
+
for await (const delta of input.providerStream({
|
|
35410
|
+
provider: input.provider,
|
|
35411
|
+
model,
|
|
35412
|
+
messages,
|
|
35413
|
+
// Tools stay advertised: dropping them would change the prefix token
|
|
35414
|
+
// sequence and destroy cache reuse (explicit DSH decision). They are
|
|
35415
|
+
// sorted canonically (same discipline as the live routed request and
|
|
35416
|
+
// the snapshot fingerprints) so the replay prefix is byte-identical.
|
|
35417
|
+
tools: [...input.tools].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
|
|
34537
35418
|
signal: controller.signal,
|
|
34538
|
-
|
|
34539
|
-
|
|
34540
|
-
authorization: `Bearer ${config2.apiKey}`
|
|
34541
|
-
},
|
|
34542
|
-
body: JSON.stringify({
|
|
34543
|
-
model,
|
|
35419
|
+
generation: {
|
|
35420
|
+
purpose: "compaction",
|
|
34544
35421
|
temperature: 0.1,
|
|
34545
|
-
|
|
34546
|
-
|
|
34547
|
-
|
|
34548
|
-
|
|
34549
|
-
|
|
34550
|
-
|
|
34551
|
-
|
|
34552
|
-
|
|
34553
|
-
|
|
34554
|
-
Transcript of dropped turns:
|
|
34555
|
-
${input.droppedTranscript}`
|
|
34556
|
-
}
|
|
34557
|
-
]
|
|
34558
|
-
})
|
|
34559
|
-
});
|
|
34560
|
-
if (!res.ok) return null;
|
|
34561
|
-
const json2 = await res.json();
|
|
34562
|
-
const text = json2.choices?.[0]?.message?.content?.trim();
|
|
34563
|
-
if (!text) return null;
|
|
34564
|
-
return "[history-summary \xB7 llm]\n" + text + "\n\nContinue from the recent messages below; honor decisions already made above.";
|
|
35422
|
+
maxTokens: 900
|
|
35423
|
+
}
|
|
35424
|
+
})) {
|
|
35425
|
+
if (delta.kind === "text") text += delta.delta;
|
|
35426
|
+
if (delta.kind === "tool_call") emittedToolCall = true;
|
|
35427
|
+
}
|
|
35428
|
+
if (emittedToolCall) return { summary: null, model, cacheReuseExpected };
|
|
35429
|
+
if (!text.trim()) return { summary: null, model, cacheReuseExpected };
|
|
35430
|
+
return { summary: text.trim(), model, cacheReuseExpected };
|
|
34565
35431
|
} catch {
|
|
34566
|
-
return null;
|
|
35432
|
+
return { summary: null, model, cacheReuseExpected };
|
|
34567
35433
|
} finally {
|
|
34568
35434
|
clearTimeout(timeout);
|
|
34569
35435
|
input.signal?.removeEventListener("abort", onOuterAbort);
|
|
34570
35436
|
}
|
|
34571
35437
|
}
|
|
34572
|
-
|
|
34573
|
-
const active = getProviderConfig().activeProviderId;
|
|
34574
|
-
const meta3 = await resolveApiKeyWithMeta(active);
|
|
34575
|
-
const apiKey = meta3?.apiKey;
|
|
34576
|
-
if (!apiKey) return null;
|
|
34577
|
-
const custom2 = getCustomEndpoint(active);
|
|
34578
|
-
let baseUrl = custom2 || (active === "openai-compatible" || active === "custom" ? process.env.OPENAI_BASE_URL ?? PROVIDER_ENDPOINTS[active] : PROVIDER_ENDPOINTS[active]);
|
|
34579
|
-
if (!baseUrl) return null;
|
|
34580
|
-
const model = getModelForProvider(active);
|
|
34581
|
-
return {
|
|
34582
|
-
apiKey,
|
|
34583
|
-
baseUrl,
|
|
34584
|
-
model,
|
|
34585
|
-
providerId: active
|
|
34586
|
-
};
|
|
34587
|
-
}
|
|
34588
|
-
var COMPACT_SYSTEM;
|
|
35438
|
+
var COMPACTION_INSTRUCTION, REPLAY_TIMEOUT_MS;
|
|
34589
35439
|
var init_llmCompact = __esm({
|
|
34590
35440
|
"src/cli/budget/llmCompact.ts"() {
|
|
34591
35441
|
"use strict";
|
|
34592
|
-
|
|
34593
|
-
|
|
34594
|
-
|
|
34595
|
-
|
|
34596
|
-
|
|
34597
|
-
|
|
34598
|
-
|
|
34599
|
-
|
|
34600
|
-
|
|
34601
|
-
|
|
35442
|
+
COMPACTION_INSTRUCTION = `
|
|
35443
|
+
You are now acting as a compaction engine for this coding-agent session.
|
|
35444
|
+
|
|
35445
|
+
Condense the conversation ABOVE into a compact checkpoint sufficient to continue the task.
|
|
35446
|
+
|
|
35447
|
+
Preserve:
|
|
35448
|
+
- user's goal and evolving intent
|
|
35449
|
+
- decisions already made
|
|
35450
|
+
- exact file paths and identifiers
|
|
35451
|
+
- code changes already completed
|
|
35452
|
+
- commands/errors that still matter
|
|
35453
|
+
- constraints
|
|
35454
|
+
- unfinished work
|
|
35455
|
+
- the single most likely next action
|
|
34602
35456
|
|
|
34603
|
-
|
|
35457
|
+
Do not call tools.
|
|
35458
|
+
Do not mention this summarization request.
|
|
35459
|
+
Output only the checkpoint.
|
|
35460
|
+
Be concise.
|
|
35461
|
+
`.trim();
|
|
35462
|
+
REPLAY_TIMEOUT_MS = 6e4;
|
|
34604
35463
|
}
|
|
34605
35464
|
});
|
|
34606
35465
|
|
|
@@ -34639,6 +35498,7 @@ function resolveMaxMessages(opts) {
|
|
|
34639
35498
|
turns = Math.min(turns, 3);
|
|
34640
35499
|
}
|
|
34641
35500
|
if (turns <= 0) return 0;
|
|
35501
|
+
if (opts?.force && opts?.maxMessages) return Math.max(1, opts.maxMessages);
|
|
34642
35502
|
return turns * 4;
|
|
34643
35503
|
}
|
|
34644
35504
|
function findValidCutIndex(messages, naiveCut) {
|
|
@@ -34704,12 +35564,18 @@ function pruneToolResultsDetailed(messages, opts) {
|
|
|
34704
35564
|
function compactHistory(messages, opts) {
|
|
34705
35565
|
return compactHistoryDetailed(messages, opts).messages;
|
|
34706
35566
|
}
|
|
35567
|
+
function buildCheckpointMessage(summaryText) {
|
|
35568
|
+
return {
|
|
35569
|
+
role: "user",
|
|
35570
|
+
content: CHECKPOINT_WRAPPER_PREFIX + "\n\n<compacted-summary>\n" + summaryText + "\n</compacted-summary>"
|
|
35571
|
+
};
|
|
35572
|
+
}
|
|
34707
35573
|
function compactHistoryDetailed(messages, opts) {
|
|
34708
35574
|
const maxMessages = resolveMaxMessages(opts);
|
|
34709
35575
|
if (maxMessages === 0) {
|
|
34710
35576
|
return { messages: [], compacted: true, messagesRemoved: messages.length, summary: "" };
|
|
34711
35577
|
}
|
|
34712
|
-
if (messages.length <= maxMessages * 2) {
|
|
35578
|
+
if (messages.length <= maxMessages * 2 && !opts?.force) {
|
|
34713
35579
|
return {
|
|
34714
35580
|
messages,
|
|
34715
35581
|
compacted: false,
|
|
@@ -34717,7 +35583,7 @@ function compactHistoryDetailed(messages, opts) {
|
|
|
34717
35583
|
summary: ""
|
|
34718
35584
|
};
|
|
34719
35585
|
}
|
|
34720
|
-
const naiveCut = messages.length - maxMessages;
|
|
35586
|
+
const naiveCut = Math.max(0, messages.length - maxMessages);
|
|
34721
35587
|
const cut = findValidCutIndex(messages, naiveCut);
|
|
34722
35588
|
if (cut === 0) {
|
|
34723
35589
|
return {
|
|
@@ -34731,10 +35597,9 @@ function compactHistoryDetailed(messages, opts) {
|
|
|
34731
35597
|
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
34732
35598
|
const kept = pruned.messages;
|
|
34733
35599
|
const summaryText = extractiveHistorySummary(droppedMsgs);
|
|
34734
|
-
const summary =
|
|
34735
|
-
|
|
34736
|
-
|
|
34737
|
-
};
|
|
35600
|
+
const summary = buildCheckpointMessage(
|
|
35601
|
+
summaryText || `${COMPACT_MARKER} ${cut} earlier message(s) dropped.`
|
|
35602
|
+
);
|
|
34738
35603
|
return {
|
|
34739
35604
|
messages: [summary, ...kept],
|
|
34740
35605
|
compacted: true,
|
|
@@ -34749,29 +35614,58 @@ async function compactHistoryAsync(messages, opts) {
|
|
|
34749
35614
|
const cut = base.messagesRemoved;
|
|
34750
35615
|
const droppedMsgs = messages.slice(0, cut);
|
|
34751
35616
|
const extractive = extractiveHistorySummary(droppedMsgs);
|
|
34752
|
-
const droppedTranscript = formatDroppedForLlm(droppedMsgs);
|
|
34753
35617
|
let summaryText = extractive;
|
|
34754
|
-
|
|
34755
|
-
|
|
34756
|
-
|
|
34757
|
-
|
|
34758
|
-
|
|
34759
|
-
|
|
34760
|
-
|
|
34761
|
-
|
|
35618
|
+
let cacheReuseExpected;
|
|
35619
|
+
let replayExactPrefix;
|
|
35620
|
+
const canReplay = !!(opts?.providerStream && opts?.requestSnapshot);
|
|
35621
|
+
if (canReplay) {
|
|
35622
|
+
try {
|
|
35623
|
+
const replay = await llmSummarizeHistoryReplay({
|
|
35624
|
+
providerStream: opts.providerStream,
|
|
35625
|
+
provider: opts.requestSnapshot.provider,
|
|
35626
|
+
model: opts.requestSnapshot.model,
|
|
35627
|
+
systemMessages: opts.requestSnapshot.systemMessages,
|
|
35628
|
+
tools: opts.requestSnapshot.tools,
|
|
35629
|
+
droppedMessages: droppedMsgs,
|
|
35630
|
+
signal: opts?.signal
|
|
35631
|
+
});
|
|
35632
|
+
cacheReuseExpected = replay.cacheReuseExpected;
|
|
35633
|
+
if (replay.summary && replay.summary.trim().length > 40) {
|
|
35634
|
+
const sourceTokens = roughTokens(droppedMsgs);
|
|
35635
|
+
const summaryTok = Math.ceil(replay.summary.length / 4);
|
|
35636
|
+
if (summaryTok < sourceTokens) {
|
|
35637
|
+
summaryText = replay.summary.trim();
|
|
35638
|
+
}
|
|
35639
|
+
}
|
|
35640
|
+
} catch {
|
|
35641
|
+
}
|
|
34762
35642
|
}
|
|
34763
35643
|
const pruned = pruneToolResultsDetailed(messages.slice(cut));
|
|
34764
35644
|
const kept = pruned.messages;
|
|
34765
|
-
const summary =
|
|
35645
|
+
const summary = buildCheckpointMessage(summaryText);
|
|
34766
35646
|
return {
|
|
34767
35647
|
messages: [summary, ...kept],
|
|
34768
35648
|
compacted: true,
|
|
34769
35649
|
messagesRemoved: cut,
|
|
34770
35650
|
summary: summaryText,
|
|
34771
|
-
prunedToolResults: pruned.stats.pruned
|
|
35651
|
+
prunedToolResults: pruned.stats.pruned,
|
|
35652
|
+
cacheReuseExpected,
|
|
35653
|
+
replayExactPrefix
|
|
34772
35654
|
};
|
|
34773
35655
|
}
|
|
34774
|
-
|
|
35656
|
+
function roughTokens(msgs) {
|
|
35657
|
+
let n = 0;
|
|
35658
|
+
for (const m of msgs) {
|
|
35659
|
+
n += Math.ceil((m.content ?? "").length / 4);
|
|
35660
|
+
if (m.toolCalls) {
|
|
35661
|
+
for (const tc of m.toolCalls) {
|
|
35662
|
+
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
35663
|
+
}
|
|
35664
|
+
}
|
|
35665
|
+
}
|
|
35666
|
+
return Math.max(1, n);
|
|
35667
|
+
}
|
|
35668
|
+
var COMPACT_MARKER, CHECKPOINT_WRAPPER_PREFIX;
|
|
34775
35669
|
var init_historyCompaction = __esm({
|
|
34776
35670
|
"src/cli/hooks/historyCompaction.ts"() {
|
|
34777
35671
|
"use strict";
|
|
@@ -34779,6 +35673,30 @@ var init_historyCompaction = __esm({
|
|
|
34779
35673
|
init_llmCompact();
|
|
34780
35674
|
init_envNumber();
|
|
34781
35675
|
COMPACT_MARKER = "[history] Earlier turns were compacted to stay within the context budget.";
|
|
35676
|
+
CHECKPOINT_WRAPPER_PREFIX = "This is an automatically generated checkpoint of earlier conversation. Treat it as established context and continue directly.";
|
|
35677
|
+
}
|
|
35678
|
+
});
|
|
35679
|
+
|
|
35680
|
+
// src/cli/budget/requestSnapshotStore.ts
|
|
35681
|
+
function recordRequestSnapshot(sessionId, snapshot) {
|
|
35682
|
+
store3.set(sessionId, { snapshot });
|
|
35683
|
+
}
|
|
35684
|
+
function recordRequestUsage(sessionId, usage) {
|
|
35685
|
+
const entry = store3.get(sessionId);
|
|
35686
|
+
if (!entry) return;
|
|
35687
|
+
entry.usage = usage;
|
|
35688
|
+
}
|
|
35689
|
+
function getRequestSnapshotWithUsage(sessionId) {
|
|
35690
|
+
return store3.get(sessionId) ?? null;
|
|
35691
|
+
}
|
|
35692
|
+
function clearAllRequestSnapshots() {
|
|
35693
|
+
store3.clear();
|
|
35694
|
+
}
|
|
35695
|
+
var store3;
|
|
35696
|
+
var init_requestSnapshotStore = __esm({
|
|
35697
|
+
"src/cli/budget/requestSnapshotStore.ts"() {
|
|
35698
|
+
"use strict";
|
|
35699
|
+
store3 = /* @__PURE__ */ new Map();
|
|
34782
35700
|
}
|
|
34783
35701
|
});
|
|
34784
35702
|
|
|
@@ -34804,8 +35722,8 @@ __export(conversationContext_exports, {
|
|
|
34804
35722
|
setHistory: () => setHistory,
|
|
34805
35723
|
setLastClarification: () => setLastClarification
|
|
34806
35724
|
});
|
|
34807
|
-
import { existsSync as
|
|
34808
|
-
import { join as
|
|
35725
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
35726
|
+
import { join as join19 } from "node:path";
|
|
34809
35727
|
function getHistory() {
|
|
34810
35728
|
return history;
|
|
34811
35729
|
}
|
|
@@ -34813,7 +35731,7 @@ function setHistory(messages) {
|
|
|
34813
35731
|
history = [...messages];
|
|
34814
35732
|
}
|
|
34815
35733
|
function compactInPlace(cwd = process.cwd()) {
|
|
34816
|
-
const durableStatePresent =
|
|
35734
|
+
const durableStatePresent = existsSync23(join19(cwd, ".zelari", "state", "HEAD.json"));
|
|
34817
35735
|
history = compactHistory(history, { durableStatePresent });
|
|
34818
35736
|
}
|
|
34819
35737
|
function appendMessages(msgs) {
|
|
@@ -34823,6 +35741,7 @@ function appendMessages(msgs) {
|
|
|
34823
35741
|
function clearHistory() {
|
|
34824
35742
|
history = [];
|
|
34825
35743
|
lastClarification = null;
|
|
35744
|
+
clearAllRequestSnapshots();
|
|
34826
35745
|
clearSessionTodos();
|
|
34827
35746
|
clearSessionPermissionGrants();
|
|
34828
35747
|
}
|
|
@@ -34981,6 +35900,7 @@ var init_conversationContext = __esm({
|
|
|
34981
35900
|
init_toolPermissions();
|
|
34982
35901
|
init_sessionTodos();
|
|
34983
35902
|
init_historyCompaction();
|
|
35903
|
+
init_requestSnapshotStore();
|
|
34984
35904
|
history = [];
|
|
34985
35905
|
lastClarification = null;
|
|
34986
35906
|
SHORT_CONTINUE = /^(procedi|continua|continue|go\s*ahead|go|ok|okay|sì|si|yes|vai|avanti|next|proceed|conferma|confermo|applica|fai|scrivi|esegui|implementa|vai pure|fai pure|ok procedi|sì procedi|si procedi)$/i;
|
|
@@ -35326,14 +36246,14 @@ var init_claudeProvider = __esm({
|
|
|
35326
36246
|
});
|
|
35327
36247
|
|
|
35328
36248
|
// src/cli/workspace/projectInstructions.ts
|
|
35329
|
-
import { existsSync as
|
|
35330
|
-
import { join as
|
|
36249
|
+
import { existsSync as existsSync24, readFileSync as readFileSync22 } from "node:fs";
|
|
36250
|
+
import { join as join20 } from "node:path";
|
|
35331
36251
|
function loadProjectInstructions(projectRoot = process.cwd(), maxChars = MAX_CHARS) {
|
|
35332
36252
|
for (const name of CANDIDATES) {
|
|
35333
|
-
const full =
|
|
35334
|
-
if (!
|
|
36253
|
+
const full = join20(projectRoot, name);
|
|
36254
|
+
if (!existsSync24(full)) continue;
|
|
35335
36255
|
try {
|
|
35336
|
-
let raw =
|
|
36256
|
+
let raw = readFileSync22(full, "utf8");
|
|
35337
36257
|
raw = raw.replace(/\r\n/g, "\n").trim();
|
|
35338
36258
|
if (!raw) continue;
|
|
35339
36259
|
if (raw.length <= maxChars) {
|
|
@@ -35368,75 +36288,6 @@ var init_projectInstructions = __esm({
|
|
|
35368
36288
|
}
|
|
35369
36289
|
});
|
|
35370
36290
|
|
|
35371
|
-
// src/cli/workspace/paths.ts
|
|
35372
|
-
import {
|
|
35373
|
-
mkdirSync as mkdirSync12,
|
|
35374
|
-
writeFileSync as writeFileSync14,
|
|
35375
|
-
existsSync as existsSync22,
|
|
35376
|
-
accessSync,
|
|
35377
|
-
constants,
|
|
35378
|
-
realpathSync
|
|
35379
|
-
} from "node:fs";
|
|
35380
|
-
import { join as join18, basename } from "node:path";
|
|
35381
|
-
import { homedir as homedir9 } from "node:os";
|
|
35382
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
35383
|
-
function resolveWorkspaceRoot(projectRoot = process.cwd()) {
|
|
35384
|
-
const candidates = [
|
|
35385
|
-
join18(projectRoot, ".zelari"),
|
|
35386
|
-
join18(homedir9(), ".zelari-code", "workspace", hashProject(projectRoot))
|
|
35387
|
-
];
|
|
35388
|
-
for (const candidate of candidates) {
|
|
35389
|
-
if (isWritableDir(projectRoot) || candidate !== candidates[0]) {
|
|
35390
|
-
ensureWorkspaceDir(candidate);
|
|
35391
|
-
return candidate;
|
|
35392
|
-
}
|
|
35393
|
-
}
|
|
35394
|
-
ensureWorkspaceDir(candidates[0]);
|
|
35395
|
-
return candidates[0];
|
|
35396
|
-
}
|
|
35397
|
-
function hashProject(projectPath) {
|
|
35398
|
-
return createHash6("sha1").update(realpathSync(projectPath)).digest("hex").slice(0, 12);
|
|
35399
|
-
}
|
|
35400
|
-
function isWritableDir(dir) {
|
|
35401
|
-
try {
|
|
35402
|
-
if (!existsSync22(dir)) return false;
|
|
35403
|
-
accessSync(dir, constants.W_OK);
|
|
35404
|
-
return true;
|
|
35405
|
-
} catch {
|
|
35406
|
-
return false;
|
|
35407
|
-
}
|
|
35408
|
-
}
|
|
35409
|
-
function ensureWorkspaceDir(workspaceDir) {
|
|
35410
|
-
mkdirSync12(workspaceDir, { recursive: true });
|
|
35411
|
-
if (workspaceDir.endsWith("/.zelari") && existsSync22(join18(workspaceDir, "..", ".git"))) {
|
|
35412
|
-
const gitignorePath = join18(workspaceDir, ".gitignore");
|
|
35413
|
-
if (!existsSync22(gitignorePath)) {
|
|
35414
|
-
writeFileSync14(gitignorePath, "*\n!.gitignore\n");
|
|
35415
|
-
}
|
|
35416
|
-
}
|
|
35417
|
-
}
|
|
35418
|
-
function workspaceFile(rootDir, kind) {
|
|
35419
|
-
switch (kind) {
|
|
35420
|
-
case "plan":
|
|
35421
|
-
return join18(rootDir, "plan.md");
|
|
35422
|
-
case "risks":
|
|
35423
|
-
return join18(rootDir, "risks.md");
|
|
35424
|
-
case "index":
|
|
35425
|
-
return join18(rootDir, "workspace.json");
|
|
35426
|
-
}
|
|
35427
|
-
}
|
|
35428
|
-
function workspaceArtifact(rootDir, subdir, slug) {
|
|
35429
|
-
return join18(rootDir, subdir, `${slug}.md`);
|
|
35430
|
-
}
|
|
35431
|
-
function projectName(projectRoot = process.cwd()) {
|
|
35432
|
-
return basename(realpathSync(projectRoot));
|
|
35433
|
-
}
|
|
35434
|
-
var init_paths2 = __esm({
|
|
35435
|
-
"src/cli/workspace/paths.ts"() {
|
|
35436
|
-
"use strict";
|
|
35437
|
-
}
|
|
35438
|
-
});
|
|
35439
|
-
|
|
35440
36291
|
// src/cli/workspace/workspaceSummary.ts
|
|
35441
36292
|
var workspaceSummary_exports = {};
|
|
35442
36293
|
__export(workspaceSummary_exports, {
|
|
@@ -35446,8 +36297,8 @@ __export(workspaceSummary_exports, {
|
|
|
35446
36297
|
buildWorkspaceSummary: () => buildWorkspaceSummary,
|
|
35447
36298
|
buildZelariReadHint: () => buildZelariReadHint
|
|
35448
36299
|
});
|
|
35449
|
-
import { existsSync as
|
|
35450
|
-
import { join as
|
|
36300
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync6, statSync as statSync4 } from "node:fs";
|
|
36301
|
+
import { join as join21, relative } from "node:path";
|
|
35451
36302
|
function buildWorkspaceSummary(projectRoot = process.cwd(), options = {}) {
|
|
35452
36303
|
const { maxEntries = 30, maxChars = 3500, maxDeps = 24, maxScripts = 16 } = options;
|
|
35453
36304
|
const name = safeProjectName(projectRoot);
|
|
@@ -35480,11 +36331,11 @@ function formatTaskLine(t) {
|
|
|
35480
36331
|
}
|
|
35481
36332
|
function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
35482
36333
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
35483
|
-
const planPath =
|
|
35484
|
-
if (!
|
|
36334
|
+
const planPath = join21(zelariRoot, "plan.json");
|
|
36335
|
+
if (!existsSync25(planPath)) return null;
|
|
35485
36336
|
let plan;
|
|
35486
36337
|
try {
|
|
35487
|
-
plan = JSON.parse(
|
|
36338
|
+
plan = JSON.parse(readFileSync23(planPath, "utf8"));
|
|
35488
36339
|
} catch {
|
|
35489
36340
|
return null;
|
|
35490
36341
|
}
|
|
@@ -35498,7 +36349,10 @@ function buildPlanSummary(projectRoot = process.cwd(), options) {
|
|
|
35498
36349
|
"# Plan ops (DRAFT \u2014 .zelari/plan.json)",
|
|
35499
36350
|
"_This is operational task list from design, not product law. Ground every change in the real source tree._"
|
|
35500
36351
|
];
|
|
35501
|
-
const open = tasks.filter((t) =>
|
|
36352
|
+
const open = tasks.filter((t) => {
|
|
36353
|
+
const s = t.status;
|
|
36354
|
+
return s !== "done" && s !== "completed" && s !== "cancelled";
|
|
36355
|
+
});
|
|
35502
36356
|
const done = tasks.length - open.length;
|
|
35503
36357
|
const userMessage = options?.userMessage?.trim();
|
|
35504
36358
|
let scopedOpen = open;
|
|
@@ -35620,8 +36474,8 @@ function pickNextTask(open) {
|
|
|
35620
36474
|
)[0];
|
|
35621
36475
|
}
|
|
35622
36476
|
function buildZelariReadHint(projectRoot = process.cwd()) {
|
|
35623
|
-
const planPath =
|
|
35624
|
-
if (!
|
|
36477
|
+
const planPath = join21(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
36478
|
+
if (!existsSync25(planPath)) return "";
|
|
35625
36479
|
return [
|
|
35626
36480
|
"# Council workspace detected (.zelari/) \u2014 DRAFT vault",
|
|
35627
36481
|
"`.zelari/plan.json` and `.zelari/docs/` hold **design hypotheses**, not verified product state.",
|
|
@@ -35636,10 +36490,10 @@ function safeProjectName(root) {
|
|
|
35636
36490
|
}
|
|
35637
36491
|
}
|
|
35638
36492
|
function readPackageJson(projectRoot) {
|
|
35639
|
-
const p3 =
|
|
35640
|
-
if (!
|
|
36493
|
+
const p3 = join21(projectRoot, "package.json");
|
|
36494
|
+
if (!existsSync25(p3)) return null;
|
|
35641
36495
|
try {
|
|
35642
|
-
return JSON.parse(
|
|
36496
|
+
return JSON.parse(readFileSync23(p3, "utf8"));
|
|
35643
36497
|
} catch {
|
|
35644
36498
|
return null;
|
|
35645
36499
|
}
|
|
@@ -35680,7 +36534,7 @@ function readBuildScripts(projectRoot, maxScripts = 16) {
|
|
|
35680
36534
|
function listShallow(projectRoot, maxEntries) {
|
|
35681
36535
|
const out = [];
|
|
35682
36536
|
try {
|
|
35683
|
-
const top =
|
|
36537
|
+
const top = readdirSync6(projectRoot, { withFileTypes: true }).filter(
|
|
35684
36538
|
(e) => !e.name.startsWith(".") && e.name !== "node_modules" && e.name !== "dist"
|
|
35685
36539
|
).sort((a, b) => a.name.localeCompare(b.name));
|
|
35686
36540
|
let count = 0;
|
|
@@ -35689,11 +36543,11 @@ function listShallow(projectRoot, maxEntries) {
|
|
|
35689
36543
|
out.push(`\u2026 (+${top.length - count} more)`);
|
|
35690
36544
|
break;
|
|
35691
36545
|
}
|
|
35692
|
-
const rel2 = relative(projectRoot,
|
|
36546
|
+
const rel2 = relative(projectRoot, join21(projectRoot, entry.name));
|
|
35693
36547
|
if (entry.isDirectory()) {
|
|
35694
36548
|
let inner = "";
|
|
35695
36549
|
try {
|
|
35696
|
-
const sub =
|
|
36550
|
+
const sub = readdirSync6(join21(projectRoot, entry.name), {
|
|
35697
36551
|
withFileTypes: true
|
|
35698
36552
|
}).filter((e) => !e.name.startsWith(".")).slice(0, 4).map((e) => e.name);
|
|
35699
36553
|
if (sub.length > 0)
|
|
@@ -35741,12 +36595,12 @@ var init_workspaceSummary = __esm({
|
|
|
35741
36595
|
});
|
|
35742
36596
|
|
|
35743
36597
|
// src/cli/workspace/buildLessonsSummary.ts
|
|
35744
|
-
import { existsSync as
|
|
35745
|
-
import { join as
|
|
36598
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
36599
|
+
import { join as join22 } from "node:path";
|
|
35746
36600
|
function buildLessonsSummary(projectRoot = process.cwd(), taskText) {
|
|
35747
36601
|
if (process.env["ZELARI_LESSONS"] === "0") return null;
|
|
35748
36602
|
const zelariRoot = resolveWorkspaceRoot(projectRoot);
|
|
35749
|
-
if (!
|
|
36603
|
+
if (!existsSync26(join22(zelariRoot, "lessons.jsonl"))) return null;
|
|
35750
36604
|
const lessons = recallLessons(zelariRoot, {
|
|
35751
36605
|
maxLessons: 5,
|
|
35752
36606
|
maxBytes: 2048,
|
|
@@ -35767,8 +36621,8 @@ var composeContext_exports = {};
|
|
|
35767
36621
|
__export(composeContext_exports, {
|
|
35768
36622
|
composeProjectContext: () => composeProjectContext
|
|
35769
36623
|
});
|
|
35770
|
-
import { existsSync as
|
|
35771
|
-
import { join as
|
|
36624
|
+
import { existsSync as existsSync27, readdirSync as readdirSync7, readFileSync as readFileSync24 } from "node:fs";
|
|
36625
|
+
import { join as join23 } from "node:path";
|
|
35772
36626
|
function cap2(text, max, label) {
|
|
35773
36627
|
if (!text || text.length <= max) return { text: text || "", truncated: false };
|
|
35774
36628
|
return {
|
|
@@ -35780,19 +36634,19 @@ function cap2(text, max, label) {
|
|
|
35780
36634
|
}
|
|
35781
36635
|
function buildDesignIndex(projectRoot, maxChars) {
|
|
35782
36636
|
const root = resolveWorkspaceRoot(projectRoot);
|
|
35783
|
-
if (!
|
|
36637
|
+
if (!existsSync27(root)) return "";
|
|
35784
36638
|
const lines = [
|
|
35785
36639
|
"# Design vault index (.zelari/) \u2014 HYPOTHESES only",
|
|
35786
36640
|
"Full design docs are NOT product source of truth. Open with list_files / read_file / searchDocuments if needed."
|
|
35787
36641
|
];
|
|
35788
|
-
const docsDir =
|
|
35789
|
-
if (
|
|
36642
|
+
const docsDir = join23(root, "docs");
|
|
36643
|
+
if (existsSync27(docsDir)) {
|
|
35790
36644
|
try {
|
|
35791
|
-
const docs =
|
|
36645
|
+
const docs = readdirSync7(docsDir).filter((n) => n.endsWith(".md")).slice(0, 12);
|
|
35792
36646
|
if (docs.length > 0) {
|
|
35793
36647
|
lines.push("", "## docs/ (titles only)");
|
|
35794
36648
|
for (const d of docs) lines.push(`- .zelari/docs/${d}`);
|
|
35795
|
-
if (
|
|
36649
|
+
if (readdirSync7(docsDir).filter((n) => n.endsWith(".md")).length > 12) {
|
|
35796
36650
|
lines.push("- \u2026 (more under .zelari/docs/)");
|
|
35797
36651
|
}
|
|
35798
36652
|
}
|
|
@@ -35800,14 +36654,14 @@ function buildDesignIndex(projectRoot, maxChars) {
|
|
|
35800
36654
|
}
|
|
35801
36655
|
}
|
|
35802
36656
|
for (const name of ["risks.md", "plan.json", "nfr-spec.json"]) {
|
|
35803
|
-
if (
|
|
36657
|
+
if (existsSync27(join23(root, name))) {
|
|
35804
36658
|
lines.push(`- .zelari/${name} present`);
|
|
35805
36659
|
}
|
|
35806
36660
|
}
|
|
35807
|
-
const decisionsDir =
|
|
35808
|
-
if (
|
|
36661
|
+
const decisionsDir = join23(root, "decisions");
|
|
36662
|
+
if (existsSync27(decisionsDir)) {
|
|
35809
36663
|
try {
|
|
35810
|
-
const n =
|
|
36664
|
+
const n = readdirSync7(decisionsDir).filter((f) => f.endsWith(".md")).length;
|
|
35811
36665
|
if (n > 0) lines.push(`- .zelari/decisions/ (${n} ADR file(s) \u2014 treat proposed as non-binding)`);
|
|
35812
36666
|
} catch {
|
|
35813
36667
|
}
|
|
@@ -35906,17 +36760,17 @@ function composeProjectContext(input) {
|
|
|
35906
36760
|
}
|
|
35907
36761
|
function readDurableHeadSync(projectRoot) {
|
|
35908
36762
|
try {
|
|
35909
|
-
const headPath =
|
|
35910
|
-
if (!
|
|
35911
|
-
const head = JSON.parse(
|
|
36763
|
+
const headPath = join23(projectRoot, ".zelari", "state", "HEAD.json");
|
|
36764
|
+
if (!existsSync27(headPath)) return "";
|
|
36765
|
+
const head = JSON.parse(readFileSync24(headPath, "utf8"));
|
|
35912
36766
|
if (!head?.id) return "";
|
|
35913
|
-
const metaPath =
|
|
35914
|
-
if (!
|
|
35915
|
-
const meta3 = JSON.parse(
|
|
35916
|
-
const discPath = meta3.artifactDir ?
|
|
36767
|
+
const metaPath = join23(projectRoot, ".zelari", "state", "commits", `${head.id}.json`);
|
|
36768
|
+
if (!existsSync27(metaPath)) return "";
|
|
36769
|
+
const meta3 = JSON.parse(readFileSync24(metaPath, "utf8"));
|
|
36770
|
+
const discPath = meta3.artifactDir ? join23(projectRoot, ".zelari", "state", meta3.artifactDir, "discoveries.json") : join23(projectRoot, ".zelari", "state", "artifacts", head.id, "discoveries.json");
|
|
35917
36771
|
let discoveries = [];
|
|
35918
|
-
if (
|
|
35919
|
-
discoveries = JSON.parse(
|
|
36772
|
+
if (existsSync27(discPath)) {
|
|
36773
|
+
discoveries = JSON.parse(readFileSync24(discPath, "utf8"));
|
|
35920
36774
|
}
|
|
35921
36775
|
const reusable = discoveries.filter((d) => d.reusable !== false);
|
|
35922
36776
|
const lines = [
|
|
@@ -35949,13 +36803,13 @@ var planDetect_exports = {};
|
|
|
35949
36803
|
__export(planDetect_exports, {
|
|
35950
36804
|
hasWorkspacePlan: () => hasWorkspacePlan
|
|
35951
36805
|
});
|
|
35952
|
-
import { existsSync as
|
|
35953
|
-
import { join as
|
|
36806
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25 } from "node:fs";
|
|
36807
|
+
import { join as join24 } from "node:path";
|
|
35954
36808
|
function hasWorkspacePlan(projectRoot = process.cwd()) {
|
|
35955
|
-
const planPath =
|
|
35956
|
-
if (!
|
|
36809
|
+
const planPath = join24(resolveWorkspaceRoot(projectRoot), "plan.json");
|
|
36810
|
+
if (!existsSync28(planPath)) return false;
|
|
35957
36811
|
try {
|
|
35958
|
-
const parsed = JSON.parse(
|
|
36812
|
+
const parsed = JSON.parse(readFileSync25(planPath, "utf8"));
|
|
35959
36813
|
return Array.isArray(parsed.phases) && parsed.phases.length > 0;
|
|
35960
36814
|
} catch {
|
|
35961
36815
|
return false;
|
|
@@ -35986,8 +36840,8 @@ async function loadDurableContext(projectRoot, opts) {
|
|
|
35986
36840
|
return cache.text;
|
|
35987
36841
|
}
|
|
35988
36842
|
try {
|
|
35989
|
-
const
|
|
35990
|
-
const text = await
|
|
36843
|
+
const store4 = await getStateStore(projectRoot, env);
|
|
36844
|
+
const text = await store4.materializeContext(void 0, opts?.maxChars);
|
|
35991
36845
|
cache = { text: text || "", at: now, projectRoot };
|
|
35992
36846
|
return cache.text;
|
|
35993
36847
|
} catch {
|
|
@@ -36004,333 +36858,6 @@ var init_loadDurableContext = __esm({
|
|
|
36004
36858
|
}
|
|
36005
36859
|
});
|
|
36006
36860
|
|
|
36007
|
-
// src/cli/workspace/storage.ts
|
|
36008
|
-
var storage_exports = {};
|
|
36009
|
-
__export(storage_exports, {
|
|
36010
|
-
Storage: () => Storage,
|
|
36011
|
-
parseFrontmatter: () => parseFrontmatter,
|
|
36012
|
-
parseYaml: () => parseYaml,
|
|
36013
|
-
serializeFrontmatter: () => serializeFrontmatter,
|
|
36014
|
-
serializeYaml: () => serializeYaml,
|
|
36015
|
-
workspaceMutex: () => workspaceMutex
|
|
36016
|
-
});
|
|
36017
|
-
import {
|
|
36018
|
-
readFileSync as readFileSync24,
|
|
36019
|
-
writeFileSync as writeFileSync15,
|
|
36020
|
-
existsSync as existsSync27,
|
|
36021
|
-
mkdirSync as mkdirSync13,
|
|
36022
|
-
readdirSync as readdirSync7,
|
|
36023
|
-
renameSync as renameSync2
|
|
36024
|
-
} from "node:fs";
|
|
36025
|
-
import { dirname as dirname4, join as join23 } from "node:path";
|
|
36026
|
-
function parseFrontmatter(md) {
|
|
36027
|
-
const m = FRONTMATTER_RE.exec(md);
|
|
36028
|
-
if (!m) return { meta: {}, body: md };
|
|
36029
|
-
const meta3 = parseYaml(m[1]);
|
|
36030
|
-
const body = m[2];
|
|
36031
|
-
return { meta: meta3, body };
|
|
36032
|
-
}
|
|
36033
|
-
function serializeFrontmatter(meta3, body) {
|
|
36034
|
-
const yamlStr = serializeYaml(meta3);
|
|
36035
|
-
return `---
|
|
36036
|
-
${yamlStr}
|
|
36037
|
-
---
|
|
36038
|
-
${body}`;
|
|
36039
|
-
}
|
|
36040
|
-
function parseYaml(input) {
|
|
36041
|
-
const lines = input.split(/\r?\n/);
|
|
36042
|
-
const ctx = { lines, i: 0 };
|
|
36043
|
-
return parseNode(ctx, 0);
|
|
36044
|
-
}
|
|
36045
|
-
function parseNode(ctx, indent) {
|
|
36046
|
-
while (ctx.i < ctx.lines.length) {
|
|
36047
|
-
const line2 = ctx.lines[ctx.i];
|
|
36048
|
-
if (line2.trim() === "" || line2.trim().startsWith("#")) {
|
|
36049
|
-
ctx.i++;
|
|
36050
|
-
continue;
|
|
36051
|
-
}
|
|
36052
|
-
break;
|
|
36053
|
-
}
|
|
36054
|
-
if (ctx.i >= ctx.lines.length) return null;
|
|
36055
|
-
const line = ctx.lines[ctx.i];
|
|
36056
|
-
const lineIndent = countIndent(line);
|
|
36057
|
-
if (/^\s*-\s+/.test(line)) {
|
|
36058
|
-
return parseBlockSequence(ctx, indent);
|
|
36059
|
-
}
|
|
36060
|
-
if (/^\s*\[.*\]\s*$/.test(line)) {
|
|
36061
|
-
const flow = line.trim().replace(/^\[/, "").replace(/\]$/, "");
|
|
36062
|
-
return parseFlowSequence(flow);
|
|
36063
|
-
}
|
|
36064
|
-
if (/^\s*\{.*\}\s*$/.test(line)) {
|
|
36065
|
-
const flow = line.trim().replace(/^\{/, "").replace(/\}$/, "");
|
|
36066
|
-
return parseFlowMap(flow);
|
|
36067
|
-
}
|
|
36068
|
-
return parseBlockMap(ctx, indent);
|
|
36069
|
-
}
|
|
36070
|
-
function parseBlockMap(ctx, indent) {
|
|
36071
|
-
const out = {};
|
|
36072
|
-
while (ctx.i < ctx.lines.length) {
|
|
36073
|
-
const line = ctx.lines[ctx.i];
|
|
36074
|
-
if (line.trim() === "" || line.trim().startsWith("#")) {
|
|
36075
|
-
ctx.i++;
|
|
36076
|
-
continue;
|
|
36077
|
-
}
|
|
36078
|
-
const lineIndent = countIndent(line);
|
|
36079
|
-
if (lineIndent < indent) break;
|
|
36080
|
-
if (lineIndent > indent) {
|
|
36081
|
-
ctx.i++;
|
|
36082
|
-
continue;
|
|
36083
|
-
}
|
|
36084
|
-
const m = /^([^:]+):\s*(.*)$/.exec(line);
|
|
36085
|
-
if (!m) {
|
|
36086
|
-
ctx.i++;
|
|
36087
|
-
continue;
|
|
36088
|
-
}
|
|
36089
|
-
const key = m[1].trim();
|
|
36090
|
-
const valuePart = m[2].trim();
|
|
36091
|
-
if (valuePart === "" || valuePart === "|" || valuePart === ">") {
|
|
36092
|
-
ctx.i++;
|
|
36093
|
-
const nested = parseNode(ctx, indent + 2);
|
|
36094
|
-
out[key] = nested;
|
|
36095
|
-
} else {
|
|
36096
|
-
if (valuePart.startsWith("[")) {
|
|
36097
|
-
out[key] = parseFlowSequence(stripFlow(valuePart, "[", "]"));
|
|
36098
|
-
} else if (valuePart.startsWith("{")) {
|
|
36099
|
-
out[key] = parseFlowMap(stripFlow(valuePart, "{", "}"));
|
|
36100
|
-
} else {
|
|
36101
|
-
out[key] = parseScalar(valuePart);
|
|
36102
|
-
}
|
|
36103
|
-
ctx.i++;
|
|
36104
|
-
}
|
|
36105
|
-
}
|
|
36106
|
-
return out;
|
|
36107
|
-
}
|
|
36108
|
-
function parseBlockSequence(ctx, indent) {
|
|
36109
|
-
const out = [];
|
|
36110
|
-
while (ctx.i < ctx.lines.length) {
|
|
36111
|
-
const line = ctx.lines[ctx.i];
|
|
36112
|
-
if (line.trim() === "") {
|
|
36113
|
-
ctx.i++;
|
|
36114
|
-
continue;
|
|
36115
|
-
}
|
|
36116
|
-
const lineIndent = countIndent(line);
|
|
36117
|
-
if (lineIndent < indent) break;
|
|
36118
|
-
if (lineIndent > indent) break;
|
|
36119
|
-
const m = /^-\s*(.*)$/.exec(line);
|
|
36120
|
-
if (!m) break;
|
|
36121
|
-
const rest = m[1];
|
|
36122
|
-
if (rest === "") {
|
|
36123
|
-
ctx.i++;
|
|
36124
|
-
out.push(parseNode(ctx, indent + 2));
|
|
36125
|
-
} else if (rest.startsWith("[") || rest.startsWith("{")) {
|
|
36126
|
-
let buffer = rest;
|
|
36127
|
-
let depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
36128
|
-
let depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
36129
|
-
while ((depthSq > 0 || depthCu > 0) && ctx.i + 1 < ctx.lines.length) {
|
|
36130
|
-
ctx.i++;
|
|
36131
|
-
const next = ctx.lines[ctx.i].trim();
|
|
36132
|
-
buffer += " " + next;
|
|
36133
|
-
depthSq = (buffer.match(/\[/g) || []).length - (buffer.match(/\]/g) || []).length;
|
|
36134
|
-
depthCu = (buffer.match(/\{/g) || []).length - (buffer.match(/\}/g) || []).length;
|
|
36135
|
-
}
|
|
36136
|
-
if (buffer.startsWith("[")) {
|
|
36137
|
-
out.push(parseFlowSequence(buffer.slice(1).replace(/\]$/, "")));
|
|
36138
|
-
} else {
|
|
36139
|
-
out.push(parseFlowMap(buffer.slice(1).replace(/\}$/, "")));
|
|
36140
|
-
}
|
|
36141
|
-
ctx.i++;
|
|
36142
|
-
} else if (rest.includes(":")) {
|
|
36143
|
-
ctx.i++;
|
|
36144
|
-
const mapCtx = { lines: [" ".repeat(indent + 2) + rest, ...ctx.lines.slice(ctx.i)], i: 0 };
|
|
36145
|
-
const val = parseBlockMap(mapCtx, indent + 2);
|
|
36146
|
-
ctx.i += mapCtx.i - 1;
|
|
36147
|
-
out.push(val);
|
|
36148
|
-
} else {
|
|
36149
|
-
out.push(parseScalar(rest));
|
|
36150
|
-
ctx.i++;
|
|
36151
|
-
}
|
|
36152
|
-
}
|
|
36153
|
-
return out;
|
|
36154
|
-
}
|
|
36155
|
-
function parseFlowSequence(input) {
|
|
36156
|
-
const parts = splitFlow(input);
|
|
36157
|
-
return parts.map((p3) => {
|
|
36158
|
-
const trimmed = p3.trim();
|
|
36159
|
-
if (trimmed.startsWith("{")) {
|
|
36160
|
-
return parseFlowMap(stripFlow(trimmed, "{", "}"));
|
|
36161
|
-
}
|
|
36162
|
-
return parseScalar(trimmed);
|
|
36163
|
-
});
|
|
36164
|
-
}
|
|
36165
|
-
function stripFlow(s, open, close) {
|
|
36166
|
-
let out = s.trim();
|
|
36167
|
-
if (out.startsWith(open)) out = out.slice(1);
|
|
36168
|
-
if (out.endsWith(close)) out = out.slice(0, -1);
|
|
36169
|
-
return out;
|
|
36170
|
-
}
|
|
36171
|
-
function parseFlowMap(input) {
|
|
36172
|
-
const parts = splitFlow(input);
|
|
36173
|
-
const out = {};
|
|
36174
|
-
for (const p3 of parts) {
|
|
36175
|
-
const colonIdx = p3.indexOf(":");
|
|
36176
|
-
if (colonIdx < 0) continue;
|
|
36177
|
-
const key = p3.slice(0, colonIdx).trim();
|
|
36178
|
-
const value = p3.slice(colonIdx + 1).trim();
|
|
36179
|
-
out[key] = parseScalar(value);
|
|
36180
|
-
}
|
|
36181
|
-
return out;
|
|
36182
|
-
}
|
|
36183
|
-
function splitFlow(input) {
|
|
36184
|
-
const out = [];
|
|
36185
|
-
let depthSq = 0, depthCu = 0, depthQu = 0;
|
|
36186
|
-
let buffer = "";
|
|
36187
|
-
for (let i = 0; i < input.length; i++) {
|
|
36188
|
-
const c = input[i];
|
|
36189
|
-
if (c === '"' || c === "'") {
|
|
36190
|
-
depthQu = depthQu === 0 ? depthQu + 1 : 0;
|
|
36191
|
-
buffer += c;
|
|
36192
|
-
} else if (depthQu === 0) {
|
|
36193
|
-
if (c === "[") depthSq++;
|
|
36194
|
-
else if (c === "]") depthSq--;
|
|
36195
|
-
else if (c === "{") depthCu++;
|
|
36196
|
-
else if (c === "}") depthCu--;
|
|
36197
|
-
else if (c === "," && depthSq === 0 && depthCu === 0) {
|
|
36198
|
-
out.push(buffer);
|
|
36199
|
-
buffer = "";
|
|
36200
|
-
continue;
|
|
36201
|
-
}
|
|
36202
|
-
buffer += c;
|
|
36203
|
-
} else {
|
|
36204
|
-
buffer += c;
|
|
36205
|
-
}
|
|
36206
|
-
}
|
|
36207
|
-
if (buffer.trim()) out.push(buffer);
|
|
36208
|
-
return out;
|
|
36209
|
-
}
|
|
36210
|
-
function parseScalar(s) {
|
|
36211
|
-
if (s === "" || s === "null" || s === "~") return null;
|
|
36212
|
-
if (VALID_SCALARS.test(s)) return s.toLowerCase() === "true";
|
|
36213
|
-
if (/^-?\d+(\.\d+)?$/.test(s)) return Number(s);
|
|
36214
|
-
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
36215
|
-
return s.slice(1, -1);
|
|
36216
|
-
}
|
|
36217
|
-
return s;
|
|
36218
|
-
}
|
|
36219
|
-
function serializeYaml(value, indent = 0) {
|
|
36220
|
-
if (value === null || value === void 0) return "";
|
|
36221
|
-
if (typeof value === "string") {
|
|
36222
|
-
if (/[:#\n\[\]\{\},&*!|>'"%@`]/.test(value)) {
|
|
36223
|
-
return JSON.stringify(value);
|
|
36224
|
-
}
|
|
36225
|
-
return value;
|
|
36226
|
-
}
|
|
36227
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
36228
|
-
if (Array.isArray(value)) {
|
|
36229
|
-
if (value.length === 0) return "[]";
|
|
36230
|
-
if (value.every((v) => v === null || typeof v !== "object")) {
|
|
36231
|
-
return `[${value.map(serializeScalarInline).join(", ")}]`;
|
|
36232
|
-
}
|
|
36233
|
-
return `[${value.map((v) => "{" + serializeInlineObject(v) + "}").join(", ")}]`;
|
|
36234
|
-
}
|
|
36235
|
-
if (typeof value === "object") {
|
|
36236
|
-
const entries = Object.entries(value).filter(([, v]) => v !== void 0);
|
|
36237
|
-
return entries.map(([k, v]) => {
|
|
36238
|
-
if (v === null || v === void 0) return `${k}:`;
|
|
36239
|
-
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
36240
|
-
return `${k}: ${serializeScalarInline(v)}`;
|
|
36241
|
-
}
|
|
36242
|
-
if (Array.isArray(v)) {
|
|
36243
|
-
if (v.length === 0) return `${k}: []`;
|
|
36244
|
-
if (v.every((x) => x === null || typeof x !== "object")) {
|
|
36245
|
-
return `${k}: [${v.map(serializeScalarInline).join(", ")}]`;
|
|
36246
|
-
}
|
|
36247
|
-
return `${k}: [${v.map((x) => "{" + serializeInlineObject(x) + "}").join(", ")}]`;
|
|
36248
|
-
}
|
|
36249
|
-
return `${k}:
|
|
36250
|
-
${serializeYaml(v, indent + 2)}`;
|
|
36251
|
-
}).map((line) => `${" ".repeat(indent)}${line}`).join("\n");
|
|
36252
|
-
}
|
|
36253
|
-
return String(value);
|
|
36254
|
-
}
|
|
36255
|
-
function serializeScalarInline(v) {
|
|
36256
|
-
if (typeof v === "string" && /[:#\n\[\]\{\},&*!|>'"%@`]/.test(v)) return JSON.stringify(v);
|
|
36257
|
-
return String(v);
|
|
36258
|
-
}
|
|
36259
|
-
function serializeInlineObject(v) {
|
|
36260
|
-
if (v === null || typeof v !== "object" || Array.isArray(v)) {
|
|
36261
|
-
return serializeScalarInline(v);
|
|
36262
|
-
}
|
|
36263
|
-
const entries = Object.entries(v).filter(([, val]) => val !== void 0);
|
|
36264
|
-
return entries.map(([k, val]) => `${k}: ${serializeYaml(val)}`).join(", ");
|
|
36265
|
-
}
|
|
36266
|
-
function countIndent(line) {
|
|
36267
|
-
let i = 0;
|
|
36268
|
-
while (i < line.length && line[i] === " ") i++;
|
|
36269
|
-
return i;
|
|
36270
|
-
}
|
|
36271
|
-
var FRONTMATTER_RE, VALID_SCALARS, Storage, KeyedMutex, workspaceMutex;
|
|
36272
|
-
var init_storage = __esm({
|
|
36273
|
-
"src/cli/workspace/storage.ts"() {
|
|
36274
|
-
"use strict";
|
|
36275
|
-
FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
36276
|
-
VALID_SCALARS = /^(true|false|null|~)$/i;
|
|
36277
|
-
Storage = class {
|
|
36278
|
-
/** Read a Markdown file with frontmatter. Throws if not found. */
|
|
36279
|
-
read(path53) {
|
|
36280
|
-
if (!existsSync27(path53)) {
|
|
36281
|
-
throw new Error(`File not found: ${path53}`);
|
|
36282
|
-
}
|
|
36283
|
-
const md = readFileSync24(path53, "utf8");
|
|
36284
|
-
return parseFrontmatter(md);
|
|
36285
|
-
}
|
|
36286
|
-
/** Read a Markdown file; returns null if not found. */
|
|
36287
|
-
readIfExists(path53) {
|
|
36288
|
-
if (!existsSync27(path53)) return null;
|
|
36289
|
-
return this.read(path53);
|
|
36290
|
-
}
|
|
36291
|
-
/**
|
|
36292
|
-
* Write a Markdown file atomically (tmp + rename). Creates parent dirs.
|
|
36293
|
-
* The meta object is serialized as YAML frontmatter; body as Markdown.
|
|
36294
|
-
*/
|
|
36295
|
-
write(path53, meta3, body) {
|
|
36296
|
-
mkdirSync13(dirname4(path53), { recursive: true });
|
|
36297
|
-
const tmp = path53 + ".tmp-" + process.pid;
|
|
36298
|
-
const md = serializeFrontmatter(meta3, body);
|
|
36299
|
-
writeFileSync15(tmp, md, "utf8");
|
|
36300
|
-
renameSync2(tmp, path53);
|
|
36301
|
-
}
|
|
36302
|
-
/** List all .md files in a directory (non-recursive). */
|
|
36303
|
-
listMarkdown(dir) {
|
|
36304
|
-
if (!existsSync27(dir)) return [];
|
|
36305
|
-
return readdirSync7(dir).filter((f) => f.endsWith(".md") && !f.startsWith(".")).map((f) => join23(dir, f));
|
|
36306
|
-
}
|
|
36307
|
-
};
|
|
36308
|
-
KeyedMutex = class {
|
|
36309
|
-
chains = /* @__PURE__ */ new Map();
|
|
36310
|
-
async run(key, fn) {
|
|
36311
|
-
const prev2 = this.chains.get(key) ?? Promise.resolve();
|
|
36312
|
-
let release = () => {
|
|
36313
|
-
};
|
|
36314
|
-
const next = new Promise((resolve3) => {
|
|
36315
|
-
release = resolve3;
|
|
36316
|
-
});
|
|
36317
|
-
const chained = prev2.then(() => next);
|
|
36318
|
-
this.chains.set(key, chained);
|
|
36319
|
-
await prev2;
|
|
36320
|
-
try {
|
|
36321
|
-
return await fn();
|
|
36322
|
-
} finally {
|
|
36323
|
-
release();
|
|
36324
|
-
if (this.chains.get(key) === chained) {
|
|
36325
|
-
this.chains.delete(key);
|
|
36326
|
-
}
|
|
36327
|
-
}
|
|
36328
|
-
}
|
|
36329
|
-
};
|
|
36330
|
-
workspaceMutex = new KeyedMutex();
|
|
36331
|
-
}
|
|
36332
|
-
});
|
|
36333
|
-
|
|
36334
36861
|
// src/cli/workspace/stubs.ts
|
|
36335
36862
|
var stubs_exports = {};
|
|
36336
36863
|
__export(stubs_exports, {
|
|
@@ -36341,14 +36868,14 @@ __export(stubs_exports, {
|
|
|
36341
36868
|
resolveWorkspaceRoot: () => resolveWorkspaceRoot
|
|
36342
36869
|
});
|
|
36343
36870
|
import {
|
|
36344
|
-
existsSync as
|
|
36871
|
+
existsSync as existsSync29,
|
|
36345
36872
|
readdirSync as readdirSync8,
|
|
36346
|
-
writeFileSync as
|
|
36347
|
-
readFileSync as
|
|
36348
|
-
mkdirSync as
|
|
36349
|
-
renameSync as
|
|
36873
|
+
writeFileSync as writeFileSync17,
|
|
36874
|
+
readFileSync as readFileSync26,
|
|
36875
|
+
mkdirSync as mkdirSync15,
|
|
36876
|
+
renameSync as renameSync4
|
|
36350
36877
|
} from "node:fs";
|
|
36351
|
-
import { join as
|
|
36878
|
+
import { join as join25, basename as basename2, dirname as dirname6, relative as relative2 } from "node:path";
|
|
36352
36879
|
function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
36353
36880
|
const rootDir = resolveWorkspaceRoot(projectRoot);
|
|
36354
36881
|
return {
|
|
@@ -36358,19 +36885,21 @@ function createWorkspaceContext(projectRoot = process.cwd()) {
|
|
|
36358
36885
|
};
|
|
36359
36886
|
}
|
|
36360
36887
|
function planJsonPath(ctx) {
|
|
36361
|
-
return
|
|
36888
|
+
return join25(ctx.rootDir, "plan.json");
|
|
36362
36889
|
}
|
|
36363
36890
|
function readPlan(ctx) {
|
|
36364
36891
|
const jsonPath = planJsonPath(ctx);
|
|
36365
|
-
if (
|
|
36892
|
+
if (existsSync29(jsonPath)) {
|
|
36366
36893
|
try {
|
|
36367
36894
|
const parsed = JSON.parse(
|
|
36368
|
-
|
|
36895
|
+
readFileSync26(jsonPath, "utf8")
|
|
36369
36896
|
);
|
|
36897
|
+
const { phases, tasks, milestones, ...root } = parsed;
|
|
36370
36898
|
return {
|
|
36371
|
-
phases: Array.isArray(
|
|
36372
|
-
tasks: Array.isArray(
|
|
36373
|
-
milestones: Array.isArray(
|
|
36899
|
+
phases: Array.isArray(phases) ? phases : [],
|
|
36900
|
+
tasks: Array.isArray(tasks) ? tasks : [],
|
|
36901
|
+
milestones: Array.isArray(milestones) ? milestones : [],
|
|
36902
|
+
root
|
|
36374
36903
|
};
|
|
36375
36904
|
} catch {
|
|
36376
36905
|
}
|
|
@@ -36387,10 +36916,23 @@ function readPlan(ctx) {
|
|
|
36387
36916
|
}
|
|
36388
36917
|
function writePlan(ctx, summary) {
|
|
36389
36918
|
const jsonPath = planJsonPath(ctx);
|
|
36390
|
-
|
|
36919
|
+
mkdirSync15(dirname6(jsonPath), { recursive: true });
|
|
36391
36920
|
const tmp = jsonPath + ".tmp-" + process.pid;
|
|
36392
|
-
|
|
36393
|
-
|
|
36921
|
+
writeFileSync17(
|
|
36922
|
+
tmp,
|
|
36923
|
+
JSON.stringify(
|
|
36924
|
+
{
|
|
36925
|
+
...summary.root ?? {},
|
|
36926
|
+
phases: summary.phases,
|
|
36927
|
+
tasks: summary.tasks,
|
|
36928
|
+
milestones: summary.milestones
|
|
36929
|
+
},
|
|
36930
|
+
null,
|
|
36931
|
+
2
|
|
36932
|
+
),
|
|
36933
|
+
"utf8"
|
|
36934
|
+
);
|
|
36935
|
+
renameSync4(tmp, jsonPath);
|
|
36394
36936
|
const mdPath = workspaceFile(ctx.rootDir, "plan");
|
|
36395
36937
|
const summaryMeta = {
|
|
36396
36938
|
kind: "plan-summary",
|
|
@@ -36456,8 +36998,8 @@ function renderPlanBody(summary) {
|
|
|
36456
36998
|
return lines.join("\n");
|
|
36457
36999
|
}
|
|
36458
37000
|
function nextAdrId(ctx) {
|
|
36459
|
-
const decisionsDir =
|
|
36460
|
-
if (!
|
|
37001
|
+
const decisionsDir = join25(ctx.rootDir, "decisions");
|
|
37002
|
+
if (!existsSync29(decisionsDir)) return "001";
|
|
36461
37003
|
const existing = readdirSync8(decisionsDir).filter((f) => f.endsWith(".md")).map((f) => f.match(/^(\d+)-/)).filter((m) => !!m).map((m) => parseInt(m[1], 10));
|
|
36462
37004
|
const max = existing.length === 0 ? 0 : Math.max(...existing);
|
|
36463
37005
|
return String(max + 1).padStart(3, "0");
|
|
@@ -36499,7 +37041,7 @@ function addTaskRecord(ctx, summary, phaseId, t, options) {
|
|
|
36499
37041
|
status: "pending",
|
|
36500
37042
|
priority: t.priority
|
|
36501
37043
|
});
|
|
36502
|
-
const taskPath =
|
|
37044
|
+
const taskPath = join25(ctx.rootDir, "plan-tasks", `${id}.md`);
|
|
36503
37045
|
const meta3 = {
|
|
36504
37046
|
kind: "task",
|
|
36505
37047
|
id,
|
|
@@ -36541,7 +37083,7 @@ function addMilestoneRecord(ctx, summary, input) {
|
|
|
36541
37083
|
dueDate: input.dueDate,
|
|
36542
37084
|
targetVersion: version2
|
|
36543
37085
|
});
|
|
36544
|
-
const path53 =
|
|
37086
|
+
const path53 = join25(ctx.rootDir, "milestones", `${id}.md`);
|
|
36545
37087
|
const meta3 = {
|
|
36546
37088
|
kind: "milestone",
|
|
36547
37089
|
id,
|
|
@@ -36839,8 +37381,8 @@ function createNfrSpecStub(ctx) {
|
|
|
36839
37381
|
},
|
|
36840
37382
|
planFeatureKeywords: Array.isArray(args["planFeatureKeywords"]) ? args["planFeatureKeywords"] : void 0
|
|
36841
37383
|
};
|
|
36842
|
-
const outPath =
|
|
36843
|
-
|
|
37384
|
+
const outPath = join25(ctx.rootDir, "nfr-spec.json");
|
|
37385
|
+
writeFileSync17(outPath, JSON.stringify(spec, null, 2), "utf8");
|
|
36844
37386
|
return `NFR spec written to nfr-spec.json (${targets.length} target(s)).`;
|
|
36845
37387
|
});
|
|
36846
37388
|
}
|
|
@@ -36903,18 +37445,18 @@ function searchDocumentsStub(ctx) {
|
|
|
36903
37445
|
(w) => w.length >= 3 && w !== "or" && w !== "and" && w !== "the"
|
|
36904
37446
|
);
|
|
36905
37447
|
const files = [
|
|
36906
|
-
...ctx.storage.listMarkdown(
|
|
36907
|
-
...ctx.storage.listMarkdown(
|
|
36908
|
-
...ctx.storage.listMarkdown(
|
|
36909
|
-
...ctx.storage.listMarkdown(
|
|
36910
|
-
...ctx.storage.listMarkdown(
|
|
37448
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "decisions")),
|
|
37449
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "docs")),
|
|
37450
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "reviews")),
|
|
37451
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "plan-tasks")),
|
|
37452
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "milestones")),
|
|
36911
37453
|
workspaceFile(ctx.rootDir, "plan"),
|
|
36912
37454
|
workspaceFile(ctx.rootDir, "risks")
|
|
36913
37455
|
];
|
|
36914
37456
|
const results = [];
|
|
36915
37457
|
for (const file2 of files) {
|
|
36916
|
-
if (!
|
|
36917
|
-
const raw =
|
|
37458
|
+
if (!existsSync29(file2)) continue;
|
|
37459
|
+
const raw = readFileSync26(file2, "utf8");
|
|
36918
37460
|
const content = raw.toLowerCase();
|
|
36919
37461
|
let idx = -1;
|
|
36920
37462
|
let matchLen = 0;
|
|
@@ -36965,9 +37507,9 @@ function linkDocumentsStub(ctx) {
|
|
|
36965
37507
|
const toId = args["toId"] ?? args["targetId"] ?? args["targetPathOrTitle"];
|
|
36966
37508
|
if (!fromId || !toId) return "linkDocuments requires fromId and toId.";
|
|
36967
37509
|
const allFiles = [
|
|
36968
|
-
...ctx.storage.listMarkdown(
|
|
36969
|
-
...ctx.storage.listMarkdown(
|
|
36970
|
-
...ctx.storage.listMarkdown(
|
|
37510
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "decisions")),
|
|
37511
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "docs")),
|
|
37512
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "reviews"))
|
|
36971
37513
|
];
|
|
36972
37514
|
const source = allFiles.find((f) => {
|
|
36973
37515
|
try {
|
|
@@ -36998,9 +37540,9 @@ function getDocumentBacklinksStub(ctx) {
|
|
|
36998
37540
|
const targetId = args["targetId"] ?? args["id"];
|
|
36999
37541
|
if (!targetId) return "getDocumentBacklinks requires targetId.";
|
|
37000
37542
|
const allFiles = [
|
|
37001
|
-
...ctx.storage.listMarkdown(
|
|
37002
|
-
...ctx.storage.listMarkdown(
|
|
37003
|
-
...ctx.storage.listMarkdown(
|
|
37543
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "decisions")),
|
|
37544
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "docs")),
|
|
37545
|
+
...ctx.storage.listMarkdown(join25(ctx.rootDir, "reviews"))
|
|
37004
37546
|
];
|
|
37005
37547
|
const backlinks = [];
|
|
37006
37548
|
for (const file2 of allFiles) {
|
|
@@ -37095,7 +37637,7 @@ __export(updater_exports, {
|
|
|
37095
37637
|
});
|
|
37096
37638
|
import { createRequire as createRequire2 } from "node:module";
|
|
37097
37639
|
import { spawn as spawn8 } from "node:child_process";
|
|
37098
|
-
import { existsSync as
|
|
37640
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
37099
37641
|
import path31 from "node:path";
|
|
37100
37642
|
import { fileURLToPath } from "node:url";
|
|
37101
37643
|
function resolveBundledNpmCli(execPath = process.execPath) {
|
|
@@ -37108,7 +37650,7 @@ function resolveBundledNpmCli(execPath = process.execPath) {
|
|
|
37108
37650
|
];
|
|
37109
37651
|
for (const candidate of candidates) {
|
|
37110
37652
|
try {
|
|
37111
|
-
if (
|
|
37653
|
+
if (existsSync30(candidate)) return candidate;
|
|
37112
37654
|
} catch {
|
|
37113
37655
|
}
|
|
37114
37656
|
}
|
|
@@ -37408,23 +37950,23 @@ var init_mcpClient = __esm({
|
|
|
37408
37950
|
|
|
37409
37951
|
// src/cli/mcp/mcpConfigIo.ts
|
|
37410
37952
|
import {
|
|
37411
|
-
existsSync as
|
|
37412
|
-
mkdirSync as
|
|
37413
|
-
readFileSync as
|
|
37414
|
-
writeFileSync as
|
|
37953
|
+
existsSync as existsSync31,
|
|
37954
|
+
mkdirSync as mkdirSync16,
|
|
37955
|
+
readFileSync as readFileSync27,
|
|
37956
|
+
writeFileSync as writeFileSync18
|
|
37415
37957
|
} from "node:fs";
|
|
37416
|
-
import { dirname as
|
|
37958
|
+
import { dirname as dirname7, join as join26 } from "node:path";
|
|
37417
37959
|
import { homedir as homedir10 } from "node:os";
|
|
37418
37960
|
function getUserMcpPath() {
|
|
37419
|
-
return
|
|
37961
|
+
return join26(homedir10(), ".zelari-code", "mcp.json");
|
|
37420
37962
|
}
|
|
37421
37963
|
function getProjectMcpPath(projectRoot) {
|
|
37422
|
-
return
|
|
37964
|
+
return join26(projectRoot, ".zelari", "mcp.json");
|
|
37423
37965
|
}
|
|
37424
37966
|
function readFile2(path53) {
|
|
37425
|
-
if (!
|
|
37967
|
+
if (!existsSync31(path53)) return {};
|
|
37426
37968
|
try {
|
|
37427
|
-
const parsed = JSON.parse(
|
|
37969
|
+
const parsed = JSON.parse(readFileSync27(path53, "utf8"));
|
|
37428
37970
|
const out = {};
|
|
37429
37971
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
37430
37972
|
if (!cfg || typeof cfg.command !== "string" || !cfg.command.trim()) continue;
|
|
@@ -37441,9 +37983,9 @@ function readFile2(path53) {
|
|
|
37441
37983
|
}
|
|
37442
37984
|
}
|
|
37443
37985
|
function writeFile(path53, servers) {
|
|
37444
|
-
|
|
37986
|
+
mkdirSync16(dirname7(path53), { recursive: true });
|
|
37445
37987
|
const body = { mcpServers: servers };
|
|
37446
|
-
|
|
37988
|
+
writeFileSync18(path53, `${JSON.stringify(body, null, 2)}
|
|
37447
37989
|
`, "utf8");
|
|
37448
37990
|
}
|
|
37449
37991
|
function listMcpServers(projectRoot) {
|
|
@@ -37652,22 +38194,22 @@ __export(mcpManager_exports, {
|
|
|
37652
38194
|
readMcpConfig: () => readMcpConfig,
|
|
37653
38195
|
registerMcpTools: () => registerMcpTools
|
|
37654
38196
|
});
|
|
37655
|
-
import { existsSync as
|
|
37656
|
-
import { join as
|
|
38197
|
+
import { existsSync as existsSync32, readFileSync as readFileSync28 } from "node:fs";
|
|
38198
|
+
import { join as join27 } from "node:path";
|
|
37657
38199
|
import { homedir as homedir11 } from "node:os";
|
|
37658
38200
|
function readMcpConfig(projectRoot = process.cwd(), opts) {
|
|
37659
38201
|
const merged = {};
|
|
37660
38202
|
const paths = [];
|
|
37661
38203
|
if (process.env["ZELARI_MCP_USER"] !== "0") {
|
|
37662
|
-
paths.push(
|
|
38204
|
+
paths.push(join27(homedir11(), ".zelari-code", "mcp.json"));
|
|
37663
38205
|
}
|
|
37664
38206
|
if (!opts?.skipProjectMcp) {
|
|
37665
|
-
paths.push(
|
|
38207
|
+
paths.push(join27(projectRoot, ".zelari", "mcp.json"));
|
|
37666
38208
|
}
|
|
37667
38209
|
for (const p3 of paths) {
|
|
37668
|
-
if (!
|
|
38210
|
+
if (!existsSync32(p3)) continue;
|
|
37669
38211
|
try {
|
|
37670
|
-
const parsed = JSON.parse(
|
|
38212
|
+
const parsed = JSON.parse(readFileSync28(p3, "utf8"));
|
|
37671
38213
|
for (const [name, cfg] of Object.entries(parsed.mcpServers ?? {})) {
|
|
37672
38214
|
if (!cfg || typeof cfg.command !== "string" || cfg.command.length === 0) continue;
|
|
37673
38215
|
merged[name] = cfg;
|
|
@@ -37681,7 +38223,7 @@ async function ensureLoaded(projectRoot) {
|
|
|
37681
38223
|
if (state2.loaded) return;
|
|
37682
38224
|
state2.loaded = true;
|
|
37683
38225
|
const trusted = isFolderTrusted(projectRoot);
|
|
37684
|
-
if (!trusted &&
|
|
38226
|
+
if (!trusted && existsSync32(join27(projectRoot, ".zelari", "mcp.json"))) {
|
|
37685
38227
|
state2.warnings.push(
|
|
37686
38228
|
"[mcp] project .zelari/mcp.json ignored \u2014 folder not trusted (run /trust or `zelari-code --trust` to enable project MCP)"
|
|
37687
38229
|
);
|
|
@@ -37906,13 +38448,13 @@ __export(agentsMd_exports, {
|
|
|
37906
38448
|
serializeAgentsMd: () => serializeAgentsMd,
|
|
37907
38449
|
updateAgentsMd: () => updateAgentsMd
|
|
37908
38450
|
});
|
|
37909
|
-
import { existsSync as
|
|
37910
|
-
import { createHash as
|
|
37911
|
-
import { join as
|
|
38451
|
+
import { existsSync as existsSync33, readFileSync as readFileSync29, writeFileSync as writeFileSync19 } from "node:fs";
|
|
38452
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
38453
|
+
import { join as join28 } from "node:path";
|
|
37912
38454
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
37913
38455
|
async function readPackageJson2(projectRoot) {
|
|
37914
|
-
const path53 =
|
|
37915
|
-
if (!
|
|
38456
|
+
const path53 = join28(projectRoot, "package.json");
|
|
38457
|
+
if (!existsSync33(path53)) return null;
|
|
37916
38458
|
try {
|
|
37917
38459
|
return JSON.parse(await readFile3(path53, "utf8"));
|
|
37918
38460
|
} catch {
|
|
@@ -37936,8 +38478,8 @@ async function genTechStack(ctx) {
|
|
|
37936
38478
|
].join("\n");
|
|
37937
38479
|
}
|
|
37938
38480
|
async function genDecisions(ctx) {
|
|
37939
|
-
const decisionsDir =
|
|
37940
|
-
if (!
|
|
38481
|
+
const decisionsDir = join28(ctx.rootDir, "decisions");
|
|
38482
|
+
if (!existsSync33(decisionsDir)) return "_No ADRs yet._";
|
|
37941
38483
|
const files = ctx.storage.listMarkdown(decisionsDir).sort();
|
|
37942
38484
|
const accepted = [];
|
|
37943
38485
|
const proposed = [];
|
|
@@ -37962,9 +38504,9 @@ async function genDecisions(ctx) {
|
|
|
37962
38504
|
}
|
|
37963
38505
|
async function genConventions(ctx) {
|
|
37964
38506
|
const lines = [];
|
|
37965
|
-
const claudeMd =
|
|
37966
|
-
if (
|
|
37967
|
-
const content =
|
|
38507
|
+
const claudeMd = join28(ctx.projectRoot, "CLAUDE.MD");
|
|
38508
|
+
if (existsSync33(claudeMd)) {
|
|
38509
|
+
const content = readFileSync29(claudeMd, "utf8");
|
|
37968
38510
|
const match = content.match(/## Architecture rules[\s\S]+?(?=\n## |\n*$)/);
|
|
37969
38511
|
if (match) {
|
|
37970
38512
|
lines.push('<!-- Extracted from CLAUDE.MD "Architecture rules" -->');
|
|
@@ -37996,9 +38538,9 @@ async function genBuild(ctx) {
|
|
|
37996
38538
|
].join("\n");
|
|
37997
38539
|
}
|
|
37998
38540
|
async function genOpenQuestions(ctx) {
|
|
37999
|
-
const path53 =
|
|
38000
|
-
if (!
|
|
38001
|
-
const content =
|
|
38541
|
+
const path53 = join28(ctx.rootDir, "risks.md");
|
|
38542
|
+
if (!existsSync33(path53)) return "_No open questions._";
|
|
38543
|
+
const content = readFileSync29(path53, "utf8");
|
|
38002
38544
|
const lines = content.split("\n");
|
|
38003
38545
|
const questions = [];
|
|
38004
38546
|
let currentTitle = "";
|
|
@@ -38072,9 +38614,9 @@ function titleCase(id) {
|
|
|
38072
38614
|
return id.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join(" ");
|
|
38073
38615
|
}
|
|
38074
38616
|
async function updateAgentsMd(ctx, projectRoot) {
|
|
38075
|
-
const agentsPath =
|
|
38076
|
-
if (
|
|
38077
|
-
const content =
|
|
38617
|
+
const agentsPath = join28(projectRoot, "AGENTS.MD");
|
|
38618
|
+
if (existsSync33(agentsPath)) {
|
|
38619
|
+
const content = readFileSync29(agentsPath, "utf8");
|
|
38078
38620
|
const hasAnyMarker = AUTO_SECTIONS.some((id) => content.includes(MARKER_OPEN(id)));
|
|
38079
38621
|
if (!hasAnyMarker) {
|
|
38080
38622
|
return {
|
|
@@ -38089,8 +38631,8 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
38089
38631
|
newSections.set(id, await GENERATORS[id](ctx));
|
|
38090
38632
|
}
|
|
38091
38633
|
let manualContent = "";
|
|
38092
|
-
if (
|
|
38093
|
-
const { manualBlocks } = parseAgentsMd(
|
|
38634
|
+
if (existsSync33(agentsPath)) {
|
|
38635
|
+
const { manualBlocks } = parseAgentsMd(readFileSync29(agentsPath, "utf8"));
|
|
38094
38636
|
manualContent = manualBlocks.after;
|
|
38095
38637
|
} else {
|
|
38096
38638
|
const projectName2 = projectName(projectRoot);
|
|
@@ -38106,7 +38648,7 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
38106
38648
|
""
|
|
38107
38649
|
].join("\n");
|
|
38108
38650
|
}
|
|
38109
|
-
const oldContent =
|
|
38651
|
+
const oldContent = existsSync33(agentsPath) ? readFileSync29(agentsPath, "utf8") : "";
|
|
38110
38652
|
const { sections: oldSections } = parseAgentsMd(oldContent);
|
|
38111
38653
|
const changedSections = [];
|
|
38112
38654
|
for (const id of AUTO_SECTIONS) {
|
|
@@ -38118,11 +38660,11 @@ async function updateAgentsMd(ctx, projectRoot) {
|
|
|
38118
38660
|
return { changed: false, sections: [] };
|
|
38119
38661
|
}
|
|
38120
38662
|
const newContent = serializeAgentsMd(manualContent, newSections);
|
|
38121
|
-
|
|
38663
|
+
writeFileSync19(agentsPath, newContent, "utf8");
|
|
38122
38664
|
return { changed: true, sections: changedSections };
|
|
38123
38665
|
}
|
|
38124
38666
|
function hash2(s) {
|
|
38125
|
-
return
|
|
38667
|
+
return createHash8("sha256").update(s).digest("hex").slice(0, 16);
|
|
38126
38668
|
}
|
|
38127
38669
|
var AUTO_SECTIONS, MARKER_OPEN, MARKER_CLOSE, GENERATORS;
|
|
38128
38670
|
var init_agentsMd = __esm({
|
|
@@ -38244,8 +38786,8 @@ var init_completeDesign = __esm({
|
|
|
38244
38786
|
|
|
38245
38787
|
// src/cli/workspace/projectSmoke.ts
|
|
38246
38788
|
import { spawn as spawn10 } from "node:child_process";
|
|
38247
|
-
import { existsSync as
|
|
38248
|
-
import { join as
|
|
38789
|
+
import { existsSync as existsSync34, readFileSync as readFileSync30 } from "node:fs";
|
|
38790
|
+
import { join as join29 } from "node:path";
|
|
38249
38791
|
function pickSmokeScript(scripts) {
|
|
38250
38792
|
if (!scripts) return null;
|
|
38251
38793
|
for (const name of SMOKE_SCRIPT_PRIORITY) {
|
|
@@ -38257,13 +38799,13 @@ async function runProjectSmoke(projectRoot, timeoutMs = DEFAULT_TIMEOUT_MS2) {
|
|
|
38257
38799
|
if (process.env["ZELARI_SMOKE"] === "0") {
|
|
38258
38800
|
return { ran: false, reason: "ZELARI_SMOKE=0 (disabled)" };
|
|
38259
38801
|
}
|
|
38260
|
-
const pkgPath =
|
|
38261
|
-
if (!
|
|
38802
|
+
const pkgPath = join29(projectRoot, "package.json");
|
|
38803
|
+
if (!existsSync34(pkgPath)) {
|
|
38262
38804
|
return { ran: false, reason: "no package.json (skipped)" };
|
|
38263
38805
|
}
|
|
38264
38806
|
let scripts = {};
|
|
38265
38807
|
try {
|
|
38266
|
-
const pkg = JSON.parse(
|
|
38808
|
+
const pkg = JSON.parse(readFileSync30(pkgPath, "utf8"));
|
|
38267
38809
|
scripts = pkg.scripts ?? {};
|
|
38268
38810
|
} catch {
|
|
38269
38811
|
return { ran: false, reason: "package.json unreadable (skipped)" };
|
|
@@ -38351,8 +38893,8 @@ __export(postCouncilHook_exports, {
|
|
|
38351
38893
|
runPostCouncilHook: () => runPostCouncilHook
|
|
38352
38894
|
});
|
|
38353
38895
|
import { spawn as spawn11 } from "node:child_process";
|
|
38354
|
-
import { existsSync as
|
|
38355
|
-
import { join as
|
|
38896
|
+
import { existsSync as existsSync35, readFileSync as readFileSync31 } from "node:fs";
|
|
38897
|
+
import { join as join30 } from "node:path";
|
|
38356
38898
|
async function runCompleteDesignPostProcessor(ctx, options) {
|
|
38357
38899
|
if (options?.runMode === "implementation") {
|
|
38358
38900
|
return {
|
|
@@ -38363,9 +38905,9 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
38363
38905
|
if (process.env["ZELARI_COMPLETE_DESIGN"] === "0") {
|
|
38364
38906
|
return { ran: false, reason: "ZELARI_COMPLETE_DESIGN=0 (disabled)" };
|
|
38365
38907
|
}
|
|
38366
|
-
const planJsonPath2 =
|
|
38367
|
-
const scriptPath =
|
|
38368
|
-
if (!
|
|
38908
|
+
const planJsonPath2 = join30(ctx.rootDir, "plan.json");
|
|
38909
|
+
const scriptPath = join30(ctx.projectRoot, "complete-design.mjs");
|
|
38910
|
+
if (!existsSync35(planJsonPath2)) {
|
|
38369
38911
|
return {
|
|
38370
38912
|
ran: false,
|
|
38371
38913
|
reason: ".zelari/plan.json missing (not design-phase)"
|
|
@@ -38373,7 +38915,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
38373
38915
|
}
|
|
38374
38916
|
let phaseCount = 0;
|
|
38375
38917
|
try {
|
|
38376
|
-
const parsed = JSON.parse(
|
|
38918
|
+
const parsed = JSON.parse(readFileSync31(planJsonPath2, "utf8"));
|
|
38377
38919
|
phaseCount = Array.isArray(parsed.phases) ? parsed.phases.length : 0;
|
|
38378
38920
|
} catch {
|
|
38379
38921
|
return { ran: false, reason: ".zelari/plan.json corrupt" };
|
|
@@ -38381,7 +38923,7 @@ async function runCompleteDesignPostProcessor(ctx, options) {
|
|
|
38381
38923
|
if (phaseCount === 0) {
|
|
38382
38924
|
return { ran: false, reason: ".zelari/plan.json has no phases" };
|
|
38383
38925
|
}
|
|
38384
|
-
if (!
|
|
38926
|
+
if (!existsSync35(scriptPath)) {
|
|
38385
38927
|
try {
|
|
38386
38928
|
const builtin = await runBuiltinCompleteDesign(ctx);
|
|
38387
38929
|
return {
|
|
@@ -38604,10 +39146,10 @@ __export(councilFeedback_exports, {
|
|
|
38604
39146
|
});
|
|
38605
39147
|
import {
|
|
38606
39148
|
promises as fs16,
|
|
38607
|
-
existsSync as
|
|
38608
|
-
readFileSync as
|
|
38609
|
-
writeFileSync as
|
|
38610
|
-
mkdirSync as
|
|
39149
|
+
existsSync as existsSync36,
|
|
39150
|
+
readFileSync as readFileSync32,
|
|
39151
|
+
writeFileSync as writeFileSync20,
|
|
39152
|
+
mkdirSync as mkdirSync17
|
|
38611
39153
|
} from "node:fs";
|
|
38612
39154
|
import path32 from "node:path";
|
|
38613
39155
|
import os9 from "node:os";
|
|
@@ -38713,9 +39255,9 @@ var init_councilFeedback = __esm({
|
|
|
38713
39255
|
}
|
|
38714
39256
|
// --- persistence ---------------------------------------------------------
|
|
38715
39257
|
load() {
|
|
38716
|
-
if (!
|
|
39258
|
+
if (!existsSync36(this.file)) return;
|
|
38717
39259
|
try {
|
|
38718
|
-
const raw =
|
|
39260
|
+
const raw = readFileSync32(this.file, "utf-8");
|
|
38719
39261
|
const parsed = JSON.parse(raw);
|
|
38720
39262
|
if (parsed && Array.isArray(parsed.entries)) {
|
|
38721
39263
|
this.entries = parsed.entries.filter(
|
|
@@ -38726,8 +39268,8 @@ var init_councilFeedback = __esm({
|
|
|
38726
39268
|
}
|
|
38727
39269
|
}
|
|
38728
39270
|
save() {
|
|
38729
|
-
|
|
38730
|
-
|
|
39271
|
+
mkdirSync17(path32.dirname(this.file), { recursive: true });
|
|
39272
|
+
writeFileSync20(
|
|
38731
39273
|
this.file,
|
|
38732
39274
|
JSON.stringify({ entries: this.entries }, null, 2),
|
|
38733
39275
|
{ encoding: "utf-8", mode: 384 }
|
|
@@ -38933,7 +39475,7 @@ __export(commitHelpers_exports, {
|
|
|
38933
39475
|
});
|
|
38934
39476
|
async function tryStateCommit(args) {
|
|
38935
39477
|
try {
|
|
38936
|
-
const
|
|
39478
|
+
const store4 = args.store ?? await getStateStore(args.projectRoot, args.env);
|
|
38937
39479
|
let workspaceCheckpointId = args.workspaceCheckpointId;
|
|
38938
39480
|
if (!workspaceCheckpointId && args.withCheckpoint && (args.env ?? process.env).ZELARI_CHECKPOINT !== "0") {
|
|
38939
39481
|
const cp = await createCheckpoint(
|
|
@@ -38942,7 +39484,7 @@ async function tryStateCommit(args) {
|
|
|
38942
39484
|
);
|
|
38943
39485
|
if (cp.ok) workspaceCheckpointId = cp.value.id;
|
|
38944
39486
|
}
|
|
38945
|
-
const meta3 = await
|
|
39487
|
+
const meta3 = await store4.commit({
|
|
38946
39488
|
mode: args.mode,
|
|
38947
39489
|
label: args.label,
|
|
38948
39490
|
layer: args.layer,
|
|
@@ -41153,7 +41695,7 @@ __export(executor_exports, {
|
|
|
41153
41695
|
resolveNodeTimeoutMs: () => resolveNodeTimeoutMs,
|
|
41154
41696
|
thoroughnessForKind: () => thoroughnessForKind
|
|
41155
41697
|
});
|
|
41156
|
-
import { existsSync as
|
|
41698
|
+
import { existsSync as existsSync37 } from "node:fs";
|
|
41157
41699
|
import path40 from "node:path";
|
|
41158
41700
|
function resolveMaxParallel(env = process.env) {
|
|
41159
41701
|
const raw = env.ZELARI_KRAKEN_MAX_PARALLEL;
|
|
@@ -41212,7 +41754,7 @@ function isWorldModelGateEnabled(cwd, env = process.env, checksExists = defaultC
|
|
|
41212
41754
|
}
|
|
41213
41755
|
function defaultChecksExists(cwd) {
|
|
41214
41756
|
try {
|
|
41215
|
-
return
|
|
41757
|
+
return existsSync37(path40.join(cwd, ".zelari", "world", "checks.json"));
|
|
41216
41758
|
} catch {
|
|
41217
41759
|
return false;
|
|
41218
41760
|
}
|
|
@@ -42179,8 +42721,8 @@ __export(prereqChecks_exports, {
|
|
|
42179
42721
|
runPrereqChecks: () => runPrereqChecks
|
|
42180
42722
|
});
|
|
42181
42723
|
import { execSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
42182
|
-
import { existsSync as
|
|
42183
|
-
import { dirname as
|
|
42724
|
+
import { existsSync as existsSync38 } from "node:fs";
|
|
42725
|
+
import { dirname as dirname8 } from "node:path";
|
|
42184
42726
|
function isWslBashPath2(p3) {
|
|
42185
42727
|
if (!p3 || typeof p3 !== "string") return false;
|
|
42186
42728
|
const n = p3.replace(/\//g, "\\").toLowerCase();
|
|
@@ -42286,7 +42828,7 @@ function resolveAgentShellSync() {
|
|
|
42286
42828
|
function agentProbeEnv() {
|
|
42287
42829
|
const env = { ...process.env };
|
|
42288
42830
|
try {
|
|
42289
|
-
const nodeDir =
|
|
42831
|
+
const nodeDir = dirname8(process.execPath);
|
|
42290
42832
|
if (!nodeDir) return env;
|
|
42291
42833
|
const sep2 = process.platform === "win32" ? ";" : ":";
|
|
42292
42834
|
const current = env.PATH ?? env.Path ?? "";
|
|
@@ -42303,7 +42845,7 @@ function agentProbeEnv() {
|
|
|
42303
42845
|
}
|
|
42304
42846
|
function existsSyncSafe2(p3) {
|
|
42305
42847
|
try {
|
|
42306
|
-
return
|
|
42848
|
+
return existsSync38(p3);
|
|
42307
42849
|
} catch {
|
|
42308
42850
|
return false;
|
|
42309
42851
|
}
|
|
@@ -42550,7 +43092,7 @@ var init_prereqChecks = __esm({
|
|
|
42550
43092
|
});
|
|
42551
43093
|
|
|
42552
43094
|
// src/cli/plugins/prefs.ts
|
|
42553
|
-
import { existsSync as
|
|
43095
|
+
import { existsSync as existsSync39, readFileSync as readFileSync33, writeFileSync as writeFileSync21, mkdirSync as mkdirSync18 } from "node:fs";
|
|
42554
43096
|
import path43 from "node:path";
|
|
42555
43097
|
import os10 from "node:os";
|
|
42556
43098
|
function getPluginPrefsPath() {
|
|
@@ -42559,8 +43101,8 @@ function getPluginPrefsPath() {
|
|
|
42559
43101
|
function getPluginPrefs() {
|
|
42560
43102
|
const file2 = getPluginPrefsPath();
|
|
42561
43103
|
try {
|
|
42562
|
-
if (!
|
|
42563
|
-
const raw =
|
|
43104
|
+
if (!existsSync39(file2)) return { ...DEFAULTS2, dontAskAgain: {} };
|
|
43105
|
+
const raw = readFileSync33(file2, "utf-8");
|
|
42564
43106
|
const parsed = JSON.parse(raw);
|
|
42565
43107
|
if (parsed && typeof parsed === "object" && parsed.dontAskAgain && typeof parsed.dontAskAgain === "object") {
|
|
42566
43108
|
const clean = {};
|
|
@@ -42575,8 +43117,8 @@ function getPluginPrefs() {
|
|
|
42575
43117
|
}
|
|
42576
43118
|
function writePluginPrefs(prefs) {
|
|
42577
43119
|
const file2 = getPluginPrefsPath();
|
|
42578
|
-
|
|
42579
|
-
|
|
43120
|
+
mkdirSync18(path43.dirname(file2), { recursive: true });
|
|
43121
|
+
writeFileSync21(file2, JSON.stringify(prefs, null, 2), {
|
|
42580
43122
|
encoding: "utf-8",
|
|
42581
43123
|
mode: 384
|
|
42582
43124
|
});
|
|
@@ -42611,7 +43153,7 @@ __export(registry_exports, {
|
|
|
42611
43153
|
findPlugin: () => findPlugin,
|
|
42612
43154
|
isBinaryOnPath: () => isBinaryOnPath
|
|
42613
43155
|
});
|
|
42614
|
-
import { existsSync as
|
|
43156
|
+
import { existsSync as existsSync40 } from "node:fs";
|
|
42615
43157
|
import path44 from "node:path";
|
|
42616
43158
|
function detectLocalBin(bin) {
|
|
42617
43159
|
return (cwd) => {
|
|
@@ -42628,7 +43170,7 @@ function isBinaryOnPath(bin, opts = {}) {
|
|
|
42628
43170
|
return false;
|
|
42629
43171
|
}
|
|
42630
43172
|
const platform = opts.platform ?? process.platform;
|
|
42631
|
-
const exists = opts.exists ??
|
|
43173
|
+
const exists = opts.exists ?? existsSync40;
|
|
42632
43174
|
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
42633
43175
|
const pathMod = platform === "win32" ? path44.win32 : path44.posix;
|
|
42634
43176
|
const sep2 = platform === "win32" ? ";" : ":";
|
|
@@ -43362,7 +43904,7 @@ __export(atMentions_exports, {
|
|
|
43362
43904
|
extractAtMentions: () => extractAtMentions,
|
|
43363
43905
|
hasAtMentions: () => hasAtMentions
|
|
43364
43906
|
});
|
|
43365
|
-
import { existsSync as
|
|
43907
|
+
import { existsSync as existsSync43, readFileSync as readFileSync35, statSync as statSync7 } from "node:fs";
|
|
43366
43908
|
import { basename as basename3, isAbsolute as isAbsolute2, relative as relative3, resolve, sep } from "node:path";
|
|
43367
43909
|
function isImagePath(abs) {
|
|
43368
43910
|
const ext = abs.split(".").pop()?.toLowerCase() ?? "";
|
|
@@ -43416,7 +43958,7 @@ function resolveMention(token, cwd) {
|
|
|
43416
43958
|
note: "outside project root \u2014 skipped"
|
|
43417
43959
|
};
|
|
43418
43960
|
}
|
|
43419
|
-
if (!
|
|
43961
|
+
if (!existsSync43(abs)) {
|
|
43420
43962
|
return {
|
|
43421
43963
|
raw: token,
|
|
43422
43964
|
path: token,
|
|
@@ -43468,7 +44010,7 @@ function resolveMention(token, cwd) {
|
|
|
43468
44010
|
note: `image too large (${Math.round(st.size / 1024)} KB) \u2014 path only`
|
|
43469
44011
|
};
|
|
43470
44012
|
}
|
|
43471
|
-
const dataBase64 =
|
|
44013
|
+
const dataBase64 = readFileSync35(abs).toString("base64");
|
|
43472
44014
|
return {
|
|
43473
44015
|
raw: token,
|
|
43474
44016
|
path: rel2,
|
|
@@ -43479,7 +44021,7 @@ function resolveMention(token, cwd) {
|
|
|
43479
44021
|
};
|
|
43480
44022
|
}
|
|
43481
44023
|
try {
|
|
43482
|
-
const buf =
|
|
44024
|
+
const buf = readFileSync35(abs);
|
|
43483
44025
|
const head = buf.subarray(0, 800).toString("utf8");
|
|
43484
44026
|
if (!isProbablyText(abs, head)) {
|
|
43485
44027
|
return {
|
|
@@ -43976,14 +44518,14 @@ var init_skillCategories = __esm({
|
|
|
43976
44518
|
|
|
43977
44519
|
// src/cli/skillConfigIo.ts
|
|
43978
44520
|
import {
|
|
43979
|
-
existsSync as
|
|
43980
|
-
mkdirSync as
|
|
44521
|
+
existsSync as existsSync45,
|
|
44522
|
+
mkdirSync as mkdirSync21,
|
|
43981
44523
|
readdirSync as readdirSync9,
|
|
43982
|
-
readFileSync as
|
|
44524
|
+
readFileSync as readFileSync37,
|
|
43983
44525
|
rmSync as rmSync4,
|
|
43984
|
-
writeFileSync as
|
|
44526
|
+
writeFileSync as writeFileSync23
|
|
43985
44527
|
} from "node:fs";
|
|
43986
|
-
import { dirname as
|
|
44528
|
+
import { dirname as dirname10, join as join36 } from "node:path";
|
|
43987
44529
|
import { homedir as homedir12 } from "node:os";
|
|
43988
44530
|
function ensureBuiltinSkillsLoadedSync() {
|
|
43989
44531
|
if (builtinsLoaded) return;
|
|
@@ -43995,13 +44537,13 @@ function ensureBuiltinSkillsLoadedSync() {
|
|
|
43995
44537
|
}
|
|
43996
44538
|
}
|
|
43997
44539
|
function getUserSkillsDir() {
|
|
43998
|
-
return
|
|
44540
|
+
return join36(homedir12(), ".zelari-code", "skills");
|
|
43999
44541
|
}
|
|
44000
44542
|
function getProjectSkillsDir(projectRoot) {
|
|
44001
|
-
return
|
|
44543
|
+
return join36(projectRoot, ".zelari", "skills");
|
|
44002
44544
|
}
|
|
44003
44545
|
function skillFilePath(dir, name) {
|
|
44004
|
-
return
|
|
44546
|
+
return join36(dir, name, "SKILL.md");
|
|
44005
44547
|
}
|
|
44006
44548
|
function classifyScope(skillPath, projectRoot) {
|
|
44007
44549
|
const userDir = getUserSkillsDir().replace(/\\/g, "/");
|
|
@@ -44050,7 +44592,7 @@ function entryFromBuiltin(skill) {
|
|
|
44050
44592
|
};
|
|
44051
44593
|
}
|
|
44052
44594
|
function scanSkillsDir(dir, projectRoot, seen, out) {
|
|
44053
|
-
if (!
|
|
44595
|
+
if (!existsSync45(dir)) return;
|
|
44054
44596
|
let entries;
|
|
44055
44597
|
try {
|
|
44056
44598
|
entries = readdirSync9(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -44059,9 +44601,9 @@ function scanSkillsDir(dir, projectRoot, seen, out) {
|
|
|
44059
44601
|
}
|
|
44060
44602
|
for (const entry of entries) {
|
|
44061
44603
|
const skillPath = skillFilePath(dir, entry);
|
|
44062
|
-
if (!
|
|
44604
|
+
if (!existsSync45(skillPath)) continue;
|
|
44063
44605
|
try {
|
|
44064
|
-
const parsed = parseSkillMd(
|
|
44606
|
+
const parsed = parseSkillMd(readFileSync37(skillPath, "utf8"), skillPath);
|
|
44065
44607
|
if (!parsed) continue;
|
|
44066
44608
|
if (seen.has(parsed.name)) continue;
|
|
44067
44609
|
seen.add(parsed.name);
|
|
@@ -44077,9 +44619,9 @@ function listSkillsSnapshot(projectRoot) {
|
|
|
44077
44619
|
const skills = [];
|
|
44078
44620
|
const seen = /* @__PURE__ */ new Set();
|
|
44079
44621
|
if (root) {
|
|
44080
|
-
scanSkillsDir(
|
|
44081
|
-
scanSkillsDir(
|
|
44082
|
-
scanSkillsDir(
|
|
44622
|
+
scanSkillsDir(join36(root, ".zelari", "skills"), root, seen, skills);
|
|
44623
|
+
scanSkillsDir(join36(root, ".claude", "skills"), root, seen, skills);
|
|
44624
|
+
scanSkillsDir(join36(root, ".opencode", "skills"), root, seen, skills);
|
|
44083
44625
|
}
|
|
44084
44626
|
scanSkillsDir(userSkillsDir, root, seen, skills);
|
|
44085
44627
|
for (const s of listCodingSkills()) {
|
|
@@ -44158,8 +44700,8 @@ function upsertSkill(opts) {
|
|
|
44158
44700
|
if (!parsed) {
|
|
44159
44701
|
return { ok: false, error: "Generated SKILL.md failed validation" };
|
|
44160
44702
|
}
|
|
44161
|
-
|
|
44162
|
-
|
|
44703
|
+
mkdirSync21(dirname10(path53), { recursive: true });
|
|
44704
|
+
writeFileSync23(path53, content, "utf8");
|
|
44163
44705
|
return { ok: true, path: path53 };
|
|
44164
44706
|
}
|
|
44165
44707
|
function removeSkill(opts) {
|
|
@@ -44177,9 +44719,9 @@ function removeSkill(opts) {
|
|
|
44177
44719
|
}
|
|
44178
44720
|
dir = getProjectSkillsDir(root);
|
|
44179
44721
|
}
|
|
44180
|
-
const skillDir =
|
|
44722
|
+
const skillDir = join36(dir, name);
|
|
44181
44723
|
const path53 = skillFilePath(dir, name);
|
|
44182
|
-
if (!
|
|
44724
|
+
if (!existsSync45(path53) && !existsSync45(skillDir)) {
|
|
44183
44725
|
return { ok: false, error: `Skill "${name}" not found in ${dir}` };
|
|
44184
44726
|
}
|
|
44185
44727
|
try {
|
|
@@ -44484,36 +45026,36 @@ var init_permissionCli = __esm({
|
|
|
44484
45026
|
|
|
44485
45027
|
// src/cli/companion/config.ts
|
|
44486
45028
|
import {
|
|
44487
|
-
existsSync as
|
|
44488
|
-
mkdirSync as
|
|
44489
|
-
readFileSync as
|
|
44490
|
-
writeFileSync as
|
|
45029
|
+
existsSync as existsSync46,
|
|
45030
|
+
mkdirSync as mkdirSync22,
|
|
45031
|
+
readFileSync as readFileSync38,
|
|
45032
|
+
writeFileSync as writeFileSync24
|
|
44491
45033
|
} from "node:fs";
|
|
44492
|
-
import { join as
|
|
45034
|
+
import { join as join37 } from "node:path";
|
|
44493
45035
|
import { homedir as homedir13 } from "node:os";
|
|
44494
|
-
import { createHash as
|
|
45036
|
+
import { createHash as createHash9, randomBytes as randomBytes5, timingSafeEqual } from "node:crypto";
|
|
44495
45037
|
function getZelariHome() {
|
|
44496
|
-
return
|
|
45038
|
+
return join37(homedir13(), ".zelari-code");
|
|
44497
45039
|
}
|
|
44498
45040
|
function getCompanionConfigPath() {
|
|
44499
|
-
return
|
|
45041
|
+
return join37(getZelariHome(), "companion.json");
|
|
44500
45042
|
}
|
|
44501
45043
|
function getCompanionTokenPath() {
|
|
44502
|
-
return
|
|
45044
|
+
return join37(getZelariHome(), "companion.token");
|
|
44503
45045
|
}
|
|
44504
45046
|
function ensureHome() {
|
|
44505
45047
|
const home = getZelariHome();
|
|
44506
|
-
if (!
|
|
44507
|
-
|
|
45048
|
+
if (!existsSync46(home)) {
|
|
45049
|
+
mkdirSync22(home, { recursive: true });
|
|
44508
45050
|
}
|
|
44509
45051
|
}
|
|
44510
45052
|
function loadCompanionConfig() {
|
|
44511
45053
|
const path53 = getCompanionConfigPath();
|
|
44512
|
-
if (!
|
|
45054
|
+
if (!existsSync46(path53)) {
|
|
44513
45055
|
return { projects: [] };
|
|
44514
45056
|
}
|
|
44515
45057
|
try {
|
|
44516
|
-
const raw = JSON.parse(
|
|
45058
|
+
const raw = JSON.parse(readFileSync38(path53, "utf8"));
|
|
44517
45059
|
const projects = Array.isArray(raw.projects) ? raw.projects.filter(
|
|
44518
45060
|
(p3) => p3 && typeof p3.path === "string" && p3.path.trim() && typeof (p3.id ?? p3.name) === "string"
|
|
44519
45061
|
).map((p3) => ({
|
|
@@ -44532,7 +45074,7 @@ function loadCompanionConfig() {
|
|
|
44532
45074
|
}
|
|
44533
45075
|
function saveCompanionConfig(cfg) {
|
|
44534
45076
|
ensureHome();
|
|
44535
|
-
|
|
45077
|
+
writeFileSync24(
|
|
44536
45078
|
getCompanionConfigPath(),
|
|
44537
45079
|
JSON.stringify(
|
|
44538
45080
|
{
|
|
@@ -44552,12 +45094,12 @@ function loadOrCreateToken(explicit) {
|
|
|
44552
45094
|
}
|
|
44553
45095
|
ensureHome();
|
|
44554
45096
|
const path53 = getCompanionTokenPath();
|
|
44555
|
-
if (
|
|
44556
|
-
const t =
|
|
45097
|
+
if (existsSync46(path53)) {
|
|
45098
|
+
const t = readFileSync38(path53, "utf8").trim();
|
|
44557
45099
|
if (t) return { token: t, created: false };
|
|
44558
45100
|
}
|
|
44559
45101
|
const token = randomBytes5(24).toString("base64url");
|
|
44560
|
-
|
|
45102
|
+
writeFileSync24(path53, token + "\n", "utf8");
|
|
44561
45103
|
try {
|
|
44562
45104
|
const fs31 = __require("node:fs");
|
|
44563
45105
|
fs31.chmodSync?.(path53, 384);
|
|
@@ -44567,8 +45109,8 @@ function loadOrCreateToken(explicit) {
|
|
|
44567
45109
|
}
|
|
44568
45110
|
function tokenMatches(expected, provided) {
|
|
44569
45111
|
if (!provided) return false;
|
|
44570
|
-
const a =
|
|
44571
|
-
const b =
|
|
45112
|
+
const a = createHash9("sha256").update(expected).digest();
|
|
45113
|
+
const b = createHash9("sha256").update(provided).digest();
|
|
44572
45114
|
try {
|
|
44573
45115
|
return timingSafeEqual(a, b);
|
|
44574
45116
|
} catch {
|
|
@@ -44646,8 +45188,8 @@ var init_config = __esm({
|
|
|
44646
45188
|
import { spawn as spawn13 } from "node:child_process";
|
|
44647
45189
|
import { createInterface as createInterface2 } from "node:readline";
|
|
44648
45190
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
44649
|
-
import { writeFileSync as
|
|
44650
|
-
import { join as
|
|
45191
|
+
import { writeFileSync as writeFileSync25, unlinkSync as unlinkSync3 } from "node:fs";
|
|
45192
|
+
import { join as join38 } from "node:path";
|
|
44651
45193
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
44652
45194
|
var RunManager;
|
|
44653
45195
|
var init_runManager = __esm({
|
|
@@ -44751,9 +45293,9 @@ var init_runManager = __esm({
|
|
|
44751
45293
|
}
|
|
44752
45294
|
let historyFile;
|
|
44753
45295
|
if (args.history && Array.isArray(args.history) && args.history.length > 0) {
|
|
44754
|
-
historyFile =
|
|
45296
|
+
historyFile = join38(tmpdir3(), `zelari-companion-hist-${id}.json`);
|
|
44755
45297
|
try {
|
|
44756
|
-
|
|
45298
|
+
writeFileSync25(historyFile, JSON.stringify(args.history), "utf8");
|
|
44757
45299
|
argv.push("--history-file", historyFile);
|
|
44758
45300
|
} catch {
|
|
44759
45301
|
historyFile = void 0;
|
|
@@ -44878,7 +45420,7 @@ __export(serve_exports, {
|
|
|
44878
45420
|
runCompanionServe: () => runCompanionServe
|
|
44879
45421
|
});
|
|
44880
45422
|
import { createServer as createServer3 } from "node:http";
|
|
44881
|
-
import { existsSync as
|
|
45423
|
+
import { existsSync as existsSync47 } from "node:fs";
|
|
44882
45424
|
import { resolve as resolve2 } from "node:path";
|
|
44883
45425
|
function readBody(req, max = 2e6) {
|
|
44884
45426
|
return new Promise((resolveBody, reject) => {
|
|
@@ -44926,7 +45468,7 @@ async function runCompanionServe(opts = {}) {
|
|
|
44926
45468
|
let projects = mergeProjects(fileCfg, opts.projects ?? []);
|
|
44927
45469
|
projects = projects.filter((p3) => {
|
|
44928
45470
|
const abs = resolve2(p3.path);
|
|
44929
|
-
if (!
|
|
45471
|
+
if (!existsSync47(abs)) {
|
|
44930
45472
|
process.stderr.write(
|
|
44931
45473
|
`[zelari-code serve] skip missing project path: ${p3.path}
|
|
44932
45474
|
`
|
|
@@ -45248,7 +45790,7 @@ __export(doctor_exports, {
|
|
|
45248
45790
|
runDoctor: () => runDoctor
|
|
45249
45791
|
});
|
|
45250
45792
|
import { execSync as execSync2 } from "node:child_process";
|
|
45251
|
-
import { existsSync as
|
|
45793
|
+
import { existsSync as existsSync48, readFileSync as readFileSync39, readlinkSync, statSync as statSync8 } from "node:fs";
|
|
45252
45794
|
import { createRequire as createRequire3 } from "node:module";
|
|
45253
45795
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
45254
45796
|
import path51 from "node:path";
|
|
@@ -45256,9 +45798,9 @@ function findPackageRoot(start) {
|
|
|
45256
45798
|
let dir = start;
|
|
45257
45799
|
for (let i = 0; i < 6; i += 1) {
|
|
45258
45800
|
const candidate = path51.join(dir, "package.json");
|
|
45259
|
-
if (
|
|
45801
|
+
if (existsSync48(candidate)) {
|
|
45260
45802
|
try {
|
|
45261
|
-
const pkg = JSON.parse(
|
|
45803
|
+
const pkg = JSON.parse(readFileSync39(candidate, "utf8"));
|
|
45262
45804
|
if (pkg.name === "zelari-code") return dir;
|
|
45263
45805
|
} catch {
|
|
45264
45806
|
}
|
|
@@ -45282,7 +45824,7 @@ function tryExec(cmd) {
|
|
|
45282
45824
|
function readPackageJson3() {
|
|
45283
45825
|
try {
|
|
45284
45826
|
const pkgPath = path51.join(packageRoot, "package.json");
|
|
45285
|
-
return JSON.parse(
|
|
45827
|
+
return JSON.parse(readFileSync39(pkgPath, "utf8"));
|
|
45286
45828
|
} catch {
|
|
45287
45829
|
return null;
|
|
45288
45830
|
}
|
|
@@ -45298,7 +45840,7 @@ function checkShim(pkgName) {
|
|
|
45298
45840
|
const isWin = process.platform === "win32";
|
|
45299
45841
|
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
45300
45842
|
const shimPath = path51.join(prefix, shimName);
|
|
45301
|
-
if (!
|
|
45843
|
+
if (!existsSync48(shimPath)) {
|
|
45302
45844
|
return FAIL(
|
|
45303
45845
|
`shim not found at ${shimPath}
|
|
45304
45846
|
fix: npm install -g ${pkgName}@latest --force`
|
|
@@ -45307,7 +45849,7 @@ function checkShim(pkgName) {
|
|
|
45307
45849
|
try {
|
|
45308
45850
|
const st = statSync8(shimPath);
|
|
45309
45851
|
if (isWin) {
|
|
45310
|
-
const content =
|
|
45852
|
+
const content = readFileSync39(shimPath, "utf8");
|
|
45311
45853
|
if (content.includes(`${pkgName}\\bin\\`) || content.includes(`${pkgName}/bin/`)) {
|
|
45312
45854
|
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
45313
45855
|
}
|
|
@@ -45366,7 +45908,7 @@ function checkNode(pkg) {
|
|
|
45366
45908
|
}
|
|
45367
45909
|
function checkBundle() {
|
|
45368
45910
|
const bundle = path51.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
45369
|
-
if (!
|
|
45911
|
+
if (!existsSync48(bundle)) {
|
|
45370
45912
|
return FAIL(
|
|
45371
45913
|
`dist/cli/main.bundled.js missing at ${bundle}
|
|
45372
45914
|
fix: npm run build:cli (then reinstall or run via tsx)`
|
|
@@ -45747,7 +46289,7 @@ __export(inspect_exports, {
|
|
|
45747
46289
|
runInspect: () => runInspect
|
|
45748
46290
|
});
|
|
45749
46291
|
import path52 from "node:path";
|
|
45750
|
-
import { existsSync as
|
|
46292
|
+
import { existsSync as existsSync49, readFileSync as readFileSync40, readdirSync as readdirSync10 } from "node:fs";
|
|
45751
46293
|
import { homedir as homedir14 } from "node:os";
|
|
45752
46294
|
async function collectInspectReport(cwd = process.cwd()) {
|
|
45753
46295
|
ensureBuiltinSkillsLoadedSync();
|
|
@@ -45780,11 +46322,11 @@ async function collectInspectReport(cwd = process.cwd()) {
|
|
|
45780
46322
|
folders: listTrustedFolders()
|
|
45781
46323
|
},
|
|
45782
46324
|
configSources: [
|
|
45783
|
-
{ path: userMcpPath, exists:
|
|
45784
|
-
{ path: projectMcpPath, exists:
|
|
45785
|
-
{ path: path52.join(homedir14(), ".zelari-code", "provider.json"), exists:
|
|
45786
|
-
{ path: path52.join(cwd, ".zelari", "AGENTS.md"), exists:
|
|
45787
|
-
{ path: path52.join(cwd, "AGENTS.md"), exists:
|
|
46325
|
+
{ path: userMcpPath, exists: existsSync49(userMcpPath) },
|
|
46326
|
+
{ path: projectMcpPath, exists: existsSync49(projectMcpPath) },
|
|
46327
|
+
{ path: path52.join(homedir14(), ".zelari-code", "provider.json"), exists: existsSync49(path52.join(homedir14(), ".zelari-code", "provider.json")) },
|
|
46328
|
+
{ path: path52.join(cwd, ".zelari", "AGENTS.md"), exists: existsSync49(path52.join(cwd, ".zelari", "AGENTS.md")) },
|
|
46329
|
+
{ path: path52.join(cwd, "AGENTS.md"), exists: existsSync49(path52.join(cwd, "AGENTS.md")) }
|
|
45788
46330
|
],
|
|
45789
46331
|
skills: {
|
|
45790
46332
|
total: snap.skills.length,
|
|
@@ -45797,7 +46339,7 @@ async function collectInspectReport(cwd = process.cwd()) {
|
|
|
45797
46339
|
user: mcp.servers.filter((s) => s.scope === "user").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
|
|
45798
46340
|
project: mcp.servers.filter((s) => s.scope === "project").map((s) => ({ name: s.name, enabled: s.enabled !== false })),
|
|
45799
46341
|
projectTrusted,
|
|
45800
|
-
projectConfigExists:
|
|
46342
|
+
projectConfigExists: existsSync49(projectMcpPath)
|
|
45801
46343
|
},
|
|
45802
46344
|
hooks: {
|
|
45803
46345
|
global: {
|
|
@@ -45829,9 +46371,9 @@ function findAgentsMd(cwd) {
|
|
|
45829
46371
|
];
|
|
45830
46372
|
const found = [];
|
|
45831
46373
|
for (const c of candidates) {
|
|
45832
|
-
if (
|
|
46374
|
+
if (existsSync49(c)) {
|
|
45833
46375
|
try {
|
|
45834
|
-
const text =
|
|
46376
|
+
const text = readFileSync40(c, "utf8");
|
|
45835
46377
|
found.push(`${c} (${text.length} bytes)`);
|
|
45836
46378
|
} catch {
|
|
45837
46379
|
found.push(`${c} (unreadable)`);
|
|
@@ -49235,34 +49777,58 @@ function phaseKnobs(phase2) {
|
|
|
49235
49777
|
})
|
|
49236
49778
|
};
|
|
49237
49779
|
}
|
|
49238
|
-
|
|
49239
|
-
const estimated = estimateHistoryTokens(hist);
|
|
49240
|
-
const occupancy = Math.min(1, (estimated + sessionExtra) / contextLimit);
|
|
49241
|
-
return { estimated, occupancy };
|
|
49242
|
-
}
|
|
49780
|
+
var RESERVED_OUTPUT_TOKENS = 8192;
|
|
49243
49781
|
async function applyBudgetPolicyAsync(history2, phase2, opts) {
|
|
49244
49782
|
const contextLimit = resolveContextLimit(opts?.model);
|
|
49245
49783
|
const sessionExtra = opts?.sessionTokens ?? 0;
|
|
49246
49784
|
const warnings = [];
|
|
49247
49785
|
let { historyTurns, maxToolLoopIterations } = phaseKnobs(phase2);
|
|
49786
|
+
const envelope = opts?.requestSnapshot ?? null;
|
|
49787
|
+
const replayBase = envelope ? {
|
|
49788
|
+
provider: envelope.snapshot.provider,
|
|
49789
|
+
model: envelope.snapshot.model,
|
|
49790
|
+
systemMessages: envelope.snapshot.systemMessages,
|
|
49791
|
+
tools: envelope.snapshot.tools
|
|
49792
|
+
} : opts?.providerStream ? { provider: "local", model: opts?.model ?? "unknown", systemMessages: [], tools: [] } : null;
|
|
49793
|
+
const headerTokens = envelope ? estimateSystemTokensLite(envelope.snapshot.systemMessages) + estimateToolSchemaTokensLite(envelope.snapshot.tools) : 0;
|
|
49794
|
+
const convTokensOf = (h) => estimateConversationTokensLite(h);
|
|
49248
49795
|
let hist = history2;
|
|
49249
|
-
let
|
|
49796
|
+
let estimated = envelope ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
49797
|
+
let occupancy = Math.min(1, estimated / contextLimit);
|
|
49250
49798
|
let compactSummary = "";
|
|
49251
49799
|
let messagesRemoved = 0;
|
|
49800
|
+
let cacheReuseExpected;
|
|
49801
|
+
let prunedTotal = 0;
|
|
49252
49802
|
if (occupancy >= 0.7 && occupancy < 0.85) {
|
|
49253
49803
|
warnings.push(
|
|
49254
|
-
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated
|
|
49804
|
+
`[budget] context ~${Math.round(occupancy * 100)}% full (${estimated}/${contextLimit} tok full-request est.) \u2014 consider /compact or shorter replies.`
|
|
49255
49805
|
);
|
|
49256
49806
|
}
|
|
49807
|
+
if (occupancy >= 0.8) {
|
|
49808
|
+
const pruned = pruneToolResultsDetailed(hist);
|
|
49809
|
+
if (pruned.stats.pruned > 0) {
|
|
49810
|
+
hist = pruned.messages;
|
|
49811
|
+
prunedTotal += pruned.stats.pruned;
|
|
49812
|
+
estimated = envelope ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
49813
|
+
occupancy = Math.min(1, estimated / contextLimit);
|
|
49814
|
+
warnings.push(
|
|
49815
|
+
`[budget] pruned ${pruned.stats.pruned} oversized tool result(s) \u2192 ${Math.round(occupancy * 100)}% (${estimated} tok).`
|
|
49816
|
+
);
|
|
49817
|
+
}
|
|
49818
|
+
}
|
|
49257
49819
|
const fold = (r, label, forcedTurns) => {
|
|
49258
49820
|
hist = r.messages;
|
|
49259
49821
|
if (r.compacted) {
|
|
49260
49822
|
messagesRemoved += r.messagesRemoved;
|
|
49261
49823
|
if (r.summary) compactSummary = r.summary;
|
|
49824
|
+
if (r.cacheReuseExpected !== void 0) {
|
|
49825
|
+
cacheReuseExpected = r.cacheReuseExpected;
|
|
49826
|
+
}
|
|
49262
49827
|
}
|
|
49263
|
-
|
|
49828
|
+
estimated = envelope ? headerTokens + convTokensOf(hist) + RESERVED_OUTPUT_TOKENS : estimateHistoryTokens(hist) + sessionExtra;
|
|
49829
|
+
occupancy = Math.min(1, estimated / contextLimit);
|
|
49264
49830
|
warnings.push(
|
|
49265
|
-
`[budget] ${label}
|
|
49831
|
+
`[budget] ${label} \u2014 kept ~${forcedTurns} turns (${estimated} tok est.` + (r.messagesRemoved ? `, removed ${r.messagesRemoved} msgs` : "") + (r.cacheReuseExpected === false ? ", cache reuse NOT expected (model override)" : "") + ")."
|
|
49266
49832
|
);
|
|
49267
49833
|
};
|
|
49268
49834
|
if (occupancy >= 0.85) {
|
|
@@ -49272,39 +49838,91 @@ async function applyBudgetPolicyAsync(history2, phase2, opts) {
|
|
|
49272
49838
|
maxToolLoopIterations,
|
|
49273
49839
|
phase2 === "plan" ? 24 : 40
|
|
49274
49840
|
);
|
|
49275
|
-
|
|
49276
|
-
maxMessages: forcedTurns * 4,
|
|
49277
|
-
|
|
49278
|
-
|
|
49279
|
-
|
|
49841
|
+
let r = await compactHistoryAsync(hist, {
|
|
49842
|
+
maxMessages: Math.max(2, forcedTurns * 4),
|
|
49843
|
+
force: true,
|
|
49844
|
+
signal: opts?.signal,
|
|
49845
|
+
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
49846
|
+
});
|
|
49847
|
+
if (!r.compacted) {
|
|
49848
|
+
r = await compactHistoryAsync(hist, {
|
|
49849
|
+
maxMessages: 2,
|
|
49850
|
+
force: true,
|
|
49851
|
+
signal: opts?.signal,
|
|
49852
|
+
...replayBase ? { requestSnapshot: replayBase, providerStream: opts?.providerStream } : {}
|
|
49853
|
+
});
|
|
49854
|
+
}
|
|
49855
|
+
const label = r.cacheReuseExpected === false ? "llm-compact (model override)" : "auto-compact";
|
|
49280
49856
|
fold(r, label, forcedTurns);
|
|
49281
49857
|
}
|
|
49282
49858
|
if (occupancy >= 0.95) {
|
|
49283
49859
|
const hard = await compactHistoryAsync(hist, {
|
|
49284
|
-
maxMessages:
|
|
49860
|
+
maxMessages: 2,
|
|
49861
|
+
force: true,
|
|
49285
49862
|
signal: opts?.signal
|
|
49286
49863
|
});
|
|
49287
|
-
fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "
|
|
49864
|
+
fold(hard, hard.summary.includes("\xB7 llm") ? "llm-compact" : "HARD trim", 2);
|
|
49288
49865
|
historyTurns = 2;
|
|
49289
49866
|
maxToolLoopIterations = Math.min(maxToolLoopIterations, 16);
|
|
49290
49867
|
warnings.push(
|
|
49291
|
-
|
|
49868
|
+
"[budget] HARD context pressure (\u226595%) \u2014 prefer /clear or a new session if quality drops."
|
|
49292
49869
|
);
|
|
49293
49870
|
}
|
|
49871
|
+
const cacheMetricsLine = envelope ? [
|
|
49872
|
+
"compaction meter:",
|
|
49873
|
+
`provider/model: ${envelope.snapshot.provider}/${envelope.snapshot.model}`,
|
|
49874
|
+
`headerFingerprint: ${envelope.snapshot.headerFingerprint.slice(0, 12)}`,
|
|
49875
|
+
`occupancy: ${Math.round(occupancy * 100)}% (${estimated}/${contextLimit})`,
|
|
49876
|
+
...envelope.usage?.cachedPromptTokens !== void 0 ? [`cachedPromptTokens: ${envelope.usage.cachedPromptTokens}`] : [],
|
|
49877
|
+
...cacheReuseExpected !== void 0 ? [`cacheReuseExpected: ${cacheReuseExpected}`] : []
|
|
49878
|
+
].join(" | ") : void 0;
|
|
49294
49879
|
return {
|
|
49295
49880
|
history: hist,
|
|
49296
49881
|
warnings,
|
|
49297
49882
|
maxToolLoopIterations,
|
|
49298
49883
|
historyTurns,
|
|
49299
|
-
estimatedHistoryTokens: estimated,
|
|
49884
|
+
estimatedHistoryTokens: envelope ? convTokensOf(hist) : estimated,
|
|
49300
49885
|
contextLimit,
|
|
49301
49886
|
occupancy,
|
|
49302
49887
|
compactSummary: compactSummary || void 0,
|
|
49303
|
-
messagesRemoved: messagesRemoved || void 0
|
|
49888
|
+
messagesRemoved: messagesRemoved || void 0,
|
|
49889
|
+
...envelope ? { contextPressureTokens: estimated } : {},
|
|
49890
|
+
...cacheReuseExpected !== void 0 ? { cacheReuseExpected } : {},
|
|
49891
|
+
...cacheMetricsLine ? { cacheMetricsLine } : {}
|
|
49304
49892
|
};
|
|
49305
49893
|
}
|
|
49894
|
+
function estimateSystemTokensLite(systemMessages) {
|
|
49895
|
+
let n = 0;
|
|
49896
|
+
for (const m of systemMessages) n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
49897
|
+
return n;
|
|
49898
|
+
}
|
|
49899
|
+
function estimateToolSchemaTokensLite(tools) {
|
|
49900
|
+
let n = 0;
|
|
49901
|
+
for (const t of tools) {
|
|
49902
|
+
n += Math.ceil((t.name ?? "").length / 4);
|
|
49903
|
+
n += Math.ceil((t.description ?? "").length / 4);
|
|
49904
|
+
n += Math.ceil(JSON.stringify(t.parameters ?? {}).length / 4);
|
|
49905
|
+
}
|
|
49906
|
+
return n + tools.length * 4;
|
|
49907
|
+
}
|
|
49908
|
+
function estimateConversationTokensLite(messages) {
|
|
49909
|
+
let n = 0;
|
|
49910
|
+
for (const m of messages) {
|
|
49911
|
+
n += 4 + Math.ceil((m.content ?? "").length / 4);
|
|
49912
|
+
if (m.toolCalls) {
|
|
49913
|
+
for (const tc of m.toolCalls) {
|
|
49914
|
+
n += Math.ceil(tc.name.length / 4) + Math.ceil(tc.id.length / 4);
|
|
49915
|
+
n += Math.ceil(JSON.stringify(tc.args ?? {}).length / 4);
|
|
49916
|
+
}
|
|
49917
|
+
}
|
|
49918
|
+
if (m.reasoningContent) n += Math.ceil(m.reasoningContent.length / 4);
|
|
49919
|
+
if (m.toolCallId) n += Math.ceil(m.toolCallId.length / 4);
|
|
49920
|
+
}
|
|
49921
|
+
return n;
|
|
49922
|
+
}
|
|
49306
49923
|
|
|
49307
49924
|
// src/cli/hooks/useChatTurn.ts
|
|
49925
|
+
init_requestSnapshotStore();
|
|
49308
49926
|
function useChatTurn(params) {
|
|
49309
49927
|
const {
|
|
49310
49928
|
sessionId,
|
|
@@ -49331,17 +49949,9 @@ function useChatTurn(params) {
|
|
|
49331
49949
|
let envConfig;
|
|
49332
49950
|
let harness;
|
|
49333
49951
|
let historySeedLen = 0;
|
|
49952
|
+
let systemPrefixLen = 0;
|
|
49334
49953
|
let turnSucceeded = false;
|
|
49335
49954
|
try {
|
|
49336
|
-
compactInPlace();
|
|
49337
|
-
const budget = await applyBudgetPolicyAsync(getHistory(), getPhase(), {
|
|
49338
|
-
model: getActiveModel()
|
|
49339
|
-
});
|
|
49340
|
-
setHistory(budget.history);
|
|
49341
|
-
for (const w of budget.warnings) {
|
|
49342
|
-
appendSystem(setMessages, w, Date.now());
|
|
49343
|
-
}
|
|
49344
|
-
historySeedLen = getHistory().length;
|
|
49345
49955
|
const anchored = maybeAnchorShortAnswer(userText);
|
|
49346
49956
|
const effectiveUserText = anchored ?? userText;
|
|
49347
49957
|
const localCli = (process.env.ZELARI_LOCAL_CLI ?? "").trim();
|
|
@@ -49436,6 +50046,33 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49436
50046
|
});
|
|
49437
50047
|
}
|
|
49438
50048
|
const cwd = process.cwd();
|
|
50049
|
+
const budget = await applyBudgetPolicyAsync(getHistory(), getPhase(), {
|
|
50050
|
+
model: getActiveModel(),
|
|
50051
|
+
sessionId,
|
|
50052
|
+
// v1.36.0: envelope for full-request metering + cache-aware
|
|
50053
|
+
// compaction replay (last warm prefix + provider usage anchor).
|
|
50054
|
+
requestSnapshot: getRequestSnapshotWithUsage(sessionId),
|
|
50055
|
+
providerStream
|
|
50056
|
+
});
|
|
50057
|
+
setHistory(budget.history);
|
|
50058
|
+
for (const w of budget.warnings) {
|
|
50059
|
+
appendSystem(setMessages, w, Date.now());
|
|
50060
|
+
}
|
|
50061
|
+
if ((budget.messagesRemoved ?? 0) > 0) {
|
|
50062
|
+
const envelope = getRequestSnapshotWithUsage(sessionId);
|
|
50063
|
+
const compactionEvent = createBrainEvent("session_compacted", sessionId, {
|
|
50064
|
+
summary: budget.compactSummary ?? "",
|
|
50065
|
+
messagesRemoved: budget.messagesRemoved ?? 0,
|
|
50066
|
+
...envelope ? {
|
|
50067
|
+
sourceRequestFingerprint: envelope.snapshot.requestFingerprint,
|
|
50068
|
+
headerFingerprint: envelope.snapshot.headerFingerprint
|
|
50069
|
+
} : {},
|
|
50070
|
+
...budget.contextPressureTokens !== void 0 ? { sourceEstimatedTokens: budget.contextPressureTokens } : {},
|
|
50071
|
+
...budget.cacheReuseExpected !== void 0 ? { cacheReuseExpected: budget.cacheReuseExpected } : {}
|
|
50072
|
+
});
|
|
50073
|
+
void writerRef.current?.append(compactionEvent);
|
|
50074
|
+
}
|
|
50075
|
+
historySeedLen = getHistory().length;
|
|
49439
50076
|
let composedWorkspace = "";
|
|
49440
50077
|
let composedInstructions = "";
|
|
49441
50078
|
let hasPlan = false;
|
|
@@ -49593,6 +50230,7 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49593
50230
|
lastStableHash = hashStablePrompt(fallback);
|
|
49594
50231
|
systemMessages = [{ role: "system", content: fallback }];
|
|
49595
50232
|
}
|
|
50233
|
+
systemPrefixLen = systemMessages.length;
|
|
49596
50234
|
const maxToolCallsPerTurn = envNumber(process.env.ZELARI_MAX_TOOL_CALLS, {
|
|
49597
50235
|
default: 25,
|
|
49598
50236
|
min: 1
|
|
@@ -49605,7 +50243,10 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49605
50243
|
});
|
|
49606
50244
|
const harness2 = new AgentHarness({
|
|
49607
50245
|
model: envConfig.model,
|
|
49608
|
-
|
|
50246
|
+
// v1.36.0 (P0.2): real provider identity — the harness used to
|
|
50247
|
+
// hardcode "openai-compatible" (the transport family) so snapshots
|
|
50248
|
+
// and telemetry mislabeled deepseek/glm/minimax routing.
|
|
50249
|
+
provider: envConfig.providerId,
|
|
49609
50250
|
messages: [
|
|
49610
50251
|
...systemMessages,
|
|
49611
50252
|
// v1.8.0: shared rolling history (agent/council/zelari) so short
|
|
@@ -49624,6 +50265,9 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49624
50265
|
cwd,
|
|
49625
50266
|
maxToolCallsPerTurn,
|
|
49626
50267
|
maxToolLoopIterations,
|
|
50268
|
+
// v1.36.0: routed-request snapshots feed the meter (occupancy) and
|
|
50269
|
+
// the cache-aware compaction replay (last warm prefix).
|
|
50270
|
+
onRequestSnapshot: (snap) => recordRequestSnapshot(sessionId, snap),
|
|
49627
50271
|
...maxToolLoopHardCap > 0 ? { maxToolLoopHardCap } : {}
|
|
49628
50272
|
});
|
|
49629
50273
|
harnessRef.current = harness2;
|
|
@@ -49638,6 +50282,14 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49638
50282
|
for await (const event of harness2.run()) {
|
|
49639
50283
|
if (event.type === "message_end") {
|
|
49640
50284
|
if (event.usage) realUsage = event.usage;
|
|
50285
|
+
if (event.usage) {
|
|
50286
|
+
recordRequestUsage(sessionId, {
|
|
50287
|
+
promptTokens: event.usage.promptTokens,
|
|
50288
|
+
completionTokens: event.usage.completionTokens,
|
|
50289
|
+
totalTokens: event.usage.totalTokens,
|
|
50290
|
+
cachedPromptTokens: event.usage.cachedPromptTokens
|
|
50291
|
+
});
|
|
50292
|
+
}
|
|
49641
50293
|
if (streamContent) {
|
|
49642
50294
|
const sealed = streamScrub.finalize(streamContent);
|
|
49643
50295
|
if (useLiveModel) setStreaming(commitStreaming, sealed, event.ts);
|
|
@@ -49797,18 +50449,17 @@ _(${req.context})_` : "") + "\n\u2192 scegli dalla lista (il turno continua dopo
|
|
|
49797
50449
|
const h = harnessRef.current;
|
|
49798
50450
|
if (h && turnSucceeded) {
|
|
49799
50451
|
const all = h.getMessages();
|
|
49800
|
-
const seedLen =
|
|
50452
|
+
const seedLen = systemPrefixLen + historySeedLen + 1;
|
|
49801
50453
|
if (all.length > seedLen) {
|
|
49802
50454
|
appendMessages(
|
|
49803
|
-
all.slice(seedLen).map(
|
|
49804
|
-
(m
|
|
49805
|
-
|
|
49806
|
-
|
|
49807
|
-
|
|
49808
|
-
|
|
49809
|
-
|
|
49810
|
-
|
|
49811
|
-
)
|
|
50455
|
+
all.slice(seedLen).map((m) => {
|
|
50456
|
+
if (m.role !== "assistant" || !m.content) return m;
|
|
50457
|
+
const cleaned = cleanAgentContent(m.content, {
|
|
50458
|
+
stripQuestion: false,
|
|
50459
|
+
stripThink: false
|
|
50460
|
+
});
|
|
50461
|
+
return cleaned === m.content ? m : { ...m, content: cleaned };
|
|
50462
|
+
})
|
|
49812
50463
|
);
|
|
49813
50464
|
}
|
|
49814
50465
|
}
|
|
@@ -51751,12 +52402,12 @@ init_fileStateStore();
|
|
|
51751
52402
|
async function restoreDurableState(opts) {
|
|
51752
52403
|
const restoreTree = opts.restoreTree !== false;
|
|
51753
52404
|
try {
|
|
51754
|
-
const
|
|
52405
|
+
const store4 = opts.store ?? await getStateStore(opts.projectRoot);
|
|
51755
52406
|
let meta3;
|
|
51756
52407
|
if (opts.commitId) {
|
|
51757
|
-
meta3 = await
|
|
52408
|
+
meta3 = await store4.setHead(opts.commitId);
|
|
51758
52409
|
} else {
|
|
51759
|
-
meta3 = await
|
|
52410
|
+
meta3 = await store4.head();
|
|
51760
52411
|
if (!meta3) {
|
|
51761
52412
|
return {
|
|
51762
52413
|
ok: false,
|
|
@@ -51809,8 +52460,8 @@ function ago2(ms) {
|
|
|
51809
52460
|
return `${Math.round(s / 3600)}h ago`;
|
|
51810
52461
|
}
|
|
51811
52462
|
async function handleStateStatus(ctx) {
|
|
51812
|
-
const
|
|
51813
|
-
const head = await
|
|
52463
|
+
const store4 = await getStateStore(ctx.cwd);
|
|
52464
|
+
const head = await store4.head();
|
|
51814
52465
|
if (!head) {
|
|
51815
52466
|
appendSystem(
|
|
51816
52467
|
ctx.setMessages,
|
|
@@ -51818,9 +52469,9 @@ async function handleStateStatus(ctx) {
|
|
|
51818
52469
|
);
|
|
51819
52470
|
return;
|
|
51820
52471
|
}
|
|
51821
|
-
const discoveries = await
|
|
52472
|
+
const discoveries = await store4.loadDiscoveries(head.id);
|
|
51822
52473
|
const reusable = discoveries.filter((d) => d.reusable).length;
|
|
51823
|
-
const recent = await
|
|
52474
|
+
const recent = await store4.list(8);
|
|
51824
52475
|
const lines = recent.map((c, i) => {
|
|
51825
52476
|
const ver2 = c.verification.ran ? c.verification.ok ? "ok" : "fail" : "n/a";
|
|
51826
52477
|
return ` ${i === 0 ? "\u2192" : " "} ${c.id} ${ago2(c.createdAt)} ${c.label} ver=${ver2}` + (c.layer ? ` [${c.layer}]` : "") + (c.stablePromptHash ? ` hash=${c.stablePromptHash.slice(0, 8)}` : "");
|
|
@@ -51839,9 +52490,9 @@ async function handleStateStatus(ctx) {
|
|
|
51839
52490
|
);
|
|
51840
52491
|
}
|
|
51841
52492
|
async function handleStateCommit(ctx, label) {
|
|
51842
|
-
const
|
|
52493
|
+
const store4 = await getStateStore(ctx.cwd);
|
|
51843
52494
|
try {
|
|
51844
|
-
const meta3 = await
|
|
52495
|
+
const meta3 = await store4.commit({
|
|
51845
52496
|
mode: "agent",
|
|
51846
52497
|
label: label?.trim() || "manual state commit",
|
|
51847
52498
|
layer: "manual",
|
|
@@ -51868,8 +52519,8 @@ async function handleStateCommit(ctx, label) {
|
|
|
51868
52519
|
}
|
|
51869
52520
|
}
|
|
51870
52521
|
async function handleStateShow(ctx, id) {
|
|
51871
|
-
const
|
|
51872
|
-
const meta3 = id ? await
|
|
52522
|
+
const store4 = await getStateStore(ctx.cwd);
|
|
52523
|
+
const meta3 = id ? await store4.get(id) : await store4.head();
|
|
51873
52524
|
if (!meta3) {
|
|
51874
52525
|
appendSystem(
|
|
51875
52526
|
ctx.setMessages,
|
|
@@ -51877,7 +52528,7 @@ async function handleStateShow(ctx, id) {
|
|
|
51877
52528
|
);
|
|
51878
52529
|
return;
|
|
51879
52530
|
}
|
|
51880
|
-
const text = await
|
|
52531
|
+
const text = await store4.materializeContext(meta3.id, 6e3);
|
|
51881
52532
|
appendSystem(ctx.setMessages, `[state] show ${meta3.id}
|
|
51882
52533
|
${text}`);
|
|
51883
52534
|
}
|
|
@@ -52916,7 +53567,7 @@ async function handlePromoteMember(ctx, memberId) {
|
|
|
52916
53567
|
}
|
|
52917
53568
|
|
|
52918
53569
|
// src/cli/branchManager.ts
|
|
52919
|
-
import { promises as fs26, existsSync as
|
|
53570
|
+
import { promises as fs26, existsSync as existsSync41, readFileSync as readFileSync34, writeFileSync as writeFileSync22, mkdirSync as mkdirSync19, statSync as statSync5, rmSync as rmSync3 } from "node:fs";
|
|
52920
53571
|
import path46 from "node:path";
|
|
52921
53572
|
import os12 from "node:os";
|
|
52922
53573
|
var META_FILENAME = "meta.json";
|
|
@@ -52938,11 +53589,11 @@ function sessionsPathFor(name, baseDir) {
|
|
|
52938
53589
|
}
|
|
52939
53590
|
function readBranchMeta(name, baseDir) {
|
|
52940
53591
|
const metaPath = metaPathFor(name, baseDir);
|
|
52941
|
-
if (!
|
|
53592
|
+
if (!existsSync41(metaPath)) {
|
|
52942
53593
|
throw new BranchNotFoundError(`Branch "${name}" not found`);
|
|
52943
53594
|
}
|
|
52944
53595
|
try {
|
|
52945
|
-
const raw =
|
|
53596
|
+
const raw = readFileSync34(metaPath, "utf-8");
|
|
52946
53597
|
const parsed = JSON.parse(raw);
|
|
52947
53598
|
if (!parsed || typeof parsed !== "object" || typeof parsed.name !== "string" || typeof parsed.createdAt !== "number" || typeof parsed.fromSessionId !== "string") {
|
|
52948
53599
|
throw new BranchCorruptError(`Branch "${name}" meta.json is malformed`);
|
|
@@ -52959,8 +53610,8 @@ function readBranchMeta(name, baseDir) {
|
|
|
52959
53610
|
}
|
|
52960
53611
|
function writeBranchMeta(name, baseDir, meta3) {
|
|
52961
53612
|
const metaPath = metaPathFor(name, baseDir);
|
|
52962
|
-
|
|
52963
|
-
|
|
53613
|
+
mkdirSync19(path46.dirname(metaPath), { recursive: true });
|
|
53614
|
+
writeFileSync22(metaPath, JSON.stringify(meta3, null, 2), "utf-8");
|
|
52964
53615
|
}
|
|
52965
53616
|
async function countSessions(name, baseDir) {
|
|
52966
53617
|
const sessionsPath = sessionsPathFor(name, baseDir);
|
|
@@ -52998,7 +53649,7 @@ var SessionNotFoundError = class extends Error {
|
|
|
52998
53649
|
};
|
|
52999
53650
|
function branchExists(name, baseDir = getBranchesBaseDir()) {
|
|
53000
53651
|
const bp = branchPathFor(name, baseDir);
|
|
53001
|
-
return
|
|
53652
|
+
return existsSync41(bp) && existsSync41(metaPathFor(name, baseDir));
|
|
53002
53653
|
}
|
|
53003
53654
|
async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(), sessionsBaseDir = getSessionsBaseDir()) {
|
|
53004
53655
|
if (!name || name.trim().length === 0) {
|
|
@@ -53011,12 +53662,12 @@ async function createBranch(name, fromSessionId, baseDir = getBranchesBaseDir(),
|
|
|
53011
53662
|
throw new BranchAlreadyExistsError(name);
|
|
53012
53663
|
}
|
|
53013
53664
|
const sourcePath = path46.join(sessionsBaseDir, `${fromSessionId}.jsonl`);
|
|
53014
|
-
if (!
|
|
53665
|
+
if (!existsSync41(sourcePath)) {
|
|
53015
53666
|
throw new SessionNotFoundError(`Source session "${fromSessionId}" not found at ${sourcePath}`);
|
|
53016
53667
|
}
|
|
53017
53668
|
const branchPath = branchPathFor(name, baseDir);
|
|
53018
53669
|
const branchSessionsPath = sessionsPathFor(name, baseDir);
|
|
53019
|
-
|
|
53670
|
+
mkdirSync19(branchSessionsPath, { recursive: true });
|
|
53020
53671
|
const destPath = path46.join(branchSessionsPath, `${fromSessionId}.jsonl`);
|
|
53021
53672
|
await fs26.copyFile(sourcePath, destPath);
|
|
53022
53673
|
const meta3 = {
|
|
@@ -53044,7 +53695,7 @@ async function listBranches(baseDir = getBranchesBaseDir()) {
|
|
|
53044
53695
|
const results = [];
|
|
53045
53696
|
for (const entry of entries) {
|
|
53046
53697
|
const metaPath = metaPathFor(entry, baseDir);
|
|
53047
|
-
if (!
|
|
53698
|
+
if (!existsSync41(metaPath)) continue;
|
|
53048
53699
|
try {
|
|
53049
53700
|
const meta3 = readBranchMeta(entry, baseDir);
|
|
53050
53701
|
const sessionCount = await countSessions(entry, baseDir);
|
|
@@ -53235,7 +53886,7 @@ import path48 from "node:path";
|
|
|
53235
53886
|
import os13 from "node:os";
|
|
53236
53887
|
|
|
53237
53888
|
// src/cli/skillHistory.ts
|
|
53238
|
-
import { promises as fs28, existsSync as
|
|
53889
|
+
import { promises as fs28, existsSync as existsSync42, statSync as statSync6, renameSync as renameSync5, appendFileSync as appendFileSync4, mkdirSync as mkdirSync20 } from "node:fs";
|
|
53239
53890
|
var SKILL_HISTORY_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
53240
53891
|
async function readSkillHistory(file2) {
|
|
53241
53892
|
let raw = "";
|
|
@@ -53386,14 +54037,14 @@ async function handleSkillCompare(ctx, ids, fallbackMessage) {
|
|
|
53386
54037
|
}
|
|
53387
54038
|
function handleCouncilFeedback(ctx, memberId, score, note) {
|
|
53388
54039
|
try {
|
|
53389
|
-
const
|
|
53390
|
-
const entry =
|
|
54040
|
+
const store4 = new FeedbackStore();
|
|
54041
|
+
const entry = store4.record({
|
|
53391
54042
|
memberId,
|
|
53392
54043
|
score,
|
|
53393
54044
|
...note ? { note } : {},
|
|
53394
54045
|
...ctx.sessionId ? { sessionId: ctx.sessionId } : {}
|
|
53395
54046
|
});
|
|
53396
|
-
const stats =
|
|
54047
|
+
const stats = store4.getStats(memberId);
|
|
53397
54048
|
appendSystem(
|
|
53398
54049
|
ctx.setMessages,
|
|
53399
54050
|
`[council-feedback] ${memberId} rated ${entry.score}/5 \u2014 running avg ${stats.avg.toFixed(2)} over ${stats.count} rating(s).`
|
|
@@ -54563,9 +55214,9 @@ function ContinueKey({ onContinue }) {
|
|
|
54563
55214
|
init_providerConfig();
|
|
54564
55215
|
|
|
54565
55216
|
// src/cli/wizard/firstRun.ts
|
|
54566
|
-
import { existsSync as
|
|
55217
|
+
import { existsSync as existsSync44 } from "node:fs";
|
|
54567
55218
|
function shouldRunWizard(input) {
|
|
54568
|
-
const exists = input.exists ??
|
|
55219
|
+
const exists = input.exists ?? existsSync44;
|
|
54569
55220
|
if (input.hasResetConfigFlag) {
|
|
54570
55221
|
return { shouldRun: true, reason: "--reset-config flag forced wizard" };
|
|
54571
55222
|
}
|
|
@@ -54850,7 +55501,7 @@ init_keyStore();
|
|
|
54850
55501
|
init_providerConfig();
|
|
54851
55502
|
init_openai_compatible();
|
|
54852
55503
|
init_phase();
|
|
54853
|
-
import { readFileSync as
|
|
55504
|
+
import { readFileSync as readFileSync36 } from "node:fs";
|
|
54854
55505
|
function parseHeadlessFlags(argv) {
|
|
54855
55506
|
if (!argv.includes("--headless")) {
|
|
54856
55507
|
return { options: null };
|
|
@@ -54923,7 +55574,7 @@ function parseHeadlessFlags(argv) {
|
|
|
54923
55574
|
let raw = null;
|
|
54924
55575
|
if (arg === "--history-file") {
|
|
54925
55576
|
try {
|
|
54926
|
-
raw =
|
|
55577
|
+
raw = readFileSync36(next, "utf-8");
|
|
54927
55578
|
} catch {
|
|
54928
55579
|
raw = null;
|
|
54929
55580
|
}
|
|
@@ -55410,6 +56061,20 @@ async function runHeadlessSingle(opts, provider, model, providerStream) {
|
|
|
55410
56061
|
const sessionId = crypto.randomUUID();
|
|
55411
56062
|
const { registry: toolRegistry } = createBuiltinToolRegistry({
|
|
55412
56063
|
planMode: planModeFromOpts(opts),
|
|
56064
|
+
// ADR-0018 3b: upgrade plan-task domain events to first-class NDJSON
|
|
56065
|
+
// BrainEvents. Rust envelopes every stdout line with runId/conversationId,
|
|
56066
|
+
// so task events ride the same multiplexed channel as the rest.
|
|
56067
|
+
onTaskEvent: (ev) => {
|
|
56068
|
+
if (opts.output !== "json") return;
|
|
56069
|
+
emitEvent({
|
|
56070
|
+
type: ev.type,
|
|
56071
|
+
id: crypto.randomUUID(),
|
|
56072
|
+
ts: Date.now(),
|
|
56073
|
+
sessionId,
|
|
56074
|
+
source: ev.source,
|
|
56075
|
+
...ev.type === "task_update" ? { task: ev.task } : { tasks: ev.tasks }
|
|
56076
|
+
});
|
|
56077
|
+
},
|
|
55413
56078
|
permissionPolicy: {
|
|
55414
56079
|
read: "allow",
|
|
55415
56080
|
write: "allow",
|