syntaur 0.67.0 → 0.68.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/.claude-plugin/plugin.json +1 -1
- package/dist/dashboard/server.js +450 -58
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +1149 -697
- package/dist/index.js.map +1 -1
- package/dist/launch/index.d.ts +18 -0
- package/dist/launch/index.js +111 -7
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/claude-code/.claude-plugin/plugin.json +1 -1
- package/platforms/claude-code/hooks/hooks.json +1 -1
- package/platforms/claude-code/hooks/session-cleanup.sh +21 -0
- package/platforms/codex/.codex-plugin/plugin.json +1 -1
- package/platforms/codex/hooks.json +1 -1
- package/platforms/codex/scripts/session-cleanup.sh +13 -0
- package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
- package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
package/dist/index.js
CHANGED
|
@@ -1591,6 +1591,21 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
1591
1591
|
});
|
|
1592
1592
|
|
|
1593
1593
|
// src/lifecycle/event-emit.ts
|
|
1594
|
+
var event_emit_exports = {};
|
|
1595
|
+
__export(event_emit_exports, {
|
|
1596
|
+
emitEvent: () => emitEvent,
|
|
1597
|
+
isSuppressingEvents: () => isSuppressingEvents,
|
|
1598
|
+
recordStatusEvent: () => recordStatusEvent,
|
|
1599
|
+
resolveActor: () => resolveActor,
|
|
1600
|
+
setSuppressEvents: () => setSuppressEvents,
|
|
1601
|
+
withSuppressedEvents: () => withSuppressedEvents
|
|
1602
|
+
});
|
|
1603
|
+
function setSuppressEvents(value) {
|
|
1604
|
+
suppressEvents = value;
|
|
1605
|
+
}
|
|
1606
|
+
function isSuppressingEvents() {
|
|
1607
|
+
return suppressEvents;
|
|
1608
|
+
}
|
|
1594
1609
|
function withSuppressedEvents(fn) {
|
|
1595
1610
|
const prior = suppressEvents;
|
|
1596
1611
|
suppressEvents = true;
|
|
@@ -3885,7 +3900,9 @@ __export(config_exports, {
|
|
|
3885
3900
|
getTerminal: () => getTerminal,
|
|
3886
3901
|
normalizeFactDeclarations: () => normalizeFactDeclarations,
|
|
3887
3902
|
parseAgentCommand: () => parseAgentCommand,
|
|
3903
|
+
parseDurationMs: () => parseDurationMs,
|
|
3888
3904
|
parseSearchConfig: () => parseSearchConfig,
|
|
3905
|
+
parseStalenessConfig: () => parseStalenessConfig,
|
|
3889
3906
|
parseStatusConfig: () => parseStatusConfig,
|
|
3890
3907
|
parseTerminalConfig: () => parseTerminalConfig,
|
|
3891
3908
|
readConfig: () => readConfig,
|
|
@@ -3901,6 +3918,7 @@ __export(config_exports, {
|
|
|
3901
3918
|
validateDeriveConfig: () => validateDeriveConfig,
|
|
3902
3919
|
validateDeriveShape: () => validateDeriveShape,
|
|
3903
3920
|
validateFactDeclarations: () => validateFactDeclarations,
|
|
3921
|
+
validateStalenessConfig: () => validateStalenessConfig,
|
|
3904
3922
|
writeAgentsConfig: () => writeAgentsConfig,
|
|
3905
3923
|
writeHotkeyBindingsConfig: () => writeHotkeyBindingsConfig,
|
|
3906
3924
|
writeSearchConfig: () => writeSearchConfig,
|
|
@@ -3912,6 +3930,13 @@ __export(config_exports, {
|
|
|
3912
3930
|
import { readFile as readFile5 } from "fs/promises";
|
|
3913
3931
|
import { spawnSync } from "child_process";
|
|
3914
3932
|
import { resolve as resolve7, isAbsolute } from "path";
|
|
3933
|
+
function parseDurationMs(raw2) {
|
|
3934
|
+
const m = raw2.trim().match(DURATION_RE);
|
|
3935
|
+
if (!m) return null;
|
|
3936
|
+
const n = Number(m[1]);
|
|
3937
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
3938
|
+
return n * DURATION_UNIT_MS[m[2] ?? "ms"];
|
|
3939
|
+
}
|
|
3915
3940
|
function parseAgentCommand(value, agentId) {
|
|
3916
3941
|
if (typeof value !== "string" || value.trim() === "") {
|
|
3917
3942
|
throw new AgentConfigError(
|
|
@@ -5148,6 +5173,65 @@ ${cleanedFm}
|
|
|
5148
5173
|
---${afterFrontmatter}`;
|
|
5149
5174
|
await writeFileForce(configPath2, newContent);
|
|
5150
5175
|
}
|
|
5176
|
+
function parseStalenessConfig(content) {
|
|
5177
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
5178
|
+
if (!match) return null;
|
|
5179
|
+
const fmBlock = match[1];
|
|
5180
|
+
const blockStart = fmBlock.match(/^staleness:\s*$/m);
|
|
5181
|
+
if (!blockStart) return null;
|
|
5182
|
+
const startIdx = (blockStart.index ?? 0) + blockStart[0].length;
|
|
5183
|
+
const lines = fmBlock.slice(startIdx).split("\n");
|
|
5184
|
+
const out = {};
|
|
5185
|
+
for (const line of lines) {
|
|
5186
|
+
if (line.trim() === "") continue;
|
|
5187
|
+
const trimmed = line.trimStart();
|
|
5188
|
+
const indent = line.length - trimmed.length;
|
|
5189
|
+
if (indent === 0) break;
|
|
5190
|
+
const ci = trimmed.indexOf(":");
|
|
5191
|
+
if (ci <= 0) continue;
|
|
5192
|
+
const key = trimmed.slice(0, ci).trim();
|
|
5193
|
+
const field = STALENESS_KEY_TO_FIELD[key];
|
|
5194
|
+
if (!field) continue;
|
|
5195
|
+
let value = trimmed.slice(ci + 1).trim();
|
|
5196
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
5197
|
+
value = value.slice(1, -1);
|
|
5198
|
+
}
|
|
5199
|
+
const ms = parseDurationMs(value);
|
|
5200
|
+
if (ms !== null) out[field] = ms;
|
|
5201
|
+
}
|
|
5202
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
5203
|
+
}
|
|
5204
|
+
function validateStalenessConfig(content) {
|
|
5205
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
5206
|
+
if (!match) return [];
|
|
5207
|
+
const fmBlock = match[1];
|
|
5208
|
+
const blockStart = fmBlock.match(/^staleness:\s*$/m);
|
|
5209
|
+
if (!blockStart) return [];
|
|
5210
|
+
const startIdx = (blockStart.index ?? 0) + blockStart[0].length;
|
|
5211
|
+
const lines = fmBlock.slice(startIdx).split("\n");
|
|
5212
|
+
const problems = [];
|
|
5213
|
+
for (const line of lines) {
|
|
5214
|
+
if (line.trim() === "") continue;
|
|
5215
|
+
const trimmed = line.trimStart();
|
|
5216
|
+
const indent = line.length - trimmed.length;
|
|
5217
|
+
if (indent === 0) break;
|
|
5218
|
+
const ci = trimmed.indexOf(":");
|
|
5219
|
+
if (ci <= 0) continue;
|
|
5220
|
+
const key = trimmed.slice(0, ci).trim();
|
|
5221
|
+
let value = trimmed.slice(ci + 1).trim();
|
|
5222
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
5223
|
+
value = value.slice(1, -1);
|
|
5224
|
+
}
|
|
5225
|
+
if (!(key in STALENESS_KEY_TO_FIELD)) {
|
|
5226
|
+
problems.push(`staleness.${key}: unknown key (expected one of ${Object.keys(STALENESS_KEY_TO_FIELD).join(", ")})`);
|
|
5227
|
+
continue;
|
|
5228
|
+
}
|
|
5229
|
+
if (parseDurationMs(value) === null) {
|
|
5230
|
+
problems.push(`staleness.${key}: "${value}" is not a positive duration (e.g. 7d, 12h, 30m, 90s, 500ms)`);
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
return problems;
|
|
5234
|
+
}
|
|
5151
5235
|
function parseSearchConfig(content) {
|
|
5152
5236
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
5153
5237
|
if (!match) return null;
|
|
@@ -5430,7 +5514,9 @@ async function readConfig() {
|
|
|
5430
5514
|
}
|
|
5431
5515
|
})(),
|
|
5432
5516
|
searchConfig: parseSearchConfig(content),
|
|
5433
|
-
workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock)
|
|
5517
|
+
workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock),
|
|
5518
|
+
staleness: parseStalenessConfig(content),
|
|
5519
|
+
stalenessWatchdog: String(fm["stalenessWatchdog"]).toLowerCase() === "true"
|
|
5434
5520
|
};
|
|
5435
5521
|
}
|
|
5436
5522
|
function getAssignmentTypes(config) {
|
|
@@ -5493,7 +5579,7 @@ async function updateAgentsConfig(mutation, options = {}) {
|
|
|
5493
5579
|
await writeAgentsConfig(next);
|
|
5494
5580
|
return { previous, next, written: true };
|
|
5495
5581
|
}
|
|
5496
|
-
var DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
5582
|
+
var STALENESS_KEY_TO_FIELD, DURATION_RE, DURATION_UNIT_MS, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
5497
5583
|
var init_config2 = __esm({
|
|
5498
5584
|
"src/utils/config.ts"() {
|
|
5499
5585
|
"use strict";
|
|
@@ -5510,6 +5596,21 @@ var init_config2 = __esm({
|
|
|
5510
5596
|
init_terminal_schema();
|
|
5511
5597
|
init_search_schema();
|
|
5512
5598
|
init_workspace_visibility_schema();
|
|
5599
|
+
STALENESS_KEY_TO_FIELD = {
|
|
5600
|
+
inProgressNoActivity: "inProgressNoActivityMs",
|
|
5601
|
+
readyUnclaimed: "readyUnclaimedMs",
|
|
5602
|
+
reviewAging: "reviewAgingMs",
|
|
5603
|
+
blockedAging: "blockedAgingMs",
|
|
5604
|
+
planApprovalAging: "planApprovalAgingMs"
|
|
5605
|
+
};
|
|
5606
|
+
DURATION_RE = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/;
|
|
5607
|
+
DURATION_UNIT_MS = {
|
|
5608
|
+
ms: 1,
|
|
5609
|
+
s: 1e3,
|
|
5610
|
+
m: 6e4,
|
|
5611
|
+
h: 36e5,
|
|
5612
|
+
d: 864e5
|
|
5613
|
+
};
|
|
5513
5614
|
DEFAULT_ASSIGNMENT_TYPES = {
|
|
5514
5615
|
definitions: [
|
|
5515
5616
|
{ id: "feature", label: "Feature" },
|
|
@@ -5552,7 +5653,9 @@ var init_config2 = __esm({
|
|
|
5552
5653
|
searchConfig: null,
|
|
5553
5654
|
workspaceVisibility: {
|
|
5554
5655
|
hidden: []
|
|
5555
|
-
}
|
|
5656
|
+
},
|
|
5657
|
+
staleness: null,
|
|
5658
|
+
stalenessWatchdog: false
|
|
5556
5659
|
};
|
|
5557
5660
|
AUTO_CREATE_WORKTREE_VALUES = ["skip", "ask", "always"];
|
|
5558
5661
|
SESSION_AUTO_TRACK_VALUES = ["all", "workspaces-only", "off"];
|
|
@@ -8907,8 +9010,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
8907
9010
|
return allSessions.length;
|
|
8908
9011
|
}
|
|
8909
9012
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
8910
|
-
const { readFile:
|
|
8911
|
-
const raw2 = await
|
|
9013
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
9014
|
+
const raw2 = await readFile82(filePath, "utf-8");
|
|
8912
9015
|
const sessions = [];
|
|
8913
9016
|
const lines = raw2.split("\n");
|
|
8914
9017
|
let inTable = false;
|
|
@@ -9177,6 +9280,74 @@ var init_overviewCopy = __esm({
|
|
|
9177
9280
|
}
|
|
9178
9281
|
});
|
|
9179
9282
|
|
|
9283
|
+
// src/staleness/classify.ts
|
|
9284
|
+
function resolveStaleThresholds(overrides) {
|
|
9285
|
+
const merged = { ...DEFAULT_STALE_THRESHOLDS };
|
|
9286
|
+
if (overrides) {
|
|
9287
|
+
for (const key of Object.keys(merged)) {
|
|
9288
|
+
const v = overrides[key];
|
|
9289
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) merged[key] = v;
|
|
9290
|
+
}
|
|
9291
|
+
}
|
|
9292
|
+
return merged;
|
|
9293
|
+
}
|
|
9294
|
+
function classifyNeedsAttention(input4, thresholds = DEFAULT_STALE_THRESHOLDS) {
|
|
9295
|
+
if (input4.isTerminal) return [];
|
|
9296
|
+
const reasons = [];
|
|
9297
|
+
const age = input4.statusAgeMs;
|
|
9298
|
+
const aged = (gate) => age !== null && age >= gate;
|
|
9299
|
+
const blocked = input4.disposition === "blocked" || input4.blockedReason !== null;
|
|
9300
|
+
if (input4.phase === IN_PROGRESS_PHASE && !blocked && aged(thresholds.inProgressNoActivityMs) && input4.lastActivityMs !== null && input4.lastActivityMs >= thresholds.inProgressNoActivityMs) {
|
|
9301
|
+
reasons.push({
|
|
9302
|
+
kind: "in_progress_no_activity",
|
|
9303
|
+
label: "In progress, but no recent activity",
|
|
9304
|
+
severity: "medium"
|
|
9305
|
+
});
|
|
9306
|
+
}
|
|
9307
|
+
if (input4.phase === READY_PHASE && input4.assignee === null && aged(thresholds.readyUnclaimedMs)) {
|
|
9308
|
+
reasons.push({
|
|
9309
|
+
kind: "ready_unclaimed",
|
|
9310
|
+
label: "Ready to implement, unclaimed",
|
|
9311
|
+
severity: "medium"
|
|
9312
|
+
});
|
|
9313
|
+
}
|
|
9314
|
+
if (input4.phase === REVIEW_PHASE && aged(thresholds.reviewAgingMs)) {
|
|
9315
|
+
reasons.push({ kind: "review_aging", label: "Awaiting review", severity: "high" });
|
|
9316
|
+
}
|
|
9317
|
+
if (blocked && aged(thresholds.blockedAgingMs)) {
|
|
9318
|
+
reasons.push({ kind: "blocked_aging", label: "Blocked and aging", severity: "high" });
|
|
9319
|
+
}
|
|
9320
|
+
if (input4.phase === PLANNING_PHASE && input4.planExists && !input4.planApproved && aged(thresholds.planApprovalAgingMs)) {
|
|
9321
|
+
reasons.push({
|
|
9322
|
+
kind: "plan_awaiting_approval",
|
|
9323
|
+
label: "Plan awaiting approval",
|
|
9324
|
+
severity: "medium"
|
|
9325
|
+
});
|
|
9326
|
+
}
|
|
9327
|
+
if (input4.depsSatisfied === false && (input4.phase === READY_PHASE || input4.phase === IN_PROGRESS_PHASE)) {
|
|
9328
|
+
reasons.push({ kind: "deps_unsatisfied", label: "Unmet dependencies", severity: "high" });
|
|
9329
|
+
}
|
|
9330
|
+
return reasons;
|
|
9331
|
+
}
|
|
9332
|
+
var DAY, DEFAULT_STALE_THRESHOLDS, PLANNING_PHASE, READY_PHASE, IN_PROGRESS_PHASE, REVIEW_PHASE;
|
|
9333
|
+
var init_classify = __esm({
|
|
9334
|
+
"src/staleness/classify.ts"() {
|
|
9335
|
+
"use strict";
|
|
9336
|
+
DAY = 24 * 60 * 60 * 1e3;
|
|
9337
|
+
DEFAULT_STALE_THRESHOLDS = {
|
|
9338
|
+
inProgressNoActivityMs: 7 * DAY,
|
|
9339
|
+
readyUnclaimedMs: 3 * DAY,
|
|
9340
|
+
reviewAgingMs: 3 * DAY,
|
|
9341
|
+
blockedAgingMs: 3 * DAY,
|
|
9342
|
+
planApprovalAgingMs: 3 * DAY
|
|
9343
|
+
};
|
|
9344
|
+
PLANNING_PHASE = "ready_for_planning";
|
|
9345
|
+
READY_PHASE = "ready_to_implement";
|
|
9346
|
+
IN_PROGRESS_PHASE = "in_progress";
|
|
9347
|
+
REVIEW_PHASE = "review";
|
|
9348
|
+
}
|
|
9349
|
+
});
|
|
9350
|
+
|
|
9180
9351
|
// src/dashboard/servers.ts
|
|
9181
9352
|
import { readdir as readdir10, readFile as readFile13, unlink as unlink2 } from "fs/promises";
|
|
9182
9353
|
import { resolve as resolve18 } from "path";
|
|
@@ -9367,8 +9538,8 @@ function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
|
|
|
9367
9538
|
return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
|
|
9368
9539
|
}
|
|
9369
9540
|
function delay(ms) {
|
|
9370
|
-
return new Promise((
|
|
9371
|
-
const timer3 = setTimeout(
|
|
9541
|
+
return new Promise((resolve111) => {
|
|
9542
|
+
const timer3 = setTimeout(resolve111, ms);
|
|
9372
9543
|
if (typeof timer3.unref === "function") {
|
|
9373
9544
|
timer3.unref();
|
|
9374
9545
|
}
|
|
@@ -9811,7 +9982,38 @@ var init_scanner = __esm({
|
|
|
9811
9982
|
});
|
|
9812
9983
|
|
|
9813
9984
|
// src/dashboard/api.ts
|
|
9814
|
-
|
|
9985
|
+
var api_exports = {};
|
|
9986
|
+
__export(api_exports, {
|
|
9987
|
+
WorkspaceBlockedError: () => WorkspaceBlockedError,
|
|
9988
|
+
clearStatusConfigCache: () => clearStatusConfigCache,
|
|
9989
|
+
collectStaleCandidates: () => collectStaleCandidates,
|
|
9990
|
+
createWorkspace: () => createWorkspace,
|
|
9991
|
+
deleteWorkspace: () => deleteWorkspace,
|
|
9992
|
+
getAssignmentDetail: () => getAssignmentDetail,
|
|
9993
|
+
getAssignmentDetailById: () => getAssignmentDetailById,
|
|
9994
|
+
getEditableDocument: () => getEditableDocument,
|
|
9995
|
+
getEditableDocumentById: () => getEditableDocumentById,
|
|
9996
|
+
getHelp: () => getHelp,
|
|
9997
|
+
getMemoryDetail: () => getMemoryDetail,
|
|
9998
|
+
getOverview: () => getOverview,
|
|
9999
|
+
getPlaybookDetail: () => getPlaybookDetail,
|
|
10000
|
+
getProjectDetail: () => getProjectDetail,
|
|
10001
|
+
getResourceDetail: () => getResourceDetail,
|
|
10002
|
+
getStatusConfig: () => getStatusConfig,
|
|
10003
|
+
installRecordsInvalidation: () => installRecordsInvalidation,
|
|
10004
|
+
invalidateRecordsCache: () => invalidateRecordsCache,
|
|
10005
|
+
listAllMemories: () => listAllMemories,
|
|
10006
|
+
listAllResources: () => listAllResources,
|
|
10007
|
+
listArchived: () => listArchived,
|
|
10008
|
+
listAssignmentsBoard: () => listAssignmentsBoard,
|
|
10009
|
+
listPlaybooks: () => listPlaybooks,
|
|
10010
|
+
listProjects: () => listProjects,
|
|
10011
|
+
listWorkspaceRecords: () => listWorkspaceRecords,
|
|
10012
|
+
listWorkspaces: () => listWorkspaces,
|
|
10013
|
+
resolveProjectPath: () => resolveProjectPath,
|
|
10014
|
+
resolveWorkspaceMembers: () => resolveWorkspaceMembers
|
|
10015
|
+
});
|
|
10016
|
+
import { readdir as readdir11, readFile as readFile14, writeFile as writeFile3, stat as stat2 } from "fs/promises";
|
|
9815
10017
|
import { resolve as resolve20, dirname as dirname3, basename } from "path";
|
|
9816
10018
|
function clearFrontmatterField(content, key) {
|
|
9817
10019
|
const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
|
|
@@ -10202,10 +10404,10 @@ async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options =
|
|
|
10202
10404
|
(total, record) => total + (record.summary.progress["failed"] ?? 0),
|
|
10203
10405
|
0
|
|
10204
10406
|
),
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10407
|
+
// Derived from the SAME classifier verdict as the stale segment (via the
|
|
10408
|
+
// pre-cap segment total) so the badge count can never diverge from the
|
|
10409
|
+
// listed rows.
|
|
10410
|
+
staleAssignments: segments.stale.total
|
|
10209
10411
|
},
|
|
10210
10412
|
hero,
|
|
10211
10413
|
segments,
|
|
@@ -11357,9 +11559,77 @@ function segmentSeverity(segment) {
|
|
|
11357
11559
|
return "medium";
|
|
11358
11560
|
}
|
|
11359
11561
|
}
|
|
11562
|
+
function topStaleReason(reasons) {
|
|
11563
|
+
if (reasons.length === 0) return null;
|
|
11564
|
+
return reasons.slice().sort((a, b) => STALE_SEVERITY_RANK[b.severity] - STALE_SEVERITY_RANK[a.severity])[0];
|
|
11565
|
+
}
|
|
11566
|
+
async function readProgressActivityMs(progressPath, now) {
|
|
11567
|
+
try {
|
|
11568
|
+
const s = await stat2(progressPath);
|
|
11569
|
+
return Math.max(0, now - s.mtimeMs);
|
|
11570
|
+
} catch {
|
|
11571
|
+
return null;
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
function classifyAssignmentRecord(assignment, terminalStatuses3, depsSatisfied, lastActivityMs, thresholds) {
|
|
11575
|
+
const virtuals = deriveStatusVirtuals(assignment, terminalStatuses3);
|
|
11576
|
+
return classifyNeedsAttention(
|
|
11577
|
+
{
|
|
11578
|
+
phase: virtuals.phase,
|
|
11579
|
+
disposition: virtuals.disposition,
|
|
11580
|
+
isTerminal: terminalStatuses3.has(assignment.status),
|
|
11581
|
+
assignee: assignment.assignee ?? null,
|
|
11582
|
+
blockedReason: assignment.blockedReason,
|
|
11583
|
+
depsSatisfied,
|
|
11584
|
+
// plan_awaiting_approval is deferred to the decision inbox's plan-approval
|
|
11585
|
+
// category for now; pass values that keep that reason dormant.
|
|
11586
|
+
planExists: false,
|
|
11587
|
+
planApproved: true,
|
|
11588
|
+
statusAgeMs: virtuals.statusAge,
|
|
11589
|
+
lastActivityMs
|
|
11590
|
+
},
|
|
11591
|
+
thresholds
|
|
11592
|
+
);
|
|
11593
|
+
}
|
|
11594
|
+
async function collectStaleCandidates(projectsDir2, assignmentsDir2) {
|
|
11595
|
+
const [projectRecords, standaloneRecords] = await Promise.all([
|
|
11596
|
+
listProjectRecords(projectsDir2),
|
|
11597
|
+
listStandaloneRecords(assignmentsDir2)
|
|
11598
|
+
]);
|
|
11599
|
+
const { terminalStatuses: terminalStatuses3 } = await getStatusConfig();
|
|
11600
|
+
const thresholds = resolveStaleThresholds((await readConfig()).staleness);
|
|
11601
|
+
const now = Date.now();
|
|
11602
|
+
const out = [];
|
|
11603
|
+
for (const record of projectRecords) {
|
|
11604
|
+
if (isProjectArchived(record.summary)) continue;
|
|
11605
|
+
const projectPath = resolve20(projectsDir2, record.summary.slug);
|
|
11606
|
+
const depMap = /* @__PURE__ */ new Map();
|
|
11607
|
+
for (const a of record.assignments) depMap.set(a.slug, a.status);
|
|
11608
|
+
for (const assignment of activeAssignments(record.assignments)) {
|
|
11609
|
+
const depsSatisfied = assignment.dependsOn.length === 0 ? true : (await getUnmetDependencies(projectPath, assignment.dependsOn, terminalStatuses3, depMap)).length === 0;
|
|
11610
|
+
const lastActivityMs = await readProgressActivityMs(
|
|
11611
|
+
resolve20(projectPath, "assignments", assignment.slug, "progress.md"),
|
|
11612
|
+
now
|
|
11613
|
+
);
|
|
11614
|
+
const reasons = classifyAssignmentRecord(assignment, terminalStatuses3, depsSatisfied, lastActivityMs, thresholds);
|
|
11615
|
+
if (reasons.length > 0) {
|
|
11616
|
+
out.push({ assignmentId: assignment.id, projectSlug: record.summary.slug, reasons });
|
|
11617
|
+
}
|
|
11618
|
+
}
|
|
11619
|
+
}
|
|
11620
|
+
for (const sr of standaloneRecords) {
|
|
11621
|
+
if (sr.record.archived === true) continue;
|
|
11622
|
+
const lastActivityMs = await readProgressActivityMs(resolve20(sr.assignmentDir, "progress.md"), now);
|
|
11623
|
+
const reasons = classifyAssignmentRecord(sr.record, terminalStatuses3, true, lastActivityMs, thresholds);
|
|
11624
|
+
if (reasons.length > 0) out.push({ assignmentId: sr.record.id, projectSlug: null, reasons });
|
|
11625
|
+
}
|
|
11626
|
+
return out;
|
|
11627
|
+
}
|
|
11360
11628
|
async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standaloneRecords, traces) {
|
|
11361
11629
|
const now = Date.now();
|
|
11362
11630
|
const buckets = emptyBuckets();
|
|
11631
|
+
const { terminalStatuses: terminalStatuses3 } = await getStatusConfig();
|
|
11632
|
+
const staleThresholds = resolveStaleThresholds((await readConfig()).staleness);
|
|
11363
11633
|
const newestPool = [];
|
|
11364
11634
|
for (const record of projectRecords) {
|
|
11365
11635
|
if (isProjectArchived(record.summary)) continue;
|
|
@@ -11368,6 +11638,7 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11368
11638
|
depMap.set(a.slug, a.status);
|
|
11369
11639
|
}
|
|
11370
11640
|
const visibleAssignments = activeAssignments(record.assignments);
|
|
11641
|
+
const projectPath = resolve20(projectsDir2, record.summary.slug);
|
|
11371
11642
|
const resolvedTransitions = await Promise.all(
|
|
11372
11643
|
visibleAssignments.map(async (assignment) => {
|
|
11373
11644
|
const t0 = traces ? performance.now() : 0;
|
|
@@ -11379,13 +11650,25 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11379
11650
|
{ traces, dependencyStatusMap: depMap }
|
|
11380
11651
|
);
|
|
11381
11652
|
if (traces) accumulatePhase(traces, "get-available-transitions", performance.now() - t0);
|
|
11382
|
-
|
|
11653
|
+
const depsSatisfied = assignment.dependsOn.length === 0 ? true : (await getUnmetDependencies(projectPath, assignment.dependsOn, terminalStatuses3, depMap)).length === 0;
|
|
11654
|
+
const lastActivityMs = await readProgressActivityMs(
|
|
11655
|
+
resolve20(projectPath, "assignments", assignment.slug, "progress.md"),
|
|
11656
|
+
now
|
|
11657
|
+
);
|
|
11658
|
+
return { assignment, availableTransitions, depsSatisfied, lastActivityMs };
|
|
11383
11659
|
})
|
|
11384
11660
|
);
|
|
11385
|
-
for (const { assignment, availableTransitions } of resolvedTransitions) {
|
|
11661
|
+
for (const { assignment, availableTransitions, depsSatisfied, lastActivityMs } of resolvedTransitions) {
|
|
11386
11662
|
const segmentId = STATUS_TO_SEGMENT[assignment.status];
|
|
11387
|
-
const
|
|
11388
|
-
const
|
|
11663
|
+
const isTerminal = terminalStatuses3.has(assignment.status);
|
|
11664
|
+
const staleReasons = classifyAssignmentRecord(
|
|
11665
|
+
assignment,
|
|
11666
|
+
terminalStatuses3,
|
|
11667
|
+
depsSatisfied,
|
|
11668
|
+
lastActivityMs,
|
|
11669
|
+
staleThresholds
|
|
11670
|
+
);
|
|
11671
|
+
const stale = staleReasons.length > 0;
|
|
11389
11672
|
const agingMs = Math.max(0, now - parseTimestamp(assignment.updated));
|
|
11390
11673
|
const baseId = `${record.summary.slug}:${assignment.slug}`;
|
|
11391
11674
|
const shared = {
|
|
@@ -11414,11 +11697,12 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11414
11697
|
buckets[segmentId].push(primary);
|
|
11415
11698
|
}
|
|
11416
11699
|
if (stale && !isTerminal) {
|
|
11700
|
+
const top = topStaleReason(staleReasons);
|
|
11417
11701
|
const staleItem = {
|
|
11418
11702
|
...shared,
|
|
11419
11703
|
id: `${baseId}:stale`,
|
|
11420
11704
|
severity: "low",
|
|
11421
|
-
reason: SEGMENT_REASON.stale,
|
|
11705
|
+
reason: top?.label ?? SEGMENT_REASON.stale,
|
|
11422
11706
|
segment: "stale"
|
|
11423
11707
|
};
|
|
11424
11708
|
buckets.stale.push(staleItem);
|
|
@@ -11442,14 +11726,22 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11442
11726
|
const t0 = traces ? performance.now() : 0;
|
|
11443
11727
|
const availableTransitions = await getStandaloneAvailableTransitions(sr.record);
|
|
11444
11728
|
if (traces) accumulatePhase(traces, "get-available-transitions", performance.now() - t0);
|
|
11445
|
-
|
|
11729
|
+
const lastActivityMs = await readProgressActivityMs(resolve20(sr.assignmentDir, "progress.md"), now);
|
|
11730
|
+
return { sr, availableTransitions, lastActivityMs };
|
|
11446
11731
|
})
|
|
11447
11732
|
);
|
|
11448
|
-
for (const { sr, availableTransitions } of resolvedStandaloneTransitions) {
|
|
11733
|
+
for (const { sr, availableTransitions, lastActivityMs } of resolvedStandaloneTransitions) {
|
|
11449
11734
|
const assignment = sr.record;
|
|
11450
11735
|
const segmentId = STATUS_TO_SEGMENT[assignment.status];
|
|
11451
|
-
const
|
|
11452
|
-
const
|
|
11736
|
+
const isTerminal = terminalStatuses3.has(assignment.status);
|
|
11737
|
+
const staleReasons = classifyAssignmentRecord(
|
|
11738
|
+
assignment,
|
|
11739
|
+
terminalStatuses3,
|
|
11740
|
+
true,
|
|
11741
|
+
lastActivityMs,
|
|
11742
|
+
staleThresholds
|
|
11743
|
+
);
|
|
11744
|
+
const stale = staleReasons.length > 0;
|
|
11453
11745
|
const agingMs = Math.max(0, now - parseTimestamp(assignment.updated));
|
|
11454
11746
|
const baseId = `standalone:${sr.id}`;
|
|
11455
11747
|
const shared = {
|
|
@@ -11477,11 +11769,12 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11477
11769
|
});
|
|
11478
11770
|
}
|
|
11479
11771
|
if (stale && !isTerminal) {
|
|
11772
|
+
const top = topStaleReason(staleReasons);
|
|
11480
11773
|
buckets.stale.push({
|
|
11481
11774
|
...shared,
|
|
11482
11775
|
id: `${baseId}:stale`,
|
|
11483
11776
|
severity: "low",
|
|
11484
|
-
reason: SEGMENT_REASON.stale,
|
|
11777
|
+
reason: top?.label ?? SEGMENT_REASON.stale,
|
|
11485
11778
|
segment: "stale"
|
|
11486
11779
|
});
|
|
11487
11780
|
}
|
|
@@ -11602,13 +11895,6 @@ function parseTimestamp(timestamp) {
|
|
|
11602
11895
|
const parsed = Date.parse(timestamp);
|
|
11603
11896
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
11604
11897
|
}
|
|
11605
|
-
function isStale(updated) {
|
|
11606
|
-
const timestamp = parseTimestamp(updated);
|
|
11607
|
-
if (timestamp === 0) {
|
|
11608
|
-
return false;
|
|
11609
|
-
}
|
|
11610
|
-
return Date.now() - timestamp > STALE_ASSIGNMENT_MS;
|
|
11611
|
-
}
|
|
11612
11898
|
async function countOpenQuestions(projectPath, assignmentSlug) {
|
|
11613
11899
|
const commentsPath = resolve20(
|
|
11614
11900
|
projectPath,
|
|
@@ -11727,7 +12013,7 @@ async function getPlaybookDetail(playbooksDir3, slug) {
|
|
|
11727
12013
|
enabled
|
|
11728
12014
|
};
|
|
11729
12015
|
}
|
|
11730
|
-
var WorkspaceBlockedError,
|
|
12016
|
+
var WorkspaceBlockedError, RECENT_PROJECTS_LIMIT, RECENT_ACTIVITY_LIMIT, RECENT_SESSIONS_LIMIT, NEWEST_CREATED_LIMIT, SEGMENT_DISPLAY_CAP, STALE_LIMIT_DEFAULT, STALE_LIMIT_MAX, STATUS_TO_SEGMENT, HERO_PRIORITY, projectRecordsCache, standaloneRecordsCache, DEFAULT_TRANSITION_DEFINITIONS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS, STALE_SEVERITY_RANK;
|
|
11731
12017
|
var init_api = __esm({
|
|
11732
12018
|
"src/dashboard/api.ts"() {
|
|
11733
12019
|
"use strict";
|
|
@@ -11745,6 +12031,7 @@ var init_api = __esm({
|
|
|
11745
12031
|
init_help();
|
|
11746
12032
|
init_agent_sessions();
|
|
11747
12033
|
init_overviewCopy();
|
|
12034
|
+
init_classify();
|
|
11748
12035
|
WorkspaceBlockedError = class extends Error {
|
|
11749
12036
|
blockedBy;
|
|
11750
12037
|
constructor(blockedBy) {
|
|
@@ -11755,7 +12042,6 @@ var init_api = __esm({
|
|
|
11755
12042
|
this.blockedBy = blockedBy;
|
|
11756
12043
|
}
|
|
11757
12044
|
};
|
|
11758
|
-
STALE_ASSIGNMENT_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
11759
12045
|
RECENT_PROJECTS_LIMIT = 6;
|
|
11760
12046
|
RECENT_ACTIVITY_LIMIT = 12;
|
|
11761
12047
|
RECENT_SESSIONS_LIMIT = 10;
|
|
@@ -11763,7 +12049,6 @@ var init_api = __esm({
|
|
|
11763
12049
|
SEGMENT_DISPLAY_CAP = 5;
|
|
11764
12050
|
STALE_LIMIT_DEFAULT = 50;
|
|
11765
12051
|
STALE_LIMIT_MAX = 200;
|
|
11766
|
-
TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["completed", "failed", "archived"]);
|
|
11767
12052
|
STATUS_TO_SEGMENT = {
|
|
11768
12053
|
review: "readyForReview",
|
|
11769
12054
|
ready_to_implement: "readyToImplement",
|
|
@@ -11856,6 +12141,7 @@ var init_api = __esm({
|
|
|
11856
12141
|
failed: "fill:#9f2d2d,stroke:#651616,color:#ffffff",
|
|
11857
12142
|
review: "fill:#c6911e,stroke:#7a5a10,color:#ffffff"
|
|
11858
12143
|
};
|
|
12144
|
+
STALE_SEVERITY_RANK = { high: 3, medium: 2, low: 1 };
|
|
11859
12145
|
}
|
|
11860
12146
|
});
|
|
11861
12147
|
|
|
@@ -11866,12 +12152,13 @@ __export(recompute_exports, {
|
|
|
11866
12152
|
markDeriveMigrated: () => markDeriveMigrated,
|
|
11867
12153
|
recomputeAll: () => recomputeAll,
|
|
11868
12154
|
recomputeAndWrite: () => recomputeAndWrite,
|
|
12155
|
+
recomputeAssignmentDir: () => recomputeAssignmentDir,
|
|
11869
12156
|
recomputeDependents: () => recomputeDependents,
|
|
11870
12157
|
resolveDeriveContext: () => resolveDeriveContext
|
|
11871
12158
|
});
|
|
11872
12159
|
import { createHash as createHash2 } from "crypto";
|
|
11873
|
-
import { open, readdir as readdir13, readFile as readFile18, unlink as unlink5, stat as
|
|
11874
|
-
import { dirname as dirname5, resolve as resolve25 } from "path";
|
|
12160
|
+
import { open, readdir as readdir13, readFile as readFile18, unlink as unlink5, stat as stat3 } from "fs/promises";
|
|
12161
|
+
import { basename as basename3, dirname as dirname5, resolve as resolve25 } from "path";
|
|
11875
12162
|
async function isDeriveMigrated() {
|
|
11876
12163
|
return fileExists(resolve25(syntaurRoot(), MIGRATION_MARKER));
|
|
11877
12164
|
}
|
|
@@ -11911,7 +12198,7 @@ async function acquireLock(assignmentDir) {
|
|
|
11911
12198
|
const code = err2.code;
|
|
11912
12199
|
if (code !== "EEXIST") throw err2;
|
|
11913
12200
|
try {
|
|
11914
|
-
const info = await
|
|
12201
|
+
const info = await stat3(lockPath);
|
|
11915
12202
|
if (Date.now() - info.mtimeMs > LOCK_STALE_MS) {
|
|
11916
12203
|
await unlink5(lockPath).catch(() => {
|
|
11917
12204
|
});
|
|
@@ -12103,6 +12390,18 @@ async function recomputeAll(projectsDir2, standaloneDir, opts) {
|
|
|
12103
12390
|
}
|
|
12104
12391
|
return summary;
|
|
12105
12392
|
}
|
|
12393
|
+
async function recomputeAssignmentDir(assignmentDir, cause, by) {
|
|
12394
|
+
try {
|
|
12395
|
+
const assignmentPath = resolve25(assignmentDir, "assignment.md");
|
|
12396
|
+
if (!await fileExists(assignmentPath)) return null;
|
|
12397
|
+
const parent = dirname5(assignmentDir);
|
|
12398
|
+
const projectDir = basename3(parent) === "assignments" ? dirname5(parent) : null;
|
|
12399
|
+
const context = await resolveDeriveContext();
|
|
12400
|
+
return await recomputeAndWrite(assignmentPath, { cause, by, projectDir, context });
|
|
12401
|
+
} catch {
|
|
12402
|
+
return null;
|
|
12403
|
+
}
|
|
12404
|
+
}
|
|
12106
12405
|
var LOCK_FILE, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_MAX_WAITS, CAS_RETRIES, MIGRATION_MARKER;
|
|
12107
12406
|
var init_recompute = __esm({
|
|
12108
12407
|
"src/lifecycle/recompute.ts"() {
|
|
@@ -12447,14 +12746,14 @@ var init_launch = __esm({
|
|
|
12447
12746
|
});
|
|
12448
12747
|
|
|
12449
12748
|
// src/usage/cwd-extractor.ts
|
|
12450
|
-
import { open as open3, readdir as readdir14, stat as
|
|
12749
|
+
import { open as open3, readdir as readdir14, stat as stat4 } from "fs/promises";
|
|
12451
12750
|
import { join as join4 } from "path";
|
|
12452
12751
|
import { homedir as homedir3 } from "os";
|
|
12453
12752
|
async function extractClaudeSessionMeta(jsonlPath) {
|
|
12454
12753
|
const cwd = await derivePathFromTranscript(jsonlPath);
|
|
12455
12754
|
if (!cwd) return null;
|
|
12456
|
-
const
|
|
12457
|
-
const sessionId =
|
|
12755
|
+
const basename10 = jsonlPath.split("/").pop() ?? "";
|
|
12756
|
+
const sessionId = basename10.replace(/\.jsonl$/, "");
|
|
12458
12757
|
if (!sessionId) return null;
|
|
12459
12758
|
const startTs = await readFirstTimestamp(jsonlPath);
|
|
12460
12759
|
const endTs = await readLastTimestamp(jsonlPath);
|
|
@@ -12549,8 +12848,8 @@ async function* walkClaudeProjects(opts = {}) {
|
|
|
12549
12848
|
async function* walkCodexSessions(opts = {}) {
|
|
12550
12849
|
const root = resolveCodexSessionsRoot(opts.root);
|
|
12551
12850
|
for await (const filePath of walkJsonlRecursive(root)) {
|
|
12552
|
-
const
|
|
12553
|
-
if (!
|
|
12851
|
+
const basename10 = filePath.split("/").pop() ?? "";
|
|
12852
|
+
if (!basename10.endsWith(".jsonl")) continue;
|
|
12554
12853
|
if (opts.sinceMtimeMs !== void 0) {
|
|
12555
12854
|
const mtime = await mtimeMs(filePath);
|
|
12556
12855
|
if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
|
|
@@ -12568,10 +12867,10 @@ function resolveCodexSessionsRoot(override) {
|
|
|
12568
12867
|
return join4(homedir3(), ".codex", "sessions");
|
|
12569
12868
|
}
|
|
12570
12869
|
async function extractPiSessionMeta(jsonlPath) {
|
|
12571
|
-
const
|
|
12572
|
-
const underscoreIdx =
|
|
12870
|
+
const basename10 = jsonlPath.split("/").pop() ?? "";
|
|
12871
|
+
const underscoreIdx = basename10.lastIndexOf("_");
|
|
12573
12872
|
if (underscoreIdx === -1) return null;
|
|
12574
|
-
const sessionId =
|
|
12873
|
+
const sessionId = basename10.slice(underscoreIdx + 1).replace(/\.jsonl$/, "");
|
|
12575
12874
|
if (!sessionId) return null;
|
|
12576
12875
|
let handle;
|
|
12577
12876
|
try {
|
|
@@ -12686,7 +12985,7 @@ async function* walkJsonlRecursive(root) {
|
|
|
12686
12985
|
}
|
|
12687
12986
|
async function mtimeMs(path) {
|
|
12688
12987
|
try {
|
|
12689
|
-
const s = await
|
|
12988
|
+
const s = await stat4(path);
|
|
12690
12989
|
return s.mtimeMs;
|
|
12691
12990
|
} catch {
|
|
12692
12991
|
return null;
|
|
@@ -13733,6 +14032,40 @@ var init_scanner2 = __esm({
|
|
|
13733
14032
|
}
|
|
13734
14033
|
});
|
|
13735
14034
|
|
|
14035
|
+
// src/staleness/watchdog.ts
|
|
14036
|
+
var watchdog_exports = {};
|
|
14037
|
+
__export(watchdog_exports, {
|
|
14038
|
+
runStalenessWatchdogTick: () => runStalenessWatchdogTick
|
|
14039
|
+
});
|
|
14040
|
+
function runStalenessWatchdogTick(candidates, seen, emit) {
|
|
14041
|
+
const staleNow = /* @__PURE__ */ new Map();
|
|
14042
|
+
for (const c2 of candidates) {
|
|
14043
|
+
if (c2.reasons.length > 0) staleNow.set(c2.assignmentId, c2);
|
|
14044
|
+
}
|
|
14045
|
+
let newlyStale = 0;
|
|
14046
|
+
for (const [id, c2] of staleNow) {
|
|
14047
|
+
if (!seen.has(id)) {
|
|
14048
|
+
seen.add(id);
|
|
14049
|
+
newlyStale++;
|
|
14050
|
+
emit({ assignmentId: id, projectSlug: c2.projectSlug, type: "staleness-detected", reasons: c2.reasons });
|
|
14051
|
+
}
|
|
14052
|
+
}
|
|
14053
|
+
let cleared = 0;
|
|
14054
|
+
for (const id of [...seen]) {
|
|
14055
|
+
if (!staleNow.has(id)) {
|
|
14056
|
+
seen.delete(id);
|
|
14057
|
+
cleared++;
|
|
14058
|
+
emit({ assignmentId: id, projectSlug: null, type: "staleness-cleared", reasons: [] });
|
|
14059
|
+
}
|
|
14060
|
+
}
|
|
14061
|
+
return { scanned: candidates.length, stale: staleNow.size, newlyStale, cleared };
|
|
14062
|
+
}
|
|
14063
|
+
var init_watchdog = __esm({
|
|
14064
|
+
"src/staleness/watchdog.ts"() {
|
|
14065
|
+
"use strict";
|
|
14066
|
+
}
|
|
14067
|
+
});
|
|
14068
|
+
|
|
13736
14069
|
// scripts/install-macos-url-handler.mjs
|
|
13737
14070
|
var install_macos_url_handler_exports = {};
|
|
13738
14071
|
__export(install_macos_url_handler_exports, {
|
|
@@ -13749,7 +14082,7 @@ import {
|
|
|
13749
14082
|
unlinkSync
|
|
13750
14083
|
} from "fs";
|
|
13751
14084
|
import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
|
|
13752
|
-
import { dirname as dirname21, resolve as
|
|
14085
|
+
import { dirname as dirname21, resolve as resolve72, join as join16 } from "path";
|
|
13753
14086
|
import { homedir as homedir13, tmpdir as tmpdir2 } from "os";
|
|
13754
14087
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
13755
14088
|
function syntaurRootMjs() {
|
|
@@ -13771,7 +14104,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
13771
14104
|
}
|
|
13772
14105
|
const stateRoot = syntaurRootMjs();
|
|
13773
14106
|
mkdirSync3(stateRoot, { recursive: true });
|
|
13774
|
-
const lockPath =
|
|
14107
|
+
const lockPath = resolve72(stateRoot, "install-url-handler.lock");
|
|
13775
14108
|
let lockFd;
|
|
13776
14109
|
try {
|
|
13777
14110
|
lockFd = openSync(lockPath, "wx");
|
|
@@ -13787,8 +14120,8 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
13787
14120
|
throw err2;
|
|
13788
14121
|
}
|
|
13789
14122
|
try {
|
|
13790
|
-
const pkgRoot =
|
|
13791
|
-
const cliBin = realpathSync3(
|
|
14123
|
+
const pkgRoot = resolve72(dirname21(fileURLToPath9(import.meta.url)), "..");
|
|
14124
|
+
const cliBin = realpathSync3(resolve72(pkgRoot, "bin/syntaur.js"));
|
|
13792
14125
|
const nodeBin = process.execPath;
|
|
13793
14126
|
const bundleParent = join16(
|
|
13794
14127
|
homedir13(),
|
|
@@ -15967,7 +16300,7 @@ init_timestamp();
|
|
|
15967
16300
|
init_fs();
|
|
15968
16301
|
init_git_worktree();
|
|
15969
16302
|
import { Router as Router2 } from "express";
|
|
15970
|
-
import { resolve as resolve26, basename as
|
|
16303
|
+
import { resolve as resolve26, basename as basename4, isAbsolute as isAbsolute4 } from "path";
|
|
15971
16304
|
import { rm, readFile as readFile19, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
|
|
15972
16305
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
15973
16306
|
|
|
@@ -16771,7 +17104,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
|
|
|
16771
17104
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
16772
17105
|
return;
|
|
16773
17106
|
}
|
|
16774
|
-
const document = await getEditableDocument(projectsDir2, "memory",
|
|
17107
|
+
const document = await getEditableDocument(projectsDir2, "memory", basename4(projectDir), itemSlug);
|
|
16775
17108
|
if (!document) {
|
|
16776
17109
|
res.status(404).json({ error: "Memory not found" });
|
|
16777
17110
|
return;
|
|
@@ -16790,7 +17123,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
|
|
|
16790
17123
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
16791
17124
|
return;
|
|
16792
17125
|
}
|
|
16793
|
-
const document = await getEditableDocument(projectsDir2, "resource",
|
|
17126
|
+
const document = await getEditableDocument(projectsDir2, "resource", basename4(projectDir), itemSlug);
|
|
16794
17127
|
if (!document) {
|
|
16795
17128
|
res.status(404).json({ error: "Resource not found" });
|
|
16796
17129
|
return;
|
|
@@ -16875,7 +17208,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16875
17208
|
let content = renderItemStub(kind, {
|
|
16876
17209
|
slug: requestedSlug,
|
|
16877
17210
|
name,
|
|
16878
|
-
projectSlug:
|
|
17211
|
+
projectSlug: basename4(projectDir),
|
|
16879
17212
|
timestamp
|
|
16880
17213
|
});
|
|
16881
17214
|
const customBody = typeof body.body === "string" ? body.body : "";
|
|
@@ -16892,13 +17225,13 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16892
17225
|
} catch (err2) {
|
|
16893
17226
|
if (err2.code === "EEXIST") {
|
|
16894
17227
|
res.status(409).json({
|
|
16895
|
-
error: `${kind === "memory" ? "Memory" : "Resource"} with slug "${requestedSlug}" already exists in project "${
|
|
17228
|
+
error: `${kind === "memory" ? "Memory" : "Resource"} with slug "${requestedSlug}" already exists in project "${basename4(projectDir)}".`
|
|
16896
17229
|
});
|
|
16897
17230
|
return;
|
|
16898
17231
|
}
|
|
16899
17232
|
throw err2;
|
|
16900
17233
|
}
|
|
16901
|
-
res.status(201).json({ slug: requestedSlug, projectSlug:
|
|
17234
|
+
res.status(201).json({ slug: requestedSlug, projectSlug: basename4(projectDir), content });
|
|
16902
17235
|
} catch (error) {
|
|
16903
17236
|
console.error(`Error creating ${kind}:`, error);
|
|
16904
17237
|
res.status(500).json({ error: `Failed to create ${kind}: ${error.message}` });
|
|
@@ -16933,7 +17266,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16933
17266
|
${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
|
|
16934
17267
|
merged = setTopLevelField(merged, "updated", nowTimestamp());
|
|
16935
17268
|
await writeFileForce(filePath, merged);
|
|
16936
|
-
const detail = await getItemDetail(kind,
|
|
17269
|
+
const detail = await getItemDetail(kind, basename4(projectDir), itemSlug);
|
|
16937
17270
|
res.json({ [kind]: detail, content: merged });
|
|
16938
17271
|
} catch (error) {
|
|
16939
17272
|
console.error(`Error updating ${kind}:`, error);
|
|
@@ -20200,7 +20533,7 @@ async function resolveSessionPlan(input4, terminal) {
|
|
|
20200
20533
|
init_launch();
|
|
20201
20534
|
import { spawn as spawn3 } from "child_process";
|
|
20202
20535
|
import { homedir as homedir5 } from "os";
|
|
20203
|
-
import { basename as
|
|
20536
|
+
import { basename as basename5, join as join6, resolve as resolve29 } from "path";
|
|
20204
20537
|
init_fs();
|
|
20205
20538
|
init_config2();
|
|
20206
20539
|
init_session_id();
|
|
@@ -20220,7 +20553,7 @@ var TerminalNotFoundError = class extends Error {
|
|
|
20220
20553
|
var realSpawn = (command, args, options) => spawn3(command, args, options);
|
|
20221
20554
|
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["osascript", "open", "sh"]);
|
|
20222
20555
|
function isWrapperCommand(command) {
|
|
20223
|
-
return WRAPPER_COMMANDS.has(
|
|
20556
|
+
return WRAPPER_COMMANDS.has(basename5(command));
|
|
20224
20557
|
}
|
|
20225
20558
|
var WRAPPER_EXIT_TIMEOUT_MS = 1500;
|
|
20226
20559
|
async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
@@ -20248,7 +20581,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20248
20581
|
`Spawn failed: ${msg2}. Verify the terminal is installed and on PATH.`
|
|
20249
20582
|
);
|
|
20250
20583
|
}
|
|
20251
|
-
await new Promise((
|
|
20584
|
+
await new Promise((resolve111, reject) => {
|
|
20252
20585
|
let settled = false;
|
|
20253
20586
|
let stderr = "";
|
|
20254
20587
|
const finishOk = () => {
|
|
@@ -20258,7 +20591,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20258
20591
|
child.unref();
|
|
20259
20592
|
} catch {
|
|
20260
20593
|
}
|
|
20261
|
-
|
|
20594
|
+
resolve111();
|
|
20262
20595
|
};
|
|
20263
20596
|
const finishErr = (remediation) => {
|
|
20264
20597
|
if (settled) return;
|
|
@@ -20524,7 +20857,7 @@ function normalizeSlashes(p) {
|
|
|
20524
20857
|
}
|
|
20525
20858
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
20526
20859
|
const realpath3 = opts.realpath ?? realpathSync.native;
|
|
20527
|
-
const
|
|
20860
|
+
const readFile82 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
|
|
20528
20861
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
20529
20862
|
const resolved = resolveScriptPath(scriptUrl, realpath3);
|
|
20530
20863
|
if (resolved === null) {
|
|
@@ -20545,7 +20878,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
20545
20878
|
const pkgJsonPath = join7(dir, "package.json");
|
|
20546
20879
|
let raw2;
|
|
20547
20880
|
try {
|
|
20548
|
-
raw2 =
|
|
20881
|
+
raw2 = readFile82(pkgJsonPath);
|
|
20549
20882
|
} catch {
|
|
20550
20883
|
const parent2 = dirname7(dir);
|
|
20551
20884
|
if (parent2 === dir) break;
|
|
@@ -22610,7 +22943,7 @@ async function readEvents(jobId) {
|
|
|
22610
22943
|
|
|
22611
22944
|
// src/schedules/attempt.ts
|
|
22612
22945
|
import { createHash as createHash3 } from "crypto";
|
|
22613
|
-
import { open as open4, readFile as readFile23, stat as
|
|
22946
|
+
import { open as open4, readFile as readFile23, stat as stat5, unlink as unlink6 } from "fs/promises";
|
|
22614
22947
|
import { resolve as resolve34 } from "path";
|
|
22615
22948
|
|
|
22616
22949
|
// src/schedules/triggers.ts
|
|
@@ -22807,7 +23140,7 @@ async function acquireJobLock(id) {
|
|
|
22807
23140
|
const code = err2.code;
|
|
22808
23141
|
if (code !== "EEXIST") throw err2;
|
|
22809
23142
|
try {
|
|
22810
|
-
const info = await
|
|
23143
|
+
const info = await stat5(lockPath);
|
|
22811
23144
|
if (Date.now() - info.mtimeMs > LOCK_STALE_MS2) {
|
|
22812
23145
|
await unlink6(lockPath).catch(() => {
|
|
22813
23146
|
});
|
|
@@ -24474,8 +24807,8 @@ init_api();
|
|
|
24474
24807
|
import { raw } from "express";
|
|
24475
24808
|
|
|
24476
24809
|
// src/todos/attachments.ts
|
|
24477
|
-
import { mkdir as mkdir4, readdir as readdir16, stat as
|
|
24478
|
-
import { resolve as resolve41, basename as
|
|
24810
|
+
import { mkdir as mkdir4, readdir as readdir16, stat as stat6, rename as rename5, rm as rm4, unlink as unlink7, writeFile as writeFile6, cp } from "fs/promises";
|
|
24811
|
+
import { resolve as resolve41, basename as basename6, dirname as dirname10, extname } from "path";
|
|
24479
24812
|
|
|
24480
24813
|
// src/utils/proof-artifact-id.ts
|
|
24481
24814
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -24572,7 +24905,7 @@ function isSafeInlineMime(mime) {
|
|
|
24572
24905
|
return SAFE_INLINE_MIME.has(mime);
|
|
24573
24906
|
}
|
|
24574
24907
|
function sanitizeAttachmentName(name) {
|
|
24575
|
-
let n =
|
|
24908
|
+
let n = basename6(name || "").replace(/["'\\/]/g, "_");
|
|
24576
24909
|
n = Array.from(n, (ch) => {
|
|
24577
24910
|
const code = ch.charCodeAt(0);
|
|
24578
24911
|
return code < 32 || code === 127 ? "_" : ch;
|
|
@@ -24595,7 +24928,7 @@ function attachmentDirFor(todosDir2, scopeId, todoId) {
|
|
|
24595
24928
|
}
|
|
24596
24929
|
async function dirExists(p) {
|
|
24597
24930
|
try {
|
|
24598
|
-
return (await
|
|
24931
|
+
return (await stat6(p)).isDirectory();
|
|
24599
24932
|
} catch {
|
|
24600
24933
|
return false;
|
|
24601
24934
|
}
|
|
@@ -24630,7 +24963,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
24630
24963
|
if (!ATTACHMENT_ID_RE.test(id)) continue;
|
|
24631
24964
|
const filename = stored.slice(sep2 + 2);
|
|
24632
24965
|
try {
|
|
24633
|
-
const st = await
|
|
24966
|
+
const st = await stat6(resolve41(dir, stored));
|
|
24634
24967
|
if (!st.isFile()) continue;
|
|
24635
24968
|
out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
|
|
24636
24969
|
} catch {
|
|
@@ -25085,8 +25418,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
25085
25418
|
router.post("/:workspace/archive", async (req2, res) => {
|
|
25086
25419
|
try {
|
|
25087
25420
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
25088
|
-
const { resolve:
|
|
25089
|
-
const { readFile:
|
|
25421
|
+
const { resolve: resolve111 } = await import("path");
|
|
25422
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
25090
25423
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
25091
25424
|
const workspace = getWorkspaceParam(req2.params.workspace);
|
|
25092
25425
|
const outcome = await wsLock(workspace, async () => {
|
|
@@ -25102,10 +25435,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
25102
25435
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
25103
25436
|
);
|
|
25104
25437
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
25105
|
-
await ensureDir(
|
|
25438
|
+
await ensureDir(resolve111(todosDir2, "archive"));
|
|
25106
25439
|
let archContent = "";
|
|
25107
25440
|
if (await fileExists(archFile)) {
|
|
25108
|
-
archContent = await
|
|
25441
|
+
archContent = await readFile82(archFile, "utf-8");
|
|
25109
25442
|
archContent = archContent.trimEnd() + "\n\n";
|
|
25110
25443
|
} else {
|
|
25111
25444
|
archContent = `---
|
|
@@ -25406,7 +25739,7 @@ workspace: ${workspace}
|
|
|
25406
25739
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
25407
25740
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
25408
25741
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
25409
|
-
const { readFile:
|
|
25742
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
25410
25743
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
25411
25744
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
25412
25745
|
let assignmentRef;
|
|
@@ -25427,7 +25760,7 @@ workspace: ${workspace}
|
|
|
25427
25760
|
}
|
|
25428
25761
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
25429
25762
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
25430
|
-
let content = await
|
|
25763
|
+
let content = await readFile82(assignmentMdPath2, "utf-8");
|
|
25431
25764
|
content = appendTodosToAssignmentBody2(
|
|
25432
25765
|
content,
|
|
25433
25766
|
items.map((it) => ({
|
|
@@ -26837,7 +27170,7 @@ init_fs();
|
|
|
26837
27170
|
init_config2();
|
|
26838
27171
|
import { execFile as execFile2 } from "child_process";
|
|
26839
27172
|
import { promisify as promisify2 } from "util";
|
|
26840
|
-
import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile30, writeFile as writeFile7, unlink as unlink8, stat as
|
|
27173
|
+
import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile30, writeFile as writeFile7, unlink as unlink8, stat as stat7, open as open5, rename as rename8 } from "fs/promises";
|
|
26841
27174
|
import { resolve as resolve44, join as join9 } from "path";
|
|
26842
27175
|
import { tmpdir } from "os";
|
|
26843
27176
|
var exec2 = promisify2(execFile2);
|
|
@@ -26931,7 +27264,7 @@ async function cloneOrInit(repoUrl, destDir) {
|
|
|
26931
27264
|
}
|
|
26932
27265
|
async function copyRecursive(src, dest) {
|
|
26933
27266
|
if (!await fileExists(src)) return;
|
|
26934
|
-
const s = await
|
|
27267
|
+
const s = await stat7(src);
|
|
26935
27268
|
if (s.isDirectory()) {
|
|
26936
27269
|
await ensureDir(dest);
|
|
26937
27270
|
await cp2(src, dest, { recursive: true, force: true });
|
|
@@ -27622,7 +27955,7 @@ async function runCcusage(opts = {}) {
|
|
|
27622
27955
|
};
|
|
27623
27956
|
}
|
|
27624
27957
|
function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
27625
|
-
return new Promise((
|
|
27958
|
+
return new Promise((resolve111) => {
|
|
27626
27959
|
const child = spawn4(binary, args, {
|
|
27627
27960
|
env: env ?? process.env,
|
|
27628
27961
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -27636,7 +27969,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
|
27636
27969
|
if (settled) return;
|
|
27637
27970
|
settled = true;
|
|
27638
27971
|
clearTimeout(timer3);
|
|
27639
|
-
|
|
27972
|
+
resolve111(result);
|
|
27640
27973
|
};
|
|
27641
27974
|
const timer3 = setTimeout(() => {
|
|
27642
27975
|
timedOut = true;
|
|
@@ -28586,6 +28919,8 @@ function createDashboardServer(options) {
|
|
|
28586
28919
|
});
|
|
28587
28920
|
}
|
|
28588
28921
|
let watcherHandle = null;
|
|
28922
|
+
const STALENESS_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
|
|
28923
|
+
let stalenessWatchdogTimer = null;
|
|
28589
28924
|
return {
|
|
28590
28925
|
async start() {
|
|
28591
28926
|
const { recomputeAndWrite: recomputeAndWrite2, recomputeAll: recomputeAll2, resolveDeriveContext: resolveDeriveContext2, isDeriveMigrated: isDeriveMigrated2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
|
|
@@ -28682,6 +29017,38 @@ function createDashboardServer(options) {
|
|
|
28682
29017
|
onAgentSessionsChanged: () => broadcast({ type: "agent-sessions-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
28683
29018
|
});
|
|
28684
29019
|
startUsageCollector();
|
|
29020
|
+
const startupConfig = await readConfig();
|
|
29021
|
+
if (startupConfig.stalenessWatchdog) {
|
|
29022
|
+
const { collectStaleCandidates: collectStaleCandidates2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
29023
|
+
const { runStalenessWatchdogTick: runStalenessWatchdogTick2 } = await Promise.resolve().then(() => (init_watchdog(), watchdog_exports));
|
|
29024
|
+
const { emitEvent: emitEvent2 } = await Promise.resolve().then(() => (init_event_emit(), event_emit_exports));
|
|
29025
|
+
const stalenessSeen = /* @__PURE__ */ new Set();
|
|
29026
|
+
const watchdogTick = async () => {
|
|
29027
|
+
if (!await migrationGate()) return;
|
|
29028
|
+
try {
|
|
29029
|
+
const candidates = await collectStaleCandidates2(projectsDir2, assignmentsDir2);
|
|
29030
|
+
const summary = runStalenessWatchdogTick2(candidates, stalenessSeen, (e) => {
|
|
29031
|
+
emitEvent2({
|
|
29032
|
+
assignmentId: e.assignmentId,
|
|
29033
|
+
projectSlug: e.projectSlug,
|
|
29034
|
+
type: e.type,
|
|
29035
|
+
actor: "system",
|
|
29036
|
+
details: { reasons: e.reasons.map((r) => r.kind) }
|
|
29037
|
+
});
|
|
29038
|
+
});
|
|
29039
|
+
if (summary.newlyStale > 0 || summary.cleared > 0) {
|
|
29040
|
+
console.log(
|
|
29041
|
+
`staleness watchdog: ${summary.newlyStale} newly stale, ${summary.cleared} cleared (${summary.stale}/${summary.scanned} stale).`
|
|
29042
|
+
);
|
|
29043
|
+
}
|
|
29044
|
+
} catch (err2) {
|
|
29045
|
+
console.error("staleness watchdog tick failed:", err2);
|
|
29046
|
+
}
|
|
29047
|
+
};
|
|
29048
|
+
void watchdogTick();
|
|
29049
|
+
stalenessWatchdogTimer = setInterval(() => void watchdogTick(), STALENESS_WATCHDOG_INTERVAL_MS);
|
|
29050
|
+
stalenessWatchdogTimer.unref?.();
|
|
29051
|
+
}
|
|
28685
29052
|
return new Promise((resolvePromise, reject) => {
|
|
28686
29053
|
server.on("error", (err2) => {
|
|
28687
29054
|
if (err2.code === "EADDRINUSE") {
|
|
@@ -28703,6 +29070,10 @@ function createDashboardServer(options) {
|
|
|
28703
29070
|
});
|
|
28704
29071
|
},
|
|
28705
29072
|
async stop() {
|
|
29073
|
+
if (stalenessWatchdogTimer) {
|
|
29074
|
+
clearInterval(stalenessWatchdogTimer);
|
|
29075
|
+
stalenessWatchdogTimer = null;
|
|
29076
|
+
}
|
|
28706
29077
|
await stopAutodiscovery();
|
|
28707
29078
|
await stopUsageCollector();
|
|
28708
29079
|
if (watcherHandle) {
|
|
@@ -29069,17 +29440,173 @@ init_facts();
|
|
|
29069
29440
|
init_git_worktree();
|
|
29070
29441
|
init_recompute();
|
|
29071
29442
|
init_event_emit();
|
|
29443
|
+
init_transitions();
|
|
29444
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
29445
|
+
import { resolve as resolve52, basename as basename7 } from "path";
|
|
29446
|
+
|
|
29447
|
+
// src/utils/assignment-target.ts
|
|
29448
|
+
init_paths();
|
|
29449
|
+
init_fs();
|
|
29450
|
+
init_config2();
|
|
29451
|
+
init_slug();
|
|
29452
|
+
init_assignment_resolver();
|
|
29453
|
+
init_parser();
|
|
29454
|
+
import { resolve as resolve51 } from "path";
|
|
29072
29455
|
import { readFile as readFile33 } from "fs/promises";
|
|
29073
|
-
|
|
29456
|
+
var AssignmentTargetError = class extends Error {
|
|
29457
|
+
};
|
|
29458
|
+
function classifyContext(ctx) {
|
|
29459
|
+
if (!ctx) return "empty";
|
|
29460
|
+
const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
|
|
29461
|
+
if (hasAssignment) return "assignment";
|
|
29462
|
+
if (ctx.bundleId) return "bundle";
|
|
29463
|
+
if (ctx.sessionId || ctx.transcriptPath) return "standalone";
|
|
29464
|
+
return "empty";
|
|
29465
|
+
}
|
|
29466
|
+
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
29467
|
+
const path = resolve51(assignmentDir, "assignment.md");
|
|
29468
|
+
if (!await fileExists(path)) return null;
|
|
29469
|
+
try {
|
|
29470
|
+
const content = await readFile33(path, "utf-8");
|
|
29471
|
+
const [fm] = extractFrontmatter(content);
|
|
29472
|
+
return getField(fm, "id");
|
|
29473
|
+
} catch {
|
|
29474
|
+
return null;
|
|
29475
|
+
}
|
|
29476
|
+
}
|
|
29477
|
+
async function readContextJson(cwd) {
|
|
29478
|
+
const path = resolve51(cwd, ".syntaur", "context.json");
|
|
29479
|
+
if (!await fileExists(path)) return null;
|
|
29480
|
+
try {
|
|
29481
|
+
const raw2 = await readFile33(path, "utf-8");
|
|
29482
|
+
return JSON.parse(raw2);
|
|
29483
|
+
} catch {
|
|
29484
|
+
return null;
|
|
29485
|
+
}
|
|
29486
|
+
}
|
|
29487
|
+
async function resolveAssignmentTarget(input4, opts = {}) {
|
|
29488
|
+
const config = await readConfig();
|
|
29489
|
+
const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
|
|
29490
|
+
if (opts.project) {
|
|
29491
|
+
if (!input4) {
|
|
29492
|
+
throw new AssignmentTargetError(
|
|
29493
|
+
"--project requires an assignment slug as a positional argument."
|
|
29494
|
+
);
|
|
29495
|
+
}
|
|
29496
|
+
if (!isValidSlug(opts.project)) {
|
|
29497
|
+
throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
|
|
29498
|
+
}
|
|
29499
|
+
if (!isValidSlug(input4)) {
|
|
29500
|
+
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
29501
|
+
}
|
|
29502
|
+
const projectDir = resolve51(baseDir, opts.project);
|
|
29503
|
+
const projectMdPath = resolve51(projectDir, "project.md");
|
|
29504
|
+
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
29505
|
+
throw new AssignmentTargetError(
|
|
29506
|
+
`Project "${opts.project}" not found at ${projectDir}.`
|
|
29507
|
+
);
|
|
29508
|
+
}
|
|
29509
|
+
const assignmentDir = resolve51(projectDir, "assignments", input4);
|
|
29510
|
+
const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
|
|
29511
|
+
if (!await fileExists(assignmentMdPath2)) {
|
|
29512
|
+
throw new AssignmentTargetError(
|
|
29513
|
+
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
29514
|
+
);
|
|
29515
|
+
}
|
|
29516
|
+
const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
|
|
29517
|
+
return {
|
|
29518
|
+
assignmentDir,
|
|
29519
|
+
projectSlug: opts.project,
|
|
29520
|
+
assignmentSlug: input4,
|
|
29521
|
+
id,
|
|
29522
|
+
standalone: false,
|
|
29523
|
+
workspaceGroup: null
|
|
29524
|
+
};
|
|
29525
|
+
}
|
|
29526
|
+
if (input4) {
|
|
29527
|
+
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
|
|
29528
|
+
if (!resolved) {
|
|
29529
|
+
throw new AssignmentTargetError(
|
|
29530
|
+
`Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
|
|
29531
|
+
);
|
|
29532
|
+
}
|
|
29533
|
+
return resolved;
|
|
29534
|
+
}
|
|
29535
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
29536
|
+
const ctx = await readContextJson(cwd);
|
|
29537
|
+
if (!ctx) {
|
|
29538
|
+
throw new AssignmentTargetError(
|
|
29539
|
+
"No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
|
|
29540
|
+
);
|
|
29541
|
+
}
|
|
29542
|
+
if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
|
|
29543
|
+
throw new AssignmentTargetError(
|
|
29544
|
+
`Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
|
|
29545
|
+
);
|
|
29546
|
+
}
|
|
29547
|
+
if (ctx.assignmentDir) {
|
|
29548
|
+
const dir = expandHome(ctx.assignmentDir);
|
|
29549
|
+
const assignmentMdPath2 = resolve51(dir, "assignment.md");
|
|
29550
|
+
if (!await fileExists(assignmentMdPath2)) {
|
|
29551
|
+
throw new AssignmentTargetError(
|
|
29552
|
+
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
29553
|
+
);
|
|
29554
|
+
}
|
|
29555
|
+
const id = await readAssignmentFrontmatterId(dir);
|
|
29556
|
+
if (!id || id.trim() === "") {
|
|
29557
|
+
throw new AssignmentTargetError(
|
|
29558
|
+
`.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
|
|
29559
|
+
);
|
|
29560
|
+
}
|
|
29561
|
+
const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
|
|
29562
|
+
const projectSlug = ctx.projectSlug ?? null;
|
|
29563
|
+
return {
|
|
29564
|
+
assignmentDir: dir,
|
|
29565
|
+
projectSlug,
|
|
29566
|
+
assignmentSlug,
|
|
29567
|
+
id,
|
|
29568
|
+
standalone: projectSlug === null,
|
|
29569
|
+
workspaceGroup: null
|
|
29570
|
+
};
|
|
29571
|
+
}
|
|
29572
|
+
if (ctx.projectSlug && ctx.assignmentSlug) {
|
|
29573
|
+
if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
|
|
29574
|
+
throw new AssignmentTargetError(
|
|
29575
|
+
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
29576
|
+
);
|
|
29577
|
+
}
|
|
29578
|
+
const assignmentDir = resolve51(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
29579
|
+
const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
|
|
29580
|
+
if (!await fileExists(assignmentMdPath2)) {
|
|
29581
|
+
throw new AssignmentTargetError(
|
|
29582
|
+
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
29583
|
+
);
|
|
29584
|
+
}
|
|
29585
|
+
const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
|
|
29586
|
+
return {
|
|
29587
|
+
assignmentDir,
|
|
29588
|
+
projectSlug: ctx.projectSlug,
|
|
29589
|
+
assignmentSlug: ctx.assignmentSlug,
|
|
29590
|
+
id,
|
|
29591
|
+
standalone: false,
|
|
29592
|
+
workspaceGroup: null
|
|
29593
|
+
};
|
|
29594
|
+
}
|
|
29595
|
+
throw new AssignmentTargetError(
|
|
29596
|
+
".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
|
|
29597
|
+
);
|
|
29598
|
+
}
|
|
29599
|
+
|
|
29600
|
+
// src/commands/derive-verbs.ts
|
|
29074
29601
|
async function resolveTarget(assignment, options) {
|
|
29075
29602
|
const config = await readConfig();
|
|
29076
29603
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
29077
29604
|
if (options.project) {
|
|
29078
29605
|
if (!isValidSlug(options.project)) throw new Error(`Invalid project slug "${options.project}".`);
|
|
29079
29606
|
if (!isValidSlug(assignment)) throw new Error(`Invalid assignment slug "${assignment}".`);
|
|
29080
|
-
const projectDir =
|
|
29081
|
-
const assignmentDir =
|
|
29082
|
-
const assignmentPath =
|
|
29607
|
+
const projectDir = resolve52(baseDir, options.project);
|
|
29608
|
+
const assignmentDir = resolve52(projectDir, "assignments", assignment);
|
|
29609
|
+
const assignmentPath = resolve52(assignmentDir, "assignment.md");
|
|
29083
29610
|
if (!await fileExists(assignmentPath)) {
|
|
29084
29611
|
throw new Error(`Assignment "${assignment}" not found at ${assignmentPath}.`);
|
|
29085
29612
|
}
|
|
@@ -29091,14 +29618,14 @@ async function resolveTarget(assignment, options) {
|
|
|
29091
29618
|
}
|
|
29092
29619
|
return {
|
|
29093
29620
|
assignmentDir: resolved.assignmentDir,
|
|
29094
|
-
assignmentPath:
|
|
29095
|
-
projectDir: resolved.standalone ? null :
|
|
29621
|
+
assignmentPath: resolve52(resolved.assignmentDir, "assignment.md"),
|
|
29622
|
+
projectDir: resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..")
|
|
29096
29623
|
};
|
|
29097
29624
|
}
|
|
29098
29625
|
async function inferActor(options) {
|
|
29099
29626
|
if (options.agent) return `agent:${options.agent}`;
|
|
29100
29627
|
try {
|
|
29101
|
-
const raw2 = await
|
|
29628
|
+
const raw2 = await readFile34(resolve52(process.cwd(), ".syntaur", "context.json"), "utf-8");
|
|
29102
29629
|
const ctx = JSON.parse(raw2);
|
|
29103
29630
|
if (ctx.sessionId) return `agent:${ctx.sessionId.slice(0, 8)}`;
|
|
29104
29631
|
} catch {
|
|
@@ -29107,10 +29634,10 @@ async function inferActor(options) {
|
|
|
29107
29634
|
}
|
|
29108
29635
|
async function emitDeriveEvent(target, type, actor, details) {
|
|
29109
29636
|
try {
|
|
29110
|
-
const fm = parseAssignmentFrontmatter(await
|
|
29637
|
+
const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
|
|
29111
29638
|
emitEvent({
|
|
29112
29639
|
assignmentId: fm.id,
|
|
29113
|
-
projectSlug: target.projectDir ?
|
|
29640
|
+
projectSlug: target.projectDir ? basename7(target.projectDir) : null,
|
|
29114
29641
|
type,
|
|
29115
29642
|
actor,
|
|
29116
29643
|
details
|
|
@@ -29168,7 +29695,7 @@ async function planApproveCommand(assignment, options) {
|
|
|
29168
29695
|
throw new Error("No plan file found (plan.md / plan-v*.md). Write a plan before approving.");
|
|
29169
29696
|
}
|
|
29170
29697
|
approvedFile = planFile;
|
|
29171
|
-
const planContent = await
|
|
29698
|
+
const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
|
|
29172
29699
|
return updatePlanApproval(content, {
|
|
29173
29700
|
file: planFile,
|
|
29174
29701
|
digest: planDigest(planContent),
|
|
@@ -29241,7 +29768,7 @@ async function attestCommand(assignment, fact, options) {
|
|
|
29241
29768
|
"No plan file found (plan.md / plan-v*.md). Write a plan before attesting a binds:plan fact."
|
|
29242
29769
|
);
|
|
29243
29770
|
}
|
|
29244
|
-
const planContent = await
|
|
29771
|
+
const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
|
|
29245
29772
|
record.file = planFile;
|
|
29246
29773
|
record.digest = planDigest(planContent);
|
|
29247
29774
|
} else if (binds === "commit") {
|
|
@@ -29291,6 +29818,15 @@ async function requestReviewCommand(assignment, options) {
|
|
|
29291
29818
|
);
|
|
29292
29819
|
}
|
|
29293
29820
|
async function implementStartedCommand(assignment, options) {
|
|
29821
|
+
const target = await resolveTarget(assignment, options);
|
|
29822
|
+
const context = await resolveDeriveContext();
|
|
29823
|
+
const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
|
|
29824
|
+
if (fm.dependsOn.length > 0 && target.projectDir) {
|
|
29825
|
+
const dep = await checkDependencies(target.projectDir, fm.dependsOn, context.terminalStatuses);
|
|
29826
|
+
if (!dep.satisfied) {
|
|
29827
|
+
console.warn(`Warning: starting with unmet dependencies: ${dep.unmet.join(", ")}`);
|
|
29828
|
+
}
|
|
29829
|
+
}
|
|
29294
29830
|
await assertFact(
|
|
29295
29831
|
assignment,
|
|
29296
29832
|
options,
|
|
@@ -29298,14 +29834,15 @@ async function implementStartedCommand(assignment, options) {
|
|
|
29298
29834
|
(content) => {
|
|
29299
29835
|
let next = updateAssignmentFile(content, { implementationStarted: true });
|
|
29300
29836
|
if (options.agent) {
|
|
29301
|
-
const
|
|
29302
|
-
if (
|
|
29837
|
+
const innerFm = parseAssignmentFrontmatter(next);
|
|
29838
|
+
if (innerFm.assignee === null) {
|
|
29303
29839
|
next = updateAssignmentFile(next, { assignee: options.agent });
|
|
29304
29840
|
}
|
|
29305
29841
|
}
|
|
29306
29842
|
return next;
|
|
29307
29843
|
},
|
|
29308
|
-
"Implementation started"
|
|
29844
|
+
"Implementation started",
|
|
29845
|
+
{ context }
|
|
29309
29846
|
);
|
|
29310
29847
|
}
|
|
29311
29848
|
async function blockFactCommand(assignment, options) {
|
|
@@ -29353,6 +29890,7 @@ async function statusUnpinCommand(assignment, options) {
|
|
|
29353
29890
|
await assertFact(assignment, options, "unpin", (content) => updateOverride(content, null), "Unpinned");
|
|
29354
29891
|
}
|
|
29355
29892
|
async function recomputeCommand(assignment, options) {
|
|
29893
|
+
if (options.ifMigrated && !await isDeriveMigrated()) return;
|
|
29356
29894
|
const context = await resolveDeriveContext();
|
|
29357
29895
|
if (options.all) {
|
|
29358
29896
|
const { recomputeAll: recomputeAll2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
|
|
@@ -29368,12 +29906,15 @@ async function recomputeCommand(assignment, options) {
|
|
|
29368
29906
|
for (const w of summary.warnings) console.warn(`Warning: ${w}`);
|
|
29369
29907
|
return;
|
|
29370
29908
|
}
|
|
29371
|
-
|
|
29372
|
-
|
|
29373
|
-
|
|
29909
|
+
const resolved = await resolveAssignmentTarget(assignment, {
|
|
29910
|
+
project: options.project,
|
|
29911
|
+
dir: options.dir
|
|
29912
|
+
});
|
|
29913
|
+
const projectDir = resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..");
|
|
29914
|
+
const result = await recomputeAndWrite(resolve52(resolved.assignmentDir, "assignment.md"), {
|
|
29374
29915
|
cause: "recompute",
|
|
29375
29916
|
by: await inferActor(options),
|
|
29376
|
-
projectDir
|
|
29917
|
+
projectDir,
|
|
29377
29918
|
context
|
|
29378
29919
|
});
|
|
29379
29920
|
reportDerived(result.changed ? "Recomputed" : "Recomputed (no change)", result);
|
|
@@ -29393,8 +29934,8 @@ init_frontmatter();
|
|
|
29393
29934
|
init_timestamp();
|
|
29394
29935
|
init_assignment_resolver();
|
|
29395
29936
|
init_event_emit();
|
|
29396
|
-
import { resolve as
|
|
29397
|
-
import { readFile as
|
|
29937
|
+
import { resolve as resolve53 } from "path";
|
|
29938
|
+
import { readFile as readFile35, writeFile as writeFile9 } from "fs/promises";
|
|
29398
29939
|
async function resolveTarget2(target, options) {
|
|
29399
29940
|
const config = await readConfig();
|
|
29400
29941
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
@@ -29405,7 +29946,7 @@ async function resolveTarget2(target, options) {
|
|
|
29405
29946
|
if (!isValidSlug(target)) {
|
|
29406
29947
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
29407
29948
|
}
|
|
29408
|
-
const assignmentMd =
|
|
29949
|
+
const assignmentMd = resolve53(baseDir, options.project, "assignments", target, "assignment.md");
|
|
29409
29950
|
if (!await fileExists(assignmentMd)) {
|
|
29410
29951
|
throw new Error(`Assignment "${target}" not found in project "${options.project}".`);
|
|
29411
29952
|
}
|
|
@@ -29415,18 +29956,18 @@ async function resolveTarget2(target, options) {
|
|
|
29415
29956
|
if (resolved) {
|
|
29416
29957
|
return {
|
|
29417
29958
|
kind: "assignment",
|
|
29418
|
-
filePath:
|
|
29959
|
+
filePath: resolve53(resolved.assignmentDir, "assignment.md"),
|
|
29419
29960
|
label: resolved.projectSlug ? `assignment "${resolved.projectSlug}/${resolved.assignmentSlug}"` : `assignment "${target}"`
|
|
29420
29961
|
};
|
|
29421
29962
|
}
|
|
29422
|
-
const projectMd =
|
|
29963
|
+
const projectMd = resolve53(baseDir, target, "project.md");
|
|
29423
29964
|
if (await fileExists(projectMd)) {
|
|
29424
29965
|
return { kind: "project", filePath: projectMd, label: `project "${target}"` };
|
|
29425
29966
|
}
|
|
29426
29967
|
return null;
|
|
29427
29968
|
}
|
|
29428
29969
|
async function writeArchiveState(filePath, archived, reason) {
|
|
29429
|
-
const content = await
|
|
29970
|
+
const content = await readFile35(filePath, "utf-8");
|
|
29430
29971
|
const updated = updateAssignmentFile(content, {
|
|
29431
29972
|
archived,
|
|
29432
29973
|
archivedAt: archived ? nowTimestamp() : null,
|
|
@@ -29438,7 +29979,7 @@ async function writeArchiveState(filePath, archived, reason) {
|
|
|
29438
29979
|
async function emitArchiveEvent(resolved, type, reason) {
|
|
29439
29980
|
if (resolved.kind !== "assignment") return;
|
|
29440
29981
|
try {
|
|
29441
|
-
const fm = parseAssignmentFrontmatter(await
|
|
29982
|
+
const fm = parseAssignmentFrontmatter(await readFile35(resolved.filePath, "utf-8"));
|
|
29442
29983
|
emitEvent({
|
|
29443
29984
|
assignmentId: fm.id,
|
|
29444
29985
|
projectSlug: fm.project,
|
|
@@ -29508,8 +30049,8 @@ init_config2();
|
|
|
29508
30049
|
init_frontmatter();
|
|
29509
30050
|
init_timestamp();
|
|
29510
30051
|
init_event_emit();
|
|
29511
|
-
import { resolve as
|
|
29512
|
-
import { readdir as readdir20, readFile as
|
|
30052
|
+
import { resolve as resolve54 } from "path";
|
|
30053
|
+
import { readdir as readdir20, readFile as readFile36 } from "fs/promises";
|
|
29513
30054
|
var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
29514
30055
|
function objectiveIsFleshedOut(content) {
|
|
29515
30056
|
const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
|
|
@@ -29531,11 +30072,11 @@ async function collectCandidates(baseDirs) {
|
|
|
29531
30072
|
for (const m of projects) {
|
|
29532
30073
|
if (!m.isDirectory()) continue;
|
|
29533
30074
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29534
|
-
const directAssignmentMd =
|
|
30075
|
+
const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
|
|
29535
30076
|
if (await fileExists(directAssignmentMd)) {
|
|
29536
30077
|
const fm = await parseSafe(directAssignmentMd);
|
|
29537
30078
|
if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
|
|
29538
|
-
const content = await
|
|
30079
|
+
const content = await readFile36(directAssignmentMd, "utf-8");
|
|
29539
30080
|
if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
|
|
29540
30081
|
candidates.push({
|
|
29541
30082
|
projectSlug: null,
|
|
@@ -29548,17 +30089,17 @@ async function collectCandidates(baseDirs) {
|
|
|
29548
30089
|
}
|
|
29549
30090
|
continue;
|
|
29550
30091
|
}
|
|
29551
|
-
const assignmentsDir2 =
|
|
30092
|
+
const assignmentsDir2 = resolve54(baseDir, m.name, "assignments");
|
|
29552
30093
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
29553
30094
|
const entries = await readdir20(assignmentsDir2, { withFileTypes: true });
|
|
29554
30095
|
for (const a of entries) {
|
|
29555
30096
|
if (!a.isDirectory()) continue;
|
|
29556
30097
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29557
|
-
const assignmentMd =
|
|
30098
|
+
const assignmentMd = resolve54(assignmentsDir2, a.name, "assignment.md");
|
|
29558
30099
|
if (!await fileExists(assignmentMd)) continue;
|
|
29559
30100
|
const fm = await parseSafe(assignmentMd);
|
|
29560
30101
|
if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
|
|
29561
|
-
const content = await
|
|
30102
|
+
const content = await readFile36(assignmentMd, "utf-8");
|
|
29562
30103
|
if (!objectiveIsFleshedOut(content)) continue;
|
|
29563
30104
|
if (!hasAcceptanceCriteria(content)) continue;
|
|
29564
30105
|
candidates.push({
|
|
@@ -29575,7 +30116,7 @@ async function collectCandidates(baseDirs) {
|
|
|
29575
30116
|
}
|
|
29576
30117
|
async function parseSafe(path) {
|
|
29577
30118
|
try {
|
|
29578
|
-
const content = await
|
|
30119
|
+
const content = await readFile36(path, "utf-8");
|
|
29579
30120
|
return parseAssignmentFrontmatter(content);
|
|
29580
30121
|
} catch {
|
|
29581
30122
|
return null;
|
|
@@ -29605,7 +30146,7 @@ async function migrateStatusesCommand(options) {
|
|
|
29605
30146
|
let migrated = 0;
|
|
29606
30147
|
await withSuppressedEvents(async () => {
|
|
29607
30148
|
for (const c2 of candidates) {
|
|
29608
|
-
const content = await
|
|
30149
|
+
const content = await readFile36(c2.assignmentMd, "utf-8");
|
|
29609
30150
|
const updated = appendStatusHistoryEntry(
|
|
29610
30151
|
updateAssignmentFile(content, {
|
|
29611
30152
|
status: c2.toStatus,
|
|
@@ -29627,11 +30168,11 @@ init_config2();
|
|
|
29627
30168
|
init_frontmatter();
|
|
29628
30169
|
init_event_emit();
|
|
29629
30170
|
init_types();
|
|
29630
|
-
import { resolve as
|
|
29631
|
-
import { readdir as readdir21, readFile as
|
|
30171
|
+
import { resolve as resolve55 } from "path";
|
|
30172
|
+
import { readdir as readdir21, readFile as readFile37 } from "fs/promises";
|
|
29632
30173
|
async function parseSafe2(path) {
|
|
29633
30174
|
try {
|
|
29634
|
-
return parseAssignmentFrontmatter(await
|
|
30175
|
+
return parseAssignmentFrontmatter(await readFile37(path, "utf-8"));
|
|
29635
30176
|
} catch {
|
|
29636
30177
|
return null;
|
|
29637
30178
|
}
|
|
@@ -29658,7 +30199,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
|
|
|
29658
30199
|
for (const m of entries) {
|
|
29659
30200
|
if (!m.isDirectory()) continue;
|
|
29660
30201
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29661
|
-
const directAssignmentMd =
|
|
30202
|
+
const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
|
|
29662
30203
|
if (await fileExists(directAssignmentMd)) {
|
|
29663
30204
|
if (seen.has(directAssignmentMd)) continue;
|
|
29664
30205
|
const fm = await parseSafe2(directAssignmentMd);
|
|
@@ -29673,13 +30214,13 @@ async function collectTargets(baseDirs, terminalStatuses3) {
|
|
|
29673
30214
|
}
|
|
29674
30215
|
continue;
|
|
29675
30216
|
}
|
|
29676
|
-
const assignmentsBase =
|
|
30217
|
+
const assignmentsBase = resolve55(baseDir, m.name, "assignments");
|
|
29677
30218
|
if (!await fileExists(assignmentsBase)) continue;
|
|
29678
30219
|
const slugs = await readdir21(assignmentsBase, { withFileTypes: true });
|
|
29679
30220
|
for (const a of slugs) {
|
|
29680
30221
|
if (!a.isDirectory()) continue;
|
|
29681
30222
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29682
|
-
const assignmentMd =
|
|
30223
|
+
const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
|
|
29683
30224
|
if (!await fileExists(assignmentMd)) continue;
|
|
29684
30225
|
if (seen.has(assignmentMd)) continue;
|
|
29685
30226
|
const fm = await parseSafe2(assignmentMd);
|
|
@@ -29723,7 +30264,7 @@ async function migrateStatusHistoryCommand(options) {
|
|
|
29723
30264
|
await withSuppressedEvents(async () => {
|
|
29724
30265
|
for (const t of targets) {
|
|
29725
30266
|
try {
|
|
29726
|
-
const content = await
|
|
30267
|
+
const content = await readFile37(t.assignmentMd, "utf-8");
|
|
29727
30268
|
if (parseAssignmentFrontmatter(content).statusHistory.length > 0) continue;
|
|
29728
30269
|
const seededContent = appendStatusHistoryEntry(content, {
|
|
29729
30270
|
at: t.seedAt,
|
|
@@ -29751,11 +30292,11 @@ init_fs();
|
|
|
29751
30292
|
init_config2();
|
|
29752
30293
|
init_parser();
|
|
29753
30294
|
init_events_db();
|
|
29754
|
-
import { resolve as
|
|
29755
|
-
import { readdir as readdir22, readFile as
|
|
30295
|
+
import { resolve as resolve56 } from "path";
|
|
30296
|
+
import { readdir as readdir22, readFile as readFile38 } from "fs/promises";
|
|
29756
30297
|
async function parseSafe3(path) {
|
|
29757
30298
|
try {
|
|
29758
|
-
return parseAssignmentFull(await
|
|
30299
|
+
return parseAssignmentFull(await readFile38(path, "utf-8"));
|
|
29759
30300
|
} catch {
|
|
29760
30301
|
return null;
|
|
29761
30302
|
}
|
|
@@ -29792,7 +30333,7 @@ async function collectTargets2(baseDirs) {
|
|
|
29792
30333
|
for (const m of entries) {
|
|
29793
30334
|
if (!m.isDirectory()) continue;
|
|
29794
30335
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29795
|
-
const directAssignmentMd =
|
|
30336
|
+
const directAssignmentMd = resolve56(baseDir, m.name, "assignment.md");
|
|
29796
30337
|
if (await fileExists(directAssignmentMd)) {
|
|
29797
30338
|
if (seen.has(directAssignmentMd)) continue;
|
|
29798
30339
|
seen.add(directAssignmentMd);
|
|
@@ -29810,13 +30351,13 @@ async function collectTargets2(baseDirs) {
|
|
|
29810
30351
|
}
|
|
29811
30352
|
continue;
|
|
29812
30353
|
}
|
|
29813
|
-
const assignmentsBase =
|
|
30354
|
+
const assignmentsBase = resolve56(baseDir, m.name, "assignments");
|
|
29814
30355
|
if (!await fileExists(assignmentsBase)) continue;
|
|
29815
30356
|
const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
|
|
29816
30357
|
for (const a of slugs) {
|
|
29817
30358
|
if (!a.isDirectory()) continue;
|
|
29818
30359
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29819
|
-
const assignmentMd =
|
|
30360
|
+
const assignmentMd = resolve56(assignmentsBase, a.name, "assignment.md");
|
|
29820
30361
|
if (!await fileExists(assignmentMd)) continue;
|
|
29821
30362
|
if (seen.has(assignmentMd)) continue;
|
|
29822
30363
|
seen.add(assignmentMd);
|
|
@@ -29897,8 +30438,8 @@ init_frontmatter();
|
|
|
29897
30438
|
init_facts();
|
|
29898
30439
|
init_derive();
|
|
29899
30440
|
init_recompute();
|
|
29900
|
-
import { readdir as readdir23, readFile as
|
|
29901
|
-
import { resolve as
|
|
30441
|
+
import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
|
|
30442
|
+
import { resolve as resolve57 } from "path";
|
|
29902
30443
|
var IMPLEMENTATION_STATUSES = /* @__PURE__ */ new Set(["in_progress", "review", "code_review"]);
|
|
29903
30444
|
var REVIEW_STATUSES = /* @__PURE__ */ new Set(["review", "code_review"]);
|
|
29904
30445
|
async function listTargets(projectsDir2, standaloneDir) {
|
|
@@ -29909,15 +30450,15 @@ async function listTargets(projectsDir2, standaloneDir) {
|
|
|
29909
30450
|
} catch {
|
|
29910
30451
|
}
|
|
29911
30452
|
for (const project of projects) {
|
|
29912
|
-
const projectDir =
|
|
30453
|
+
const projectDir = resolve57(projectsDir2, project);
|
|
29913
30454
|
let slugs = [];
|
|
29914
30455
|
try {
|
|
29915
|
-
slugs = await readdir23(
|
|
30456
|
+
slugs = await readdir23(resolve57(projectDir, "assignments"));
|
|
29916
30457
|
} catch {
|
|
29917
30458
|
continue;
|
|
29918
30459
|
}
|
|
29919
30460
|
for (const slug of slugs) {
|
|
29920
|
-
const path =
|
|
30461
|
+
const path = resolve57(projectDir, "assignments", slug, "assignment.md");
|
|
29921
30462
|
if (await fileExists(path)) targets.push({ path, projectDir, ref: `${project}/${slug}` });
|
|
29922
30463
|
}
|
|
29923
30464
|
}
|
|
@@ -29927,7 +30468,7 @@ async function listTargets(projectsDir2, standaloneDir) {
|
|
|
29927
30468
|
} catch {
|
|
29928
30469
|
}
|
|
29929
30470
|
for (const id of ids) {
|
|
29930
|
-
const path =
|
|
30471
|
+
const path = resolve57(standaloneDir, id, "assignment.md");
|
|
29931
30472
|
if (await fileExists(path)) targets.push({ path, projectDir: null, ref: id });
|
|
29932
30473
|
}
|
|
29933
30474
|
return targets;
|
|
@@ -29982,7 +30523,7 @@ async function migrateDeriveCommand(options) {
|
|
|
29982
30523
|
for (const target of targets) {
|
|
29983
30524
|
let content;
|
|
29984
30525
|
try {
|
|
29985
|
-
content = await
|
|
30526
|
+
content = await readFile39(target.path, "utf-8");
|
|
29986
30527
|
} catch {
|
|
29987
30528
|
continue;
|
|
29988
30529
|
}
|
|
@@ -29997,7 +30538,7 @@ async function migrateDeriveCommand(options) {
|
|
|
29997
30538
|
const seededFm = parseAssignmentFrontmatter(seededContent);
|
|
29998
30539
|
const body = seededContent.replace(/^---\n[\s\S]*?\n---/, "");
|
|
29999
30540
|
const facts = await computeFacts({
|
|
30000
|
-
assignmentDir:
|
|
30541
|
+
assignmentDir: resolve57(target.path, ".."),
|
|
30001
30542
|
frontmatter: seededFm,
|
|
30002
30543
|
body,
|
|
30003
30544
|
projectDir: target.projectDir,
|
|
@@ -30057,7 +30598,7 @@ async function migrateDeriveCommand(options) {
|
|
|
30057
30598
|
}
|
|
30058
30599
|
|
|
30059
30600
|
// src/commands/complete.ts
|
|
30060
|
-
import { resolve as
|
|
30601
|
+
import { resolve as resolve58 } from "path";
|
|
30061
30602
|
init_config2();
|
|
30062
30603
|
init_paths();
|
|
30063
30604
|
init_assignment_resolver();
|
|
@@ -30071,12 +30612,12 @@ async function completeCommand(assignment, options) {
|
|
|
30071
30612
|
let projectDir;
|
|
30072
30613
|
let changedSlug;
|
|
30073
30614
|
if (options.project) {
|
|
30074
|
-
projectDir =
|
|
30615
|
+
projectDir = resolve58(baseDir, options.project);
|
|
30075
30616
|
changedSlug = assignment;
|
|
30076
30617
|
} else {
|
|
30077
30618
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30078
30619
|
if (!resolved) return;
|
|
30079
|
-
projectDir = resolved.standalone ? null :
|
|
30620
|
+
projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
|
|
30080
30621
|
changedSlug = resolved.assignmentSlug;
|
|
30081
30622
|
}
|
|
30082
30623
|
if (projectDir) {
|
|
@@ -30107,7 +30648,7 @@ async function reviewCommand(assignment, options) {
|
|
|
30107
30648
|
}
|
|
30108
30649
|
|
|
30109
30650
|
// src/commands/fail.ts
|
|
30110
|
-
import { resolve as
|
|
30651
|
+
import { resolve as resolve59 } from "path";
|
|
30111
30652
|
init_config2();
|
|
30112
30653
|
init_paths();
|
|
30113
30654
|
init_assignment_resolver();
|
|
@@ -30121,12 +30662,12 @@ async function failCommand(assignment, options) {
|
|
|
30121
30662
|
let projectDir;
|
|
30122
30663
|
let changedSlug;
|
|
30123
30664
|
if (options.project) {
|
|
30124
|
-
projectDir =
|
|
30665
|
+
projectDir = resolve59(baseDir, options.project);
|
|
30125
30666
|
changedSlug = assignment;
|
|
30126
30667
|
} else {
|
|
30127
30668
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30128
30669
|
if (!resolved) return;
|
|
30129
|
-
projectDir = resolved.standalone ? null :
|
|
30670
|
+
projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
|
|
30130
30671
|
changedSlug = resolved.assignmentSlug;
|
|
30131
30672
|
}
|
|
30132
30673
|
if (projectDir) {
|
|
@@ -30142,7 +30683,7 @@ async function failCommand(assignment, options) {
|
|
|
30142
30683
|
}
|
|
30143
30684
|
|
|
30144
30685
|
// src/commands/reopen.ts
|
|
30145
|
-
import { resolve as
|
|
30686
|
+
import { resolve as resolve60 } from "path";
|
|
30146
30687
|
init_config2();
|
|
30147
30688
|
init_paths();
|
|
30148
30689
|
init_assignment_resolver();
|
|
@@ -30158,14 +30699,14 @@ async function reopenCommand(assignment, options) {
|
|
|
30158
30699
|
let projectDir;
|
|
30159
30700
|
let changedSlug;
|
|
30160
30701
|
if (options.project) {
|
|
30161
|
-
projectDir =
|
|
30162
|
-
assignmentPath =
|
|
30702
|
+
projectDir = resolve60(baseDir, options.project);
|
|
30703
|
+
assignmentPath = resolve60(projectDir, "assignments", assignment, "assignment.md");
|
|
30163
30704
|
changedSlug = assignment;
|
|
30164
30705
|
} else {
|
|
30165
30706
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30166
30707
|
if (!resolved) return;
|
|
30167
|
-
assignmentPath =
|
|
30168
|
-
projectDir = resolved.standalone ? null :
|
|
30708
|
+
assignmentPath = resolve60(resolved.assignmentDir, "assignment.md");
|
|
30709
|
+
projectDir = resolved.standalone ? null : resolve60(resolved.assignmentDir, "..", "..");
|
|
30169
30710
|
changedSlug = resolved.assignmentSlug;
|
|
30170
30711
|
}
|
|
30171
30712
|
const derived = await recomputeAndWrite(assignmentPath, {
|
|
@@ -30199,7 +30740,7 @@ import {
|
|
|
30199
30740
|
readdir as readdir24,
|
|
30200
30741
|
symlink,
|
|
30201
30742
|
lstat,
|
|
30202
|
-
readFile as
|
|
30743
|
+
readFile as readFile40,
|
|
30203
30744
|
readlink,
|
|
30204
30745
|
rename as rename9,
|
|
30205
30746
|
rm as rm6,
|
|
@@ -30208,20 +30749,20 @@ import {
|
|
|
30208
30749
|
} from "fs/promises";
|
|
30209
30750
|
import { existsSync as existsSync4 } from "fs";
|
|
30210
30751
|
import { homedir as homedir8 } from "os";
|
|
30211
|
-
import { basename as
|
|
30752
|
+
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute7, relative as relative2, resolve as resolve62 } from "path";
|
|
30212
30753
|
|
|
30213
30754
|
// src/utils/package-root.ts
|
|
30214
30755
|
init_fs();
|
|
30215
|
-
import { dirname as dirname14, resolve as
|
|
30756
|
+
import { dirname as dirname14, resolve as resolve61 } from "path";
|
|
30216
30757
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
30217
30758
|
async function findPackageRoot(expectedRelativePath) {
|
|
30218
30759
|
let currentDir = dirname14(fileURLToPath4(import.meta.url));
|
|
30219
30760
|
while (true) {
|
|
30220
|
-
const candidate =
|
|
30761
|
+
const candidate = resolve61(currentDir, expectedRelativePath);
|
|
30221
30762
|
if (await fileExists(candidate)) {
|
|
30222
30763
|
return currentDir;
|
|
30223
30764
|
}
|
|
30224
|
-
const parentDir =
|
|
30765
|
+
const parentDir = resolve61(currentDir, "..");
|
|
30225
30766
|
if (parentDir === currentDir) {
|
|
30226
30767
|
throw new Error(
|
|
30227
30768
|
`Could not locate package root containing ${expectedRelativePath}.`
|
|
@@ -30242,25 +30783,25 @@ function getPluginManifestRelativePath(pluginKind) {
|
|
|
30242
30783
|
}
|
|
30243
30784
|
function getDefaultPluginTargetDir(pluginKind) {
|
|
30244
30785
|
const home2 = homedir8();
|
|
30245
|
-
return pluginKind === "claude" ?
|
|
30786
|
+
return pluginKind === "claude" ? resolve62(home2, ".claude", "plugins", "syntaur") : resolve62(home2, "plugins", "syntaur");
|
|
30246
30787
|
}
|
|
30247
30788
|
function getDefaultMarketplacePath() {
|
|
30248
|
-
return
|
|
30789
|
+
return resolve62(homedir8(), ".agents", "plugins", "marketplace.json");
|
|
30249
30790
|
}
|
|
30250
30791
|
function getClaudeMarketplacesRoot() {
|
|
30251
|
-
return
|
|
30792
|
+
return resolve62(homedir8(), ".claude", "plugins", "marketplaces");
|
|
30252
30793
|
}
|
|
30253
30794
|
function getClaudeKnownMarketplacesPath() {
|
|
30254
|
-
return
|
|
30795
|
+
return resolve62(homedir8(), ".claude", "plugins", "known_marketplaces.json");
|
|
30255
30796
|
}
|
|
30256
30797
|
function getClaudeInstalledPluginsPath() {
|
|
30257
|
-
return
|
|
30798
|
+
return resolve62(homedir8(), ".claude", "plugins", "installed_plugins.json");
|
|
30258
30799
|
}
|
|
30259
30800
|
function getInstallMarkerPath(targetDir) {
|
|
30260
|
-
return
|
|
30801
|
+
return resolve62(targetDir, INSTALL_MARKER_FILENAME);
|
|
30261
30802
|
}
|
|
30262
30803
|
async function readPackageManifest(packageRoot) {
|
|
30263
|
-
const raw2 = await
|
|
30804
|
+
const raw2 = await readFile40(resolve62(packageRoot, "package.json"), "utf-8");
|
|
30264
30805
|
return JSON.parse(raw2);
|
|
30265
30806
|
}
|
|
30266
30807
|
async function readJsonFileIfExists(pathValue) {
|
|
@@ -30268,7 +30809,7 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
30268
30809
|
return null;
|
|
30269
30810
|
}
|
|
30270
30811
|
try {
|
|
30271
|
-
const raw2 = await
|
|
30812
|
+
const raw2 = await readFile40(pathValue, "utf-8");
|
|
30272
30813
|
return JSON.parse(raw2);
|
|
30273
30814
|
} catch {
|
|
30274
30815
|
return null;
|
|
@@ -30276,15 +30817,15 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
30276
30817
|
}
|
|
30277
30818
|
async function readClaudePluginManifest(pluginDir) {
|
|
30278
30819
|
return await readJsonFileIfExists(
|
|
30279
|
-
|
|
30820
|
+
resolve62(pluginDir, ".claude-plugin", "plugin.json")
|
|
30280
30821
|
) ?? {};
|
|
30281
30822
|
}
|
|
30282
30823
|
async function readPluginManifestName(targetDir, pluginKind) {
|
|
30283
|
-
const manifestPath =
|
|
30824
|
+
const manifestPath = resolve62(targetDir, getPluginManifestRelativePath(pluginKind));
|
|
30284
30825
|
if (!await fileExists(manifestPath)) {
|
|
30285
30826
|
return void 0;
|
|
30286
30827
|
}
|
|
30287
|
-
const raw2 = await
|
|
30828
|
+
const raw2 = await readFile40(manifestPath, "utf-8");
|
|
30288
30829
|
const parsed = JSON.parse(raw2);
|
|
30289
30830
|
return parsed.name;
|
|
30290
30831
|
}
|
|
@@ -30294,7 +30835,7 @@ async function readInstallMetadata(targetDir) {
|
|
|
30294
30835
|
return null;
|
|
30295
30836
|
}
|
|
30296
30837
|
try {
|
|
30297
|
-
const raw2 = await
|
|
30838
|
+
const raw2 = await readFile40(markerPath, "utf-8");
|
|
30298
30839
|
return JSON.parse(raw2);
|
|
30299
30840
|
} catch {
|
|
30300
30841
|
return null;
|
|
@@ -30307,7 +30848,7 @@ async function getInstallStatus(targetDir, pluginKind) {
|
|
|
30307
30848
|
const info = await lstat(targetDir);
|
|
30308
30849
|
if (info.isSymbolicLink()) {
|
|
30309
30850
|
const symlinkTarget = await readlink(targetDir);
|
|
30310
|
-
const resolvedTarget =
|
|
30851
|
+
const resolvedTarget = resolve62(dirname15(targetDir), symlinkTarget);
|
|
30311
30852
|
const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
|
|
30312
30853
|
return {
|
|
30313
30854
|
exists: true,
|
|
@@ -30353,7 +30894,7 @@ async function installLink(paths) {
|
|
|
30353
30894
|
await ensureDir(dirname15(paths.targetDir));
|
|
30354
30895
|
await rm6(paths.targetDir, { recursive: true, force: true });
|
|
30355
30896
|
await ensureDir(dirname15(paths.targetDir));
|
|
30356
|
-
await symlink(
|
|
30897
|
+
await symlink(resolve62(paths.sourceDir), paths.targetDir, "dir");
|
|
30357
30898
|
}
|
|
30358
30899
|
async function removeInstallMarker(targetDir) {
|
|
30359
30900
|
const markerPath = getInstallMarkerPath(targetDir);
|
|
@@ -30367,13 +30908,13 @@ function normalizeAbsoluteInstallPath(pathValue, label) {
|
|
|
30367
30908
|
if (!isAbsolute7(expanded)) {
|
|
30368
30909
|
throw new Error(`${label} must be an absolute path.`);
|
|
30369
30910
|
}
|
|
30370
|
-
return
|
|
30911
|
+
return resolve62(expanded);
|
|
30371
30912
|
}
|
|
30372
30913
|
async function resolvePluginPaths(pluginKind, targetDir) {
|
|
30373
30914
|
const packageRoot = await findPackageRoot(getPluginRelativePath(pluginKind));
|
|
30374
30915
|
return {
|
|
30375
30916
|
packageRoot,
|
|
30376
|
-
sourceDir:
|
|
30917
|
+
sourceDir: resolve62(packageRoot, getPluginRelativePath(pluginKind)),
|
|
30377
30918
|
targetDir: targetDir ?? getDefaultPluginTargetDir(pluginKind)
|
|
30378
30919
|
};
|
|
30379
30920
|
}
|
|
@@ -30417,7 +30958,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
|
30417
30958
|
}
|
|
30418
30959
|
if (await fileExists(manifestPath)) {
|
|
30419
30960
|
try {
|
|
30420
|
-
const prev = await
|
|
30961
|
+
const prev = await readFile40(manifestPath, "utf-8");
|
|
30421
30962
|
JSON.parse(prev);
|
|
30422
30963
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30423
30964
|
await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
@@ -30498,8 +31039,8 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
30498
31039
|
if (!entry.isDirectory()) {
|
|
30499
31040
|
continue;
|
|
30500
31041
|
}
|
|
30501
|
-
const candidateRoot =
|
|
30502
|
-
const manifestPath =
|
|
31042
|
+
const candidateRoot = resolve62(rootDir, entry.name);
|
|
31043
|
+
const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
30503
31044
|
if (!await fileExists(manifestPath)) {
|
|
30504
31045
|
continue;
|
|
30505
31046
|
}
|
|
@@ -30526,11 +31067,11 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
30526
31067
|
if (!installLocation) {
|
|
30527
31068
|
continue;
|
|
30528
31069
|
}
|
|
30529
|
-
const candidateRoot =
|
|
31070
|
+
const candidateRoot = resolve62(expandHome(installLocation));
|
|
30530
31071
|
if (seen.has(candidateRoot)) {
|
|
30531
31072
|
continue;
|
|
30532
31073
|
}
|
|
30533
|
-
const manifestPath =
|
|
31074
|
+
const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
30534
31075
|
if (!await fileExists(manifestPath)) {
|
|
30535
31076
|
continue;
|
|
30536
31077
|
}
|
|
@@ -30558,14 +31099,14 @@ async function getPreferredClaudeMarketplace() {
|
|
|
30558
31099
|
name: candidate.name,
|
|
30559
31100
|
rootDir: candidate.rootDir,
|
|
30560
31101
|
manifestPath: candidate.manifestPath,
|
|
30561
|
-
targetDir:
|
|
31102
|
+
targetDir: resolve62(candidate.rootDir, "plugins", "syntaur")
|
|
30562
31103
|
};
|
|
30563
31104
|
}
|
|
30564
31105
|
async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
30565
31106
|
const manifestPath = getClaudeKnownMarketplacesPath();
|
|
30566
31107
|
let existing = {};
|
|
30567
31108
|
if (await fileExists(manifestPath)) {
|
|
30568
|
-
const raw2 = await
|
|
31109
|
+
const raw2 = await readFile40(manifestPath, "utf-8");
|
|
30569
31110
|
try {
|
|
30570
31111
|
const parsed = JSON.parse(raw2);
|
|
30571
31112
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -30592,7 +31133,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
30592
31133
|
existing[name].autoUpdate = true;
|
|
30593
31134
|
await ensureDir(dirname15(manifestPath));
|
|
30594
31135
|
if (await fileExists(manifestPath)) {
|
|
30595
|
-
const prev = await
|
|
31136
|
+
const prev = await readFile40(manifestPath, "utf-8");
|
|
30596
31137
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30597
31138
|
await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
30598
31139
|
}
|
|
@@ -30606,11 +31147,11 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
|
|
|
30606
31147
|
return registerKnownClaudeMarketplace(options.name, options.rootDir);
|
|
30607
31148
|
}
|
|
30608
31149
|
async function setSyntaurPluginEnabled(options) {
|
|
30609
|
-
const settingsPath =
|
|
31150
|
+
const settingsPath = resolve62(homedir8(), ".claude", "settings.json");
|
|
30610
31151
|
const key = `syntaur@${options.marketplaceName}`;
|
|
30611
31152
|
let parsed = {};
|
|
30612
31153
|
if (await fileExists(settingsPath)) {
|
|
30613
|
-
const raw2 = await
|
|
31154
|
+
const raw2 = await readFile40(settingsPath, "utf-8");
|
|
30614
31155
|
try {
|
|
30615
31156
|
parsed = JSON.parse(raw2);
|
|
30616
31157
|
} catch {
|
|
@@ -30629,7 +31170,7 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
30629
31170
|
await ensureDir(dirname15(settingsPath));
|
|
30630
31171
|
if (await fileExists(settingsPath)) {
|
|
30631
31172
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30632
|
-
const prev = await
|
|
31173
|
+
const prev = await readFile40(settingsPath, "utf-8");
|
|
30633
31174
|
await writeFile10(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
|
|
30634
31175
|
}
|
|
30635
31176
|
const tmpPath = `${settingsPath}.tmp`;
|
|
@@ -30643,9 +31184,9 @@ async function ensureClaudeUserMarketplace() {
|
|
|
30643
31184
|
if (existing) {
|
|
30644
31185
|
return existing;
|
|
30645
31186
|
}
|
|
30646
|
-
const rootDir =
|
|
30647
|
-
const manifestPath =
|
|
30648
|
-
await ensureDir(
|
|
31187
|
+
const rootDir = resolve62(getClaudeMarketplacesRoot(), "user-plugins");
|
|
31188
|
+
const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
|
|
31189
|
+
await ensureDir(resolve62(rootDir, "plugins"));
|
|
30649
31190
|
if (!await fileExists(manifestPath)) {
|
|
30650
31191
|
const scaffold = {
|
|
30651
31192
|
plugins: []
|
|
@@ -30664,22 +31205,22 @@ async function ensureClaudeUserMarketplace() {
|
|
|
30664
31205
|
name: "user-plugins",
|
|
30665
31206
|
rootDir,
|
|
30666
31207
|
manifestPath,
|
|
30667
|
-
targetDir:
|
|
31208
|
+
targetDir: resolve62(rootDir, "plugins", "syntaur")
|
|
30668
31209
|
};
|
|
30669
31210
|
}
|
|
30670
31211
|
async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
30671
31212
|
const normalizedTargetDir = normalizeAbsoluteInstallPath(targetDir, "Claude plugin target");
|
|
30672
31213
|
const pluginsDir = dirname15(normalizedTargetDir);
|
|
30673
|
-
if (
|
|
31214
|
+
if (basename8(pluginsDir) !== "plugins") {
|
|
30674
31215
|
return null;
|
|
30675
31216
|
}
|
|
30676
31217
|
const rootDir = dirname15(pluginsDir);
|
|
30677
|
-
const manifestPath =
|
|
31218
|
+
const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
|
|
30678
31219
|
if (!await fileExists(manifestPath)) {
|
|
30679
31220
|
return null;
|
|
30680
31221
|
}
|
|
30681
31222
|
const marketplace = await readClaudeMarketplaceFile(manifestPath);
|
|
30682
|
-
const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name :
|
|
31223
|
+
const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name : basename8(rootDir);
|
|
30683
31224
|
return {
|
|
30684
31225
|
name,
|
|
30685
31226
|
rootDir,
|
|
@@ -30690,7 +31231,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
|
30690
31231
|
async function findManagedClaudeMarketplacePluginDir() {
|
|
30691
31232
|
const marketplaces = await listClaudeMarketplaceCandidates();
|
|
30692
31233
|
for (const marketplace of marketplaces) {
|
|
30693
|
-
const targetDir =
|
|
31234
|
+
const targetDir = resolve62(marketplace.rootDir, "plugins", "syntaur");
|
|
30694
31235
|
const status = await getInstallStatus(targetDir, "claude");
|
|
30695
31236
|
if (status.exists && status.managed) {
|
|
30696
31237
|
return targetDir;
|
|
@@ -30815,7 +31356,7 @@ async function installManagedPlugin(options) {
|
|
|
30815
31356
|
`${paths.targetDir} already exists and is not a Syntaur-managed install. Remove it manually before installing Syntaur there.`
|
|
30816
31357
|
);
|
|
30817
31358
|
}
|
|
30818
|
-
if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget ===
|
|
31359
|
+
if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget === resolve62(paths.sourceDir) && !force) {
|
|
30819
31360
|
return {
|
|
30820
31361
|
targetDir: paths.targetDir,
|
|
30821
31362
|
sourceDir: paths.sourceDir,
|
|
@@ -30867,7 +31408,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
30867
31408
|
plugins: []
|
|
30868
31409
|
};
|
|
30869
31410
|
}
|
|
30870
|
-
const raw2 = await
|
|
31411
|
+
const raw2 = await readFile40(marketplacePath, "utf-8");
|
|
30871
31412
|
const parsed = JSON.parse(raw2);
|
|
30872
31413
|
return {
|
|
30873
31414
|
name: parsed.name ?? "local",
|
|
@@ -31034,13 +31575,13 @@ async function recommendMarketplacePath() {
|
|
|
31034
31575
|
return configuredOrManaged ?? getDefaultMarketplacePath();
|
|
31035
31576
|
}
|
|
31036
31577
|
async function isSyntaurDataInstalled() {
|
|
31037
|
-
return fileExists(
|
|
31578
|
+
return fileExists(resolve62(syntaurRoot(), "config.md"));
|
|
31038
31579
|
}
|
|
31039
31580
|
async function removeSyntaurData() {
|
|
31040
31581
|
await rm6(syntaurRoot(), { recursive: true, force: true });
|
|
31041
31582
|
}
|
|
31042
31583
|
async function getConfiguredProjectDir() {
|
|
31043
|
-
if (!await fileExists(
|
|
31584
|
+
if (!await fileExists(resolve62(syntaurRoot(), "config.md"))) {
|
|
31044
31585
|
return null;
|
|
31045
31586
|
}
|
|
31046
31587
|
return (await readConfig()).defaultProjectDir;
|
|
@@ -31109,24 +31650,24 @@ async function textPrompt(question, defaultValue) {
|
|
|
31109
31650
|
|
|
31110
31651
|
// src/utils/install-skills.ts
|
|
31111
31652
|
init_fs();
|
|
31112
|
-
import { readFile as
|
|
31113
|
-
import { resolve as
|
|
31653
|
+
import { readFile as readFile42, readdir as readdir25, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
|
|
31654
|
+
import { resolve as resolve64, relative as relative3, join as join11 } from "path";
|
|
31114
31655
|
import { homedir as homedir10 } from "os";
|
|
31115
31656
|
|
|
31116
31657
|
// src/utils/plugin-state.ts
|
|
31117
31658
|
init_fs();
|
|
31118
|
-
import { readFile as
|
|
31119
|
-
import { resolve as
|
|
31659
|
+
import { readFile as readFile41 } from "fs/promises";
|
|
31660
|
+
import { resolve as resolve63 } from "path";
|
|
31120
31661
|
import { homedir as homedir9 } from "os";
|
|
31121
31662
|
function settingsPathFor(agent) {
|
|
31122
|
-
if (agent === "claude") return
|
|
31663
|
+
if (agent === "claude") return resolve63(homedir9(), ".claude", "settings.json");
|
|
31123
31664
|
return null;
|
|
31124
31665
|
}
|
|
31125
31666
|
async function readJsonOrNull(path) {
|
|
31126
31667
|
if (!path) return null;
|
|
31127
31668
|
if (!await fileExists(path)) return null;
|
|
31128
31669
|
try {
|
|
31129
|
-
const raw2 = await
|
|
31670
|
+
const raw2 = await readFile41(path, "utf-8");
|
|
31130
31671
|
return JSON.parse(raw2);
|
|
31131
31672
|
} catch {
|
|
31132
31673
|
return null;
|
|
@@ -31176,11 +31717,11 @@ var KNOWN_SKILL_NAMES = [
|
|
|
31176
31717
|
var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
|
|
31177
31718
|
async function getSkillsDir() {
|
|
31178
31719
|
const packageRoot = await findPackageRoot("skills");
|
|
31179
|
-
return
|
|
31720
|
+
return resolve64(packageRoot, "skills");
|
|
31180
31721
|
}
|
|
31181
31722
|
function defaultSkillTargetDir(target) {
|
|
31182
|
-
if (target === "claude") return
|
|
31183
|
-
return
|
|
31723
|
+
if (target === "claude") return resolve64(homedir10(), ".claude", "skills");
|
|
31724
|
+
return resolve64(homedir10(), ".codex", "skills");
|
|
31184
31725
|
}
|
|
31185
31726
|
async function walkFiles(root) {
|
|
31186
31727
|
const out = [];
|
|
@@ -31200,7 +31741,7 @@ async function walkFiles(root) {
|
|
|
31200
31741
|
}
|
|
31201
31742
|
async function filesEqual(a, b) {
|
|
31202
31743
|
try {
|
|
31203
|
-
const [ba, bb] = await Promise.all([
|
|
31744
|
+
const [ba, bb] = await Promise.all([readFile42(a), readFile42(b)]);
|
|
31204
31745
|
if (ba.length !== bb.length) return false;
|
|
31205
31746
|
return ba.equals(bb);
|
|
31206
31747
|
} catch {
|
|
@@ -31234,8 +31775,8 @@ async function skillMatches(srcDir, destDir) {
|
|
|
31234
31775
|
}
|
|
31235
31776
|
async function isSymlink(path) {
|
|
31236
31777
|
try {
|
|
31237
|
-
const
|
|
31238
|
-
return
|
|
31778
|
+
const stat17 = await lstat2(path);
|
|
31779
|
+
return stat17.isSymbolicLink();
|
|
31239
31780
|
} catch {
|
|
31240
31781
|
return false;
|
|
31241
31782
|
}
|
|
@@ -31340,7 +31881,7 @@ async function uninstallSkills(options) {
|
|
|
31340
31881
|
if (await isSymlink(destDir)) continue;
|
|
31341
31882
|
const skillMd = join11(destDir, "SKILL.md");
|
|
31342
31883
|
if (!await fileExists(skillMd)) continue;
|
|
31343
|
-
const content = await
|
|
31884
|
+
const content = await readFile42(skillMd, "utf-8").catch(() => "");
|
|
31344
31885
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
31345
31886
|
if (!match || match[1] !== skill) continue;
|
|
31346
31887
|
await rm7(destDir, { recursive: true, force: true });
|
|
@@ -31535,20 +32076,20 @@ import { realpathSync as realpathSync2 } from "fs";
|
|
|
31535
32076
|
init_paths();
|
|
31536
32077
|
init_fs();
|
|
31537
32078
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
31538
|
-
import { readFile as
|
|
31539
|
-
import { dirname as dirname17, join as join13, resolve as
|
|
32079
|
+
import { readFile as readFile44 } from "fs/promises";
|
|
32080
|
+
import { dirname as dirname17, join as join13, resolve as resolve65 } from "path";
|
|
31540
32081
|
import { spawn as spawn6 } from "child_process";
|
|
31541
32082
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
31542
32083
|
|
|
31543
32084
|
// src/utils/version.ts
|
|
31544
32085
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
31545
|
-
import { readFile as
|
|
32086
|
+
import { readFile as readFile43 } from "fs/promises";
|
|
31546
32087
|
import { dirname as dirname16, join as join12 } from "path";
|
|
31547
32088
|
async function readPackageVersion(scriptUrl) {
|
|
31548
32089
|
try {
|
|
31549
32090
|
const scriptPath = fileURLToPath5(scriptUrl);
|
|
31550
32091
|
const pkgRoot = dirname16(dirname16(scriptPath));
|
|
31551
|
-
const raw2 = await
|
|
32092
|
+
const raw2 = await readFile43(join12(pkgRoot, "package.json"), "utf-8");
|
|
31552
32093
|
const parsed = JSON.parse(raw2);
|
|
31553
32094
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
31554
32095
|
} catch {
|
|
@@ -31557,7 +32098,7 @@ async function readPackageVersion(scriptUrl) {
|
|
|
31557
32098
|
}
|
|
31558
32099
|
|
|
31559
32100
|
// src/utils/npx-prompt.ts
|
|
31560
|
-
var STATE_FILE =
|
|
32101
|
+
var STATE_FILE = resolve65(syntaurRoot(), "npx-install.json");
|
|
31561
32102
|
var META_ARGS2 = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
|
|
31562
32103
|
var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
|
|
31563
32104
|
function isRunningViaNpx(scriptUrl) {
|
|
@@ -31578,7 +32119,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
31578
32119
|
async function readState() {
|
|
31579
32120
|
if (!await fileExists(STATE_FILE)) return null;
|
|
31580
32121
|
try {
|
|
31581
|
-
const raw2 = await
|
|
32122
|
+
const raw2 = await readFile44(STATE_FILE, "utf-8");
|
|
31582
32123
|
return JSON.parse(raw2);
|
|
31583
32124
|
} catch {
|
|
31584
32125
|
return null;
|
|
@@ -31637,7 +32178,7 @@ async function readGlobalVersion() {
|
|
|
31637
32178
|
try {
|
|
31638
32179
|
const manifestPath = join13(rootPath, "syntaur", "package.json");
|
|
31639
32180
|
if (!await fileExists(manifestPath)) return null;
|
|
31640
|
-
const raw2 = await
|
|
32181
|
+
const raw2 = await readFile44(manifestPath, "utf-8");
|
|
31641
32182
|
const parsed = JSON.parse(raw2);
|
|
31642
32183
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
31643
32184
|
} catch {
|
|
@@ -31995,16 +32536,16 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
|
|
|
31995
32536
|
// src/commands/install-statusline.ts
|
|
31996
32537
|
init_paths();
|
|
31997
32538
|
init_fs();
|
|
31998
|
-
import { readFile as
|
|
31999
|
-
import { resolve as
|
|
32539
|
+
import { readFile as readFile46, writeFile as writeFile12, copyFile as copyFile2, rm as rm8, stat as stat8, symlink as symlink2, unlink as unlink11, lstat as lstat3 } from "fs/promises";
|
|
32540
|
+
import { resolve as resolve67, dirname as dirname19 } from "path";
|
|
32000
32541
|
import { homedir as homedir12 } from "os";
|
|
32001
32542
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
32002
32543
|
|
|
32003
32544
|
// src/commands/configure-statusline.ts
|
|
32004
32545
|
init_paths();
|
|
32005
32546
|
init_fs();
|
|
32006
|
-
import { readFile as
|
|
32007
|
-
import { resolve as
|
|
32547
|
+
import { readFile as readFile45, writeFile as writeFile11 } from "fs/promises";
|
|
32548
|
+
import { resolve as resolve66, dirname as dirname18 } from "path";
|
|
32008
32549
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
32009
32550
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
32010
32551
|
var AVAILABLE_SEGMENTS = [
|
|
@@ -32025,12 +32566,12 @@ var PRESETS = {
|
|
|
32025
32566
|
tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
|
|
32026
32567
|
};
|
|
32027
32568
|
function getConfigPath(installRoot) {
|
|
32028
|
-
return
|
|
32569
|
+
return resolve66(installRoot, "statusline.config.json");
|
|
32029
32570
|
}
|
|
32030
32571
|
async function readConfig2(path) {
|
|
32031
32572
|
if (!await fileExists(path)) return null;
|
|
32032
32573
|
try {
|
|
32033
|
-
const raw2 = await
|
|
32574
|
+
const raw2 = await readFile45(path, "utf-8");
|
|
32034
32575
|
const parsed = JSON.parse(raw2);
|
|
32035
32576
|
if (!parsed || typeof parsed !== "object") return null;
|
|
32036
32577
|
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
@@ -32183,7 +32724,7 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
32183
32724
|
console.log(` segments: ${config.segments.join(", ")}`);
|
|
32184
32725
|
console.log(` separator: ${JSON.stringify(config.separator)}`);
|
|
32185
32726
|
if (config.wrap) console.log(` wrap: ${config.wrap}`);
|
|
32186
|
-
const script = options.statuslineScript ??
|
|
32727
|
+
const script = options.statuslineScript ?? resolve66(installRoot, "statusline.sh");
|
|
32187
32728
|
if (await fileExists(script)) {
|
|
32188
32729
|
console.log("");
|
|
32189
32730
|
console.log("Live preview:");
|
|
@@ -32214,11 +32755,11 @@ async function writeDefaultConfigIfMissing(installRoot) {
|
|
|
32214
32755
|
// src/commands/install-statusline.ts
|
|
32215
32756
|
function getPackageStatuslineSource() {
|
|
32216
32757
|
const here = dirname19(fileURLToPath8(import.meta.url));
|
|
32217
|
-
return
|
|
32758
|
+
return resolve67(here, "..", "statusline", "statusline.sh");
|
|
32218
32759
|
}
|
|
32219
32760
|
async function readSettingsJson(settingsPath) {
|
|
32220
32761
|
if (!await fileExists(settingsPath)) return {};
|
|
32221
|
-
const raw2 = await
|
|
32762
|
+
const raw2 = await readFile46(settingsPath, "utf-8");
|
|
32222
32763
|
if (raw2.trim() === "") return {};
|
|
32223
32764
|
try {
|
|
32224
32765
|
const parsed = JSON.parse(raw2);
|
|
@@ -32298,12 +32839,12 @@ async function installScript(sourceScript, destScript, link) {
|
|
|
32298
32839
|
}
|
|
32299
32840
|
async function installStatuslineCommand(options = {}) {
|
|
32300
32841
|
const mode = options.mode ?? "ask";
|
|
32301
|
-
const settingsPath = options.settingsPath ??
|
|
32842
|
+
const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
|
|
32302
32843
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
32303
32844
|
const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
|
|
32304
|
-
const destScript =
|
|
32305
|
-
const confPath =
|
|
32306
|
-
const backupPath =
|
|
32845
|
+
const destScript = resolve67(installRoot, "statusline.sh");
|
|
32846
|
+
const confPath = resolve67(installRoot, "statusline.conf");
|
|
32847
|
+
const backupPath = resolve67(installRoot, "statusline.backup.json");
|
|
32307
32848
|
if (!await fileExists(sourceScript)) {
|
|
32308
32849
|
throw new Error(
|
|
32309
32850
|
`Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
|
|
@@ -32339,7 +32880,7 @@ async function installStatuslineCommand(options = {}) {
|
|
|
32339
32880
|
if (parsed) {
|
|
32340
32881
|
wrapTarget = parsed;
|
|
32341
32882
|
} else {
|
|
32342
|
-
const wrapperPath =
|
|
32883
|
+
const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
|
|
32343
32884
|
const wrapperBody = `#!/usr/bin/env bash
|
|
32344
32885
|
# Auto-generated by syntaur install-statusline.
|
|
32345
32886
|
# Executes the previously configured statusLine command.
|
|
@@ -32388,25 +32929,25 @@ function parseWrapCommand(command) {
|
|
|
32388
32929
|
async function chmodExec(path) {
|
|
32389
32930
|
const fs = await import("fs/promises");
|
|
32390
32931
|
try {
|
|
32391
|
-
const s = await
|
|
32932
|
+
const s = await stat8(path);
|
|
32392
32933
|
await fs.chmod(path, s.mode | 73);
|
|
32393
32934
|
} catch {
|
|
32394
32935
|
}
|
|
32395
32936
|
}
|
|
32396
32937
|
async function uninstallStatuslineCommand(options = {}) {
|
|
32397
|
-
const settingsPath = options.settingsPath ??
|
|
32938
|
+
const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
|
|
32398
32939
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
32399
|
-
const destScript =
|
|
32400
|
-
const confPath =
|
|
32401
|
-
const backupPath =
|
|
32402
|
-
const wrapperPath =
|
|
32940
|
+
const destScript = resolve67(installRoot, "statusline.sh");
|
|
32941
|
+
const confPath = resolve67(installRoot, "statusline.conf");
|
|
32942
|
+
const backupPath = resolve67(installRoot, "statusline.backup.json");
|
|
32943
|
+
const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
|
|
32403
32944
|
const settings = await readSettingsJson(settingsPath);
|
|
32404
32945
|
const existing = extractExistingCommand(settings);
|
|
32405
32946
|
const ourCommand = `bash ${destScript}`;
|
|
32406
32947
|
let restored = null;
|
|
32407
32948
|
if (await fileExists(backupPath)) {
|
|
32408
32949
|
try {
|
|
32409
|
-
const raw2 = await
|
|
32950
|
+
const raw2 = await readFile46(backupPath, "utf-8");
|
|
32410
32951
|
const parsed = JSON.parse(raw2);
|
|
32411
32952
|
const prev = parsed?.previousStatusLine;
|
|
32412
32953
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -32427,7 +32968,7 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
32427
32968
|
await writeSettingsJson(settingsPath, settings);
|
|
32428
32969
|
}
|
|
32429
32970
|
if (!options.keepScript) {
|
|
32430
|
-
const configPath2 =
|
|
32971
|
+
const configPath2 = resolve67(installRoot, "statusline.config.json");
|
|
32431
32972
|
for (const path of [destScript, confPath, backupPath, wrapperPath, configPath2]) {
|
|
32432
32973
|
try {
|
|
32433
32974
|
await rm8(path, { force: true });
|
|
@@ -32587,8 +33128,8 @@ init_config2();
|
|
|
32587
33128
|
// src/commands/cross-agent-install.ts
|
|
32588
33129
|
init_fs();
|
|
32589
33130
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
32590
|
-
import { dirname as dirname20, join as join15, resolve as
|
|
32591
|
-
import { cp as cp4, mkdir as mkdir9, readFile as
|
|
33131
|
+
import { dirname as dirname20, join as join15, resolve as resolve69 } from "path";
|
|
33132
|
+
import { cp as cp4, mkdir as mkdir9, readFile as readFile47 } from "fs/promises";
|
|
32592
33133
|
|
|
32593
33134
|
// src/commands/setup-adapter.ts
|
|
32594
33135
|
init_paths();
|
|
@@ -32597,7 +33138,7 @@ init_config2();
|
|
|
32597
33138
|
init_slug();
|
|
32598
33139
|
init_registry();
|
|
32599
33140
|
init_renderers();
|
|
32600
|
-
import { resolve as
|
|
33141
|
+
import { resolve as resolve68 } from "path";
|
|
32601
33142
|
async function setupAdapterCommand(framework, options) {
|
|
32602
33143
|
const { targets: known, warnings } = await resolveAgentTargets();
|
|
32603
33144
|
const target = known.find((t) => t.id === framework);
|
|
@@ -32626,13 +33167,13 @@ async function setupAdapterCommand(framework, options) {
|
|
|
32626
33167
|
}
|
|
32627
33168
|
const config = await readConfig();
|
|
32628
33169
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
32629
|
-
const projectDir =
|
|
32630
|
-
const assignmentDir =
|
|
32631
|
-
const projectMdPath =
|
|
33170
|
+
const projectDir = resolve68(baseDir, options.project);
|
|
33171
|
+
const assignmentDir = resolve68(projectDir, "assignments", options.assignment);
|
|
33172
|
+
const projectMdPath = resolve68(projectDir, "project.md");
|
|
32632
33173
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
32633
33174
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
32634
33175
|
}
|
|
32635
|
-
const assignmentMdPath2 =
|
|
33176
|
+
const assignmentMdPath2 = resolve68(assignmentDir, "assignment.md");
|
|
32636
33177
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
|
|
32637
33178
|
throw new Error(
|
|
32638
33179
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -32649,7 +33190,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
32649
33190
|
const upToDateFiles = [];
|
|
32650
33191
|
const skippedFiles = [];
|
|
32651
33192
|
for (const file of target.instructions.files) {
|
|
32652
|
-
const filePath =
|
|
33193
|
+
const filePath = resolve68(cwd, file.path);
|
|
32653
33194
|
const content = RENDERERS[file.renderer](rendererParams);
|
|
32654
33195
|
const status = await writeFileReport(filePath, content, {
|
|
32655
33196
|
force: options.force
|
|
@@ -32705,7 +33246,7 @@ async function installTier3Plugin(t, opts = {}) {
|
|
|
32705
33246
|
return "already-present";
|
|
32706
33247
|
}
|
|
32707
33248
|
try {
|
|
32708
|
-
const sourceDir =
|
|
33249
|
+
const sourceDir = resolve69(await findPackageRoot(plugin.source), plugin.source);
|
|
32709
33250
|
await mkdir9(dirname20(installDir), { recursive: true });
|
|
32710
33251
|
await cp4(sourceDir, installDir, { recursive: true, force: true });
|
|
32711
33252
|
console.log(`Tier 3 (${t.id}): installed ${plugin.kind} -> ${installDir}`);
|
|
@@ -32730,10 +33271,10 @@ function isNpxAvailable() {
|
|
|
32730
33271
|
}
|
|
32731
33272
|
}
|
|
32732
33273
|
async function readAssignmentContext() {
|
|
32733
|
-
const p =
|
|
33274
|
+
const p = resolve69(process.cwd(), ".syntaur", "context.json");
|
|
32734
33275
|
if (!await fileExists(p)) return null;
|
|
32735
33276
|
try {
|
|
32736
|
-
return JSON.parse(await
|
|
33277
|
+
return JSON.parse(await readFile47(p, "utf-8"));
|
|
32737
33278
|
} catch {
|
|
32738
33279
|
return null;
|
|
32739
33280
|
}
|
|
@@ -32813,7 +33354,7 @@ async function crossAgentInstallCommand(options) {
|
|
|
32813
33354
|
if (dryRun) {
|
|
32814
33355
|
for (const f of t.instructions.files) {
|
|
32815
33356
|
console.log(
|
|
32816
|
-
`${prefix}Tier 2 (${t.id}): ${
|
|
33357
|
+
`${prefix}Tier 2 (${t.id}): ${resolve69(process.cwd(), f.path)}`
|
|
32817
33358
|
);
|
|
32818
33359
|
}
|
|
32819
33360
|
continue;
|
|
@@ -32970,7 +33511,7 @@ async function setupCommand(options) {
|
|
|
32970
33511
|
}
|
|
32971
33512
|
|
|
32972
33513
|
// src/commands/uninstall.ts
|
|
32973
|
-
import { resolve as
|
|
33514
|
+
import { resolve as resolve70 } from "path";
|
|
32974
33515
|
init_paths();
|
|
32975
33516
|
function expandTargets(options) {
|
|
32976
33517
|
if (options.all) {
|
|
@@ -33050,7 +33591,7 @@ async function uninstallCommand(options) {
|
|
|
33050
33591
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
33051
33592
|
await removeSyntaurData();
|
|
33052
33593
|
console.log(`Removed ${syntaurRoot()}`);
|
|
33053
|
-
if (configuredProjectDir &&
|
|
33594
|
+
if (configuredProjectDir && resolve70(configuredProjectDir) !== resolve70(syntaurRoot(), "projects")) {
|
|
33054
33595
|
console.warn(
|
|
33055
33596
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
33056
33597
|
);
|
|
@@ -33072,8 +33613,8 @@ init_session_id();
|
|
|
33072
33613
|
init_cwd();
|
|
33073
33614
|
init_session_db();
|
|
33074
33615
|
init_agent_sessions();
|
|
33075
|
-
import { resolve as
|
|
33076
|
-
import { readFile as
|
|
33616
|
+
import { resolve as resolve71 } from "path";
|
|
33617
|
+
import { readFile as readFile48 } from "fs/promises";
|
|
33077
33618
|
async function trackSessionCommand(options, deps = {}) {
|
|
33078
33619
|
if (!options.agent) {
|
|
33079
33620
|
throw new Error("--agent <name> is required.");
|
|
@@ -33083,7 +33624,7 @@ async function trackSessionCommand(options, deps = {}) {
|
|
|
33083
33624
|
const cwd = process.cwd();
|
|
33084
33625
|
let legacyHint;
|
|
33085
33626
|
try {
|
|
33086
|
-
const raw2 = await
|
|
33627
|
+
const raw2 = await readFile48(resolve71(cwd, ".syntaur", "context.json"), "utf-8");
|
|
33087
33628
|
const parsed = JSON.parse(raw2);
|
|
33088
33629
|
if (typeof parsed.sessionId === "string") legacyHint = parsed.sessionId;
|
|
33089
33630
|
} catch {
|
|
@@ -33098,7 +33639,7 @@ async function trackSessionCommand(options, deps = {}) {
|
|
|
33098
33639
|
if (options.project) {
|
|
33099
33640
|
const config = await readConfig();
|
|
33100
33641
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
33101
|
-
const projectDir =
|
|
33642
|
+
const projectDir = resolve71(baseDir, options.project);
|
|
33102
33643
|
if (!await fileExists(projectDir)) {
|
|
33103
33644
|
throw new Error(
|
|
33104
33645
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -33231,8 +33772,8 @@ function formatInstallUrlHandlerError(err2) {
|
|
|
33231
33772
|
init_config2();
|
|
33232
33773
|
init_paths();
|
|
33233
33774
|
init_fs();
|
|
33234
|
-
import { resolve as
|
|
33235
|
-
import { readFile as
|
|
33775
|
+
import { resolve as resolve73, isAbsolute as isAbsolute8 } from "path";
|
|
33776
|
+
import { readFile as readFile49 } from "fs/promises";
|
|
33236
33777
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
33237
33778
|
async function browseCommand(options) {
|
|
33238
33779
|
const config = await readConfig();
|
|
@@ -33301,7 +33842,7 @@ async function pickAgent2(agents) {
|
|
|
33301
33842
|
return picked;
|
|
33302
33843
|
}
|
|
33303
33844
|
async function ensureWorktree(opts) {
|
|
33304
|
-
const assignmentPath =
|
|
33845
|
+
const assignmentPath = resolve73(
|
|
33305
33846
|
opts.projectsDir,
|
|
33306
33847
|
opts.projectSlug,
|
|
33307
33848
|
"assignments",
|
|
@@ -33311,7 +33852,7 @@ async function ensureWorktree(opts) {
|
|
|
33311
33852
|
if (!await fileExists(assignmentPath)) {
|
|
33312
33853
|
return void 0;
|
|
33313
33854
|
}
|
|
33314
|
-
const content = await
|
|
33855
|
+
const content = await readFile49(assignmentPath, "utf-8");
|
|
33315
33856
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
33316
33857
|
const fm = parseAssignmentFrontmatter2(content);
|
|
33317
33858
|
const { workspace } = fm;
|
|
@@ -33381,7 +33922,7 @@ async function ensureWorktree(opts) {
|
|
|
33381
33922
|
async function runCreate(opts) {
|
|
33382
33923
|
const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
33383
33924
|
const expandedWorktree = expandHome(opts.worktreePath);
|
|
33384
|
-
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree :
|
|
33925
|
+
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve73(expandedWorktree);
|
|
33385
33926
|
try {
|
|
33386
33927
|
await createWorktreeAndRecord2({
|
|
33387
33928
|
assignmentPath: opts.assignmentPath,
|
|
@@ -33410,7 +33951,7 @@ init_paths();
|
|
|
33410
33951
|
init_fs();
|
|
33411
33952
|
init_playbook();
|
|
33412
33953
|
init_playbooks();
|
|
33413
|
-
import { resolve as
|
|
33954
|
+
import { resolve as resolve74 } from "path";
|
|
33414
33955
|
async function createPlaybookCommand(name, options) {
|
|
33415
33956
|
if (!name.trim()) {
|
|
33416
33957
|
throw new Error("Playbook name cannot be empty.");
|
|
@@ -33423,7 +33964,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
33423
33964
|
}
|
|
33424
33965
|
const dir = playbooksDir();
|
|
33425
33966
|
await ensureDir(dir);
|
|
33426
|
-
const filePath =
|
|
33967
|
+
const filePath = resolve74(dir, `${slug}.md`);
|
|
33427
33968
|
if (await fileExists(filePath)) {
|
|
33428
33969
|
throw new Error(
|
|
33429
33970
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -33445,8 +33986,8 @@ init_paths();
|
|
|
33445
33986
|
init_fs();
|
|
33446
33987
|
init_parser();
|
|
33447
33988
|
init_config2();
|
|
33448
|
-
import { readdir as readdir26, readFile as
|
|
33449
|
-
import { resolve as
|
|
33989
|
+
import { readdir as readdir26, readFile as readFile50 } from "fs/promises";
|
|
33990
|
+
import { resolve as resolve75 } from "path";
|
|
33450
33991
|
async function listPlaybooksCommand(options = {}) {
|
|
33451
33992
|
const dir = playbooksDir();
|
|
33452
33993
|
if (!await fileExists(dir)) {
|
|
@@ -33461,8 +34002,8 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
33461
34002
|
);
|
|
33462
34003
|
const rows = [];
|
|
33463
34004
|
for (const entry of mdFiles) {
|
|
33464
|
-
const filePath =
|
|
33465
|
-
const raw2 = await
|
|
34005
|
+
const filePath = resolve75(dir, entry.name);
|
|
34006
|
+
const raw2 = await readFile50(filePath, "utf-8");
|
|
33466
34007
|
const parsed = parsePlaybook(raw2);
|
|
33467
34008
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
33468
34009
|
const disabled = disabledSet.has(slug);
|
|
@@ -33585,14 +34126,14 @@ init_fs();
|
|
|
33585
34126
|
init_config2();
|
|
33586
34127
|
init_slug();
|
|
33587
34128
|
import { Command as Command2 } from "commander";
|
|
33588
|
-
import { readFile as
|
|
33589
|
-
import { resolve as
|
|
34129
|
+
import { readFile as readFile52 } from "fs/promises";
|
|
34130
|
+
import { resolve as resolve77 } from "path";
|
|
33590
34131
|
|
|
33591
34132
|
// src/commands/bundle.ts
|
|
33592
34133
|
init_paths();
|
|
33593
34134
|
import { Command } from "commander";
|
|
33594
|
-
import { mkdir as mkdir10, readFile as
|
|
33595
|
-
import { resolve as
|
|
34135
|
+
import { mkdir as mkdir10, readFile as readFile51, readdir as readdir27, rm as rm9, writeFile as writeFile13 } from "fs/promises";
|
|
34136
|
+
import { resolve as resolve76 } from "path";
|
|
33596
34137
|
init_parser2();
|
|
33597
34138
|
init_fs();
|
|
33598
34139
|
init_config2();
|
|
@@ -33610,7 +34151,7 @@ async function resolveBundleScope(options) {
|
|
|
33610
34151
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
33611
34152
|
}
|
|
33612
34153
|
const config = await readConfig();
|
|
33613
|
-
const projectMd =
|
|
34154
|
+
const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
|
|
33614
34155
|
if (!await fileExists(projectMd)) {
|
|
33615
34156
|
throw new Error(`Project "${options.project}" not found.`);
|
|
33616
34157
|
}
|
|
@@ -33680,10 +34221,10 @@ function pickNextPlanFile(planDir, existingFiles) {
|
|
|
33680
34221
|
const m = f.match(/^plan-v(\d+)\.md$/);
|
|
33681
34222
|
if (m) versions.add(parseInt(m[1], 10));
|
|
33682
34223
|
}
|
|
33683
|
-
if (versions.size === 0) return { target:
|
|
34224
|
+
if (versions.size === 0) return { target: resolve76(planDir, "plan.md"), version: 1 };
|
|
33684
34225
|
let n = 2;
|
|
33685
34226
|
while (versions.has(n)) n++;
|
|
33686
|
-
return { target:
|
|
34227
|
+
return { target: resolve76(planDir, `plan-v${n}.md`), version: n };
|
|
33687
34228
|
}
|
|
33688
34229
|
function dedupePreserveOrder(ids) {
|
|
33689
34230
|
const out = [];
|
|
@@ -33767,7 +34308,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
|
|
|
33767
34308
|
if (options.plan) {
|
|
33768
34309
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
33769
34310
|
await ensureDir(planDir);
|
|
33770
|
-
const target =
|
|
34311
|
+
const target = resolve76(planDir, "plan.md");
|
|
33771
34312
|
const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
|
|
33772
34313
|
const stub = [
|
|
33773
34314
|
"---",
|
|
@@ -33943,7 +34484,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33943
34484
|
}
|
|
33944
34485
|
const repository = options.repository ?? process.cwd();
|
|
33945
34486
|
const parentBranch = options.parentBranch ?? "main";
|
|
33946
|
-
const worktreePath = options.worktreePath ??
|
|
34487
|
+
const worktreePath = options.worktreePath ?? resolve76(repository, ".worktrees", options.branch);
|
|
33947
34488
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
33948
34489
|
for (const memberId of bundle.todoIds) {
|
|
33949
34490
|
const item = checklist.items.find((i) => i.id === memberId);
|
|
@@ -33953,8 +34494,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33953
34494
|
}
|
|
33954
34495
|
const bundlesFilePath = bundlesPath(sc.todosPath);
|
|
33955
34496
|
const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
|
|
33956
|
-
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await
|
|
33957
|
-
const checklistSnapshot = await fileExists(checklistFilePath) ? await
|
|
34497
|
+
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile51(bundlesFilePath, "utf-8") : null;
|
|
34498
|
+
const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile51(checklistFilePath, "utf-8") : null;
|
|
33958
34499
|
const record = async () => {
|
|
33959
34500
|
bundle.branch = options.branch;
|
|
33960
34501
|
bundle.worktreePath = worktreePath;
|
|
@@ -33970,7 +34511,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33970
34511
|
try {
|
|
33971
34512
|
await writeBundles(sc.todosPath, bundles);
|
|
33972
34513
|
await writeChecklist(sc.todosPath, checklist);
|
|
33973
|
-
const ctxDir =
|
|
34514
|
+
const ctxDir = resolve76(worktreePath, ".syntaur");
|
|
33974
34515
|
await mkdir10(ctxDir, { recursive: true });
|
|
33975
34516
|
const payload = {
|
|
33976
34517
|
bundleId: bundle.id,
|
|
@@ -33984,7 +34525,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33984
34525
|
repository,
|
|
33985
34526
|
boundAt: nowISO()
|
|
33986
34527
|
};
|
|
33987
|
-
await writeFile13(
|
|
34528
|
+
await writeFile13(resolve76(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
|
|
33988
34529
|
} catch (err2) {
|
|
33989
34530
|
try {
|
|
33990
34531
|
if (bundlesSnapshot === null) {
|
|
@@ -34174,7 +34715,7 @@ async function resolveScope(options) {
|
|
|
34174
34715
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
34175
34716
|
}
|
|
34176
34717
|
const config = await readConfig();
|
|
34177
|
-
const projectMd =
|
|
34718
|
+
const projectMd = resolve77(config.defaultProjectDir, options.project, "project.md");
|
|
34178
34719
|
if (!await fileExists(projectMd)) {
|
|
34179
34720
|
throw new Error(`Project "${options.project}" not found.`);
|
|
34180
34721
|
}
|
|
@@ -34507,10 +35048,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
34507
35048
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
34508
35049
|
);
|
|
34509
35050
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
34510
|
-
await ensureDir(
|
|
35051
|
+
await ensureDir(resolve77(todosPath, "archive"));
|
|
34511
35052
|
let archContent = "";
|
|
34512
35053
|
if (await fileExists(archFile)) {
|
|
34513
|
-
archContent = await
|
|
35054
|
+
archContent = await readFile52(archFile, "utf-8");
|
|
34514
35055
|
archContent = archContent.trimEnd() + "\n\n";
|
|
34515
35056
|
} else {
|
|
34516
35057
|
archContent = `---
|
|
@@ -34688,12 +35229,12 @@ function describeScope(scope) {
|
|
|
34688
35229
|
}
|
|
34689
35230
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
34690
35231
|
const { resolve: resolvePath2 } = await import("path");
|
|
34691
|
-
const { readFile:
|
|
35232
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
34692
35233
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
34693
35234
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
34694
35235
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
34695
35236
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
34696
|
-
let content = await
|
|
35237
|
+
let content = await readFile82(assignmentMdPath2, "utf-8");
|
|
34697
35238
|
content = appendTodosToAssignmentBody2(
|
|
34698
35239
|
content,
|
|
34699
35240
|
todos.map((t) => ({
|
|
@@ -34744,7 +35285,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
34744
35285
|
);
|
|
34745
35286
|
let target;
|
|
34746
35287
|
if (existingFiles.length === 0) {
|
|
34747
|
-
target =
|
|
35288
|
+
target = resolve77(planDir, "plan.md");
|
|
34748
35289
|
} else {
|
|
34749
35290
|
const versions = /* @__PURE__ */ new Set();
|
|
34750
35291
|
for (const f of existingFiles) {
|
|
@@ -34754,7 +35295,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
34754
35295
|
}
|
|
34755
35296
|
let n = 2;
|
|
34756
35297
|
while (versions.has(n)) n++;
|
|
34757
|
-
target =
|
|
35298
|
+
target = resolve77(planDir, `plan-v${n}.md`);
|
|
34758
35299
|
}
|
|
34759
35300
|
if (!await fileExists(target)) {
|
|
34760
35301
|
const stub = `---
|
|
@@ -34948,12 +35489,12 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
34948
35489
|
|
|
34949
35490
|
// src/commands/doctor.ts
|
|
34950
35491
|
import { Command as Command4 } from "commander";
|
|
34951
|
-
import { readFile as
|
|
34952
|
-
import { isAbsolute as isAbsolute11, resolve as
|
|
35492
|
+
import { readFile as readFile64 } from "fs/promises";
|
|
35493
|
+
import { isAbsolute as isAbsolute11, resolve as resolve91 } from "path";
|
|
34953
35494
|
|
|
34954
35495
|
// src/utils/doctor/index.ts
|
|
34955
35496
|
import { fileURLToPath as fileURLToPath12 } from "url";
|
|
34956
|
-
import { readFile as
|
|
35497
|
+
import { readFile as readFile63 } from "fs/promises";
|
|
34957
35498
|
import { dirname as dirname25, join as join21 } from "path";
|
|
34958
35499
|
|
|
34959
35500
|
// src/utils/doctor/context.ts
|
|
@@ -34961,11 +35502,11 @@ init_config2();
|
|
|
34961
35502
|
init_paths();
|
|
34962
35503
|
init_fs();
|
|
34963
35504
|
import Database5 from "better-sqlite3";
|
|
34964
|
-
import { resolve as
|
|
35505
|
+
import { resolve as resolve78 } from "path";
|
|
34965
35506
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
34966
35507
|
const config = await readConfig();
|
|
34967
35508
|
const root = syntaurRoot();
|
|
34968
|
-
const dbPath =
|
|
35509
|
+
const dbPath = resolve78(root, "syntaur.db");
|
|
34969
35510
|
let db6 = null;
|
|
34970
35511
|
let dbError = null;
|
|
34971
35512
|
if (await fileExists(dbPath)) {
|
|
@@ -34999,8 +35540,8 @@ function closeCheckContext(ctx) {
|
|
|
34999
35540
|
// src/utils/doctor/checks/env.ts
|
|
35000
35541
|
init_fs();
|
|
35001
35542
|
init_paths();
|
|
35002
|
-
import { resolve as
|
|
35003
|
-
import { readFile as
|
|
35543
|
+
import { resolve as resolve79, isAbsolute as isAbsolute9 } from "path";
|
|
35544
|
+
import { readFile as readFile53, stat as stat9 } from "fs/promises";
|
|
35004
35545
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
35005
35546
|
import { dirname as dirname22, join as join17 } from "path";
|
|
35006
35547
|
var CATEGORY = "env";
|
|
@@ -35010,7 +35551,7 @@ var syntaurRootExists = {
|
|
|
35010
35551
|
title: "~/.syntaur/ directory exists",
|
|
35011
35552
|
async run(ctx) {
|
|
35012
35553
|
try {
|
|
35013
|
-
const s = await
|
|
35554
|
+
const s = await stat9(ctx.syntaurRoot);
|
|
35014
35555
|
if (!s.isDirectory()) {
|
|
35015
35556
|
return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
|
|
35016
35557
|
ctx.syntaurRoot
|
|
@@ -35040,7 +35581,7 @@ var configValid = {
|
|
|
35040
35581
|
category: CATEGORY,
|
|
35041
35582
|
title: "~/.syntaur/config.md is valid",
|
|
35042
35583
|
async run(ctx) {
|
|
35043
|
-
const configPath2 =
|
|
35584
|
+
const configPath2 = resolve79(ctx.syntaurRoot, "config.md");
|
|
35044
35585
|
if (!await fileExists(configPath2)) {
|
|
35045
35586
|
return {
|
|
35046
35587
|
id: this.id,
|
|
@@ -35057,7 +35598,7 @@ var configValid = {
|
|
|
35057
35598
|
autoFixable: false
|
|
35058
35599
|
};
|
|
35059
35600
|
}
|
|
35060
|
-
const content = await
|
|
35601
|
+
const content = await readFile53(configPath2, "utf-8");
|
|
35061
35602
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
35062
35603
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
35063
35604
|
return {
|
|
@@ -35337,7 +35878,7 @@ async function readLocalPkg() {
|
|
|
35337
35878
|
for (let i = 0; i < 6; i++) {
|
|
35338
35879
|
const candidate = join17(dir, "package.json");
|
|
35339
35880
|
try {
|
|
35340
|
-
const text = await
|
|
35881
|
+
const text = await readFile53(candidate, "utf-8");
|
|
35341
35882
|
return JSON.parse(text);
|
|
35342
35883
|
} catch {
|
|
35343
35884
|
dir = dirname22(dir);
|
|
@@ -35389,8 +35930,8 @@ function versionGte(a, b) {
|
|
|
35389
35930
|
|
|
35390
35931
|
// src/utils/doctor/checks/structure.ts
|
|
35391
35932
|
init_fs();
|
|
35392
|
-
import { resolve as
|
|
35393
|
-
import { readdir as readdir28, stat as
|
|
35933
|
+
import { resolve as resolve80 } from "path";
|
|
35934
|
+
import { readdir as readdir28, stat as stat10 } from "fs/promises";
|
|
35394
35935
|
var CATEGORY2 = "structure";
|
|
35395
35936
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
35396
35937
|
"projects",
|
|
@@ -35409,7 +35950,7 @@ var projectsDir = {
|
|
|
35409
35950
|
category: CATEGORY2,
|
|
35410
35951
|
title: "projects/ directory exists",
|
|
35411
35952
|
async run(ctx) {
|
|
35412
|
-
const p =
|
|
35953
|
+
const p = resolve80(ctx.syntaurRoot, "projects");
|
|
35413
35954
|
if (!await fileExists(p)) {
|
|
35414
35955
|
return {
|
|
35415
35956
|
id: this.id,
|
|
@@ -35434,7 +35975,7 @@ var playbooksDir2 = {
|
|
|
35434
35975
|
category: CATEGORY2,
|
|
35435
35976
|
title: "playbooks/ directory exists",
|
|
35436
35977
|
async run(ctx) {
|
|
35437
|
-
const p =
|
|
35978
|
+
const p = resolve80(ctx.syntaurRoot, "playbooks");
|
|
35438
35979
|
if (!await fileExists(p)) {
|
|
35439
35980
|
return {
|
|
35440
35981
|
id: this.id,
|
|
@@ -35459,7 +36000,7 @@ var todosDirValid = {
|
|
|
35459
36000
|
category: CATEGORY2,
|
|
35460
36001
|
title: "todos/ directory is readable (if present)",
|
|
35461
36002
|
async run(ctx) {
|
|
35462
|
-
const p =
|
|
36003
|
+
const p = resolve80(ctx.syntaurRoot, "todos");
|
|
35463
36004
|
if (!await fileExists(p)) {
|
|
35464
36005
|
return {
|
|
35465
36006
|
id: this.id,
|
|
@@ -35470,7 +36011,7 @@ var todosDirValid = {
|
|
|
35470
36011
|
autoFixable: false
|
|
35471
36012
|
};
|
|
35472
36013
|
}
|
|
35473
|
-
const s = await
|
|
36014
|
+
const s = await stat10(p);
|
|
35474
36015
|
if (!s.isDirectory()) {
|
|
35475
36016
|
return {
|
|
35476
36017
|
id: this.id,
|
|
@@ -35490,7 +36031,7 @@ var serversDirValid = {
|
|
|
35490
36031
|
category: CATEGORY2,
|
|
35491
36032
|
title: "servers/ directory is readable (if present)",
|
|
35492
36033
|
async run(ctx) {
|
|
35493
|
-
const p =
|
|
36034
|
+
const p = resolve80(ctx.syntaurRoot, "servers");
|
|
35494
36035
|
if (!await fileExists(p)) {
|
|
35495
36036
|
return {
|
|
35496
36037
|
id: this.id,
|
|
@@ -35501,7 +36042,7 @@ var serversDirValid = {
|
|
|
35501
36042
|
autoFixable: false
|
|
35502
36043
|
};
|
|
35503
36044
|
}
|
|
35504
|
-
const s = await
|
|
36045
|
+
const s = await stat10(p);
|
|
35505
36046
|
if (!s.isDirectory()) {
|
|
35506
36047
|
return {
|
|
35507
36048
|
id: this.id,
|
|
@@ -35535,7 +36076,7 @@ var knownFilesRecognized = {
|
|
|
35535
36076
|
title: this.title,
|
|
35536
36077
|
status: "warn",
|
|
35537
36078
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
35538
|
-
affected: unexpected.map((n) =>
|
|
36079
|
+
affected: unexpected.map((n) => resolve80(ctx.syntaurRoot, n)),
|
|
35539
36080
|
remediation: {
|
|
35540
36081
|
kind: "manual",
|
|
35541
36082
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -35564,8 +36105,8 @@ function pass2(check) {
|
|
|
35564
36105
|
|
|
35565
36106
|
// src/utils/doctor/checks/project.ts
|
|
35566
36107
|
init_fs();
|
|
35567
|
-
import { resolve as
|
|
35568
|
-
import { readdir as readdir29, stat as
|
|
36108
|
+
import { resolve as resolve81 } from "path";
|
|
36109
|
+
import { readdir as readdir29, stat as stat11 } from "fs/promises";
|
|
35569
36110
|
var CATEGORY3 = "project";
|
|
35570
36111
|
var REQUIRED_PROJECT_FILES = [
|
|
35571
36112
|
"project.md",
|
|
@@ -35594,10 +36135,10 @@ async function listProjects2(ctx) {
|
|
|
35594
36135
|
for (const e of entries) {
|
|
35595
36136
|
if (!e.isDirectory()) continue;
|
|
35596
36137
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
35597
|
-
const projectDir =
|
|
36138
|
+
const projectDir = resolve81(dir, e.name);
|
|
35598
36139
|
let looksLikeProject = false;
|
|
35599
36140
|
for (const marker of PROJECT_MARKERS) {
|
|
35600
|
-
if (await fileExists(
|
|
36141
|
+
if (await fileExists(resolve81(projectDir, marker))) {
|
|
35601
36142
|
looksLikeProject = true;
|
|
35602
36143
|
break;
|
|
35603
36144
|
}
|
|
@@ -35616,7 +36157,7 @@ var requiredFiles = {
|
|
|
35616
36157
|
for (const projectDir of projects) {
|
|
35617
36158
|
const missing = [];
|
|
35618
36159
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
35619
|
-
const p =
|
|
36160
|
+
const p = resolve81(projectDir, rel);
|
|
35620
36161
|
if (!await fileExists(p)) missing.push(rel);
|
|
35621
36162
|
}
|
|
35622
36163
|
if (missing.length === 0) continue;
|
|
@@ -35626,7 +36167,7 @@ var requiredFiles = {
|
|
|
35626
36167
|
title: this.title,
|
|
35627
36168
|
status: "error",
|
|
35628
36169
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
35629
|
-
affected: missing.map((m) =>
|
|
36170
|
+
affected: missing.map((m) => resolve81(projectDir, m)),
|
|
35630
36171
|
remediation: {
|
|
35631
36172
|
kind: "manual",
|
|
35632
36173
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -35649,9 +36190,9 @@ var manifestStale = {
|
|
|
35649
36190
|
const projects = await listProjects2(ctx);
|
|
35650
36191
|
const results = [];
|
|
35651
36192
|
for (const projectDir of projects) {
|
|
35652
|
-
const manifestPath =
|
|
36193
|
+
const manifestPath = resolve81(projectDir, "manifest.md");
|
|
35653
36194
|
if (!await fileExists(manifestPath)) continue;
|
|
35654
|
-
const manifestMtime = (await
|
|
36195
|
+
const manifestMtime = (await stat11(manifestPath)).mtimeMs;
|
|
35655
36196
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
35656
36197
|
if (newestAssignment === 0) continue;
|
|
35657
36198
|
if (newestAssignment > manifestMtime) {
|
|
@@ -35698,7 +36239,7 @@ var orphanFiles = {
|
|
|
35698
36239
|
title: this.title,
|
|
35699
36240
|
status: "warn",
|
|
35700
36241
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
35701
|
-
affected: orphans.map((o) =>
|
|
36242
|
+
affected: orphans.map((o) => resolve81(projectDir, o)),
|
|
35702
36243
|
autoFixable: false
|
|
35703
36244
|
});
|
|
35704
36245
|
}
|
|
@@ -35708,7 +36249,7 @@ var orphanFiles = {
|
|
|
35708
36249
|
};
|
|
35709
36250
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
35710
36251
|
async function newestAssignmentMtime(projectDir) {
|
|
35711
|
-
const assignmentsRoot =
|
|
36252
|
+
const assignmentsRoot = resolve81(projectDir, "assignments");
|
|
35712
36253
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
35713
36254
|
let newest = 0;
|
|
35714
36255
|
let entries;
|
|
@@ -35719,9 +36260,9 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
35719
36260
|
}
|
|
35720
36261
|
for (const e of entries) {
|
|
35721
36262
|
if (!e.isDirectory()) continue;
|
|
35722
|
-
const assignmentMd =
|
|
36263
|
+
const assignmentMd = resolve81(assignmentsRoot, e.name, "assignment.md");
|
|
35723
36264
|
try {
|
|
35724
|
-
const s = await
|
|
36265
|
+
const s = await stat11(assignmentMd);
|
|
35725
36266
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
35726
36267
|
} catch {
|
|
35727
36268
|
}
|
|
@@ -35744,8 +36285,8 @@ init_parser();
|
|
|
35744
36285
|
init_types();
|
|
35745
36286
|
init_paths();
|
|
35746
36287
|
init_assignment_walk();
|
|
35747
|
-
import { resolve as
|
|
35748
|
-
import { readFile as
|
|
36288
|
+
import { resolve as resolve82 } from "path";
|
|
36289
|
+
import { readFile as readFile54, readdir as readdir30 } from "fs/promises";
|
|
35749
36290
|
var CATEGORY4 = "assignment";
|
|
35750
36291
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
35751
36292
|
var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -35839,7 +36380,7 @@ var invalidStatus = {
|
|
|
35839
36380
|
const allowed = configuredStatuses(ctx);
|
|
35840
36381
|
const results = [];
|
|
35841
36382
|
for (const a of withAssignmentMd) {
|
|
35842
|
-
const path =
|
|
36383
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35843
36384
|
const parsed = await parseSafe4(path);
|
|
35844
36385
|
if (!parsed) continue;
|
|
35845
36386
|
if (!allowed.has(parsed.status)) {
|
|
@@ -35872,7 +36413,7 @@ var workspaceMissing = {
|
|
|
35872
36413
|
const terminal = terminalStatuses(ctx);
|
|
35873
36414
|
const results = [];
|
|
35874
36415
|
for (const a of withAssignmentMd) {
|
|
35875
|
-
const path =
|
|
36416
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35876
36417
|
const parsed = await parseSafe4(path);
|
|
35877
36418
|
if (!parsed) continue;
|
|
35878
36419
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -35919,12 +36460,12 @@ var requiredFilesByStatus = {
|
|
|
35919
36460
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
35920
36461
|
const results = [];
|
|
35921
36462
|
for (const a of withAssignmentMd) {
|
|
35922
|
-
const assignmentPath =
|
|
36463
|
+
const assignmentPath = resolve82(a.assignmentDir, "assignment.md");
|
|
35923
36464
|
const parsed = await parseSafe4(assignmentPath);
|
|
35924
36465
|
if (!parsed) continue;
|
|
35925
36466
|
const missing = [];
|
|
35926
36467
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
35927
|
-
const handoffPath =
|
|
36468
|
+
const handoffPath = resolve82(a.assignmentDir, "handoff.md");
|
|
35928
36469
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
35929
36470
|
}
|
|
35930
36471
|
if (missing.length === 0) continue;
|
|
@@ -35934,7 +36475,7 @@ var requiredFilesByStatus = {
|
|
|
35934
36475
|
title: this.title,
|
|
35935
36476
|
status: "warn",
|
|
35936
36477
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
35937
|
-
affected: missing.map((m) =>
|
|
36478
|
+
affected: missing.map((m) => resolve82(a.assignmentDir, m)),
|
|
35938
36479
|
remediation: {
|
|
35939
36480
|
kind: "manual",
|
|
35940
36481
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -35957,7 +36498,7 @@ var companionFilesScaffolded = {
|
|
|
35957
36498
|
for (const a of withAssignmentMd) {
|
|
35958
36499
|
const missing = [];
|
|
35959
36500
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
35960
|
-
if (!await fileExists(
|
|
36501
|
+
if (!await fileExists(resolve82(a.assignmentDir, filename))) {
|
|
35961
36502
|
missing.push(filename);
|
|
35962
36503
|
}
|
|
35963
36504
|
}
|
|
@@ -35969,7 +36510,7 @@ var companionFilesScaffolded = {
|
|
|
35969
36510
|
title: this.title,
|
|
35970
36511
|
status: "warn",
|
|
35971
36512
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
35972
|
-
affected: missing.map((m) =>
|
|
36513
|
+
affected: missing.map((m) => resolve82(a.assignmentDir, m)),
|
|
35973
36514
|
remediation: {
|
|
35974
36515
|
kind: "manual",
|
|
35975
36516
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -36002,7 +36543,7 @@ var typeDefinition = {
|
|
|
36002
36543
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36003
36544
|
const results = [];
|
|
36004
36545
|
for (const a of withAssignmentMd) {
|
|
36005
|
-
const path =
|
|
36546
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36006
36547
|
const parsed = await parseSafe4(path);
|
|
36007
36548
|
if (!parsed) continue;
|
|
36008
36549
|
if (!parsed.type) continue;
|
|
@@ -36036,7 +36577,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
36036
36577
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36037
36578
|
const results = [];
|
|
36038
36579
|
for (const a of withAssignmentMd) {
|
|
36039
|
-
const path =
|
|
36580
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36040
36581
|
const parsed = await parseSafe4(path);
|
|
36041
36582
|
if (!parsed) continue;
|
|
36042
36583
|
if (a.standalone) {
|
|
@@ -36087,13 +36628,13 @@ var draftMissingObjective = {
|
|
|
36087
36628
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36088
36629
|
const results = [];
|
|
36089
36630
|
for (const a of withAssignmentMd) {
|
|
36090
|
-
const path =
|
|
36631
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36091
36632
|
const parsed = await parseSafe4(path);
|
|
36092
36633
|
if (!parsed) continue;
|
|
36093
36634
|
if (parsed.status !== "draft") continue;
|
|
36094
36635
|
let raw2;
|
|
36095
36636
|
try {
|
|
36096
|
-
raw2 = await
|
|
36637
|
+
raw2 = await readFile54(path, "utf-8");
|
|
36097
36638
|
} catch {
|
|
36098
36639
|
continue;
|
|
36099
36640
|
}
|
|
@@ -36126,7 +36667,7 @@ var readyToImplementMissingPlan = {
|
|
|
36126
36667
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36127
36668
|
const results = [];
|
|
36128
36669
|
for (const a of withAssignmentMd) {
|
|
36129
|
-
const path =
|
|
36670
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36130
36671
|
const parsed = await parseSafe4(path);
|
|
36131
36672
|
if (!parsed) continue;
|
|
36132
36673
|
if (parsed.status !== "ready_to_implement") continue;
|
|
@@ -36135,7 +36676,7 @@ var readyToImplementMissingPlan = {
|
|
|
36135
36676
|
let hasPlanContent = false;
|
|
36136
36677
|
for (const f of planFiles) {
|
|
36137
36678
|
try {
|
|
36138
|
-
const c2 = await
|
|
36679
|
+
const c2 = await readFile54(resolve82(a.assignmentDir, f), "utf-8");
|
|
36139
36680
|
if (c2.trim().length > 0) {
|
|
36140
36681
|
hasPlanContent = true;
|
|
36141
36682
|
break;
|
|
@@ -36151,7 +36692,7 @@ var readyToImplementMissingPlan = {
|
|
|
36151
36692
|
title: this.title,
|
|
36152
36693
|
status: "warn",
|
|
36153
36694
|
detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
|
|
36154
|
-
affected: [
|
|
36695
|
+
affected: [resolve82(a.assignmentDir, "plan.md")],
|
|
36155
36696
|
remediation: {
|
|
36156
36697
|
kind: "manual",
|
|
36157
36698
|
suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
|
|
@@ -36178,7 +36719,7 @@ var assignmentChecks = [
|
|
|
36178
36719
|
];
|
|
36179
36720
|
async function parseSafe4(path) {
|
|
36180
36721
|
try {
|
|
36181
|
-
const content = await
|
|
36722
|
+
const content = await readFile54(path, "utf-8");
|
|
36182
36723
|
return parseAssignmentFull(content);
|
|
36183
36724
|
} catch {
|
|
36184
36725
|
return null;
|
|
@@ -36197,7 +36738,7 @@ function pass4(check, detail) {
|
|
|
36197
36738
|
|
|
36198
36739
|
// src/utils/doctor/checks/dashboard.ts
|
|
36199
36740
|
init_fs();
|
|
36200
|
-
import { resolve as
|
|
36741
|
+
import { resolve as resolve83 } from "path";
|
|
36201
36742
|
var CATEGORY5 = "dashboard";
|
|
36202
36743
|
var dbReachable = {
|
|
36203
36744
|
id: "dashboard.db-reachable",
|
|
@@ -36211,7 +36752,7 @@ var dbReachable = {
|
|
|
36211
36752
|
title: this.title,
|
|
36212
36753
|
status: "error",
|
|
36213
36754
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
36214
|
-
affected: [
|
|
36755
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36215
36756
|
remediation: {
|
|
36216
36757
|
kind: "manual",
|
|
36217
36758
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -36229,7 +36770,7 @@ var dbReachable = {
|
|
|
36229
36770
|
title: this.title,
|
|
36230
36771
|
status: "error",
|
|
36231
36772
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
36232
|
-
affected: [
|
|
36773
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36233
36774
|
autoFixable: false
|
|
36234
36775
|
};
|
|
36235
36776
|
}
|
|
@@ -36241,7 +36782,7 @@ var dbReachable = {
|
|
|
36241
36782
|
title: this.title,
|
|
36242
36783
|
status: "error",
|
|
36243
36784
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
36244
|
-
affected: [
|
|
36785
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36245
36786
|
autoFixable: false
|
|
36246
36787
|
};
|
|
36247
36788
|
}
|
|
@@ -36267,7 +36808,7 @@ var ghostSessions = {
|
|
|
36267
36808
|
const results = [];
|
|
36268
36809
|
for (const row of rows) {
|
|
36269
36810
|
if (!row.project_slug) continue;
|
|
36270
|
-
const projectPath =
|
|
36811
|
+
const projectPath = resolve83(projectsDir2, row.project_slug, "project.md");
|
|
36271
36812
|
if (!await fileExists(projectPath)) {
|
|
36272
36813
|
results.push({
|
|
36273
36814
|
id: this.id,
|
|
@@ -36286,7 +36827,7 @@ var ghostSessions = {
|
|
|
36286
36827
|
continue;
|
|
36287
36828
|
}
|
|
36288
36829
|
if (row.assignment_slug) {
|
|
36289
|
-
const assignmentPath =
|
|
36830
|
+
const assignmentPath = resolve83(
|
|
36290
36831
|
projectsDir2,
|
|
36291
36832
|
row.project_slug,
|
|
36292
36833
|
"assignments",
|
|
@@ -36338,8 +36879,8 @@ function skipped(check, reason) {
|
|
|
36338
36879
|
|
|
36339
36880
|
// src/utils/doctor/checks/integrations.ts
|
|
36340
36881
|
init_fs();
|
|
36341
|
-
import { resolve as
|
|
36342
|
-
import { readdir as readdir31, readFile as
|
|
36882
|
+
import { resolve as resolve84, dirname as dirname23, basename as basename9 } from "path";
|
|
36883
|
+
import { readdir as readdir31, readFile as readFile55 } from "fs/promises";
|
|
36343
36884
|
import { homedir as homedir14 } from "os";
|
|
36344
36885
|
var CATEGORY6 = "integrations";
|
|
36345
36886
|
var claudePluginLinked = {
|
|
@@ -36421,10 +36962,10 @@ var backupConfigured = {
|
|
|
36421
36962
|
}
|
|
36422
36963
|
};
|
|
36423
36964
|
async function readKnownMarketplaces() {
|
|
36424
|
-
const path =
|
|
36965
|
+
const path = resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json");
|
|
36425
36966
|
if (!await fileExists(path)) return {};
|
|
36426
36967
|
try {
|
|
36427
|
-
const raw2 = await
|
|
36968
|
+
const raw2 = await readFile55(path, "utf-8");
|
|
36428
36969
|
return JSON.parse(raw2);
|
|
36429
36970
|
} catch {
|
|
36430
36971
|
return {};
|
|
@@ -36441,7 +36982,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36441
36982
|
return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
|
|
36442
36983
|
}
|
|
36443
36984
|
const pluginsParent = dirname23(dir);
|
|
36444
|
-
if (
|
|
36985
|
+
if (basename9(pluginsParent) !== "plugins") {
|
|
36445
36986
|
return {
|
|
36446
36987
|
id: this.id,
|
|
36447
36988
|
category: this.category,
|
|
@@ -36458,7 +36999,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36458
36999
|
};
|
|
36459
37000
|
}
|
|
36460
37001
|
const marketplaceRoot = dirname23(pluginsParent);
|
|
36461
|
-
const marketplaceManifest =
|
|
37002
|
+
const marketplaceManifest = resolve84(marketplaceRoot, ".claude-plugin", "marketplace.json");
|
|
36462
37003
|
if (!await fileExists(marketplaceManifest)) {
|
|
36463
37004
|
return {
|
|
36464
37005
|
id: this.id,
|
|
@@ -36477,7 +37018,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36477
37018
|
}
|
|
36478
37019
|
let parsed = {};
|
|
36479
37020
|
try {
|
|
36480
|
-
parsed = JSON.parse(await
|
|
37021
|
+
parsed = JSON.parse(await readFile55(marketplaceManifest, "utf-8"));
|
|
36481
37022
|
} catch {
|
|
36482
37023
|
return {
|
|
36483
37024
|
id: this.id,
|
|
@@ -36489,7 +37030,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36489
37030
|
autoFixable: false
|
|
36490
37031
|
};
|
|
36491
37032
|
}
|
|
36492
|
-
const marketplaceName = parsed.name ??
|
|
37033
|
+
const marketplaceName = parsed.name ?? basename9(marketplaceRoot);
|
|
36493
37034
|
const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
|
|
36494
37035
|
const known = await readKnownMarketplaces();
|
|
36495
37036
|
const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
|
|
@@ -36509,7 +37050,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36509
37050
|
title: this.title,
|
|
36510
37051
|
status: "error",
|
|
36511
37052
|
detail: issues.join("; "),
|
|
36512
|
-
affected: [marketplaceManifest,
|
|
37053
|
+
affected: [marketplaceManifest, resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
|
|
36513
37054
|
remediation: {
|
|
36514
37055
|
kind: "manual",
|
|
36515
37056
|
suggestion: "Re-run install-plugin to ensure both files are in sync.",
|
|
@@ -36549,8 +37090,8 @@ function skipped2(check, reason) {
|
|
|
36549
37090
|
init_fs();
|
|
36550
37091
|
init_parser();
|
|
36551
37092
|
init_types();
|
|
36552
|
-
import { resolve as
|
|
36553
|
-
import { readFile as
|
|
37093
|
+
import { resolve as resolve85 } from "path";
|
|
37094
|
+
import { readFile as readFile56 } from "fs/promises";
|
|
36554
37095
|
var CATEGORY7 = "workspace";
|
|
36555
37096
|
var ASSIGNMENT_FIELDS2 = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
36556
37097
|
var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
|
|
@@ -36572,12 +37113,12 @@ function isStandaloneSession(ctx) {
|
|
|
36572
37113
|
return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && hasSessionMeta;
|
|
36573
37114
|
}
|
|
36574
37115
|
async function loadContext(ctx) {
|
|
36575
|
-
const path =
|
|
37116
|
+
const path = resolve85(ctx.cwd, ".syntaur", "context.json");
|
|
36576
37117
|
if (!await fileExists(path)) {
|
|
36577
37118
|
return { data: null, path, exists: false, parseError: null };
|
|
36578
37119
|
}
|
|
36579
37120
|
try {
|
|
36580
|
-
const raw2 = await
|
|
37121
|
+
const raw2 = await readFile56(path, "utf-8");
|
|
36581
37122
|
return { data: JSON.parse(raw2), path, exists: true, parseError: null };
|
|
36582
37123
|
} catch (err2) {
|
|
36583
37124
|
return {
|
|
@@ -36671,7 +37212,7 @@ var contextAssignmentResolves = {
|
|
|
36671
37212
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
36672
37213
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
|
|
36673
37214
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
36674
|
-
const assignmentMd =
|
|
37215
|
+
const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
|
|
36675
37216
|
if (!await fileExists(assignmentMd)) {
|
|
36676
37217
|
return {
|
|
36677
37218
|
id: this.id,
|
|
@@ -36701,10 +37242,10 @@ var contextTerminal = {
|
|
|
36701
37242
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
36702
37243
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
|
|
36703
37244
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
36704
|
-
const assignmentMd =
|
|
37245
|
+
const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
|
|
36705
37246
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
36706
37247
|
try {
|
|
36707
|
-
const content = await
|
|
37248
|
+
const content = await readFile56(assignmentMd, "utf-8");
|
|
36708
37249
|
const parsed = parseAssignmentFull(content);
|
|
36709
37250
|
const terminal = terminalStatuses2(ctx);
|
|
36710
37251
|
if (terminal.has(parsed.status)) {
|
|
@@ -36850,15 +37391,15 @@ var agentChecks = [agentsResolvable];
|
|
|
36850
37391
|
// src/utils/doctor/checks/terminal.ts
|
|
36851
37392
|
init_config2();
|
|
36852
37393
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
36853
|
-
import { readFile as
|
|
36854
|
-
import { resolve as
|
|
37394
|
+
import { readFile as readFile57 } from "fs/promises";
|
|
37395
|
+
import { resolve as resolve86 } from "path";
|
|
36855
37396
|
init_paths();
|
|
36856
37397
|
init_fs();
|
|
36857
37398
|
var CATEGORY9 = "terminal";
|
|
36858
37399
|
async function readRawTerminalKey() {
|
|
36859
|
-
const configPath2 =
|
|
37400
|
+
const configPath2 = resolve86(syntaurRoot(), "config.md");
|
|
36860
37401
|
if (!await fileExists(configPath2)) return null;
|
|
36861
|
-
const content = await
|
|
37402
|
+
const content = await readFile57(configPath2, "utf-8");
|
|
36862
37403
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
36863
37404
|
if (!fmMatch) return null;
|
|
36864
37405
|
const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
|
|
@@ -37008,13 +37549,13 @@ var terminalChecks = [
|
|
|
37008
37549
|
|
|
37009
37550
|
// src/utils/doctor/checks/skills.ts
|
|
37010
37551
|
init_fs();
|
|
37011
|
-
import { resolve as
|
|
37012
|
-
import { readdir as readdir32, readFile as
|
|
37552
|
+
import { resolve as resolve87, join as join18 } from "path";
|
|
37553
|
+
import { readdir as readdir32, readFile as readFile58, lstat as lstat4 } from "fs/promises";
|
|
37013
37554
|
import { homedir as homedir15 } from "os";
|
|
37014
37555
|
var CATEGORY10 = "skills";
|
|
37015
37556
|
var skillTargets = [
|
|
37016
|
-
{ agent: "claude", dir:
|
|
37017
|
-
{ agent: "codex", dir:
|
|
37557
|
+
{ agent: "claude", dir: resolve87(homedir15(), ".claude", "skills"), label: "~/.claude/skills" },
|
|
37558
|
+
{ agent: "codex", dir: resolve87(homedir15(), ".codex", "skills"), label: "~/.codex/skills" }
|
|
37018
37559
|
];
|
|
37019
37560
|
var skillsDedupCheck = {
|
|
37020
37561
|
id: "skills.dedup",
|
|
@@ -37038,7 +37579,7 @@ var skillsDedupCheck = {
|
|
|
37038
37579
|
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
37039
37580
|
const skillMd = join18(dir, entry.name, "SKILL.md");
|
|
37040
37581
|
if (!await fileExists(skillMd)) continue;
|
|
37041
|
-
const content = await
|
|
37582
|
+
const content = await readFile58(skillMd, "utf-8").catch(() => "");
|
|
37042
37583
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
37043
37584
|
if (!match || match[1] !== entry.name) continue;
|
|
37044
37585
|
let isSymlink2 = false;
|
|
@@ -37088,12 +37629,12 @@ var skillsChecks = [skillsDedupCheck];
|
|
|
37088
37629
|
|
|
37089
37630
|
// src/utils/doctor/checks/cross-agent.ts
|
|
37090
37631
|
init_fs();
|
|
37091
|
-
import { join as join19, resolve as
|
|
37092
|
-
import { readFile as
|
|
37632
|
+
import { join as join19, resolve as resolve88 } from "path";
|
|
37633
|
+
import { readFile as readFile60 } from "fs/promises";
|
|
37093
37634
|
|
|
37094
37635
|
// src/utils/skill-frontmatter.ts
|
|
37095
37636
|
import { createHash as createHash4 } from "crypto";
|
|
37096
|
-
import { readFile as
|
|
37637
|
+
import { readFile as readFile59 } from "fs/promises";
|
|
37097
37638
|
function stripQuotes(raw2) {
|
|
37098
37639
|
const t = raw2.trim();
|
|
37099
37640
|
if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
|
|
@@ -37137,7 +37678,7 @@ function readSkillIdentity(skillMdText) {
|
|
|
37137
37678
|
return { name, hasDescription };
|
|
37138
37679
|
}
|
|
37139
37680
|
async function sha256File(path) {
|
|
37140
|
-
return createHash4("sha256").update(await
|
|
37681
|
+
return createHash4("sha256").update(await readFile59(path)).digest("hex");
|
|
37141
37682
|
}
|
|
37142
37683
|
|
|
37143
37684
|
// src/utils/doctor/checks/cross-agent.ts
|
|
@@ -37154,7 +37695,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
|
|
|
37154
37695
|
}
|
|
37155
37696
|
let text;
|
|
37156
37697
|
try {
|
|
37157
|
-
text = await
|
|
37698
|
+
text = await readFile60(installedPath, "utf-8");
|
|
37158
37699
|
} catch {
|
|
37159
37700
|
problems.push({ skill, kind: "invalid-frontmatter" });
|
|
37160
37701
|
continue;
|
|
@@ -37231,7 +37772,7 @@ var crossAgentSkillsCheck = {
|
|
|
37231
37772
|
}
|
|
37232
37773
|
if (recorded && t.instructions) {
|
|
37233
37774
|
for (const f of t.instructions.files) {
|
|
37234
|
-
const p =
|
|
37775
|
+
const p = resolve88(ctx.cwd, f.path);
|
|
37235
37776
|
if (!await fileExists(p)) {
|
|
37236
37777
|
problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
|
|
37237
37778
|
affected.push(p);
|
|
@@ -37307,7 +37848,7 @@ var crossAgentChecks = [crossAgentSkillsCheck];
|
|
|
37307
37848
|
// src/utils/doctor/checks/bundles.ts
|
|
37308
37849
|
init_fs();
|
|
37309
37850
|
init_paths();
|
|
37310
|
-
import { resolve as
|
|
37851
|
+
import { resolve as resolve89 } from "path";
|
|
37311
37852
|
import { readdir as readdir33 } from "fs/promises";
|
|
37312
37853
|
import { spawnSync as spawnSync10 } from "child_process";
|
|
37313
37854
|
init_parser2();
|
|
@@ -37337,7 +37878,7 @@ async function listScopes(ctx) {
|
|
|
37337
37878
|
if (!e.isDirectory()) continue;
|
|
37338
37879
|
const slug = e.name;
|
|
37339
37880
|
if (typeof slug !== "string" || slug.startsWith(".")) continue;
|
|
37340
|
-
const projectMd =
|
|
37881
|
+
const projectMd = resolve89(ctx.config.defaultProjectDir, slug, "project.md");
|
|
37341
37882
|
if (!await fileExists(projectMd)) continue;
|
|
37342
37883
|
out.push({
|
|
37343
37884
|
scopeLabel: `project:${slug}`,
|
|
@@ -37530,7 +38071,7 @@ var bundleChecks = [
|
|
|
37530
38071
|
];
|
|
37531
38072
|
|
|
37532
38073
|
// src/utils/doctor/checks/plugin.ts
|
|
37533
|
-
import { readFile as
|
|
38074
|
+
import { readFile as readFile61 } from "fs/promises";
|
|
37534
38075
|
import { dirname as dirname24, join as join20 } from "path";
|
|
37535
38076
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
37536
38077
|
var CATEGORY13 = "plugin";
|
|
@@ -37540,7 +38081,7 @@ async function readCliVersion() {
|
|
|
37540
38081
|
let dir = dirname24(fileURLToPath11(import.meta.url));
|
|
37541
38082
|
for (let i = 0; i < 8; i += 1) {
|
|
37542
38083
|
try {
|
|
37543
|
-
const parsed = JSON.parse(await
|
|
38084
|
+
const parsed = JSON.parse(await readFile61(join20(dir, "package.json"), "utf-8"));
|
|
37544
38085
|
if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
|
|
37545
38086
|
} catch {
|
|
37546
38087
|
}
|
|
@@ -37648,6 +38189,68 @@ var deriveConfigValid = {
|
|
|
37648
38189
|
};
|
|
37649
38190
|
var deriveConfigChecks = [deriveConfigValid];
|
|
37650
38191
|
|
|
38192
|
+
// src/utils/doctor/checks/staleness.ts
|
|
38193
|
+
init_config2();
|
|
38194
|
+
import { resolve as resolve90 } from "path";
|
|
38195
|
+
import { readFile as readFile62 } from "fs/promises";
|
|
38196
|
+
var CATEGORY15 = "staleness";
|
|
38197
|
+
var stalenessConfigValid = {
|
|
38198
|
+
id: "staleness.valid",
|
|
38199
|
+
category: CATEGORY15,
|
|
38200
|
+
title: "Staleness threshold overrides are valid",
|
|
38201
|
+
async run(ctx) {
|
|
38202
|
+
let content;
|
|
38203
|
+
try {
|
|
38204
|
+
content = await readFile62(resolve90(ctx.syntaurRoot, "config.md"), "utf-8");
|
|
38205
|
+
} catch {
|
|
38206
|
+
return {
|
|
38207
|
+
id: this.id,
|
|
38208
|
+
category: this.category,
|
|
38209
|
+
title: this.title,
|
|
38210
|
+
status: "skipped",
|
|
38211
|
+
detail: "no config.md",
|
|
38212
|
+
autoFixable: false
|
|
38213
|
+
};
|
|
38214
|
+
}
|
|
38215
|
+
if (!/^\s*staleness:\s*$/m.test(content)) {
|
|
38216
|
+
return {
|
|
38217
|
+
id: this.id,
|
|
38218
|
+
category: this.category,
|
|
38219
|
+
title: this.title,
|
|
38220
|
+
status: "skipped",
|
|
38221
|
+
detail: "no staleness: block configured (using defaults)",
|
|
38222
|
+
autoFixable: false
|
|
38223
|
+
};
|
|
38224
|
+
}
|
|
38225
|
+
const problems = validateStalenessConfig(content);
|
|
38226
|
+
if (problems.length === 0) {
|
|
38227
|
+
return {
|
|
38228
|
+
id: this.id,
|
|
38229
|
+
category: this.category,
|
|
38230
|
+
title: this.title,
|
|
38231
|
+
status: "pass",
|
|
38232
|
+
detail: "staleness threshold overrides are valid",
|
|
38233
|
+
autoFixable: false
|
|
38234
|
+
};
|
|
38235
|
+
}
|
|
38236
|
+
return {
|
|
38237
|
+
id: this.id,
|
|
38238
|
+
category: this.category,
|
|
38239
|
+
title: this.title,
|
|
38240
|
+
status: "warn",
|
|
38241
|
+
detail: problems.join("; "),
|
|
38242
|
+
affected: problems,
|
|
38243
|
+
remediation: {
|
|
38244
|
+
kind: "manual",
|
|
38245
|
+
suggestion: "Fix the flagged staleness: entries in ~/.syntaur/config.md. Each value must be a positive duration (7d, 12h, 30m, 90s, 500ms). Invalid entries are ignored and fall back to the default gate.",
|
|
38246
|
+
command: null
|
|
38247
|
+
},
|
|
38248
|
+
autoFixable: false
|
|
38249
|
+
};
|
|
38250
|
+
}
|
|
38251
|
+
};
|
|
38252
|
+
var stalenessChecks = [stalenessConfigValid];
|
|
38253
|
+
|
|
37651
38254
|
// src/utils/doctor/registry.ts
|
|
37652
38255
|
function allChecks() {
|
|
37653
38256
|
return [
|
|
@@ -37664,7 +38267,8 @@ function allChecks() {
|
|
|
37664
38267
|
...crossAgentChecks,
|
|
37665
38268
|
...bundleChecks,
|
|
37666
38269
|
...pluginChecks,
|
|
37667
|
-
...deriveConfigChecks
|
|
38270
|
+
...deriveConfigChecks,
|
|
38271
|
+
...stalenessChecks
|
|
37668
38272
|
];
|
|
37669
38273
|
}
|
|
37670
38274
|
|
|
@@ -37750,7 +38354,7 @@ async function readVersion() {
|
|
|
37750
38354
|
let dir = dirname25(here);
|
|
37751
38355
|
for (let i = 0; i < 6; i++) {
|
|
37752
38356
|
try {
|
|
37753
|
-
const raw2 = await
|
|
38357
|
+
const raw2 = await readFile63(join21(dir, "package.json"), "utf-8");
|
|
37754
38358
|
const parsed = JSON.parse(raw2);
|
|
37755
38359
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
37756
38360
|
} catch {
|
|
@@ -37847,7 +38451,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
|
|
|
37847
38451
|
];
|
|
37848
38452
|
var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
37849
38453
|
async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
37850
|
-
const absolute = isAbsolute11(inputPath) ? inputPath :
|
|
38454
|
+
const absolute = isAbsolute11(inputPath) ? inputPath : resolve91(cwd, inputPath);
|
|
37851
38455
|
const errors = [];
|
|
37852
38456
|
const warnings = [];
|
|
37853
38457
|
if (!await fileExists(absolute)) {
|
|
@@ -37860,7 +38464,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
|
37860
38464
|
}
|
|
37861
38465
|
let content;
|
|
37862
38466
|
try {
|
|
37863
|
-
content = await
|
|
38467
|
+
content = await readFile64(absolute, "utf-8");
|
|
37864
38468
|
} catch (err2) {
|
|
37865
38469
|
return {
|
|
37866
38470
|
ok: false,
|
|
@@ -38227,8 +38831,8 @@ init_assignment_resolver();
|
|
|
38227
38831
|
init_frontmatter();
|
|
38228
38832
|
init_event_emit();
|
|
38229
38833
|
init_templates();
|
|
38230
|
-
import { resolve as
|
|
38231
|
-
import { readFile as
|
|
38834
|
+
import { resolve as resolve92 } from "path";
|
|
38835
|
+
import { readFile as readFile65 } from "fs/promises";
|
|
38232
38836
|
function shortId() {
|
|
38233
38837
|
return generateId().split("-")[0];
|
|
38234
38838
|
}
|
|
@@ -38259,7 +38863,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
38259
38863
|
if (!isValidSlug(target)) {
|
|
38260
38864
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
38261
38865
|
}
|
|
38262
|
-
assignmentDir =
|
|
38866
|
+
assignmentDir = resolve92(baseDir, options.project, "assignments", target);
|
|
38263
38867
|
assignmentRef = target;
|
|
38264
38868
|
projectSlug = options.project;
|
|
38265
38869
|
} else {
|
|
@@ -38271,13 +38875,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
38271
38875
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
38272
38876
|
projectSlug = resolved.projectSlug;
|
|
38273
38877
|
}
|
|
38274
|
-
const commentsPath =
|
|
38878
|
+
const commentsPath = resolve92(assignmentDir, "comments.md");
|
|
38275
38879
|
const timestamp = nowTimestamp();
|
|
38276
38880
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
38277
38881
|
let currentContent;
|
|
38278
38882
|
let currentCount = 0;
|
|
38279
38883
|
if (await fileExists(commentsPath)) {
|
|
38280
|
-
currentContent = await
|
|
38884
|
+
currentContent = await readFile65(commentsPath, "utf-8");
|
|
38281
38885
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
38282
38886
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
38283
38887
|
} else {
|
|
@@ -38305,9 +38909,9 @@ ${entry}`;
|
|
|
38305
38909
|
}
|
|
38306
38910
|
await writeFileForce(commentsPath, next);
|
|
38307
38911
|
try {
|
|
38308
|
-
const assignmentMd =
|
|
38912
|
+
const assignmentMd = resolve92(assignmentDir, "assignment.md");
|
|
38309
38913
|
if (await fileExists(assignmentMd)) {
|
|
38310
|
-
const fm = parseAssignmentFrontmatter(await
|
|
38914
|
+
const fm = parseAssignmentFrontmatter(await readFile65(assignmentMd, "utf-8"));
|
|
38311
38915
|
emitEvent({
|
|
38312
38916
|
assignmentId: fm.id,
|
|
38313
38917
|
projectSlug,
|
|
@@ -38331,170 +38935,15 @@ ${entry}`;
|
|
|
38331
38935
|
}
|
|
38332
38936
|
|
|
38333
38937
|
// src/commands/capture.ts
|
|
38334
|
-
import { resolve as
|
|
38335
|
-
import { copyFile as copyFile3, mkdir as mkdir12, realpath as realpath2, rm as rm14, stat as
|
|
38938
|
+
import { resolve as resolve95, relative as relative4, dirname as dirname26 } from "path";
|
|
38939
|
+
import { copyFile as copyFile3, mkdir as mkdir12, realpath as realpath2, rm as rm14, stat as stat14, writeFile as writeFile15 } from "fs/promises";
|
|
38336
38940
|
import { existsSync as existsSync7 } from "fs";
|
|
38337
|
-
|
|
38338
|
-
// src/utils/assignment-target.ts
|
|
38339
|
-
init_paths();
|
|
38340
|
-
init_fs();
|
|
38341
|
-
init_config2();
|
|
38342
|
-
init_slug();
|
|
38343
|
-
init_assignment_resolver();
|
|
38344
|
-
init_parser();
|
|
38345
|
-
import { resolve as resolve91 } from "path";
|
|
38346
|
-
import { readFile as readFile64 } from "fs/promises";
|
|
38347
|
-
var AssignmentTargetError = class extends Error {
|
|
38348
|
-
};
|
|
38349
|
-
function classifyContext(ctx) {
|
|
38350
|
-
if (!ctx) return "empty";
|
|
38351
|
-
const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
|
|
38352
|
-
if (hasAssignment) return "assignment";
|
|
38353
|
-
if (ctx.bundleId) return "bundle";
|
|
38354
|
-
if (ctx.sessionId || ctx.transcriptPath) return "standalone";
|
|
38355
|
-
return "empty";
|
|
38356
|
-
}
|
|
38357
|
-
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
38358
|
-
const path = resolve91(assignmentDir, "assignment.md");
|
|
38359
|
-
if (!await fileExists(path)) return null;
|
|
38360
|
-
try {
|
|
38361
|
-
const content = await readFile64(path, "utf-8");
|
|
38362
|
-
const [fm] = extractFrontmatter(content);
|
|
38363
|
-
return getField(fm, "id");
|
|
38364
|
-
} catch {
|
|
38365
|
-
return null;
|
|
38366
|
-
}
|
|
38367
|
-
}
|
|
38368
|
-
async function readContextJson(cwd) {
|
|
38369
|
-
const path = resolve91(cwd, ".syntaur", "context.json");
|
|
38370
|
-
if (!await fileExists(path)) return null;
|
|
38371
|
-
try {
|
|
38372
|
-
const raw2 = await readFile64(path, "utf-8");
|
|
38373
|
-
return JSON.parse(raw2);
|
|
38374
|
-
} catch {
|
|
38375
|
-
return null;
|
|
38376
|
-
}
|
|
38377
|
-
}
|
|
38378
|
-
async function resolveAssignmentTarget(input4, opts = {}) {
|
|
38379
|
-
const config = await readConfig();
|
|
38380
|
-
const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
|
|
38381
|
-
if (opts.project) {
|
|
38382
|
-
if (!input4) {
|
|
38383
|
-
throw new AssignmentTargetError(
|
|
38384
|
-
"--project requires an assignment slug as a positional argument."
|
|
38385
|
-
);
|
|
38386
|
-
}
|
|
38387
|
-
if (!isValidSlug(opts.project)) {
|
|
38388
|
-
throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
|
|
38389
|
-
}
|
|
38390
|
-
if (!isValidSlug(input4)) {
|
|
38391
|
-
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
38392
|
-
}
|
|
38393
|
-
const projectDir = resolve91(baseDir, opts.project);
|
|
38394
|
-
const projectMdPath = resolve91(projectDir, "project.md");
|
|
38395
|
-
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
38396
|
-
throw new AssignmentTargetError(
|
|
38397
|
-
`Project "${opts.project}" not found at ${projectDir}.`
|
|
38398
|
-
);
|
|
38399
|
-
}
|
|
38400
|
-
const assignmentDir = resolve91(projectDir, "assignments", input4);
|
|
38401
|
-
const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
|
|
38402
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38403
|
-
throw new AssignmentTargetError(
|
|
38404
|
-
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
38405
|
-
);
|
|
38406
|
-
}
|
|
38407
|
-
const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
|
|
38408
|
-
return {
|
|
38409
|
-
assignmentDir,
|
|
38410
|
-
projectSlug: opts.project,
|
|
38411
|
-
assignmentSlug: input4,
|
|
38412
|
-
id,
|
|
38413
|
-
standalone: false,
|
|
38414
|
-
workspaceGroup: null
|
|
38415
|
-
};
|
|
38416
|
-
}
|
|
38417
|
-
if (input4) {
|
|
38418
|
-
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
|
|
38419
|
-
if (!resolved) {
|
|
38420
|
-
throw new AssignmentTargetError(
|
|
38421
|
-
`Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
|
|
38422
|
-
);
|
|
38423
|
-
}
|
|
38424
|
-
return resolved;
|
|
38425
|
-
}
|
|
38426
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
38427
|
-
const ctx = await readContextJson(cwd);
|
|
38428
|
-
if (!ctx) {
|
|
38429
|
-
throw new AssignmentTargetError(
|
|
38430
|
-
"No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
|
|
38431
|
-
);
|
|
38432
|
-
}
|
|
38433
|
-
if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
|
|
38434
|
-
throw new AssignmentTargetError(
|
|
38435
|
-
`Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
|
|
38436
|
-
);
|
|
38437
|
-
}
|
|
38438
|
-
if (ctx.assignmentDir) {
|
|
38439
|
-
const dir = expandHome(ctx.assignmentDir);
|
|
38440
|
-
const assignmentMdPath2 = resolve91(dir, "assignment.md");
|
|
38441
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38442
|
-
throw new AssignmentTargetError(
|
|
38443
|
-
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
38444
|
-
);
|
|
38445
|
-
}
|
|
38446
|
-
const id = await readAssignmentFrontmatterId(dir);
|
|
38447
|
-
if (!id || id.trim() === "") {
|
|
38448
|
-
throw new AssignmentTargetError(
|
|
38449
|
-
`.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
|
|
38450
|
-
);
|
|
38451
|
-
}
|
|
38452
|
-
const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
|
|
38453
|
-
const projectSlug = ctx.projectSlug ?? null;
|
|
38454
|
-
return {
|
|
38455
|
-
assignmentDir: dir,
|
|
38456
|
-
projectSlug,
|
|
38457
|
-
assignmentSlug,
|
|
38458
|
-
id,
|
|
38459
|
-
standalone: projectSlug === null,
|
|
38460
|
-
workspaceGroup: null
|
|
38461
|
-
};
|
|
38462
|
-
}
|
|
38463
|
-
if (ctx.projectSlug && ctx.assignmentSlug) {
|
|
38464
|
-
if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
|
|
38465
|
-
throw new AssignmentTargetError(
|
|
38466
|
-
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
38467
|
-
);
|
|
38468
|
-
}
|
|
38469
|
-
const assignmentDir = resolve91(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
38470
|
-
const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
|
|
38471
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38472
|
-
throw new AssignmentTargetError(
|
|
38473
|
-
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
38474
|
-
);
|
|
38475
|
-
}
|
|
38476
|
-
const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
|
|
38477
|
-
return {
|
|
38478
|
-
assignmentDir,
|
|
38479
|
-
projectSlug: ctx.projectSlug,
|
|
38480
|
-
assignmentSlug: ctx.assignmentSlug,
|
|
38481
|
-
id,
|
|
38482
|
-
standalone: false,
|
|
38483
|
-
workspaceGroup: null
|
|
38484
|
-
};
|
|
38485
|
-
}
|
|
38486
|
-
throw new AssignmentTargetError(
|
|
38487
|
-
".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
|
|
38488
|
-
);
|
|
38489
|
-
}
|
|
38490
|
-
|
|
38491
|
-
// src/commands/capture.ts
|
|
38492
38941
|
init_paths();
|
|
38493
38942
|
init_fs();
|
|
38494
38943
|
|
|
38495
38944
|
// src/utils/screencapture.ts
|
|
38496
38945
|
import { spawn as spawn8 } from "child_process";
|
|
38497
|
-
import { mkdtemp as mkdtemp2, rm as rm10, stat as
|
|
38946
|
+
import { mkdtemp as mkdtemp2, rm as rm10, stat as stat12 } from "fs/promises";
|
|
38498
38947
|
import { tmpdir as tmpdir3 } from "os";
|
|
38499
38948
|
import { join as join22 } from "path";
|
|
38500
38949
|
function argsFor(mode, pngPath) {
|
|
@@ -38552,7 +39001,7 @@ async function captureScreenshot(mode) {
|
|
|
38552
39001
|
}
|
|
38553
39002
|
let size = 0;
|
|
38554
39003
|
try {
|
|
38555
|
-
size = (await
|
|
39004
|
+
size = (await stat12(pngPath)).size;
|
|
38556
39005
|
} catch {
|
|
38557
39006
|
throw new Error("screencapture exited 0 but produced no image.");
|
|
38558
39007
|
}
|
|
@@ -38568,7 +39017,7 @@ async function captureScreenshot(mode) {
|
|
|
38568
39017
|
|
|
38569
39018
|
// src/utils/asciinema.ts
|
|
38570
39019
|
import { spawn as spawn9 } from "child_process";
|
|
38571
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
39020
|
+
import { mkdtemp as mkdtemp3, readFile as readFile66, rm as rm11 } from "fs/promises";
|
|
38572
39021
|
import { tmpdir as tmpdir4 } from "os";
|
|
38573
39022
|
import { join as join23 } from "path";
|
|
38574
39023
|
var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
@@ -38631,7 +39080,7 @@ async function captureAsciinema(opts) {
|
|
|
38631
39080
|
}
|
|
38632
39081
|
throw err2;
|
|
38633
39082
|
}
|
|
38634
|
-
const text = await
|
|
39083
|
+
const text = await readFile66(castPath, "utf8").catch(() => null);
|
|
38635
39084
|
if (text === null) {
|
|
38636
39085
|
throw new Error(
|
|
38637
39086
|
`asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
|
|
@@ -38659,9 +39108,9 @@ async function captureAsciinema(opts) {
|
|
|
38659
39108
|
// src/utils/recording.ts
|
|
38660
39109
|
init_paths();
|
|
38661
39110
|
import { spawn as spawn10 } from "child_process";
|
|
38662
|
-
import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as
|
|
39111
|
+
import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as readFile67, rm as rm12, stat as stat13, unlink as unlink12, writeFile as writeFile14 } from "fs/promises";
|
|
38663
39112
|
import { tmpdir as tmpdir5 } from "os";
|
|
38664
|
-
import { join as join24, resolve as
|
|
39113
|
+
import { join as join24, resolve as resolve93 } from "path";
|
|
38665
39114
|
import { setTimeout as sleep } from "timers/promises";
|
|
38666
39115
|
function sigintPollIntervalMs() {
|
|
38667
39116
|
const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
|
|
@@ -38682,13 +39131,13 @@ function sigtermWaitMs() {
|
|
|
38682
39131
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
|
|
38683
39132
|
}
|
|
38684
39133
|
function pidfilePath() {
|
|
38685
|
-
return
|
|
39134
|
+
return resolve93(syntaurRoot(), "recording.pid");
|
|
38686
39135
|
}
|
|
38687
39136
|
function logPath2() {
|
|
38688
|
-
return
|
|
39137
|
+
return resolve93(syntaurRoot(), "recording.log");
|
|
38689
39138
|
}
|
|
38690
39139
|
function sidecarPath() {
|
|
38691
|
-
return
|
|
39140
|
+
return resolve93(syntaurRoot(), "recording.json");
|
|
38692
39141
|
}
|
|
38693
39142
|
function ffmpegArgs(device, fps, mp4Path) {
|
|
38694
39143
|
return [
|
|
@@ -38733,7 +39182,7 @@ async function acquirePidfile(pidfile) {
|
|
|
38733
39182
|
} catch (err2) {
|
|
38734
39183
|
if (err2.code !== "EEXIST") throw err2;
|
|
38735
39184
|
if (attempt === 1) throw err2;
|
|
38736
|
-
const existing = (await
|
|
39185
|
+
const existing = (await readFile67(pidfile, "utf-8").catch(() => "")).trim();
|
|
38737
39186
|
if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
|
|
38738
39187
|
const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
|
|
38739
39188
|
const parentPid = Number.parseInt(parentPidRaw, 10);
|
|
@@ -38835,7 +39284,7 @@ async function startRecording(input4) {
|
|
|
38835
39284
|
logHandle = null;
|
|
38836
39285
|
if (warmupMs > 0) await sleep(warmupMs);
|
|
38837
39286
|
if (!await isProcessAlive(pid)) {
|
|
38838
|
-
const tail = await
|
|
39287
|
+
const tail = await readFile67(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
|
|
38839
39288
|
acquiredPid = null;
|
|
38840
39289
|
throw new Error(
|
|
38841
39290
|
`ffmpeg exited during startup \u2014 likely macOS Screen Recording permission missing. Grant access to your terminal in System Settings \u2192 Privacy & Security \u2192 Screen Recording, then retry. Log: ${log}
|
|
@@ -38892,7 +39341,7 @@ ${tail}`
|
|
|
38892
39341
|
async function stopRecording() {
|
|
38893
39342
|
const pidfile = pidfilePath();
|
|
38894
39343
|
const sidecar = sidecarPath();
|
|
38895
|
-
const pidRaw = await
|
|
39344
|
+
const pidRaw = await readFile67(pidfile, "utf-8").catch(() => null);
|
|
38896
39345
|
if (pidRaw === null) {
|
|
38897
39346
|
throw new Error(
|
|
38898
39347
|
`No active recording found (no pidfile at ${pidfile}). Did you run --start?`
|
|
@@ -38902,7 +39351,7 @@ async function stopRecording() {
|
|
|
38902
39351
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
38903
39352
|
throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
|
|
38904
39353
|
}
|
|
38905
|
-
const sidecarRaw = await
|
|
39354
|
+
const sidecarRaw = await readFile67(sidecar, "utf-8").catch(() => null);
|
|
38906
39355
|
if (sidecarRaw === null) {
|
|
38907
39356
|
throw new Error(
|
|
38908
39357
|
`No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
|
|
@@ -38947,7 +39396,7 @@ async function stopRecording() {
|
|
|
38947
39396
|
if (alive) {
|
|
38948
39397
|
throw new Error(`ffmpeg (PID ${pid}) refused to die after SIGKILL`);
|
|
38949
39398
|
}
|
|
38950
|
-
const st = await
|
|
39399
|
+
const st = await stat13(sidecarData.mp4Path).catch(() => null);
|
|
38951
39400
|
if (st === null || st.size === 0) {
|
|
38952
39401
|
await unlink12(pidfile).catch(() => {
|
|
38953
39402
|
});
|
|
@@ -38967,7 +39416,7 @@ async function stopRecording() {
|
|
|
38967
39416
|
// src/db/proof-db.ts
|
|
38968
39417
|
init_paths();
|
|
38969
39418
|
import Database6 from "better-sqlite3";
|
|
38970
|
-
import { resolve as
|
|
39419
|
+
import { resolve as resolve94 } from "path";
|
|
38971
39420
|
var db5 = null;
|
|
38972
39421
|
var PROOF_SCHEMA_VERSION = "1";
|
|
38973
39422
|
var SCHEMA_SQL5 = `
|
|
@@ -38987,7 +39436,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
38987
39436
|
`;
|
|
38988
39437
|
function initProofDb(dbPath) {
|
|
38989
39438
|
if (db5) return db5;
|
|
38990
|
-
const finalPath = dbPath ??
|
|
39439
|
+
const finalPath = dbPath ?? resolve94(syntaurRoot(), "syntaur.db");
|
|
38991
39440
|
db5 = new Database6(finalPath);
|
|
38992
39441
|
db5.pragma("journal_mode = WAL");
|
|
38993
39442
|
db5.exec(SCHEMA_SQL5);
|
|
@@ -39035,7 +39484,7 @@ function listArtifactsByAssignment(assignmentId) {
|
|
|
39035
39484
|
|
|
39036
39485
|
// src/utils/transcribers/elevenlabs.ts
|
|
39037
39486
|
import { spawn as spawn11 } from "child_process";
|
|
39038
|
-
import { mkdtemp as mkdtemp5, readFile as
|
|
39487
|
+
import { mkdtemp as mkdtemp5, readFile as readFile68, rm as rm13 } from "fs/promises";
|
|
39039
39488
|
import { tmpdir as tmpdir6 } from "os";
|
|
39040
39489
|
import { join as join25 } from "path";
|
|
39041
39490
|
var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
|
|
@@ -39099,7 +39548,7 @@ async function extractAudio(videoAbsPath, wavOut) {
|
|
|
39099
39548
|
throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
|
|
39100
39549
|
}
|
|
39101
39550
|
async function callScribe(wavPath, apiKey, opts) {
|
|
39102
|
-
const audio = await
|
|
39551
|
+
const audio = await readFile68(wavPath);
|
|
39103
39552
|
const form = new FormData();
|
|
39104
39553
|
form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
|
|
39105
39554
|
form.set("model_id", "scribe_v1");
|
|
@@ -39415,7 +39864,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39415
39864
|
});
|
|
39416
39865
|
}
|
|
39417
39866
|
if (options.file) {
|
|
39418
|
-
const expanded = options.file.startsWith("~/") ?
|
|
39867
|
+
const expanded = options.file.startsWith("~/") ? resolve95(process.env.HOME ?? "", options.file.slice(2)) : resolve95(options.file);
|
|
39419
39868
|
if (!await fileExists(expanded)) {
|
|
39420
39869
|
throw new Error(`--file does not exist: ${options.file}`);
|
|
39421
39870
|
}
|
|
@@ -39427,7 +39876,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39427
39876
|
`--file is unreadable: ${options.file} (${e instanceof Error ? e.message : String(e)})`
|
|
39428
39877
|
);
|
|
39429
39878
|
}
|
|
39430
|
-
const st = await
|
|
39879
|
+
const st = await stat14(real);
|
|
39431
39880
|
if (!st.isFile()) {
|
|
39432
39881
|
throw new Error(`--file is not a regular file: ${options.file}`);
|
|
39433
39882
|
}
|
|
@@ -39456,7 +39905,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39456
39905
|
}
|
|
39457
39906
|
initProofDb();
|
|
39458
39907
|
const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
|
|
39459
|
-
const destDir =
|
|
39908
|
+
const destDir = resolve95(proofDir(resolved.assignmentDir), subdir);
|
|
39460
39909
|
if (resolvedSource) await mkdir12(destDir, { recursive: true });
|
|
39461
39910
|
const ext = resolvedSource ? extensionForKind(kind) : null;
|
|
39462
39911
|
let id = null;
|
|
@@ -39465,7 +39914,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39465
39914
|
let lastErr = null;
|
|
39466
39915
|
for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
|
|
39467
39916
|
const candidate = generateArtifactId();
|
|
39468
|
-
const candidateAbsPath = resolvedSource && ext ?
|
|
39917
|
+
const candidateAbsPath = resolvedSource && ext ? resolve95(destDir, `${candidate}.${ext}`) : null;
|
|
39469
39918
|
const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
|
|
39470
39919
|
try {
|
|
39471
39920
|
insertArtifact({
|
|
@@ -39509,7 +39958,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39509
39958
|
}
|
|
39510
39959
|
}
|
|
39511
39960
|
if (options.transcribe && kind === "video" && absPath && id) {
|
|
39512
|
-
const sidecarPath2 =
|
|
39961
|
+
const sidecarPath2 = resolve95(destDir, `${id}.transcript.md`);
|
|
39513
39962
|
if (existsSync7(sidecarPath2)) {
|
|
39514
39963
|
console.warn(
|
|
39515
39964
|
`transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
|
|
@@ -39541,7 +39990,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39541
39990
|
const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
|
|
39542
39991
|
console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
|
|
39543
39992
|
if (relativeFilePath) {
|
|
39544
|
-
console.log(` file: ${
|
|
39993
|
+
console.log(` file: ${resolve95(resolved.assignmentDir, relativeFilePath)}`);
|
|
39545
39994
|
}
|
|
39546
39995
|
} catch (err2) {
|
|
39547
39996
|
if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
|
|
@@ -39564,8 +40013,8 @@ async function captureCommand(target, options = {}) {
|
|
|
39564
40013
|
|
|
39565
40014
|
// src/commands/proof.ts
|
|
39566
40015
|
import { Command as Command6 } from "commander";
|
|
39567
|
-
import { readFile as
|
|
39568
|
-
import { resolve as
|
|
40016
|
+
import { readFile as readFile69, writeFile as writeFile16, rename as rename10, stat as stat15 } from "fs/promises";
|
|
40017
|
+
import { resolve as resolve96, relative as relative5, isAbsolute as isAbsolute12, dirname as dirname27 } from "path";
|
|
39569
40018
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
39570
40019
|
|
|
39571
40020
|
// src/utils/acceptance-criteria-parse.ts
|
|
@@ -39805,11 +40254,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
|
|
|
39805
40254
|
|
|
39806
40255
|
// src/commands/proof.ts
|
|
39807
40256
|
async function readAssignmentMeta(assignmentDir) {
|
|
39808
|
-
const path =
|
|
40257
|
+
const path = resolve96(assignmentDir, "assignment.md");
|
|
39809
40258
|
if (!await fileExists(path)) {
|
|
39810
40259
|
return { title: "", body: "" };
|
|
39811
40260
|
}
|
|
39812
|
-
const content = await
|
|
40261
|
+
const content = await readFile69(path, "utf-8");
|
|
39813
40262
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
39814
40263
|
let title = "";
|
|
39815
40264
|
if (fmMatch) {
|
|
@@ -39856,7 +40305,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
39856
40305
|
for (const r of rows) {
|
|
39857
40306
|
if (!r.file_path) continue;
|
|
39858
40307
|
if (r.kind !== "http" && r.kind !== "text") continue;
|
|
39859
|
-
const abs =
|
|
40308
|
+
const abs = resolve96(assignmentDir, r.file_path);
|
|
39860
40309
|
if (!isWithin(proofRoot, abs)) {
|
|
39861
40310
|
out.set(r.file_path, null);
|
|
39862
40311
|
continue;
|
|
@@ -39865,13 +40314,13 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
39865
40314
|
out.set(r.file_path, null);
|
|
39866
40315
|
continue;
|
|
39867
40316
|
}
|
|
39868
|
-
const st = await
|
|
40317
|
+
const st = await stat15(abs);
|
|
39869
40318
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) {
|
|
39870
40319
|
out.set(r.file_path, null);
|
|
39871
40320
|
continue;
|
|
39872
40321
|
}
|
|
39873
40322
|
try {
|
|
39874
|
-
out.set(r.file_path, await
|
|
40323
|
+
out.set(r.file_path, await readFile69(abs, "utf-8"));
|
|
39875
40324
|
} catch {
|
|
39876
40325
|
out.set(r.file_path, null);
|
|
39877
40326
|
}
|
|
@@ -39883,14 +40332,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
|
|
|
39883
40332
|
const proofRoot = proofDir(assignmentDir);
|
|
39884
40333
|
for (const r of rows) {
|
|
39885
40334
|
if (r.kind !== "video" || !r.file_path) continue;
|
|
39886
|
-
const videoAbs =
|
|
39887
|
-
const sidecar =
|
|
40335
|
+
const videoAbs = resolve96(assignmentDir, r.file_path);
|
|
40336
|
+
const sidecar = resolve96(dirname27(videoAbs), `${r.id}.transcript.md`);
|
|
39888
40337
|
if (!isWithin(proofRoot, sidecar)) continue;
|
|
39889
40338
|
if (!await fileExists(sidecar)) continue;
|
|
39890
|
-
const st = await
|
|
40339
|
+
const st = await stat15(sidecar);
|
|
39891
40340
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
|
|
39892
40341
|
try {
|
|
39893
|
-
out.set(r.id, await
|
|
40342
|
+
out.set(r.id, await readFile69(sidecar, "utf-8"));
|
|
39894
40343
|
} catch {
|
|
39895
40344
|
}
|
|
39896
40345
|
}
|
|
@@ -39924,8 +40373,8 @@ async function proofBuildCommand(target, options = {}) {
|
|
|
39924
40373
|
};
|
|
39925
40374
|
const md = renderProofMarkdown(renderParams);
|
|
39926
40375
|
const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
|
|
39927
|
-
const mdPath =
|
|
39928
|
-
const htmlPath =
|
|
40376
|
+
const mdPath = resolve96(resolved.assignmentDir, "proof.md");
|
|
40377
|
+
const htmlPath = resolve96(resolved.assignmentDir, "proof.html");
|
|
39929
40378
|
await atomicWrite(mdPath, md);
|
|
39930
40379
|
await atomicWrite(htmlPath, html);
|
|
39931
40380
|
console.log(`Wrote ${htmlPath}`);
|
|
@@ -40468,7 +40917,7 @@ function isScheduledSessionLive(sessionId, launchPid) {
|
|
|
40468
40917
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
40469
40918
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2, realpathSync as realpathSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
40470
40919
|
import { homedir as homedir16, userInfo } from "os";
|
|
40471
|
-
import { dirname as dirname28, join as join26, resolve as
|
|
40920
|
+
import { dirname as dirname28, join as join26, resolve as resolve97 } from "path";
|
|
40472
40921
|
var LAUNCH_AGENT_LABEL = "com.syntaur.schedule.tick";
|
|
40473
40922
|
var LaunchAgentRefusalError = class extends Error {
|
|
40474
40923
|
constructor(message) {
|
|
@@ -40522,7 +40971,7 @@ function absolutize(p) {
|
|
|
40522
40971
|
try {
|
|
40523
40972
|
return realpathSync4(p);
|
|
40524
40973
|
} catch {
|
|
40525
|
-
return
|
|
40974
|
+
return resolve97(p);
|
|
40526
40975
|
}
|
|
40527
40976
|
}
|
|
40528
40977
|
function defaultRun(command, args) {
|
|
@@ -40583,7 +41032,7 @@ function uninstallLaunchAgent(deps = {}) {
|
|
|
40583
41032
|
// src/commands/schedule.ts
|
|
40584
41033
|
init_timestamp();
|
|
40585
41034
|
var DURATION_REGEX2 = /^(\d+)\s*(s|m|h|d)?$/i;
|
|
40586
|
-
function
|
|
41035
|
+
function parseDurationMs2(input4) {
|
|
40587
41036
|
const m = DURATION_REGEX2.exec(input4.trim());
|
|
40588
41037
|
if (!m) throw new Error(`invalid duration "${input4}" \u2014 use e.g. 30s, 5m, 2h, 1d`);
|
|
40589
41038
|
const n = Number.parseInt(m[1], 10);
|
|
@@ -40604,7 +41053,7 @@ function validateTerminalChoice(value) {
|
|
|
40604
41053
|
function buildTrigger(opts) {
|
|
40605
41054
|
const chosen = [];
|
|
40606
41055
|
if (opts.at) chosen.push({ kind: "at", at: opts.at });
|
|
40607
|
-
if (opts.in) chosen.push({ kind: "in", durationMs:
|
|
41056
|
+
if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs2(opts.in), anchorIso: nowTimestamp() });
|
|
40608
41057
|
if (opts.cron) {
|
|
40609
41058
|
try {
|
|
40610
41059
|
new Cron2(opts.cron).nextRun();
|
|
@@ -40672,7 +41121,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
|
|
|
40672
41121
|
const unattended = !opts.interactive;
|
|
40673
41122
|
if (unattended) assertUnattendedTerminalSupported(terminal);
|
|
40674
41123
|
const limits = defaultLimits();
|
|
40675
|
-
if (opts.maxRuntime) limits.maxRuntimeMs =
|
|
41124
|
+
if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs2(opts.maxRuntime);
|
|
40676
41125
|
if (opts.maxLaunchesPerDay) {
|
|
40677
41126
|
const n = Number(opts.maxLaunchesPerDay);
|
|
40678
41127
|
if (!Number.isInteger(n) || n <= 0) {
|
|
@@ -40680,7 +41129,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
|
|
|
40680
41129
|
}
|
|
40681
41130
|
limits.maxLaunchesPerDay = n;
|
|
40682
41131
|
}
|
|
40683
|
-
if (opts.cooldown) limits.cooldownMs =
|
|
41132
|
+
if (opts.cooldown) limits.cooldownMs = parseDurationMs2(opts.cooldown);
|
|
40684
41133
|
const now = nowTimestamp();
|
|
40685
41134
|
const job = {
|
|
40686
41135
|
id: newJobId(),
|
|
@@ -40898,8 +41347,8 @@ init_slug();
|
|
|
40898
41347
|
init_timestamp();
|
|
40899
41348
|
init_assignment_resolver();
|
|
40900
41349
|
init_assignment_todos();
|
|
40901
|
-
import { resolve as
|
|
40902
|
-
import { readFile as
|
|
41350
|
+
import { resolve as resolve98 } from "path";
|
|
41351
|
+
import { readFile as readFile70 } from "fs/promises";
|
|
40903
41352
|
async function requestCommand(target, text, options = {}) {
|
|
40904
41353
|
if (!text || !text.trim()) {
|
|
40905
41354
|
throw new Error("Request text cannot be empty.");
|
|
@@ -40915,7 +41364,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40915
41364
|
if (!isValidSlug(target)) {
|
|
40916
41365
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
40917
41366
|
}
|
|
40918
|
-
assignmentDir =
|
|
41367
|
+
assignmentDir = resolve98(baseDir, options.project, "assignments", target);
|
|
40919
41368
|
targetRef = target;
|
|
40920
41369
|
} else {
|
|
40921
41370
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -40925,12 +41374,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40925
41374
|
assignmentDir = resolved.assignmentDir;
|
|
40926
41375
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
40927
41376
|
}
|
|
40928
|
-
const assignmentMdPath2 =
|
|
41377
|
+
const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
|
|
40929
41378
|
if (!await fileExists(assignmentMdPath2)) {
|
|
40930
41379
|
throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
|
|
40931
41380
|
}
|
|
40932
41381
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
40933
|
-
let content = await
|
|
41382
|
+
let content = await readFile70(assignmentMdPath2, "utf-8");
|
|
40934
41383
|
content = appendTodosToAssignmentBody(content, [
|
|
40935
41384
|
{ description: `${text.trim()} (from: ${source})` }
|
|
40936
41385
|
]);
|
|
@@ -40943,14 +41392,15 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40943
41392
|
init_fs();
|
|
40944
41393
|
init_paths();
|
|
40945
41394
|
init_config2();
|
|
41395
|
+
init_recompute();
|
|
40946
41396
|
import { Command as Command10 } from "commander";
|
|
40947
|
-
import { readFile as
|
|
40948
|
-
import { resolve as
|
|
41397
|
+
import { readFile as readFile71, readdir as readdir34 } from "fs/promises";
|
|
41398
|
+
import { resolve as resolve99 } from "path";
|
|
40949
41399
|
async function readContextAssignmentDir(cwd) {
|
|
40950
|
-
const path =
|
|
41400
|
+
const path = resolve99(cwd, ".syntaur", "context.json");
|
|
40951
41401
|
if (!await fileExists(path)) return null;
|
|
40952
41402
|
try {
|
|
40953
|
-
const raw2 = await
|
|
41403
|
+
const raw2 = await readFile71(path, "utf-8");
|
|
40954
41404
|
const ctx = JSON.parse(raw2);
|
|
40955
41405
|
if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
|
|
40956
41406
|
return ctx.assignmentDir;
|
|
@@ -40964,9 +41414,9 @@ async function resolveAssignmentDir(opts) {
|
|
|
40964
41414
|
const cwd = opts.cwd ?? process.cwd();
|
|
40965
41415
|
if (opts.assignment) {
|
|
40966
41416
|
if (opts.project) {
|
|
40967
|
-
return
|
|
41417
|
+
return resolve99((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
|
|
40968
41418
|
}
|
|
40969
|
-
return
|
|
41419
|
+
return resolve99(assignmentsDir(), opts.assignment);
|
|
40970
41420
|
}
|
|
40971
41421
|
const fromCtx = await readContextAssignmentDir(cwd);
|
|
40972
41422
|
if (fromCtx) return fromCtx;
|
|
@@ -41162,17 +41612,17 @@ async function runPlanCreate(options) {
|
|
|
41162
41612
|
if (!await fileExists(assignmentDir)) {
|
|
41163
41613
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
41164
41614
|
}
|
|
41165
|
-
const assignmentMdPath2 =
|
|
41615
|
+
const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
|
|
41166
41616
|
if (!await fileExists(assignmentMdPath2)) {
|
|
41167
41617
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
41168
41618
|
}
|
|
41169
|
-
const planPath =
|
|
41619
|
+
const planPath = resolve99(assignmentDir, "plan.md");
|
|
41170
41620
|
if (await fileExists(planPath) && !options.force) {
|
|
41171
41621
|
throw new Error(
|
|
41172
41622
|
"plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
|
|
41173
41623
|
);
|
|
41174
41624
|
}
|
|
41175
|
-
const assignmentMd = await
|
|
41625
|
+
const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
|
|
41176
41626
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
41177
41627
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
41178
41628
|
await writeFileForce(planPath, buildInitialPlanStub(slug));
|
|
@@ -41186,13 +41636,14 @@ async function runPlanCreate(options) {
|
|
|
41186
41636
|
}
|
|
41187
41637
|
console.log(`Created ${planPath}`);
|
|
41188
41638
|
if (appended > 0) console.log(`Appended ${appended} plan todo(s) to assignment.md.`);
|
|
41639
|
+
await recomputeAssignmentDir(assignmentDir, "plan-create", null);
|
|
41189
41640
|
}
|
|
41190
41641
|
async function runPlanVersion(options) {
|
|
41191
41642
|
const assignmentDir = await resolveAssignmentDir(options);
|
|
41192
41643
|
if (!await fileExists(assignmentDir)) {
|
|
41193
41644
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
41194
41645
|
}
|
|
41195
|
-
const assignmentMdPath2 =
|
|
41646
|
+
const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
|
|
41196
41647
|
if (!await fileExists(assignmentMdPath2)) {
|
|
41197
41648
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
41198
41649
|
}
|
|
@@ -41204,15 +41655,15 @@ async function runPlanVersion(options) {
|
|
|
41204
41655
|
}
|
|
41205
41656
|
const current = planFiles[planFiles.length - 1];
|
|
41206
41657
|
const next = nextPlanFileName(current.version);
|
|
41207
|
-
const newPath =
|
|
41658
|
+
const newPath = resolve99(assignmentDir, next.fileName);
|
|
41208
41659
|
if (await fileExists(newPath) && !options.force) {
|
|
41209
41660
|
throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
|
|
41210
41661
|
}
|
|
41211
|
-
const assignmentMd = await
|
|
41662
|
+
const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
|
|
41212
41663
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
41213
41664
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
41214
|
-
const oldPlanPath =
|
|
41215
|
-
const oldPlanContent = await
|
|
41665
|
+
const oldPlanPath = resolve99(assignmentDir, current.fileName);
|
|
41666
|
+
const oldPlanContent = await readFile71(oldPlanPath, "utf-8");
|
|
41216
41667
|
const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
|
|
41217
41668
|
const carriedTodos = extractUncheckedTodos(oldBody);
|
|
41218
41669
|
const stub = buildNewPlanStub({
|
|
@@ -41233,6 +41684,7 @@ async function runPlanVersion(options) {
|
|
|
41233
41684
|
);
|
|
41234
41685
|
console.log(`Path: ${newPath}`);
|
|
41235
41686
|
console.log(`Carried forward: ${carriedTodos.length} unchecked task(s).`);
|
|
41687
|
+
await recomputeAssignmentDir(assignmentDir, "plan-version", null);
|
|
41236
41688
|
}
|
|
41237
41689
|
var planCommand = new Command10("plan").description("Manage plan files for the active assignment");
|
|
41238
41690
|
planCommand.command("create").description("Create the initial plan.md and append the plan todo-cycle to assignment.md ## Todos").option("--assignment <slug>", "Assignment slug (UUID for standalone). Defaults to .syntaur/context.json").option("--project <slug>", "Project slug. Required when --assignment is given for a project-nested assignment").option("--force", "Overwrite an existing plan.md").action(async (options) => {
|
|
@@ -41282,28 +41734,28 @@ init_cwd();
|
|
|
41282
41734
|
init_session_db();
|
|
41283
41735
|
init_agent_sessions();
|
|
41284
41736
|
import { Command as Command11 } from "commander";
|
|
41285
|
-
import { readFile as
|
|
41286
|
-
import { resolve as
|
|
41737
|
+
import { readFile as readFile72, readdir as readdir35, stat as stat16 } from "fs/promises";
|
|
41738
|
+
import { resolve as resolve100 } from "path";
|
|
41287
41739
|
async function readContext(cwd) {
|
|
41288
|
-
const path =
|
|
41740
|
+
const path = resolve100(cwd, ".syntaur", "context.json");
|
|
41289
41741
|
if (!await fileExists(path)) return null;
|
|
41290
41742
|
try {
|
|
41291
|
-
const raw2 = await
|
|
41743
|
+
const raw2 = await readFile72(path, "utf-8");
|
|
41292
41744
|
return JSON.parse(raw2);
|
|
41293
41745
|
} catch {
|
|
41294
41746
|
return null;
|
|
41295
41747
|
}
|
|
41296
41748
|
}
|
|
41297
41749
|
async function findLatestSessionSummary(assignmentDir) {
|
|
41298
|
-
const sessionsRoot =
|
|
41750
|
+
const sessionsRoot = resolve100(assignmentDir, "sessions");
|
|
41299
41751
|
if (!await fileExists(sessionsRoot)) return null;
|
|
41300
41752
|
const entries = await readdir35(sessionsRoot, { withFileTypes: true });
|
|
41301
41753
|
let best = null;
|
|
41302
41754
|
for (const entry of entries) {
|
|
41303
41755
|
if (!entry.isDirectory()) continue;
|
|
41304
|
-
const summaryPath =
|
|
41756
|
+
const summaryPath = resolve100(sessionsRoot, entry.name, "summary.md");
|
|
41305
41757
|
if (!await fileExists(summaryPath)) continue;
|
|
41306
|
-
const st = await
|
|
41758
|
+
const st = await stat16(summaryPath);
|
|
41307
41759
|
if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
|
|
41308
41760
|
best = { sessionId: entry.name, path: summaryPath, mtime: st.mtime };
|
|
41309
41761
|
}
|
|
@@ -41311,9 +41763,9 @@ async function findLatestSessionSummary(assignmentDir) {
|
|
|
41311
41763
|
return best;
|
|
41312
41764
|
}
|
|
41313
41765
|
async function findOpenHandoff(assignmentDir) {
|
|
41314
|
-
const handoffPath =
|
|
41766
|
+
const handoffPath = resolve100(assignmentDir, "handoff.md");
|
|
41315
41767
|
if (!await fileExists(handoffPath)) return null;
|
|
41316
|
-
const content = await
|
|
41768
|
+
const content = await readFile72(handoffPath, "utf-8");
|
|
41317
41769
|
const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
|
|
41318
41770
|
if (body.length === 0) return null;
|
|
41319
41771
|
if (/^<!--[\s\S]*-->$/.test(body)) return null;
|
|
@@ -41408,7 +41860,7 @@ async function resolveSaveTarget(options, cwd) {
|
|
|
41408
41860
|
let slug;
|
|
41409
41861
|
const ctx = await readContext(cwd);
|
|
41410
41862
|
if (options.assignment) {
|
|
41411
|
-
assignmentDir = options.project ?
|
|
41863
|
+
assignmentDir = options.project ? resolve100((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve100(assignmentsDir(), options.assignment);
|
|
41412
41864
|
slug = options.assignment;
|
|
41413
41865
|
} else {
|
|
41414
41866
|
if (!ctx?.assignmentDir) {
|
|
@@ -41465,21 +41917,21 @@ function extractCreated(content) {
|
|
|
41465
41917
|
}
|
|
41466
41918
|
async function runSessionSave(options, cwd = process.cwd(), body) {
|
|
41467
41919
|
const { assignmentDir, slug, sessionId } = await resolveSaveTarget(options, cwd);
|
|
41468
|
-
if (!await fileExists(
|
|
41920
|
+
if (!await fileExists(resolve100(assignmentDir, "assignment.md"))) {
|
|
41469
41921
|
throw new Error(`No assignment found at ${assignmentDir} (missing assignment.md).`);
|
|
41470
41922
|
}
|
|
41471
|
-
const sessionDir =
|
|
41472
|
-
const summaryPath =
|
|
41923
|
+
const sessionDir = resolve100(assignmentDir, "sessions", sessionId);
|
|
41924
|
+
const summaryPath = resolve100(sessionDir, "summary.md");
|
|
41473
41925
|
const now = nowTimestamp();
|
|
41474
41926
|
let created = now;
|
|
41475
41927
|
if (await fileExists(summaryPath)) {
|
|
41476
|
-
const existing = await
|
|
41928
|
+
const existing = await readFile72(summaryPath, "utf-8");
|
|
41477
41929
|
created = extractCreated(existing) ?? now;
|
|
41478
41930
|
}
|
|
41479
41931
|
let sectionBody2 = body;
|
|
41480
41932
|
if (sectionBody2 === void 0) {
|
|
41481
41933
|
if (options.fromFile) {
|
|
41482
|
-
sectionBody2 = await
|
|
41934
|
+
sectionBody2 = await readFile72(resolve100(cwd, options.fromFile), "utf-8");
|
|
41483
41935
|
} else {
|
|
41484
41936
|
sectionBody2 = await readStdin();
|
|
41485
41937
|
}
|
|
@@ -41515,7 +41967,7 @@ async function runSessionRegister(rawStdin, options = {}, deps = {}) {
|
|
|
41515
41967
|
if (!isSafeSessionId(sessionId) || !cwd) return result;
|
|
41516
41968
|
result.sessionId = sessionId;
|
|
41517
41969
|
const transcriptPath = payload.transcript_path ?? "";
|
|
41518
|
-
const contextPath =
|
|
41970
|
+
const contextPath = resolve100(cwd, ".syntaur", "context.json");
|
|
41519
41971
|
const hasContextFile = await fileExists(contextPath);
|
|
41520
41972
|
const ctx = hasContextFile ? await readContext(cwd) : null;
|
|
41521
41973
|
if (ctx) {
|
|
@@ -41661,8 +42113,8 @@ sessionCommand.command("resolve-id").description(
|
|
|
41661
42113
|
// src/commands/worktree.ts
|
|
41662
42114
|
init_git_worktree();
|
|
41663
42115
|
import { Command as Command12 } from "commander";
|
|
41664
|
-
import { readFile as
|
|
41665
|
-
import { resolve as
|
|
42116
|
+
import { readFile as readFile73 } from "fs/promises";
|
|
42117
|
+
import { resolve as resolve102 } from "path";
|
|
41666
42118
|
init_fs();
|
|
41667
42119
|
init_paths();
|
|
41668
42120
|
init_config2();
|
|
@@ -41671,12 +42123,12 @@ init_state_machine();
|
|
|
41671
42123
|
|
|
41672
42124
|
// src/utils/path-canon.ts
|
|
41673
42125
|
import { realpathSync as realpathSync5 } from "fs";
|
|
41674
|
-
import { resolve as
|
|
42126
|
+
import { resolve as resolve101 } from "path";
|
|
41675
42127
|
function canonicalPath(p) {
|
|
41676
42128
|
try {
|
|
41677
|
-
return realpathSync5(
|
|
42129
|
+
return realpathSync5(resolve101(p));
|
|
41678
42130
|
} catch {
|
|
41679
|
-
return
|
|
42131
|
+
return resolve101(p).replace(/\/+$/, "");
|
|
41680
42132
|
}
|
|
41681
42133
|
}
|
|
41682
42134
|
|
|
@@ -41704,10 +42156,10 @@ function countSessionsByPath(dbPath, paths) {
|
|
|
41704
42156
|
init_timestamp();
|
|
41705
42157
|
init_frontmatter();
|
|
41706
42158
|
async function readContext2(cwd) {
|
|
41707
|
-
const path =
|
|
42159
|
+
const path = resolve102(cwd, ".syntaur", "context.json");
|
|
41708
42160
|
if (!await fileExists(path)) return null;
|
|
41709
42161
|
try {
|
|
41710
|
-
return JSON.parse(await
|
|
42162
|
+
return JSON.parse(await readFile73(path, "utf-8"));
|
|
41711
42163
|
} catch {
|
|
41712
42164
|
return null;
|
|
41713
42165
|
}
|
|
@@ -41716,12 +42168,12 @@ async function resolveAssignmentPath2(opts) {
|
|
|
41716
42168
|
if (opts.assignment) {
|
|
41717
42169
|
if (opts.project) {
|
|
41718
42170
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
41719
|
-
return
|
|
42171
|
+
return resolve102(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
41720
42172
|
}
|
|
41721
|
-
return
|
|
42173
|
+
return resolve102(assignmentsDir(), opts.assignment, "assignment.md");
|
|
41722
42174
|
}
|
|
41723
42175
|
const ctx = await readContext2(opts.cwd);
|
|
41724
|
-
if (ctx?.assignmentDir) return
|
|
42176
|
+
if (ctx?.assignmentDir) return resolve102(ctx.assignmentDir, "assignment.md");
|
|
41725
42177
|
throw new Error(
|
|
41726
42178
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
41727
42179
|
);
|
|
@@ -41732,7 +42184,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
|
|
|
41732
42184
|
}
|
|
41733
42185
|
const repository = options.repository ?? cwd;
|
|
41734
42186
|
const parentBranch = options.parentBranch ?? "main";
|
|
41735
|
-
const worktreePath = options.worktreePath ??
|
|
42187
|
+
const worktreePath = options.worktreePath ?? resolve102(repository, ".worktrees", options.branch);
|
|
41736
42188
|
const assignmentPath = await resolveAssignmentPath2({
|
|
41737
42189
|
assignment: options.assignment,
|
|
41738
42190
|
project: options.project,
|
|
@@ -41762,7 +42214,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
|
|
|
41762
42214
|
if (!await fileExists(assignmentPath)) {
|
|
41763
42215
|
throw new Error(`Assignment file not found: ${assignmentPath}`);
|
|
41764
42216
|
}
|
|
41765
|
-
const original = await
|
|
42217
|
+
const original = await readFile73(assignmentPath, "utf-8");
|
|
41766
42218
|
const fm = parseAssignmentFrontmatter(original);
|
|
41767
42219
|
const repository = options.repository ?? fm.workspace.repository ?? void 0;
|
|
41768
42220
|
const worktreePath = fm.workspace.worktreePath ?? void 0;
|
|
@@ -41828,7 +42280,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
|
|
|
41828
42280
|
const owners = /* @__PURE__ */ new Map();
|
|
41829
42281
|
for (const entry of walk.withAssignmentMd) {
|
|
41830
42282
|
try {
|
|
41831
|
-
const content = await
|
|
42283
|
+
const content = await readFile73(resolve102(entry.assignmentDir, "assignment.md"), "utf-8");
|
|
41832
42284
|
const fm = parseAssignmentFrontmatter(content);
|
|
41833
42285
|
const wp = fm.workspace?.worktreePath;
|
|
41834
42286
|
if (!wp) continue;
|
|
@@ -41850,7 +42302,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
|
|
|
41850
42302
|
const repoTop = rt ? canonicalPath(rt) : null;
|
|
41851
42303
|
const cwdTop = ct ? canonicalPath(ct) : null;
|
|
41852
42304
|
const mainPath = entries.length > 0 ? canonicalPath(entries[0].worktreePath) : null;
|
|
41853
|
-
const dbPath =
|
|
42305
|
+
const dbPath = resolve102(syntaurRoot(), "syntaur.db");
|
|
41854
42306
|
const candidates = [];
|
|
41855
42307
|
for (const entry of entries) {
|
|
41856
42308
|
const canon = canonicalPath(entry.worktreePath);
|
|
@@ -42069,8 +42521,8 @@ worktreeCommand.command("gc").description(
|
|
|
42069
42521
|
// src/commands/open.ts
|
|
42070
42522
|
init_fs();
|
|
42071
42523
|
import { Command as Command13 } from "commander";
|
|
42072
|
-
import { readFile as
|
|
42073
|
-
import { resolve as
|
|
42524
|
+
import { readFile as readFile74 } from "fs/promises";
|
|
42525
|
+
import { resolve as resolve103 } from "path";
|
|
42074
42526
|
init_frontmatter();
|
|
42075
42527
|
init_config2();
|
|
42076
42528
|
init_paths();
|
|
@@ -42124,13 +42576,13 @@ function openInTerminal(path, config) {
|
|
|
42124
42576
|
// src/commands/open.ts
|
|
42125
42577
|
async function runOpen(assignmentArg, options) {
|
|
42126
42578
|
const resolved = options.id ? await resolveAssignmentTarget(options.id, {}) : await resolveAssignmentTarget(assignmentArg, { project: options.project });
|
|
42127
|
-
const assignmentPath =
|
|
42579
|
+
const assignmentPath = resolve103(resolved.assignmentDir, "assignment.md");
|
|
42128
42580
|
if (!await fileExists(assignmentPath)) {
|
|
42129
42581
|
throw new SyntaurError(`Assignment file not found: ${assignmentPath}`, {
|
|
42130
42582
|
remediation: "check the assignment slug or --id"
|
|
42131
42583
|
});
|
|
42132
42584
|
}
|
|
42133
|
-
const fm = parseAssignmentFrontmatter(await
|
|
42585
|
+
const fm = parseAssignmentFrontmatter(await readFile74(assignmentPath, "utf-8"));
|
|
42134
42586
|
const worktreePath = fm.workspace?.worktreePath;
|
|
42135
42587
|
if (!worktreePath) {
|
|
42136
42588
|
throw new SyntaurError("No worktree recorded for this assignment.", {
|
|
@@ -42195,14 +42647,14 @@ init_config2();
|
|
|
42195
42647
|
init_fs();
|
|
42196
42648
|
init_slug();
|
|
42197
42649
|
import { Command as Command14 } from "commander";
|
|
42198
|
-
import { resolve as
|
|
42199
|
-
import { readFile as
|
|
42650
|
+
import { resolve as resolve105 } from "path";
|
|
42651
|
+
import { readFile as readFile76, readdir as readdir37, rm as rm15 } from "fs/promises";
|
|
42200
42652
|
|
|
42201
42653
|
// src/utils/project-indexes.ts
|
|
42202
42654
|
init_parser();
|
|
42203
42655
|
init_fs();
|
|
42204
|
-
import { readdir as readdir36, readFile as
|
|
42205
|
-
import { resolve as
|
|
42656
|
+
import { readdir as readdir36, readFile as readFile75 } from "fs/promises";
|
|
42657
|
+
import { resolve as resolve104 } from "path";
|
|
42206
42658
|
function nowIso3() {
|
|
42207
42659
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
42208
42660
|
}
|
|
@@ -42221,7 +42673,7 @@ function joinList(items) {
|
|
|
42221
42673
|
return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
|
|
42222
42674
|
}
|
|
42223
42675
|
async function rebuildResourcesIndex(projectDir) {
|
|
42224
|
-
const dir =
|
|
42676
|
+
const dir = resolve104(projectDir, "resources");
|
|
42225
42677
|
await ensureDir(dir);
|
|
42226
42678
|
const files = await listSlugFiles(dir);
|
|
42227
42679
|
const slug = readProjectSlug(projectDir);
|
|
@@ -42237,7 +42689,7 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
42237
42689
|
lines.push("| Name | Category | Source | Related Assignments | Updated |");
|
|
42238
42690
|
lines.push("|------|----------|--------|---------------------|---------|");
|
|
42239
42691
|
for (const fileName of files) {
|
|
42240
|
-
const content = await
|
|
42692
|
+
const content = await readFile75(resolve104(dir, fileName), "utf-8");
|
|
42241
42693
|
const parsed = parseResource(content);
|
|
42242
42694
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
42243
42695
|
const name = parsed.name || slugBase;
|
|
@@ -42247,12 +42699,12 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
42247
42699
|
);
|
|
42248
42700
|
}
|
|
42249
42701
|
lines.push("");
|
|
42250
|
-
const indexPath =
|
|
42702
|
+
const indexPath = resolve104(dir, "_index.md");
|
|
42251
42703
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
42252
42704
|
return { total: files.length, path: indexPath };
|
|
42253
42705
|
}
|
|
42254
42706
|
async function rebuildMemoriesIndex(projectDir) {
|
|
42255
|
-
const dir =
|
|
42707
|
+
const dir = resolve104(projectDir, "memories");
|
|
42256
42708
|
await ensureDir(dir);
|
|
42257
42709
|
const files = await listSlugFiles(dir);
|
|
42258
42710
|
const slug = readProjectSlug(projectDir);
|
|
@@ -42268,7 +42720,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
42268
42720
|
lines.push("| Name | Source | Scope | Source Assignment | Updated |");
|
|
42269
42721
|
lines.push("|------|--------|-------|-------------------|---------|");
|
|
42270
42722
|
for (const fileName of files) {
|
|
42271
|
-
const content = await
|
|
42723
|
+
const content = await readFile75(resolve104(dir, fileName), "utf-8");
|
|
42272
42724
|
const parsed = parseMemory(content);
|
|
42273
42725
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
42274
42726
|
const name = parsed.name || slugBase;
|
|
@@ -42278,7 +42730,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
42278
42730
|
);
|
|
42279
42731
|
}
|
|
42280
42732
|
lines.push("");
|
|
42281
|
-
const indexPath =
|
|
42733
|
+
const indexPath = resolve104(dir, "_index.md");
|
|
42282
42734
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
42283
42735
|
return { total: files.length, path: indexPath };
|
|
42284
42736
|
}
|
|
@@ -42342,8 +42794,8 @@ ${opts.body ?? "<!-- Add notes about this resource here. -->"}
|
|
|
42342
42794
|
}
|
|
42343
42795
|
async function resolveProjectDir(project) {
|
|
42344
42796
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
42345
|
-
const projectDir =
|
|
42346
|
-
if (!await fileExists(
|
|
42797
|
+
const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
|
|
42798
|
+
if (!await fileExists(resolve105(projectDir, "project.md"))) {
|
|
42347
42799
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
42348
42800
|
}
|
|
42349
42801
|
return projectDir;
|
|
@@ -42356,7 +42808,7 @@ async function runResourceAdd(options) {
|
|
|
42356
42808
|
if (!isValidSlug(slug)) {
|
|
42357
42809
|
throw new Error(`Invalid resource slug: "${slug}".`);
|
|
42358
42810
|
}
|
|
42359
|
-
const filePath =
|
|
42811
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42360
42812
|
if (await fileExists(filePath) && !options.force) {
|
|
42361
42813
|
throw new Error(
|
|
42362
42814
|
`Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -42373,7 +42825,7 @@ async function runResourceAdd(options) {
|
|
|
42373
42825
|
return { filePath, indexPath, total };
|
|
42374
42826
|
}
|
|
42375
42827
|
async function listResourceSlugs(projectDir) {
|
|
42376
|
-
const dir =
|
|
42828
|
+
const dir = resolve105(projectDir, "resources");
|
|
42377
42829
|
if (!await fileExists(dir)) return [];
|
|
42378
42830
|
const entries = await readdir37(dir, { withFileTypes: true });
|
|
42379
42831
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
@@ -42383,16 +42835,16 @@ async function runResourceList(project) {
|
|
|
42383
42835
|
const slugs = await listResourceSlugs(projectDir);
|
|
42384
42836
|
const out = [];
|
|
42385
42837
|
for (const slug of slugs) {
|
|
42386
|
-
const parsed = parseResource(await
|
|
42838
|
+
const parsed = parseResource(await readFile76(resolve105(projectDir, "resources", `${slug}.md`), "utf-8"));
|
|
42387
42839
|
out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
|
|
42388
42840
|
}
|
|
42389
42841
|
return out;
|
|
42390
42842
|
}
|
|
42391
42843
|
async function runResourceShow(project, slug) {
|
|
42392
42844
|
const projectDir = await resolveProjectDir(project);
|
|
42393
|
-
const filePath =
|
|
42845
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42394
42846
|
if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
|
|
42395
|
-
const parsed = parseResource(await
|
|
42847
|
+
const parsed = parseResource(await readFile76(filePath, "utf-8"));
|
|
42396
42848
|
return {
|
|
42397
42849
|
slug,
|
|
42398
42850
|
name: parsed.name,
|
|
@@ -42406,14 +42858,14 @@ async function runResourceShow(project, slug) {
|
|
|
42406
42858
|
}
|
|
42407
42859
|
async function runResourceUpdate(slug, options) {
|
|
42408
42860
|
const projectDir = await resolveProjectDir(options.project);
|
|
42409
|
-
const filePath =
|
|
42861
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42410
42862
|
if (!await fileExists(filePath)) {
|
|
42411
42863
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
42412
42864
|
}
|
|
42413
42865
|
if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
|
|
42414
42866
|
throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
|
|
42415
42867
|
}
|
|
42416
|
-
const original = await
|
|
42868
|
+
const original = await readFile76(filePath, "utf-8");
|
|
42417
42869
|
const content = editResourceFrontmatter(original, {
|
|
42418
42870
|
name: options.name,
|
|
42419
42871
|
category: options.category,
|
|
@@ -42426,7 +42878,7 @@ async function runResourceUpdate(slug, options) {
|
|
|
42426
42878
|
}
|
|
42427
42879
|
async function runResourceRemove(slug, options) {
|
|
42428
42880
|
const projectDir = await resolveProjectDir(options.project);
|
|
42429
|
-
const filePath =
|
|
42881
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42430
42882
|
if (!await fileExists(filePath)) {
|
|
42431
42883
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
42432
42884
|
}
|
|
@@ -42509,8 +42961,8 @@ init_config2();
|
|
|
42509
42961
|
init_fs();
|
|
42510
42962
|
init_slug();
|
|
42511
42963
|
import { Command as Command15 } from "commander";
|
|
42512
|
-
import { resolve as
|
|
42513
|
-
import { readFile as
|
|
42964
|
+
import { resolve as resolve106 } from "path";
|
|
42965
|
+
import { readFile as readFile77, readdir as readdir38, rm as rm16 } from "fs/promises";
|
|
42514
42966
|
init_parser();
|
|
42515
42967
|
function nowIso5() {
|
|
42516
42968
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -42574,8 +43026,8 @@ ${opts.body ?? "<!-- Capture the load-bearing context for this memory here. -->"
|
|
|
42574
43026
|
}
|
|
42575
43027
|
async function resolveProjectDir2(project) {
|
|
42576
43028
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
42577
|
-
const projectDir =
|
|
42578
|
-
if (!await fileExists(
|
|
43029
|
+
const projectDir = resolve106((await readConfig()).defaultProjectDir, project);
|
|
43030
|
+
if (!await fileExists(resolve106(projectDir, "project.md"))) {
|
|
42579
43031
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
42580
43032
|
}
|
|
42581
43033
|
return projectDir;
|
|
@@ -42588,7 +43040,7 @@ async function runMemoryAdd(options) {
|
|
|
42588
43040
|
if (!isValidSlug(slug)) {
|
|
42589
43041
|
throw new Error(`Invalid memory slug: "${slug}".`);
|
|
42590
43042
|
}
|
|
42591
|
-
const filePath =
|
|
43043
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42592
43044
|
if (await fileExists(filePath) && !options.force) {
|
|
42593
43045
|
throw new Error(
|
|
42594
43046
|
`Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -42606,7 +43058,7 @@ async function runMemoryAdd(options) {
|
|
|
42606
43058
|
return { filePath, indexPath, total };
|
|
42607
43059
|
}
|
|
42608
43060
|
async function listMemorySlugs(projectDir) {
|
|
42609
|
-
const dir =
|
|
43061
|
+
const dir = resolve106(projectDir, "memories");
|
|
42610
43062
|
if (!await fileExists(dir)) return [];
|
|
42611
43063
|
const entries = await readdir38(dir, { withFileTypes: true });
|
|
42612
43064
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
@@ -42616,16 +43068,16 @@ async function runMemoryList(project) {
|
|
|
42616
43068
|
const slugs = await listMemorySlugs(projectDir);
|
|
42617
43069
|
const out = [];
|
|
42618
43070
|
for (const slug of slugs) {
|
|
42619
|
-
const parsed = parseMemory(await
|
|
43071
|
+
const parsed = parseMemory(await readFile77(resolve106(projectDir, "memories", `${slug}.md`), "utf-8"));
|
|
42620
43072
|
out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
|
|
42621
43073
|
}
|
|
42622
43074
|
return out;
|
|
42623
43075
|
}
|
|
42624
43076
|
async function runMemoryShow(project, slug) {
|
|
42625
43077
|
const projectDir = await resolveProjectDir2(project);
|
|
42626
|
-
const filePath =
|
|
43078
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42627
43079
|
if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
|
|
42628
|
-
const parsed = parseMemory(await
|
|
43080
|
+
const parsed = parseMemory(await readFile77(filePath, "utf-8"));
|
|
42629
43081
|
return {
|
|
42630
43082
|
slug,
|
|
42631
43083
|
name: parsed.name,
|
|
@@ -42640,7 +43092,7 @@ async function runMemoryShow(project, slug) {
|
|
|
42640
43092
|
}
|
|
42641
43093
|
async function runMemoryUpdate(slug, options) {
|
|
42642
43094
|
const projectDir = await resolveProjectDir2(options.project);
|
|
42643
|
-
const filePath =
|
|
43095
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42644
43096
|
if (!await fileExists(filePath)) {
|
|
42645
43097
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
42646
43098
|
}
|
|
@@ -42649,7 +43101,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
42649
43101
|
"Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
|
|
42650
43102
|
);
|
|
42651
43103
|
}
|
|
42652
|
-
const original = await
|
|
43104
|
+
const original = await readFile77(filePath, "utf-8");
|
|
42653
43105
|
const content = editMemoryFrontmatter(original, {
|
|
42654
43106
|
name: options.name,
|
|
42655
43107
|
source: options.source,
|
|
@@ -42663,7 +43115,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
42663
43115
|
}
|
|
42664
43116
|
async function runMemoryRemove(slug, options) {
|
|
42665
43117
|
const projectDir = await resolveProjectDir2(options.project);
|
|
42666
|
-
const filePath =
|
|
43118
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42667
43119
|
if (!await fileExists(filePath)) {
|
|
42668
43120
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
42669
43121
|
}
|
|
@@ -42752,8 +43204,8 @@ init_derive();
|
|
|
42752
43204
|
init_recompute();
|
|
42753
43205
|
init_query();
|
|
42754
43206
|
import { Command as Command16 } from "commander";
|
|
42755
|
-
import { readFile as
|
|
42756
|
-
import { dirname as dirname29, resolve as
|
|
43207
|
+
import { readFile as readFile78 } from "fs/promises";
|
|
43208
|
+
import { dirname as dirname29, resolve as resolve107 } from "path";
|
|
42757
43209
|
var AGE_PATTERN = /^(\d+)([dhwm])$/i;
|
|
42758
43210
|
function parseAgeToCutoff(age) {
|
|
42759
43211
|
const match = age.match(AGE_PATTERN);
|
|
@@ -42772,7 +43224,7 @@ function parseAgeToCutoff(age) {
|
|
|
42772
43224
|
}
|
|
42773
43225
|
function assignmentMdPath(item) {
|
|
42774
43226
|
if (item.projectSlug) {
|
|
42775
|
-
return
|
|
43227
|
+
return resolve107(
|
|
42776
43228
|
defaultProjectDir(),
|
|
42777
43229
|
item.projectSlug,
|
|
42778
43230
|
"assignments",
|
|
@@ -42780,13 +43232,13 @@ function assignmentMdPath(item) {
|
|
|
42780
43232
|
"assignment.md"
|
|
42781
43233
|
);
|
|
42782
43234
|
}
|
|
42783
|
-
return
|
|
43235
|
+
return resolve107(assignmentsDir(), item.id, "assignment.md");
|
|
42784
43236
|
}
|
|
42785
43237
|
async function loadTags(item) {
|
|
42786
43238
|
const path = assignmentMdPath(item);
|
|
42787
43239
|
if (!await fileExists(path)) return [];
|
|
42788
43240
|
try {
|
|
42789
|
-
const content = await
|
|
43241
|
+
const content = await readFile78(path, "utf-8");
|
|
42790
43242
|
return parseAssignmentFrontmatter(content).tags;
|
|
42791
43243
|
} catch {
|
|
42792
43244
|
return [];
|
|
@@ -42850,11 +43302,11 @@ async function loadQueryItem(item, terminalStatuses3, now, declarations) {
|
|
|
42850
43302
|
const path = assignmentMdPath(item);
|
|
42851
43303
|
if (!await fileExists(path)) return null;
|
|
42852
43304
|
try {
|
|
42853
|
-
const content = await
|
|
43305
|
+
const content = await readFile78(path, "utf-8");
|
|
42854
43306
|
const fm = parseAssignmentFrontmatter(content);
|
|
42855
43307
|
const body = content.replace(/^---\n[\s\S]*?\n---/, "");
|
|
42856
43308
|
const assignmentDir = dirname29(path);
|
|
42857
|
-
const projectDir = item.projectSlug ?
|
|
43309
|
+
const projectDir = item.projectSlug ? resolve107(defaultProjectDir(), item.projectSlug) : null;
|
|
42858
43310
|
const facts = await computeFacts({ assignmentDir, frontmatter: fm, body, projectDir, terminalStatuses: terminalStatuses3, declarations });
|
|
42859
43311
|
const history = fm.statusHistory;
|
|
42860
43312
|
const lastHeadlineChange = [...history].reverse().find((e) => e.from !== e.to || e.from === null);
|
|
@@ -43624,8 +44076,8 @@ init_fs();
|
|
|
43624
44076
|
init_timestamp();
|
|
43625
44077
|
init_frontmatter();
|
|
43626
44078
|
import { Command as Command21 } from "commander";
|
|
43627
|
-
import { readFile as
|
|
43628
|
-
import { resolve as
|
|
44079
|
+
import { readFile as readFile79 } from "fs/promises";
|
|
44080
|
+
import { resolve as resolve108 } from "path";
|
|
43629
44081
|
function renameAssignmentStatusRefs(content, id, newId, now) {
|
|
43630
44082
|
const fm = parseAssignmentFrontmatter(content);
|
|
43631
44083
|
const updates = { updated: now };
|
|
@@ -43646,12 +44098,12 @@ function fail3(error) {
|
|
|
43646
44098
|
process.exit(1);
|
|
43647
44099
|
}
|
|
43648
44100
|
function configPath() {
|
|
43649
|
-
return
|
|
44101
|
+
return resolve108(syntaurRoot(), "config.md");
|
|
43650
44102
|
}
|
|
43651
44103
|
async function readStatusBlock() {
|
|
43652
44104
|
const p = configPath();
|
|
43653
44105
|
if (!await fileExists(p)) return null;
|
|
43654
|
-
const content = await
|
|
44106
|
+
const content = await readFile79(p, "utf-8");
|
|
43655
44107
|
return parseStatusConfig(content);
|
|
43656
44108
|
}
|
|
43657
44109
|
async function requireStatusBlock() {
|
|
@@ -43935,7 +44387,7 @@ async function runStatusRename(id, opts) {
|
|
|
43935
44387
|
printBlockDiff(before, after);
|
|
43936
44388
|
const now2 = nowTimestamp();
|
|
43937
44389
|
for (const a of affected) {
|
|
43938
|
-
const original = await
|
|
44390
|
+
const original = await readFile79(a.path, "utf-8");
|
|
43939
44391
|
const rewritten = renameAssignmentStatusRefs(original, id, newId, now2);
|
|
43940
44392
|
console.log(`
|
|
43941
44393
|
--- ${a.display}/assignment.md`);
|
|
@@ -43946,9 +44398,9 @@ async function runStatusRename(id, opts) {
|
|
|
43946
44398
|
}
|
|
43947
44399
|
const cfgPath = configPath();
|
|
43948
44400
|
const buffers = /* @__PURE__ */ new Map();
|
|
43949
|
-
buffers.set(cfgPath, await fileExists(cfgPath) ? await
|
|
44401
|
+
buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile79(cfgPath, "utf-8") : "");
|
|
43950
44402
|
for (const a of affected) {
|
|
43951
|
-
buffers.set(a.path, await
|
|
44403
|
+
buffers.set(a.path, await readFile79(a.path, "utf-8"));
|
|
43952
44404
|
}
|
|
43953
44405
|
const now = nowTimestamp();
|
|
43954
44406
|
try {
|
|
@@ -44150,13 +44602,13 @@ init_config2();
|
|
|
44150
44602
|
init_timestamp();
|
|
44151
44603
|
init_frontmatter();
|
|
44152
44604
|
import { Command as Command22 } from "commander";
|
|
44153
|
-
import { readFile as
|
|
44154
|
-
import { resolve as
|
|
44605
|
+
import { readFile as readFile80 } from "fs/promises";
|
|
44606
|
+
import { resolve as resolve109 } from "path";
|
|
44155
44607
|
async function readContext3(cwd) {
|
|
44156
|
-
const path =
|
|
44608
|
+
const path = resolve109(cwd, ".syntaur", "context.json");
|
|
44157
44609
|
if (!await fileExists(path)) return null;
|
|
44158
44610
|
try {
|
|
44159
|
-
return JSON.parse(await
|
|
44611
|
+
return JSON.parse(await readFile80(path, "utf-8"));
|
|
44160
44612
|
} catch {
|
|
44161
44613
|
return null;
|
|
44162
44614
|
}
|
|
@@ -44165,12 +44617,12 @@ async function resolveAssignmentPath3(opts) {
|
|
|
44165
44617
|
if (opts.assignment) {
|
|
44166
44618
|
if (opts.project) {
|
|
44167
44619
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
44168
|
-
return
|
|
44620
|
+
return resolve109(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
44169
44621
|
}
|
|
44170
|
-
return
|
|
44622
|
+
return resolve109(assignmentsDir(), opts.assignment, "assignment.md");
|
|
44171
44623
|
}
|
|
44172
44624
|
const ctx = await readContext3(opts.cwd);
|
|
44173
|
-
if (ctx?.assignmentDir) return
|
|
44625
|
+
if (ctx?.assignmentDir) return resolve109(ctx.assignmentDir, "assignment.md");
|
|
44174
44626
|
throw new Error(
|
|
44175
44627
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
44176
44628
|
);
|
|
@@ -44207,7 +44659,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
|
|
|
44207
44659
|
${pre.errors.map((e) => ` - ${e}`).join("\n")}`
|
|
44208
44660
|
);
|
|
44209
44661
|
}
|
|
44210
|
-
const original = await
|
|
44662
|
+
const original = await readFile80(path, "utf-8");
|
|
44211
44663
|
let next = updateAssignmentWorkspace(original, partial);
|
|
44212
44664
|
next = updateAssignmentFile(next, { updated: nowTimestamp() });
|
|
44213
44665
|
await writeFileForce(path, next);
|
|
@@ -44244,13 +44696,13 @@ init_config2();
|
|
|
44244
44696
|
init_timestamp();
|
|
44245
44697
|
init_templates();
|
|
44246
44698
|
import { Command as Command23 } from "commander";
|
|
44247
|
-
import { readFile as
|
|
44248
|
-
import { resolve as
|
|
44699
|
+
import { readFile as readFile81 } from "fs/promises";
|
|
44700
|
+
import { resolve as resolve110 } from "path";
|
|
44249
44701
|
async function readContext4(cwd) {
|
|
44250
|
-
const path =
|
|
44702
|
+
const path = resolve110(cwd, ".syntaur", "context.json");
|
|
44251
44703
|
if (!await fileExists(path)) return null;
|
|
44252
44704
|
try {
|
|
44253
|
-
return JSON.parse(await
|
|
44705
|
+
return JSON.parse(await readFile81(path, "utf-8"));
|
|
44254
44706
|
} catch {
|
|
44255
44707
|
return null;
|
|
44256
44708
|
}
|
|
@@ -44260,11 +44712,11 @@ async function resolveAssignmentDir2(opts) {
|
|
|
44260
44712
|
if (opts.project) {
|
|
44261
44713
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
44262
44714
|
return {
|
|
44263
|
-
dir:
|
|
44715
|
+
dir: resolve110(projectsDir2, opts.project, "assignments", opts.assignment),
|
|
44264
44716
|
slug: opts.assignment
|
|
44265
44717
|
};
|
|
44266
44718
|
}
|
|
44267
|
-
return { dir:
|
|
44719
|
+
return { dir: resolve110(assignmentsDir(), opts.assignment), slug: opts.assignment };
|
|
44268
44720
|
}
|
|
44269
44721
|
const ctx = await readContext4(opts.cwd);
|
|
44270
44722
|
if (ctx?.assignmentDir) {
|
|
@@ -44326,12 +44778,12 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
|
|
|
44326
44778
|
project: options.project,
|
|
44327
44779
|
cwd
|
|
44328
44780
|
});
|
|
44329
|
-
if (!await fileExists(
|
|
44781
|
+
if (!await fileExists(resolve110(dir, "assignment.md"))) {
|
|
44330
44782
|
throw new Error(`No assignment found at ${dir} (missing assignment.md).`);
|
|
44331
44783
|
}
|
|
44332
|
-
const path =
|
|
44784
|
+
const path = resolve110(dir, "progress.md");
|
|
44333
44785
|
const now = nowTimestamp();
|
|
44334
|
-
const content = await fileExists(path) ? await
|
|
44786
|
+
const content = await fileExists(path) ? await readFile81(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
|
|
44335
44787
|
const next = appendProgressEntry(content, text, now);
|
|
44336
44788
|
await writeFileForce(path, next);
|
|
44337
44789
|
return path;
|
|
@@ -44536,7 +44988,7 @@ program.command("attest").description("Record an attestation (agent reviewed a r
|
|
|
44536
44988
|
await attestCommand(assignment, fact, options);
|
|
44537
44989
|
})
|
|
44538
44990
|
);
|
|
44539
|
-
program.command("recompute").description("Recompute derived status for one assignment or --all (headless reconcile)").argument("[assignment]", "Assignment slug or standalone UUID").option("--all", "Recompute every assignment (projects + standalone)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(
|
|
44991
|
+
program.command("recompute").description("Recompute derived status for one assignment or --all (headless reconcile)").argument("[assignment]", "Assignment slug or standalone UUID").option("--all", "Recompute every assignment (projects + standalone)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").option("--if-migrated", "No-op unless derive migration has run (for implicit triggers like session-end hooks)").action(
|
|
44540
44992
|
runCommand(async (assignment, options) => {
|
|
44541
44993
|
await recomputeCommand(assignment, options);
|
|
44542
44994
|
})
|