syntaur 0.66.1 → 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 +568 -60
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +1267 -699
- 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;
|
|
@@ -13368,7 +13667,7 @@ async function resolveAgentTargets() {
|
|
|
13368
13667
|
});
|
|
13369
13668
|
return { targets: [...AGENT_TARGETS, ...user], warnings };
|
|
13370
13669
|
}
|
|
13371
|
-
var detectDir, claudeSessions, codexSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
|
|
13670
|
+
var detectDir, claudeSessions, codexSessions, piSessions, AGENT_TARGETS, AGENT_TARGETS_BY_ID;
|
|
13372
13671
|
var init_registry = __esm({
|
|
13373
13672
|
"src/targets/registry.ts"() {
|
|
13374
13673
|
"use strict";
|
|
@@ -13396,6 +13695,16 @@ var init_registry = __esm({
|
|
|
13396
13695
|
}
|
|
13397
13696
|
}
|
|
13398
13697
|
};
|
|
13698
|
+
piSessions = {
|
|
13699
|
+
globs: (root) => [join10(root ?? resolvePiSessionsRoot(), "*", "*.jsonl")],
|
|
13700
|
+
parse: async (file) => toDiscovered(await extractPiSessionMeta(file)),
|
|
13701
|
+
walk: async function* (opts = {}) {
|
|
13702
|
+
for await (const meta of walkPiSessions({ root: opts.root, sinceMtimeMs: opts.sinceMtimeMs })) {
|
|
13703
|
+
const d = toDiscovered(meta);
|
|
13704
|
+
if (d) yield d;
|
|
13705
|
+
}
|
|
13706
|
+
}
|
|
13707
|
+
};
|
|
13399
13708
|
AGENT_TARGETS = [
|
|
13400
13709
|
{
|
|
13401
13710
|
id: "cursor",
|
|
@@ -13452,6 +13761,7 @@ var init_registry = __esm({
|
|
|
13452
13761
|
detect: detectDir(home(".pi")),
|
|
13453
13762
|
skillsDir: { global: home(".pi", "agent", "skills") },
|
|
13454
13763
|
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
|
|
13764
|
+
sessions: piSessions,
|
|
13455
13765
|
tier3: {
|
|
13456
13766
|
kind: "pi-extension",
|
|
13457
13767
|
source: "platforms/pi/extensions/syntaur",
|
|
@@ -13722,6 +14032,40 @@ var init_scanner2 = __esm({
|
|
|
13722
14032
|
}
|
|
13723
14033
|
});
|
|
13724
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
|
+
|
|
13725
14069
|
// scripts/install-macos-url-handler.mjs
|
|
13726
14070
|
var install_macos_url_handler_exports = {};
|
|
13727
14071
|
__export(install_macos_url_handler_exports, {
|
|
@@ -13738,7 +14082,7 @@ import {
|
|
|
13738
14082
|
unlinkSync
|
|
13739
14083
|
} from "fs";
|
|
13740
14084
|
import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
|
|
13741
|
-
import { dirname as dirname21, resolve as
|
|
14085
|
+
import { dirname as dirname21, resolve as resolve72, join as join16 } from "path";
|
|
13742
14086
|
import { homedir as homedir13, tmpdir as tmpdir2 } from "os";
|
|
13743
14087
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
13744
14088
|
function syntaurRootMjs() {
|
|
@@ -13760,7 +14104,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
13760
14104
|
}
|
|
13761
14105
|
const stateRoot = syntaurRootMjs();
|
|
13762
14106
|
mkdirSync3(stateRoot, { recursive: true });
|
|
13763
|
-
const lockPath =
|
|
14107
|
+
const lockPath = resolve72(stateRoot, "install-url-handler.lock");
|
|
13764
14108
|
let lockFd;
|
|
13765
14109
|
try {
|
|
13766
14110
|
lockFd = openSync(lockPath, "wx");
|
|
@@ -13776,8 +14120,8 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
13776
14120
|
throw err2;
|
|
13777
14121
|
}
|
|
13778
14122
|
try {
|
|
13779
|
-
const pkgRoot =
|
|
13780
|
-
const cliBin = realpathSync3(
|
|
14123
|
+
const pkgRoot = resolve72(dirname21(fileURLToPath9(import.meta.url)), "..");
|
|
14124
|
+
const cliBin = realpathSync3(resolve72(pkgRoot, "bin/syntaur.js"));
|
|
13781
14125
|
const nodeBin = process.execPath;
|
|
13782
14126
|
const bundleParent = join16(
|
|
13783
14127
|
homedir13(),
|
|
@@ -15956,7 +16300,7 @@ init_timestamp();
|
|
|
15956
16300
|
init_fs();
|
|
15957
16301
|
init_git_worktree();
|
|
15958
16302
|
import { Router as Router2 } from "express";
|
|
15959
|
-
import { resolve as resolve26, basename as
|
|
16303
|
+
import { resolve as resolve26, basename as basename4, isAbsolute as isAbsolute4 } from "path";
|
|
15960
16304
|
import { rm, readFile as readFile19, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
|
|
15961
16305
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
15962
16306
|
|
|
@@ -16760,7 +17104,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
|
|
|
16760
17104
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
16761
17105
|
return;
|
|
16762
17106
|
}
|
|
16763
|
-
const document = await getEditableDocument(projectsDir2, "memory",
|
|
17107
|
+
const document = await getEditableDocument(projectsDir2, "memory", basename4(projectDir), itemSlug);
|
|
16764
17108
|
if (!document) {
|
|
16765
17109
|
res.status(404).json({ error: "Memory not found" });
|
|
16766
17110
|
return;
|
|
@@ -16779,7 +17123,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
|
|
|
16779
17123
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
16780
17124
|
return;
|
|
16781
17125
|
}
|
|
16782
|
-
const document = await getEditableDocument(projectsDir2, "resource",
|
|
17126
|
+
const document = await getEditableDocument(projectsDir2, "resource", basename4(projectDir), itemSlug);
|
|
16783
17127
|
if (!document) {
|
|
16784
17128
|
res.status(404).json({ error: "Resource not found" });
|
|
16785
17129
|
return;
|
|
@@ -16864,7 +17208,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16864
17208
|
let content = renderItemStub(kind, {
|
|
16865
17209
|
slug: requestedSlug,
|
|
16866
17210
|
name,
|
|
16867
|
-
projectSlug:
|
|
17211
|
+
projectSlug: basename4(projectDir),
|
|
16868
17212
|
timestamp
|
|
16869
17213
|
});
|
|
16870
17214
|
const customBody = typeof body.body === "string" ? body.body : "";
|
|
@@ -16881,13 +17225,13 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16881
17225
|
} catch (err2) {
|
|
16882
17226
|
if (err2.code === "EEXIST") {
|
|
16883
17227
|
res.status(409).json({
|
|
16884
|
-
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)}".`
|
|
16885
17229
|
});
|
|
16886
17230
|
return;
|
|
16887
17231
|
}
|
|
16888
17232
|
throw err2;
|
|
16889
17233
|
}
|
|
16890
|
-
res.status(201).json({ slug: requestedSlug, projectSlug:
|
|
17234
|
+
res.status(201).json({ slug: requestedSlug, projectSlug: basename4(projectDir), content });
|
|
16891
17235
|
} catch (error) {
|
|
16892
17236
|
console.error(`Error creating ${kind}:`, error);
|
|
16893
17237
|
res.status(500).json({ error: `Failed to create ${kind}: ${error.message}` });
|
|
@@ -16922,7 +17266,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16922
17266
|
${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
|
|
16923
17267
|
merged = setTopLevelField(merged, "updated", nowTimestamp());
|
|
16924
17268
|
await writeFileForce(filePath, merged);
|
|
16925
|
-
const detail = await getItemDetail(kind,
|
|
17269
|
+
const detail = await getItemDetail(kind, basename4(projectDir), itemSlug);
|
|
16926
17270
|
res.json({ [kind]: detail, content: merged });
|
|
16927
17271
|
} catch (error) {
|
|
16928
17272
|
console.error(`Error updating ${kind}:`, error);
|
|
@@ -20189,7 +20533,7 @@ async function resolveSessionPlan(input4, terminal) {
|
|
|
20189
20533
|
init_launch();
|
|
20190
20534
|
import { spawn as spawn3 } from "child_process";
|
|
20191
20535
|
import { homedir as homedir5 } from "os";
|
|
20192
|
-
import { basename as
|
|
20536
|
+
import { basename as basename5, join as join6, resolve as resolve29 } from "path";
|
|
20193
20537
|
init_fs();
|
|
20194
20538
|
init_config2();
|
|
20195
20539
|
init_session_id();
|
|
@@ -20209,7 +20553,7 @@ var TerminalNotFoundError = class extends Error {
|
|
|
20209
20553
|
var realSpawn = (command, args, options) => spawn3(command, args, options);
|
|
20210
20554
|
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["osascript", "open", "sh"]);
|
|
20211
20555
|
function isWrapperCommand(command) {
|
|
20212
|
-
return WRAPPER_COMMANDS.has(
|
|
20556
|
+
return WRAPPER_COMMANDS.has(basename5(command));
|
|
20213
20557
|
}
|
|
20214
20558
|
var WRAPPER_EXIT_TIMEOUT_MS = 1500;
|
|
20215
20559
|
async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
@@ -20237,7 +20581,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20237
20581
|
`Spawn failed: ${msg2}. Verify the terminal is installed and on PATH.`
|
|
20238
20582
|
);
|
|
20239
20583
|
}
|
|
20240
|
-
await new Promise((
|
|
20584
|
+
await new Promise((resolve111, reject) => {
|
|
20241
20585
|
let settled = false;
|
|
20242
20586
|
let stderr = "";
|
|
20243
20587
|
const finishOk = () => {
|
|
@@ -20247,7 +20591,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20247
20591
|
child.unref();
|
|
20248
20592
|
} catch {
|
|
20249
20593
|
}
|
|
20250
|
-
|
|
20594
|
+
resolve111();
|
|
20251
20595
|
};
|
|
20252
20596
|
const finishErr = (remediation) => {
|
|
20253
20597
|
if (settled) return;
|
|
@@ -20513,7 +20857,7 @@ function normalizeSlashes(p) {
|
|
|
20513
20857
|
}
|
|
20514
20858
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
20515
20859
|
const realpath3 = opts.realpath ?? realpathSync.native;
|
|
20516
|
-
const
|
|
20860
|
+
const readFile82 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
|
|
20517
20861
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
20518
20862
|
const resolved = resolveScriptPath(scriptUrl, realpath3);
|
|
20519
20863
|
if (resolved === null) {
|
|
@@ -20534,7 +20878,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
20534
20878
|
const pkgJsonPath = join7(dir, "package.json");
|
|
20535
20879
|
let raw2;
|
|
20536
20880
|
try {
|
|
20537
|
-
raw2 =
|
|
20881
|
+
raw2 = readFile82(pkgJsonPath);
|
|
20538
20882
|
} catch {
|
|
20539
20883
|
const parent2 = dirname7(dir);
|
|
20540
20884
|
if (parent2 === dir) break;
|
|
@@ -22599,7 +22943,7 @@ async function readEvents(jobId) {
|
|
|
22599
22943
|
|
|
22600
22944
|
// src/schedules/attempt.ts
|
|
22601
22945
|
import { createHash as createHash3 } from "crypto";
|
|
22602
|
-
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";
|
|
22603
22947
|
import { resolve as resolve34 } from "path";
|
|
22604
22948
|
|
|
22605
22949
|
// src/schedules/triggers.ts
|
|
@@ -22796,7 +23140,7 @@ async function acquireJobLock(id) {
|
|
|
22796
23140
|
const code = err2.code;
|
|
22797
23141
|
if (code !== "EEXIST") throw err2;
|
|
22798
23142
|
try {
|
|
22799
|
-
const info = await
|
|
23143
|
+
const info = await stat5(lockPath);
|
|
22800
23144
|
if (Date.now() - info.mtimeMs > LOCK_STALE_MS2) {
|
|
22801
23145
|
await unlink6(lockPath).catch(() => {
|
|
22802
23146
|
});
|
|
@@ -24463,8 +24807,8 @@ init_api();
|
|
|
24463
24807
|
import { raw } from "express";
|
|
24464
24808
|
|
|
24465
24809
|
// src/todos/attachments.ts
|
|
24466
|
-
import { mkdir as mkdir4, readdir as readdir16, stat as
|
|
24467
|
-
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";
|
|
24468
24812
|
|
|
24469
24813
|
// src/utils/proof-artifact-id.ts
|
|
24470
24814
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -24561,7 +24905,7 @@ function isSafeInlineMime(mime) {
|
|
|
24561
24905
|
return SAFE_INLINE_MIME.has(mime);
|
|
24562
24906
|
}
|
|
24563
24907
|
function sanitizeAttachmentName(name) {
|
|
24564
|
-
let n =
|
|
24908
|
+
let n = basename6(name || "").replace(/["'\\/]/g, "_");
|
|
24565
24909
|
n = Array.from(n, (ch) => {
|
|
24566
24910
|
const code = ch.charCodeAt(0);
|
|
24567
24911
|
return code < 32 || code === 127 ? "_" : ch;
|
|
@@ -24584,7 +24928,7 @@ function attachmentDirFor(todosDir2, scopeId, todoId) {
|
|
|
24584
24928
|
}
|
|
24585
24929
|
async function dirExists(p) {
|
|
24586
24930
|
try {
|
|
24587
|
-
return (await
|
|
24931
|
+
return (await stat6(p)).isDirectory();
|
|
24588
24932
|
} catch {
|
|
24589
24933
|
return false;
|
|
24590
24934
|
}
|
|
@@ -24619,7 +24963,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
24619
24963
|
if (!ATTACHMENT_ID_RE.test(id)) continue;
|
|
24620
24964
|
const filename = stored.slice(sep2 + 2);
|
|
24621
24965
|
try {
|
|
24622
|
-
const st = await
|
|
24966
|
+
const st = await stat6(resolve41(dir, stored));
|
|
24623
24967
|
if (!st.isFile()) continue;
|
|
24624
24968
|
out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
|
|
24625
24969
|
} catch {
|
|
@@ -25074,8 +25418,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
25074
25418
|
router.post("/:workspace/archive", async (req2, res) => {
|
|
25075
25419
|
try {
|
|
25076
25420
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
25077
|
-
const { resolve:
|
|
25078
|
-
const { readFile:
|
|
25421
|
+
const { resolve: resolve111 } = await import("path");
|
|
25422
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
25079
25423
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
25080
25424
|
const workspace = getWorkspaceParam(req2.params.workspace);
|
|
25081
25425
|
const outcome = await wsLock(workspace, async () => {
|
|
@@ -25091,10 +25435,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
25091
25435
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
25092
25436
|
);
|
|
25093
25437
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
25094
|
-
await ensureDir(
|
|
25438
|
+
await ensureDir(resolve111(todosDir2, "archive"));
|
|
25095
25439
|
let archContent = "";
|
|
25096
25440
|
if (await fileExists(archFile)) {
|
|
25097
|
-
archContent = await
|
|
25441
|
+
archContent = await readFile82(archFile, "utf-8");
|
|
25098
25442
|
archContent = archContent.trimEnd() + "\n\n";
|
|
25099
25443
|
} else {
|
|
25100
25444
|
archContent = `---
|
|
@@ -25395,7 +25739,7 @@ workspace: ${workspace}
|
|
|
25395
25739
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
25396
25740
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
25397
25741
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
25398
|
-
const { readFile:
|
|
25742
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
25399
25743
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
25400
25744
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
25401
25745
|
let assignmentRef;
|
|
@@ -25416,7 +25760,7 @@ workspace: ${workspace}
|
|
|
25416
25760
|
}
|
|
25417
25761
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
25418
25762
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
25419
|
-
let content = await
|
|
25763
|
+
let content = await readFile82(assignmentMdPath2, "utf-8");
|
|
25420
25764
|
content = appendTodosToAssignmentBody2(
|
|
25421
25765
|
content,
|
|
25422
25766
|
items.map((it) => ({
|
|
@@ -26826,7 +27170,7 @@ init_fs();
|
|
|
26826
27170
|
init_config2();
|
|
26827
27171
|
import { execFile as execFile2 } from "child_process";
|
|
26828
27172
|
import { promisify as promisify2 } from "util";
|
|
26829
|
-
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";
|
|
26830
27174
|
import { resolve as resolve44, join as join9 } from "path";
|
|
26831
27175
|
import { tmpdir } from "os";
|
|
26832
27176
|
var exec2 = promisify2(execFile2);
|
|
@@ -26920,7 +27264,7 @@ async function cloneOrInit(repoUrl, destDir) {
|
|
|
26920
27264
|
}
|
|
26921
27265
|
async function copyRecursive(src, dest) {
|
|
26922
27266
|
if (!await fileExists(src)) return;
|
|
26923
|
-
const s = await
|
|
27267
|
+
const s = await stat7(src);
|
|
26924
27268
|
if (s.isDirectory()) {
|
|
26925
27269
|
await ensureDir(dest);
|
|
26926
27270
|
await cp2(src, dest, { recursive: true, force: true });
|
|
@@ -27611,7 +27955,7 @@ async function runCcusage(opts = {}) {
|
|
|
27611
27955
|
};
|
|
27612
27956
|
}
|
|
27613
27957
|
function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
27614
|
-
return new Promise((
|
|
27958
|
+
return new Promise((resolve111) => {
|
|
27615
27959
|
const child = spawn4(binary, args, {
|
|
27616
27960
|
env: env ?? process.env,
|
|
27617
27961
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -27625,7 +27969,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
|
27625
27969
|
if (settled) return;
|
|
27626
27970
|
settled = true;
|
|
27627
27971
|
clearTimeout(timer3);
|
|
27628
|
-
|
|
27972
|
+
resolve111(result);
|
|
27629
27973
|
};
|
|
27630
27974
|
const timer3 = setTimeout(() => {
|
|
27631
27975
|
timedOut = true;
|
|
@@ -27776,6 +28120,41 @@ function runRollup() {
|
|
|
27776
28120
|
return { daysComputed, rowsWritten };
|
|
27777
28121
|
}
|
|
27778
28122
|
|
|
28123
|
+
// src/usage/pricing.ts
|
|
28124
|
+
var PER_MILLION = 1e6;
|
|
28125
|
+
var MODEL_PRICING = {
|
|
28126
|
+
// Moonshot Kimi K2.6 — the model pi emits today. Official Moonshot list price.
|
|
28127
|
+
// source: https://platform.moonshot.ai/ (official) — cross-checked
|
|
28128
|
+
// https://openrouter.ai/moonshotai/kimi-k2.6 (retrieved 2026-06-17)
|
|
28129
|
+
"moonshotai/kimi-k2.6": { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0.95 },
|
|
28130
|
+
// Moonshot Kimi K2.5 — prior Kimi model. Official Moonshot list price.
|
|
28131
|
+
// source: https://platform.moonshot.ai/ — cross-checked
|
|
28132
|
+
// https://openrouter.ai/moonshotai/kimi-k2.5 (retrieved 2026-06-17)
|
|
28133
|
+
"moonshotai/kimi-k2.5": { input: 0.6, output: 3, cacheRead: 0.1, cacheWrite: 0.6 },
|
|
28134
|
+
// Z.ai (Zhipu) GLM-5.2 — official z.ai platform list price.
|
|
28135
|
+
// source: https://docs.z.ai/guides/overview/pricing — cross-checked
|
|
28136
|
+
// https://openrouter.ai/z-ai/glm-5 (retrieved 2026-06-18)
|
|
28137
|
+
"zai-org/glm-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 1.4 },
|
|
28138
|
+
// MiniMax M2.5 — official MiniMax platform list price. No separately-pinned
|
|
28139
|
+
// cached-read rate is published, so cacheRead is set conservatively to the
|
|
28140
|
+
// input rate (upper bound); revise if MiniMax publishes a cache rate.
|
|
28141
|
+
// source: https://platform.minimax.io/docs/guides/pricing-token-plan
|
|
28142
|
+
// — cross-checked https://openrouter.ai/minimax/minimax-m2.5 (retrieved 2026-06-18)
|
|
28143
|
+
"minimaxai/minimax-m2.5": { input: 0.15, output: 0.9, cacheRead: 0.15, cacheWrite: 0.15 }
|
|
28144
|
+
// NOTE: opaque Synthetic tier aliases like `syn:large:text` have no public
|
|
28145
|
+
// per-token rate (they route to whatever Synthetic assigns), so they remain
|
|
28146
|
+
// unpriced (→ $0). Reseller discounts (e.g. DeepInfra K2.6 0.75/3.50/0.15) are
|
|
28147
|
+
// rejected by the canonical-source rule and are NOT used here.
|
|
28148
|
+
};
|
|
28149
|
+
function normalizeModelKey(model) {
|
|
28150
|
+
return model.replace(/^\s*\[[^\]]*\]\s*/, "").replace(/^hf:/i, "").trim().toLowerCase();
|
|
28151
|
+
}
|
|
28152
|
+
function priceForModel(model, tokens) {
|
|
28153
|
+
const rate = MODEL_PRICING[normalizeModelKey(model)];
|
|
28154
|
+
if (!rate) return null;
|
|
28155
|
+
return (tokens.inputTokens * rate.input + tokens.outputTokens * rate.output + tokens.cacheReadTokens * rate.cacheRead + tokens.cacheCreationTokens * rate.cacheWrite) / PER_MILLION;
|
|
28156
|
+
}
|
|
28157
|
+
|
|
27779
28158
|
// src/usage/collect.ts
|
|
27780
28159
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
27781
28160
|
function formatDayFromCcusageDate(yyyymmdd) {
|
|
@@ -27809,6 +28188,12 @@ async function collectAndPersist() {
|
|
|
27809
28188
|
cwd,
|
|
27810
28189
|
eventTs: row.eventTs
|
|
27811
28190
|
});
|
|
28191
|
+
const totalCost = row.totalCost > 0 ? row.totalCost : priceForModel(row.model, {
|
|
28192
|
+
inputTokens: row.inputTokens,
|
|
28193
|
+
outputTokens: row.outputTokens,
|
|
28194
|
+
cacheCreationTokens: row.cacheCreationTokens,
|
|
28195
|
+
cacheReadTokens: row.cacheReadTokens
|
|
28196
|
+
}) ?? 0;
|
|
27812
28197
|
return {
|
|
27813
28198
|
sessionId: row.sessionId,
|
|
27814
28199
|
model: row.model,
|
|
@@ -27819,7 +28204,7 @@ async function collectAndPersist() {
|
|
|
27819
28204
|
cacheCreationTokens: row.cacheCreationTokens,
|
|
27820
28205
|
cacheReadTokens: row.cacheReadTokens,
|
|
27821
28206
|
totalTokens: row.totalTokens,
|
|
27822
|
-
totalCost
|
|
28207
|
+
totalCost,
|
|
27823
28208
|
cwd,
|
|
27824
28209
|
projectSlug: attr.projectSlug ?? "",
|
|
27825
28210
|
assignmentSlug: attr.assignmentSlug ?? "",
|
|
@@ -27836,8 +28221,72 @@ async function collectAndPersist() {
|
|
|
27836
28221
|
tx.immediate();
|
|
27837
28222
|
return { isFirstRun, rowsIngested: enriched.length };
|
|
27838
28223
|
}
|
|
28224
|
+
function backfillZeroCostEvents() {
|
|
28225
|
+
const db6 = getUsageDb();
|
|
28226
|
+
const select2 = db6.prepare(
|
|
28227
|
+
`SELECT session_id, model, input_tokens, output_tokens,
|
|
28228
|
+
cache_creation_tokens, cache_read_tokens
|
|
28229
|
+
FROM usage_events
|
|
28230
|
+
WHERE total_cost = 0`
|
|
28231
|
+
);
|
|
28232
|
+
const update = db6.prepare(
|
|
28233
|
+
`UPDATE usage_events SET total_cost = @cost, updated_at = @updatedAt
|
|
28234
|
+
WHERE session_id = @sessionId AND model = @model AND total_cost = 0`
|
|
28235
|
+
);
|
|
28236
|
+
let updated = 0;
|
|
28237
|
+
const tx = db6.transaction(() => {
|
|
28238
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28239
|
+
for (const r of select2.all()) {
|
|
28240
|
+
const cost = priceForModel(r.model, {
|
|
28241
|
+
inputTokens: r.input_tokens,
|
|
28242
|
+
outputTokens: r.output_tokens,
|
|
28243
|
+
cacheCreationTokens: r.cache_creation_tokens,
|
|
28244
|
+
cacheReadTokens: r.cache_read_tokens
|
|
28245
|
+
});
|
|
28246
|
+
if (cost !== null && cost > 0) {
|
|
28247
|
+
updated += update.run({ cost, updatedAt, sessionId: r.session_id, model: r.model }).changes;
|
|
28248
|
+
}
|
|
28249
|
+
}
|
|
28250
|
+
});
|
|
28251
|
+
tx.immediate();
|
|
28252
|
+
return updated;
|
|
28253
|
+
}
|
|
28254
|
+
function reattributeOrphanEvents() {
|
|
28255
|
+
const db6 = getUsageDb();
|
|
28256
|
+
const select2 = db6.prepare(
|
|
28257
|
+
`SELECT session_id, model, cwd, event_ts
|
|
28258
|
+
FROM usage_events
|
|
28259
|
+
WHERE project_slug = '' AND assignment_slug = ''`
|
|
28260
|
+
);
|
|
28261
|
+
const update = db6.prepare(
|
|
28262
|
+
`UPDATE usage_events
|
|
28263
|
+
SET project_slug = @projectSlug, assignment_slug = @assignmentSlug, updated_at = @updatedAt
|
|
28264
|
+
WHERE session_id = @sessionId AND model = @model
|
|
28265
|
+
AND project_slug = '' AND assignment_slug = ''`
|
|
28266
|
+
);
|
|
28267
|
+
let updated = 0;
|
|
28268
|
+
const tx = db6.transaction(() => {
|
|
28269
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
28270
|
+
for (const r of select2.all()) {
|
|
28271
|
+
const attr = resolveAttribution({
|
|
28272
|
+
sessionId: r.session_id,
|
|
28273
|
+
cwd: r.cwd,
|
|
28274
|
+
eventTs: r.event_ts
|
|
28275
|
+
});
|
|
28276
|
+
const projectSlug = attr.projectSlug ?? "";
|
|
28277
|
+
const assignmentSlug = attr.assignmentSlug ?? "";
|
|
28278
|
+
if (projectSlug !== "" || assignmentSlug !== "") {
|
|
28279
|
+
updated += update.run({ projectSlug, assignmentSlug, updatedAt, sessionId: r.session_id, model: r.model }).changes;
|
|
28280
|
+
}
|
|
28281
|
+
}
|
|
28282
|
+
});
|
|
28283
|
+
tx.immediate();
|
|
28284
|
+
return updated;
|
|
28285
|
+
}
|
|
27839
28286
|
async function collectUsage() {
|
|
27840
28287
|
const info = await collectAndPersist();
|
|
28288
|
+
backfillZeroCostEvents();
|
|
28289
|
+
reattributeOrphanEvents();
|
|
27841
28290
|
runRollup();
|
|
27842
28291
|
advanceMetaIso("usage_collector_heartbeat", (/* @__PURE__ */ new Date()).toISOString());
|
|
27843
28292
|
return info;
|
|
@@ -28470,6 +28919,8 @@ function createDashboardServer(options) {
|
|
|
28470
28919
|
});
|
|
28471
28920
|
}
|
|
28472
28921
|
let watcherHandle = null;
|
|
28922
|
+
const STALENESS_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
|
|
28923
|
+
let stalenessWatchdogTimer = null;
|
|
28473
28924
|
return {
|
|
28474
28925
|
async start() {
|
|
28475
28926
|
const { recomputeAndWrite: recomputeAndWrite2, recomputeAll: recomputeAll2, resolveDeriveContext: resolveDeriveContext2, isDeriveMigrated: isDeriveMigrated2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
|
|
@@ -28566,6 +29017,38 @@ function createDashboardServer(options) {
|
|
|
28566
29017
|
onAgentSessionsChanged: () => broadcast({ type: "agent-sessions-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
28567
29018
|
});
|
|
28568
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
|
+
}
|
|
28569
29052
|
return new Promise((resolvePromise, reject) => {
|
|
28570
29053
|
server.on("error", (err2) => {
|
|
28571
29054
|
if (err2.code === "EADDRINUSE") {
|
|
@@ -28587,6 +29070,10 @@ function createDashboardServer(options) {
|
|
|
28587
29070
|
});
|
|
28588
29071
|
},
|
|
28589
29072
|
async stop() {
|
|
29073
|
+
if (stalenessWatchdogTimer) {
|
|
29074
|
+
clearInterval(stalenessWatchdogTimer);
|
|
29075
|
+
stalenessWatchdogTimer = null;
|
|
29076
|
+
}
|
|
28590
29077
|
await stopAutodiscovery();
|
|
28591
29078
|
await stopUsageCollector();
|
|
28592
29079
|
if (watcherHandle) {
|
|
@@ -28953,17 +29440,173 @@ init_facts();
|
|
|
28953
29440
|
init_git_worktree();
|
|
28954
29441
|
init_recompute();
|
|
28955
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";
|
|
28956
29455
|
import { readFile as readFile33 } from "fs/promises";
|
|
28957
|
-
|
|
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
|
|
28958
29601
|
async function resolveTarget(assignment, options) {
|
|
28959
29602
|
const config = await readConfig();
|
|
28960
29603
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
28961
29604
|
if (options.project) {
|
|
28962
29605
|
if (!isValidSlug(options.project)) throw new Error(`Invalid project slug "${options.project}".`);
|
|
28963
29606
|
if (!isValidSlug(assignment)) throw new Error(`Invalid assignment slug "${assignment}".`);
|
|
28964
|
-
const projectDir =
|
|
28965
|
-
const assignmentDir =
|
|
28966
|
-
const assignmentPath =
|
|
29607
|
+
const projectDir = resolve52(baseDir, options.project);
|
|
29608
|
+
const assignmentDir = resolve52(projectDir, "assignments", assignment);
|
|
29609
|
+
const assignmentPath = resolve52(assignmentDir, "assignment.md");
|
|
28967
29610
|
if (!await fileExists(assignmentPath)) {
|
|
28968
29611
|
throw new Error(`Assignment "${assignment}" not found at ${assignmentPath}.`);
|
|
28969
29612
|
}
|
|
@@ -28975,14 +29618,14 @@ async function resolveTarget(assignment, options) {
|
|
|
28975
29618
|
}
|
|
28976
29619
|
return {
|
|
28977
29620
|
assignmentDir: resolved.assignmentDir,
|
|
28978
|
-
assignmentPath:
|
|
28979
|
-
projectDir: resolved.standalone ? null :
|
|
29621
|
+
assignmentPath: resolve52(resolved.assignmentDir, "assignment.md"),
|
|
29622
|
+
projectDir: resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..")
|
|
28980
29623
|
};
|
|
28981
29624
|
}
|
|
28982
29625
|
async function inferActor(options) {
|
|
28983
29626
|
if (options.agent) return `agent:${options.agent}`;
|
|
28984
29627
|
try {
|
|
28985
|
-
const raw2 = await
|
|
29628
|
+
const raw2 = await readFile34(resolve52(process.cwd(), ".syntaur", "context.json"), "utf-8");
|
|
28986
29629
|
const ctx = JSON.parse(raw2);
|
|
28987
29630
|
if (ctx.sessionId) return `agent:${ctx.sessionId.slice(0, 8)}`;
|
|
28988
29631
|
} catch {
|
|
@@ -28991,10 +29634,10 @@ async function inferActor(options) {
|
|
|
28991
29634
|
}
|
|
28992
29635
|
async function emitDeriveEvent(target, type, actor, details) {
|
|
28993
29636
|
try {
|
|
28994
|
-
const fm = parseAssignmentFrontmatter(await
|
|
29637
|
+
const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
|
|
28995
29638
|
emitEvent({
|
|
28996
29639
|
assignmentId: fm.id,
|
|
28997
|
-
projectSlug: target.projectDir ?
|
|
29640
|
+
projectSlug: target.projectDir ? basename7(target.projectDir) : null,
|
|
28998
29641
|
type,
|
|
28999
29642
|
actor,
|
|
29000
29643
|
details
|
|
@@ -29052,7 +29695,7 @@ async function planApproveCommand(assignment, options) {
|
|
|
29052
29695
|
throw new Error("No plan file found (plan.md / plan-v*.md). Write a plan before approving.");
|
|
29053
29696
|
}
|
|
29054
29697
|
approvedFile = planFile;
|
|
29055
|
-
const planContent = await
|
|
29698
|
+
const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
|
|
29056
29699
|
return updatePlanApproval(content, {
|
|
29057
29700
|
file: planFile,
|
|
29058
29701
|
digest: planDigest(planContent),
|
|
@@ -29125,7 +29768,7 @@ async function attestCommand(assignment, fact, options) {
|
|
|
29125
29768
|
"No plan file found (plan.md / plan-v*.md). Write a plan before attesting a binds:plan fact."
|
|
29126
29769
|
);
|
|
29127
29770
|
}
|
|
29128
|
-
const planContent = await
|
|
29771
|
+
const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
|
|
29129
29772
|
record.file = planFile;
|
|
29130
29773
|
record.digest = planDigest(planContent);
|
|
29131
29774
|
} else if (binds === "commit") {
|
|
@@ -29175,6 +29818,15 @@ async function requestReviewCommand(assignment, options) {
|
|
|
29175
29818
|
);
|
|
29176
29819
|
}
|
|
29177
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
|
+
}
|
|
29178
29830
|
await assertFact(
|
|
29179
29831
|
assignment,
|
|
29180
29832
|
options,
|
|
@@ -29182,14 +29834,15 @@ async function implementStartedCommand(assignment, options) {
|
|
|
29182
29834
|
(content) => {
|
|
29183
29835
|
let next = updateAssignmentFile(content, { implementationStarted: true });
|
|
29184
29836
|
if (options.agent) {
|
|
29185
|
-
const
|
|
29186
|
-
if (
|
|
29837
|
+
const innerFm = parseAssignmentFrontmatter(next);
|
|
29838
|
+
if (innerFm.assignee === null) {
|
|
29187
29839
|
next = updateAssignmentFile(next, { assignee: options.agent });
|
|
29188
29840
|
}
|
|
29189
29841
|
}
|
|
29190
29842
|
return next;
|
|
29191
29843
|
},
|
|
29192
|
-
"Implementation started"
|
|
29844
|
+
"Implementation started",
|
|
29845
|
+
{ context }
|
|
29193
29846
|
);
|
|
29194
29847
|
}
|
|
29195
29848
|
async function blockFactCommand(assignment, options) {
|
|
@@ -29237,6 +29890,7 @@ async function statusUnpinCommand(assignment, options) {
|
|
|
29237
29890
|
await assertFact(assignment, options, "unpin", (content) => updateOverride(content, null), "Unpinned");
|
|
29238
29891
|
}
|
|
29239
29892
|
async function recomputeCommand(assignment, options) {
|
|
29893
|
+
if (options.ifMigrated && !await isDeriveMigrated()) return;
|
|
29240
29894
|
const context = await resolveDeriveContext();
|
|
29241
29895
|
if (options.all) {
|
|
29242
29896
|
const { recomputeAll: recomputeAll2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
|
|
@@ -29252,12 +29906,15 @@ async function recomputeCommand(assignment, options) {
|
|
|
29252
29906
|
for (const w of summary.warnings) console.warn(`Warning: ${w}`);
|
|
29253
29907
|
return;
|
|
29254
29908
|
}
|
|
29255
|
-
|
|
29256
|
-
|
|
29257
|
-
|
|
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"), {
|
|
29258
29915
|
cause: "recompute",
|
|
29259
29916
|
by: await inferActor(options),
|
|
29260
|
-
projectDir
|
|
29917
|
+
projectDir,
|
|
29261
29918
|
context
|
|
29262
29919
|
});
|
|
29263
29920
|
reportDerived(result.changed ? "Recomputed" : "Recomputed (no change)", result);
|
|
@@ -29277,8 +29934,8 @@ init_frontmatter();
|
|
|
29277
29934
|
init_timestamp();
|
|
29278
29935
|
init_assignment_resolver();
|
|
29279
29936
|
init_event_emit();
|
|
29280
|
-
import { resolve as
|
|
29281
|
-
import { readFile as
|
|
29937
|
+
import { resolve as resolve53 } from "path";
|
|
29938
|
+
import { readFile as readFile35, writeFile as writeFile9 } from "fs/promises";
|
|
29282
29939
|
async function resolveTarget2(target, options) {
|
|
29283
29940
|
const config = await readConfig();
|
|
29284
29941
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
@@ -29289,7 +29946,7 @@ async function resolveTarget2(target, options) {
|
|
|
29289
29946
|
if (!isValidSlug(target)) {
|
|
29290
29947
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
29291
29948
|
}
|
|
29292
|
-
const assignmentMd =
|
|
29949
|
+
const assignmentMd = resolve53(baseDir, options.project, "assignments", target, "assignment.md");
|
|
29293
29950
|
if (!await fileExists(assignmentMd)) {
|
|
29294
29951
|
throw new Error(`Assignment "${target}" not found in project "${options.project}".`);
|
|
29295
29952
|
}
|
|
@@ -29299,18 +29956,18 @@ async function resolveTarget2(target, options) {
|
|
|
29299
29956
|
if (resolved) {
|
|
29300
29957
|
return {
|
|
29301
29958
|
kind: "assignment",
|
|
29302
|
-
filePath:
|
|
29959
|
+
filePath: resolve53(resolved.assignmentDir, "assignment.md"),
|
|
29303
29960
|
label: resolved.projectSlug ? `assignment "${resolved.projectSlug}/${resolved.assignmentSlug}"` : `assignment "${target}"`
|
|
29304
29961
|
};
|
|
29305
29962
|
}
|
|
29306
|
-
const projectMd =
|
|
29963
|
+
const projectMd = resolve53(baseDir, target, "project.md");
|
|
29307
29964
|
if (await fileExists(projectMd)) {
|
|
29308
29965
|
return { kind: "project", filePath: projectMd, label: `project "${target}"` };
|
|
29309
29966
|
}
|
|
29310
29967
|
return null;
|
|
29311
29968
|
}
|
|
29312
29969
|
async function writeArchiveState(filePath, archived, reason) {
|
|
29313
|
-
const content = await
|
|
29970
|
+
const content = await readFile35(filePath, "utf-8");
|
|
29314
29971
|
const updated = updateAssignmentFile(content, {
|
|
29315
29972
|
archived,
|
|
29316
29973
|
archivedAt: archived ? nowTimestamp() : null,
|
|
@@ -29322,7 +29979,7 @@ async function writeArchiveState(filePath, archived, reason) {
|
|
|
29322
29979
|
async function emitArchiveEvent(resolved, type, reason) {
|
|
29323
29980
|
if (resolved.kind !== "assignment") return;
|
|
29324
29981
|
try {
|
|
29325
|
-
const fm = parseAssignmentFrontmatter(await
|
|
29982
|
+
const fm = parseAssignmentFrontmatter(await readFile35(resolved.filePath, "utf-8"));
|
|
29326
29983
|
emitEvent({
|
|
29327
29984
|
assignmentId: fm.id,
|
|
29328
29985
|
projectSlug: fm.project,
|
|
@@ -29392,8 +30049,8 @@ init_config2();
|
|
|
29392
30049
|
init_frontmatter();
|
|
29393
30050
|
init_timestamp();
|
|
29394
30051
|
init_event_emit();
|
|
29395
|
-
import { resolve as
|
|
29396
|
-
import { readdir as readdir20, readFile as
|
|
30052
|
+
import { resolve as resolve54 } from "path";
|
|
30053
|
+
import { readdir as readdir20, readFile as readFile36 } from "fs/promises";
|
|
29397
30054
|
var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
29398
30055
|
function objectiveIsFleshedOut(content) {
|
|
29399
30056
|
const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
|
|
@@ -29415,11 +30072,11 @@ async function collectCandidates(baseDirs) {
|
|
|
29415
30072
|
for (const m of projects) {
|
|
29416
30073
|
if (!m.isDirectory()) continue;
|
|
29417
30074
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29418
|
-
const directAssignmentMd =
|
|
30075
|
+
const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
|
|
29419
30076
|
if (await fileExists(directAssignmentMd)) {
|
|
29420
30077
|
const fm = await parseSafe(directAssignmentMd);
|
|
29421
30078
|
if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
|
|
29422
|
-
const content = await
|
|
30079
|
+
const content = await readFile36(directAssignmentMd, "utf-8");
|
|
29423
30080
|
if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
|
|
29424
30081
|
candidates.push({
|
|
29425
30082
|
projectSlug: null,
|
|
@@ -29432,17 +30089,17 @@ async function collectCandidates(baseDirs) {
|
|
|
29432
30089
|
}
|
|
29433
30090
|
continue;
|
|
29434
30091
|
}
|
|
29435
|
-
const assignmentsDir2 =
|
|
30092
|
+
const assignmentsDir2 = resolve54(baseDir, m.name, "assignments");
|
|
29436
30093
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
29437
30094
|
const entries = await readdir20(assignmentsDir2, { withFileTypes: true });
|
|
29438
30095
|
for (const a of entries) {
|
|
29439
30096
|
if (!a.isDirectory()) continue;
|
|
29440
30097
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29441
|
-
const assignmentMd =
|
|
30098
|
+
const assignmentMd = resolve54(assignmentsDir2, a.name, "assignment.md");
|
|
29442
30099
|
if (!await fileExists(assignmentMd)) continue;
|
|
29443
30100
|
const fm = await parseSafe(assignmentMd);
|
|
29444
30101
|
if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
|
|
29445
|
-
const content = await
|
|
30102
|
+
const content = await readFile36(assignmentMd, "utf-8");
|
|
29446
30103
|
if (!objectiveIsFleshedOut(content)) continue;
|
|
29447
30104
|
if (!hasAcceptanceCriteria(content)) continue;
|
|
29448
30105
|
candidates.push({
|
|
@@ -29459,7 +30116,7 @@ async function collectCandidates(baseDirs) {
|
|
|
29459
30116
|
}
|
|
29460
30117
|
async function parseSafe(path) {
|
|
29461
30118
|
try {
|
|
29462
|
-
const content = await
|
|
30119
|
+
const content = await readFile36(path, "utf-8");
|
|
29463
30120
|
return parseAssignmentFrontmatter(content);
|
|
29464
30121
|
} catch {
|
|
29465
30122
|
return null;
|
|
@@ -29489,7 +30146,7 @@ async function migrateStatusesCommand(options) {
|
|
|
29489
30146
|
let migrated = 0;
|
|
29490
30147
|
await withSuppressedEvents(async () => {
|
|
29491
30148
|
for (const c2 of candidates) {
|
|
29492
|
-
const content = await
|
|
30149
|
+
const content = await readFile36(c2.assignmentMd, "utf-8");
|
|
29493
30150
|
const updated = appendStatusHistoryEntry(
|
|
29494
30151
|
updateAssignmentFile(content, {
|
|
29495
30152
|
status: c2.toStatus,
|
|
@@ -29511,11 +30168,11 @@ init_config2();
|
|
|
29511
30168
|
init_frontmatter();
|
|
29512
30169
|
init_event_emit();
|
|
29513
30170
|
init_types();
|
|
29514
|
-
import { resolve as
|
|
29515
|
-
import { readdir as readdir21, readFile as
|
|
30171
|
+
import { resolve as resolve55 } from "path";
|
|
30172
|
+
import { readdir as readdir21, readFile as readFile37 } from "fs/promises";
|
|
29516
30173
|
async function parseSafe2(path) {
|
|
29517
30174
|
try {
|
|
29518
|
-
return parseAssignmentFrontmatter(await
|
|
30175
|
+
return parseAssignmentFrontmatter(await readFile37(path, "utf-8"));
|
|
29519
30176
|
} catch {
|
|
29520
30177
|
return null;
|
|
29521
30178
|
}
|
|
@@ -29542,7 +30199,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
|
|
|
29542
30199
|
for (const m of entries) {
|
|
29543
30200
|
if (!m.isDirectory()) continue;
|
|
29544
30201
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29545
|
-
const directAssignmentMd =
|
|
30202
|
+
const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
|
|
29546
30203
|
if (await fileExists(directAssignmentMd)) {
|
|
29547
30204
|
if (seen.has(directAssignmentMd)) continue;
|
|
29548
30205
|
const fm = await parseSafe2(directAssignmentMd);
|
|
@@ -29557,13 +30214,13 @@ async function collectTargets(baseDirs, terminalStatuses3) {
|
|
|
29557
30214
|
}
|
|
29558
30215
|
continue;
|
|
29559
30216
|
}
|
|
29560
|
-
const assignmentsBase =
|
|
30217
|
+
const assignmentsBase = resolve55(baseDir, m.name, "assignments");
|
|
29561
30218
|
if (!await fileExists(assignmentsBase)) continue;
|
|
29562
30219
|
const slugs = await readdir21(assignmentsBase, { withFileTypes: true });
|
|
29563
30220
|
for (const a of slugs) {
|
|
29564
30221
|
if (!a.isDirectory()) continue;
|
|
29565
30222
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29566
|
-
const assignmentMd =
|
|
30223
|
+
const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
|
|
29567
30224
|
if (!await fileExists(assignmentMd)) continue;
|
|
29568
30225
|
if (seen.has(assignmentMd)) continue;
|
|
29569
30226
|
const fm = await parseSafe2(assignmentMd);
|
|
@@ -29607,7 +30264,7 @@ async function migrateStatusHistoryCommand(options) {
|
|
|
29607
30264
|
await withSuppressedEvents(async () => {
|
|
29608
30265
|
for (const t of targets) {
|
|
29609
30266
|
try {
|
|
29610
|
-
const content = await
|
|
30267
|
+
const content = await readFile37(t.assignmentMd, "utf-8");
|
|
29611
30268
|
if (parseAssignmentFrontmatter(content).statusHistory.length > 0) continue;
|
|
29612
30269
|
const seededContent = appendStatusHistoryEntry(content, {
|
|
29613
30270
|
at: t.seedAt,
|
|
@@ -29635,11 +30292,11 @@ init_fs();
|
|
|
29635
30292
|
init_config2();
|
|
29636
30293
|
init_parser();
|
|
29637
30294
|
init_events_db();
|
|
29638
|
-
import { resolve as
|
|
29639
|
-
import { readdir as readdir22, readFile as
|
|
30295
|
+
import { resolve as resolve56 } from "path";
|
|
30296
|
+
import { readdir as readdir22, readFile as readFile38 } from "fs/promises";
|
|
29640
30297
|
async function parseSafe3(path) {
|
|
29641
30298
|
try {
|
|
29642
|
-
return parseAssignmentFull(await
|
|
30299
|
+
return parseAssignmentFull(await readFile38(path, "utf-8"));
|
|
29643
30300
|
} catch {
|
|
29644
30301
|
return null;
|
|
29645
30302
|
}
|
|
@@ -29676,7 +30333,7 @@ async function collectTargets2(baseDirs) {
|
|
|
29676
30333
|
for (const m of entries) {
|
|
29677
30334
|
if (!m.isDirectory()) continue;
|
|
29678
30335
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29679
|
-
const directAssignmentMd =
|
|
30336
|
+
const directAssignmentMd = resolve56(baseDir, m.name, "assignment.md");
|
|
29680
30337
|
if (await fileExists(directAssignmentMd)) {
|
|
29681
30338
|
if (seen.has(directAssignmentMd)) continue;
|
|
29682
30339
|
seen.add(directAssignmentMd);
|
|
@@ -29694,13 +30351,13 @@ async function collectTargets2(baseDirs) {
|
|
|
29694
30351
|
}
|
|
29695
30352
|
continue;
|
|
29696
30353
|
}
|
|
29697
|
-
const assignmentsBase =
|
|
30354
|
+
const assignmentsBase = resolve56(baseDir, m.name, "assignments");
|
|
29698
30355
|
if (!await fileExists(assignmentsBase)) continue;
|
|
29699
30356
|
const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
|
|
29700
30357
|
for (const a of slugs) {
|
|
29701
30358
|
if (!a.isDirectory()) continue;
|
|
29702
30359
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29703
|
-
const assignmentMd =
|
|
30360
|
+
const assignmentMd = resolve56(assignmentsBase, a.name, "assignment.md");
|
|
29704
30361
|
if (!await fileExists(assignmentMd)) continue;
|
|
29705
30362
|
if (seen.has(assignmentMd)) continue;
|
|
29706
30363
|
seen.add(assignmentMd);
|
|
@@ -29781,8 +30438,8 @@ init_frontmatter();
|
|
|
29781
30438
|
init_facts();
|
|
29782
30439
|
init_derive();
|
|
29783
30440
|
init_recompute();
|
|
29784
|
-
import { readdir as readdir23, readFile as
|
|
29785
|
-
import { resolve as
|
|
30441
|
+
import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
|
|
30442
|
+
import { resolve as resolve57 } from "path";
|
|
29786
30443
|
var IMPLEMENTATION_STATUSES = /* @__PURE__ */ new Set(["in_progress", "review", "code_review"]);
|
|
29787
30444
|
var REVIEW_STATUSES = /* @__PURE__ */ new Set(["review", "code_review"]);
|
|
29788
30445
|
async function listTargets(projectsDir2, standaloneDir) {
|
|
@@ -29793,15 +30450,15 @@ async function listTargets(projectsDir2, standaloneDir) {
|
|
|
29793
30450
|
} catch {
|
|
29794
30451
|
}
|
|
29795
30452
|
for (const project of projects) {
|
|
29796
|
-
const projectDir =
|
|
30453
|
+
const projectDir = resolve57(projectsDir2, project);
|
|
29797
30454
|
let slugs = [];
|
|
29798
30455
|
try {
|
|
29799
|
-
slugs = await readdir23(
|
|
30456
|
+
slugs = await readdir23(resolve57(projectDir, "assignments"));
|
|
29800
30457
|
} catch {
|
|
29801
30458
|
continue;
|
|
29802
30459
|
}
|
|
29803
30460
|
for (const slug of slugs) {
|
|
29804
|
-
const path =
|
|
30461
|
+
const path = resolve57(projectDir, "assignments", slug, "assignment.md");
|
|
29805
30462
|
if (await fileExists(path)) targets.push({ path, projectDir, ref: `${project}/${slug}` });
|
|
29806
30463
|
}
|
|
29807
30464
|
}
|
|
@@ -29811,7 +30468,7 @@ async function listTargets(projectsDir2, standaloneDir) {
|
|
|
29811
30468
|
} catch {
|
|
29812
30469
|
}
|
|
29813
30470
|
for (const id of ids) {
|
|
29814
|
-
const path =
|
|
30471
|
+
const path = resolve57(standaloneDir, id, "assignment.md");
|
|
29815
30472
|
if (await fileExists(path)) targets.push({ path, projectDir: null, ref: id });
|
|
29816
30473
|
}
|
|
29817
30474
|
return targets;
|
|
@@ -29866,7 +30523,7 @@ async function migrateDeriveCommand(options) {
|
|
|
29866
30523
|
for (const target of targets) {
|
|
29867
30524
|
let content;
|
|
29868
30525
|
try {
|
|
29869
|
-
content = await
|
|
30526
|
+
content = await readFile39(target.path, "utf-8");
|
|
29870
30527
|
} catch {
|
|
29871
30528
|
continue;
|
|
29872
30529
|
}
|
|
@@ -29881,7 +30538,7 @@ async function migrateDeriveCommand(options) {
|
|
|
29881
30538
|
const seededFm = parseAssignmentFrontmatter(seededContent);
|
|
29882
30539
|
const body = seededContent.replace(/^---\n[\s\S]*?\n---/, "");
|
|
29883
30540
|
const facts = await computeFacts({
|
|
29884
|
-
assignmentDir:
|
|
30541
|
+
assignmentDir: resolve57(target.path, ".."),
|
|
29885
30542
|
frontmatter: seededFm,
|
|
29886
30543
|
body,
|
|
29887
30544
|
projectDir: target.projectDir,
|
|
@@ -29941,7 +30598,7 @@ async function migrateDeriveCommand(options) {
|
|
|
29941
30598
|
}
|
|
29942
30599
|
|
|
29943
30600
|
// src/commands/complete.ts
|
|
29944
|
-
import { resolve as
|
|
30601
|
+
import { resolve as resolve58 } from "path";
|
|
29945
30602
|
init_config2();
|
|
29946
30603
|
init_paths();
|
|
29947
30604
|
init_assignment_resolver();
|
|
@@ -29955,12 +30612,12 @@ async function completeCommand(assignment, options) {
|
|
|
29955
30612
|
let projectDir;
|
|
29956
30613
|
let changedSlug;
|
|
29957
30614
|
if (options.project) {
|
|
29958
|
-
projectDir =
|
|
30615
|
+
projectDir = resolve58(baseDir, options.project);
|
|
29959
30616
|
changedSlug = assignment;
|
|
29960
30617
|
} else {
|
|
29961
30618
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
29962
30619
|
if (!resolved) return;
|
|
29963
|
-
projectDir = resolved.standalone ? null :
|
|
30620
|
+
projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
|
|
29964
30621
|
changedSlug = resolved.assignmentSlug;
|
|
29965
30622
|
}
|
|
29966
30623
|
if (projectDir) {
|
|
@@ -29991,7 +30648,7 @@ async function reviewCommand(assignment, options) {
|
|
|
29991
30648
|
}
|
|
29992
30649
|
|
|
29993
30650
|
// src/commands/fail.ts
|
|
29994
|
-
import { resolve as
|
|
30651
|
+
import { resolve as resolve59 } from "path";
|
|
29995
30652
|
init_config2();
|
|
29996
30653
|
init_paths();
|
|
29997
30654
|
init_assignment_resolver();
|
|
@@ -30005,12 +30662,12 @@ async function failCommand(assignment, options) {
|
|
|
30005
30662
|
let projectDir;
|
|
30006
30663
|
let changedSlug;
|
|
30007
30664
|
if (options.project) {
|
|
30008
|
-
projectDir =
|
|
30665
|
+
projectDir = resolve59(baseDir, options.project);
|
|
30009
30666
|
changedSlug = assignment;
|
|
30010
30667
|
} else {
|
|
30011
30668
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30012
30669
|
if (!resolved) return;
|
|
30013
|
-
projectDir = resolved.standalone ? null :
|
|
30670
|
+
projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
|
|
30014
30671
|
changedSlug = resolved.assignmentSlug;
|
|
30015
30672
|
}
|
|
30016
30673
|
if (projectDir) {
|
|
@@ -30026,7 +30683,7 @@ async function failCommand(assignment, options) {
|
|
|
30026
30683
|
}
|
|
30027
30684
|
|
|
30028
30685
|
// src/commands/reopen.ts
|
|
30029
|
-
import { resolve as
|
|
30686
|
+
import { resolve as resolve60 } from "path";
|
|
30030
30687
|
init_config2();
|
|
30031
30688
|
init_paths();
|
|
30032
30689
|
init_assignment_resolver();
|
|
@@ -30042,14 +30699,14 @@ async function reopenCommand(assignment, options) {
|
|
|
30042
30699
|
let projectDir;
|
|
30043
30700
|
let changedSlug;
|
|
30044
30701
|
if (options.project) {
|
|
30045
|
-
projectDir =
|
|
30046
|
-
assignmentPath =
|
|
30702
|
+
projectDir = resolve60(baseDir, options.project);
|
|
30703
|
+
assignmentPath = resolve60(projectDir, "assignments", assignment, "assignment.md");
|
|
30047
30704
|
changedSlug = assignment;
|
|
30048
30705
|
} else {
|
|
30049
30706
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30050
30707
|
if (!resolved) return;
|
|
30051
|
-
assignmentPath =
|
|
30052
|
-
projectDir = resolved.standalone ? null :
|
|
30708
|
+
assignmentPath = resolve60(resolved.assignmentDir, "assignment.md");
|
|
30709
|
+
projectDir = resolved.standalone ? null : resolve60(resolved.assignmentDir, "..", "..");
|
|
30053
30710
|
changedSlug = resolved.assignmentSlug;
|
|
30054
30711
|
}
|
|
30055
30712
|
const derived = await recomputeAndWrite(assignmentPath, {
|
|
@@ -30083,7 +30740,7 @@ import {
|
|
|
30083
30740
|
readdir as readdir24,
|
|
30084
30741
|
symlink,
|
|
30085
30742
|
lstat,
|
|
30086
|
-
readFile as
|
|
30743
|
+
readFile as readFile40,
|
|
30087
30744
|
readlink,
|
|
30088
30745
|
rename as rename9,
|
|
30089
30746
|
rm as rm6,
|
|
@@ -30092,20 +30749,20 @@ import {
|
|
|
30092
30749
|
} from "fs/promises";
|
|
30093
30750
|
import { existsSync as existsSync4 } from "fs";
|
|
30094
30751
|
import { homedir as homedir8 } from "os";
|
|
30095
|
-
import { basename as
|
|
30752
|
+
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute7, relative as relative2, resolve as resolve62 } from "path";
|
|
30096
30753
|
|
|
30097
30754
|
// src/utils/package-root.ts
|
|
30098
30755
|
init_fs();
|
|
30099
|
-
import { dirname as dirname14, resolve as
|
|
30756
|
+
import { dirname as dirname14, resolve as resolve61 } from "path";
|
|
30100
30757
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
30101
30758
|
async function findPackageRoot(expectedRelativePath) {
|
|
30102
30759
|
let currentDir = dirname14(fileURLToPath4(import.meta.url));
|
|
30103
30760
|
while (true) {
|
|
30104
|
-
const candidate =
|
|
30761
|
+
const candidate = resolve61(currentDir, expectedRelativePath);
|
|
30105
30762
|
if (await fileExists(candidate)) {
|
|
30106
30763
|
return currentDir;
|
|
30107
30764
|
}
|
|
30108
|
-
const parentDir =
|
|
30765
|
+
const parentDir = resolve61(currentDir, "..");
|
|
30109
30766
|
if (parentDir === currentDir) {
|
|
30110
30767
|
throw new Error(
|
|
30111
30768
|
`Could not locate package root containing ${expectedRelativePath}.`
|
|
@@ -30126,25 +30783,25 @@ function getPluginManifestRelativePath(pluginKind) {
|
|
|
30126
30783
|
}
|
|
30127
30784
|
function getDefaultPluginTargetDir(pluginKind) {
|
|
30128
30785
|
const home2 = homedir8();
|
|
30129
|
-
return pluginKind === "claude" ?
|
|
30786
|
+
return pluginKind === "claude" ? resolve62(home2, ".claude", "plugins", "syntaur") : resolve62(home2, "plugins", "syntaur");
|
|
30130
30787
|
}
|
|
30131
30788
|
function getDefaultMarketplacePath() {
|
|
30132
|
-
return
|
|
30789
|
+
return resolve62(homedir8(), ".agents", "plugins", "marketplace.json");
|
|
30133
30790
|
}
|
|
30134
30791
|
function getClaudeMarketplacesRoot() {
|
|
30135
|
-
return
|
|
30792
|
+
return resolve62(homedir8(), ".claude", "plugins", "marketplaces");
|
|
30136
30793
|
}
|
|
30137
30794
|
function getClaudeKnownMarketplacesPath() {
|
|
30138
|
-
return
|
|
30795
|
+
return resolve62(homedir8(), ".claude", "plugins", "known_marketplaces.json");
|
|
30139
30796
|
}
|
|
30140
30797
|
function getClaudeInstalledPluginsPath() {
|
|
30141
|
-
return
|
|
30798
|
+
return resolve62(homedir8(), ".claude", "plugins", "installed_plugins.json");
|
|
30142
30799
|
}
|
|
30143
30800
|
function getInstallMarkerPath(targetDir) {
|
|
30144
|
-
return
|
|
30801
|
+
return resolve62(targetDir, INSTALL_MARKER_FILENAME);
|
|
30145
30802
|
}
|
|
30146
30803
|
async function readPackageManifest(packageRoot) {
|
|
30147
|
-
const raw2 = await
|
|
30804
|
+
const raw2 = await readFile40(resolve62(packageRoot, "package.json"), "utf-8");
|
|
30148
30805
|
return JSON.parse(raw2);
|
|
30149
30806
|
}
|
|
30150
30807
|
async function readJsonFileIfExists(pathValue) {
|
|
@@ -30152,7 +30809,7 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
30152
30809
|
return null;
|
|
30153
30810
|
}
|
|
30154
30811
|
try {
|
|
30155
|
-
const raw2 = await
|
|
30812
|
+
const raw2 = await readFile40(pathValue, "utf-8");
|
|
30156
30813
|
return JSON.parse(raw2);
|
|
30157
30814
|
} catch {
|
|
30158
30815
|
return null;
|
|
@@ -30160,15 +30817,15 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
30160
30817
|
}
|
|
30161
30818
|
async function readClaudePluginManifest(pluginDir) {
|
|
30162
30819
|
return await readJsonFileIfExists(
|
|
30163
|
-
|
|
30820
|
+
resolve62(pluginDir, ".claude-plugin", "plugin.json")
|
|
30164
30821
|
) ?? {};
|
|
30165
30822
|
}
|
|
30166
30823
|
async function readPluginManifestName(targetDir, pluginKind) {
|
|
30167
|
-
const manifestPath =
|
|
30824
|
+
const manifestPath = resolve62(targetDir, getPluginManifestRelativePath(pluginKind));
|
|
30168
30825
|
if (!await fileExists(manifestPath)) {
|
|
30169
30826
|
return void 0;
|
|
30170
30827
|
}
|
|
30171
|
-
const raw2 = await
|
|
30828
|
+
const raw2 = await readFile40(manifestPath, "utf-8");
|
|
30172
30829
|
const parsed = JSON.parse(raw2);
|
|
30173
30830
|
return parsed.name;
|
|
30174
30831
|
}
|
|
@@ -30178,7 +30835,7 @@ async function readInstallMetadata(targetDir) {
|
|
|
30178
30835
|
return null;
|
|
30179
30836
|
}
|
|
30180
30837
|
try {
|
|
30181
|
-
const raw2 = await
|
|
30838
|
+
const raw2 = await readFile40(markerPath, "utf-8");
|
|
30182
30839
|
return JSON.parse(raw2);
|
|
30183
30840
|
} catch {
|
|
30184
30841
|
return null;
|
|
@@ -30191,7 +30848,7 @@ async function getInstallStatus(targetDir, pluginKind) {
|
|
|
30191
30848
|
const info = await lstat(targetDir);
|
|
30192
30849
|
if (info.isSymbolicLink()) {
|
|
30193
30850
|
const symlinkTarget = await readlink(targetDir);
|
|
30194
|
-
const resolvedTarget =
|
|
30851
|
+
const resolvedTarget = resolve62(dirname15(targetDir), symlinkTarget);
|
|
30195
30852
|
const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
|
|
30196
30853
|
return {
|
|
30197
30854
|
exists: true,
|
|
@@ -30237,7 +30894,7 @@ async function installLink(paths) {
|
|
|
30237
30894
|
await ensureDir(dirname15(paths.targetDir));
|
|
30238
30895
|
await rm6(paths.targetDir, { recursive: true, force: true });
|
|
30239
30896
|
await ensureDir(dirname15(paths.targetDir));
|
|
30240
|
-
await symlink(
|
|
30897
|
+
await symlink(resolve62(paths.sourceDir), paths.targetDir, "dir");
|
|
30241
30898
|
}
|
|
30242
30899
|
async function removeInstallMarker(targetDir) {
|
|
30243
30900
|
const markerPath = getInstallMarkerPath(targetDir);
|
|
@@ -30251,13 +30908,13 @@ function normalizeAbsoluteInstallPath(pathValue, label) {
|
|
|
30251
30908
|
if (!isAbsolute7(expanded)) {
|
|
30252
30909
|
throw new Error(`${label} must be an absolute path.`);
|
|
30253
30910
|
}
|
|
30254
|
-
return
|
|
30911
|
+
return resolve62(expanded);
|
|
30255
30912
|
}
|
|
30256
30913
|
async function resolvePluginPaths(pluginKind, targetDir) {
|
|
30257
30914
|
const packageRoot = await findPackageRoot(getPluginRelativePath(pluginKind));
|
|
30258
30915
|
return {
|
|
30259
30916
|
packageRoot,
|
|
30260
|
-
sourceDir:
|
|
30917
|
+
sourceDir: resolve62(packageRoot, getPluginRelativePath(pluginKind)),
|
|
30261
30918
|
targetDir: targetDir ?? getDefaultPluginTargetDir(pluginKind)
|
|
30262
30919
|
};
|
|
30263
30920
|
}
|
|
@@ -30301,7 +30958,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
|
30301
30958
|
}
|
|
30302
30959
|
if (await fileExists(manifestPath)) {
|
|
30303
30960
|
try {
|
|
30304
|
-
const prev = await
|
|
30961
|
+
const prev = await readFile40(manifestPath, "utf-8");
|
|
30305
30962
|
JSON.parse(prev);
|
|
30306
30963
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30307
30964
|
await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
@@ -30382,8 +31039,8 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
30382
31039
|
if (!entry.isDirectory()) {
|
|
30383
31040
|
continue;
|
|
30384
31041
|
}
|
|
30385
|
-
const candidateRoot =
|
|
30386
|
-
const manifestPath =
|
|
31042
|
+
const candidateRoot = resolve62(rootDir, entry.name);
|
|
31043
|
+
const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
30387
31044
|
if (!await fileExists(manifestPath)) {
|
|
30388
31045
|
continue;
|
|
30389
31046
|
}
|
|
@@ -30410,11 +31067,11 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
30410
31067
|
if (!installLocation) {
|
|
30411
31068
|
continue;
|
|
30412
31069
|
}
|
|
30413
|
-
const candidateRoot =
|
|
31070
|
+
const candidateRoot = resolve62(expandHome(installLocation));
|
|
30414
31071
|
if (seen.has(candidateRoot)) {
|
|
30415
31072
|
continue;
|
|
30416
31073
|
}
|
|
30417
|
-
const manifestPath =
|
|
31074
|
+
const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
30418
31075
|
if (!await fileExists(manifestPath)) {
|
|
30419
31076
|
continue;
|
|
30420
31077
|
}
|
|
@@ -30442,14 +31099,14 @@ async function getPreferredClaudeMarketplace() {
|
|
|
30442
31099
|
name: candidate.name,
|
|
30443
31100
|
rootDir: candidate.rootDir,
|
|
30444
31101
|
manifestPath: candidate.manifestPath,
|
|
30445
|
-
targetDir:
|
|
31102
|
+
targetDir: resolve62(candidate.rootDir, "plugins", "syntaur")
|
|
30446
31103
|
};
|
|
30447
31104
|
}
|
|
30448
31105
|
async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
30449
31106
|
const manifestPath = getClaudeKnownMarketplacesPath();
|
|
30450
31107
|
let existing = {};
|
|
30451
31108
|
if (await fileExists(manifestPath)) {
|
|
30452
|
-
const raw2 = await
|
|
31109
|
+
const raw2 = await readFile40(manifestPath, "utf-8");
|
|
30453
31110
|
try {
|
|
30454
31111
|
const parsed = JSON.parse(raw2);
|
|
30455
31112
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -30476,7 +31133,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
30476
31133
|
existing[name].autoUpdate = true;
|
|
30477
31134
|
await ensureDir(dirname15(manifestPath));
|
|
30478
31135
|
if (await fileExists(manifestPath)) {
|
|
30479
|
-
const prev = await
|
|
31136
|
+
const prev = await readFile40(manifestPath, "utf-8");
|
|
30480
31137
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30481
31138
|
await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
30482
31139
|
}
|
|
@@ -30490,11 +31147,11 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
|
|
|
30490
31147
|
return registerKnownClaudeMarketplace(options.name, options.rootDir);
|
|
30491
31148
|
}
|
|
30492
31149
|
async function setSyntaurPluginEnabled(options) {
|
|
30493
|
-
const settingsPath =
|
|
31150
|
+
const settingsPath = resolve62(homedir8(), ".claude", "settings.json");
|
|
30494
31151
|
const key = `syntaur@${options.marketplaceName}`;
|
|
30495
31152
|
let parsed = {};
|
|
30496
31153
|
if (await fileExists(settingsPath)) {
|
|
30497
|
-
const raw2 = await
|
|
31154
|
+
const raw2 = await readFile40(settingsPath, "utf-8");
|
|
30498
31155
|
try {
|
|
30499
31156
|
parsed = JSON.parse(raw2);
|
|
30500
31157
|
} catch {
|
|
@@ -30513,7 +31170,7 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
30513
31170
|
await ensureDir(dirname15(settingsPath));
|
|
30514
31171
|
if (await fileExists(settingsPath)) {
|
|
30515
31172
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30516
|
-
const prev = await
|
|
31173
|
+
const prev = await readFile40(settingsPath, "utf-8");
|
|
30517
31174
|
await writeFile10(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
|
|
30518
31175
|
}
|
|
30519
31176
|
const tmpPath = `${settingsPath}.tmp`;
|
|
@@ -30527,9 +31184,9 @@ async function ensureClaudeUserMarketplace() {
|
|
|
30527
31184
|
if (existing) {
|
|
30528
31185
|
return existing;
|
|
30529
31186
|
}
|
|
30530
|
-
const rootDir =
|
|
30531
|
-
const manifestPath =
|
|
30532
|
-
await ensureDir(
|
|
31187
|
+
const rootDir = resolve62(getClaudeMarketplacesRoot(), "user-plugins");
|
|
31188
|
+
const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
|
|
31189
|
+
await ensureDir(resolve62(rootDir, "plugins"));
|
|
30533
31190
|
if (!await fileExists(manifestPath)) {
|
|
30534
31191
|
const scaffold = {
|
|
30535
31192
|
plugins: []
|
|
@@ -30548,22 +31205,22 @@ async function ensureClaudeUserMarketplace() {
|
|
|
30548
31205
|
name: "user-plugins",
|
|
30549
31206
|
rootDir,
|
|
30550
31207
|
manifestPath,
|
|
30551
|
-
targetDir:
|
|
31208
|
+
targetDir: resolve62(rootDir, "plugins", "syntaur")
|
|
30552
31209
|
};
|
|
30553
31210
|
}
|
|
30554
31211
|
async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
30555
31212
|
const normalizedTargetDir = normalizeAbsoluteInstallPath(targetDir, "Claude plugin target");
|
|
30556
31213
|
const pluginsDir = dirname15(normalizedTargetDir);
|
|
30557
|
-
if (
|
|
31214
|
+
if (basename8(pluginsDir) !== "plugins") {
|
|
30558
31215
|
return null;
|
|
30559
31216
|
}
|
|
30560
31217
|
const rootDir = dirname15(pluginsDir);
|
|
30561
|
-
const manifestPath =
|
|
31218
|
+
const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
|
|
30562
31219
|
if (!await fileExists(manifestPath)) {
|
|
30563
31220
|
return null;
|
|
30564
31221
|
}
|
|
30565
31222
|
const marketplace = await readClaudeMarketplaceFile(manifestPath);
|
|
30566
|
-
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);
|
|
30567
31224
|
return {
|
|
30568
31225
|
name,
|
|
30569
31226
|
rootDir,
|
|
@@ -30574,7 +31231,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
|
30574
31231
|
async function findManagedClaudeMarketplacePluginDir() {
|
|
30575
31232
|
const marketplaces = await listClaudeMarketplaceCandidates();
|
|
30576
31233
|
for (const marketplace of marketplaces) {
|
|
30577
|
-
const targetDir =
|
|
31234
|
+
const targetDir = resolve62(marketplace.rootDir, "plugins", "syntaur");
|
|
30578
31235
|
const status = await getInstallStatus(targetDir, "claude");
|
|
30579
31236
|
if (status.exists && status.managed) {
|
|
30580
31237
|
return targetDir;
|
|
@@ -30699,7 +31356,7 @@ async function installManagedPlugin(options) {
|
|
|
30699
31356
|
`${paths.targetDir} already exists and is not a Syntaur-managed install. Remove it manually before installing Syntaur there.`
|
|
30700
31357
|
);
|
|
30701
31358
|
}
|
|
30702
|
-
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) {
|
|
30703
31360
|
return {
|
|
30704
31361
|
targetDir: paths.targetDir,
|
|
30705
31362
|
sourceDir: paths.sourceDir,
|
|
@@ -30751,7 +31408,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
30751
31408
|
plugins: []
|
|
30752
31409
|
};
|
|
30753
31410
|
}
|
|
30754
|
-
const raw2 = await
|
|
31411
|
+
const raw2 = await readFile40(marketplacePath, "utf-8");
|
|
30755
31412
|
const parsed = JSON.parse(raw2);
|
|
30756
31413
|
return {
|
|
30757
31414
|
name: parsed.name ?? "local",
|
|
@@ -30918,13 +31575,13 @@ async function recommendMarketplacePath() {
|
|
|
30918
31575
|
return configuredOrManaged ?? getDefaultMarketplacePath();
|
|
30919
31576
|
}
|
|
30920
31577
|
async function isSyntaurDataInstalled() {
|
|
30921
|
-
return fileExists(
|
|
31578
|
+
return fileExists(resolve62(syntaurRoot(), "config.md"));
|
|
30922
31579
|
}
|
|
30923
31580
|
async function removeSyntaurData() {
|
|
30924
31581
|
await rm6(syntaurRoot(), { recursive: true, force: true });
|
|
30925
31582
|
}
|
|
30926
31583
|
async function getConfiguredProjectDir() {
|
|
30927
|
-
if (!await fileExists(
|
|
31584
|
+
if (!await fileExists(resolve62(syntaurRoot(), "config.md"))) {
|
|
30928
31585
|
return null;
|
|
30929
31586
|
}
|
|
30930
31587
|
return (await readConfig()).defaultProjectDir;
|
|
@@ -30993,24 +31650,24 @@ async function textPrompt(question, defaultValue) {
|
|
|
30993
31650
|
|
|
30994
31651
|
// src/utils/install-skills.ts
|
|
30995
31652
|
init_fs();
|
|
30996
|
-
import { readFile as
|
|
30997
|
-
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";
|
|
30998
31655
|
import { homedir as homedir10 } from "os";
|
|
30999
31656
|
|
|
31000
31657
|
// src/utils/plugin-state.ts
|
|
31001
31658
|
init_fs();
|
|
31002
|
-
import { readFile as
|
|
31003
|
-
import { resolve as
|
|
31659
|
+
import { readFile as readFile41 } from "fs/promises";
|
|
31660
|
+
import { resolve as resolve63 } from "path";
|
|
31004
31661
|
import { homedir as homedir9 } from "os";
|
|
31005
31662
|
function settingsPathFor(agent) {
|
|
31006
|
-
if (agent === "claude") return
|
|
31663
|
+
if (agent === "claude") return resolve63(homedir9(), ".claude", "settings.json");
|
|
31007
31664
|
return null;
|
|
31008
31665
|
}
|
|
31009
31666
|
async function readJsonOrNull(path) {
|
|
31010
31667
|
if (!path) return null;
|
|
31011
31668
|
if (!await fileExists(path)) return null;
|
|
31012
31669
|
try {
|
|
31013
|
-
const raw2 = await
|
|
31670
|
+
const raw2 = await readFile41(path, "utf-8");
|
|
31014
31671
|
return JSON.parse(raw2);
|
|
31015
31672
|
} catch {
|
|
31016
31673
|
return null;
|
|
@@ -31060,11 +31717,11 @@ var KNOWN_SKILL_NAMES = [
|
|
|
31060
31717
|
var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
|
|
31061
31718
|
async function getSkillsDir() {
|
|
31062
31719
|
const packageRoot = await findPackageRoot("skills");
|
|
31063
|
-
return
|
|
31720
|
+
return resolve64(packageRoot, "skills");
|
|
31064
31721
|
}
|
|
31065
31722
|
function defaultSkillTargetDir(target) {
|
|
31066
|
-
if (target === "claude") return
|
|
31067
|
-
return
|
|
31723
|
+
if (target === "claude") return resolve64(homedir10(), ".claude", "skills");
|
|
31724
|
+
return resolve64(homedir10(), ".codex", "skills");
|
|
31068
31725
|
}
|
|
31069
31726
|
async function walkFiles(root) {
|
|
31070
31727
|
const out = [];
|
|
@@ -31084,7 +31741,7 @@ async function walkFiles(root) {
|
|
|
31084
31741
|
}
|
|
31085
31742
|
async function filesEqual(a, b) {
|
|
31086
31743
|
try {
|
|
31087
|
-
const [ba, bb] = await Promise.all([
|
|
31744
|
+
const [ba, bb] = await Promise.all([readFile42(a), readFile42(b)]);
|
|
31088
31745
|
if (ba.length !== bb.length) return false;
|
|
31089
31746
|
return ba.equals(bb);
|
|
31090
31747
|
} catch {
|
|
@@ -31118,8 +31775,8 @@ async function skillMatches(srcDir, destDir) {
|
|
|
31118
31775
|
}
|
|
31119
31776
|
async function isSymlink(path) {
|
|
31120
31777
|
try {
|
|
31121
|
-
const
|
|
31122
|
-
return
|
|
31778
|
+
const stat17 = await lstat2(path);
|
|
31779
|
+
return stat17.isSymbolicLink();
|
|
31123
31780
|
} catch {
|
|
31124
31781
|
return false;
|
|
31125
31782
|
}
|
|
@@ -31224,7 +31881,7 @@ async function uninstallSkills(options) {
|
|
|
31224
31881
|
if (await isSymlink(destDir)) continue;
|
|
31225
31882
|
const skillMd = join11(destDir, "SKILL.md");
|
|
31226
31883
|
if (!await fileExists(skillMd)) continue;
|
|
31227
|
-
const content = await
|
|
31884
|
+
const content = await readFile42(skillMd, "utf-8").catch(() => "");
|
|
31228
31885
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
31229
31886
|
if (!match || match[1] !== skill) continue;
|
|
31230
31887
|
await rm7(destDir, { recursive: true, force: true });
|
|
@@ -31419,20 +32076,20 @@ import { realpathSync as realpathSync2 } from "fs";
|
|
|
31419
32076
|
init_paths();
|
|
31420
32077
|
init_fs();
|
|
31421
32078
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
31422
|
-
import { readFile as
|
|
31423
|
-
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";
|
|
31424
32081
|
import { spawn as spawn6 } from "child_process";
|
|
31425
32082
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
31426
32083
|
|
|
31427
32084
|
// src/utils/version.ts
|
|
31428
32085
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
31429
|
-
import { readFile as
|
|
32086
|
+
import { readFile as readFile43 } from "fs/promises";
|
|
31430
32087
|
import { dirname as dirname16, join as join12 } from "path";
|
|
31431
32088
|
async function readPackageVersion(scriptUrl) {
|
|
31432
32089
|
try {
|
|
31433
32090
|
const scriptPath = fileURLToPath5(scriptUrl);
|
|
31434
32091
|
const pkgRoot = dirname16(dirname16(scriptPath));
|
|
31435
|
-
const raw2 = await
|
|
32092
|
+
const raw2 = await readFile43(join12(pkgRoot, "package.json"), "utf-8");
|
|
31436
32093
|
const parsed = JSON.parse(raw2);
|
|
31437
32094
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
31438
32095
|
} catch {
|
|
@@ -31441,7 +32098,7 @@ async function readPackageVersion(scriptUrl) {
|
|
|
31441
32098
|
}
|
|
31442
32099
|
|
|
31443
32100
|
// src/utils/npx-prompt.ts
|
|
31444
|
-
var STATE_FILE =
|
|
32101
|
+
var STATE_FILE = resolve65(syntaurRoot(), "npx-install.json");
|
|
31445
32102
|
var META_ARGS2 = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
|
|
31446
32103
|
var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
|
|
31447
32104
|
function isRunningViaNpx(scriptUrl) {
|
|
@@ -31462,7 +32119,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
31462
32119
|
async function readState() {
|
|
31463
32120
|
if (!await fileExists(STATE_FILE)) return null;
|
|
31464
32121
|
try {
|
|
31465
|
-
const raw2 = await
|
|
32122
|
+
const raw2 = await readFile44(STATE_FILE, "utf-8");
|
|
31466
32123
|
return JSON.parse(raw2);
|
|
31467
32124
|
} catch {
|
|
31468
32125
|
return null;
|
|
@@ -31521,7 +32178,7 @@ async function readGlobalVersion() {
|
|
|
31521
32178
|
try {
|
|
31522
32179
|
const manifestPath = join13(rootPath, "syntaur", "package.json");
|
|
31523
32180
|
if (!await fileExists(manifestPath)) return null;
|
|
31524
|
-
const raw2 = await
|
|
32181
|
+
const raw2 = await readFile44(manifestPath, "utf-8");
|
|
31525
32182
|
const parsed = JSON.parse(raw2);
|
|
31526
32183
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
31527
32184
|
} catch {
|
|
@@ -31879,16 +32536,16 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
|
|
|
31879
32536
|
// src/commands/install-statusline.ts
|
|
31880
32537
|
init_paths();
|
|
31881
32538
|
init_fs();
|
|
31882
|
-
import { readFile as
|
|
31883
|
-
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";
|
|
31884
32541
|
import { homedir as homedir12 } from "os";
|
|
31885
32542
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
31886
32543
|
|
|
31887
32544
|
// src/commands/configure-statusline.ts
|
|
31888
32545
|
init_paths();
|
|
31889
32546
|
init_fs();
|
|
31890
|
-
import { readFile as
|
|
31891
|
-
import { resolve as
|
|
32547
|
+
import { readFile as readFile45, writeFile as writeFile11 } from "fs/promises";
|
|
32548
|
+
import { resolve as resolve66, dirname as dirname18 } from "path";
|
|
31892
32549
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
31893
32550
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
31894
32551
|
var AVAILABLE_SEGMENTS = [
|
|
@@ -31909,12 +32566,12 @@ var PRESETS = {
|
|
|
31909
32566
|
tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
|
|
31910
32567
|
};
|
|
31911
32568
|
function getConfigPath(installRoot) {
|
|
31912
|
-
return
|
|
32569
|
+
return resolve66(installRoot, "statusline.config.json");
|
|
31913
32570
|
}
|
|
31914
32571
|
async function readConfig2(path) {
|
|
31915
32572
|
if (!await fileExists(path)) return null;
|
|
31916
32573
|
try {
|
|
31917
|
-
const raw2 = await
|
|
32574
|
+
const raw2 = await readFile45(path, "utf-8");
|
|
31918
32575
|
const parsed = JSON.parse(raw2);
|
|
31919
32576
|
if (!parsed || typeof parsed !== "object") return null;
|
|
31920
32577
|
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
@@ -32067,7 +32724,7 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
32067
32724
|
console.log(` segments: ${config.segments.join(", ")}`);
|
|
32068
32725
|
console.log(` separator: ${JSON.stringify(config.separator)}`);
|
|
32069
32726
|
if (config.wrap) console.log(` wrap: ${config.wrap}`);
|
|
32070
|
-
const script = options.statuslineScript ??
|
|
32727
|
+
const script = options.statuslineScript ?? resolve66(installRoot, "statusline.sh");
|
|
32071
32728
|
if (await fileExists(script)) {
|
|
32072
32729
|
console.log("");
|
|
32073
32730
|
console.log("Live preview:");
|
|
@@ -32098,11 +32755,11 @@ async function writeDefaultConfigIfMissing(installRoot) {
|
|
|
32098
32755
|
// src/commands/install-statusline.ts
|
|
32099
32756
|
function getPackageStatuslineSource() {
|
|
32100
32757
|
const here = dirname19(fileURLToPath8(import.meta.url));
|
|
32101
|
-
return
|
|
32758
|
+
return resolve67(here, "..", "statusline", "statusline.sh");
|
|
32102
32759
|
}
|
|
32103
32760
|
async function readSettingsJson(settingsPath) {
|
|
32104
32761
|
if (!await fileExists(settingsPath)) return {};
|
|
32105
|
-
const raw2 = await
|
|
32762
|
+
const raw2 = await readFile46(settingsPath, "utf-8");
|
|
32106
32763
|
if (raw2.trim() === "") return {};
|
|
32107
32764
|
try {
|
|
32108
32765
|
const parsed = JSON.parse(raw2);
|
|
@@ -32182,12 +32839,12 @@ async function installScript(sourceScript, destScript, link) {
|
|
|
32182
32839
|
}
|
|
32183
32840
|
async function installStatuslineCommand(options = {}) {
|
|
32184
32841
|
const mode = options.mode ?? "ask";
|
|
32185
|
-
const settingsPath = options.settingsPath ??
|
|
32842
|
+
const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
|
|
32186
32843
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
32187
32844
|
const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
|
|
32188
|
-
const destScript =
|
|
32189
|
-
const confPath =
|
|
32190
|
-
const backupPath =
|
|
32845
|
+
const destScript = resolve67(installRoot, "statusline.sh");
|
|
32846
|
+
const confPath = resolve67(installRoot, "statusline.conf");
|
|
32847
|
+
const backupPath = resolve67(installRoot, "statusline.backup.json");
|
|
32191
32848
|
if (!await fileExists(sourceScript)) {
|
|
32192
32849
|
throw new Error(
|
|
32193
32850
|
`Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
|
|
@@ -32223,7 +32880,7 @@ async function installStatuslineCommand(options = {}) {
|
|
|
32223
32880
|
if (parsed) {
|
|
32224
32881
|
wrapTarget = parsed;
|
|
32225
32882
|
} else {
|
|
32226
|
-
const wrapperPath =
|
|
32883
|
+
const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
|
|
32227
32884
|
const wrapperBody = `#!/usr/bin/env bash
|
|
32228
32885
|
# Auto-generated by syntaur install-statusline.
|
|
32229
32886
|
# Executes the previously configured statusLine command.
|
|
@@ -32272,25 +32929,25 @@ function parseWrapCommand(command) {
|
|
|
32272
32929
|
async function chmodExec(path) {
|
|
32273
32930
|
const fs = await import("fs/promises");
|
|
32274
32931
|
try {
|
|
32275
|
-
const s = await
|
|
32932
|
+
const s = await stat8(path);
|
|
32276
32933
|
await fs.chmod(path, s.mode | 73);
|
|
32277
32934
|
} catch {
|
|
32278
32935
|
}
|
|
32279
32936
|
}
|
|
32280
32937
|
async function uninstallStatuslineCommand(options = {}) {
|
|
32281
|
-
const settingsPath = options.settingsPath ??
|
|
32938
|
+
const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
|
|
32282
32939
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
32283
|
-
const destScript =
|
|
32284
|
-
const confPath =
|
|
32285
|
-
const backupPath =
|
|
32286
|
-
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");
|
|
32287
32944
|
const settings = await readSettingsJson(settingsPath);
|
|
32288
32945
|
const existing = extractExistingCommand(settings);
|
|
32289
32946
|
const ourCommand = `bash ${destScript}`;
|
|
32290
32947
|
let restored = null;
|
|
32291
32948
|
if (await fileExists(backupPath)) {
|
|
32292
32949
|
try {
|
|
32293
|
-
const raw2 = await
|
|
32950
|
+
const raw2 = await readFile46(backupPath, "utf-8");
|
|
32294
32951
|
const parsed = JSON.parse(raw2);
|
|
32295
32952
|
const prev = parsed?.previousStatusLine;
|
|
32296
32953
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -32311,7 +32968,7 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
32311
32968
|
await writeSettingsJson(settingsPath, settings);
|
|
32312
32969
|
}
|
|
32313
32970
|
if (!options.keepScript) {
|
|
32314
|
-
const configPath2 =
|
|
32971
|
+
const configPath2 = resolve67(installRoot, "statusline.config.json");
|
|
32315
32972
|
for (const path of [destScript, confPath, backupPath, wrapperPath, configPath2]) {
|
|
32316
32973
|
try {
|
|
32317
32974
|
await rm8(path, { force: true });
|
|
@@ -32471,8 +33128,8 @@ init_config2();
|
|
|
32471
33128
|
// src/commands/cross-agent-install.ts
|
|
32472
33129
|
init_fs();
|
|
32473
33130
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
32474
|
-
import { dirname as dirname20, join as join15, resolve as
|
|
32475
|
-
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";
|
|
32476
33133
|
|
|
32477
33134
|
// src/commands/setup-adapter.ts
|
|
32478
33135
|
init_paths();
|
|
@@ -32481,7 +33138,7 @@ init_config2();
|
|
|
32481
33138
|
init_slug();
|
|
32482
33139
|
init_registry();
|
|
32483
33140
|
init_renderers();
|
|
32484
|
-
import { resolve as
|
|
33141
|
+
import { resolve as resolve68 } from "path";
|
|
32485
33142
|
async function setupAdapterCommand(framework, options) {
|
|
32486
33143
|
const { targets: known, warnings } = await resolveAgentTargets();
|
|
32487
33144
|
const target = known.find((t) => t.id === framework);
|
|
@@ -32510,13 +33167,13 @@ async function setupAdapterCommand(framework, options) {
|
|
|
32510
33167
|
}
|
|
32511
33168
|
const config = await readConfig();
|
|
32512
33169
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
32513
|
-
const projectDir =
|
|
32514
|
-
const assignmentDir =
|
|
32515
|
-
const projectMdPath =
|
|
33170
|
+
const projectDir = resolve68(baseDir, options.project);
|
|
33171
|
+
const assignmentDir = resolve68(projectDir, "assignments", options.assignment);
|
|
33172
|
+
const projectMdPath = resolve68(projectDir, "project.md");
|
|
32516
33173
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
32517
33174
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
32518
33175
|
}
|
|
32519
|
-
const assignmentMdPath2 =
|
|
33176
|
+
const assignmentMdPath2 = resolve68(assignmentDir, "assignment.md");
|
|
32520
33177
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
|
|
32521
33178
|
throw new Error(
|
|
32522
33179
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -32533,7 +33190,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
32533
33190
|
const upToDateFiles = [];
|
|
32534
33191
|
const skippedFiles = [];
|
|
32535
33192
|
for (const file of target.instructions.files) {
|
|
32536
|
-
const filePath =
|
|
33193
|
+
const filePath = resolve68(cwd, file.path);
|
|
32537
33194
|
const content = RENDERERS[file.renderer](rendererParams);
|
|
32538
33195
|
const status = await writeFileReport(filePath, content, {
|
|
32539
33196
|
force: options.force
|
|
@@ -32589,7 +33246,7 @@ async function installTier3Plugin(t, opts = {}) {
|
|
|
32589
33246
|
return "already-present";
|
|
32590
33247
|
}
|
|
32591
33248
|
try {
|
|
32592
|
-
const sourceDir =
|
|
33249
|
+
const sourceDir = resolve69(await findPackageRoot(plugin.source), plugin.source);
|
|
32593
33250
|
await mkdir9(dirname20(installDir), { recursive: true });
|
|
32594
33251
|
await cp4(sourceDir, installDir, { recursive: true, force: true });
|
|
32595
33252
|
console.log(`Tier 3 (${t.id}): installed ${plugin.kind} -> ${installDir}`);
|
|
@@ -32614,10 +33271,10 @@ function isNpxAvailable() {
|
|
|
32614
33271
|
}
|
|
32615
33272
|
}
|
|
32616
33273
|
async function readAssignmentContext() {
|
|
32617
|
-
const p =
|
|
33274
|
+
const p = resolve69(process.cwd(), ".syntaur", "context.json");
|
|
32618
33275
|
if (!await fileExists(p)) return null;
|
|
32619
33276
|
try {
|
|
32620
|
-
return JSON.parse(await
|
|
33277
|
+
return JSON.parse(await readFile47(p, "utf-8"));
|
|
32621
33278
|
} catch {
|
|
32622
33279
|
return null;
|
|
32623
33280
|
}
|
|
@@ -32697,7 +33354,7 @@ async function crossAgentInstallCommand(options) {
|
|
|
32697
33354
|
if (dryRun) {
|
|
32698
33355
|
for (const f of t.instructions.files) {
|
|
32699
33356
|
console.log(
|
|
32700
|
-
`${prefix}Tier 2 (${t.id}): ${
|
|
33357
|
+
`${prefix}Tier 2 (${t.id}): ${resolve69(process.cwd(), f.path)}`
|
|
32701
33358
|
);
|
|
32702
33359
|
}
|
|
32703
33360
|
continue;
|
|
@@ -32854,7 +33511,7 @@ async function setupCommand(options) {
|
|
|
32854
33511
|
}
|
|
32855
33512
|
|
|
32856
33513
|
// src/commands/uninstall.ts
|
|
32857
|
-
import { resolve as
|
|
33514
|
+
import { resolve as resolve70 } from "path";
|
|
32858
33515
|
init_paths();
|
|
32859
33516
|
function expandTargets(options) {
|
|
32860
33517
|
if (options.all) {
|
|
@@ -32934,7 +33591,7 @@ async function uninstallCommand(options) {
|
|
|
32934
33591
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
32935
33592
|
await removeSyntaurData();
|
|
32936
33593
|
console.log(`Removed ${syntaurRoot()}`);
|
|
32937
|
-
if (configuredProjectDir &&
|
|
33594
|
+
if (configuredProjectDir && resolve70(configuredProjectDir) !== resolve70(syntaurRoot(), "projects")) {
|
|
32938
33595
|
console.warn(
|
|
32939
33596
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
32940
33597
|
);
|
|
@@ -32956,8 +33613,8 @@ init_session_id();
|
|
|
32956
33613
|
init_cwd();
|
|
32957
33614
|
init_session_db();
|
|
32958
33615
|
init_agent_sessions();
|
|
32959
|
-
import { resolve as
|
|
32960
|
-
import { readFile as
|
|
33616
|
+
import { resolve as resolve71 } from "path";
|
|
33617
|
+
import { readFile as readFile48 } from "fs/promises";
|
|
32961
33618
|
async function trackSessionCommand(options, deps = {}) {
|
|
32962
33619
|
if (!options.agent) {
|
|
32963
33620
|
throw new Error("--agent <name> is required.");
|
|
@@ -32967,7 +33624,7 @@ async function trackSessionCommand(options, deps = {}) {
|
|
|
32967
33624
|
const cwd = process.cwd();
|
|
32968
33625
|
let legacyHint;
|
|
32969
33626
|
try {
|
|
32970
|
-
const raw2 = await
|
|
33627
|
+
const raw2 = await readFile48(resolve71(cwd, ".syntaur", "context.json"), "utf-8");
|
|
32971
33628
|
const parsed = JSON.parse(raw2);
|
|
32972
33629
|
if (typeof parsed.sessionId === "string") legacyHint = parsed.sessionId;
|
|
32973
33630
|
} catch {
|
|
@@ -32982,7 +33639,7 @@ async function trackSessionCommand(options, deps = {}) {
|
|
|
32982
33639
|
if (options.project) {
|
|
32983
33640
|
const config = await readConfig();
|
|
32984
33641
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
32985
|
-
const projectDir =
|
|
33642
|
+
const projectDir = resolve71(baseDir, options.project);
|
|
32986
33643
|
if (!await fileExists(projectDir)) {
|
|
32987
33644
|
throw new Error(
|
|
32988
33645
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -33115,8 +33772,8 @@ function formatInstallUrlHandlerError(err2) {
|
|
|
33115
33772
|
init_config2();
|
|
33116
33773
|
init_paths();
|
|
33117
33774
|
init_fs();
|
|
33118
|
-
import { resolve as
|
|
33119
|
-
import { readFile as
|
|
33775
|
+
import { resolve as resolve73, isAbsolute as isAbsolute8 } from "path";
|
|
33776
|
+
import { readFile as readFile49 } from "fs/promises";
|
|
33120
33777
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
33121
33778
|
async function browseCommand(options) {
|
|
33122
33779
|
const config = await readConfig();
|
|
@@ -33185,7 +33842,7 @@ async function pickAgent2(agents) {
|
|
|
33185
33842
|
return picked;
|
|
33186
33843
|
}
|
|
33187
33844
|
async function ensureWorktree(opts) {
|
|
33188
|
-
const assignmentPath =
|
|
33845
|
+
const assignmentPath = resolve73(
|
|
33189
33846
|
opts.projectsDir,
|
|
33190
33847
|
opts.projectSlug,
|
|
33191
33848
|
"assignments",
|
|
@@ -33195,7 +33852,7 @@ async function ensureWorktree(opts) {
|
|
|
33195
33852
|
if (!await fileExists(assignmentPath)) {
|
|
33196
33853
|
return void 0;
|
|
33197
33854
|
}
|
|
33198
|
-
const content = await
|
|
33855
|
+
const content = await readFile49(assignmentPath, "utf-8");
|
|
33199
33856
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
33200
33857
|
const fm = parseAssignmentFrontmatter2(content);
|
|
33201
33858
|
const { workspace } = fm;
|
|
@@ -33265,7 +33922,7 @@ async function ensureWorktree(opts) {
|
|
|
33265
33922
|
async function runCreate(opts) {
|
|
33266
33923
|
const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
33267
33924
|
const expandedWorktree = expandHome(opts.worktreePath);
|
|
33268
|
-
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree :
|
|
33925
|
+
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve73(expandedWorktree);
|
|
33269
33926
|
try {
|
|
33270
33927
|
await createWorktreeAndRecord2({
|
|
33271
33928
|
assignmentPath: opts.assignmentPath,
|
|
@@ -33294,7 +33951,7 @@ init_paths();
|
|
|
33294
33951
|
init_fs();
|
|
33295
33952
|
init_playbook();
|
|
33296
33953
|
init_playbooks();
|
|
33297
|
-
import { resolve as
|
|
33954
|
+
import { resolve as resolve74 } from "path";
|
|
33298
33955
|
async function createPlaybookCommand(name, options) {
|
|
33299
33956
|
if (!name.trim()) {
|
|
33300
33957
|
throw new Error("Playbook name cannot be empty.");
|
|
@@ -33307,7 +33964,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
33307
33964
|
}
|
|
33308
33965
|
const dir = playbooksDir();
|
|
33309
33966
|
await ensureDir(dir);
|
|
33310
|
-
const filePath =
|
|
33967
|
+
const filePath = resolve74(dir, `${slug}.md`);
|
|
33311
33968
|
if (await fileExists(filePath)) {
|
|
33312
33969
|
throw new Error(
|
|
33313
33970
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -33329,8 +33986,8 @@ init_paths();
|
|
|
33329
33986
|
init_fs();
|
|
33330
33987
|
init_parser();
|
|
33331
33988
|
init_config2();
|
|
33332
|
-
import { readdir as readdir26, readFile as
|
|
33333
|
-
import { resolve as
|
|
33989
|
+
import { readdir as readdir26, readFile as readFile50 } from "fs/promises";
|
|
33990
|
+
import { resolve as resolve75 } from "path";
|
|
33334
33991
|
async function listPlaybooksCommand(options = {}) {
|
|
33335
33992
|
const dir = playbooksDir();
|
|
33336
33993
|
if (!await fileExists(dir)) {
|
|
@@ -33345,8 +34002,8 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
33345
34002
|
);
|
|
33346
34003
|
const rows = [];
|
|
33347
34004
|
for (const entry of mdFiles) {
|
|
33348
|
-
const filePath =
|
|
33349
|
-
const raw2 = await
|
|
34005
|
+
const filePath = resolve75(dir, entry.name);
|
|
34006
|
+
const raw2 = await readFile50(filePath, "utf-8");
|
|
33350
34007
|
const parsed = parsePlaybook(raw2);
|
|
33351
34008
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
33352
34009
|
const disabled = disabledSet.has(slug);
|
|
@@ -33469,14 +34126,14 @@ init_fs();
|
|
|
33469
34126
|
init_config2();
|
|
33470
34127
|
init_slug();
|
|
33471
34128
|
import { Command as Command2 } from "commander";
|
|
33472
|
-
import { readFile as
|
|
33473
|
-
import { resolve as
|
|
34129
|
+
import { readFile as readFile52 } from "fs/promises";
|
|
34130
|
+
import { resolve as resolve77 } from "path";
|
|
33474
34131
|
|
|
33475
34132
|
// src/commands/bundle.ts
|
|
33476
34133
|
init_paths();
|
|
33477
34134
|
import { Command } from "commander";
|
|
33478
|
-
import { mkdir as mkdir10, readFile as
|
|
33479
|
-
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";
|
|
33480
34137
|
init_parser2();
|
|
33481
34138
|
init_fs();
|
|
33482
34139
|
init_config2();
|
|
@@ -33494,7 +34151,7 @@ async function resolveBundleScope(options) {
|
|
|
33494
34151
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
33495
34152
|
}
|
|
33496
34153
|
const config = await readConfig();
|
|
33497
|
-
const projectMd =
|
|
34154
|
+
const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
|
|
33498
34155
|
if (!await fileExists(projectMd)) {
|
|
33499
34156
|
throw new Error(`Project "${options.project}" not found.`);
|
|
33500
34157
|
}
|
|
@@ -33564,10 +34221,10 @@ function pickNextPlanFile(planDir, existingFiles) {
|
|
|
33564
34221
|
const m = f.match(/^plan-v(\d+)\.md$/);
|
|
33565
34222
|
if (m) versions.add(parseInt(m[1], 10));
|
|
33566
34223
|
}
|
|
33567
|
-
if (versions.size === 0) return { target:
|
|
34224
|
+
if (versions.size === 0) return { target: resolve76(planDir, "plan.md"), version: 1 };
|
|
33568
34225
|
let n = 2;
|
|
33569
34226
|
while (versions.has(n)) n++;
|
|
33570
|
-
return { target:
|
|
34227
|
+
return { target: resolve76(planDir, `plan-v${n}.md`), version: n };
|
|
33571
34228
|
}
|
|
33572
34229
|
function dedupePreserveOrder(ids) {
|
|
33573
34230
|
const out = [];
|
|
@@ -33651,7 +34308,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
|
|
|
33651
34308
|
if (options.plan) {
|
|
33652
34309
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
33653
34310
|
await ensureDir(planDir);
|
|
33654
|
-
const target =
|
|
34311
|
+
const target = resolve76(planDir, "plan.md");
|
|
33655
34312
|
const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
|
|
33656
34313
|
const stub = [
|
|
33657
34314
|
"---",
|
|
@@ -33827,7 +34484,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33827
34484
|
}
|
|
33828
34485
|
const repository = options.repository ?? process.cwd();
|
|
33829
34486
|
const parentBranch = options.parentBranch ?? "main";
|
|
33830
|
-
const worktreePath = options.worktreePath ??
|
|
34487
|
+
const worktreePath = options.worktreePath ?? resolve76(repository, ".worktrees", options.branch);
|
|
33831
34488
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
33832
34489
|
for (const memberId of bundle.todoIds) {
|
|
33833
34490
|
const item = checklist.items.find((i) => i.id === memberId);
|
|
@@ -33837,8 +34494,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33837
34494
|
}
|
|
33838
34495
|
const bundlesFilePath = bundlesPath(sc.todosPath);
|
|
33839
34496
|
const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
|
|
33840
|
-
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await
|
|
33841
|
-
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;
|
|
33842
34499
|
const record = async () => {
|
|
33843
34500
|
bundle.branch = options.branch;
|
|
33844
34501
|
bundle.worktreePath = worktreePath;
|
|
@@ -33854,7 +34511,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33854
34511
|
try {
|
|
33855
34512
|
await writeBundles(sc.todosPath, bundles);
|
|
33856
34513
|
await writeChecklist(sc.todosPath, checklist);
|
|
33857
|
-
const ctxDir =
|
|
34514
|
+
const ctxDir = resolve76(worktreePath, ".syntaur");
|
|
33858
34515
|
await mkdir10(ctxDir, { recursive: true });
|
|
33859
34516
|
const payload = {
|
|
33860
34517
|
bundleId: bundle.id,
|
|
@@ -33868,7 +34525,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33868
34525
|
repository,
|
|
33869
34526
|
boundAt: nowISO()
|
|
33870
34527
|
};
|
|
33871
|
-
await writeFile13(
|
|
34528
|
+
await writeFile13(resolve76(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
|
|
33872
34529
|
} catch (err2) {
|
|
33873
34530
|
try {
|
|
33874
34531
|
if (bundlesSnapshot === null) {
|
|
@@ -34058,7 +34715,7 @@ async function resolveScope(options) {
|
|
|
34058
34715
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
34059
34716
|
}
|
|
34060
34717
|
const config = await readConfig();
|
|
34061
|
-
const projectMd =
|
|
34718
|
+
const projectMd = resolve77(config.defaultProjectDir, options.project, "project.md");
|
|
34062
34719
|
if (!await fileExists(projectMd)) {
|
|
34063
34720
|
throw new Error(`Project "${options.project}" not found.`);
|
|
34064
34721
|
}
|
|
@@ -34391,10 +35048,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
34391
35048
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
34392
35049
|
);
|
|
34393
35050
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
34394
|
-
await ensureDir(
|
|
35051
|
+
await ensureDir(resolve77(todosPath, "archive"));
|
|
34395
35052
|
let archContent = "";
|
|
34396
35053
|
if (await fileExists(archFile)) {
|
|
34397
|
-
archContent = await
|
|
35054
|
+
archContent = await readFile52(archFile, "utf-8");
|
|
34398
35055
|
archContent = archContent.trimEnd() + "\n\n";
|
|
34399
35056
|
} else {
|
|
34400
35057
|
archContent = `---
|
|
@@ -34572,12 +35229,12 @@ function describeScope(scope) {
|
|
|
34572
35229
|
}
|
|
34573
35230
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
34574
35231
|
const { resolve: resolvePath2 } = await import("path");
|
|
34575
|
-
const { readFile:
|
|
35232
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
34576
35233
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
34577
35234
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
34578
35235
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
34579
35236
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
34580
|
-
let content = await
|
|
35237
|
+
let content = await readFile82(assignmentMdPath2, "utf-8");
|
|
34581
35238
|
content = appendTodosToAssignmentBody2(
|
|
34582
35239
|
content,
|
|
34583
35240
|
todos.map((t) => ({
|
|
@@ -34628,7 +35285,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
34628
35285
|
);
|
|
34629
35286
|
let target;
|
|
34630
35287
|
if (existingFiles.length === 0) {
|
|
34631
|
-
target =
|
|
35288
|
+
target = resolve77(planDir, "plan.md");
|
|
34632
35289
|
} else {
|
|
34633
35290
|
const versions = /* @__PURE__ */ new Set();
|
|
34634
35291
|
for (const f of existingFiles) {
|
|
@@ -34638,7 +35295,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
34638
35295
|
}
|
|
34639
35296
|
let n = 2;
|
|
34640
35297
|
while (versions.has(n)) n++;
|
|
34641
|
-
target =
|
|
35298
|
+
target = resolve77(planDir, `plan-v${n}.md`);
|
|
34642
35299
|
}
|
|
34643
35300
|
if (!await fileExists(target)) {
|
|
34644
35301
|
const stub = `---
|
|
@@ -34832,12 +35489,12 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
34832
35489
|
|
|
34833
35490
|
// src/commands/doctor.ts
|
|
34834
35491
|
import { Command as Command4 } from "commander";
|
|
34835
|
-
import { readFile as
|
|
34836
|
-
import { isAbsolute as isAbsolute11, resolve as
|
|
35492
|
+
import { readFile as readFile64 } from "fs/promises";
|
|
35493
|
+
import { isAbsolute as isAbsolute11, resolve as resolve91 } from "path";
|
|
34837
35494
|
|
|
34838
35495
|
// src/utils/doctor/index.ts
|
|
34839
35496
|
import { fileURLToPath as fileURLToPath12 } from "url";
|
|
34840
|
-
import { readFile as
|
|
35497
|
+
import { readFile as readFile63 } from "fs/promises";
|
|
34841
35498
|
import { dirname as dirname25, join as join21 } from "path";
|
|
34842
35499
|
|
|
34843
35500
|
// src/utils/doctor/context.ts
|
|
@@ -34845,11 +35502,11 @@ init_config2();
|
|
|
34845
35502
|
init_paths();
|
|
34846
35503
|
init_fs();
|
|
34847
35504
|
import Database5 from "better-sqlite3";
|
|
34848
|
-
import { resolve as
|
|
35505
|
+
import { resolve as resolve78 } from "path";
|
|
34849
35506
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
34850
35507
|
const config = await readConfig();
|
|
34851
35508
|
const root = syntaurRoot();
|
|
34852
|
-
const dbPath =
|
|
35509
|
+
const dbPath = resolve78(root, "syntaur.db");
|
|
34853
35510
|
let db6 = null;
|
|
34854
35511
|
let dbError = null;
|
|
34855
35512
|
if (await fileExists(dbPath)) {
|
|
@@ -34883,8 +35540,8 @@ function closeCheckContext(ctx) {
|
|
|
34883
35540
|
// src/utils/doctor/checks/env.ts
|
|
34884
35541
|
init_fs();
|
|
34885
35542
|
init_paths();
|
|
34886
|
-
import { resolve as
|
|
34887
|
-
import { readFile as
|
|
35543
|
+
import { resolve as resolve79, isAbsolute as isAbsolute9 } from "path";
|
|
35544
|
+
import { readFile as readFile53, stat as stat9 } from "fs/promises";
|
|
34888
35545
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
34889
35546
|
import { dirname as dirname22, join as join17 } from "path";
|
|
34890
35547
|
var CATEGORY = "env";
|
|
@@ -34894,7 +35551,7 @@ var syntaurRootExists = {
|
|
|
34894
35551
|
title: "~/.syntaur/ directory exists",
|
|
34895
35552
|
async run(ctx) {
|
|
34896
35553
|
try {
|
|
34897
|
-
const s = await
|
|
35554
|
+
const s = await stat9(ctx.syntaurRoot);
|
|
34898
35555
|
if (!s.isDirectory()) {
|
|
34899
35556
|
return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
|
|
34900
35557
|
ctx.syntaurRoot
|
|
@@ -34924,7 +35581,7 @@ var configValid = {
|
|
|
34924
35581
|
category: CATEGORY,
|
|
34925
35582
|
title: "~/.syntaur/config.md is valid",
|
|
34926
35583
|
async run(ctx) {
|
|
34927
|
-
const configPath2 =
|
|
35584
|
+
const configPath2 = resolve79(ctx.syntaurRoot, "config.md");
|
|
34928
35585
|
if (!await fileExists(configPath2)) {
|
|
34929
35586
|
return {
|
|
34930
35587
|
id: this.id,
|
|
@@ -34941,7 +35598,7 @@ var configValid = {
|
|
|
34941
35598
|
autoFixable: false
|
|
34942
35599
|
};
|
|
34943
35600
|
}
|
|
34944
|
-
const content = await
|
|
35601
|
+
const content = await readFile53(configPath2, "utf-8");
|
|
34945
35602
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
34946
35603
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
34947
35604
|
return {
|
|
@@ -35221,7 +35878,7 @@ async function readLocalPkg() {
|
|
|
35221
35878
|
for (let i = 0; i < 6; i++) {
|
|
35222
35879
|
const candidate = join17(dir, "package.json");
|
|
35223
35880
|
try {
|
|
35224
|
-
const text = await
|
|
35881
|
+
const text = await readFile53(candidate, "utf-8");
|
|
35225
35882
|
return JSON.parse(text);
|
|
35226
35883
|
} catch {
|
|
35227
35884
|
dir = dirname22(dir);
|
|
@@ -35273,8 +35930,8 @@ function versionGte(a, b) {
|
|
|
35273
35930
|
|
|
35274
35931
|
// src/utils/doctor/checks/structure.ts
|
|
35275
35932
|
init_fs();
|
|
35276
|
-
import { resolve as
|
|
35277
|
-
import { readdir as readdir28, stat as
|
|
35933
|
+
import { resolve as resolve80 } from "path";
|
|
35934
|
+
import { readdir as readdir28, stat as stat10 } from "fs/promises";
|
|
35278
35935
|
var CATEGORY2 = "structure";
|
|
35279
35936
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
35280
35937
|
"projects",
|
|
@@ -35293,7 +35950,7 @@ var projectsDir = {
|
|
|
35293
35950
|
category: CATEGORY2,
|
|
35294
35951
|
title: "projects/ directory exists",
|
|
35295
35952
|
async run(ctx) {
|
|
35296
|
-
const p =
|
|
35953
|
+
const p = resolve80(ctx.syntaurRoot, "projects");
|
|
35297
35954
|
if (!await fileExists(p)) {
|
|
35298
35955
|
return {
|
|
35299
35956
|
id: this.id,
|
|
@@ -35318,7 +35975,7 @@ var playbooksDir2 = {
|
|
|
35318
35975
|
category: CATEGORY2,
|
|
35319
35976
|
title: "playbooks/ directory exists",
|
|
35320
35977
|
async run(ctx) {
|
|
35321
|
-
const p =
|
|
35978
|
+
const p = resolve80(ctx.syntaurRoot, "playbooks");
|
|
35322
35979
|
if (!await fileExists(p)) {
|
|
35323
35980
|
return {
|
|
35324
35981
|
id: this.id,
|
|
@@ -35343,7 +36000,7 @@ var todosDirValid = {
|
|
|
35343
36000
|
category: CATEGORY2,
|
|
35344
36001
|
title: "todos/ directory is readable (if present)",
|
|
35345
36002
|
async run(ctx) {
|
|
35346
|
-
const p =
|
|
36003
|
+
const p = resolve80(ctx.syntaurRoot, "todos");
|
|
35347
36004
|
if (!await fileExists(p)) {
|
|
35348
36005
|
return {
|
|
35349
36006
|
id: this.id,
|
|
@@ -35354,7 +36011,7 @@ var todosDirValid = {
|
|
|
35354
36011
|
autoFixable: false
|
|
35355
36012
|
};
|
|
35356
36013
|
}
|
|
35357
|
-
const s = await
|
|
36014
|
+
const s = await stat10(p);
|
|
35358
36015
|
if (!s.isDirectory()) {
|
|
35359
36016
|
return {
|
|
35360
36017
|
id: this.id,
|
|
@@ -35374,7 +36031,7 @@ var serversDirValid = {
|
|
|
35374
36031
|
category: CATEGORY2,
|
|
35375
36032
|
title: "servers/ directory is readable (if present)",
|
|
35376
36033
|
async run(ctx) {
|
|
35377
|
-
const p =
|
|
36034
|
+
const p = resolve80(ctx.syntaurRoot, "servers");
|
|
35378
36035
|
if (!await fileExists(p)) {
|
|
35379
36036
|
return {
|
|
35380
36037
|
id: this.id,
|
|
@@ -35385,7 +36042,7 @@ var serversDirValid = {
|
|
|
35385
36042
|
autoFixable: false
|
|
35386
36043
|
};
|
|
35387
36044
|
}
|
|
35388
|
-
const s = await
|
|
36045
|
+
const s = await stat10(p);
|
|
35389
36046
|
if (!s.isDirectory()) {
|
|
35390
36047
|
return {
|
|
35391
36048
|
id: this.id,
|
|
@@ -35419,7 +36076,7 @@ var knownFilesRecognized = {
|
|
|
35419
36076
|
title: this.title,
|
|
35420
36077
|
status: "warn",
|
|
35421
36078
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
35422
|
-
affected: unexpected.map((n) =>
|
|
36079
|
+
affected: unexpected.map((n) => resolve80(ctx.syntaurRoot, n)),
|
|
35423
36080
|
remediation: {
|
|
35424
36081
|
kind: "manual",
|
|
35425
36082
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -35448,8 +36105,8 @@ function pass2(check) {
|
|
|
35448
36105
|
|
|
35449
36106
|
// src/utils/doctor/checks/project.ts
|
|
35450
36107
|
init_fs();
|
|
35451
|
-
import { resolve as
|
|
35452
|
-
import { readdir as readdir29, stat as
|
|
36108
|
+
import { resolve as resolve81 } from "path";
|
|
36109
|
+
import { readdir as readdir29, stat as stat11 } from "fs/promises";
|
|
35453
36110
|
var CATEGORY3 = "project";
|
|
35454
36111
|
var REQUIRED_PROJECT_FILES = [
|
|
35455
36112
|
"project.md",
|
|
@@ -35478,10 +36135,10 @@ async function listProjects2(ctx) {
|
|
|
35478
36135
|
for (const e of entries) {
|
|
35479
36136
|
if (!e.isDirectory()) continue;
|
|
35480
36137
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
35481
|
-
const projectDir =
|
|
36138
|
+
const projectDir = resolve81(dir, e.name);
|
|
35482
36139
|
let looksLikeProject = false;
|
|
35483
36140
|
for (const marker of PROJECT_MARKERS) {
|
|
35484
|
-
if (await fileExists(
|
|
36141
|
+
if (await fileExists(resolve81(projectDir, marker))) {
|
|
35485
36142
|
looksLikeProject = true;
|
|
35486
36143
|
break;
|
|
35487
36144
|
}
|
|
@@ -35500,7 +36157,7 @@ var requiredFiles = {
|
|
|
35500
36157
|
for (const projectDir of projects) {
|
|
35501
36158
|
const missing = [];
|
|
35502
36159
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
35503
|
-
const p =
|
|
36160
|
+
const p = resolve81(projectDir, rel);
|
|
35504
36161
|
if (!await fileExists(p)) missing.push(rel);
|
|
35505
36162
|
}
|
|
35506
36163
|
if (missing.length === 0) continue;
|
|
@@ -35510,7 +36167,7 @@ var requiredFiles = {
|
|
|
35510
36167
|
title: this.title,
|
|
35511
36168
|
status: "error",
|
|
35512
36169
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
35513
|
-
affected: missing.map((m) =>
|
|
36170
|
+
affected: missing.map((m) => resolve81(projectDir, m)),
|
|
35514
36171
|
remediation: {
|
|
35515
36172
|
kind: "manual",
|
|
35516
36173
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -35533,9 +36190,9 @@ var manifestStale = {
|
|
|
35533
36190
|
const projects = await listProjects2(ctx);
|
|
35534
36191
|
const results = [];
|
|
35535
36192
|
for (const projectDir of projects) {
|
|
35536
|
-
const manifestPath =
|
|
36193
|
+
const manifestPath = resolve81(projectDir, "manifest.md");
|
|
35537
36194
|
if (!await fileExists(manifestPath)) continue;
|
|
35538
|
-
const manifestMtime = (await
|
|
36195
|
+
const manifestMtime = (await stat11(manifestPath)).mtimeMs;
|
|
35539
36196
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
35540
36197
|
if (newestAssignment === 0) continue;
|
|
35541
36198
|
if (newestAssignment > manifestMtime) {
|
|
@@ -35582,7 +36239,7 @@ var orphanFiles = {
|
|
|
35582
36239
|
title: this.title,
|
|
35583
36240
|
status: "warn",
|
|
35584
36241
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
35585
|
-
affected: orphans.map((o) =>
|
|
36242
|
+
affected: orphans.map((o) => resolve81(projectDir, o)),
|
|
35586
36243
|
autoFixable: false
|
|
35587
36244
|
});
|
|
35588
36245
|
}
|
|
@@ -35592,7 +36249,7 @@ var orphanFiles = {
|
|
|
35592
36249
|
};
|
|
35593
36250
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
35594
36251
|
async function newestAssignmentMtime(projectDir) {
|
|
35595
|
-
const assignmentsRoot =
|
|
36252
|
+
const assignmentsRoot = resolve81(projectDir, "assignments");
|
|
35596
36253
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
35597
36254
|
let newest = 0;
|
|
35598
36255
|
let entries;
|
|
@@ -35603,9 +36260,9 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
35603
36260
|
}
|
|
35604
36261
|
for (const e of entries) {
|
|
35605
36262
|
if (!e.isDirectory()) continue;
|
|
35606
|
-
const assignmentMd =
|
|
36263
|
+
const assignmentMd = resolve81(assignmentsRoot, e.name, "assignment.md");
|
|
35607
36264
|
try {
|
|
35608
|
-
const s = await
|
|
36265
|
+
const s = await stat11(assignmentMd);
|
|
35609
36266
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
35610
36267
|
} catch {
|
|
35611
36268
|
}
|
|
@@ -35628,8 +36285,8 @@ init_parser();
|
|
|
35628
36285
|
init_types();
|
|
35629
36286
|
init_paths();
|
|
35630
36287
|
init_assignment_walk();
|
|
35631
|
-
import { resolve as
|
|
35632
|
-
import { readFile as
|
|
36288
|
+
import { resolve as resolve82 } from "path";
|
|
36289
|
+
import { readFile as readFile54, readdir as readdir30 } from "fs/promises";
|
|
35633
36290
|
var CATEGORY4 = "assignment";
|
|
35634
36291
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
35635
36292
|
var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -35723,7 +36380,7 @@ var invalidStatus = {
|
|
|
35723
36380
|
const allowed = configuredStatuses(ctx);
|
|
35724
36381
|
const results = [];
|
|
35725
36382
|
for (const a of withAssignmentMd) {
|
|
35726
|
-
const path =
|
|
36383
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35727
36384
|
const parsed = await parseSafe4(path);
|
|
35728
36385
|
if (!parsed) continue;
|
|
35729
36386
|
if (!allowed.has(parsed.status)) {
|
|
@@ -35756,7 +36413,7 @@ var workspaceMissing = {
|
|
|
35756
36413
|
const terminal = terminalStatuses(ctx);
|
|
35757
36414
|
const results = [];
|
|
35758
36415
|
for (const a of withAssignmentMd) {
|
|
35759
|
-
const path =
|
|
36416
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35760
36417
|
const parsed = await parseSafe4(path);
|
|
35761
36418
|
if (!parsed) continue;
|
|
35762
36419
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -35803,12 +36460,12 @@ var requiredFilesByStatus = {
|
|
|
35803
36460
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
35804
36461
|
const results = [];
|
|
35805
36462
|
for (const a of withAssignmentMd) {
|
|
35806
|
-
const assignmentPath =
|
|
36463
|
+
const assignmentPath = resolve82(a.assignmentDir, "assignment.md");
|
|
35807
36464
|
const parsed = await parseSafe4(assignmentPath);
|
|
35808
36465
|
if (!parsed) continue;
|
|
35809
36466
|
const missing = [];
|
|
35810
36467
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
35811
|
-
const handoffPath =
|
|
36468
|
+
const handoffPath = resolve82(a.assignmentDir, "handoff.md");
|
|
35812
36469
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
35813
36470
|
}
|
|
35814
36471
|
if (missing.length === 0) continue;
|
|
@@ -35818,7 +36475,7 @@ var requiredFilesByStatus = {
|
|
|
35818
36475
|
title: this.title,
|
|
35819
36476
|
status: "warn",
|
|
35820
36477
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
35821
|
-
affected: missing.map((m) =>
|
|
36478
|
+
affected: missing.map((m) => resolve82(a.assignmentDir, m)),
|
|
35822
36479
|
remediation: {
|
|
35823
36480
|
kind: "manual",
|
|
35824
36481
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -35841,7 +36498,7 @@ var companionFilesScaffolded = {
|
|
|
35841
36498
|
for (const a of withAssignmentMd) {
|
|
35842
36499
|
const missing = [];
|
|
35843
36500
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
35844
|
-
if (!await fileExists(
|
|
36501
|
+
if (!await fileExists(resolve82(a.assignmentDir, filename))) {
|
|
35845
36502
|
missing.push(filename);
|
|
35846
36503
|
}
|
|
35847
36504
|
}
|
|
@@ -35853,7 +36510,7 @@ var companionFilesScaffolded = {
|
|
|
35853
36510
|
title: this.title,
|
|
35854
36511
|
status: "warn",
|
|
35855
36512
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
35856
|
-
affected: missing.map((m) =>
|
|
36513
|
+
affected: missing.map((m) => resolve82(a.assignmentDir, m)),
|
|
35857
36514
|
remediation: {
|
|
35858
36515
|
kind: "manual",
|
|
35859
36516
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -35886,7 +36543,7 @@ var typeDefinition = {
|
|
|
35886
36543
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
35887
36544
|
const results = [];
|
|
35888
36545
|
for (const a of withAssignmentMd) {
|
|
35889
|
-
const path =
|
|
36546
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35890
36547
|
const parsed = await parseSafe4(path);
|
|
35891
36548
|
if (!parsed) continue;
|
|
35892
36549
|
if (!parsed.type) continue;
|
|
@@ -35920,7 +36577,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
35920
36577
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
35921
36578
|
const results = [];
|
|
35922
36579
|
for (const a of withAssignmentMd) {
|
|
35923
|
-
const path =
|
|
36580
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35924
36581
|
const parsed = await parseSafe4(path);
|
|
35925
36582
|
if (!parsed) continue;
|
|
35926
36583
|
if (a.standalone) {
|
|
@@ -35971,13 +36628,13 @@ var draftMissingObjective = {
|
|
|
35971
36628
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
35972
36629
|
const results = [];
|
|
35973
36630
|
for (const a of withAssignmentMd) {
|
|
35974
|
-
const path =
|
|
36631
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35975
36632
|
const parsed = await parseSafe4(path);
|
|
35976
36633
|
if (!parsed) continue;
|
|
35977
36634
|
if (parsed.status !== "draft") continue;
|
|
35978
36635
|
let raw2;
|
|
35979
36636
|
try {
|
|
35980
|
-
raw2 = await
|
|
36637
|
+
raw2 = await readFile54(path, "utf-8");
|
|
35981
36638
|
} catch {
|
|
35982
36639
|
continue;
|
|
35983
36640
|
}
|
|
@@ -36010,7 +36667,7 @@ var readyToImplementMissingPlan = {
|
|
|
36010
36667
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36011
36668
|
const results = [];
|
|
36012
36669
|
for (const a of withAssignmentMd) {
|
|
36013
|
-
const path =
|
|
36670
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36014
36671
|
const parsed = await parseSafe4(path);
|
|
36015
36672
|
if (!parsed) continue;
|
|
36016
36673
|
if (parsed.status !== "ready_to_implement") continue;
|
|
@@ -36019,7 +36676,7 @@ var readyToImplementMissingPlan = {
|
|
|
36019
36676
|
let hasPlanContent = false;
|
|
36020
36677
|
for (const f of planFiles) {
|
|
36021
36678
|
try {
|
|
36022
|
-
const c2 = await
|
|
36679
|
+
const c2 = await readFile54(resolve82(a.assignmentDir, f), "utf-8");
|
|
36023
36680
|
if (c2.trim().length > 0) {
|
|
36024
36681
|
hasPlanContent = true;
|
|
36025
36682
|
break;
|
|
@@ -36035,7 +36692,7 @@ var readyToImplementMissingPlan = {
|
|
|
36035
36692
|
title: this.title,
|
|
36036
36693
|
status: "warn",
|
|
36037
36694
|
detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
|
|
36038
|
-
affected: [
|
|
36695
|
+
affected: [resolve82(a.assignmentDir, "plan.md")],
|
|
36039
36696
|
remediation: {
|
|
36040
36697
|
kind: "manual",
|
|
36041
36698
|
suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
|
|
@@ -36062,7 +36719,7 @@ var assignmentChecks = [
|
|
|
36062
36719
|
];
|
|
36063
36720
|
async function parseSafe4(path) {
|
|
36064
36721
|
try {
|
|
36065
|
-
const content = await
|
|
36722
|
+
const content = await readFile54(path, "utf-8");
|
|
36066
36723
|
return parseAssignmentFull(content);
|
|
36067
36724
|
} catch {
|
|
36068
36725
|
return null;
|
|
@@ -36081,7 +36738,7 @@ function pass4(check, detail) {
|
|
|
36081
36738
|
|
|
36082
36739
|
// src/utils/doctor/checks/dashboard.ts
|
|
36083
36740
|
init_fs();
|
|
36084
|
-
import { resolve as
|
|
36741
|
+
import { resolve as resolve83 } from "path";
|
|
36085
36742
|
var CATEGORY5 = "dashboard";
|
|
36086
36743
|
var dbReachable = {
|
|
36087
36744
|
id: "dashboard.db-reachable",
|
|
@@ -36095,7 +36752,7 @@ var dbReachable = {
|
|
|
36095
36752
|
title: this.title,
|
|
36096
36753
|
status: "error",
|
|
36097
36754
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
36098
|
-
affected: [
|
|
36755
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36099
36756
|
remediation: {
|
|
36100
36757
|
kind: "manual",
|
|
36101
36758
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -36113,7 +36770,7 @@ var dbReachable = {
|
|
|
36113
36770
|
title: this.title,
|
|
36114
36771
|
status: "error",
|
|
36115
36772
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
36116
|
-
affected: [
|
|
36773
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36117
36774
|
autoFixable: false
|
|
36118
36775
|
};
|
|
36119
36776
|
}
|
|
@@ -36125,7 +36782,7 @@ var dbReachable = {
|
|
|
36125
36782
|
title: this.title,
|
|
36126
36783
|
status: "error",
|
|
36127
36784
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
36128
|
-
affected: [
|
|
36785
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36129
36786
|
autoFixable: false
|
|
36130
36787
|
};
|
|
36131
36788
|
}
|
|
@@ -36151,7 +36808,7 @@ var ghostSessions = {
|
|
|
36151
36808
|
const results = [];
|
|
36152
36809
|
for (const row of rows) {
|
|
36153
36810
|
if (!row.project_slug) continue;
|
|
36154
|
-
const projectPath =
|
|
36811
|
+
const projectPath = resolve83(projectsDir2, row.project_slug, "project.md");
|
|
36155
36812
|
if (!await fileExists(projectPath)) {
|
|
36156
36813
|
results.push({
|
|
36157
36814
|
id: this.id,
|
|
@@ -36170,7 +36827,7 @@ var ghostSessions = {
|
|
|
36170
36827
|
continue;
|
|
36171
36828
|
}
|
|
36172
36829
|
if (row.assignment_slug) {
|
|
36173
|
-
const assignmentPath =
|
|
36830
|
+
const assignmentPath = resolve83(
|
|
36174
36831
|
projectsDir2,
|
|
36175
36832
|
row.project_slug,
|
|
36176
36833
|
"assignments",
|
|
@@ -36222,8 +36879,8 @@ function skipped(check, reason) {
|
|
|
36222
36879
|
|
|
36223
36880
|
// src/utils/doctor/checks/integrations.ts
|
|
36224
36881
|
init_fs();
|
|
36225
|
-
import { resolve as
|
|
36226
|
-
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";
|
|
36227
36884
|
import { homedir as homedir14 } from "os";
|
|
36228
36885
|
var CATEGORY6 = "integrations";
|
|
36229
36886
|
var claudePluginLinked = {
|
|
@@ -36305,10 +36962,10 @@ var backupConfigured = {
|
|
|
36305
36962
|
}
|
|
36306
36963
|
};
|
|
36307
36964
|
async function readKnownMarketplaces() {
|
|
36308
|
-
const path =
|
|
36965
|
+
const path = resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json");
|
|
36309
36966
|
if (!await fileExists(path)) return {};
|
|
36310
36967
|
try {
|
|
36311
|
-
const raw2 = await
|
|
36968
|
+
const raw2 = await readFile55(path, "utf-8");
|
|
36312
36969
|
return JSON.parse(raw2);
|
|
36313
36970
|
} catch {
|
|
36314
36971
|
return {};
|
|
@@ -36325,7 +36982,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36325
36982
|
return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
|
|
36326
36983
|
}
|
|
36327
36984
|
const pluginsParent = dirname23(dir);
|
|
36328
|
-
if (
|
|
36985
|
+
if (basename9(pluginsParent) !== "plugins") {
|
|
36329
36986
|
return {
|
|
36330
36987
|
id: this.id,
|
|
36331
36988
|
category: this.category,
|
|
@@ -36342,7 +36999,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36342
36999
|
};
|
|
36343
37000
|
}
|
|
36344
37001
|
const marketplaceRoot = dirname23(pluginsParent);
|
|
36345
|
-
const marketplaceManifest =
|
|
37002
|
+
const marketplaceManifest = resolve84(marketplaceRoot, ".claude-plugin", "marketplace.json");
|
|
36346
37003
|
if (!await fileExists(marketplaceManifest)) {
|
|
36347
37004
|
return {
|
|
36348
37005
|
id: this.id,
|
|
@@ -36361,7 +37018,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36361
37018
|
}
|
|
36362
37019
|
let parsed = {};
|
|
36363
37020
|
try {
|
|
36364
|
-
parsed = JSON.parse(await
|
|
37021
|
+
parsed = JSON.parse(await readFile55(marketplaceManifest, "utf-8"));
|
|
36365
37022
|
} catch {
|
|
36366
37023
|
return {
|
|
36367
37024
|
id: this.id,
|
|
@@ -36373,7 +37030,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36373
37030
|
autoFixable: false
|
|
36374
37031
|
};
|
|
36375
37032
|
}
|
|
36376
|
-
const marketplaceName = parsed.name ??
|
|
37033
|
+
const marketplaceName = parsed.name ?? basename9(marketplaceRoot);
|
|
36377
37034
|
const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
|
|
36378
37035
|
const known = await readKnownMarketplaces();
|
|
36379
37036
|
const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
|
|
@@ -36393,7 +37050,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36393
37050
|
title: this.title,
|
|
36394
37051
|
status: "error",
|
|
36395
37052
|
detail: issues.join("; "),
|
|
36396
|
-
affected: [marketplaceManifest,
|
|
37053
|
+
affected: [marketplaceManifest, resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
|
|
36397
37054
|
remediation: {
|
|
36398
37055
|
kind: "manual",
|
|
36399
37056
|
suggestion: "Re-run install-plugin to ensure both files are in sync.",
|
|
@@ -36433,8 +37090,8 @@ function skipped2(check, reason) {
|
|
|
36433
37090
|
init_fs();
|
|
36434
37091
|
init_parser();
|
|
36435
37092
|
init_types();
|
|
36436
|
-
import { resolve as
|
|
36437
|
-
import { readFile as
|
|
37093
|
+
import { resolve as resolve85 } from "path";
|
|
37094
|
+
import { readFile as readFile56 } from "fs/promises";
|
|
36438
37095
|
var CATEGORY7 = "workspace";
|
|
36439
37096
|
var ASSIGNMENT_FIELDS2 = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
36440
37097
|
var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
|
|
@@ -36456,12 +37113,12 @@ function isStandaloneSession(ctx) {
|
|
|
36456
37113
|
return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && hasSessionMeta;
|
|
36457
37114
|
}
|
|
36458
37115
|
async function loadContext(ctx) {
|
|
36459
|
-
const path =
|
|
37116
|
+
const path = resolve85(ctx.cwd, ".syntaur", "context.json");
|
|
36460
37117
|
if (!await fileExists(path)) {
|
|
36461
37118
|
return { data: null, path, exists: false, parseError: null };
|
|
36462
37119
|
}
|
|
36463
37120
|
try {
|
|
36464
|
-
const raw2 = await
|
|
37121
|
+
const raw2 = await readFile56(path, "utf-8");
|
|
36465
37122
|
return { data: JSON.parse(raw2), path, exists: true, parseError: null };
|
|
36466
37123
|
} catch (err2) {
|
|
36467
37124
|
return {
|
|
@@ -36555,7 +37212,7 @@ var contextAssignmentResolves = {
|
|
|
36555
37212
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
36556
37213
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
|
|
36557
37214
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
36558
|
-
const assignmentMd =
|
|
37215
|
+
const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
|
|
36559
37216
|
if (!await fileExists(assignmentMd)) {
|
|
36560
37217
|
return {
|
|
36561
37218
|
id: this.id,
|
|
@@ -36585,10 +37242,10 @@ var contextTerminal = {
|
|
|
36585
37242
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
36586
37243
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
|
|
36587
37244
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
36588
|
-
const assignmentMd =
|
|
37245
|
+
const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
|
|
36589
37246
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
36590
37247
|
try {
|
|
36591
|
-
const content = await
|
|
37248
|
+
const content = await readFile56(assignmentMd, "utf-8");
|
|
36592
37249
|
const parsed = parseAssignmentFull(content);
|
|
36593
37250
|
const terminal = terminalStatuses2(ctx);
|
|
36594
37251
|
if (terminal.has(parsed.status)) {
|
|
@@ -36734,15 +37391,15 @@ var agentChecks = [agentsResolvable];
|
|
|
36734
37391
|
// src/utils/doctor/checks/terminal.ts
|
|
36735
37392
|
init_config2();
|
|
36736
37393
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
36737
|
-
import { readFile as
|
|
36738
|
-
import { resolve as
|
|
37394
|
+
import { readFile as readFile57 } from "fs/promises";
|
|
37395
|
+
import { resolve as resolve86 } from "path";
|
|
36739
37396
|
init_paths();
|
|
36740
37397
|
init_fs();
|
|
36741
37398
|
var CATEGORY9 = "terminal";
|
|
36742
37399
|
async function readRawTerminalKey() {
|
|
36743
|
-
const configPath2 =
|
|
37400
|
+
const configPath2 = resolve86(syntaurRoot(), "config.md");
|
|
36744
37401
|
if (!await fileExists(configPath2)) return null;
|
|
36745
|
-
const content = await
|
|
37402
|
+
const content = await readFile57(configPath2, "utf-8");
|
|
36746
37403
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
36747
37404
|
if (!fmMatch) return null;
|
|
36748
37405
|
const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
|
|
@@ -36892,13 +37549,13 @@ var terminalChecks = [
|
|
|
36892
37549
|
|
|
36893
37550
|
// src/utils/doctor/checks/skills.ts
|
|
36894
37551
|
init_fs();
|
|
36895
|
-
import { resolve as
|
|
36896
|
-
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";
|
|
36897
37554
|
import { homedir as homedir15 } from "os";
|
|
36898
37555
|
var CATEGORY10 = "skills";
|
|
36899
37556
|
var skillTargets = [
|
|
36900
|
-
{ agent: "claude", dir:
|
|
36901
|
-
{ 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" }
|
|
36902
37559
|
];
|
|
36903
37560
|
var skillsDedupCheck = {
|
|
36904
37561
|
id: "skills.dedup",
|
|
@@ -36922,7 +37579,7 @@ var skillsDedupCheck = {
|
|
|
36922
37579
|
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
36923
37580
|
const skillMd = join18(dir, entry.name, "SKILL.md");
|
|
36924
37581
|
if (!await fileExists(skillMd)) continue;
|
|
36925
|
-
const content = await
|
|
37582
|
+
const content = await readFile58(skillMd, "utf-8").catch(() => "");
|
|
36926
37583
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
36927
37584
|
if (!match || match[1] !== entry.name) continue;
|
|
36928
37585
|
let isSymlink2 = false;
|
|
@@ -36972,12 +37629,12 @@ var skillsChecks = [skillsDedupCheck];
|
|
|
36972
37629
|
|
|
36973
37630
|
// src/utils/doctor/checks/cross-agent.ts
|
|
36974
37631
|
init_fs();
|
|
36975
|
-
import { join as join19, resolve as
|
|
36976
|
-
import { readFile as
|
|
37632
|
+
import { join as join19, resolve as resolve88 } from "path";
|
|
37633
|
+
import { readFile as readFile60 } from "fs/promises";
|
|
36977
37634
|
|
|
36978
37635
|
// src/utils/skill-frontmatter.ts
|
|
36979
37636
|
import { createHash as createHash4 } from "crypto";
|
|
36980
|
-
import { readFile as
|
|
37637
|
+
import { readFile as readFile59 } from "fs/promises";
|
|
36981
37638
|
function stripQuotes(raw2) {
|
|
36982
37639
|
const t = raw2.trim();
|
|
36983
37640
|
if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
|
|
@@ -37021,7 +37678,7 @@ function readSkillIdentity(skillMdText) {
|
|
|
37021
37678
|
return { name, hasDescription };
|
|
37022
37679
|
}
|
|
37023
37680
|
async function sha256File(path) {
|
|
37024
|
-
return createHash4("sha256").update(await
|
|
37681
|
+
return createHash4("sha256").update(await readFile59(path)).digest("hex");
|
|
37025
37682
|
}
|
|
37026
37683
|
|
|
37027
37684
|
// src/utils/doctor/checks/cross-agent.ts
|
|
@@ -37038,7 +37695,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
|
|
|
37038
37695
|
}
|
|
37039
37696
|
let text;
|
|
37040
37697
|
try {
|
|
37041
|
-
text = await
|
|
37698
|
+
text = await readFile60(installedPath, "utf-8");
|
|
37042
37699
|
} catch {
|
|
37043
37700
|
problems.push({ skill, kind: "invalid-frontmatter" });
|
|
37044
37701
|
continue;
|
|
@@ -37115,7 +37772,7 @@ var crossAgentSkillsCheck = {
|
|
|
37115
37772
|
}
|
|
37116
37773
|
if (recorded && t.instructions) {
|
|
37117
37774
|
for (const f of t.instructions.files) {
|
|
37118
|
-
const p =
|
|
37775
|
+
const p = resolve88(ctx.cwd, f.path);
|
|
37119
37776
|
if (!await fileExists(p)) {
|
|
37120
37777
|
problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
|
|
37121
37778
|
affected.push(p);
|
|
@@ -37191,7 +37848,7 @@ var crossAgentChecks = [crossAgentSkillsCheck];
|
|
|
37191
37848
|
// src/utils/doctor/checks/bundles.ts
|
|
37192
37849
|
init_fs();
|
|
37193
37850
|
init_paths();
|
|
37194
|
-
import { resolve as
|
|
37851
|
+
import { resolve as resolve89 } from "path";
|
|
37195
37852
|
import { readdir as readdir33 } from "fs/promises";
|
|
37196
37853
|
import { spawnSync as spawnSync10 } from "child_process";
|
|
37197
37854
|
init_parser2();
|
|
@@ -37221,7 +37878,7 @@ async function listScopes(ctx) {
|
|
|
37221
37878
|
if (!e.isDirectory()) continue;
|
|
37222
37879
|
const slug = e.name;
|
|
37223
37880
|
if (typeof slug !== "string" || slug.startsWith(".")) continue;
|
|
37224
|
-
const projectMd =
|
|
37881
|
+
const projectMd = resolve89(ctx.config.defaultProjectDir, slug, "project.md");
|
|
37225
37882
|
if (!await fileExists(projectMd)) continue;
|
|
37226
37883
|
out.push({
|
|
37227
37884
|
scopeLabel: `project:${slug}`,
|
|
@@ -37414,7 +38071,7 @@ var bundleChecks = [
|
|
|
37414
38071
|
];
|
|
37415
38072
|
|
|
37416
38073
|
// src/utils/doctor/checks/plugin.ts
|
|
37417
|
-
import { readFile as
|
|
38074
|
+
import { readFile as readFile61 } from "fs/promises";
|
|
37418
38075
|
import { dirname as dirname24, join as join20 } from "path";
|
|
37419
38076
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
37420
38077
|
var CATEGORY13 = "plugin";
|
|
@@ -37424,7 +38081,7 @@ async function readCliVersion() {
|
|
|
37424
38081
|
let dir = dirname24(fileURLToPath11(import.meta.url));
|
|
37425
38082
|
for (let i = 0; i < 8; i += 1) {
|
|
37426
38083
|
try {
|
|
37427
|
-
const parsed = JSON.parse(await
|
|
38084
|
+
const parsed = JSON.parse(await readFile61(join20(dir, "package.json"), "utf-8"));
|
|
37428
38085
|
if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
|
|
37429
38086
|
} catch {
|
|
37430
38087
|
}
|
|
@@ -37532,6 +38189,68 @@ var deriveConfigValid = {
|
|
|
37532
38189
|
};
|
|
37533
38190
|
var deriveConfigChecks = [deriveConfigValid];
|
|
37534
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
|
+
|
|
37535
38254
|
// src/utils/doctor/registry.ts
|
|
37536
38255
|
function allChecks() {
|
|
37537
38256
|
return [
|
|
@@ -37548,7 +38267,8 @@ function allChecks() {
|
|
|
37548
38267
|
...crossAgentChecks,
|
|
37549
38268
|
...bundleChecks,
|
|
37550
38269
|
...pluginChecks,
|
|
37551
|
-
...deriveConfigChecks
|
|
38270
|
+
...deriveConfigChecks,
|
|
38271
|
+
...stalenessChecks
|
|
37552
38272
|
];
|
|
37553
38273
|
}
|
|
37554
38274
|
|
|
@@ -37634,7 +38354,7 @@ async function readVersion() {
|
|
|
37634
38354
|
let dir = dirname25(here);
|
|
37635
38355
|
for (let i = 0; i < 6; i++) {
|
|
37636
38356
|
try {
|
|
37637
|
-
const raw2 = await
|
|
38357
|
+
const raw2 = await readFile63(join21(dir, "package.json"), "utf-8");
|
|
37638
38358
|
const parsed = JSON.parse(raw2);
|
|
37639
38359
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
37640
38360
|
} catch {
|
|
@@ -37731,7 +38451,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
|
|
|
37731
38451
|
];
|
|
37732
38452
|
var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
37733
38453
|
async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
37734
|
-
const absolute = isAbsolute11(inputPath) ? inputPath :
|
|
38454
|
+
const absolute = isAbsolute11(inputPath) ? inputPath : resolve91(cwd, inputPath);
|
|
37735
38455
|
const errors = [];
|
|
37736
38456
|
const warnings = [];
|
|
37737
38457
|
if (!await fileExists(absolute)) {
|
|
@@ -37744,7 +38464,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
|
37744
38464
|
}
|
|
37745
38465
|
let content;
|
|
37746
38466
|
try {
|
|
37747
|
-
content = await
|
|
38467
|
+
content = await readFile64(absolute, "utf-8");
|
|
37748
38468
|
} catch (err2) {
|
|
37749
38469
|
return {
|
|
37750
38470
|
ok: false,
|
|
@@ -38111,8 +38831,8 @@ init_assignment_resolver();
|
|
|
38111
38831
|
init_frontmatter();
|
|
38112
38832
|
init_event_emit();
|
|
38113
38833
|
init_templates();
|
|
38114
|
-
import { resolve as
|
|
38115
|
-
import { readFile as
|
|
38834
|
+
import { resolve as resolve92 } from "path";
|
|
38835
|
+
import { readFile as readFile65 } from "fs/promises";
|
|
38116
38836
|
function shortId() {
|
|
38117
38837
|
return generateId().split("-")[0];
|
|
38118
38838
|
}
|
|
@@ -38143,7 +38863,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
38143
38863
|
if (!isValidSlug(target)) {
|
|
38144
38864
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
38145
38865
|
}
|
|
38146
|
-
assignmentDir =
|
|
38866
|
+
assignmentDir = resolve92(baseDir, options.project, "assignments", target);
|
|
38147
38867
|
assignmentRef = target;
|
|
38148
38868
|
projectSlug = options.project;
|
|
38149
38869
|
} else {
|
|
@@ -38155,13 +38875,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
38155
38875
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
38156
38876
|
projectSlug = resolved.projectSlug;
|
|
38157
38877
|
}
|
|
38158
|
-
const commentsPath =
|
|
38878
|
+
const commentsPath = resolve92(assignmentDir, "comments.md");
|
|
38159
38879
|
const timestamp = nowTimestamp();
|
|
38160
38880
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
38161
38881
|
let currentContent;
|
|
38162
38882
|
let currentCount = 0;
|
|
38163
38883
|
if (await fileExists(commentsPath)) {
|
|
38164
|
-
currentContent = await
|
|
38884
|
+
currentContent = await readFile65(commentsPath, "utf-8");
|
|
38165
38885
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
38166
38886
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
38167
38887
|
} else {
|
|
@@ -38189,9 +38909,9 @@ ${entry}`;
|
|
|
38189
38909
|
}
|
|
38190
38910
|
await writeFileForce(commentsPath, next);
|
|
38191
38911
|
try {
|
|
38192
|
-
const assignmentMd =
|
|
38912
|
+
const assignmentMd = resolve92(assignmentDir, "assignment.md");
|
|
38193
38913
|
if (await fileExists(assignmentMd)) {
|
|
38194
|
-
const fm = parseAssignmentFrontmatter(await
|
|
38914
|
+
const fm = parseAssignmentFrontmatter(await readFile65(assignmentMd, "utf-8"));
|
|
38195
38915
|
emitEvent({
|
|
38196
38916
|
assignmentId: fm.id,
|
|
38197
38917
|
projectSlug,
|
|
@@ -38215,170 +38935,15 @@ ${entry}`;
|
|
|
38215
38935
|
}
|
|
38216
38936
|
|
|
38217
38937
|
// src/commands/capture.ts
|
|
38218
|
-
import { resolve as
|
|
38219
|
-
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";
|
|
38220
38940
|
import { existsSync as existsSync7 } from "fs";
|
|
38221
|
-
|
|
38222
|
-
// src/utils/assignment-target.ts
|
|
38223
|
-
init_paths();
|
|
38224
|
-
init_fs();
|
|
38225
|
-
init_config2();
|
|
38226
|
-
init_slug();
|
|
38227
|
-
init_assignment_resolver();
|
|
38228
|
-
init_parser();
|
|
38229
|
-
import { resolve as resolve91 } from "path";
|
|
38230
|
-
import { readFile as readFile64 } from "fs/promises";
|
|
38231
|
-
var AssignmentTargetError = class extends Error {
|
|
38232
|
-
};
|
|
38233
|
-
function classifyContext(ctx) {
|
|
38234
|
-
if (!ctx) return "empty";
|
|
38235
|
-
const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
|
|
38236
|
-
if (hasAssignment) return "assignment";
|
|
38237
|
-
if (ctx.bundleId) return "bundle";
|
|
38238
|
-
if (ctx.sessionId || ctx.transcriptPath) return "standalone";
|
|
38239
|
-
return "empty";
|
|
38240
|
-
}
|
|
38241
|
-
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
38242
|
-
const path = resolve91(assignmentDir, "assignment.md");
|
|
38243
|
-
if (!await fileExists(path)) return null;
|
|
38244
|
-
try {
|
|
38245
|
-
const content = await readFile64(path, "utf-8");
|
|
38246
|
-
const [fm] = extractFrontmatter(content);
|
|
38247
|
-
return getField(fm, "id");
|
|
38248
|
-
} catch {
|
|
38249
|
-
return null;
|
|
38250
|
-
}
|
|
38251
|
-
}
|
|
38252
|
-
async function readContextJson(cwd) {
|
|
38253
|
-
const path = resolve91(cwd, ".syntaur", "context.json");
|
|
38254
|
-
if (!await fileExists(path)) return null;
|
|
38255
|
-
try {
|
|
38256
|
-
const raw2 = await readFile64(path, "utf-8");
|
|
38257
|
-
return JSON.parse(raw2);
|
|
38258
|
-
} catch {
|
|
38259
|
-
return null;
|
|
38260
|
-
}
|
|
38261
|
-
}
|
|
38262
|
-
async function resolveAssignmentTarget(input4, opts = {}) {
|
|
38263
|
-
const config = await readConfig();
|
|
38264
|
-
const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
|
|
38265
|
-
if (opts.project) {
|
|
38266
|
-
if (!input4) {
|
|
38267
|
-
throw new AssignmentTargetError(
|
|
38268
|
-
"--project requires an assignment slug as a positional argument."
|
|
38269
|
-
);
|
|
38270
|
-
}
|
|
38271
|
-
if (!isValidSlug(opts.project)) {
|
|
38272
|
-
throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
|
|
38273
|
-
}
|
|
38274
|
-
if (!isValidSlug(input4)) {
|
|
38275
|
-
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
38276
|
-
}
|
|
38277
|
-
const projectDir = resolve91(baseDir, opts.project);
|
|
38278
|
-
const projectMdPath = resolve91(projectDir, "project.md");
|
|
38279
|
-
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
38280
|
-
throw new AssignmentTargetError(
|
|
38281
|
-
`Project "${opts.project}" not found at ${projectDir}.`
|
|
38282
|
-
);
|
|
38283
|
-
}
|
|
38284
|
-
const assignmentDir = resolve91(projectDir, "assignments", input4);
|
|
38285
|
-
const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
|
|
38286
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38287
|
-
throw new AssignmentTargetError(
|
|
38288
|
-
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
38289
|
-
);
|
|
38290
|
-
}
|
|
38291
|
-
const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
|
|
38292
|
-
return {
|
|
38293
|
-
assignmentDir,
|
|
38294
|
-
projectSlug: opts.project,
|
|
38295
|
-
assignmentSlug: input4,
|
|
38296
|
-
id,
|
|
38297
|
-
standalone: false,
|
|
38298
|
-
workspaceGroup: null
|
|
38299
|
-
};
|
|
38300
|
-
}
|
|
38301
|
-
if (input4) {
|
|
38302
|
-
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
|
|
38303
|
-
if (!resolved) {
|
|
38304
|
-
throw new AssignmentTargetError(
|
|
38305
|
-
`Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
|
|
38306
|
-
);
|
|
38307
|
-
}
|
|
38308
|
-
return resolved;
|
|
38309
|
-
}
|
|
38310
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
38311
|
-
const ctx = await readContextJson(cwd);
|
|
38312
|
-
if (!ctx) {
|
|
38313
|
-
throw new AssignmentTargetError(
|
|
38314
|
-
"No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
|
|
38315
|
-
);
|
|
38316
|
-
}
|
|
38317
|
-
if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
|
|
38318
|
-
throw new AssignmentTargetError(
|
|
38319
|
-
`Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
|
|
38320
|
-
);
|
|
38321
|
-
}
|
|
38322
|
-
if (ctx.assignmentDir) {
|
|
38323
|
-
const dir = expandHome(ctx.assignmentDir);
|
|
38324
|
-
const assignmentMdPath2 = resolve91(dir, "assignment.md");
|
|
38325
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38326
|
-
throw new AssignmentTargetError(
|
|
38327
|
-
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
38328
|
-
);
|
|
38329
|
-
}
|
|
38330
|
-
const id = await readAssignmentFrontmatterId(dir);
|
|
38331
|
-
if (!id || id.trim() === "") {
|
|
38332
|
-
throw new AssignmentTargetError(
|
|
38333
|
-
`.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
|
|
38334
|
-
);
|
|
38335
|
-
}
|
|
38336
|
-
const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
|
|
38337
|
-
const projectSlug = ctx.projectSlug ?? null;
|
|
38338
|
-
return {
|
|
38339
|
-
assignmentDir: dir,
|
|
38340
|
-
projectSlug,
|
|
38341
|
-
assignmentSlug,
|
|
38342
|
-
id,
|
|
38343
|
-
standalone: projectSlug === null,
|
|
38344
|
-
workspaceGroup: null
|
|
38345
|
-
};
|
|
38346
|
-
}
|
|
38347
|
-
if (ctx.projectSlug && ctx.assignmentSlug) {
|
|
38348
|
-
if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
|
|
38349
|
-
throw new AssignmentTargetError(
|
|
38350
|
-
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
38351
|
-
);
|
|
38352
|
-
}
|
|
38353
|
-
const assignmentDir = resolve91(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
38354
|
-
const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
|
|
38355
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38356
|
-
throw new AssignmentTargetError(
|
|
38357
|
-
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
38358
|
-
);
|
|
38359
|
-
}
|
|
38360
|
-
const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
|
|
38361
|
-
return {
|
|
38362
|
-
assignmentDir,
|
|
38363
|
-
projectSlug: ctx.projectSlug,
|
|
38364
|
-
assignmentSlug: ctx.assignmentSlug,
|
|
38365
|
-
id,
|
|
38366
|
-
standalone: false,
|
|
38367
|
-
workspaceGroup: null
|
|
38368
|
-
};
|
|
38369
|
-
}
|
|
38370
|
-
throw new AssignmentTargetError(
|
|
38371
|
-
".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
|
|
38372
|
-
);
|
|
38373
|
-
}
|
|
38374
|
-
|
|
38375
|
-
// src/commands/capture.ts
|
|
38376
38941
|
init_paths();
|
|
38377
38942
|
init_fs();
|
|
38378
38943
|
|
|
38379
38944
|
// src/utils/screencapture.ts
|
|
38380
38945
|
import { spawn as spawn8 } from "child_process";
|
|
38381
|
-
import { mkdtemp as mkdtemp2, rm as rm10, stat as
|
|
38946
|
+
import { mkdtemp as mkdtemp2, rm as rm10, stat as stat12 } from "fs/promises";
|
|
38382
38947
|
import { tmpdir as tmpdir3 } from "os";
|
|
38383
38948
|
import { join as join22 } from "path";
|
|
38384
38949
|
function argsFor(mode, pngPath) {
|
|
@@ -38436,7 +39001,7 @@ async function captureScreenshot(mode) {
|
|
|
38436
39001
|
}
|
|
38437
39002
|
let size = 0;
|
|
38438
39003
|
try {
|
|
38439
|
-
size = (await
|
|
39004
|
+
size = (await stat12(pngPath)).size;
|
|
38440
39005
|
} catch {
|
|
38441
39006
|
throw new Error("screencapture exited 0 but produced no image.");
|
|
38442
39007
|
}
|
|
@@ -38452,7 +39017,7 @@ async function captureScreenshot(mode) {
|
|
|
38452
39017
|
|
|
38453
39018
|
// src/utils/asciinema.ts
|
|
38454
39019
|
import { spawn as spawn9 } from "child_process";
|
|
38455
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
39020
|
+
import { mkdtemp as mkdtemp3, readFile as readFile66, rm as rm11 } from "fs/promises";
|
|
38456
39021
|
import { tmpdir as tmpdir4 } from "os";
|
|
38457
39022
|
import { join as join23 } from "path";
|
|
38458
39023
|
var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
@@ -38515,7 +39080,7 @@ async function captureAsciinema(opts) {
|
|
|
38515
39080
|
}
|
|
38516
39081
|
throw err2;
|
|
38517
39082
|
}
|
|
38518
|
-
const text = await
|
|
39083
|
+
const text = await readFile66(castPath, "utf8").catch(() => null);
|
|
38519
39084
|
if (text === null) {
|
|
38520
39085
|
throw new Error(
|
|
38521
39086
|
`asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
|
|
@@ -38543,9 +39108,9 @@ async function captureAsciinema(opts) {
|
|
|
38543
39108
|
// src/utils/recording.ts
|
|
38544
39109
|
init_paths();
|
|
38545
39110
|
import { spawn as spawn10 } from "child_process";
|
|
38546
|
-
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";
|
|
38547
39112
|
import { tmpdir as tmpdir5 } from "os";
|
|
38548
|
-
import { join as join24, resolve as
|
|
39113
|
+
import { join as join24, resolve as resolve93 } from "path";
|
|
38549
39114
|
import { setTimeout as sleep } from "timers/promises";
|
|
38550
39115
|
function sigintPollIntervalMs() {
|
|
38551
39116
|
const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
|
|
@@ -38566,13 +39131,13 @@ function sigtermWaitMs() {
|
|
|
38566
39131
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
|
|
38567
39132
|
}
|
|
38568
39133
|
function pidfilePath() {
|
|
38569
|
-
return
|
|
39134
|
+
return resolve93(syntaurRoot(), "recording.pid");
|
|
38570
39135
|
}
|
|
38571
39136
|
function logPath2() {
|
|
38572
|
-
return
|
|
39137
|
+
return resolve93(syntaurRoot(), "recording.log");
|
|
38573
39138
|
}
|
|
38574
39139
|
function sidecarPath() {
|
|
38575
|
-
return
|
|
39140
|
+
return resolve93(syntaurRoot(), "recording.json");
|
|
38576
39141
|
}
|
|
38577
39142
|
function ffmpegArgs(device, fps, mp4Path) {
|
|
38578
39143
|
return [
|
|
@@ -38617,7 +39182,7 @@ async function acquirePidfile(pidfile) {
|
|
|
38617
39182
|
} catch (err2) {
|
|
38618
39183
|
if (err2.code !== "EEXIST") throw err2;
|
|
38619
39184
|
if (attempt === 1) throw err2;
|
|
38620
|
-
const existing = (await
|
|
39185
|
+
const existing = (await readFile67(pidfile, "utf-8").catch(() => "")).trim();
|
|
38621
39186
|
if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
|
|
38622
39187
|
const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
|
|
38623
39188
|
const parentPid = Number.parseInt(parentPidRaw, 10);
|
|
@@ -38719,7 +39284,7 @@ async function startRecording(input4) {
|
|
|
38719
39284
|
logHandle = null;
|
|
38720
39285
|
if (warmupMs > 0) await sleep(warmupMs);
|
|
38721
39286
|
if (!await isProcessAlive(pid)) {
|
|
38722
|
-
const tail = await
|
|
39287
|
+
const tail = await readFile67(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
|
|
38723
39288
|
acquiredPid = null;
|
|
38724
39289
|
throw new Error(
|
|
38725
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}
|
|
@@ -38776,7 +39341,7 @@ ${tail}`
|
|
|
38776
39341
|
async function stopRecording() {
|
|
38777
39342
|
const pidfile = pidfilePath();
|
|
38778
39343
|
const sidecar = sidecarPath();
|
|
38779
|
-
const pidRaw = await
|
|
39344
|
+
const pidRaw = await readFile67(pidfile, "utf-8").catch(() => null);
|
|
38780
39345
|
if (pidRaw === null) {
|
|
38781
39346
|
throw new Error(
|
|
38782
39347
|
`No active recording found (no pidfile at ${pidfile}). Did you run --start?`
|
|
@@ -38786,7 +39351,7 @@ async function stopRecording() {
|
|
|
38786
39351
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
38787
39352
|
throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
|
|
38788
39353
|
}
|
|
38789
|
-
const sidecarRaw = await
|
|
39354
|
+
const sidecarRaw = await readFile67(sidecar, "utf-8").catch(() => null);
|
|
38790
39355
|
if (sidecarRaw === null) {
|
|
38791
39356
|
throw new Error(
|
|
38792
39357
|
`No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
|
|
@@ -38831,7 +39396,7 @@ async function stopRecording() {
|
|
|
38831
39396
|
if (alive) {
|
|
38832
39397
|
throw new Error(`ffmpeg (PID ${pid}) refused to die after SIGKILL`);
|
|
38833
39398
|
}
|
|
38834
|
-
const st = await
|
|
39399
|
+
const st = await stat13(sidecarData.mp4Path).catch(() => null);
|
|
38835
39400
|
if (st === null || st.size === 0) {
|
|
38836
39401
|
await unlink12(pidfile).catch(() => {
|
|
38837
39402
|
});
|
|
@@ -38851,7 +39416,7 @@ async function stopRecording() {
|
|
|
38851
39416
|
// src/db/proof-db.ts
|
|
38852
39417
|
init_paths();
|
|
38853
39418
|
import Database6 from "better-sqlite3";
|
|
38854
|
-
import { resolve as
|
|
39419
|
+
import { resolve as resolve94 } from "path";
|
|
38855
39420
|
var db5 = null;
|
|
38856
39421
|
var PROOF_SCHEMA_VERSION = "1";
|
|
38857
39422
|
var SCHEMA_SQL5 = `
|
|
@@ -38871,7 +39436,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
38871
39436
|
`;
|
|
38872
39437
|
function initProofDb(dbPath) {
|
|
38873
39438
|
if (db5) return db5;
|
|
38874
|
-
const finalPath = dbPath ??
|
|
39439
|
+
const finalPath = dbPath ?? resolve94(syntaurRoot(), "syntaur.db");
|
|
38875
39440
|
db5 = new Database6(finalPath);
|
|
38876
39441
|
db5.pragma("journal_mode = WAL");
|
|
38877
39442
|
db5.exec(SCHEMA_SQL5);
|
|
@@ -38919,7 +39484,7 @@ function listArtifactsByAssignment(assignmentId) {
|
|
|
38919
39484
|
|
|
38920
39485
|
// src/utils/transcribers/elevenlabs.ts
|
|
38921
39486
|
import { spawn as spawn11 } from "child_process";
|
|
38922
|
-
import { mkdtemp as mkdtemp5, readFile as
|
|
39487
|
+
import { mkdtemp as mkdtemp5, readFile as readFile68, rm as rm13 } from "fs/promises";
|
|
38923
39488
|
import { tmpdir as tmpdir6 } from "os";
|
|
38924
39489
|
import { join as join25 } from "path";
|
|
38925
39490
|
var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
|
|
@@ -38983,7 +39548,7 @@ async function extractAudio(videoAbsPath, wavOut) {
|
|
|
38983
39548
|
throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
|
|
38984
39549
|
}
|
|
38985
39550
|
async function callScribe(wavPath, apiKey, opts) {
|
|
38986
|
-
const audio = await
|
|
39551
|
+
const audio = await readFile68(wavPath);
|
|
38987
39552
|
const form = new FormData();
|
|
38988
39553
|
form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
|
|
38989
39554
|
form.set("model_id", "scribe_v1");
|
|
@@ -39299,7 +39864,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39299
39864
|
});
|
|
39300
39865
|
}
|
|
39301
39866
|
if (options.file) {
|
|
39302
|
-
const expanded = options.file.startsWith("~/") ?
|
|
39867
|
+
const expanded = options.file.startsWith("~/") ? resolve95(process.env.HOME ?? "", options.file.slice(2)) : resolve95(options.file);
|
|
39303
39868
|
if (!await fileExists(expanded)) {
|
|
39304
39869
|
throw new Error(`--file does not exist: ${options.file}`);
|
|
39305
39870
|
}
|
|
@@ -39311,7 +39876,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39311
39876
|
`--file is unreadable: ${options.file} (${e instanceof Error ? e.message : String(e)})`
|
|
39312
39877
|
);
|
|
39313
39878
|
}
|
|
39314
|
-
const st = await
|
|
39879
|
+
const st = await stat14(real);
|
|
39315
39880
|
if (!st.isFile()) {
|
|
39316
39881
|
throw new Error(`--file is not a regular file: ${options.file}`);
|
|
39317
39882
|
}
|
|
@@ -39340,7 +39905,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39340
39905
|
}
|
|
39341
39906
|
initProofDb();
|
|
39342
39907
|
const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
|
|
39343
|
-
const destDir =
|
|
39908
|
+
const destDir = resolve95(proofDir(resolved.assignmentDir), subdir);
|
|
39344
39909
|
if (resolvedSource) await mkdir12(destDir, { recursive: true });
|
|
39345
39910
|
const ext = resolvedSource ? extensionForKind(kind) : null;
|
|
39346
39911
|
let id = null;
|
|
@@ -39349,7 +39914,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39349
39914
|
let lastErr = null;
|
|
39350
39915
|
for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
|
|
39351
39916
|
const candidate = generateArtifactId();
|
|
39352
|
-
const candidateAbsPath = resolvedSource && ext ?
|
|
39917
|
+
const candidateAbsPath = resolvedSource && ext ? resolve95(destDir, `${candidate}.${ext}`) : null;
|
|
39353
39918
|
const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
|
|
39354
39919
|
try {
|
|
39355
39920
|
insertArtifact({
|
|
@@ -39393,7 +39958,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39393
39958
|
}
|
|
39394
39959
|
}
|
|
39395
39960
|
if (options.transcribe && kind === "video" && absPath && id) {
|
|
39396
|
-
const sidecarPath2 =
|
|
39961
|
+
const sidecarPath2 = resolve95(destDir, `${id}.transcript.md`);
|
|
39397
39962
|
if (existsSync7(sidecarPath2)) {
|
|
39398
39963
|
console.warn(
|
|
39399
39964
|
`transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
|
|
@@ -39425,7 +39990,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39425
39990
|
const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
|
|
39426
39991
|
console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
|
|
39427
39992
|
if (relativeFilePath) {
|
|
39428
|
-
console.log(` file: ${
|
|
39993
|
+
console.log(` file: ${resolve95(resolved.assignmentDir, relativeFilePath)}`);
|
|
39429
39994
|
}
|
|
39430
39995
|
} catch (err2) {
|
|
39431
39996
|
if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
|
|
@@ -39448,8 +40013,8 @@ async function captureCommand(target, options = {}) {
|
|
|
39448
40013
|
|
|
39449
40014
|
// src/commands/proof.ts
|
|
39450
40015
|
import { Command as Command6 } from "commander";
|
|
39451
|
-
import { readFile as
|
|
39452
|
-
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";
|
|
39453
40018
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
39454
40019
|
|
|
39455
40020
|
// src/utils/acceptance-criteria-parse.ts
|
|
@@ -39689,11 +40254,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
|
|
|
39689
40254
|
|
|
39690
40255
|
// src/commands/proof.ts
|
|
39691
40256
|
async function readAssignmentMeta(assignmentDir) {
|
|
39692
|
-
const path =
|
|
40257
|
+
const path = resolve96(assignmentDir, "assignment.md");
|
|
39693
40258
|
if (!await fileExists(path)) {
|
|
39694
40259
|
return { title: "", body: "" };
|
|
39695
40260
|
}
|
|
39696
|
-
const content = await
|
|
40261
|
+
const content = await readFile69(path, "utf-8");
|
|
39697
40262
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
39698
40263
|
let title = "";
|
|
39699
40264
|
if (fmMatch) {
|
|
@@ -39740,7 +40305,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
39740
40305
|
for (const r of rows) {
|
|
39741
40306
|
if (!r.file_path) continue;
|
|
39742
40307
|
if (r.kind !== "http" && r.kind !== "text") continue;
|
|
39743
|
-
const abs =
|
|
40308
|
+
const abs = resolve96(assignmentDir, r.file_path);
|
|
39744
40309
|
if (!isWithin(proofRoot, abs)) {
|
|
39745
40310
|
out.set(r.file_path, null);
|
|
39746
40311
|
continue;
|
|
@@ -39749,13 +40314,13 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
39749
40314
|
out.set(r.file_path, null);
|
|
39750
40315
|
continue;
|
|
39751
40316
|
}
|
|
39752
|
-
const st = await
|
|
40317
|
+
const st = await stat15(abs);
|
|
39753
40318
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) {
|
|
39754
40319
|
out.set(r.file_path, null);
|
|
39755
40320
|
continue;
|
|
39756
40321
|
}
|
|
39757
40322
|
try {
|
|
39758
|
-
out.set(r.file_path, await
|
|
40323
|
+
out.set(r.file_path, await readFile69(abs, "utf-8"));
|
|
39759
40324
|
} catch {
|
|
39760
40325
|
out.set(r.file_path, null);
|
|
39761
40326
|
}
|
|
@@ -39767,14 +40332,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
|
|
|
39767
40332
|
const proofRoot = proofDir(assignmentDir);
|
|
39768
40333
|
for (const r of rows) {
|
|
39769
40334
|
if (r.kind !== "video" || !r.file_path) continue;
|
|
39770
|
-
const videoAbs =
|
|
39771
|
-
const sidecar =
|
|
40335
|
+
const videoAbs = resolve96(assignmentDir, r.file_path);
|
|
40336
|
+
const sidecar = resolve96(dirname27(videoAbs), `${r.id}.transcript.md`);
|
|
39772
40337
|
if (!isWithin(proofRoot, sidecar)) continue;
|
|
39773
40338
|
if (!await fileExists(sidecar)) continue;
|
|
39774
|
-
const st = await
|
|
40339
|
+
const st = await stat15(sidecar);
|
|
39775
40340
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
|
|
39776
40341
|
try {
|
|
39777
|
-
out.set(r.id, await
|
|
40342
|
+
out.set(r.id, await readFile69(sidecar, "utf-8"));
|
|
39778
40343
|
} catch {
|
|
39779
40344
|
}
|
|
39780
40345
|
}
|
|
@@ -39808,8 +40373,8 @@ async function proofBuildCommand(target, options = {}) {
|
|
|
39808
40373
|
};
|
|
39809
40374
|
const md = renderProofMarkdown(renderParams);
|
|
39810
40375
|
const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
|
|
39811
|
-
const mdPath =
|
|
39812
|
-
const htmlPath =
|
|
40376
|
+
const mdPath = resolve96(resolved.assignmentDir, "proof.md");
|
|
40377
|
+
const htmlPath = resolve96(resolved.assignmentDir, "proof.html");
|
|
39813
40378
|
await atomicWrite(mdPath, md);
|
|
39814
40379
|
await atomicWrite(htmlPath, html);
|
|
39815
40380
|
console.log(`Wrote ${htmlPath}`);
|
|
@@ -40352,7 +40917,7 @@ function isScheduledSessionLive(sessionId, launchPid) {
|
|
|
40352
40917
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
40353
40918
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2, realpathSync as realpathSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
40354
40919
|
import { homedir as homedir16, userInfo } from "os";
|
|
40355
|
-
import { dirname as dirname28, join as join26, resolve as
|
|
40920
|
+
import { dirname as dirname28, join as join26, resolve as resolve97 } from "path";
|
|
40356
40921
|
var LAUNCH_AGENT_LABEL = "com.syntaur.schedule.tick";
|
|
40357
40922
|
var LaunchAgentRefusalError = class extends Error {
|
|
40358
40923
|
constructor(message) {
|
|
@@ -40406,7 +40971,7 @@ function absolutize(p) {
|
|
|
40406
40971
|
try {
|
|
40407
40972
|
return realpathSync4(p);
|
|
40408
40973
|
} catch {
|
|
40409
|
-
return
|
|
40974
|
+
return resolve97(p);
|
|
40410
40975
|
}
|
|
40411
40976
|
}
|
|
40412
40977
|
function defaultRun(command, args) {
|
|
@@ -40467,7 +41032,7 @@ function uninstallLaunchAgent(deps = {}) {
|
|
|
40467
41032
|
// src/commands/schedule.ts
|
|
40468
41033
|
init_timestamp();
|
|
40469
41034
|
var DURATION_REGEX2 = /^(\d+)\s*(s|m|h|d)?$/i;
|
|
40470
|
-
function
|
|
41035
|
+
function parseDurationMs2(input4) {
|
|
40471
41036
|
const m = DURATION_REGEX2.exec(input4.trim());
|
|
40472
41037
|
if (!m) throw new Error(`invalid duration "${input4}" \u2014 use e.g. 30s, 5m, 2h, 1d`);
|
|
40473
41038
|
const n = Number.parseInt(m[1], 10);
|
|
@@ -40488,7 +41053,7 @@ function validateTerminalChoice(value) {
|
|
|
40488
41053
|
function buildTrigger(opts) {
|
|
40489
41054
|
const chosen = [];
|
|
40490
41055
|
if (opts.at) chosen.push({ kind: "at", at: opts.at });
|
|
40491
|
-
if (opts.in) chosen.push({ kind: "in", durationMs:
|
|
41056
|
+
if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs2(opts.in), anchorIso: nowTimestamp() });
|
|
40492
41057
|
if (opts.cron) {
|
|
40493
41058
|
try {
|
|
40494
41059
|
new Cron2(opts.cron).nextRun();
|
|
@@ -40556,7 +41121,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
|
|
|
40556
41121
|
const unattended = !opts.interactive;
|
|
40557
41122
|
if (unattended) assertUnattendedTerminalSupported(terminal);
|
|
40558
41123
|
const limits = defaultLimits();
|
|
40559
|
-
if (opts.maxRuntime) limits.maxRuntimeMs =
|
|
41124
|
+
if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs2(opts.maxRuntime);
|
|
40560
41125
|
if (opts.maxLaunchesPerDay) {
|
|
40561
41126
|
const n = Number(opts.maxLaunchesPerDay);
|
|
40562
41127
|
if (!Number.isInteger(n) || n <= 0) {
|
|
@@ -40564,7 +41129,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
|
|
|
40564
41129
|
}
|
|
40565
41130
|
limits.maxLaunchesPerDay = n;
|
|
40566
41131
|
}
|
|
40567
|
-
if (opts.cooldown) limits.cooldownMs =
|
|
41132
|
+
if (opts.cooldown) limits.cooldownMs = parseDurationMs2(opts.cooldown);
|
|
40568
41133
|
const now = nowTimestamp();
|
|
40569
41134
|
const job = {
|
|
40570
41135
|
id: newJobId(),
|
|
@@ -40782,8 +41347,8 @@ init_slug();
|
|
|
40782
41347
|
init_timestamp();
|
|
40783
41348
|
init_assignment_resolver();
|
|
40784
41349
|
init_assignment_todos();
|
|
40785
|
-
import { resolve as
|
|
40786
|
-
import { readFile as
|
|
41350
|
+
import { resolve as resolve98 } from "path";
|
|
41351
|
+
import { readFile as readFile70 } from "fs/promises";
|
|
40787
41352
|
async function requestCommand(target, text, options = {}) {
|
|
40788
41353
|
if (!text || !text.trim()) {
|
|
40789
41354
|
throw new Error("Request text cannot be empty.");
|
|
@@ -40799,7 +41364,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40799
41364
|
if (!isValidSlug(target)) {
|
|
40800
41365
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
40801
41366
|
}
|
|
40802
|
-
assignmentDir =
|
|
41367
|
+
assignmentDir = resolve98(baseDir, options.project, "assignments", target);
|
|
40803
41368
|
targetRef = target;
|
|
40804
41369
|
} else {
|
|
40805
41370
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -40809,12 +41374,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40809
41374
|
assignmentDir = resolved.assignmentDir;
|
|
40810
41375
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
40811
41376
|
}
|
|
40812
|
-
const assignmentMdPath2 =
|
|
41377
|
+
const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
|
|
40813
41378
|
if (!await fileExists(assignmentMdPath2)) {
|
|
40814
41379
|
throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
|
|
40815
41380
|
}
|
|
40816
41381
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
40817
|
-
let content = await
|
|
41382
|
+
let content = await readFile70(assignmentMdPath2, "utf-8");
|
|
40818
41383
|
content = appendTodosToAssignmentBody(content, [
|
|
40819
41384
|
{ description: `${text.trim()} (from: ${source})` }
|
|
40820
41385
|
]);
|
|
@@ -40827,14 +41392,15 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40827
41392
|
init_fs();
|
|
40828
41393
|
init_paths();
|
|
40829
41394
|
init_config2();
|
|
41395
|
+
init_recompute();
|
|
40830
41396
|
import { Command as Command10 } from "commander";
|
|
40831
|
-
import { readFile as
|
|
40832
|
-
import { resolve as
|
|
41397
|
+
import { readFile as readFile71, readdir as readdir34 } from "fs/promises";
|
|
41398
|
+
import { resolve as resolve99 } from "path";
|
|
40833
41399
|
async function readContextAssignmentDir(cwd) {
|
|
40834
|
-
const path =
|
|
41400
|
+
const path = resolve99(cwd, ".syntaur", "context.json");
|
|
40835
41401
|
if (!await fileExists(path)) return null;
|
|
40836
41402
|
try {
|
|
40837
|
-
const raw2 = await
|
|
41403
|
+
const raw2 = await readFile71(path, "utf-8");
|
|
40838
41404
|
const ctx = JSON.parse(raw2);
|
|
40839
41405
|
if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
|
|
40840
41406
|
return ctx.assignmentDir;
|
|
@@ -40848,9 +41414,9 @@ async function resolveAssignmentDir(opts) {
|
|
|
40848
41414
|
const cwd = opts.cwd ?? process.cwd();
|
|
40849
41415
|
if (opts.assignment) {
|
|
40850
41416
|
if (opts.project) {
|
|
40851
|
-
return
|
|
41417
|
+
return resolve99((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
|
|
40852
41418
|
}
|
|
40853
|
-
return
|
|
41419
|
+
return resolve99(assignmentsDir(), opts.assignment);
|
|
40854
41420
|
}
|
|
40855
41421
|
const fromCtx = await readContextAssignmentDir(cwd);
|
|
40856
41422
|
if (fromCtx) return fromCtx;
|
|
@@ -41046,17 +41612,17 @@ async function runPlanCreate(options) {
|
|
|
41046
41612
|
if (!await fileExists(assignmentDir)) {
|
|
41047
41613
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
41048
41614
|
}
|
|
41049
|
-
const assignmentMdPath2 =
|
|
41615
|
+
const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
|
|
41050
41616
|
if (!await fileExists(assignmentMdPath2)) {
|
|
41051
41617
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
41052
41618
|
}
|
|
41053
|
-
const planPath =
|
|
41619
|
+
const planPath = resolve99(assignmentDir, "plan.md");
|
|
41054
41620
|
if (await fileExists(planPath) && !options.force) {
|
|
41055
41621
|
throw new Error(
|
|
41056
41622
|
"plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
|
|
41057
41623
|
);
|
|
41058
41624
|
}
|
|
41059
|
-
const assignmentMd = await
|
|
41625
|
+
const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
|
|
41060
41626
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
41061
41627
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
41062
41628
|
await writeFileForce(planPath, buildInitialPlanStub(slug));
|
|
@@ -41070,13 +41636,14 @@ async function runPlanCreate(options) {
|
|
|
41070
41636
|
}
|
|
41071
41637
|
console.log(`Created ${planPath}`);
|
|
41072
41638
|
if (appended > 0) console.log(`Appended ${appended} plan todo(s) to assignment.md.`);
|
|
41639
|
+
await recomputeAssignmentDir(assignmentDir, "plan-create", null);
|
|
41073
41640
|
}
|
|
41074
41641
|
async function runPlanVersion(options) {
|
|
41075
41642
|
const assignmentDir = await resolveAssignmentDir(options);
|
|
41076
41643
|
if (!await fileExists(assignmentDir)) {
|
|
41077
41644
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
41078
41645
|
}
|
|
41079
|
-
const assignmentMdPath2 =
|
|
41646
|
+
const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
|
|
41080
41647
|
if (!await fileExists(assignmentMdPath2)) {
|
|
41081
41648
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
41082
41649
|
}
|
|
@@ -41088,15 +41655,15 @@ async function runPlanVersion(options) {
|
|
|
41088
41655
|
}
|
|
41089
41656
|
const current = planFiles[planFiles.length - 1];
|
|
41090
41657
|
const next = nextPlanFileName(current.version);
|
|
41091
|
-
const newPath =
|
|
41658
|
+
const newPath = resolve99(assignmentDir, next.fileName);
|
|
41092
41659
|
if (await fileExists(newPath) && !options.force) {
|
|
41093
41660
|
throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
|
|
41094
41661
|
}
|
|
41095
|
-
const assignmentMd = await
|
|
41662
|
+
const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
|
|
41096
41663
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
41097
41664
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
41098
|
-
const oldPlanPath =
|
|
41099
|
-
const oldPlanContent = await
|
|
41665
|
+
const oldPlanPath = resolve99(assignmentDir, current.fileName);
|
|
41666
|
+
const oldPlanContent = await readFile71(oldPlanPath, "utf-8");
|
|
41100
41667
|
const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
|
|
41101
41668
|
const carriedTodos = extractUncheckedTodos(oldBody);
|
|
41102
41669
|
const stub = buildNewPlanStub({
|
|
@@ -41117,6 +41684,7 @@ async function runPlanVersion(options) {
|
|
|
41117
41684
|
);
|
|
41118
41685
|
console.log(`Path: ${newPath}`);
|
|
41119
41686
|
console.log(`Carried forward: ${carriedTodos.length} unchecked task(s).`);
|
|
41687
|
+
await recomputeAssignmentDir(assignmentDir, "plan-version", null);
|
|
41120
41688
|
}
|
|
41121
41689
|
var planCommand = new Command10("plan").description("Manage plan files for the active assignment");
|
|
41122
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) => {
|
|
@@ -41166,28 +41734,28 @@ init_cwd();
|
|
|
41166
41734
|
init_session_db();
|
|
41167
41735
|
init_agent_sessions();
|
|
41168
41736
|
import { Command as Command11 } from "commander";
|
|
41169
|
-
import { readFile as
|
|
41170
|
-
import { resolve as
|
|
41737
|
+
import { readFile as readFile72, readdir as readdir35, stat as stat16 } from "fs/promises";
|
|
41738
|
+
import { resolve as resolve100 } from "path";
|
|
41171
41739
|
async function readContext(cwd) {
|
|
41172
|
-
const path =
|
|
41740
|
+
const path = resolve100(cwd, ".syntaur", "context.json");
|
|
41173
41741
|
if (!await fileExists(path)) return null;
|
|
41174
41742
|
try {
|
|
41175
|
-
const raw2 = await
|
|
41743
|
+
const raw2 = await readFile72(path, "utf-8");
|
|
41176
41744
|
return JSON.parse(raw2);
|
|
41177
41745
|
} catch {
|
|
41178
41746
|
return null;
|
|
41179
41747
|
}
|
|
41180
41748
|
}
|
|
41181
41749
|
async function findLatestSessionSummary(assignmentDir) {
|
|
41182
|
-
const sessionsRoot =
|
|
41750
|
+
const sessionsRoot = resolve100(assignmentDir, "sessions");
|
|
41183
41751
|
if (!await fileExists(sessionsRoot)) return null;
|
|
41184
41752
|
const entries = await readdir35(sessionsRoot, { withFileTypes: true });
|
|
41185
41753
|
let best = null;
|
|
41186
41754
|
for (const entry of entries) {
|
|
41187
41755
|
if (!entry.isDirectory()) continue;
|
|
41188
|
-
const summaryPath =
|
|
41756
|
+
const summaryPath = resolve100(sessionsRoot, entry.name, "summary.md");
|
|
41189
41757
|
if (!await fileExists(summaryPath)) continue;
|
|
41190
|
-
const st = await
|
|
41758
|
+
const st = await stat16(summaryPath);
|
|
41191
41759
|
if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
|
|
41192
41760
|
best = { sessionId: entry.name, path: summaryPath, mtime: st.mtime };
|
|
41193
41761
|
}
|
|
@@ -41195,9 +41763,9 @@ async function findLatestSessionSummary(assignmentDir) {
|
|
|
41195
41763
|
return best;
|
|
41196
41764
|
}
|
|
41197
41765
|
async function findOpenHandoff(assignmentDir) {
|
|
41198
|
-
const handoffPath =
|
|
41766
|
+
const handoffPath = resolve100(assignmentDir, "handoff.md");
|
|
41199
41767
|
if (!await fileExists(handoffPath)) return null;
|
|
41200
|
-
const content = await
|
|
41768
|
+
const content = await readFile72(handoffPath, "utf-8");
|
|
41201
41769
|
const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
|
|
41202
41770
|
if (body.length === 0) return null;
|
|
41203
41771
|
if (/^<!--[\s\S]*-->$/.test(body)) return null;
|
|
@@ -41292,7 +41860,7 @@ async function resolveSaveTarget(options, cwd) {
|
|
|
41292
41860
|
let slug;
|
|
41293
41861
|
const ctx = await readContext(cwd);
|
|
41294
41862
|
if (options.assignment) {
|
|
41295
|
-
assignmentDir = options.project ?
|
|
41863
|
+
assignmentDir = options.project ? resolve100((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve100(assignmentsDir(), options.assignment);
|
|
41296
41864
|
slug = options.assignment;
|
|
41297
41865
|
} else {
|
|
41298
41866
|
if (!ctx?.assignmentDir) {
|
|
@@ -41349,21 +41917,21 @@ function extractCreated(content) {
|
|
|
41349
41917
|
}
|
|
41350
41918
|
async function runSessionSave(options, cwd = process.cwd(), body) {
|
|
41351
41919
|
const { assignmentDir, slug, sessionId } = await resolveSaveTarget(options, cwd);
|
|
41352
|
-
if (!await fileExists(
|
|
41920
|
+
if (!await fileExists(resolve100(assignmentDir, "assignment.md"))) {
|
|
41353
41921
|
throw new Error(`No assignment found at ${assignmentDir} (missing assignment.md).`);
|
|
41354
41922
|
}
|
|
41355
|
-
const sessionDir =
|
|
41356
|
-
const summaryPath =
|
|
41923
|
+
const sessionDir = resolve100(assignmentDir, "sessions", sessionId);
|
|
41924
|
+
const summaryPath = resolve100(sessionDir, "summary.md");
|
|
41357
41925
|
const now = nowTimestamp();
|
|
41358
41926
|
let created = now;
|
|
41359
41927
|
if (await fileExists(summaryPath)) {
|
|
41360
|
-
const existing = await
|
|
41928
|
+
const existing = await readFile72(summaryPath, "utf-8");
|
|
41361
41929
|
created = extractCreated(existing) ?? now;
|
|
41362
41930
|
}
|
|
41363
41931
|
let sectionBody2 = body;
|
|
41364
41932
|
if (sectionBody2 === void 0) {
|
|
41365
41933
|
if (options.fromFile) {
|
|
41366
|
-
sectionBody2 = await
|
|
41934
|
+
sectionBody2 = await readFile72(resolve100(cwd, options.fromFile), "utf-8");
|
|
41367
41935
|
} else {
|
|
41368
41936
|
sectionBody2 = await readStdin();
|
|
41369
41937
|
}
|
|
@@ -41399,7 +41967,7 @@ async function runSessionRegister(rawStdin, options = {}, deps = {}) {
|
|
|
41399
41967
|
if (!isSafeSessionId(sessionId) || !cwd) return result;
|
|
41400
41968
|
result.sessionId = sessionId;
|
|
41401
41969
|
const transcriptPath = payload.transcript_path ?? "";
|
|
41402
|
-
const contextPath =
|
|
41970
|
+
const contextPath = resolve100(cwd, ".syntaur", "context.json");
|
|
41403
41971
|
const hasContextFile = await fileExists(contextPath);
|
|
41404
41972
|
const ctx = hasContextFile ? await readContext(cwd) : null;
|
|
41405
41973
|
if (ctx) {
|
|
@@ -41545,8 +42113,8 @@ sessionCommand.command("resolve-id").description(
|
|
|
41545
42113
|
// src/commands/worktree.ts
|
|
41546
42114
|
init_git_worktree();
|
|
41547
42115
|
import { Command as Command12 } from "commander";
|
|
41548
|
-
import { readFile as
|
|
41549
|
-
import { resolve as
|
|
42116
|
+
import { readFile as readFile73 } from "fs/promises";
|
|
42117
|
+
import { resolve as resolve102 } from "path";
|
|
41550
42118
|
init_fs();
|
|
41551
42119
|
init_paths();
|
|
41552
42120
|
init_config2();
|
|
@@ -41555,12 +42123,12 @@ init_state_machine();
|
|
|
41555
42123
|
|
|
41556
42124
|
// src/utils/path-canon.ts
|
|
41557
42125
|
import { realpathSync as realpathSync5 } from "fs";
|
|
41558
|
-
import { resolve as
|
|
42126
|
+
import { resolve as resolve101 } from "path";
|
|
41559
42127
|
function canonicalPath(p) {
|
|
41560
42128
|
try {
|
|
41561
|
-
return realpathSync5(
|
|
42129
|
+
return realpathSync5(resolve101(p));
|
|
41562
42130
|
} catch {
|
|
41563
|
-
return
|
|
42131
|
+
return resolve101(p).replace(/\/+$/, "");
|
|
41564
42132
|
}
|
|
41565
42133
|
}
|
|
41566
42134
|
|
|
@@ -41588,10 +42156,10 @@ function countSessionsByPath(dbPath, paths) {
|
|
|
41588
42156
|
init_timestamp();
|
|
41589
42157
|
init_frontmatter();
|
|
41590
42158
|
async function readContext2(cwd) {
|
|
41591
|
-
const path =
|
|
42159
|
+
const path = resolve102(cwd, ".syntaur", "context.json");
|
|
41592
42160
|
if (!await fileExists(path)) return null;
|
|
41593
42161
|
try {
|
|
41594
|
-
return JSON.parse(await
|
|
42162
|
+
return JSON.parse(await readFile73(path, "utf-8"));
|
|
41595
42163
|
} catch {
|
|
41596
42164
|
return null;
|
|
41597
42165
|
}
|
|
@@ -41600,12 +42168,12 @@ async function resolveAssignmentPath2(opts) {
|
|
|
41600
42168
|
if (opts.assignment) {
|
|
41601
42169
|
if (opts.project) {
|
|
41602
42170
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
41603
|
-
return
|
|
42171
|
+
return resolve102(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
41604
42172
|
}
|
|
41605
|
-
return
|
|
42173
|
+
return resolve102(assignmentsDir(), opts.assignment, "assignment.md");
|
|
41606
42174
|
}
|
|
41607
42175
|
const ctx = await readContext2(opts.cwd);
|
|
41608
|
-
if (ctx?.assignmentDir) return
|
|
42176
|
+
if (ctx?.assignmentDir) return resolve102(ctx.assignmentDir, "assignment.md");
|
|
41609
42177
|
throw new Error(
|
|
41610
42178
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
41611
42179
|
);
|
|
@@ -41616,7 +42184,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
|
|
|
41616
42184
|
}
|
|
41617
42185
|
const repository = options.repository ?? cwd;
|
|
41618
42186
|
const parentBranch = options.parentBranch ?? "main";
|
|
41619
|
-
const worktreePath = options.worktreePath ??
|
|
42187
|
+
const worktreePath = options.worktreePath ?? resolve102(repository, ".worktrees", options.branch);
|
|
41620
42188
|
const assignmentPath = await resolveAssignmentPath2({
|
|
41621
42189
|
assignment: options.assignment,
|
|
41622
42190
|
project: options.project,
|
|
@@ -41646,7 +42214,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
|
|
|
41646
42214
|
if (!await fileExists(assignmentPath)) {
|
|
41647
42215
|
throw new Error(`Assignment file not found: ${assignmentPath}`);
|
|
41648
42216
|
}
|
|
41649
|
-
const original = await
|
|
42217
|
+
const original = await readFile73(assignmentPath, "utf-8");
|
|
41650
42218
|
const fm = parseAssignmentFrontmatter(original);
|
|
41651
42219
|
const repository = options.repository ?? fm.workspace.repository ?? void 0;
|
|
41652
42220
|
const worktreePath = fm.workspace.worktreePath ?? void 0;
|
|
@@ -41712,7 +42280,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
|
|
|
41712
42280
|
const owners = /* @__PURE__ */ new Map();
|
|
41713
42281
|
for (const entry of walk.withAssignmentMd) {
|
|
41714
42282
|
try {
|
|
41715
|
-
const content = await
|
|
42283
|
+
const content = await readFile73(resolve102(entry.assignmentDir, "assignment.md"), "utf-8");
|
|
41716
42284
|
const fm = parseAssignmentFrontmatter(content);
|
|
41717
42285
|
const wp = fm.workspace?.worktreePath;
|
|
41718
42286
|
if (!wp) continue;
|
|
@@ -41734,7 +42302,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
|
|
|
41734
42302
|
const repoTop = rt ? canonicalPath(rt) : null;
|
|
41735
42303
|
const cwdTop = ct ? canonicalPath(ct) : null;
|
|
41736
42304
|
const mainPath = entries.length > 0 ? canonicalPath(entries[0].worktreePath) : null;
|
|
41737
|
-
const dbPath =
|
|
42305
|
+
const dbPath = resolve102(syntaurRoot(), "syntaur.db");
|
|
41738
42306
|
const candidates = [];
|
|
41739
42307
|
for (const entry of entries) {
|
|
41740
42308
|
const canon = canonicalPath(entry.worktreePath);
|
|
@@ -41953,8 +42521,8 @@ worktreeCommand.command("gc").description(
|
|
|
41953
42521
|
// src/commands/open.ts
|
|
41954
42522
|
init_fs();
|
|
41955
42523
|
import { Command as Command13 } from "commander";
|
|
41956
|
-
import { readFile as
|
|
41957
|
-
import { resolve as
|
|
42524
|
+
import { readFile as readFile74 } from "fs/promises";
|
|
42525
|
+
import { resolve as resolve103 } from "path";
|
|
41958
42526
|
init_frontmatter();
|
|
41959
42527
|
init_config2();
|
|
41960
42528
|
init_paths();
|
|
@@ -42008,13 +42576,13 @@ function openInTerminal(path, config) {
|
|
|
42008
42576
|
// src/commands/open.ts
|
|
42009
42577
|
async function runOpen(assignmentArg, options) {
|
|
42010
42578
|
const resolved = options.id ? await resolveAssignmentTarget(options.id, {}) : await resolveAssignmentTarget(assignmentArg, { project: options.project });
|
|
42011
|
-
const assignmentPath =
|
|
42579
|
+
const assignmentPath = resolve103(resolved.assignmentDir, "assignment.md");
|
|
42012
42580
|
if (!await fileExists(assignmentPath)) {
|
|
42013
42581
|
throw new SyntaurError(`Assignment file not found: ${assignmentPath}`, {
|
|
42014
42582
|
remediation: "check the assignment slug or --id"
|
|
42015
42583
|
});
|
|
42016
42584
|
}
|
|
42017
|
-
const fm = parseAssignmentFrontmatter(await
|
|
42585
|
+
const fm = parseAssignmentFrontmatter(await readFile74(assignmentPath, "utf-8"));
|
|
42018
42586
|
const worktreePath = fm.workspace?.worktreePath;
|
|
42019
42587
|
if (!worktreePath) {
|
|
42020
42588
|
throw new SyntaurError("No worktree recorded for this assignment.", {
|
|
@@ -42079,14 +42647,14 @@ init_config2();
|
|
|
42079
42647
|
init_fs();
|
|
42080
42648
|
init_slug();
|
|
42081
42649
|
import { Command as Command14 } from "commander";
|
|
42082
|
-
import { resolve as
|
|
42083
|
-
import { readFile as
|
|
42650
|
+
import { resolve as resolve105 } from "path";
|
|
42651
|
+
import { readFile as readFile76, readdir as readdir37, rm as rm15 } from "fs/promises";
|
|
42084
42652
|
|
|
42085
42653
|
// src/utils/project-indexes.ts
|
|
42086
42654
|
init_parser();
|
|
42087
42655
|
init_fs();
|
|
42088
|
-
import { readdir as readdir36, readFile as
|
|
42089
|
-
import { resolve as
|
|
42656
|
+
import { readdir as readdir36, readFile as readFile75 } from "fs/promises";
|
|
42657
|
+
import { resolve as resolve104 } from "path";
|
|
42090
42658
|
function nowIso3() {
|
|
42091
42659
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
42092
42660
|
}
|
|
@@ -42105,7 +42673,7 @@ function joinList(items) {
|
|
|
42105
42673
|
return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
|
|
42106
42674
|
}
|
|
42107
42675
|
async function rebuildResourcesIndex(projectDir) {
|
|
42108
|
-
const dir =
|
|
42676
|
+
const dir = resolve104(projectDir, "resources");
|
|
42109
42677
|
await ensureDir(dir);
|
|
42110
42678
|
const files = await listSlugFiles(dir);
|
|
42111
42679
|
const slug = readProjectSlug(projectDir);
|
|
@@ -42121,7 +42689,7 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
42121
42689
|
lines.push("| Name | Category | Source | Related Assignments | Updated |");
|
|
42122
42690
|
lines.push("|------|----------|--------|---------------------|---------|");
|
|
42123
42691
|
for (const fileName of files) {
|
|
42124
|
-
const content = await
|
|
42692
|
+
const content = await readFile75(resolve104(dir, fileName), "utf-8");
|
|
42125
42693
|
const parsed = parseResource(content);
|
|
42126
42694
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
42127
42695
|
const name = parsed.name || slugBase;
|
|
@@ -42131,12 +42699,12 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
42131
42699
|
);
|
|
42132
42700
|
}
|
|
42133
42701
|
lines.push("");
|
|
42134
|
-
const indexPath =
|
|
42702
|
+
const indexPath = resolve104(dir, "_index.md");
|
|
42135
42703
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
42136
42704
|
return { total: files.length, path: indexPath };
|
|
42137
42705
|
}
|
|
42138
42706
|
async function rebuildMemoriesIndex(projectDir) {
|
|
42139
|
-
const dir =
|
|
42707
|
+
const dir = resolve104(projectDir, "memories");
|
|
42140
42708
|
await ensureDir(dir);
|
|
42141
42709
|
const files = await listSlugFiles(dir);
|
|
42142
42710
|
const slug = readProjectSlug(projectDir);
|
|
@@ -42152,7 +42720,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
42152
42720
|
lines.push("| Name | Source | Scope | Source Assignment | Updated |");
|
|
42153
42721
|
lines.push("|------|--------|-------|-------------------|---------|");
|
|
42154
42722
|
for (const fileName of files) {
|
|
42155
|
-
const content = await
|
|
42723
|
+
const content = await readFile75(resolve104(dir, fileName), "utf-8");
|
|
42156
42724
|
const parsed = parseMemory(content);
|
|
42157
42725
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
42158
42726
|
const name = parsed.name || slugBase;
|
|
@@ -42162,7 +42730,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
42162
42730
|
);
|
|
42163
42731
|
}
|
|
42164
42732
|
lines.push("");
|
|
42165
|
-
const indexPath =
|
|
42733
|
+
const indexPath = resolve104(dir, "_index.md");
|
|
42166
42734
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
42167
42735
|
return { total: files.length, path: indexPath };
|
|
42168
42736
|
}
|
|
@@ -42226,8 +42794,8 @@ ${opts.body ?? "<!-- Add notes about this resource here. -->"}
|
|
|
42226
42794
|
}
|
|
42227
42795
|
async function resolveProjectDir(project) {
|
|
42228
42796
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
42229
|
-
const projectDir =
|
|
42230
|
-
if (!await fileExists(
|
|
42797
|
+
const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
|
|
42798
|
+
if (!await fileExists(resolve105(projectDir, "project.md"))) {
|
|
42231
42799
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
42232
42800
|
}
|
|
42233
42801
|
return projectDir;
|
|
@@ -42240,7 +42808,7 @@ async function runResourceAdd(options) {
|
|
|
42240
42808
|
if (!isValidSlug(slug)) {
|
|
42241
42809
|
throw new Error(`Invalid resource slug: "${slug}".`);
|
|
42242
42810
|
}
|
|
42243
|
-
const filePath =
|
|
42811
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42244
42812
|
if (await fileExists(filePath) && !options.force) {
|
|
42245
42813
|
throw new Error(
|
|
42246
42814
|
`Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -42257,7 +42825,7 @@ async function runResourceAdd(options) {
|
|
|
42257
42825
|
return { filePath, indexPath, total };
|
|
42258
42826
|
}
|
|
42259
42827
|
async function listResourceSlugs(projectDir) {
|
|
42260
|
-
const dir =
|
|
42828
|
+
const dir = resolve105(projectDir, "resources");
|
|
42261
42829
|
if (!await fileExists(dir)) return [];
|
|
42262
42830
|
const entries = await readdir37(dir, { withFileTypes: true });
|
|
42263
42831
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
@@ -42267,16 +42835,16 @@ async function runResourceList(project) {
|
|
|
42267
42835
|
const slugs = await listResourceSlugs(projectDir);
|
|
42268
42836
|
const out = [];
|
|
42269
42837
|
for (const slug of slugs) {
|
|
42270
|
-
const parsed = parseResource(await
|
|
42838
|
+
const parsed = parseResource(await readFile76(resolve105(projectDir, "resources", `${slug}.md`), "utf-8"));
|
|
42271
42839
|
out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
|
|
42272
42840
|
}
|
|
42273
42841
|
return out;
|
|
42274
42842
|
}
|
|
42275
42843
|
async function runResourceShow(project, slug) {
|
|
42276
42844
|
const projectDir = await resolveProjectDir(project);
|
|
42277
|
-
const filePath =
|
|
42845
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42278
42846
|
if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
|
|
42279
|
-
const parsed = parseResource(await
|
|
42847
|
+
const parsed = parseResource(await readFile76(filePath, "utf-8"));
|
|
42280
42848
|
return {
|
|
42281
42849
|
slug,
|
|
42282
42850
|
name: parsed.name,
|
|
@@ -42290,14 +42858,14 @@ async function runResourceShow(project, slug) {
|
|
|
42290
42858
|
}
|
|
42291
42859
|
async function runResourceUpdate(slug, options) {
|
|
42292
42860
|
const projectDir = await resolveProjectDir(options.project);
|
|
42293
|
-
const filePath =
|
|
42861
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42294
42862
|
if (!await fileExists(filePath)) {
|
|
42295
42863
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
42296
42864
|
}
|
|
42297
42865
|
if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
|
|
42298
42866
|
throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
|
|
42299
42867
|
}
|
|
42300
|
-
const original = await
|
|
42868
|
+
const original = await readFile76(filePath, "utf-8");
|
|
42301
42869
|
const content = editResourceFrontmatter(original, {
|
|
42302
42870
|
name: options.name,
|
|
42303
42871
|
category: options.category,
|
|
@@ -42310,7 +42878,7 @@ async function runResourceUpdate(slug, options) {
|
|
|
42310
42878
|
}
|
|
42311
42879
|
async function runResourceRemove(slug, options) {
|
|
42312
42880
|
const projectDir = await resolveProjectDir(options.project);
|
|
42313
|
-
const filePath =
|
|
42881
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42314
42882
|
if (!await fileExists(filePath)) {
|
|
42315
42883
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
42316
42884
|
}
|
|
@@ -42393,8 +42961,8 @@ init_config2();
|
|
|
42393
42961
|
init_fs();
|
|
42394
42962
|
init_slug();
|
|
42395
42963
|
import { Command as Command15 } from "commander";
|
|
42396
|
-
import { resolve as
|
|
42397
|
-
import { readFile as
|
|
42964
|
+
import { resolve as resolve106 } from "path";
|
|
42965
|
+
import { readFile as readFile77, readdir as readdir38, rm as rm16 } from "fs/promises";
|
|
42398
42966
|
init_parser();
|
|
42399
42967
|
function nowIso5() {
|
|
42400
42968
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -42458,8 +43026,8 @@ ${opts.body ?? "<!-- Capture the load-bearing context for this memory here. -->"
|
|
|
42458
43026
|
}
|
|
42459
43027
|
async function resolveProjectDir2(project) {
|
|
42460
43028
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
42461
|
-
const projectDir =
|
|
42462
|
-
if (!await fileExists(
|
|
43029
|
+
const projectDir = resolve106((await readConfig()).defaultProjectDir, project);
|
|
43030
|
+
if (!await fileExists(resolve106(projectDir, "project.md"))) {
|
|
42463
43031
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
42464
43032
|
}
|
|
42465
43033
|
return projectDir;
|
|
@@ -42472,7 +43040,7 @@ async function runMemoryAdd(options) {
|
|
|
42472
43040
|
if (!isValidSlug(slug)) {
|
|
42473
43041
|
throw new Error(`Invalid memory slug: "${slug}".`);
|
|
42474
43042
|
}
|
|
42475
|
-
const filePath =
|
|
43043
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42476
43044
|
if (await fileExists(filePath) && !options.force) {
|
|
42477
43045
|
throw new Error(
|
|
42478
43046
|
`Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -42490,7 +43058,7 @@ async function runMemoryAdd(options) {
|
|
|
42490
43058
|
return { filePath, indexPath, total };
|
|
42491
43059
|
}
|
|
42492
43060
|
async function listMemorySlugs(projectDir) {
|
|
42493
|
-
const dir =
|
|
43061
|
+
const dir = resolve106(projectDir, "memories");
|
|
42494
43062
|
if (!await fileExists(dir)) return [];
|
|
42495
43063
|
const entries = await readdir38(dir, { withFileTypes: true });
|
|
42496
43064
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
@@ -42500,16 +43068,16 @@ async function runMemoryList(project) {
|
|
|
42500
43068
|
const slugs = await listMemorySlugs(projectDir);
|
|
42501
43069
|
const out = [];
|
|
42502
43070
|
for (const slug of slugs) {
|
|
42503
|
-
const parsed = parseMemory(await
|
|
43071
|
+
const parsed = parseMemory(await readFile77(resolve106(projectDir, "memories", `${slug}.md`), "utf-8"));
|
|
42504
43072
|
out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
|
|
42505
43073
|
}
|
|
42506
43074
|
return out;
|
|
42507
43075
|
}
|
|
42508
43076
|
async function runMemoryShow(project, slug) {
|
|
42509
43077
|
const projectDir = await resolveProjectDir2(project);
|
|
42510
|
-
const filePath =
|
|
43078
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42511
43079
|
if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
|
|
42512
|
-
const parsed = parseMemory(await
|
|
43080
|
+
const parsed = parseMemory(await readFile77(filePath, "utf-8"));
|
|
42513
43081
|
return {
|
|
42514
43082
|
slug,
|
|
42515
43083
|
name: parsed.name,
|
|
@@ -42524,7 +43092,7 @@ async function runMemoryShow(project, slug) {
|
|
|
42524
43092
|
}
|
|
42525
43093
|
async function runMemoryUpdate(slug, options) {
|
|
42526
43094
|
const projectDir = await resolveProjectDir2(options.project);
|
|
42527
|
-
const filePath =
|
|
43095
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42528
43096
|
if (!await fileExists(filePath)) {
|
|
42529
43097
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
42530
43098
|
}
|
|
@@ -42533,7 +43101,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
42533
43101
|
"Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
|
|
42534
43102
|
);
|
|
42535
43103
|
}
|
|
42536
|
-
const original = await
|
|
43104
|
+
const original = await readFile77(filePath, "utf-8");
|
|
42537
43105
|
const content = editMemoryFrontmatter(original, {
|
|
42538
43106
|
name: options.name,
|
|
42539
43107
|
source: options.source,
|
|
@@ -42547,7 +43115,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
42547
43115
|
}
|
|
42548
43116
|
async function runMemoryRemove(slug, options) {
|
|
42549
43117
|
const projectDir = await resolveProjectDir2(options.project);
|
|
42550
|
-
const filePath =
|
|
43118
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42551
43119
|
if (!await fileExists(filePath)) {
|
|
42552
43120
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
42553
43121
|
}
|
|
@@ -42636,8 +43204,8 @@ init_derive();
|
|
|
42636
43204
|
init_recompute();
|
|
42637
43205
|
init_query();
|
|
42638
43206
|
import { Command as Command16 } from "commander";
|
|
42639
|
-
import { readFile as
|
|
42640
|
-
import { dirname as dirname29, resolve as
|
|
43207
|
+
import { readFile as readFile78 } from "fs/promises";
|
|
43208
|
+
import { dirname as dirname29, resolve as resolve107 } from "path";
|
|
42641
43209
|
var AGE_PATTERN = /^(\d+)([dhwm])$/i;
|
|
42642
43210
|
function parseAgeToCutoff(age) {
|
|
42643
43211
|
const match = age.match(AGE_PATTERN);
|
|
@@ -42656,7 +43224,7 @@ function parseAgeToCutoff(age) {
|
|
|
42656
43224
|
}
|
|
42657
43225
|
function assignmentMdPath(item) {
|
|
42658
43226
|
if (item.projectSlug) {
|
|
42659
|
-
return
|
|
43227
|
+
return resolve107(
|
|
42660
43228
|
defaultProjectDir(),
|
|
42661
43229
|
item.projectSlug,
|
|
42662
43230
|
"assignments",
|
|
@@ -42664,13 +43232,13 @@ function assignmentMdPath(item) {
|
|
|
42664
43232
|
"assignment.md"
|
|
42665
43233
|
);
|
|
42666
43234
|
}
|
|
42667
|
-
return
|
|
43235
|
+
return resolve107(assignmentsDir(), item.id, "assignment.md");
|
|
42668
43236
|
}
|
|
42669
43237
|
async function loadTags(item) {
|
|
42670
43238
|
const path = assignmentMdPath(item);
|
|
42671
43239
|
if (!await fileExists(path)) return [];
|
|
42672
43240
|
try {
|
|
42673
|
-
const content = await
|
|
43241
|
+
const content = await readFile78(path, "utf-8");
|
|
42674
43242
|
return parseAssignmentFrontmatter(content).tags;
|
|
42675
43243
|
} catch {
|
|
42676
43244
|
return [];
|
|
@@ -42734,11 +43302,11 @@ async function loadQueryItem(item, terminalStatuses3, now, declarations) {
|
|
|
42734
43302
|
const path = assignmentMdPath(item);
|
|
42735
43303
|
if (!await fileExists(path)) return null;
|
|
42736
43304
|
try {
|
|
42737
|
-
const content = await
|
|
43305
|
+
const content = await readFile78(path, "utf-8");
|
|
42738
43306
|
const fm = parseAssignmentFrontmatter(content);
|
|
42739
43307
|
const body = content.replace(/^---\n[\s\S]*?\n---/, "");
|
|
42740
43308
|
const assignmentDir = dirname29(path);
|
|
42741
|
-
const projectDir = item.projectSlug ?
|
|
43309
|
+
const projectDir = item.projectSlug ? resolve107(defaultProjectDir(), item.projectSlug) : null;
|
|
42742
43310
|
const facts = await computeFacts({ assignmentDir, frontmatter: fm, body, projectDir, terminalStatuses: terminalStatuses3, declarations });
|
|
42743
43311
|
const history = fm.statusHistory;
|
|
42744
43312
|
const lastHeadlineChange = [...history].reverse().find((e) => e.from !== e.to || e.from === null);
|
|
@@ -43508,8 +44076,8 @@ init_fs();
|
|
|
43508
44076
|
init_timestamp();
|
|
43509
44077
|
init_frontmatter();
|
|
43510
44078
|
import { Command as Command21 } from "commander";
|
|
43511
|
-
import { readFile as
|
|
43512
|
-
import { resolve as
|
|
44079
|
+
import { readFile as readFile79 } from "fs/promises";
|
|
44080
|
+
import { resolve as resolve108 } from "path";
|
|
43513
44081
|
function renameAssignmentStatusRefs(content, id, newId, now) {
|
|
43514
44082
|
const fm = parseAssignmentFrontmatter(content);
|
|
43515
44083
|
const updates = { updated: now };
|
|
@@ -43530,12 +44098,12 @@ function fail3(error) {
|
|
|
43530
44098
|
process.exit(1);
|
|
43531
44099
|
}
|
|
43532
44100
|
function configPath() {
|
|
43533
|
-
return
|
|
44101
|
+
return resolve108(syntaurRoot(), "config.md");
|
|
43534
44102
|
}
|
|
43535
44103
|
async function readStatusBlock() {
|
|
43536
44104
|
const p = configPath();
|
|
43537
44105
|
if (!await fileExists(p)) return null;
|
|
43538
|
-
const content = await
|
|
44106
|
+
const content = await readFile79(p, "utf-8");
|
|
43539
44107
|
return parseStatusConfig(content);
|
|
43540
44108
|
}
|
|
43541
44109
|
async function requireStatusBlock() {
|
|
@@ -43819,7 +44387,7 @@ async function runStatusRename(id, opts) {
|
|
|
43819
44387
|
printBlockDiff(before, after);
|
|
43820
44388
|
const now2 = nowTimestamp();
|
|
43821
44389
|
for (const a of affected) {
|
|
43822
|
-
const original = await
|
|
44390
|
+
const original = await readFile79(a.path, "utf-8");
|
|
43823
44391
|
const rewritten = renameAssignmentStatusRefs(original, id, newId, now2);
|
|
43824
44392
|
console.log(`
|
|
43825
44393
|
--- ${a.display}/assignment.md`);
|
|
@@ -43830,9 +44398,9 @@ async function runStatusRename(id, opts) {
|
|
|
43830
44398
|
}
|
|
43831
44399
|
const cfgPath = configPath();
|
|
43832
44400
|
const buffers = /* @__PURE__ */ new Map();
|
|
43833
|
-
buffers.set(cfgPath, await fileExists(cfgPath) ? await
|
|
44401
|
+
buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile79(cfgPath, "utf-8") : "");
|
|
43834
44402
|
for (const a of affected) {
|
|
43835
|
-
buffers.set(a.path, await
|
|
44403
|
+
buffers.set(a.path, await readFile79(a.path, "utf-8"));
|
|
43836
44404
|
}
|
|
43837
44405
|
const now = nowTimestamp();
|
|
43838
44406
|
try {
|
|
@@ -44034,13 +44602,13 @@ init_config2();
|
|
|
44034
44602
|
init_timestamp();
|
|
44035
44603
|
init_frontmatter();
|
|
44036
44604
|
import { Command as Command22 } from "commander";
|
|
44037
|
-
import { readFile as
|
|
44038
|
-
import { resolve as
|
|
44605
|
+
import { readFile as readFile80 } from "fs/promises";
|
|
44606
|
+
import { resolve as resolve109 } from "path";
|
|
44039
44607
|
async function readContext3(cwd) {
|
|
44040
|
-
const path =
|
|
44608
|
+
const path = resolve109(cwd, ".syntaur", "context.json");
|
|
44041
44609
|
if (!await fileExists(path)) return null;
|
|
44042
44610
|
try {
|
|
44043
|
-
return JSON.parse(await
|
|
44611
|
+
return JSON.parse(await readFile80(path, "utf-8"));
|
|
44044
44612
|
} catch {
|
|
44045
44613
|
return null;
|
|
44046
44614
|
}
|
|
@@ -44049,12 +44617,12 @@ async function resolveAssignmentPath3(opts) {
|
|
|
44049
44617
|
if (opts.assignment) {
|
|
44050
44618
|
if (opts.project) {
|
|
44051
44619
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
44052
|
-
return
|
|
44620
|
+
return resolve109(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
44053
44621
|
}
|
|
44054
|
-
return
|
|
44622
|
+
return resolve109(assignmentsDir(), opts.assignment, "assignment.md");
|
|
44055
44623
|
}
|
|
44056
44624
|
const ctx = await readContext3(opts.cwd);
|
|
44057
|
-
if (ctx?.assignmentDir) return
|
|
44625
|
+
if (ctx?.assignmentDir) return resolve109(ctx.assignmentDir, "assignment.md");
|
|
44058
44626
|
throw new Error(
|
|
44059
44627
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
44060
44628
|
);
|
|
@@ -44091,7 +44659,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
|
|
|
44091
44659
|
${pre.errors.map((e) => ` - ${e}`).join("\n")}`
|
|
44092
44660
|
);
|
|
44093
44661
|
}
|
|
44094
|
-
const original = await
|
|
44662
|
+
const original = await readFile80(path, "utf-8");
|
|
44095
44663
|
let next = updateAssignmentWorkspace(original, partial);
|
|
44096
44664
|
next = updateAssignmentFile(next, { updated: nowTimestamp() });
|
|
44097
44665
|
await writeFileForce(path, next);
|
|
@@ -44128,13 +44696,13 @@ init_config2();
|
|
|
44128
44696
|
init_timestamp();
|
|
44129
44697
|
init_templates();
|
|
44130
44698
|
import { Command as Command23 } from "commander";
|
|
44131
|
-
import { readFile as
|
|
44132
|
-
import { resolve as
|
|
44699
|
+
import { readFile as readFile81 } from "fs/promises";
|
|
44700
|
+
import { resolve as resolve110 } from "path";
|
|
44133
44701
|
async function readContext4(cwd) {
|
|
44134
|
-
const path =
|
|
44702
|
+
const path = resolve110(cwd, ".syntaur", "context.json");
|
|
44135
44703
|
if (!await fileExists(path)) return null;
|
|
44136
44704
|
try {
|
|
44137
|
-
return JSON.parse(await
|
|
44705
|
+
return JSON.parse(await readFile81(path, "utf-8"));
|
|
44138
44706
|
} catch {
|
|
44139
44707
|
return null;
|
|
44140
44708
|
}
|
|
@@ -44144,11 +44712,11 @@ async function resolveAssignmentDir2(opts) {
|
|
|
44144
44712
|
if (opts.project) {
|
|
44145
44713
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
44146
44714
|
return {
|
|
44147
|
-
dir:
|
|
44715
|
+
dir: resolve110(projectsDir2, opts.project, "assignments", opts.assignment),
|
|
44148
44716
|
slug: opts.assignment
|
|
44149
44717
|
};
|
|
44150
44718
|
}
|
|
44151
|
-
return { dir:
|
|
44719
|
+
return { dir: resolve110(assignmentsDir(), opts.assignment), slug: opts.assignment };
|
|
44152
44720
|
}
|
|
44153
44721
|
const ctx = await readContext4(opts.cwd);
|
|
44154
44722
|
if (ctx?.assignmentDir) {
|
|
@@ -44210,12 +44778,12 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
|
|
|
44210
44778
|
project: options.project,
|
|
44211
44779
|
cwd
|
|
44212
44780
|
});
|
|
44213
|
-
if (!await fileExists(
|
|
44781
|
+
if (!await fileExists(resolve110(dir, "assignment.md"))) {
|
|
44214
44782
|
throw new Error(`No assignment found at ${dir} (missing assignment.md).`);
|
|
44215
44783
|
}
|
|
44216
|
-
const path =
|
|
44784
|
+
const path = resolve110(dir, "progress.md");
|
|
44217
44785
|
const now = nowTimestamp();
|
|
44218
|
-
const content = await fileExists(path) ? await
|
|
44786
|
+
const content = await fileExists(path) ? await readFile81(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
|
|
44219
44787
|
const next = appendProgressEntry(content, text, now);
|
|
44220
44788
|
await writeFileForce(path, next);
|
|
44221
44789
|
return path;
|
|
@@ -44420,7 +44988,7 @@ program.command("attest").description("Record an attestation (agent reviewed a r
|
|
|
44420
44988
|
await attestCommand(assignment, fact, options);
|
|
44421
44989
|
})
|
|
44422
44990
|
);
|
|
44423
|
-
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(
|
|
44424
44992
|
runCommand(async (assignment, options) => {
|
|
44425
44993
|
await recomputeCommand(assignment, options);
|
|
44426
44994
|
})
|