syntaur 0.67.0 → 0.69.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/dashboard/dist/assets/{_basePickBy-CE2CIvur.js → _basePickBy-DKk6tHtk.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-BznLQqID.js → _baseUniq-DM-f7DWz.js} +1 -1
- package/dashboard/dist/assets/{arc-B6JqtWve.js → arc-ZBlA3YdV.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-P8UCT3rj.js → architectureDiagram-2XIMDMQ5-BUmvtGTF.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-C1ATZKSf.js → blockDiagram-WCTKOSBZ-B3qxWK6s.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-AvN1yayQ.js → c4Diagram-IC4MRINW-BEq_UJO-.js} +1 -1
- package/dashboard/dist/assets/channel-fypxffzQ.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-CyYz6mlJ.js → chunk-4BX2VUAB-C-Y9ryMm.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-QyOF7ox_.js → chunk-55IACEB6-CGdtbsjw.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-DVTHM99U.js → chunk-FMBD7UC4-DllxJhUp.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-DQfxaQtT.js → chunk-JSJVCQXG-jjMM8O5F.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-4R1oobH6.js → chunk-KX2RTZJC-B_6BPltQ.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-D8H_7yNS.js → chunk-NQ4KR5QH-D0hJiXHp.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-DLxDUSuo.js → chunk-QZHKN3VN-BCWo4hLS.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-CWuFDkVp.js → chunk-WL4C6EOR-DH_jEAwg.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-1KnjQvtL.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-1KnjQvtL.js +1 -0
- package/dashboard/dist/assets/clone-CKMabBhS.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-D23Dy_Za.js → cose-bilkent-S5V4N54A-5ld00TOH.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-CaKBk8eh.js → dagre-KLK3FWXG-Cnu6eQWy.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-BAPQPki-.js → diagram-E7M64L7V-_CBBKNP-.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-Cla6qyvn.js → diagram-IFDJBPK2-DE6WjIb1.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-DnTZq_y6.js → diagram-P4PSJMXO-DpW7UzNK.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-yc1_7ebn.js → erDiagram-INFDFZHY-BD2409fE.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-CMt2tF6O.js → flowDiagram-PKNHOUZH-1p0khhFI.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-CXiK-5Gp.js → ganttDiagram-A5KZAMGK-B2zFyA4s.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-B4PbW06T.js → gitGraphDiagram-K3NZZRJ6-bH-4YH7h.js} +1 -1
- package/dashboard/dist/assets/{graph-CjdFy-q-.js → graph-BT24B6iQ.js} +1 -1
- package/dashboard/dist/assets/{index-DlUgV5eO.css → index-BZwPAi8K.css} +1 -1
- package/dashboard/dist/assets/index-Cxqr3rQB.js +670 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BeOWVt7p.js → infoDiagram-LFFYTUFH-CMzP4Hcg.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-LE7swZOH.js → ishikawaDiagram-PHBUUO56-DMEmFC7M.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DG9-sksf.js → journeyDiagram-4ABVD52K-CAtzYQUm.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-7CKAw6eI.js → kanban-definition-K7BYSVSG-d1JVbtvX.js} +1 -1
- package/dashboard/dist/assets/{layout-CHChKPuz.js → layout-BVuI38I_.js} +1 -1
- package/dashboard/dist/assets/{linear-BWSj4au4.js → linear-Bc8PGMbp.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-CAwQaPqG.js → mermaid.core-UtwFYLNj.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-CtZWMkVN.js → mindmap-definition-YRQLILUH-DHc5RCDj.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CstCoaK7.js → pieDiagram-SKSYHLDU-9anIsdIA.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-DD8zRtaB.js → quadrantDiagram-337W2JSQ-FZ0D9HnU.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-BWpKIK3b.js → requirementDiagram-Z7DCOOCP-BkjCvH_u.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-08gqQtFM.js → sankeyDiagram-WA2Y5GQK-DzqwYHDo.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-DsfIDBUH.js → sequenceDiagram-2WXFIKYE-BW4g5Ao-.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-Dqzfk_yy.js → stateDiagram-RAJIS63D-D0tKU3Z0.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DxWQhjNO.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-DgH_p9jR.js → timeline-definition-YZTLITO2-Bu269QDX.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-7XKqMhq9.js → treemap-KZPCXAKY-BGu_rrva.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-BFtNLBWM.js → vennDiagram-LZ73GAT5-Cx_n5FRZ.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-CQPevMNl.js → xychartDiagram-JWTSCODW-BOJsKV_W.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +459 -63
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +1158 -702
- package/dist/index.js.map +1 -1
- package/dist/launch/index.d.ts +18 -0
- package/dist/launch/index.js +120 -12
- 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/dashboard/dist/assets/channel-IZujZyS6.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-ChnJofe3.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-ChnJofe3.js +0 -1
- package/dashboard/dist/assets/clone-JjIbzsqJ.js +0 -1
- package/dashboard/dist/assets/index-COOcebN7.js +0 -659
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-D542UPiR.js +0 -1
package/dist/index.js
CHANGED
|
@@ -1591,6 +1591,21 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
1591
1591
|
});
|
|
1592
1592
|
|
|
1593
1593
|
// src/lifecycle/event-emit.ts
|
|
1594
|
+
var event_emit_exports = {};
|
|
1595
|
+
__export(event_emit_exports, {
|
|
1596
|
+
emitEvent: () => emitEvent,
|
|
1597
|
+
isSuppressingEvents: () => isSuppressingEvents,
|
|
1598
|
+
recordStatusEvent: () => recordStatusEvent,
|
|
1599
|
+
resolveActor: () => resolveActor,
|
|
1600
|
+
setSuppressEvents: () => setSuppressEvents,
|
|
1601
|
+
withSuppressedEvents: () => withSuppressedEvents
|
|
1602
|
+
});
|
|
1603
|
+
function setSuppressEvents(value) {
|
|
1604
|
+
suppressEvents = value;
|
|
1605
|
+
}
|
|
1606
|
+
function isSuppressingEvents() {
|
|
1607
|
+
return suppressEvents;
|
|
1608
|
+
}
|
|
1594
1609
|
function withSuppressedEvents(fn) {
|
|
1595
1610
|
const prior = suppressEvents;
|
|
1596
1611
|
suppressEvents = true;
|
|
@@ -3885,7 +3900,9 @@ __export(config_exports, {
|
|
|
3885
3900
|
getTerminal: () => getTerminal,
|
|
3886
3901
|
normalizeFactDeclarations: () => normalizeFactDeclarations,
|
|
3887
3902
|
parseAgentCommand: () => parseAgentCommand,
|
|
3903
|
+
parseDurationMs: () => parseDurationMs,
|
|
3888
3904
|
parseSearchConfig: () => parseSearchConfig,
|
|
3905
|
+
parseStalenessConfig: () => parseStalenessConfig,
|
|
3889
3906
|
parseStatusConfig: () => parseStatusConfig,
|
|
3890
3907
|
parseTerminalConfig: () => parseTerminalConfig,
|
|
3891
3908
|
readConfig: () => readConfig,
|
|
@@ -3901,6 +3918,7 @@ __export(config_exports, {
|
|
|
3901
3918
|
validateDeriveConfig: () => validateDeriveConfig,
|
|
3902
3919
|
validateDeriveShape: () => validateDeriveShape,
|
|
3903
3920
|
validateFactDeclarations: () => validateFactDeclarations,
|
|
3921
|
+
validateStalenessConfig: () => validateStalenessConfig,
|
|
3904
3922
|
writeAgentsConfig: () => writeAgentsConfig,
|
|
3905
3923
|
writeHotkeyBindingsConfig: () => writeHotkeyBindingsConfig,
|
|
3906
3924
|
writeSearchConfig: () => writeSearchConfig,
|
|
@@ -3912,6 +3930,13 @@ __export(config_exports, {
|
|
|
3912
3930
|
import { readFile as readFile5 } from "fs/promises";
|
|
3913
3931
|
import { spawnSync } from "child_process";
|
|
3914
3932
|
import { resolve as resolve7, isAbsolute } from "path";
|
|
3933
|
+
function parseDurationMs(raw2) {
|
|
3934
|
+
const m = raw2.trim().match(DURATION_RE);
|
|
3935
|
+
if (!m) return null;
|
|
3936
|
+
const n = Number(m[1]);
|
|
3937
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
3938
|
+
return n * DURATION_UNIT_MS[m[2] ?? "ms"];
|
|
3939
|
+
}
|
|
3915
3940
|
function parseAgentCommand(value, agentId) {
|
|
3916
3941
|
if (typeof value !== "string" || value.trim() === "") {
|
|
3917
3942
|
throw new AgentConfigError(
|
|
@@ -5148,6 +5173,65 @@ ${cleanedFm}
|
|
|
5148
5173
|
---${afterFrontmatter}`;
|
|
5149
5174
|
await writeFileForce(configPath2, newContent);
|
|
5150
5175
|
}
|
|
5176
|
+
function parseStalenessConfig(content) {
|
|
5177
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
5178
|
+
if (!match) return null;
|
|
5179
|
+
const fmBlock = match[1];
|
|
5180
|
+
const blockStart = fmBlock.match(/^staleness:\s*$/m);
|
|
5181
|
+
if (!blockStart) return null;
|
|
5182
|
+
const startIdx = (blockStart.index ?? 0) + blockStart[0].length;
|
|
5183
|
+
const lines = fmBlock.slice(startIdx).split("\n");
|
|
5184
|
+
const out = {};
|
|
5185
|
+
for (const line of lines) {
|
|
5186
|
+
if (line.trim() === "") continue;
|
|
5187
|
+
const trimmed = line.trimStart();
|
|
5188
|
+
const indent = line.length - trimmed.length;
|
|
5189
|
+
if (indent === 0) break;
|
|
5190
|
+
const ci = trimmed.indexOf(":");
|
|
5191
|
+
if (ci <= 0) continue;
|
|
5192
|
+
const key = trimmed.slice(0, ci).trim();
|
|
5193
|
+
const field = STALENESS_KEY_TO_FIELD[key];
|
|
5194
|
+
if (!field) continue;
|
|
5195
|
+
let value = trimmed.slice(ci + 1).trim();
|
|
5196
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
5197
|
+
value = value.slice(1, -1);
|
|
5198
|
+
}
|
|
5199
|
+
const ms = parseDurationMs(value);
|
|
5200
|
+
if (ms !== null) out[field] = ms;
|
|
5201
|
+
}
|
|
5202
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
5203
|
+
}
|
|
5204
|
+
function validateStalenessConfig(content) {
|
|
5205
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
5206
|
+
if (!match) return [];
|
|
5207
|
+
const fmBlock = match[1];
|
|
5208
|
+
const blockStart = fmBlock.match(/^staleness:\s*$/m);
|
|
5209
|
+
if (!blockStart) return [];
|
|
5210
|
+
const startIdx = (blockStart.index ?? 0) + blockStart[0].length;
|
|
5211
|
+
const lines = fmBlock.slice(startIdx).split("\n");
|
|
5212
|
+
const problems = [];
|
|
5213
|
+
for (const line of lines) {
|
|
5214
|
+
if (line.trim() === "") continue;
|
|
5215
|
+
const trimmed = line.trimStart();
|
|
5216
|
+
const indent = line.length - trimmed.length;
|
|
5217
|
+
if (indent === 0) break;
|
|
5218
|
+
const ci = trimmed.indexOf(":");
|
|
5219
|
+
if (ci <= 0) continue;
|
|
5220
|
+
const key = trimmed.slice(0, ci).trim();
|
|
5221
|
+
let value = trimmed.slice(ci + 1).trim();
|
|
5222
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
5223
|
+
value = value.slice(1, -1);
|
|
5224
|
+
}
|
|
5225
|
+
if (!(key in STALENESS_KEY_TO_FIELD)) {
|
|
5226
|
+
problems.push(`staleness.${key}: unknown key (expected one of ${Object.keys(STALENESS_KEY_TO_FIELD).join(", ")})`);
|
|
5227
|
+
continue;
|
|
5228
|
+
}
|
|
5229
|
+
if (parseDurationMs(value) === null) {
|
|
5230
|
+
problems.push(`staleness.${key}: "${value}" is not a positive duration (e.g. 7d, 12h, 30m, 90s, 500ms)`);
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
return problems;
|
|
5234
|
+
}
|
|
5151
5235
|
function parseSearchConfig(content) {
|
|
5152
5236
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
5153
5237
|
if (!match) return null;
|
|
@@ -5430,7 +5514,9 @@ async function readConfig() {
|
|
|
5430
5514
|
}
|
|
5431
5515
|
})(),
|
|
5432
5516
|
searchConfig: parseSearchConfig(content),
|
|
5433
|
-
workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock)
|
|
5517
|
+
workspaceVisibility: parseWorkspaceVisibilityConfig(fmBlock),
|
|
5518
|
+
staleness: parseStalenessConfig(content),
|
|
5519
|
+
stalenessWatchdog: String(fm["stalenessWatchdog"]).toLowerCase() === "true"
|
|
5434
5520
|
};
|
|
5435
5521
|
}
|
|
5436
5522
|
function getAssignmentTypes(config) {
|
|
@@ -5493,7 +5579,7 @@ async function updateAgentsConfig(mutation, options = {}) {
|
|
|
5493
5579
|
await writeAgentsConfig(next);
|
|
5494
5580
|
return { previous, next, written: true };
|
|
5495
5581
|
}
|
|
5496
|
-
var DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
5582
|
+
var STALENESS_KEY_TO_FIELD, DURATION_RE, DURATION_UNIT_MS, DEFAULT_ASSIGNMENT_TYPES, DEFAULT_CONFIG, AUTO_CREATE_WORKTREE_VALUES, SESSION_AUTO_TRACK_VALUES, AgentConfigError, DEFAULT_STATUS_COLORS, KNOWN_AGENT_SCALAR_FIELDS, migratedConfigPaths, TerminalConfigError;
|
|
5497
5583
|
var init_config2 = __esm({
|
|
5498
5584
|
"src/utils/config.ts"() {
|
|
5499
5585
|
"use strict";
|
|
@@ -5510,6 +5596,21 @@ var init_config2 = __esm({
|
|
|
5510
5596
|
init_terminal_schema();
|
|
5511
5597
|
init_search_schema();
|
|
5512
5598
|
init_workspace_visibility_schema();
|
|
5599
|
+
STALENESS_KEY_TO_FIELD = {
|
|
5600
|
+
inProgressNoActivity: "inProgressNoActivityMs",
|
|
5601
|
+
readyUnclaimed: "readyUnclaimedMs",
|
|
5602
|
+
reviewAging: "reviewAgingMs",
|
|
5603
|
+
blockedAging: "blockedAgingMs",
|
|
5604
|
+
planApprovalAging: "planApprovalAgingMs"
|
|
5605
|
+
};
|
|
5606
|
+
DURATION_RE = /^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/;
|
|
5607
|
+
DURATION_UNIT_MS = {
|
|
5608
|
+
ms: 1,
|
|
5609
|
+
s: 1e3,
|
|
5610
|
+
m: 6e4,
|
|
5611
|
+
h: 36e5,
|
|
5612
|
+
d: 864e5
|
|
5613
|
+
};
|
|
5513
5614
|
DEFAULT_ASSIGNMENT_TYPES = {
|
|
5514
5615
|
definitions: [
|
|
5515
5616
|
{ id: "feature", label: "Feature" },
|
|
@@ -5552,7 +5653,9 @@ var init_config2 = __esm({
|
|
|
5552
5653
|
searchConfig: null,
|
|
5553
5654
|
workspaceVisibility: {
|
|
5554
5655
|
hidden: []
|
|
5555
|
-
}
|
|
5656
|
+
},
|
|
5657
|
+
staleness: null,
|
|
5658
|
+
stalenessWatchdog: false
|
|
5556
5659
|
};
|
|
5557
5660
|
AUTO_CREATE_WORKTREE_VALUES = ["skip", "ask", "always"];
|
|
5558
5661
|
SESSION_AUTO_TRACK_VALUES = ["all", "workspaces-only", "off"];
|
|
@@ -8907,8 +9010,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
8907
9010
|
return allSessions.length;
|
|
8908
9011
|
}
|
|
8909
9012
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
8910
|
-
const { readFile:
|
|
8911
|
-
const raw2 = await
|
|
9013
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
9014
|
+
const raw2 = await readFile82(filePath, "utf-8");
|
|
8912
9015
|
const sessions = [];
|
|
8913
9016
|
const lines = raw2.split("\n");
|
|
8914
9017
|
let inTable = false;
|
|
@@ -9177,6 +9280,74 @@ var init_overviewCopy = __esm({
|
|
|
9177
9280
|
}
|
|
9178
9281
|
});
|
|
9179
9282
|
|
|
9283
|
+
// src/staleness/classify.ts
|
|
9284
|
+
function resolveStaleThresholds(overrides) {
|
|
9285
|
+
const merged = { ...DEFAULT_STALE_THRESHOLDS };
|
|
9286
|
+
if (overrides) {
|
|
9287
|
+
for (const key of Object.keys(merged)) {
|
|
9288
|
+
const v = overrides[key];
|
|
9289
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) merged[key] = v;
|
|
9290
|
+
}
|
|
9291
|
+
}
|
|
9292
|
+
return merged;
|
|
9293
|
+
}
|
|
9294
|
+
function classifyNeedsAttention(input4, thresholds = DEFAULT_STALE_THRESHOLDS) {
|
|
9295
|
+
if (input4.isTerminal) return [];
|
|
9296
|
+
const reasons = [];
|
|
9297
|
+
const age = input4.statusAgeMs;
|
|
9298
|
+
const aged = (gate) => age !== null && age >= gate;
|
|
9299
|
+
const blocked = input4.disposition === "blocked" || input4.blockedReason !== null;
|
|
9300
|
+
if (input4.phase === IN_PROGRESS_PHASE && !blocked && aged(thresholds.inProgressNoActivityMs) && input4.lastActivityMs !== null && input4.lastActivityMs >= thresholds.inProgressNoActivityMs) {
|
|
9301
|
+
reasons.push({
|
|
9302
|
+
kind: "in_progress_no_activity",
|
|
9303
|
+
label: "In progress, but no recent activity",
|
|
9304
|
+
severity: "medium"
|
|
9305
|
+
});
|
|
9306
|
+
}
|
|
9307
|
+
if (input4.phase === READY_PHASE && input4.assignee === null && aged(thresholds.readyUnclaimedMs)) {
|
|
9308
|
+
reasons.push({
|
|
9309
|
+
kind: "ready_unclaimed",
|
|
9310
|
+
label: "Ready to implement, unclaimed",
|
|
9311
|
+
severity: "medium"
|
|
9312
|
+
});
|
|
9313
|
+
}
|
|
9314
|
+
if (input4.phase === REVIEW_PHASE && aged(thresholds.reviewAgingMs)) {
|
|
9315
|
+
reasons.push({ kind: "review_aging", label: "Awaiting review", severity: "high" });
|
|
9316
|
+
}
|
|
9317
|
+
if (blocked && aged(thresholds.blockedAgingMs)) {
|
|
9318
|
+
reasons.push({ kind: "blocked_aging", label: "Blocked and aging", severity: "high" });
|
|
9319
|
+
}
|
|
9320
|
+
if (input4.phase === PLANNING_PHASE && input4.planExists && !input4.planApproved && aged(thresholds.planApprovalAgingMs)) {
|
|
9321
|
+
reasons.push({
|
|
9322
|
+
kind: "plan_awaiting_approval",
|
|
9323
|
+
label: "Plan awaiting approval",
|
|
9324
|
+
severity: "medium"
|
|
9325
|
+
});
|
|
9326
|
+
}
|
|
9327
|
+
if (input4.depsSatisfied === false && (input4.phase === READY_PHASE || input4.phase === IN_PROGRESS_PHASE)) {
|
|
9328
|
+
reasons.push({ kind: "deps_unsatisfied", label: "Unmet dependencies", severity: "high" });
|
|
9329
|
+
}
|
|
9330
|
+
return reasons;
|
|
9331
|
+
}
|
|
9332
|
+
var DAY, DEFAULT_STALE_THRESHOLDS, PLANNING_PHASE, READY_PHASE, IN_PROGRESS_PHASE, REVIEW_PHASE;
|
|
9333
|
+
var init_classify = __esm({
|
|
9334
|
+
"src/staleness/classify.ts"() {
|
|
9335
|
+
"use strict";
|
|
9336
|
+
DAY = 24 * 60 * 60 * 1e3;
|
|
9337
|
+
DEFAULT_STALE_THRESHOLDS = {
|
|
9338
|
+
inProgressNoActivityMs: 7 * DAY,
|
|
9339
|
+
readyUnclaimedMs: 3 * DAY,
|
|
9340
|
+
reviewAgingMs: 3 * DAY,
|
|
9341
|
+
blockedAgingMs: 3 * DAY,
|
|
9342
|
+
planApprovalAgingMs: 3 * DAY
|
|
9343
|
+
};
|
|
9344
|
+
PLANNING_PHASE = "ready_for_planning";
|
|
9345
|
+
READY_PHASE = "ready_to_implement";
|
|
9346
|
+
IN_PROGRESS_PHASE = "in_progress";
|
|
9347
|
+
REVIEW_PHASE = "review";
|
|
9348
|
+
}
|
|
9349
|
+
});
|
|
9350
|
+
|
|
9180
9351
|
// src/dashboard/servers.ts
|
|
9181
9352
|
import { readdir as readdir10, readFile as readFile13, unlink as unlink2 } from "fs/promises";
|
|
9182
9353
|
import { resolve as resolve18 } from "path";
|
|
@@ -9367,8 +9538,8 @@ function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
|
|
|
9367
9538
|
return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
|
|
9368
9539
|
}
|
|
9369
9540
|
function delay(ms) {
|
|
9370
|
-
return new Promise((
|
|
9371
|
-
const timer3 = setTimeout(
|
|
9541
|
+
return new Promise((resolve111) => {
|
|
9542
|
+
const timer3 = setTimeout(resolve111, ms);
|
|
9372
9543
|
if (typeof timer3.unref === "function") {
|
|
9373
9544
|
timer3.unref();
|
|
9374
9545
|
}
|
|
@@ -9811,7 +9982,38 @@ var init_scanner = __esm({
|
|
|
9811
9982
|
});
|
|
9812
9983
|
|
|
9813
9984
|
// src/dashboard/api.ts
|
|
9814
|
-
|
|
9985
|
+
var api_exports = {};
|
|
9986
|
+
__export(api_exports, {
|
|
9987
|
+
WorkspaceBlockedError: () => WorkspaceBlockedError,
|
|
9988
|
+
clearStatusConfigCache: () => clearStatusConfigCache,
|
|
9989
|
+
collectStaleCandidates: () => collectStaleCandidates,
|
|
9990
|
+
createWorkspace: () => createWorkspace,
|
|
9991
|
+
deleteWorkspace: () => deleteWorkspace,
|
|
9992
|
+
getAssignmentDetail: () => getAssignmentDetail,
|
|
9993
|
+
getAssignmentDetailById: () => getAssignmentDetailById,
|
|
9994
|
+
getEditableDocument: () => getEditableDocument,
|
|
9995
|
+
getEditableDocumentById: () => getEditableDocumentById,
|
|
9996
|
+
getHelp: () => getHelp,
|
|
9997
|
+
getMemoryDetail: () => getMemoryDetail,
|
|
9998
|
+
getOverview: () => getOverview,
|
|
9999
|
+
getPlaybookDetail: () => getPlaybookDetail,
|
|
10000
|
+
getProjectDetail: () => getProjectDetail,
|
|
10001
|
+
getResourceDetail: () => getResourceDetail,
|
|
10002
|
+
getStatusConfig: () => getStatusConfig,
|
|
10003
|
+
installRecordsInvalidation: () => installRecordsInvalidation,
|
|
10004
|
+
invalidateRecordsCache: () => invalidateRecordsCache,
|
|
10005
|
+
listAllMemories: () => listAllMemories,
|
|
10006
|
+
listAllResources: () => listAllResources,
|
|
10007
|
+
listArchived: () => listArchived,
|
|
10008
|
+
listAssignmentsBoard: () => listAssignmentsBoard,
|
|
10009
|
+
listPlaybooks: () => listPlaybooks,
|
|
10010
|
+
listProjects: () => listProjects,
|
|
10011
|
+
listWorkspaceRecords: () => listWorkspaceRecords,
|
|
10012
|
+
listWorkspaces: () => listWorkspaces,
|
|
10013
|
+
resolveProjectPath: () => resolveProjectPath,
|
|
10014
|
+
resolveWorkspaceMembers: () => resolveWorkspaceMembers
|
|
10015
|
+
});
|
|
10016
|
+
import { readdir as readdir11, readFile as readFile14, writeFile as writeFile3, stat as stat2 } from "fs/promises";
|
|
9815
10017
|
import { resolve as resolve20, dirname as dirname3, basename } from "path";
|
|
9816
10018
|
function clearFrontmatterField(content, key) {
|
|
9817
10019
|
const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
|
|
@@ -10202,10 +10404,10 @@ async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options =
|
|
|
10202
10404
|
(total, record) => total + (record.summary.progress["failed"] ?? 0),
|
|
10203
10405
|
0
|
|
10204
10406
|
),
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10407
|
+
// Derived from the SAME classifier verdict as the stale segment (via the
|
|
10408
|
+
// pre-cap segment total) so the badge count can never diverge from the
|
|
10409
|
+
// listed rows.
|
|
10410
|
+
staleAssignments: segments.stale.total
|
|
10209
10411
|
},
|
|
10210
10412
|
hero,
|
|
10211
10413
|
segments,
|
|
@@ -11357,9 +11559,77 @@ function segmentSeverity(segment) {
|
|
|
11357
11559
|
return "medium";
|
|
11358
11560
|
}
|
|
11359
11561
|
}
|
|
11562
|
+
function topStaleReason(reasons) {
|
|
11563
|
+
if (reasons.length === 0) return null;
|
|
11564
|
+
return reasons.slice().sort((a, b) => STALE_SEVERITY_RANK[b.severity] - STALE_SEVERITY_RANK[a.severity])[0];
|
|
11565
|
+
}
|
|
11566
|
+
async function readProgressActivityMs(progressPath, now) {
|
|
11567
|
+
try {
|
|
11568
|
+
const s = await stat2(progressPath);
|
|
11569
|
+
return Math.max(0, now - s.mtimeMs);
|
|
11570
|
+
} catch {
|
|
11571
|
+
return null;
|
|
11572
|
+
}
|
|
11573
|
+
}
|
|
11574
|
+
function classifyAssignmentRecord(assignment, terminalStatuses3, depsSatisfied, lastActivityMs, thresholds) {
|
|
11575
|
+
const virtuals = deriveStatusVirtuals(assignment, terminalStatuses3);
|
|
11576
|
+
return classifyNeedsAttention(
|
|
11577
|
+
{
|
|
11578
|
+
phase: virtuals.phase,
|
|
11579
|
+
disposition: virtuals.disposition,
|
|
11580
|
+
isTerminal: terminalStatuses3.has(assignment.status),
|
|
11581
|
+
assignee: assignment.assignee ?? null,
|
|
11582
|
+
blockedReason: assignment.blockedReason,
|
|
11583
|
+
depsSatisfied,
|
|
11584
|
+
// plan_awaiting_approval is deferred to the decision inbox's plan-approval
|
|
11585
|
+
// category for now; pass values that keep that reason dormant.
|
|
11586
|
+
planExists: false,
|
|
11587
|
+
planApproved: true,
|
|
11588
|
+
statusAgeMs: virtuals.statusAge,
|
|
11589
|
+
lastActivityMs
|
|
11590
|
+
},
|
|
11591
|
+
thresholds
|
|
11592
|
+
);
|
|
11593
|
+
}
|
|
11594
|
+
async function collectStaleCandidates(projectsDir2, assignmentsDir2) {
|
|
11595
|
+
const [projectRecords, standaloneRecords] = await Promise.all([
|
|
11596
|
+
listProjectRecords(projectsDir2),
|
|
11597
|
+
listStandaloneRecords(assignmentsDir2)
|
|
11598
|
+
]);
|
|
11599
|
+
const { terminalStatuses: terminalStatuses3 } = await getStatusConfig();
|
|
11600
|
+
const thresholds = resolveStaleThresholds((await readConfig()).staleness);
|
|
11601
|
+
const now = Date.now();
|
|
11602
|
+
const out = [];
|
|
11603
|
+
for (const record of projectRecords) {
|
|
11604
|
+
if (isProjectArchived(record.summary)) continue;
|
|
11605
|
+
const projectPath = resolve20(projectsDir2, record.summary.slug);
|
|
11606
|
+
const depMap = /* @__PURE__ */ new Map();
|
|
11607
|
+
for (const a of record.assignments) depMap.set(a.slug, a.status);
|
|
11608
|
+
for (const assignment of activeAssignments(record.assignments)) {
|
|
11609
|
+
const depsSatisfied = assignment.dependsOn.length === 0 ? true : (await getUnmetDependencies(projectPath, assignment.dependsOn, terminalStatuses3, depMap)).length === 0;
|
|
11610
|
+
const lastActivityMs = await readProgressActivityMs(
|
|
11611
|
+
resolve20(projectPath, "assignments", assignment.slug, "progress.md"),
|
|
11612
|
+
now
|
|
11613
|
+
);
|
|
11614
|
+
const reasons = classifyAssignmentRecord(assignment, terminalStatuses3, depsSatisfied, lastActivityMs, thresholds);
|
|
11615
|
+
if (reasons.length > 0) {
|
|
11616
|
+
out.push({ assignmentId: assignment.id, projectSlug: record.summary.slug, reasons });
|
|
11617
|
+
}
|
|
11618
|
+
}
|
|
11619
|
+
}
|
|
11620
|
+
for (const sr of standaloneRecords) {
|
|
11621
|
+
if (sr.record.archived === true) continue;
|
|
11622
|
+
const lastActivityMs = await readProgressActivityMs(resolve20(sr.assignmentDir, "progress.md"), now);
|
|
11623
|
+
const reasons = classifyAssignmentRecord(sr.record, terminalStatuses3, true, lastActivityMs, thresholds);
|
|
11624
|
+
if (reasons.length > 0) out.push({ assignmentId: sr.record.id, projectSlug: null, reasons });
|
|
11625
|
+
}
|
|
11626
|
+
return out;
|
|
11627
|
+
}
|
|
11360
11628
|
async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standaloneRecords, traces) {
|
|
11361
11629
|
const now = Date.now();
|
|
11362
11630
|
const buckets = emptyBuckets();
|
|
11631
|
+
const { terminalStatuses: terminalStatuses3 } = await getStatusConfig();
|
|
11632
|
+
const staleThresholds = resolveStaleThresholds((await readConfig()).staleness);
|
|
11363
11633
|
const newestPool = [];
|
|
11364
11634
|
for (const record of projectRecords) {
|
|
11365
11635
|
if (isProjectArchived(record.summary)) continue;
|
|
@@ -11368,6 +11638,7 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11368
11638
|
depMap.set(a.slug, a.status);
|
|
11369
11639
|
}
|
|
11370
11640
|
const visibleAssignments = activeAssignments(record.assignments);
|
|
11641
|
+
const projectPath = resolve20(projectsDir2, record.summary.slug);
|
|
11371
11642
|
const resolvedTransitions = await Promise.all(
|
|
11372
11643
|
visibleAssignments.map(async (assignment) => {
|
|
11373
11644
|
const t0 = traces ? performance.now() : 0;
|
|
@@ -11379,13 +11650,25 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11379
11650
|
{ traces, dependencyStatusMap: depMap }
|
|
11380
11651
|
);
|
|
11381
11652
|
if (traces) accumulatePhase(traces, "get-available-transitions", performance.now() - t0);
|
|
11382
|
-
|
|
11653
|
+
const depsSatisfied = assignment.dependsOn.length === 0 ? true : (await getUnmetDependencies(projectPath, assignment.dependsOn, terminalStatuses3, depMap)).length === 0;
|
|
11654
|
+
const lastActivityMs = await readProgressActivityMs(
|
|
11655
|
+
resolve20(projectPath, "assignments", assignment.slug, "progress.md"),
|
|
11656
|
+
now
|
|
11657
|
+
);
|
|
11658
|
+
return { assignment, availableTransitions, depsSatisfied, lastActivityMs };
|
|
11383
11659
|
})
|
|
11384
11660
|
);
|
|
11385
|
-
for (const { assignment, availableTransitions } of resolvedTransitions) {
|
|
11661
|
+
for (const { assignment, availableTransitions, depsSatisfied, lastActivityMs } of resolvedTransitions) {
|
|
11386
11662
|
const segmentId = STATUS_TO_SEGMENT[assignment.status];
|
|
11387
|
-
const
|
|
11388
|
-
const
|
|
11663
|
+
const isTerminal = terminalStatuses3.has(assignment.status);
|
|
11664
|
+
const staleReasons = classifyAssignmentRecord(
|
|
11665
|
+
assignment,
|
|
11666
|
+
terminalStatuses3,
|
|
11667
|
+
depsSatisfied,
|
|
11668
|
+
lastActivityMs,
|
|
11669
|
+
staleThresholds
|
|
11670
|
+
);
|
|
11671
|
+
const stale = staleReasons.length > 0;
|
|
11389
11672
|
const agingMs = Math.max(0, now - parseTimestamp(assignment.updated));
|
|
11390
11673
|
const baseId = `${record.summary.slug}:${assignment.slug}`;
|
|
11391
11674
|
const shared = {
|
|
@@ -11414,11 +11697,12 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11414
11697
|
buckets[segmentId].push(primary);
|
|
11415
11698
|
}
|
|
11416
11699
|
if (stale && !isTerminal) {
|
|
11700
|
+
const top = topStaleReason(staleReasons);
|
|
11417
11701
|
const staleItem = {
|
|
11418
11702
|
...shared,
|
|
11419
11703
|
id: `${baseId}:stale`,
|
|
11420
11704
|
severity: "low",
|
|
11421
|
-
reason: SEGMENT_REASON.stale,
|
|
11705
|
+
reason: top?.label ?? SEGMENT_REASON.stale,
|
|
11422
11706
|
segment: "stale"
|
|
11423
11707
|
};
|
|
11424
11708
|
buckets.stale.push(staleItem);
|
|
@@ -11442,14 +11726,22 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11442
11726
|
const t0 = traces ? performance.now() : 0;
|
|
11443
11727
|
const availableTransitions = await getStandaloneAvailableTransitions(sr.record);
|
|
11444
11728
|
if (traces) accumulatePhase(traces, "get-available-transitions", performance.now() - t0);
|
|
11445
|
-
|
|
11729
|
+
const lastActivityMs = await readProgressActivityMs(resolve20(sr.assignmentDir, "progress.md"), now);
|
|
11730
|
+
return { sr, availableTransitions, lastActivityMs };
|
|
11446
11731
|
})
|
|
11447
11732
|
);
|
|
11448
|
-
for (const { sr, availableTransitions } of resolvedStandaloneTransitions) {
|
|
11733
|
+
for (const { sr, availableTransitions, lastActivityMs } of resolvedStandaloneTransitions) {
|
|
11449
11734
|
const assignment = sr.record;
|
|
11450
11735
|
const segmentId = STATUS_TO_SEGMENT[assignment.status];
|
|
11451
|
-
const
|
|
11452
|
-
const
|
|
11736
|
+
const isTerminal = terminalStatuses3.has(assignment.status);
|
|
11737
|
+
const staleReasons = classifyAssignmentRecord(
|
|
11738
|
+
assignment,
|
|
11739
|
+
terminalStatuses3,
|
|
11740
|
+
true,
|
|
11741
|
+
lastActivityMs,
|
|
11742
|
+
staleThresholds
|
|
11743
|
+
);
|
|
11744
|
+
const stale = staleReasons.length > 0;
|
|
11453
11745
|
const agingMs = Math.max(0, now - parseTimestamp(assignment.updated));
|
|
11454
11746
|
const baseId = `standalone:${sr.id}`;
|
|
11455
11747
|
const shared = {
|
|
@@ -11477,11 +11769,12 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
11477
11769
|
});
|
|
11478
11770
|
}
|
|
11479
11771
|
if (stale && !isTerminal) {
|
|
11772
|
+
const top = topStaleReason(staleReasons);
|
|
11480
11773
|
buckets.stale.push({
|
|
11481
11774
|
...shared,
|
|
11482
11775
|
id: `${baseId}:stale`,
|
|
11483
11776
|
severity: "low",
|
|
11484
|
-
reason: SEGMENT_REASON.stale,
|
|
11777
|
+
reason: top?.label ?? SEGMENT_REASON.stale,
|
|
11485
11778
|
segment: "stale"
|
|
11486
11779
|
});
|
|
11487
11780
|
}
|
|
@@ -11602,13 +11895,6 @@ function parseTimestamp(timestamp) {
|
|
|
11602
11895
|
const parsed = Date.parse(timestamp);
|
|
11603
11896
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
11604
11897
|
}
|
|
11605
|
-
function isStale(updated) {
|
|
11606
|
-
const timestamp = parseTimestamp(updated);
|
|
11607
|
-
if (timestamp === 0) {
|
|
11608
|
-
return false;
|
|
11609
|
-
}
|
|
11610
|
-
return Date.now() - timestamp > STALE_ASSIGNMENT_MS;
|
|
11611
|
-
}
|
|
11612
11898
|
async function countOpenQuestions(projectPath, assignmentSlug) {
|
|
11613
11899
|
const commentsPath = resolve20(
|
|
11614
11900
|
projectPath,
|
|
@@ -11727,7 +12013,7 @@ async function getPlaybookDetail(playbooksDir3, slug) {
|
|
|
11727
12013
|
enabled
|
|
11728
12014
|
};
|
|
11729
12015
|
}
|
|
11730
|
-
var WorkspaceBlockedError,
|
|
12016
|
+
var WorkspaceBlockedError, RECENT_PROJECTS_LIMIT, RECENT_ACTIVITY_LIMIT, RECENT_SESSIONS_LIMIT, NEWEST_CREATED_LIMIT, SEGMENT_DISPLAY_CAP, STALE_LIMIT_DEFAULT, STALE_LIMIT_MAX, STATUS_TO_SEGMENT, HERO_PRIORITY, projectRecordsCache, standaloneRecordsCache, DEFAULT_TRANSITION_DEFINITIONS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS, STALE_SEVERITY_RANK;
|
|
11731
12017
|
var init_api = __esm({
|
|
11732
12018
|
"src/dashboard/api.ts"() {
|
|
11733
12019
|
"use strict";
|
|
@@ -11745,6 +12031,7 @@ var init_api = __esm({
|
|
|
11745
12031
|
init_help();
|
|
11746
12032
|
init_agent_sessions();
|
|
11747
12033
|
init_overviewCopy();
|
|
12034
|
+
init_classify();
|
|
11748
12035
|
WorkspaceBlockedError = class extends Error {
|
|
11749
12036
|
blockedBy;
|
|
11750
12037
|
constructor(blockedBy) {
|
|
@@ -11755,7 +12042,6 @@ var init_api = __esm({
|
|
|
11755
12042
|
this.blockedBy = blockedBy;
|
|
11756
12043
|
}
|
|
11757
12044
|
};
|
|
11758
|
-
STALE_ASSIGNMENT_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
11759
12045
|
RECENT_PROJECTS_LIMIT = 6;
|
|
11760
12046
|
RECENT_ACTIVITY_LIMIT = 12;
|
|
11761
12047
|
RECENT_SESSIONS_LIMIT = 10;
|
|
@@ -11763,7 +12049,6 @@ var init_api = __esm({
|
|
|
11763
12049
|
SEGMENT_DISPLAY_CAP = 5;
|
|
11764
12050
|
STALE_LIMIT_DEFAULT = 50;
|
|
11765
12051
|
STALE_LIMIT_MAX = 200;
|
|
11766
|
-
TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["completed", "failed", "archived"]);
|
|
11767
12052
|
STATUS_TO_SEGMENT = {
|
|
11768
12053
|
review: "readyForReview",
|
|
11769
12054
|
ready_to_implement: "readyToImplement",
|
|
@@ -11856,6 +12141,7 @@ var init_api = __esm({
|
|
|
11856
12141
|
failed: "fill:#9f2d2d,stroke:#651616,color:#ffffff",
|
|
11857
12142
|
review: "fill:#c6911e,stroke:#7a5a10,color:#ffffff"
|
|
11858
12143
|
};
|
|
12144
|
+
STALE_SEVERITY_RANK = { high: 3, medium: 2, low: 1 };
|
|
11859
12145
|
}
|
|
11860
12146
|
});
|
|
11861
12147
|
|
|
@@ -11866,12 +12152,13 @@ __export(recompute_exports, {
|
|
|
11866
12152
|
markDeriveMigrated: () => markDeriveMigrated,
|
|
11867
12153
|
recomputeAll: () => recomputeAll,
|
|
11868
12154
|
recomputeAndWrite: () => recomputeAndWrite,
|
|
12155
|
+
recomputeAssignmentDir: () => recomputeAssignmentDir,
|
|
11869
12156
|
recomputeDependents: () => recomputeDependents,
|
|
11870
12157
|
resolveDeriveContext: () => resolveDeriveContext
|
|
11871
12158
|
});
|
|
11872
12159
|
import { createHash as createHash2 } from "crypto";
|
|
11873
|
-
import { open, readdir as readdir13, readFile as readFile18, unlink as unlink5, stat as
|
|
11874
|
-
import { dirname as dirname5, resolve as resolve25 } from "path";
|
|
12160
|
+
import { open, readdir as readdir13, readFile as readFile18, unlink as unlink5, stat as stat3 } from "fs/promises";
|
|
12161
|
+
import { basename as basename3, dirname as dirname5, resolve as resolve25 } from "path";
|
|
11875
12162
|
async function isDeriveMigrated() {
|
|
11876
12163
|
return fileExists(resolve25(syntaurRoot(), MIGRATION_MARKER));
|
|
11877
12164
|
}
|
|
@@ -11911,7 +12198,7 @@ async function acquireLock(assignmentDir) {
|
|
|
11911
12198
|
const code = err2.code;
|
|
11912
12199
|
if (code !== "EEXIST") throw err2;
|
|
11913
12200
|
try {
|
|
11914
|
-
const info = await
|
|
12201
|
+
const info = await stat3(lockPath);
|
|
11915
12202
|
if (Date.now() - info.mtimeMs > LOCK_STALE_MS) {
|
|
11916
12203
|
await unlink5(lockPath).catch(() => {
|
|
11917
12204
|
});
|
|
@@ -12103,6 +12390,18 @@ async function recomputeAll(projectsDir2, standaloneDir, opts) {
|
|
|
12103
12390
|
}
|
|
12104
12391
|
return summary;
|
|
12105
12392
|
}
|
|
12393
|
+
async function recomputeAssignmentDir(assignmentDir, cause, by) {
|
|
12394
|
+
try {
|
|
12395
|
+
const assignmentPath = resolve25(assignmentDir, "assignment.md");
|
|
12396
|
+
if (!await fileExists(assignmentPath)) return null;
|
|
12397
|
+
const parent = dirname5(assignmentDir);
|
|
12398
|
+
const projectDir = basename3(parent) === "assignments" ? dirname5(parent) : null;
|
|
12399
|
+
const context = await resolveDeriveContext();
|
|
12400
|
+
return await recomputeAndWrite(assignmentPath, { cause, by, projectDir, context });
|
|
12401
|
+
} catch {
|
|
12402
|
+
return null;
|
|
12403
|
+
}
|
|
12404
|
+
}
|
|
12106
12405
|
var LOCK_FILE, LOCK_STALE_MS, LOCK_WAIT_MS, LOCK_MAX_WAITS, CAS_RETRIES, MIGRATION_MARKER;
|
|
12107
12406
|
var init_recompute = __esm({
|
|
12108
12407
|
"src/lifecycle/recompute.ts"() {
|
|
@@ -12447,14 +12746,14 @@ var init_launch = __esm({
|
|
|
12447
12746
|
});
|
|
12448
12747
|
|
|
12449
12748
|
// src/usage/cwd-extractor.ts
|
|
12450
|
-
import { open as open3, readdir as readdir14, stat as
|
|
12749
|
+
import { open as open3, readdir as readdir14, stat as stat4 } from "fs/promises";
|
|
12451
12750
|
import { join as join4 } from "path";
|
|
12452
12751
|
import { homedir as homedir3 } from "os";
|
|
12453
12752
|
async function extractClaudeSessionMeta(jsonlPath) {
|
|
12454
12753
|
const cwd = await derivePathFromTranscript(jsonlPath);
|
|
12455
12754
|
if (!cwd) return null;
|
|
12456
|
-
const
|
|
12457
|
-
const sessionId =
|
|
12755
|
+
const basename10 = jsonlPath.split("/").pop() ?? "";
|
|
12756
|
+
const sessionId = basename10.replace(/\.jsonl$/, "");
|
|
12458
12757
|
if (!sessionId) return null;
|
|
12459
12758
|
const startTs = await readFirstTimestamp(jsonlPath);
|
|
12460
12759
|
const endTs = await readLastTimestamp(jsonlPath);
|
|
@@ -12549,8 +12848,8 @@ async function* walkClaudeProjects(opts = {}) {
|
|
|
12549
12848
|
async function* walkCodexSessions(opts = {}) {
|
|
12550
12849
|
const root = resolveCodexSessionsRoot(opts.root);
|
|
12551
12850
|
for await (const filePath of walkJsonlRecursive(root)) {
|
|
12552
|
-
const
|
|
12553
|
-
if (!
|
|
12851
|
+
const basename10 = filePath.split("/").pop() ?? "";
|
|
12852
|
+
if (!basename10.endsWith(".jsonl")) continue;
|
|
12554
12853
|
if (opts.sinceMtimeMs !== void 0) {
|
|
12555
12854
|
const mtime = await mtimeMs(filePath);
|
|
12556
12855
|
if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
|
|
@@ -12568,10 +12867,10 @@ function resolveCodexSessionsRoot(override) {
|
|
|
12568
12867
|
return join4(homedir3(), ".codex", "sessions");
|
|
12569
12868
|
}
|
|
12570
12869
|
async function extractPiSessionMeta(jsonlPath) {
|
|
12571
|
-
const
|
|
12572
|
-
const underscoreIdx =
|
|
12870
|
+
const basename10 = jsonlPath.split("/").pop() ?? "";
|
|
12871
|
+
const underscoreIdx = basename10.lastIndexOf("_");
|
|
12573
12872
|
if (underscoreIdx === -1) return null;
|
|
12574
|
-
const sessionId =
|
|
12873
|
+
const sessionId = basename10.slice(underscoreIdx + 1).replace(/\.jsonl$/, "");
|
|
12575
12874
|
if (!sessionId) return null;
|
|
12576
12875
|
let handle;
|
|
12577
12876
|
try {
|
|
@@ -12686,7 +12985,7 @@ async function* walkJsonlRecursive(root) {
|
|
|
12686
12985
|
}
|
|
12687
12986
|
async function mtimeMs(path) {
|
|
12688
12987
|
try {
|
|
12689
|
-
const s = await
|
|
12988
|
+
const s = await stat4(path);
|
|
12690
12989
|
return s.mtimeMs;
|
|
12691
12990
|
} catch {
|
|
12692
12991
|
return null;
|
|
@@ -13733,6 +14032,40 @@ var init_scanner2 = __esm({
|
|
|
13733
14032
|
}
|
|
13734
14033
|
});
|
|
13735
14034
|
|
|
14035
|
+
// src/staleness/watchdog.ts
|
|
14036
|
+
var watchdog_exports = {};
|
|
14037
|
+
__export(watchdog_exports, {
|
|
14038
|
+
runStalenessWatchdogTick: () => runStalenessWatchdogTick
|
|
14039
|
+
});
|
|
14040
|
+
function runStalenessWatchdogTick(candidates, seen, emit) {
|
|
14041
|
+
const staleNow = /* @__PURE__ */ new Map();
|
|
14042
|
+
for (const c2 of candidates) {
|
|
14043
|
+
if (c2.reasons.length > 0) staleNow.set(c2.assignmentId, c2);
|
|
14044
|
+
}
|
|
14045
|
+
let newlyStale = 0;
|
|
14046
|
+
for (const [id, c2] of staleNow) {
|
|
14047
|
+
if (!seen.has(id)) {
|
|
14048
|
+
seen.add(id);
|
|
14049
|
+
newlyStale++;
|
|
14050
|
+
emit({ assignmentId: id, projectSlug: c2.projectSlug, type: "staleness-detected", reasons: c2.reasons });
|
|
14051
|
+
}
|
|
14052
|
+
}
|
|
14053
|
+
let cleared = 0;
|
|
14054
|
+
for (const id of [...seen]) {
|
|
14055
|
+
if (!staleNow.has(id)) {
|
|
14056
|
+
seen.delete(id);
|
|
14057
|
+
cleared++;
|
|
14058
|
+
emit({ assignmentId: id, projectSlug: null, type: "staleness-cleared", reasons: [] });
|
|
14059
|
+
}
|
|
14060
|
+
}
|
|
14061
|
+
return { scanned: candidates.length, stale: staleNow.size, newlyStale, cleared };
|
|
14062
|
+
}
|
|
14063
|
+
var init_watchdog = __esm({
|
|
14064
|
+
"src/staleness/watchdog.ts"() {
|
|
14065
|
+
"use strict";
|
|
14066
|
+
}
|
|
14067
|
+
});
|
|
14068
|
+
|
|
13736
14069
|
// scripts/install-macos-url-handler.mjs
|
|
13737
14070
|
var install_macos_url_handler_exports = {};
|
|
13738
14071
|
__export(install_macos_url_handler_exports, {
|
|
@@ -13749,7 +14082,7 @@ import {
|
|
|
13749
14082
|
unlinkSync
|
|
13750
14083
|
} from "fs";
|
|
13751
14084
|
import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
|
|
13752
|
-
import { dirname as dirname21, resolve as
|
|
14085
|
+
import { dirname as dirname21, resolve as resolve72, join as join16 } from "path";
|
|
13753
14086
|
import { homedir as homedir13, tmpdir as tmpdir2 } from "os";
|
|
13754
14087
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
13755
14088
|
function syntaurRootMjs() {
|
|
@@ -13771,7 +14104,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
13771
14104
|
}
|
|
13772
14105
|
const stateRoot = syntaurRootMjs();
|
|
13773
14106
|
mkdirSync3(stateRoot, { recursive: true });
|
|
13774
|
-
const lockPath =
|
|
14107
|
+
const lockPath = resolve72(stateRoot, "install-url-handler.lock");
|
|
13775
14108
|
let lockFd;
|
|
13776
14109
|
try {
|
|
13777
14110
|
lockFd = openSync(lockPath, "wx");
|
|
@@ -13787,8 +14120,8 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
13787
14120
|
throw err2;
|
|
13788
14121
|
}
|
|
13789
14122
|
try {
|
|
13790
|
-
const pkgRoot =
|
|
13791
|
-
const cliBin = realpathSync3(
|
|
14123
|
+
const pkgRoot = resolve72(dirname21(fileURLToPath9(import.meta.url)), "..");
|
|
14124
|
+
const cliBin = realpathSync3(resolve72(pkgRoot, "bin/syntaur.js"));
|
|
13792
14125
|
const nodeBin = process.execPath;
|
|
13793
14126
|
const bundleParent = join16(
|
|
13794
14127
|
homedir13(),
|
|
@@ -15967,7 +16300,7 @@ init_timestamp();
|
|
|
15967
16300
|
init_fs();
|
|
15968
16301
|
init_git_worktree();
|
|
15969
16302
|
import { Router as Router2 } from "express";
|
|
15970
|
-
import { resolve as resolve26, basename as
|
|
16303
|
+
import { resolve as resolve26, basename as basename4, isAbsolute as isAbsolute4 } from "path";
|
|
15971
16304
|
import { rm, readFile as readFile19, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
|
|
15972
16305
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
15973
16306
|
|
|
@@ -16771,7 +17104,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
|
|
|
16771
17104
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
16772
17105
|
return;
|
|
16773
17106
|
}
|
|
16774
|
-
const document = await getEditableDocument(projectsDir2, "memory",
|
|
17107
|
+
const document = await getEditableDocument(projectsDir2, "memory", basename4(projectDir), itemSlug);
|
|
16775
17108
|
if (!document) {
|
|
16776
17109
|
res.status(404).json({ error: "Memory not found" });
|
|
16777
17110
|
return;
|
|
@@ -16790,7 +17123,7 @@ function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
|
|
|
16790
17123
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
16791
17124
|
return;
|
|
16792
17125
|
}
|
|
16793
|
-
const document = await getEditableDocument(projectsDir2, "resource",
|
|
17126
|
+
const document = await getEditableDocument(projectsDir2, "resource", basename4(projectDir), itemSlug);
|
|
16794
17127
|
if (!document) {
|
|
16795
17128
|
res.status(404).json({ error: "Resource not found" });
|
|
16796
17129
|
return;
|
|
@@ -16875,7 +17208,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16875
17208
|
let content = renderItemStub(kind, {
|
|
16876
17209
|
slug: requestedSlug,
|
|
16877
17210
|
name,
|
|
16878
|
-
projectSlug:
|
|
17211
|
+
projectSlug: basename4(projectDir),
|
|
16879
17212
|
timestamp
|
|
16880
17213
|
});
|
|
16881
17214
|
const customBody = typeof body.body === "string" ? body.body : "";
|
|
@@ -16892,13 +17225,13 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16892
17225
|
} catch (err2) {
|
|
16893
17226
|
if (err2.code === "EEXIST") {
|
|
16894
17227
|
res.status(409).json({
|
|
16895
|
-
error: `${kind === "memory" ? "Memory" : "Resource"} with slug "${requestedSlug}" already exists in project "${
|
|
17228
|
+
error: `${kind === "memory" ? "Memory" : "Resource"} with slug "${requestedSlug}" already exists in project "${basename4(projectDir)}".`
|
|
16896
17229
|
});
|
|
16897
17230
|
return;
|
|
16898
17231
|
}
|
|
16899
17232
|
throw err2;
|
|
16900
17233
|
}
|
|
16901
|
-
res.status(201).json({ slug: requestedSlug, projectSlug:
|
|
17234
|
+
res.status(201).json({ slug: requestedSlug, projectSlug: basename4(projectDir), content });
|
|
16902
17235
|
} catch (error) {
|
|
16903
17236
|
console.error(`Error creating ${kind}:`, error);
|
|
16904
17237
|
res.status(500).json({ error: `Failed to create ${kind}: ${error.message}` });
|
|
@@ -16933,7 +17266,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
16933
17266
|
${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
|
|
16934
17267
|
merged = setTopLevelField(merged, "updated", nowTimestamp());
|
|
16935
17268
|
await writeFileForce(filePath, merged);
|
|
16936
|
-
const detail = await getItemDetail(kind,
|
|
17269
|
+
const detail = await getItemDetail(kind, basename4(projectDir), itemSlug);
|
|
16937
17270
|
res.json({ [kind]: detail, content: merged });
|
|
16938
17271
|
} catch (error) {
|
|
16939
17272
|
console.error(`Error updating ${kind}:`, error);
|
|
@@ -20145,25 +20478,29 @@ async function resolveSessionPlan(input4, terminal) {
|
|
|
20145
20478
|
}
|
|
20146
20479
|
let cwd = session.path;
|
|
20147
20480
|
let fallbackWarning = null;
|
|
20148
|
-
if (session.
|
|
20149
|
-
const detail = await getAssignmentDetail(
|
|
20481
|
+
if (!isExistingDir(session.path)) {
|
|
20482
|
+
const detail = session.projectSlug && session.assignmentSlug ? await getAssignmentDetail(
|
|
20150
20483
|
input4.projectsDir,
|
|
20151
20484
|
session.projectSlug,
|
|
20152
20485
|
session.assignmentSlug
|
|
20153
|
-
)
|
|
20486
|
+
) : session.assignmentSlug ? await getAssignmentDetailById(
|
|
20487
|
+
input4.projectsDir,
|
|
20488
|
+
input4.assignmentsDir,
|
|
20489
|
+
session.assignmentSlug
|
|
20490
|
+
) : null;
|
|
20154
20491
|
if (detail) {
|
|
20155
20492
|
const picked = resolveWorkspaceCwd({
|
|
20156
20493
|
worktreePath: detail.workspace.worktreePath,
|
|
20157
20494
|
repository: detail.workspace.repository,
|
|
20158
20495
|
branch: detail.workspace.branch,
|
|
20159
|
-
assignmentSlug:
|
|
20496
|
+
assignmentSlug: detail.slug
|
|
20160
20497
|
});
|
|
20161
20498
|
if (picked.cwd !== null) {
|
|
20162
20499
|
cwd = picked.cwd;
|
|
20163
20500
|
fallbackWarning = picked.fallbackWarning;
|
|
20164
20501
|
} else {
|
|
20165
20502
|
fallbackWarning = formatFallbackCwdWarning({
|
|
20166
|
-
assignmentSlug:
|
|
20503
|
+
assignmentSlug: detail.slug,
|
|
20167
20504
|
workspaceDir: session.path,
|
|
20168
20505
|
worktreePath: detail.workspace.worktreePath,
|
|
20169
20506
|
branch: detail.workspace.branch
|
|
@@ -20200,7 +20537,7 @@ async function resolveSessionPlan(input4, terminal) {
|
|
|
20200
20537
|
init_launch();
|
|
20201
20538
|
import { spawn as spawn3 } from "child_process";
|
|
20202
20539
|
import { homedir as homedir5 } from "os";
|
|
20203
|
-
import { basename as
|
|
20540
|
+
import { basename as basename5, join as join6, resolve as resolve29 } from "path";
|
|
20204
20541
|
init_fs();
|
|
20205
20542
|
init_config2();
|
|
20206
20543
|
init_session_id();
|
|
@@ -20220,7 +20557,7 @@ var TerminalNotFoundError = class extends Error {
|
|
|
20220
20557
|
var realSpawn = (command, args, options) => spawn3(command, args, options);
|
|
20221
20558
|
var WRAPPER_COMMANDS = /* @__PURE__ */ new Set(["osascript", "open", "sh"]);
|
|
20222
20559
|
function isWrapperCommand(command) {
|
|
20223
|
-
return WRAPPER_COMMANDS.has(
|
|
20560
|
+
return WRAPPER_COMMANDS.has(basename5(command));
|
|
20224
20561
|
}
|
|
20225
20562
|
var WRAPPER_EXIT_TIMEOUT_MS = 1500;
|
|
20226
20563
|
async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
@@ -20248,7 +20585,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20248
20585
|
`Spawn failed: ${msg2}. Verify the terminal is installed and on PATH.`
|
|
20249
20586
|
);
|
|
20250
20587
|
}
|
|
20251
|
-
await new Promise((
|
|
20588
|
+
await new Promise((resolve111, reject) => {
|
|
20252
20589
|
let settled = false;
|
|
20253
20590
|
let stderr = "";
|
|
20254
20591
|
const finishOk = () => {
|
|
@@ -20258,7 +20595,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20258
20595
|
child.unref();
|
|
20259
20596
|
} catch {
|
|
20260
20597
|
}
|
|
20261
|
-
|
|
20598
|
+
resolve111();
|
|
20262
20599
|
};
|
|
20263
20600
|
const finishErr = (remediation) => {
|
|
20264
20601
|
if (settled) return;
|
|
@@ -20524,7 +20861,7 @@ function normalizeSlashes(p) {
|
|
|
20524
20861
|
}
|
|
20525
20862
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
20526
20863
|
const realpath3 = opts.realpath ?? realpathSync.native;
|
|
20527
|
-
const
|
|
20864
|
+
const readFile82 = opts.readFile ?? ((p) => readFileSync2(p, "utf-8"));
|
|
20528
20865
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
20529
20866
|
const resolved = resolveScriptPath(scriptUrl, realpath3);
|
|
20530
20867
|
if (resolved === null) {
|
|
@@ -20545,7 +20882,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
20545
20882
|
const pkgJsonPath = join7(dir, "package.json");
|
|
20546
20883
|
let raw2;
|
|
20547
20884
|
try {
|
|
20548
|
-
raw2 =
|
|
20885
|
+
raw2 = readFile82(pkgJsonPath);
|
|
20549
20886
|
} catch {
|
|
20550
20887
|
const parent2 = dirname7(dir);
|
|
20551
20888
|
if (parent2 === dir) break;
|
|
@@ -22610,7 +22947,7 @@ async function readEvents(jobId) {
|
|
|
22610
22947
|
|
|
22611
22948
|
// src/schedules/attempt.ts
|
|
22612
22949
|
import { createHash as createHash3 } from "crypto";
|
|
22613
|
-
import { open as open4, readFile as readFile23, stat as
|
|
22950
|
+
import { open as open4, readFile as readFile23, stat as stat5, unlink as unlink6 } from "fs/promises";
|
|
22614
22951
|
import { resolve as resolve34 } from "path";
|
|
22615
22952
|
|
|
22616
22953
|
// src/schedules/triggers.ts
|
|
@@ -22807,7 +23144,7 @@ async function acquireJobLock(id) {
|
|
|
22807
23144
|
const code = err2.code;
|
|
22808
23145
|
if (code !== "EEXIST") throw err2;
|
|
22809
23146
|
try {
|
|
22810
|
-
const info = await
|
|
23147
|
+
const info = await stat5(lockPath);
|
|
22811
23148
|
if (Date.now() - info.mtimeMs > LOCK_STALE_MS2) {
|
|
22812
23149
|
await unlink6(lockPath).catch(() => {
|
|
22813
23150
|
});
|
|
@@ -24474,8 +24811,8 @@ init_api();
|
|
|
24474
24811
|
import { raw } from "express";
|
|
24475
24812
|
|
|
24476
24813
|
// src/todos/attachments.ts
|
|
24477
|
-
import { mkdir as mkdir4, readdir as readdir16, stat as
|
|
24478
|
-
import { resolve as resolve41, basename as
|
|
24814
|
+
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";
|
|
24815
|
+
import { resolve as resolve41, basename as basename6, dirname as dirname10, extname } from "path";
|
|
24479
24816
|
|
|
24480
24817
|
// src/utils/proof-artifact-id.ts
|
|
24481
24818
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -24572,7 +24909,7 @@ function isSafeInlineMime(mime) {
|
|
|
24572
24909
|
return SAFE_INLINE_MIME.has(mime);
|
|
24573
24910
|
}
|
|
24574
24911
|
function sanitizeAttachmentName(name) {
|
|
24575
|
-
let n =
|
|
24912
|
+
let n = basename6(name || "").replace(/["'\\/]/g, "_");
|
|
24576
24913
|
n = Array.from(n, (ch) => {
|
|
24577
24914
|
const code = ch.charCodeAt(0);
|
|
24578
24915
|
return code < 32 || code === 127 ? "_" : ch;
|
|
@@ -24595,7 +24932,7 @@ function attachmentDirFor(todosDir2, scopeId, todoId) {
|
|
|
24595
24932
|
}
|
|
24596
24933
|
async function dirExists(p) {
|
|
24597
24934
|
try {
|
|
24598
|
-
return (await
|
|
24935
|
+
return (await stat6(p)).isDirectory();
|
|
24599
24936
|
} catch {
|
|
24600
24937
|
return false;
|
|
24601
24938
|
}
|
|
@@ -24630,7 +24967,7 @@ async function listAttachments(todosDir2, scopeId, todoId) {
|
|
|
24630
24967
|
if (!ATTACHMENT_ID_RE.test(id)) continue;
|
|
24631
24968
|
const filename = stored.slice(sep2 + 2);
|
|
24632
24969
|
try {
|
|
24633
|
-
const st = await
|
|
24970
|
+
const st = await stat6(resolve41(dir, stored));
|
|
24634
24971
|
if (!st.isFile()) continue;
|
|
24635
24972
|
out.push({ id, filename, mime: mimeForName(filename), size: st.size, createdAt: st.mtime.toISOString() });
|
|
24636
24973
|
} catch {
|
|
@@ -25085,8 +25422,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
25085
25422
|
router.post("/:workspace/archive", async (req2, res) => {
|
|
25086
25423
|
try {
|
|
25087
25424
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
25088
|
-
const { resolve:
|
|
25089
|
-
const { readFile:
|
|
25425
|
+
const { resolve: resolve111 } = await import("path");
|
|
25426
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
25090
25427
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
25091
25428
|
const workspace = getWorkspaceParam(req2.params.workspace);
|
|
25092
25429
|
const outcome = await wsLock(workspace, async () => {
|
|
@@ -25102,10 +25439,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
25102
25439
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
25103
25440
|
);
|
|
25104
25441
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
25105
|
-
await ensureDir(
|
|
25442
|
+
await ensureDir(resolve111(todosDir2, "archive"));
|
|
25106
25443
|
let archContent = "";
|
|
25107
25444
|
if (await fileExists(archFile)) {
|
|
25108
|
-
archContent = await
|
|
25445
|
+
archContent = await readFile82(archFile, "utf-8");
|
|
25109
25446
|
archContent = archContent.trimEnd() + "\n\n";
|
|
25110
25447
|
} else {
|
|
25111
25448
|
archContent = `---
|
|
@@ -25406,7 +25743,7 @@ workspace: ${workspace}
|
|
|
25406
25743
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
25407
25744
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
25408
25745
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
25409
|
-
const { readFile:
|
|
25746
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
25410
25747
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
25411
25748
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
25412
25749
|
let assignmentRef;
|
|
@@ -25427,7 +25764,7 @@ workspace: ${workspace}
|
|
|
25427
25764
|
}
|
|
25428
25765
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
25429
25766
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
25430
|
-
let content = await
|
|
25767
|
+
let content = await readFile82(assignmentMdPath2, "utf-8");
|
|
25431
25768
|
content = appendTodosToAssignmentBody2(
|
|
25432
25769
|
content,
|
|
25433
25770
|
items.map((it) => ({
|
|
@@ -26837,7 +27174,7 @@ init_fs();
|
|
|
26837
27174
|
init_config2();
|
|
26838
27175
|
import { execFile as execFile2 } from "child_process";
|
|
26839
27176
|
import { promisify as promisify2 } from "util";
|
|
26840
|
-
import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile30, writeFile as writeFile7, unlink as unlink8, stat as
|
|
27177
|
+
import { cp as cp2, mkdtemp, rm as rm5, readFile as readFile30, writeFile as writeFile7, unlink as unlink8, stat as stat7, open as open5, rename as rename8 } from "fs/promises";
|
|
26841
27178
|
import { resolve as resolve44, join as join9 } from "path";
|
|
26842
27179
|
import { tmpdir } from "os";
|
|
26843
27180
|
var exec2 = promisify2(execFile2);
|
|
@@ -26931,7 +27268,7 @@ async function cloneOrInit(repoUrl, destDir) {
|
|
|
26931
27268
|
}
|
|
26932
27269
|
async function copyRecursive(src, dest) {
|
|
26933
27270
|
if (!await fileExists(src)) return;
|
|
26934
|
-
const s = await
|
|
27271
|
+
const s = await stat7(src);
|
|
26935
27272
|
if (s.isDirectory()) {
|
|
26936
27273
|
await ensureDir(dest);
|
|
26937
27274
|
await cp2(src, dest, { recursive: true, force: true });
|
|
@@ -27622,7 +27959,7 @@ async function runCcusage(opts = {}) {
|
|
|
27622
27959
|
};
|
|
27623
27960
|
}
|
|
27624
27961
|
function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
27625
|
-
return new Promise((
|
|
27962
|
+
return new Promise((resolve111) => {
|
|
27626
27963
|
const child = spawn4(binary, args, {
|
|
27627
27964
|
env: env ?? process.env,
|
|
27628
27965
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -27636,7 +27973,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
|
27636
27973
|
if (settled) return;
|
|
27637
27974
|
settled = true;
|
|
27638
27975
|
clearTimeout(timer3);
|
|
27639
|
-
|
|
27976
|
+
resolve111(result);
|
|
27640
27977
|
};
|
|
27641
27978
|
const timer3 = setTimeout(() => {
|
|
27642
27979
|
timedOut = true;
|
|
@@ -28586,6 +28923,8 @@ function createDashboardServer(options) {
|
|
|
28586
28923
|
});
|
|
28587
28924
|
}
|
|
28588
28925
|
let watcherHandle = null;
|
|
28926
|
+
const STALENESS_WATCHDOG_INTERVAL_MS = 5 * 60 * 1e3;
|
|
28927
|
+
let stalenessWatchdogTimer = null;
|
|
28589
28928
|
return {
|
|
28590
28929
|
async start() {
|
|
28591
28930
|
const { recomputeAndWrite: recomputeAndWrite2, recomputeAll: recomputeAll2, resolveDeriveContext: resolveDeriveContext2, isDeriveMigrated: isDeriveMigrated2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
|
|
@@ -28682,6 +29021,38 @@ function createDashboardServer(options) {
|
|
|
28682
29021
|
onAgentSessionsChanged: () => broadcast({ type: "agent-sessions-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
28683
29022
|
});
|
|
28684
29023
|
startUsageCollector();
|
|
29024
|
+
const startupConfig = await readConfig();
|
|
29025
|
+
if (startupConfig.stalenessWatchdog) {
|
|
29026
|
+
const { collectStaleCandidates: collectStaleCandidates2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
29027
|
+
const { runStalenessWatchdogTick: runStalenessWatchdogTick2 } = await Promise.resolve().then(() => (init_watchdog(), watchdog_exports));
|
|
29028
|
+
const { emitEvent: emitEvent2 } = await Promise.resolve().then(() => (init_event_emit(), event_emit_exports));
|
|
29029
|
+
const stalenessSeen = /* @__PURE__ */ new Set();
|
|
29030
|
+
const watchdogTick = async () => {
|
|
29031
|
+
if (!await migrationGate()) return;
|
|
29032
|
+
try {
|
|
29033
|
+
const candidates = await collectStaleCandidates2(projectsDir2, assignmentsDir2);
|
|
29034
|
+
const summary = runStalenessWatchdogTick2(candidates, stalenessSeen, (e) => {
|
|
29035
|
+
emitEvent2({
|
|
29036
|
+
assignmentId: e.assignmentId,
|
|
29037
|
+
projectSlug: e.projectSlug,
|
|
29038
|
+
type: e.type,
|
|
29039
|
+
actor: "system",
|
|
29040
|
+
details: { reasons: e.reasons.map((r) => r.kind) }
|
|
29041
|
+
});
|
|
29042
|
+
});
|
|
29043
|
+
if (summary.newlyStale > 0 || summary.cleared > 0) {
|
|
29044
|
+
console.log(
|
|
29045
|
+
`staleness watchdog: ${summary.newlyStale} newly stale, ${summary.cleared} cleared (${summary.stale}/${summary.scanned} stale).`
|
|
29046
|
+
);
|
|
29047
|
+
}
|
|
29048
|
+
} catch (err2) {
|
|
29049
|
+
console.error("staleness watchdog tick failed:", err2);
|
|
29050
|
+
}
|
|
29051
|
+
};
|
|
29052
|
+
void watchdogTick();
|
|
29053
|
+
stalenessWatchdogTimer = setInterval(() => void watchdogTick(), STALENESS_WATCHDOG_INTERVAL_MS);
|
|
29054
|
+
stalenessWatchdogTimer.unref?.();
|
|
29055
|
+
}
|
|
28685
29056
|
return new Promise((resolvePromise, reject) => {
|
|
28686
29057
|
server.on("error", (err2) => {
|
|
28687
29058
|
if (err2.code === "EADDRINUSE") {
|
|
@@ -28703,6 +29074,10 @@ function createDashboardServer(options) {
|
|
|
28703
29074
|
});
|
|
28704
29075
|
},
|
|
28705
29076
|
async stop() {
|
|
29077
|
+
if (stalenessWatchdogTimer) {
|
|
29078
|
+
clearInterval(stalenessWatchdogTimer);
|
|
29079
|
+
stalenessWatchdogTimer = null;
|
|
29080
|
+
}
|
|
28706
29081
|
await stopAutodiscovery();
|
|
28707
29082
|
await stopUsageCollector();
|
|
28708
29083
|
if (watcherHandle) {
|
|
@@ -29069,17 +29444,173 @@ init_facts();
|
|
|
29069
29444
|
init_git_worktree();
|
|
29070
29445
|
init_recompute();
|
|
29071
29446
|
init_event_emit();
|
|
29447
|
+
init_transitions();
|
|
29448
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
29449
|
+
import { resolve as resolve52, basename as basename7 } from "path";
|
|
29450
|
+
|
|
29451
|
+
// src/utils/assignment-target.ts
|
|
29452
|
+
init_paths();
|
|
29453
|
+
init_fs();
|
|
29454
|
+
init_config2();
|
|
29455
|
+
init_slug();
|
|
29456
|
+
init_assignment_resolver();
|
|
29457
|
+
init_parser();
|
|
29458
|
+
import { resolve as resolve51 } from "path";
|
|
29072
29459
|
import { readFile as readFile33 } from "fs/promises";
|
|
29073
|
-
|
|
29460
|
+
var AssignmentTargetError = class extends Error {
|
|
29461
|
+
};
|
|
29462
|
+
function classifyContext(ctx) {
|
|
29463
|
+
if (!ctx) return "empty";
|
|
29464
|
+
const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
|
|
29465
|
+
if (hasAssignment) return "assignment";
|
|
29466
|
+
if (ctx.bundleId) return "bundle";
|
|
29467
|
+
if (ctx.sessionId || ctx.transcriptPath) return "standalone";
|
|
29468
|
+
return "empty";
|
|
29469
|
+
}
|
|
29470
|
+
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
29471
|
+
const path = resolve51(assignmentDir, "assignment.md");
|
|
29472
|
+
if (!await fileExists(path)) return null;
|
|
29473
|
+
try {
|
|
29474
|
+
const content = await readFile33(path, "utf-8");
|
|
29475
|
+
const [fm] = extractFrontmatter(content);
|
|
29476
|
+
return getField(fm, "id");
|
|
29477
|
+
} catch {
|
|
29478
|
+
return null;
|
|
29479
|
+
}
|
|
29480
|
+
}
|
|
29481
|
+
async function readContextJson(cwd) {
|
|
29482
|
+
const path = resolve51(cwd, ".syntaur", "context.json");
|
|
29483
|
+
if (!await fileExists(path)) return null;
|
|
29484
|
+
try {
|
|
29485
|
+
const raw2 = await readFile33(path, "utf-8");
|
|
29486
|
+
return JSON.parse(raw2);
|
|
29487
|
+
} catch {
|
|
29488
|
+
return null;
|
|
29489
|
+
}
|
|
29490
|
+
}
|
|
29491
|
+
async function resolveAssignmentTarget(input4, opts = {}) {
|
|
29492
|
+
const config = await readConfig();
|
|
29493
|
+
const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
|
|
29494
|
+
if (opts.project) {
|
|
29495
|
+
if (!input4) {
|
|
29496
|
+
throw new AssignmentTargetError(
|
|
29497
|
+
"--project requires an assignment slug as a positional argument."
|
|
29498
|
+
);
|
|
29499
|
+
}
|
|
29500
|
+
if (!isValidSlug(opts.project)) {
|
|
29501
|
+
throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
|
|
29502
|
+
}
|
|
29503
|
+
if (!isValidSlug(input4)) {
|
|
29504
|
+
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
29505
|
+
}
|
|
29506
|
+
const projectDir = resolve51(baseDir, opts.project);
|
|
29507
|
+
const projectMdPath = resolve51(projectDir, "project.md");
|
|
29508
|
+
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
29509
|
+
throw new AssignmentTargetError(
|
|
29510
|
+
`Project "${opts.project}" not found at ${projectDir}.`
|
|
29511
|
+
);
|
|
29512
|
+
}
|
|
29513
|
+
const assignmentDir = resolve51(projectDir, "assignments", input4);
|
|
29514
|
+
const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
|
|
29515
|
+
if (!await fileExists(assignmentMdPath2)) {
|
|
29516
|
+
throw new AssignmentTargetError(
|
|
29517
|
+
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
29518
|
+
);
|
|
29519
|
+
}
|
|
29520
|
+
const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
|
|
29521
|
+
return {
|
|
29522
|
+
assignmentDir,
|
|
29523
|
+
projectSlug: opts.project,
|
|
29524
|
+
assignmentSlug: input4,
|
|
29525
|
+
id,
|
|
29526
|
+
standalone: false,
|
|
29527
|
+
workspaceGroup: null
|
|
29528
|
+
};
|
|
29529
|
+
}
|
|
29530
|
+
if (input4) {
|
|
29531
|
+
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
|
|
29532
|
+
if (!resolved) {
|
|
29533
|
+
throw new AssignmentTargetError(
|
|
29534
|
+
`Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
|
|
29535
|
+
);
|
|
29536
|
+
}
|
|
29537
|
+
return resolved;
|
|
29538
|
+
}
|
|
29539
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
29540
|
+
const ctx = await readContextJson(cwd);
|
|
29541
|
+
if (!ctx) {
|
|
29542
|
+
throw new AssignmentTargetError(
|
|
29543
|
+
"No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
|
|
29544
|
+
);
|
|
29545
|
+
}
|
|
29546
|
+
if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
|
|
29547
|
+
throw new AssignmentTargetError(
|
|
29548
|
+
`Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
|
|
29549
|
+
);
|
|
29550
|
+
}
|
|
29551
|
+
if (ctx.assignmentDir) {
|
|
29552
|
+
const dir = expandHome(ctx.assignmentDir);
|
|
29553
|
+
const assignmentMdPath2 = resolve51(dir, "assignment.md");
|
|
29554
|
+
if (!await fileExists(assignmentMdPath2)) {
|
|
29555
|
+
throw new AssignmentTargetError(
|
|
29556
|
+
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
29557
|
+
);
|
|
29558
|
+
}
|
|
29559
|
+
const id = await readAssignmentFrontmatterId(dir);
|
|
29560
|
+
if (!id || id.trim() === "") {
|
|
29561
|
+
throw new AssignmentTargetError(
|
|
29562
|
+
`.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
|
|
29563
|
+
);
|
|
29564
|
+
}
|
|
29565
|
+
const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
|
|
29566
|
+
const projectSlug = ctx.projectSlug ?? null;
|
|
29567
|
+
return {
|
|
29568
|
+
assignmentDir: dir,
|
|
29569
|
+
projectSlug,
|
|
29570
|
+
assignmentSlug,
|
|
29571
|
+
id,
|
|
29572
|
+
standalone: projectSlug === null,
|
|
29573
|
+
workspaceGroup: null
|
|
29574
|
+
};
|
|
29575
|
+
}
|
|
29576
|
+
if (ctx.projectSlug && ctx.assignmentSlug) {
|
|
29577
|
+
if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
|
|
29578
|
+
throw new AssignmentTargetError(
|
|
29579
|
+
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
29580
|
+
);
|
|
29581
|
+
}
|
|
29582
|
+
const assignmentDir = resolve51(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
29583
|
+
const assignmentMdPath2 = resolve51(assignmentDir, "assignment.md");
|
|
29584
|
+
if (!await fileExists(assignmentMdPath2)) {
|
|
29585
|
+
throw new AssignmentTargetError(
|
|
29586
|
+
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
29587
|
+
);
|
|
29588
|
+
}
|
|
29589
|
+
const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
|
|
29590
|
+
return {
|
|
29591
|
+
assignmentDir,
|
|
29592
|
+
projectSlug: ctx.projectSlug,
|
|
29593
|
+
assignmentSlug: ctx.assignmentSlug,
|
|
29594
|
+
id,
|
|
29595
|
+
standalone: false,
|
|
29596
|
+
workspaceGroup: null
|
|
29597
|
+
};
|
|
29598
|
+
}
|
|
29599
|
+
throw new AssignmentTargetError(
|
|
29600
|
+
".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
|
|
29601
|
+
);
|
|
29602
|
+
}
|
|
29603
|
+
|
|
29604
|
+
// src/commands/derive-verbs.ts
|
|
29074
29605
|
async function resolveTarget(assignment, options) {
|
|
29075
29606
|
const config = await readConfig();
|
|
29076
29607
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
29077
29608
|
if (options.project) {
|
|
29078
29609
|
if (!isValidSlug(options.project)) throw new Error(`Invalid project slug "${options.project}".`);
|
|
29079
29610
|
if (!isValidSlug(assignment)) throw new Error(`Invalid assignment slug "${assignment}".`);
|
|
29080
|
-
const projectDir =
|
|
29081
|
-
const assignmentDir =
|
|
29082
|
-
const assignmentPath =
|
|
29611
|
+
const projectDir = resolve52(baseDir, options.project);
|
|
29612
|
+
const assignmentDir = resolve52(projectDir, "assignments", assignment);
|
|
29613
|
+
const assignmentPath = resolve52(assignmentDir, "assignment.md");
|
|
29083
29614
|
if (!await fileExists(assignmentPath)) {
|
|
29084
29615
|
throw new Error(`Assignment "${assignment}" not found at ${assignmentPath}.`);
|
|
29085
29616
|
}
|
|
@@ -29091,14 +29622,14 @@ async function resolveTarget(assignment, options) {
|
|
|
29091
29622
|
}
|
|
29092
29623
|
return {
|
|
29093
29624
|
assignmentDir: resolved.assignmentDir,
|
|
29094
|
-
assignmentPath:
|
|
29095
|
-
projectDir: resolved.standalone ? null :
|
|
29625
|
+
assignmentPath: resolve52(resolved.assignmentDir, "assignment.md"),
|
|
29626
|
+
projectDir: resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..")
|
|
29096
29627
|
};
|
|
29097
29628
|
}
|
|
29098
29629
|
async function inferActor(options) {
|
|
29099
29630
|
if (options.agent) return `agent:${options.agent}`;
|
|
29100
29631
|
try {
|
|
29101
|
-
const raw2 = await
|
|
29632
|
+
const raw2 = await readFile34(resolve52(process.cwd(), ".syntaur", "context.json"), "utf-8");
|
|
29102
29633
|
const ctx = JSON.parse(raw2);
|
|
29103
29634
|
if (ctx.sessionId) return `agent:${ctx.sessionId.slice(0, 8)}`;
|
|
29104
29635
|
} catch {
|
|
@@ -29107,10 +29638,10 @@ async function inferActor(options) {
|
|
|
29107
29638
|
}
|
|
29108
29639
|
async function emitDeriveEvent(target, type, actor, details) {
|
|
29109
29640
|
try {
|
|
29110
|
-
const fm = parseAssignmentFrontmatter(await
|
|
29641
|
+
const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
|
|
29111
29642
|
emitEvent({
|
|
29112
29643
|
assignmentId: fm.id,
|
|
29113
|
-
projectSlug: target.projectDir ?
|
|
29644
|
+
projectSlug: target.projectDir ? basename7(target.projectDir) : null,
|
|
29114
29645
|
type,
|
|
29115
29646
|
actor,
|
|
29116
29647
|
details
|
|
@@ -29168,7 +29699,7 @@ async function planApproveCommand(assignment, options) {
|
|
|
29168
29699
|
throw new Error("No plan file found (plan.md / plan-v*.md). Write a plan before approving.");
|
|
29169
29700
|
}
|
|
29170
29701
|
approvedFile = planFile;
|
|
29171
|
-
const planContent = await
|
|
29702
|
+
const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
|
|
29172
29703
|
return updatePlanApproval(content, {
|
|
29173
29704
|
file: planFile,
|
|
29174
29705
|
digest: planDigest(planContent),
|
|
@@ -29241,7 +29772,7 @@ async function attestCommand(assignment, fact, options) {
|
|
|
29241
29772
|
"No plan file found (plan.md / plan-v*.md). Write a plan before attesting a binds:plan fact."
|
|
29242
29773
|
);
|
|
29243
29774
|
}
|
|
29244
|
-
const planContent = await
|
|
29775
|
+
const planContent = await readFile34(resolve52(target2.assignmentDir, planFile), "utf-8");
|
|
29245
29776
|
record.file = planFile;
|
|
29246
29777
|
record.digest = planDigest(planContent);
|
|
29247
29778
|
} else if (binds === "commit") {
|
|
@@ -29291,6 +29822,15 @@ async function requestReviewCommand(assignment, options) {
|
|
|
29291
29822
|
);
|
|
29292
29823
|
}
|
|
29293
29824
|
async function implementStartedCommand(assignment, options) {
|
|
29825
|
+
const target = await resolveTarget(assignment, options);
|
|
29826
|
+
const context = await resolveDeriveContext();
|
|
29827
|
+
const fm = parseAssignmentFrontmatter(await readFile34(target.assignmentPath, "utf-8"));
|
|
29828
|
+
if (fm.dependsOn.length > 0 && target.projectDir) {
|
|
29829
|
+
const dep = await checkDependencies(target.projectDir, fm.dependsOn, context.terminalStatuses);
|
|
29830
|
+
if (!dep.satisfied) {
|
|
29831
|
+
console.warn(`Warning: starting with unmet dependencies: ${dep.unmet.join(", ")}`);
|
|
29832
|
+
}
|
|
29833
|
+
}
|
|
29294
29834
|
await assertFact(
|
|
29295
29835
|
assignment,
|
|
29296
29836
|
options,
|
|
@@ -29298,14 +29838,15 @@ async function implementStartedCommand(assignment, options) {
|
|
|
29298
29838
|
(content) => {
|
|
29299
29839
|
let next = updateAssignmentFile(content, { implementationStarted: true });
|
|
29300
29840
|
if (options.agent) {
|
|
29301
|
-
const
|
|
29302
|
-
if (
|
|
29841
|
+
const innerFm = parseAssignmentFrontmatter(next);
|
|
29842
|
+
if (innerFm.assignee === null) {
|
|
29303
29843
|
next = updateAssignmentFile(next, { assignee: options.agent });
|
|
29304
29844
|
}
|
|
29305
29845
|
}
|
|
29306
29846
|
return next;
|
|
29307
29847
|
},
|
|
29308
|
-
"Implementation started"
|
|
29848
|
+
"Implementation started",
|
|
29849
|
+
{ context }
|
|
29309
29850
|
);
|
|
29310
29851
|
}
|
|
29311
29852
|
async function blockFactCommand(assignment, options) {
|
|
@@ -29353,6 +29894,7 @@ async function statusUnpinCommand(assignment, options) {
|
|
|
29353
29894
|
await assertFact(assignment, options, "unpin", (content) => updateOverride(content, null), "Unpinned");
|
|
29354
29895
|
}
|
|
29355
29896
|
async function recomputeCommand(assignment, options) {
|
|
29897
|
+
if (options.ifMigrated && !await isDeriveMigrated()) return;
|
|
29356
29898
|
const context = await resolveDeriveContext();
|
|
29357
29899
|
if (options.all) {
|
|
29358
29900
|
const { recomputeAll: recomputeAll2 } = await Promise.resolve().then(() => (init_recompute(), recompute_exports));
|
|
@@ -29368,12 +29910,15 @@ async function recomputeCommand(assignment, options) {
|
|
|
29368
29910
|
for (const w of summary.warnings) console.warn(`Warning: ${w}`);
|
|
29369
29911
|
return;
|
|
29370
29912
|
}
|
|
29371
|
-
|
|
29372
|
-
|
|
29373
|
-
|
|
29913
|
+
const resolved = await resolveAssignmentTarget(assignment, {
|
|
29914
|
+
project: options.project,
|
|
29915
|
+
dir: options.dir
|
|
29916
|
+
});
|
|
29917
|
+
const projectDir = resolved.standalone ? null : resolve52(resolved.assignmentDir, "..", "..");
|
|
29918
|
+
const result = await recomputeAndWrite(resolve52(resolved.assignmentDir, "assignment.md"), {
|
|
29374
29919
|
cause: "recompute",
|
|
29375
29920
|
by: await inferActor(options),
|
|
29376
|
-
projectDir
|
|
29921
|
+
projectDir,
|
|
29377
29922
|
context
|
|
29378
29923
|
});
|
|
29379
29924
|
reportDerived(result.changed ? "Recomputed" : "Recomputed (no change)", result);
|
|
@@ -29393,8 +29938,8 @@ init_frontmatter();
|
|
|
29393
29938
|
init_timestamp();
|
|
29394
29939
|
init_assignment_resolver();
|
|
29395
29940
|
init_event_emit();
|
|
29396
|
-
import { resolve as
|
|
29397
|
-
import { readFile as
|
|
29941
|
+
import { resolve as resolve53 } from "path";
|
|
29942
|
+
import { readFile as readFile35, writeFile as writeFile9 } from "fs/promises";
|
|
29398
29943
|
async function resolveTarget2(target, options) {
|
|
29399
29944
|
const config = await readConfig();
|
|
29400
29945
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
@@ -29405,7 +29950,7 @@ async function resolveTarget2(target, options) {
|
|
|
29405
29950
|
if (!isValidSlug(target)) {
|
|
29406
29951
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
29407
29952
|
}
|
|
29408
|
-
const assignmentMd =
|
|
29953
|
+
const assignmentMd = resolve53(baseDir, options.project, "assignments", target, "assignment.md");
|
|
29409
29954
|
if (!await fileExists(assignmentMd)) {
|
|
29410
29955
|
throw new Error(`Assignment "${target}" not found in project "${options.project}".`);
|
|
29411
29956
|
}
|
|
@@ -29415,18 +29960,18 @@ async function resolveTarget2(target, options) {
|
|
|
29415
29960
|
if (resolved) {
|
|
29416
29961
|
return {
|
|
29417
29962
|
kind: "assignment",
|
|
29418
|
-
filePath:
|
|
29963
|
+
filePath: resolve53(resolved.assignmentDir, "assignment.md"),
|
|
29419
29964
|
label: resolved.projectSlug ? `assignment "${resolved.projectSlug}/${resolved.assignmentSlug}"` : `assignment "${target}"`
|
|
29420
29965
|
};
|
|
29421
29966
|
}
|
|
29422
|
-
const projectMd =
|
|
29967
|
+
const projectMd = resolve53(baseDir, target, "project.md");
|
|
29423
29968
|
if (await fileExists(projectMd)) {
|
|
29424
29969
|
return { kind: "project", filePath: projectMd, label: `project "${target}"` };
|
|
29425
29970
|
}
|
|
29426
29971
|
return null;
|
|
29427
29972
|
}
|
|
29428
29973
|
async function writeArchiveState(filePath, archived, reason) {
|
|
29429
|
-
const content = await
|
|
29974
|
+
const content = await readFile35(filePath, "utf-8");
|
|
29430
29975
|
const updated = updateAssignmentFile(content, {
|
|
29431
29976
|
archived,
|
|
29432
29977
|
archivedAt: archived ? nowTimestamp() : null,
|
|
@@ -29438,7 +29983,7 @@ async function writeArchiveState(filePath, archived, reason) {
|
|
|
29438
29983
|
async function emitArchiveEvent(resolved, type, reason) {
|
|
29439
29984
|
if (resolved.kind !== "assignment") return;
|
|
29440
29985
|
try {
|
|
29441
|
-
const fm = parseAssignmentFrontmatter(await
|
|
29986
|
+
const fm = parseAssignmentFrontmatter(await readFile35(resolved.filePath, "utf-8"));
|
|
29442
29987
|
emitEvent({
|
|
29443
29988
|
assignmentId: fm.id,
|
|
29444
29989
|
projectSlug: fm.project,
|
|
@@ -29508,8 +30053,8 @@ init_config2();
|
|
|
29508
30053
|
init_frontmatter();
|
|
29509
30054
|
init_timestamp();
|
|
29510
30055
|
init_event_emit();
|
|
29511
|
-
import { resolve as
|
|
29512
|
-
import { readdir as readdir20, readFile as
|
|
30056
|
+
import { resolve as resolve54 } from "path";
|
|
30057
|
+
import { readdir as readdir20, readFile as readFile36 } from "fs/promises";
|
|
29513
30058
|
var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
29514
30059
|
function objectiveIsFleshedOut(content) {
|
|
29515
30060
|
const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
|
|
@@ -29531,11 +30076,11 @@ async function collectCandidates(baseDirs) {
|
|
|
29531
30076
|
for (const m of projects) {
|
|
29532
30077
|
if (!m.isDirectory()) continue;
|
|
29533
30078
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29534
|
-
const directAssignmentMd =
|
|
30079
|
+
const directAssignmentMd = resolve54(baseDir, m.name, "assignment.md");
|
|
29535
30080
|
if (await fileExists(directAssignmentMd)) {
|
|
29536
30081
|
const fm = await parseSafe(directAssignmentMd);
|
|
29537
30082
|
if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
|
|
29538
|
-
const content = await
|
|
30083
|
+
const content = await readFile36(directAssignmentMd, "utf-8");
|
|
29539
30084
|
if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
|
|
29540
30085
|
candidates.push({
|
|
29541
30086
|
projectSlug: null,
|
|
@@ -29548,17 +30093,17 @@ async function collectCandidates(baseDirs) {
|
|
|
29548
30093
|
}
|
|
29549
30094
|
continue;
|
|
29550
30095
|
}
|
|
29551
|
-
const assignmentsDir2 =
|
|
30096
|
+
const assignmentsDir2 = resolve54(baseDir, m.name, "assignments");
|
|
29552
30097
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
29553
30098
|
const entries = await readdir20(assignmentsDir2, { withFileTypes: true });
|
|
29554
30099
|
for (const a of entries) {
|
|
29555
30100
|
if (!a.isDirectory()) continue;
|
|
29556
30101
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29557
|
-
const assignmentMd =
|
|
30102
|
+
const assignmentMd = resolve54(assignmentsDir2, a.name, "assignment.md");
|
|
29558
30103
|
if (!await fileExists(assignmentMd)) continue;
|
|
29559
30104
|
const fm = await parseSafe(assignmentMd);
|
|
29560
30105
|
if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
|
|
29561
|
-
const content = await
|
|
30106
|
+
const content = await readFile36(assignmentMd, "utf-8");
|
|
29562
30107
|
if (!objectiveIsFleshedOut(content)) continue;
|
|
29563
30108
|
if (!hasAcceptanceCriteria(content)) continue;
|
|
29564
30109
|
candidates.push({
|
|
@@ -29575,7 +30120,7 @@ async function collectCandidates(baseDirs) {
|
|
|
29575
30120
|
}
|
|
29576
30121
|
async function parseSafe(path) {
|
|
29577
30122
|
try {
|
|
29578
|
-
const content = await
|
|
30123
|
+
const content = await readFile36(path, "utf-8");
|
|
29579
30124
|
return parseAssignmentFrontmatter(content);
|
|
29580
30125
|
} catch {
|
|
29581
30126
|
return null;
|
|
@@ -29605,7 +30150,7 @@ async function migrateStatusesCommand(options) {
|
|
|
29605
30150
|
let migrated = 0;
|
|
29606
30151
|
await withSuppressedEvents(async () => {
|
|
29607
30152
|
for (const c2 of candidates) {
|
|
29608
|
-
const content = await
|
|
30153
|
+
const content = await readFile36(c2.assignmentMd, "utf-8");
|
|
29609
30154
|
const updated = appendStatusHistoryEntry(
|
|
29610
30155
|
updateAssignmentFile(content, {
|
|
29611
30156
|
status: c2.toStatus,
|
|
@@ -29627,11 +30172,11 @@ init_config2();
|
|
|
29627
30172
|
init_frontmatter();
|
|
29628
30173
|
init_event_emit();
|
|
29629
30174
|
init_types();
|
|
29630
|
-
import { resolve as
|
|
29631
|
-
import { readdir as readdir21, readFile as
|
|
30175
|
+
import { resolve as resolve55 } from "path";
|
|
30176
|
+
import { readdir as readdir21, readFile as readFile37 } from "fs/promises";
|
|
29632
30177
|
async function parseSafe2(path) {
|
|
29633
30178
|
try {
|
|
29634
|
-
return parseAssignmentFrontmatter(await
|
|
30179
|
+
return parseAssignmentFrontmatter(await readFile37(path, "utf-8"));
|
|
29635
30180
|
} catch {
|
|
29636
30181
|
return null;
|
|
29637
30182
|
}
|
|
@@ -29658,7 +30203,7 @@ async function collectTargets(baseDirs, terminalStatuses3) {
|
|
|
29658
30203
|
for (const m of entries) {
|
|
29659
30204
|
if (!m.isDirectory()) continue;
|
|
29660
30205
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29661
|
-
const directAssignmentMd =
|
|
30206
|
+
const directAssignmentMd = resolve55(baseDir, m.name, "assignment.md");
|
|
29662
30207
|
if (await fileExists(directAssignmentMd)) {
|
|
29663
30208
|
if (seen.has(directAssignmentMd)) continue;
|
|
29664
30209
|
const fm = await parseSafe2(directAssignmentMd);
|
|
@@ -29673,13 +30218,13 @@ async function collectTargets(baseDirs, terminalStatuses3) {
|
|
|
29673
30218
|
}
|
|
29674
30219
|
continue;
|
|
29675
30220
|
}
|
|
29676
|
-
const assignmentsBase =
|
|
30221
|
+
const assignmentsBase = resolve55(baseDir, m.name, "assignments");
|
|
29677
30222
|
if (!await fileExists(assignmentsBase)) continue;
|
|
29678
30223
|
const slugs = await readdir21(assignmentsBase, { withFileTypes: true });
|
|
29679
30224
|
for (const a of slugs) {
|
|
29680
30225
|
if (!a.isDirectory()) continue;
|
|
29681
30226
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29682
|
-
const assignmentMd =
|
|
30227
|
+
const assignmentMd = resolve55(assignmentsBase, a.name, "assignment.md");
|
|
29683
30228
|
if (!await fileExists(assignmentMd)) continue;
|
|
29684
30229
|
if (seen.has(assignmentMd)) continue;
|
|
29685
30230
|
const fm = await parseSafe2(assignmentMd);
|
|
@@ -29723,7 +30268,7 @@ async function migrateStatusHistoryCommand(options) {
|
|
|
29723
30268
|
await withSuppressedEvents(async () => {
|
|
29724
30269
|
for (const t of targets) {
|
|
29725
30270
|
try {
|
|
29726
|
-
const content = await
|
|
30271
|
+
const content = await readFile37(t.assignmentMd, "utf-8");
|
|
29727
30272
|
if (parseAssignmentFrontmatter(content).statusHistory.length > 0) continue;
|
|
29728
30273
|
const seededContent = appendStatusHistoryEntry(content, {
|
|
29729
30274
|
at: t.seedAt,
|
|
@@ -29751,11 +30296,11 @@ init_fs();
|
|
|
29751
30296
|
init_config2();
|
|
29752
30297
|
init_parser();
|
|
29753
30298
|
init_events_db();
|
|
29754
|
-
import { resolve as
|
|
29755
|
-
import { readdir as readdir22, readFile as
|
|
30299
|
+
import { resolve as resolve56 } from "path";
|
|
30300
|
+
import { readdir as readdir22, readFile as readFile38 } from "fs/promises";
|
|
29756
30301
|
async function parseSafe3(path) {
|
|
29757
30302
|
try {
|
|
29758
|
-
return parseAssignmentFull(await
|
|
30303
|
+
return parseAssignmentFull(await readFile38(path, "utf-8"));
|
|
29759
30304
|
} catch {
|
|
29760
30305
|
return null;
|
|
29761
30306
|
}
|
|
@@ -29792,7 +30337,7 @@ async function collectTargets2(baseDirs) {
|
|
|
29792
30337
|
for (const m of entries) {
|
|
29793
30338
|
if (!m.isDirectory()) continue;
|
|
29794
30339
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
29795
|
-
const directAssignmentMd =
|
|
30340
|
+
const directAssignmentMd = resolve56(baseDir, m.name, "assignment.md");
|
|
29796
30341
|
if (await fileExists(directAssignmentMd)) {
|
|
29797
30342
|
if (seen.has(directAssignmentMd)) continue;
|
|
29798
30343
|
seen.add(directAssignmentMd);
|
|
@@ -29810,13 +30355,13 @@ async function collectTargets2(baseDirs) {
|
|
|
29810
30355
|
}
|
|
29811
30356
|
continue;
|
|
29812
30357
|
}
|
|
29813
|
-
const assignmentsBase =
|
|
30358
|
+
const assignmentsBase = resolve56(baseDir, m.name, "assignments");
|
|
29814
30359
|
if (!await fileExists(assignmentsBase)) continue;
|
|
29815
30360
|
const slugs = await readdir22(assignmentsBase, { withFileTypes: true });
|
|
29816
30361
|
for (const a of slugs) {
|
|
29817
30362
|
if (!a.isDirectory()) continue;
|
|
29818
30363
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
29819
|
-
const assignmentMd =
|
|
30364
|
+
const assignmentMd = resolve56(assignmentsBase, a.name, "assignment.md");
|
|
29820
30365
|
if (!await fileExists(assignmentMd)) continue;
|
|
29821
30366
|
if (seen.has(assignmentMd)) continue;
|
|
29822
30367
|
seen.add(assignmentMd);
|
|
@@ -29897,8 +30442,8 @@ init_frontmatter();
|
|
|
29897
30442
|
init_facts();
|
|
29898
30443
|
init_derive();
|
|
29899
30444
|
init_recompute();
|
|
29900
|
-
import { readdir as readdir23, readFile as
|
|
29901
|
-
import { resolve as
|
|
30445
|
+
import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
|
|
30446
|
+
import { resolve as resolve57 } from "path";
|
|
29902
30447
|
var IMPLEMENTATION_STATUSES = /* @__PURE__ */ new Set(["in_progress", "review", "code_review"]);
|
|
29903
30448
|
var REVIEW_STATUSES = /* @__PURE__ */ new Set(["review", "code_review"]);
|
|
29904
30449
|
async function listTargets(projectsDir2, standaloneDir) {
|
|
@@ -29909,15 +30454,15 @@ async function listTargets(projectsDir2, standaloneDir) {
|
|
|
29909
30454
|
} catch {
|
|
29910
30455
|
}
|
|
29911
30456
|
for (const project of projects) {
|
|
29912
|
-
const projectDir =
|
|
30457
|
+
const projectDir = resolve57(projectsDir2, project);
|
|
29913
30458
|
let slugs = [];
|
|
29914
30459
|
try {
|
|
29915
|
-
slugs = await readdir23(
|
|
30460
|
+
slugs = await readdir23(resolve57(projectDir, "assignments"));
|
|
29916
30461
|
} catch {
|
|
29917
30462
|
continue;
|
|
29918
30463
|
}
|
|
29919
30464
|
for (const slug of slugs) {
|
|
29920
|
-
const path =
|
|
30465
|
+
const path = resolve57(projectDir, "assignments", slug, "assignment.md");
|
|
29921
30466
|
if (await fileExists(path)) targets.push({ path, projectDir, ref: `${project}/${slug}` });
|
|
29922
30467
|
}
|
|
29923
30468
|
}
|
|
@@ -29927,7 +30472,7 @@ async function listTargets(projectsDir2, standaloneDir) {
|
|
|
29927
30472
|
} catch {
|
|
29928
30473
|
}
|
|
29929
30474
|
for (const id of ids) {
|
|
29930
|
-
const path =
|
|
30475
|
+
const path = resolve57(standaloneDir, id, "assignment.md");
|
|
29931
30476
|
if (await fileExists(path)) targets.push({ path, projectDir: null, ref: id });
|
|
29932
30477
|
}
|
|
29933
30478
|
return targets;
|
|
@@ -29982,7 +30527,7 @@ async function migrateDeriveCommand(options) {
|
|
|
29982
30527
|
for (const target of targets) {
|
|
29983
30528
|
let content;
|
|
29984
30529
|
try {
|
|
29985
|
-
content = await
|
|
30530
|
+
content = await readFile39(target.path, "utf-8");
|
|
29986
30531
|
} catch {
|
|
29987
30532
|
continue;
|
|
29988
30533
|
}
|
|
@@ -29997,7 +30542,7 @@ async function migrateDeriveCommand(options) {
|
|
|
29997
30542
|
const seededFm = parseAssignmentFrontmatter(seededContent);
|
|
29998
30543
|
const body = seededContent.replace(/^---\n[\s\S]*?\n---/, "");
|
|
29999
30544
|
const facts = await computeFacts({
|
|
30000
|
-
assignmentDir:
|
|
30545
|
+
assignmentDir: resolve57(target.path, ".."),
|
|
30001
30546
|
frontmatter: seededFm,
|
|
30002
30547
|
body,
|
|
30003
30548
|
projectDir: target.projectDir,
|
|
@@ -30057,7 +30602,7 @@ async function migrateDeriveCommand(options) {
|
|
|
30057
30602
|
}
|
|
30058
30603
|
|
|
30059
30604
|
// src/commands/complete.ts
|
|
30060
|
-
import { resolve as
|
|
30605
|
+
import { resolve as resolve58 } from "path";
|
|
30061
30606
|
init_config2();
|
|
30062
30607
|
init_paths();
|
|
30063
30608
|
init_assignment_resolver();
|
|
@@ -30071,12 +30616,12 @@ async function completeCommand(assignment, options) {
|
|
|
30071
30616
|
let projectDir;
|
|
30072
30617
|
let changedSlug;
|
|
30073
30618
|
if (options.project) {
|
|
30074
|
-
projectDir =
|
|
30619
|
+
projectDir = resolve58(baseDir, options.project);
|
|
30075
30620
|
changedSlug = assignment;
|
|
30076
30621
|
} else {
|
|
30077
30622
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30078
30623
|
if (!resolved) return;
|
|
30079
|
-
projectDir = resolved.standalone ? null :
|
|
30624
|
+
projectDir = resolved.standalone ? null : resolve58(resolved.assignmentDir, "..", "..");
|
|
30080
30625
|
changedSlug = resolved.assignmentSlug;
|
|
30081
30626
|
}
|
|
30082
30627
|
if (projectDir) {
|
|
@@ -30107,7 +30652,7 @@ async function reviewCommand(assignment, options) {
|
|
|
30107
30652
|
}
|
|
30108
30653
|
|
|
30109
30654
|
// src/commands/fail.ts
|
|
30110
|
-
import { resolve as
|
|
30655
|
+
import { resolve as resolve59 } from "path";
|
|
30111
30656
|
init_config2();
|
|
30112
30657
|
init_paths();
|
|
30113
30658
|
init_assignment_resolver();
|
|
@@ -30121,12 +30666,12 @@ async function failCommand(assignment, options) {
|
|
|
30121
30666
|
let projectDir;
|
|
30122
30667
|
let changedSlug;
|
|
30123
30668
|
if (options.project) {
|
|
30124
|
-
projectDir =
|
|
30669
|
+
projectDir = resolve59(baseDir, options.project);
|
|
30125
30670
|
changedSlug = assignment;
|
|
30126
30671
|
} else {
|
|
30127
30672
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30128
30673
|
if (!resolved) return;
|
|
30129
|
-
projectDir = resolved.standalone ? null :
|
|
30674
|
+
projectDir = resolved.standalone ? null : resolve59(resolved.assignmentDir, "..", "..");
|
|
30130
30675
|
changedSlug = resolved.assignmentSlug;
|
|
30131
30676
|
}
|
|
30132
30677
|
if (projectDir) {
|
|
@@ -30142,7 +30687,7 @@ async function failCommand(assignment, options) {
|
|
|
30142
30687
|
}
|
|
30143
30688
|
|
|
30144
30689
|
// src/commands/reopen.ts
|
|
30145
|
-
import { resolve as
|
|
30690
|
+
import { resolve as resolve60 } from "path";
|
|
30146
30691
|
init_config2();
|
|
30147
30692
|
init_paths();
|
|
30148
30693
|
init_assignment_resolver();
|
|
@@ -30158,14 +30703,14 @@ async function reopenCommand(assignment, options) {
|
|
|
30158
30703
|
let projectDir;
|
|
30159
30704
|
let changedSlug;
|
|
30160
30705
|
if (options.project) {
|
|
30161
|
-
projectDir =
|
|
30162
|
-
assignmentPath =
|
|
30706
|
+
projectDir = resolve60(baseDir, options.project);
|
|
30707
|
+
assignmentPath = resolve60(projectDir, "assignments", assignment, "assignment.md");
|
|
30163
30708
|
changedSlug = assignment;
|
|
30164
30709
|
} else {
|
|
30165
30710
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), assignment);
|
|
30166
30711
|
if (!resolved) return;
|
|
30167
|
-
assignmentPath =
|
|
30168
|
-
projectDir = resolved.standalone ? null :
|
|
30712
|
+
assignmentPath = resolve60(resolved.assignmentDir, "assignment.md");
|
|
30713
|
+
projectDir = resolved.standalone ? null : resolve60(resolved.assignmentDir, "..", "..");
|
|
30169
30714
|
changedSlug = resolved.assignmentSlug;
|
|
30170
30715
|
}
|
|
30171
30716
|
const derived = await recomputeAndWrite(assignmentPath, {
|
|
@@ -30199,7 +30744,7 @@ import {
|
|
|
30199
30744
|
readdir as readdir24,
|
|
30200
30745
|
symlink,
|
|
30201
30746
|
lstat,
|
|
30202
|
-
readFile as
|
|
30747
|
+
readFile as readFile40,
|
|
30203
30748
|
readlink,
|
|
30204
30749
|
rename as rename9,
|
|
30205
30750
|
rm as rm6,
|
|
@@ -30208,20 +30753,20 @@ import {
|
|
|
30208
30753
|
} from "fs/promises";
|
|
30209
30754
|
import { existsSync as existsSync4 } from "fs";
|
|
30210
30755
|
import { homedir as homedir8 } from "os";
|
|
30211
|
-
import { basename as
|
|
30756
|
+
import { basename as basename8, dirname as dirname15, isAbsolute as isAbsolute7, relative as relative2, resolve as resolve62 } from "path";
|
|
30212
30757
|
|
|
30213
30758
|
// src/utils/package-root.ts
|
|
30214
30759
|
init_fs();
|
|
30215
|
-
import { dirname as dirname14, resolve as
|
|
30760
|
+
import { dirname as dirname14, resolve as resolve61 } from "path";
|
|
30216
30761
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
30217
30762
|
async function findPackageRoot(expectedRelativePath) {
|
|
30218
30763
|
let currentDir = dirname14(fileURLToPath4(import.meta.url));
|
|
30219
30764
|
while (true) {
|
|
30220
|
-
const candidate =
|
|
30765
|
+
const candidate = resolve61(currentDir, expectedRelativePath);
|
|
30221
30766
|
if (await fileExists(candidate)) {
|
|
30222
30767
|
return currentDir;
|
|
30223
30768
|
}
|
|
30224
|
-
const parentDir =
|
|
30769
|
+
const parentDir = resolve61(currentDir, "..");
|
|
30225
30770
|
if (parentDir === currentDir) {
|
|
30226
30771
|
throw new Error(
|
|
30227
30772
|
`Could not locate package root containing ${expectedRelativePath}.`
|
|
@@ -30242,25 +30787,25 @@ function getPluginManifestRelativePath(pluginKind) {
|
|
|
30242
30787
|
}
|
|
30243
30788
|
function getDefaultPluginTargetDir(pluginKind) {
|
|
30244
30789
|
const home2 = homedir8();
|
|
30245
|
-
return pluginKind === "claude" ?
|
|
30790
|
+
return pluginKind === "claude" ? resolve62(home2, ".claude", "plugins", "syntaur") : resolve62(home2, "plugins", "syntaur");
|
|
30246
30791
|
}
|
|
30247
30792
|
function getDefaultMarketplacePath() {
|
|
30248
|
-
return
|
|
30793
|
+
return resolve62(homedir8(), ".agents", "plugins", "marketplace.json");
|
|
30249
30794
|
}
|
|
30250
30795
|
function getClaudeMarketplacesRoot() {
|
|
30251
|
-
return
|
|
30796
|
+
return resolve62(homedir8(), ".claude", "plugins", "marketplaces");
|
|
30252
30797
|
}
|
|
30253
30798
|
function getClaudeKnownMarketplacesPath() {
|
|
30254
|
-
return
|
|
30799
|
+
return resolve62(homedir8(), ".claude", "plugins", "known_marketplaces.json");
|
|
30255
30800
|
}
|
|
30256
30801
|
function getClaudeInstalledPluginsPath() {
|
|
30257
|
-
return
|
|
30802
|
+
return resolve62(homedir8(), ".claude", "plugins", "installed_plugins.json");
|
|
30258
30803
|
}
|
|
30259
30804
|
function getInstallMarkerPath(targetDir) {
|
|
30260
|
-
return
|
|
30805
|
+
return resolve62(targetDir, INSTALL_MARKER_FILENAME);
|
|
30261
30806
|
}
|
|
30262
30807
|
async function readPackageManifest(packageRoot) {
|
|
30263
|
-
const raw2 = await
|
|
30808
|
+
const raw2 = await readFile40(resolve62(packageRoot, "package.json"), "utf-8");
|
|
30264
30809
|
return JSON.parse(raw2);
|
|
30265
30810
|
}
|
|
30266
30811
|
async function readJsonFileIfExists(pathValue) {
|
|
@@ -30268,7 +30813,7 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
30268
30813
|
return null;
|
|
30269
30814
|
}
|
|
30270
30815
|
try {
|
|
30271
|
-
const raw2 = await
|
|
30816
|
+
const raw2 = await readFile40(pathValue, "utf-8");
|
|
30272
30817
|
return JSON.parse(raw2);
|
|
30273
30818
|
} catch {
|
|
30274
30819
|
return null;
|
|
@@ -30276,15 +30821,15 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
30276
30821
|
}
|
|
30277
30822
|
async function readClaudePluginManifest(pluginDir) {
|
|
30278
30823
|
return await readJsonFileIfExists(
|
|
30279
|
-
|
|
30824
|
+
resolve62(pluginDir, ".claude-plugin", "plugin.json")
|
|
30280
30825
|
) ?? {};
|
|
30281
30826
|
}
|
|
30282
30827
|
async function readPluginManifestName(targetDir, pluginKind) {
|
|
30283
|
-
const manifestPath =
|
|
30828
|
+
const manifestPath = resolve62(targetDir, getPluginManifestRelativePath(pluginKind));
|
|
30284
30829
|
if (!await fileExists(manifestPath)) {
|
|
30285
30830
|
return void 0;
|
|
30286
30831
|
}
|
|
30287
|
-
const raw2 = await
|
|
30832
|
+
const raw2 = await readFile40(manifestPath, "utf-8");
|
|
30288
30833
|
const parsed = JSON.parse(raw2);
|
|
30289
30834
|
return parsed.name;
|
|
30290
30835
|
}
|
|
@@ -30294,7 +30839,7 @@ async function readInstallMetadata(targetDir) {
|
|
|
30294
30839
|
return null;
|
|
30295
30840
|
}
|
|
30296
30841
|
try {
|
|
30297
|
-
const raw2 = await
|
|
30842
|
+
const raw2 = await readFile40(markerPath, "utf-8");
|
|
30298
30843
|
return JSON.parse(raw2);
|
|
30299
30844
|
} catch {
|
|
30300
30845
|
return null;
|
|
@@ -30307,7 +30852,7 @@ async function getInstallStatus(targetDir, pluginKind) {
|
|
|
30307
30852
|
const info = await lstat(targetDir);
|
|
30308
30853
|
if (info.isSymbolicLink()) {
|
|
30309
30854
|
const symlinkTarget = await readlink(targetDir);
|
|
30310
|
-
const resolvedTarget =
|
|
30855
|
+
const resolvedTarget = resolve62(dirname15(targetDir), symlinkTarget);
|
|
30311
30856
|
const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
|
|
30312
30857
|
return {
|
|
30313
30858
|
exists: true,
|
|
@@ -30353,7 +30898,7 @@ async function installLink(paths) {
|
|
|
30353
30898
|
await ensureDir(dirname15(paths.targetDir));
|
|
30354
30899
|
await rm6(paths.targetDir, { recursive: true, force: true });
|
|
30355
30900
|
await ensureDir(dirname15(paths.targetDir));
|
|
30356
|
-
await symlink(
|
|
30901
|
+
await symlink(resolve62(paths.sourceDir), paths.targetDir, "dir");
|
|
30357
30902
|
}
|
|
30358
30903
|
async function removeInstallMarker(targetDir) {
|
|
30359
30904
|
const markerPath = getInstallMarkerPath(targetDir);
|
|
@@ -30367,13 +30912,13 @@ function normalizeAbsoluteInstallPath(pathValue, label) {
|
|
|
30367
30912
|
if (!isAbsolute7(expanded)) {
|
|
30368
30913
|
throw new Error(`${label} must be an absolute path.`);
|
|
30369
30914
|
}
|
|
30370
|
-
return
|
|
30915
|
+
return resolve62(expanded);
|
|
30371
30916
|
}
|
|
30372
30917
|
async function resolvePluginPaths(pluginKind, targetDir) {
|
|
30373
30918
|
const packageRoot = await findPackageRoot(getPluginRelativePath(pluginKind));
|
|
30374
30919
|
return {
|
|
30375
30920
|
packageRoot,
|
|
30376
|
-
sourceDir:
|
|
30921
|
+
sourceDir: resolve62(packageRoot, getPluginRelativePath(pluginKind)),
|
|
30377
30922
|
targetDir: targetDir ?? getDefaultPluginTargetDir(pluginKind)
|
|
30378
30923
|
};
|
|
30379
30924
|
}
|
|
@@ -30417,7 +30962,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
|
30417
30962
|
}
|
|
30418
30963
|
if (await fileExists(manifestPath)) {
|
|
30419
30964
|
try {
|
|
30420
|
-
const prev = await
|
|
30965
|
+
const prev = await readFile40(manifestPath, "utf-8");
|
|
30421
30966
|
JSON.parse(prev);
|
|
30422
30967
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30423
30968
|
await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
@@ -30498,8 +31043,8 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
30498
31043
|
if (!entry.isDirectory()) {
|
|
30499
31044
|
continue;
|
|
30500
31045
|
}
|
|
30501
|
-
const candidateRoot =
|
|
30502
|
-
const manifestPath =
|
|
31046
|
+
const candidateRoot = resolve62(rootDir, entry.name);
|
|
31047
|
+
const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
30503
31048
|
if (!await fileExists(manifestPath)) {
|
|
30504
31049
|
continue;
|
|
30505
31050
|
}
|
|
@@ -30526,11 +31071,11 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
30526
31071
|
if (!installLocation) {
|
|
30527
31072
|
continue;
|
|
30528
31073
|
}
|
|
30529
|
-
const candidateRoot =
|
|
31074
|
+
const candidateRoot = resolve62(expandHome(installLocation));
|
|
30530
31075
|
if (seen.has(candidateRoot)) {
|
|
30531
31076
|
continue;
|
|
30532
31077
|
}
|
|
30533
|
-
const manifestPath =
|
|
31078
|
+
const manifestPath = resolve62(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
30534
31079
|
if (!await fileExists(manifestPath)) {
|
|
30535
31080
|
continue;
|
|
30536
31081
|
}
|
|
@@ -30558,14 +31103,14 @@ async function getPreferredClaudeMarketplace() {
|
|
|
30558
31103
|
name: candidate.name,
|
|
30559
31104
|
rootDir: candidate.rootDir,
|
|
30560
31105
|
manifestPath: candidate.manifestPath,
|
|
30561
|
-
targetDir:
|
|
31106
|
+
targetDir: resolve62(candidate.rootDir, "plugins", "syntaur")
|
|
30562
31107
|
};
|
|
30563
31108
|
}
|
|
30564
31109
|
async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
30565
31110
|
const manifestPath = getClaudeKnownMarketplacesPath();
|
|
30566
31111
|
let existing = {};
|
|
30567
31112
|
if (await fileExists(manifestPath)) {
|
|
30568
|
-
const raw2 = await
|
|
31113
|
+
const raw2 = await readFile40(manifestPath, "utf-8");
|
|
30569
31114
|
try {
|
|
30570
31115
|
const parsed = JSON.parse(raw2);
|
|
30571
31116
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -30592,7 +31137,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
30592
31137
|
existing[name].autoUpdate = true;
|
|
30593
31138
|
await ensureDir(dirname15(manifestPath));
|
|
30594
31139
|
if (await fileExists(manifestPath)) {
|
|
30595
|
-
const prev = await
|
|
31140
|
+
const prev = await readFile40(manifestPath, "utf-8");
|
|
30596
31141
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30597
31142
|
await writeFile10(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
30598
31143
|
}
|
|
@@ -30606,11 +31151,11 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
|
|
|
30606
31151
|
return registerKnownClaudeMarketplace(options.name, options.rootDir);
|
|
30607
31152
|
}
|
|
30608
31153
|
async function setSyntaurPluginEnabled(options) {
|
|
30609
|
-
const settingsPath =
|
|
31154
|
+
const settingsPath = resolve62(homedir8(), ".claude", "settings.json");
|
|
30610
31155
|
const key = `syntaur@${options.marketplaceName}`;
|
|
30611
31156
|
let parsed = {};
|
|
30612
31157
|
if (await fileExists(settingsPath)) {
|
|
30613
|
-
const raw2 = await
|
|
31158
|
+
const raw2 = await readFile40(settingsPath, "utf-8");
|
|
30614
31159
|
try {
|
|
30615
31160
|
parsed = JSON.parse(raw2);
|
|
30616
31161
|
} catch {
|
|
@@ -30629,7 +31174,7 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
30629
31174
|
await ensureDir(dirname15(settingsPath));
|
|
30630
31175
|
if (await fileExists(settingsPath)) {
|
|
30631
31176
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
30632
|
-
const prev = await
|
|
31177
|
+
const prev = await readFile40(settingsPath, "utf-8");
|
|
30633
31178
|
await writeFile10(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
|
|
30634
31179
|
}
|
|
30635
31180
|
const tmpPath = `${settingsPath}.tmp`;
|
|
@@ -30643,9 +31188,9 @@ async function ensureClaudeUserMarketplace() {
|
|
|
30643
31188
|
if (existing) {
|
|
30644
31189
|
return existing;
|
|
30645
31190
|
}
|
|
30646
|
-
const rootDir =
|
|
30647
|
-
const manifestPath =
|
|
30648
|
-
await ensureDir(
|
|
31191
|
+
const rootDir = resolve62(getClaudeMarketplacesRoot(), "user-plugins");
|
|
31192
|
+
const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
|
|
31193
|
+
await ensureDir(resolve62(rootDir, "plugins"));
|
|
30649
31194
|
if (!await fileExists(manifestPath)) {
|
|
30650
31195
|
const scaffold = {
|
|
30651
31196
|
plugins: []
|
|
@@ -30664,22 +31209,22 @@ async function ensureClaudeUserMarketplace() {
|
|
|
30664
31209
|
name: "user-plugins",
|
|
30665
31210
|
rootDir,
|
|
30666
31211
|
manifestPath,
|
|
30667
|
-
targetDir:
|
|
31212
|
+
targetDir: resolve62(rootDir, "plugins", "syntaur")
|
|
30668
31213
|
};
|
|
30669
31214
|
}
|
|
30670
31215
|
async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
30671
31216
|
const normalizedTargetDir = normalizeAbsoluteInstallPath(targetDir, "Claude plugin target");
|
|
30672
31217
|
const pluginsDir = dirname15(normalizedTargetDir);
|
|
30673
|
-
if (
|
|
31218
|
+
if (basename8(pluginsDir) !== "plugins") {
|
|
30674
31219
|
return null;
|
|
30675
31220
|
}
|
|
30676
31221
|
const rootDir = dirname15(pluginsDir);
|
|
30677
|
-
const manifestPath =
|
|
31222
|
+
const manifestPath = resolve62(rootDir, ".claude-plugin", "marketplace.json");
|
|
30678
31223
|
if (!await fileExists(manifestPath)) {
|
|
30679
31224
|
return null;
|
|
30680
31225
|
}
|
|
30681
31226
|
const marketplace = await readClaudeMarketplaceFile(manifestPath);
|
|
30682
|
-
const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name :
|
|
31227
|
+
const name = typeof marketplace.name === "string" && marketplace.name.trim() !== "" ? marketplace.name : basename8(rootDir);
|
|
30683
31228
|
return {
|
|
30684
31229
|
name,
|
|
30685
31230
|
rootDir,
|
|
@@ -30690,7 +31235,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
|
30690
31235
|
async function findManagedClaudeMarketplacePluginDir() {
|
|
30691
31236
|
const marketplaces = await listClaudeMarketplaceCandidates();
|
|
30692
31237
|
for (const marketplace of marketplaces) {
|
|
30693
|
-
const targetDir =
|
|
31238
|
+
const targetDir = resolve62(marketplace.rootDir, "plugins", "syntaur");
|
|
30694
31239
|
const status = await getInstallStatus(targetDir, "claude");
|
|
30695
31240
|
if (status.exists && status.managed) {
|
|
30696
31241
|
return targetDir;
|
|
@@ -30815,7 +31360,7 @@ async function installManagedPlugin(options) {
|
|
|
30815
31360
|
`${paths.targetDir} already exists and is not a Syntaur-managed install. Remove it manually before installing Syntaur there.`
|
|
30816
31361
|
);
|
|
30817
31362
|
}
|
|
30818
|
-
if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget ===
|
|
31363
|
+
if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget === resolve62(paths.sourceDir) && !force) {
|
|
30819
31364
|
return {
|
|
30820
31365
|
targetDir: paths.targetDir,
|
|
30821
31366
|
sourceDir: paths.sourceDir,
|
|
@@ -30867,7 +31412,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
30867
31412
|
plugins: []
|
|
30868
31413
|
};
|
|
30869
31414
|
}
|
|
30870
|
-
const raw2 = await
|
|
31415
|
+
const raw2 = await readFile40(marketplacePath, "utf-8");
|
|
30871
31416
|
const parsed = JSON.parse(raw2);
|
|
30872
31417
|
return {
|
|
30873
31418
|
name: parsed.name ?? "local",
|
|
@@ -31034,13 +31579,13 @@ async function recommendMarketplacePath() {
|
|
|
31034
31579
|
return configuredOrManaged ?? getDefaultMarketplacePath();
|
|
31035
31580
|
}
|
|
31036
31581
|
async function isSyntaurDataInstalled() {
|
|
31037
|
-
return fileExists(
|
|
31582
|
+
return fileExists(resolve62(syntaurRoot(), "config.md"));
|
|
31038
31583
|
}
|
|
31039
31584
|
async function removeSyntaurData() {
|
|
31040
31585
|
await rm6(syntaurRoot(), { recursive: true, force: true });
|
|
31041
31586
|
}
|
|
31042
31587
|
async function getConfiguredProjectDir() {
|
|
31043
|
-
if (!await fileExists(
|
|
31588
|
+
if (!await fileExists(resolve62(syntaurRoot(), "config.md"))) {
|
|
31044
31589
|
return null;
|
|
31045
31590
|
}
|
|
31046
31591
|
return (await readConfig()).defaultProjectDir;
|
|
@@ -31109,24 +31654,24 @@ async function textPrompt(question, defaultValue) {
|
|
|
31109
31654
|
|
|
31110
31655
|
// src/utils/install-skills.ts
|
|
31111
31656
|
init_fs();
|
|
31112
|
-
import { readFile as
|
|
31113
|
-
import { resolve as
|
|
31657
|
+
import { readFile as readFile42, readdir as readdir25, mkdir as mkdir7, copyFile, rm as rm7, lstat as lstat2 } from "fs/promises";
|
|
31658
|
+
import { resolve as resolve64, relative as relative3, join as join11 } from "path";
|
|
31114
31659
|
import { homedir as homedir10 } from "os";
|
|
31115
31660
|
|
|
31116
31661
|
// src/utils/plugin-state.ts
|
|
31117
31662
|
init_fs();
|
|
31118
|
-
import { readFile as
|
|
31119
|
-
import { resolve as
|
|
31663
|
+
import { readFile as readFile41 } from "fs/promises";
|
|
31664
|
+
import { resolve as resolve63 } from "path";
|
|
31120
31665
|
import { homedir as homedir9 } from "os";
|
|
31121
31666
|
function settingsPathFor(agent) {
|
|
31122
|
-
if (agent === "claude") return
|
|
31667
|
+
if (agent === "claude") return resolve63(homedir9(), ".claude", "settings.json");
|
|
31123
31668
|
return null;
|
|
31124
31669
|
}
|
|
31125
31670
|
async function readJsonOrNull(path) {
|
|
31126
31671
|
if (!path) return null;
|
|
31127
31672
|
if (!await fileExists(path)) return null;
|
|
31128
31673
|
try {
|
|
31129
|
-
const raw2 = await
|
|
31674
|
+
const raw2 = await readFile41(path, "utf-8");
|
|
31130
31675
|
return JSON.parse(raw2);
|
|
31131
31676
|
} catch {
|
|
31132
31677
|
return null;
|
|
@@ -31176,11 +31721,11 @@ var KNOWN_SKILL_NAMES = [
|
|
|
31176
31721
|
var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
|
|
31177
31722
|
async function getSkillsDir() {
|
|
31178
31723
|
const packageRoot = await findPackageRoot("skills");
|
|
31179
|
-
return
|
|
31724
|
+
return resolve64(packageRoot, "skills");
|
|
31180
31725
|
}
|
|
31181
31726
|
function defaultSkillTargetDir(target) {
|
|
31182
|
-
if (target === "claude") return
|
|
31183
|
-
return
|
|
31727
|
+
if (target === "claude") return resolve64(homedir10(), ".claude", "skills");
|
|
31728
|
+
return resolve64(homedir10(), ".codex", "skills");
|
|
31184
31729
|
}
|
|
31185
31730
|
async function walkFiles(root) {
|
|
31186
31731
|
const out = [];
|
|
@@ -31200,7 +31745,7 @@ async function walkFiles(root) {
|
|
|
31200
31745
|
}
|
|
31201
31746
|
async function filesEqual(a, b) {
|
|
31202
31747
|
try {
|
|
31203
|
-
const [ba, bb] = await Promise.all([
|
|
31748
|
+
const [ba, bb] = await Promise.all([readFile42(a), readFile42(b)]);
|
|
31204
31749
|
if (ba.length !== bb.length) return false;
|
|
31205
31750
|
return ba.equals(bb);
|
|
31206
31751
|
} catch {
|
|
@@ -31234,8 +31779,8 @@ async function skillMatches(srcDir, destDir) {
|
|
|
31234
31779
|
}
|
|
31235
31780
|
async function isSymlink(path) {
|
|
31236
31781
|
try {
|
|
31237
|
-
const
|
|
31238
|
-
return
|
|
31782
|
+
const stat17 = await lstat2(path);
|
|
31783
|
+
return stat17.isSymbolicLink();
|
|
31239
31784
|
} catch {
|
|
31240
31785
|
return false;
|
|
31241
31786
|
}
|
|
@@ -31340,7 +31885,7 @@ async function uninstallSkills(options) {
|
|
|
31340
31885
|
if (await isSymlink(destDir)) continue;
|
|
31341
31886
|
const skillMd = join11(destDir, "SKILL.md");
|
|
31342
31887
|
if (!await fileExists(skillMd)) continue;
|
|
31343
|
-
const content = await
|
|
31888
|
+
const content = await readFile42(skillMd, "utf-8").catch(() => "");
|
|
31344
31889
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
31345
31890
|
if (!match || match[1] !== skill) continue;
|
|
31346
31891
|
await rm7(destDir, { recursive: true, force: true });
|
|
@@ -31535,20 +32080,20 @@ import { realpathSync as realpathSync2 } from "fs";
|
|
|
31535
32080
|
init_paths();
|
|
31536
32081
|
init_fs();
|
|
31537
32082
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
31538
|
-
import { readFile as
|
|
31539
|
-
import { dirname as dirname17, join as join13, resolve as
|
|
32083
|
+
import { readFile as readFile44 } from "fs/promises";
|
|
32084
|
+
import { dirname as dirname17, join as join13, resolve as resolve65 } from "path";
|
|
31540
32085
|
import { spawn as spawn6 } from "child_process";
|
|
31541
32086
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
31542
32087
|
|
|
31543
32088
|
// src/utils/version.ts
|
|
31544
32089
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
31545
|
-
import { readFile as
|
|
32090
|
+
import { readFile as readFile43 } from "fs/promises";
|
|
31546
32091
|
import { dirname as dirname16, join as join12 } from "path";
|
|
31547
32092
|
async function readPackageVersion(scriptUrl) {
|
|
31548
32093
|
try {
|
|
31549
32094
|
const scriptPath = fileURLToPath5(scriptUrl);
|
|
31550
32095
|
const pkgRoot = dirname16(dirname16(scriptPath));
|
|
31551
|
-
const raw2 = await
|
|
32096
|
+
const raw2 = await readFile43(join12(pkgRoot, "package.json"), "utf-8");
|
|
31552
32097
|
const parsed = JSON.parse(raw2);
|
|
31553
32098
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
31554
32099
|
} catch {
|
|
@@ -31557,7 +32102,7 @@ async function readPackageVersion(scriptUrl) {
|
|
|
31557
32102
|
}
|
|
31558
32103
|
|
|
31559
32104
|
// src/utils/npx-prompt.ts
|
|
31560
|
-
var STATE_FILE =
|
|
32105
|
+
var STATE_FILE = resolve65(syntaurRoot(), "npx-install.json");
|
|
31561
32106
|
var META_ARGS2 = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
|
|
31562
32107
|
var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
|
|
31563
32108
|
function isRunningViaNpx(scriptUrl) {
|
|
@@ -31578,7 +32123,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
31578
32123
|
async function readState() {
|
|
31579
32124
|
if (!await fileExists(STATE_FILE)) return null;
|
|
31580
32125
|
try {
|
|
31581
|
-
const raw2 = await
|
|
32126
|
+
const raw2 = await readFile44(STATE_FILE, "utf-8");
|
|
31582
32127
|
return JSON.parse(raw2);
|
|
31583
32128
|
} catch {
|
|
31584
32129
|
return null;
|
|
@@ -31637,7 +32182,7 @@ async function readGlobalVersion() {
|
|
|
31637
32182
|
try {
|
|
31638
32183
|
const manifestPath = join13(rootPath, "syntaur", "package.json");
|
|
31639
32184
|
if (!await fileExists(manifestPath)) return null;
|
|
31640
|
-
const raw2 = await
|
|
32185
|
+
const raw2 = await readFile44(manifestPath, "utf-8");
|
|
31641
32186
|
const parsed = JSON.parse(raw2);
|
|
31642
32187
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
31643
32188
|
} catch {
|
|
@@ -31995,16 +32540,16 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
|
|
|
31995
32540
|
// src/commands/install-statusline.ts
|
|
31996
32541
|
init_paths();
|
|
31997
32542
|
init_fs();
|
|
31998
|
-
import { readFile as
|
|
31999
|
-
import { resolve as
|
|
32543
|
+
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";
|
|
32544
|
+
import { resolve as resolve67, dirname as dirname19 } from "path";
|
|
32000
32545
|
import { homedir as homedir12 } from "os";
|
|
32001
32546
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
32002
32547
|
|
|
32003
32548
|
// src/commands/configure-statusline.ts
|
|
32004
32549
|
init_paths();
|
|
32005
32550
|
init_fs();
|
|
32006
|
-
import { readFile as
|
|
32007
|
-
import { resolve as
|
|
32551
|
+
import { readFile as readFile45, writeFile as writeFile11 } from "fs/promises";
|
|
32552
|
+
import { resolve as resolve66, dirname as dirname18 } from "path";
|
|
32008
32553
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
32009
32554
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
32010
32555
|
var AVAILABLE_SEGMENTS = [
|
|
@@ -32025,12 +32570,12 @@ var PRESETS = {
|
|
|
32025
32570
|
tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
|
|
32026
32571
|
};
|
|
32027
32572
|
function getConfigPath(installRoot) {
|
|
32028
|
-
return
|
|
32573
|
+
return resolve66(installRoot, "statusline.config.json");
|
|
32029
32574
|
}
|
|
32030
32575
|
async function readConfig2(path) {
|
|
32031
32576
|
if (!await fileExists(path)) return null;
|
|
32032
32577
|
try {
|
|
32033
|
-
const raw2 = await
|
|
32578
|
+
const raw2 = await readFile45(path, "utf-8");
|
|
32034
32579
|
const parsed = JSON.parse(raw2);
|
|
32035
32580
|
if (!parsed || typeof parsed !== "object") return null;
|
|
32036
32581
|
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
@@ -32183,7 +32728,7 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
32183
32728
|
console.log(` segments: ${config.segments.join(", ")}`);
|
|
32184
32729
|
console.log(` separator: ${JSON.stringify(config.separator)}`);
|
|
32185
32730
|
if (config.wrap) console.log(` wrap: ${config.wrap}`);
|
|
32186
|
-
const script = options.statuslineScript ??
|
|
32731
|
+
const script = options.statuslineScript ?? resolve66(installRoot, "statusline.sh");
|
|
32187
32732
|
if (await fileExists(script)) {
|
|
32188
32733
|
console.log("");
|
|
32189
32734
|
console.log("Live preview:");
|
|
@@ -32214,11 +32759,11 @@ async function writeDefaultConfigIfMissing(installRoot) {
|
|
|
32214
32759
|
// src/commands/install-statusline.ts
|
|
32215
32760
|
function getPackageStatuslineSource() {
|
|
32216
32761
|
const here = dirname19(fileURLToPath8(import.meta.url));
|
|
32217
|
-
return
|
|
32762
|
+
return resolve67(here, "..", "statusline", "statusline.sh");
|
|
32218
32763
|
}
|
|
32219
32764
|
async function readSettingsJson(settingsPath) {
|
|
32220
32765
|
if (!await fileExists(settingsPath)) return {};
|
|
32221
|
-
const raw2 = await
|
|
32766
|
+
const raw2 = await readFile46(settingsPath, "utf-8");
|
|
32222
32767
|
if (raw2.trim() === "") return {};
|
|
32223
32768
|
try {
|
|
32224
32769
|
const parsed = JSON.parse(raw2);
|
|
@@ -32298,12 +32843,12 @@ async function installScript(sourceScript, destScript, link) {
|
|
|
32298
32843
|
}
|
|
32299
32844
|
async function installStatuslineCommand(options = {}) {
|
|
32300
32845
|
const mode = options.mode ?? "ask";
|
|
32301
|
-
const settingsPath = options.settingsPath ??
|
|
32846
|
+
const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
|
|
32302
32847
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
32303
32848
|
const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
|
|
32304
|
-
const destScript =
|
|
32305
|
-
const confPath =
|
|
32306
|
-
const backupPath =
|
|
32849
|
+
const destScript = resolve67(installRoot, "statusline.sh");
|
|
32850
|
+
const confPath = resolve67(installRoot, "statusline.conf");
|
|
32851
|
+
const backupPath = resolve67(installRoot, "statusline.backup.json");
|
|
32307
32852
|
if (!await fileExists(sourceScript)) {
|
|
32308
32853
|
throw new Error(
|
|
32309
32854
|
`Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
|
|
@@ -32339,7 +32884,7 @@ async function installStatuslineCommand(options = {}) {
|
|
|
32339
32884
|
if (parsed) {
|
|
32340
32885
|
wrapTarget = parsed;
|
|
32341
32886
|
} else {
|
|
32342
|
-
const wrapperPath =
|
|
32887
|
+
const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
|
|
32343
32888
|
const wrapperBody = `#!/usr/bin/env bash
|
|
32344
32889
|
# Auto-generated by syntaur install-statusline.
|
|
32345
32890
|
# Executes the previously configured statusLine command.
|
|
@@ -32388,25 +32933,25 @@ function parseWrapCommand(command) {
|
|
|
32388
32933
|
async function chmodExec(path) {
|
|
32389
32934
|
const fs = await import("fs/promises");
|
|
32390
32935
|
try {
|
|
32391
|
-
const s = await
|
|
32936
|
+
const s = await stat8(path);
|
|
32392
32937
|
await fs.chmod(path, s.mode | 73);
|
|
32393
32938
|
} catch {
|
|
32394
32939
|
}
|
|
32395
32940
|
}
|
|
32396
32941
|
async function uninstallStatuslineCommand(options = {}) {
|
|
32397
|
-
const settingsPath = options.settingsPath ??
|
|
32942
|
+
const settingsPath = options.settingsPath ?? resolve67(homedir12(), ".claude", "settings.json");
|
|
32398
32943
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
32399
|
-
const destScript =
|
|
32400
|
-
const confPath =
|
|
32401
|
-
const backupPath =
|
|
32402
|
-
const wrapperPath =
|
|
32944
|
+
const destScript = resolve67(installRoot, "statusline.sh");
|
|
32945
|
+
const confPath = resolve67(installRoot, "statusline.conf");
|
|
32946
|
+
const backupPath = resolve67(installRoot, "statusline.backup.json");
|
|
32947
|
+
const wrapperPath = resolve67(installRoot, "statusline-wrapped.sh");
|
|
32403
32948
|
const settings = await readSettingsJson(settingsPath);
|
|
32404
32949
|
const existing = extractExistingCommand(settings);
|
|
32405
32950
|
const ourCommand = `bash ${destScript}`;
|
|
32406
32951
|
let restored = null;
|
|
32407
32952
|
if (await fileExists(backupPath)) {
|
|
32408
32953
|
try {
|
|
32409
|
-
const raw2 = await
|
|
32954
|
+
const raw2 = await readFile46(backupPath, "utf-8");
|
|
32410
32955
|
const parsed = JSON.parse(raw2);
|
|
32411
32956
|
const prev = parsed?.previousStatusLine;
|
|
32412
32957
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -32427,7 +32972,7 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
32427
32972
|
await writeSettingsJson(settingsPath, settings);
|
|
32428
32973
|
}
|
|
32429
32974
|
if (!options.keepScript) {
|
|
32430
|
-
const configPath2 =
|
|
32975
|
+
const configPath2 = resolve67(installRoot, "statusline.config.json");
|
|
32431
32976
|
for (const path of [destScript, confPath, backupPath, wrapperPath, configPath2]) {
|
|
32432
32977
|
try {
|
|
32433
32978
|
await rm8(path, { force: true });
|
|
@@ -32587,8 +33132,8 @@ init_config2();
|
|
|
32587
33132
|
// src/commands/cross-agent-install.ts
|
|
32588
33133
|
init_fs();
|
|
32589
33134
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
32590
|
-
import { dirname as dirname20, join as join15, resolve as
|
|
32591
|
-
import { cp as cp4, mkdir as mkdir9, readFile as
|
|
33135
|
+
import { dirname as dirname20, join as join15, resolve as resolve69 } from "path";
|
|
33136
|
+
import { cp as cp4, mkdir as mkdir9, readFile as readFile47 } from "fs/promises";
|
|
32592
33137
|
|
|
32593
33138
|
// src/commands/setup-adapter.ts
|
|
32594
33139
|
init_paths();
|
|
@@ -32597,7 +33142,7 @@ init_config2();
|
|
|
32597
33142
|
init_slug();
|
|
32598
33143
|
init_registry();
|
|
32599
33144
|
init_renderers();
|
|
32600
|
-
import { resolve as
|
|
33145
|
+
import { resolve as resolve68 } from "path";
|
|
32601
33146
|
async function setupAdapterCommand(framework, options) {
|
|
32602
33147
|
const { targets: known, warnings } = await resolveAgentTargets();
|
|
32603
33148
|
const target = known.find((t) => t.id === framework);
|
|
@@ -32626,13 +33171,13 @@ async function setupAdapterCommand(framework, options) {
|
|
|
32626
33171
|
}
|
|
32627
33172
|
const config = await readConfig();
|
|
32628
33173
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
32629
|
-
const projectDir =
|
|
32630
|
-
const assignmentDir =
|
|
32631
|
-
const projectMdPath =
|
|
33174
|
+
const projectDir = resolve68(baseDir, options.project);
|
|
33175
|
+
const assignmentDir = resolve68(projectDir, "assignments", options.assignment);
|
|
33176
|
+
const projectMdPath = resolve68(projectDir, "project.md");
|
|
32632
33177
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
32633
33178
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
32634
33179
|
}
|
|
32635
|
-
const assignmentMdPath2 =
|
|
33180
|
+
const assignmentMdPath2 = resolve68(assignmentDir, "assignment.md");
|
|
32636
33181
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
|
|
32637
33182
|
throw new Error(
|
|
32638
33183
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -32649,7 +33194,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
32649
33194
|
const upToDateFiles = [];
|
|
32650
33195
|
const skippedFiles = [];
|
|
32651
33196
|
for (const file of target.instructions.files) {
|
|
32652
|
-
const filePath =
|
|
33197
|
+
const filePath = resolve68(cwd, file.path);
|
|
32653
33198
|
const content = RENDERERS[file.renderer](rendererParams);
|
|
32654
33199
|
const status = await writeFileReport(filePath, content, {
|
|
32655
33200
|
force: options.force
|
|
@@ -32705,7 +33250,7 @@ async function installTier3Plugin(t, opts = {}) {
|
|
|
32705
33250
|
return "already-present";
|
|
32706
33251
|
}
|
|
32707
33252
|
try {
|
|
32708
|
-
const sourceDir =
|
|
33253
|
+
const sourceDir = resolve69(await findPackageRoot(plugin.source), plugin.source);
|
|
32709
33254
|
await mkdir9(dirname20(installDir), { recursive: true });
|
|
32710
33255
|
await cp4(sourceDir, installDir, { recursive: true, force: true });
|
|
32711
33256
|
console.log(`Tier 3 (${t.id}): installed ${plugin.kind} -> ${installDir}`);
|
|
@@ -32730,10 +33275,10 @@ function isNpxAvailable() {
|
|
|
32730
33275
|
}
|
|
32731
33276
|
}
|
|
32732
33277
|
async function readAssignmentContext() {
|
|
32733
|
-
const p =
|
|
33278
|
+
const p = resolve69(process.cwd(), ".syntaur", "context.json");
|
|
32734
33279
|
if (!await fileExists(p)) return null;
|
|
32735
33280
|
try {
|
|
32736
|
-
return JSON.parse(await
|
|
33281
|
+
return JSON.parse(await readFile47(p, "utf-8"));
|
|
32737
33282
|
} catch {
|
|
32738
33283
|
return null;
|
|
32739
33284
|
}
|
|
@@ -32813,7 +33358,7 @@ async function crossAgentInstallCommand(options) {
|
|
|
32813
33358
|
if (dryRun) {
|
|
32814
33359
|
for (const f of t.instructions.files) {
|
|
32815
33360
|
console.log(
|
|
32816
|
-
`${prefix}Tier 2 (${t.id}): ${
|
|
33361
|
+
`${prefix}Tier 2 (${t.id}): ${resolve69(process.cwd(), f.path)}`
|
|
32817
33362
|
);
|
|
32818
33363
|
}
|
|
32819
33364
|
continue;
|
|
@@ -32970,7 +33515,7 @@ async function setupCommand(options) {
|
|
|
32970
33515
|
}
|
|
32971
33516
|
|
|
32972
33517
|
// src/commands/uninstall.ts
|
|
32973
|
-
import { resolve as
|
|
33518
|
+
import { resolve as resolve70 } from "path";
|
|
32974
33519
|
init_paths();
|
|
32975
33520
|
function expandTargets(options) {
|
|
32976
33521
|
if (options.all) {
|
|
@@ -33050,7 +33595,7 @@ async function uninstallCommand(options) {
|
|
|
33050
33595
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
33051
33596
|
await removeSyntaurData();
|
|
33052
33597
|
console.log(`Removed ${syntaurRoot()}`);
|
|
33053
|
-
if (configuredProjectDir &&
|
|
33598
|
+
if (configuredProjectDir && resolve70(configuredProjectDir) !== resolve70(syntaurRoot(), "projects")) {
|
|
33054
33599
|
console.warn(
|
|
33055
33600
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
33056
33601
|
);
|
|
@@ -33072,8 +33617,8 @@ init_session_id();
|
|
|
33072
33617
|
init_cwd();
|
|
33073
33618
|
init_session_db();
|
|
33074
33619
|
init_agent_sessions();
|
|
33075
|
-
import { resolve as
|
|
33076
|
-
import { readFile as
|
|
33620
|
+
import { resolve as resolve71 } from "path";
|
|
33621
|
+
import { readFile as readFile48 } from "fs/promises";
|
|
33077
33622
|
async function trackSessionCommand(options, deps = {}) {
|
|
33078
33623
|
if (!options.agent) {
|
|
33079
33624
|
throw new Error("--agent <name> is required.");
|
|
@@ -33083,7 +33628,7 @@ async function trackSessionCommand(options, deps = {}) {
|
|
|
33083
33628
|
const cwd = process.cwd();
|
|
33084
33629
|
let legacyHint;
|
|
33085
33630
|
try {
|
|
33086
|
-
const raw2 = await
|
|
33631
|
+
const raw2 = await readFile48(resolve71(cwd, ".syntaur", "context.json"), "utf-8");
|
|
33087
33632
|
const parsed = JSON.parse(raw2);
|
|
33088
33633
|
if (typeof parsed.sessionId === "string") legacyHint = parsed.sessionId;
|
|
33089
33634
|
} catch {
|
|
@@ -33098,7 +33643,7 @@ async function trackSessionCommand(options, deps = {}) {
|
|
|
33098
33643
|
if (options.project) {
|
|
33099
33644
|
const config = await readConfig();
|
|
33100
33645
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
33101
|
-
const projectDir =
|
|
33646
|
+
const projectDir = resolve71(baseDir, options.project);
|
|
33102
33647
|
if (!await fileExists(projectDir)) {
|
|
33103
33648
|
throw new Error(
|
|
33104
33649
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -33231,8 +33776,8 @@ function formatInstallUrlHandlerError(err2) {
|
|
|
33231
33776
|
init_config2();
|
|
33232
33777
|
init_paths();
|
|
33233
33778
|
init_fs();
|
|
33234
|
-
import { resolve as
|
|
33235
|
-
import { readFile as
|
|
33779
|
+
import { resolve as resolve73, isAbsolute as isAbsolute8 } from "path";
|
|
33780
|
+
import { readFile as readFile49 } from "fs/promises";
|
|
33236
33781
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
33237
33782
|
async function browseCommand(options) {
|
|
33238
33783
|
const config = await readConfig();
|
|
@@ -33301,7 +33846,7 @@ async function pickAgent2(agents) {
|
|
|
33301
33846
|
return picked;
|
|
33302
33847
|
}
|
|
33303
33848
|
async function ensureWorktree(opts) {
|
|
33304
|
-
const assignmentPath =
|
|
33849
|
+
const assignmentPath = resolve73(
|
|
33305
33850
|
opts.projectsDir,
|
|
33306
33851
|
opts.projectSlug,
|
|
33307
33852
|
"assignments",
|
|
@@ -33311,7 +33856,7 @@ async function ensureWorktree(opts) {
|
|
|
33311
33856
|
if (!await fileExists(assignmentPath)) {
|
|
33312
33857
|
return void 0;
|
|
33313
33858
|
}
|
|
33314
|
-
const content = await
|
|
33859
|
+
const content = await readFile49(assignmentPath, "utf-8");
|
|
33315
33860
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
33316
33861
|
const fm = parseAssignmentFrontmatter2(content);
|
|
33317
33862
|
const { workspace } = fm;
|
|
@@ -33381,7 +33926,7 @@ async function ensureWorktree(opts) {
|
|
|
33381
33926
|
async function runCreate(opts) {
|
|
33382
33927
|
const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
33383
33928
|
const expandedWorktree = expandHome(opts.worktreePath);
|
|
33384
|
-
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree :
|
|
33929
|
+
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve73(expandedWorktree);
|
|
33385
33930
|
try {
|
|
33386
33931
|
await createWorktreeAndRecord2({
|
|
33387
33932
|
assignmentPath: opts.assignmentPath,
|
|
@@ -33410,7 +33955,7 @@ init_paths();
|
|
|
33410
33955
|
init_fs();
|
|
33411
33956
|
init_playbook();
|
|
33412
33957
|
init_playbooks();
|
|
33413
|
-
import { resolve as
|
|
33958
|
+
import { resolve as resolve74 } from "path";
|
|
33414
33959
|
async function createPlaybookCommand(name, options) {
|
|
33415
33960
|
if (!name.trim()) {
|
|
33416
33961
|
throw new Error("Playbook name cannot be empty.");
|
|
@@ -33423,7 +33968,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
33423
33968
|
}
|
|
33424
33969
|
const dir = playbooksDir();
|
|
33425
33970
|
await ensureDir(dir);
|
|
33426
|
-
const filePath =
|
|
33971
|
+
const filePath = resolve74(dir, `${slug}.md`);
|
|
33427
33972
|
if (await fileExists(filePath)) {
|
|
33428
33973
|
throw new Error(
|
|
33429
33974
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -33445,8 +33990,8 @@ init_paths();
|
|
|
33445
33990
|
init_fs();
|
|
33446
33991
|
init_parser();
|
|
33447
33992
|
init_config2();
|
|
33448
|
-
import { readdir as readdir26, readFile as
|
|
33449
|
-
import { resolve as
|
|
33993
|
+
import { readdir as readdir26, readFile as readFile50 } from "fs/promises";
|
|
33994
|
+
import { resolve as resolve75 } from "path";
|
|
33450
33995
|
async function listPlaybooksCommand(options = {}) {
|
|
33451
33996
|
const dir = playbooksDir();
|
|
33452
33997
|
if (!await fileExists(dir)) {
|
|
@@ -33461,8 +34006,8 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
33461
34006
|
);
|
|
33462
34007
|
const rows = [];
|
|
33463
34008
|
for (const entry of mdFiles) {
|
|
33464
|
-
const filePath =
|
|
33465
|
-
const raw2 = await
|
|
34009
|
+
const filePath = resolve75(dir, entry.name);
|
|
34010
|
+
const raw2 = await readFile50(filePath, "utf-8");
|
|
33466
34011
|
const parsed = parsePlaybook(raw2);
|
|
33467
34012
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
33468
34013
|
const disabled = disabledSet.has(slug);
|
|
@@ -33585,14 +34130,14 @@ init_fs();
|
|
|
33585
34130
|
init_config2();
|
|
33586
34131
|
init_slug();
|
|
33587
34132
|
import { Command as Command2 } from "commander";
|
|
33588
|
-
import { readFile as
|
|
33589
|
-
import { resolve as
|
|
34133
|
+
import { readFile as readFile52 } from "fs/promises";
|
|
34134
|
+
import { resolve as resolve77 } from "path";
|
|
33590
34135
|
|
|
33591
34136
|
// src/commands/bundle.ts
|
|
33592
34137
|
init_paths();
|
|
33593
34138
|
import { Command } from "commander";
|
|
33594
|
-
import { mkdir as mkdir10, readFile as
|
|
33595
|
-
import { resolve as
|
|
34139
|
+
import { mkdir as mkdir10, readFile as readFile51, readdir as readdir27, rm as rm9, writeFile as writeFile13 } from "fs/promises";
|
|
34140
|
+
import { resolve as resolve76 } from "path";
|
|
33596
34141
|
init_parser2();
|
|
33597
34142
|
init_fs();
|
|
33598
34143
|
init_config2();
|
|
@@ -33610,7 +34155,7 @@ async function resolveBundleScope(options) {
|
|
|
33610
34155
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
33611
34156
|
}
|
|
33612
34157
|
const config = await readConfig();
|
|
33613
|
-
const projectMd =
|
|
34158
|
+
const projectMd = resolve76(config.defaultProjectDir, options.project, "project.md");
|
|
33614
34159
|
if (!await fileExists(projectMd)) {
|
|
33615
34160
|
throw new Error(`Project "${options.project}" not found.`);
|
|
33616
34161
|
}
|
|
@@ -33680,10 +34225,10 @@ function pickNextPlanFile(planDir, existingFiles) {
|
|
|
33680
34225
|
const m = f.match(/^plan-v(\d+)\.md$/);
|
|
33681
34226
|
if (m) versions.add(parseInt(m[1], 10));
|
|
33682
34227
|
}
|
|
33683
|
-
if (versions.size === 0) return { target:
|
|
34228
|
+
if (versions.size === 0) return { target: resolve76(planDir, "plan.md"), version: 1 };
|
|
33684
34229
|
let n = 2;
|
|
33685
34230
|
while (versions.has(n)) n++;
|
|
33686
|
-
return { target:
|
|
34231
|
+
return { target: resolve76(planDir, `plan-v${n}.md`), version: n };
|
|
33687
34232
|
}
|
|
33688
34233
|
function dedupePreserveOrder(ids) {
|
|
33689
34234
|
const out = [];
|
|
@@ -33767,7 +34312,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
|
|
|
33767
34312
|
if (options.plan) {
|
|
33768
34313
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
33769
34314
|
await ensureDir(planDir);
|
|
33770
|
-
const target =
|
|
34315
|
+
const target = resolve76(planDir, "plan.md");
|
|
33771
34316
|
const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
|
|
33772
34317
|
const stub = [
|
|
33773
34318
|
"---",
|
|
@@ -33943,7 +34488,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33943
34488
|
}
|
|
33944
34489
|
const repository = options.repository ?? process.cwd();
|
|
33945
34490
|
const parentBranch = options.parentBranch ?? "main";
|
|
33946
|
-
const worktreePath = options.worktreePath ??
|
|
34491
|
+
const worktreePath = options.worktreePath ?? resolve76(repository, ".worktrees", options.branch);
|
|
33947
34492
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
33948
34493
|
for (const memberId of bundle.todoIds) {
|
|
33949
34494
|
const item = checklist.items.find((i) => i.id === memberId);
|
|
@@ -33953,8 +34498,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33953
34498
|
}
|
|
33954
34499
|
const bundlesFilePath = bundlesPath(sc.todosPath);
|
|
33955
34500
|
const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
|
|
33956
|
-
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await
|
|
33957
|
-
const checklistSnapshot = await fileExists(checklistFilePath) ? await
|
|
34501
|
+
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile51(bundlesFilePath, "utf-8") : null;
|
|
34502
|
+
const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile51(checklistFilePath, "utf-8") : null;
|
|
33958
34503
|
const record = async () => {
|
|
33959
34504
|
bundle.branch = options.branch;
|
|
33960
34505
|
bundle.worktreePath = worktreePath;
|
|
@@ -33970,7 +34515,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33970
34515
|
try {
|
|
33971
34516
|
await writeBundles(sc.todosPath, bundles);
|
|
33972
34517
|
await writeChecklist(sc.todosPath, checklist);
|
|
33973
|
-
const ctxDir =
|
|
34518
|
+
const ctxDir = resolve76(worktreePath, ".syntaur");
|
|
33974
34519
|
await mkdir10(ctxDir, { recursive: true });
|
|
33975
34520
|
const payload = {
|
|
33976
34521
|
bundleId: bundle.id,
|
|
@@ -33984,7 +34529,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
33984
34529
|
repository,
|
|
33985
34530
|
boundAt: nowISO()
|
|
33986
34531
|
};
|
|
33987
|
-
await writeFile13(
|
|
34532
|
+
await writeFile13(resolve76(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
|
|
33988
34533
|
} catch (err2) {
|
|
33989
34534
|
try {
|
|
33990
34535
|
if (bundlesSnapshot === null) {
|
|
@@ -34174,7 +34719,7 @@ async function resolveScope(options) {
|
|
|
34174
34719
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
34175
34720
|
}
|
|
34176
34721
|
const config = await readConfig();
|
|
34177
|
-
const projectMd =
|
|
34722
|
+
const projectMd = resolve77(config.defaultProjectDir, options.project, "project.md");
|
|
34178
34723
|
if (!await fileExists(projectMd)) {
|
|
34179
34724
|
throw new Error(`Project "${options.project}" not found.`);
|
|
34180
34725
|
}
|
|
@@ -34507,10 +35052,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
34507
35052
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
34508
35053
|
);
|
|
34509
35054
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
34510
|
-
await ensureDir(
|
|
35055
|
+
await ensureDir(resolve77(todosPath, "archive"));
|
|
34511
35056
|
let archContent = "";
|
|
34512
35057
|
if (await fileExists(archFile)) {
|
|
34513
|
-
archContent = await
|
|
35058
|
+
archContent = await readFile52(archFile, "utf-8");
|
|
34514
35059
|
archContent = archContent.trimEnd() + "\n\n";
|
|
34515
35060
|
} else {
|
|
34516
35061
|
archContent = `---
|
|
@@ -34688,12 +35233,12 @@ function describeScope(scope) {
|
|
|
34688
35233
|
}
|
|
34689
35234
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
34690
35235
|
const { resolve: resolvePath2 } = await import("path");
|
|
34691
|
-
const { readFile:
|
|
35236
|
+
const { readFile: readFile82 } = await import("fs/promises");
|
|
34692
35237
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
34693
35238
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
34694
35239
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
34695
35240
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
34696
|
-
let content = await
|
|
35241
|
+
let content = await readFile82(assignmentMdPath2, "utf-8");
|
|
34697
35242
|
content = appendTodosToAssignmentBody2(
|
|
34698
35243
|
content,
|
|
34699
35244
|
todos.map((t) => ({
|
|
@@ -34744,7 +35289,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
34744
35289
|
);
|
|
34745
35290
|
let target;
|
|
34746
35291
|
if (existingFiles.length === 0) {
|
|
34747
|
-
target =
|
|
35292
|
+
target = resolve77(planDir, "plan.md");
|
|
34748
35293
|
} else {
|
|
34749
35294
|
const versions = /* @__PURE__ */ new Set();
|
|
34750
35295
|
for (const f of existingFiles) {
|
|
@@ -34754,7 +35299,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
34754
35299
|
}
|
|
34755
35300
|
let n = 2;
|
|
34756
35301
|
while (versions.has(n)) n++;
|
|
34757
|
-
target =
|
|
35302
|
+
target = resolve77(planDir, `plan-v${n}.md`);
|
|
34758
35303
|
}
|
|
34759
35304
|
if (!await fileExists(target)) {
|
|
34760
35305
|
const stub = `---
|
|
@@ -34948,12 +35493,12 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
34948
35493
|
|
|
34949
35494
|
// src/commands/doctor.ts
|
|
34950
35495
|
import { Command as Command4 } from "commander";
|
|
34951
|
-
import { readFile as
|
|
34952
|
-
import { isAbsolute as isAbsolute11, resolve as
|
|
35496
|
+
import { readFile as readFile64 } from "fs/promises";
|
|
35497
|
+
import { isAbsolute as isAbsolute11, resolve as resolve91 } from "path";
|
|
34953
35498
|
|
|
34954
35499
|
// src/utils/doctor/index.ts
|
|
34955
35500
|
import { fileURLToPath as fileURLToPath12 } from "url";
|
|
34956
|
-
import { readFile as
|
|
35501
|
+
import { readFile as readFile63 } from "fs/promises";
|
|
34957
35502
|
import { dirname as dirname25, join as join21 } from "path";
|
|
34958
35503
|
|
|
34959
35504
|
// src/utils/doctor/context.ts
|
|
@@ -34961,11 +35506,11 @@ init_config2();
|
|
|
34961
35506
|
init_paths();
|
|
34962
35507
|
init_fs();
|
|
34963
35508
|
import Database5 from "better-sqlite3";
|
|
34964
|
-
import { resolve as
|
|
35509
|
+
import { resolve as resolve78 } from "path";
|
|
34965
35510
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
34966
35511
|
const config = await readConfig();
|
|
34967
35512
|
const root = syntaurRoot();
|
|
34968
|
-
const dbPath =
|
|
35513
|
+
const dbPath = resolve78(root, "syntaur.db");
|
|
34969
35514
|
let db6 = null;
|
|
34970
35515
|
let dbError = null;
|
|
34971
35516
|
if (await fileExists(dbPath)) {
|
|
@@ -34999,8 +35544,8 @@ function closeCheckContext(ctx) {
|
|
|
34999
35544
|
// src/utils/doctor/checks/env.ts
|
|
35000
35545
|
init_fs();
|
|
35001
35546
|
init_paths();
|
|
35002
|
-
import { resolve as
|
|
35003
|
-
import { readFile as
|
|
35547
|
+
import { resolve as resolve79, isAbsolute as isAbsolute9 } from "path";
|
|
35548
|
+
import { readFile as readFile53, stat as stat9 } from "fs/promises";
|
|
35004
35549
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
35005
35550
|
import { dirname as dirname22, join as join17 } from "path";
|
|
35006
35551
|
var CATEGORY = "env";
|
|
@@ -35010,7 +35555,7 @@ var syntaurRootExists = {
|
|
|
35010
35555
|
title: "~/.syntaur/ directory exists",
|
|
35011
35556
|
async run(ctx) {
|
|
35012
35557
|
try {
|
|
35013
|
-
const s = await
|
|
35558
|
+
const s = await stat9(ctx.syntaurRoot);
|
|
35014
35559
|
if (!s.isDirectory()) {
|
|
35015
35560
|
return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
|
|
35016
35561
|
ctx.syntaurRoot
|
|
@@ -35040,7 +35585,7 @@ var configValid = {
|
|
|
35040
35585
|
category: CATEGORY,
|
|
35041
35586
|
title: "~/.syntaur/config.md is valid",
|
|
35042
35587
|
async run(ctx) {
|
|
35043
|
-
const configPath2 =
|
|
35588
|
+
const configPath2 = resolve79(ctx.syntaurRoot, "config.md");
|
|
35044
35589
|
if (!await fileExists(configPath2)) {
|
|
35045
35590
|
return {
|
|
35046
35591
|
id: this.id,
|
|
@@ -35057,7 +35602,7 @@ var configValid = {
|
|
|
35057
35602
|
autoFixable: false
|
|
35058
35603
|
};
|
|
35059
35604
|
}
|
|
35060
|
-
const content = await
|
|
35605
|
+
const content = await readFile53(configPath2, "utf-8");
|
|
35061
35606
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
35062
35607
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
35063
35608
|
return {
|
|
@@ -35337,7 +35882,7 @@ async function readLocalPkg() {
|
|
|
35337
35882
|
for (let i = 0; i < 6; i++) {
|
|
35338
35883
|
const candidate = join17(dir, "package.json");
|
|
35339
35884
|
try {
|
|
35340
|
-
const text = await
|
|
35885
|
+
const text = await readFile53(candidate, "utf-8");
|
|
35341
35886
|
return JSON.parse(text);
|
|
35342
35887
|
} catch {
|
|
35343
35888
|
dir = dirname22(dir);
|
|
@@ -35389,8 +35934,8 @@ function versionGte(a, b) {
|
|
|
35389
35934
|
|
|
35390
35935
|
// src/utils/doctor/checks/structure.ts
|
|
35391
35936
|
init_fs();
|
|
35392
|
-
import { resolve as
|
|
35393
|
-
import { readdir as readdir28, stat as
|
|
35937
|
+
import { resolve as resolve80 } from "path";
|
|
35938
|
+
import { readdir as readdir28, stat as stat10 } from "fs/promises";
|
|
35394
35939
|
var CATEGORY2 = "structure";
|
|
35395
35940
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
35396
35941
|
"projects",
|
|
@@ -35409,7 +35954,7 @@ var projectsDir = {
|
|
|
35409
35954
|
category: CATEGORY2,
|
|
35410
35955
|
title: "projects/ directory exists",
|
|
35411
35956
|
async run(ctx) {
|
|
35412
|
-
const p =
|
|
35957
|
+
const p = resolve80(ctx.syntaurRoot, "projects");
|
|
35413
35958
|
if (!await fileExists(p)) {
|
|
35414
35959
|
return {
|
|
35415
35960
|
id: this.id,
|
|
@@ -35434,7 +35979,7 @@ var playbooksDir2 = {
|
|
|
35434
35979
|
category: CATEGORY2,
|
|
35435
35980
|
title: "playbooks/ directory exists",
|
|
35436
35981
|
async run(ctx) {
|
|
35437
|
-
const p =
|
|
35982
|
+
const p = resolve80(ctx.syntaurRoot, "playbooks");
|
|
35438
35983
|
if (!await fileExists(p)) {
|
|
35439
35984
|
return {
|
|
35440
35985
|
id: this.id,
|
|
@@ -35459,7 +36004,7 @@ var todosDirValid = {
|
|
|
35459
36004
|
category: CATEGORY2,
|
|
35460
36005
|
title: "todos/ directory is readable (if present)",
|
|
35461
36006
|
async run(ctx) {
|
|
35462
|
-
const p =
|
|
36007
|
+
const p = resolve80(ctx.syntaurRoot, "todos");
|
|
35463
36008
|
if (!await fileExists(p)) {
|
|
35464
36009
|
return {
|
|
35465
36010
|
id: this.id,
|
|
@@ -35470,7 +36015,7 @@ var todosDirValid = {
|
|
|
35470
36015
|
autoFixable: false
|
|
35471
36016
|
};
|
|
35472
36017
|
}
|
|
35473
|
-
const s = await
|
|
36018
|
+
const s = await stat10(p);
|
|
35474
36019
|
if (!s.isDirectory()) {
|
|
35475
36020
|
return {
|
|
35476
36021
|
id: this.id,
|
|
@@ -35490,7 +36035,7 @@ var serversDirValid = {
|
|
|
35490
36035
|
category: CATEGORY2,
|
|
35491
36036
|
title: "servers/ directory is readable (if present)",
|
|
35492
36037
|
async run(ctx) {
|
|
35493
|
-
const p =
|
|
36038
|
+
const p = resolve80(ctx.syntaurRoot, "servers");
|
|
35494
36039
|
if (!await fileExists(p)) {
|
|
35495
36040
|
return {
|
|
35496
36041
|
id: this.id,
|
|
@@ -35501,7 +36046,7 @@ var serversDirValid = {
|
|
|
35501
36046
|
autoFixable: false
|
|
35502
36047
|
};
|
|
35503
36048
|
}
|
|
35504
|
-
const s = await
|
|
36049
|
+
const s = await stat10(p);
|
|
35505
36050
|
if (!s.isDirectory()) {
|
|
35506
36051
|
return {
|
|
35507
36052
|
id: this.id,
|
|
@@ -35535,7 +36080,7 @@ var knownFilesRecognized = {
|
|
|
35535
36080
|
title: this.title,
|
|
35536
36081
|
status: "warn",
|
|
35537
36082
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
35538
|
-
affected: unexpected.map((n) =>
|
|
36083
|
+
affected: unexpected.map((n) => resolve80(ctx.syntaurRoot, n)),
|
|
35539
36084
|
remediation: {
|
|
35540
36085
|
kind: "manual",
|
|
35541
36086
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -35564,8 +36109,8 @@ function pass2(check) {
|
|
|
35564
36109
|
|
|
35565
36110
|
// src/utils/doctor/checks/project.ts
|
|
35566
36111
|
init_fs();
|
|
35567
|
-
import { resolve as
|
|
35568
|
-
import { readdir as readdir29, stat as
|
|
36112
|
+
import { resolve as resolve81 } from "path";
|
|
36113
|
+
import { readdir as readdir29, stat as stat11 } from "fs/promises";
|
|
35569
36114
|
var CATEGORY3 = "project";
|
|
35570
36115
|
var REQUIRED_PROJECT_FILES = [
|
|
35571
36116
|
"project.md",
|
|
@@ -35594,10 +36139,10 @@ async function listProjects2(ctx) {
|
|
|
35594
36139
|
for (const e of entries) {
|
|
35595
36140
|
if (!e.isDirectory()) continue;
|
|
35596
36141
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
35597
|
-
const projectDir =
|
|
36142
|
+
const projectDir = resolve81(dir, e.name);
|
|
35598
36143
|
let looksLikeProject = false;
|
|
35599
36144
|
for (const marker of PROJECT_MARKERS) {
|
|
35600
|
-
if (await fileExists(
|
|
36145
|
+
if (await fileExists(resolve81(projectDir, marker))) {
|
|
35601
36146
|
looksLikeProject = true;
|
|
35602
36147
|
break;
|
|
35603
36148
|
}
|
|
@@ -35616,7 +36161,7 @@ var requiredFiles = {
|
|
|
35616
36161
|
for (const projectDir of projects) {
|
|
35617
36162
|
const missing = [];
|
|
35618
36163
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
35619
|
-
const p =
|
|
36164
|
+
const p = resolve81(projectDir, rel);
|
|
35620
36165
|
if (!await fileExists(p)) missing.push(rel);
|
|
35621
36166
|
}
|
|
35622
36167
|
if (missing.length === 0) continue;
|
|
@@ -35626,7 +36171,7 @@ var requiredFiles = {
|
|
|
35626
36171
|
title: this.title,
|
|
35627
36172
|
status: "error",
|
|
35628
36173
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
35629
|
-
affected: missing.map((m) =>
|
|
36174
|
+
affected: missing.map((m) => resolve81(projectDir, m)),
|
|
35630
36175
|
remediation: {
|
|
35631
36176
|
kind: "manual",
|
|
35632
36177
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -35649,9 +36194,9 @@ var manifestStale = {
|
|
|
35649
36194
|
const projects = await listProjects2(ctx);
|
|
35650
36195
|
const results = [];
|
|
35651
36196
|
for (const projectDir of projects) {
|
|
35652
|
-
const manifestPath =
|
|
36197
|
+
const manifestPath = resolve81(projectDir, "manifest.md");
|
|
35653
36198
|
if (!await fileExists(manifestPath)) continue;
|
|
35654
|
-
const manifestMtime = (await
|
|
36199
|
+
const manifestMtime = (await stat11(manifestPath)).mtimeMs;
|
|
35655
36200
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
35656
36201
|
if (newestAssignment === 0) continue;
|
|
35657
36202
|
if (newestAssignment > manifestMtime) {
|
|
@@ -35698,7 +36243,7 @@ var orphanFiles = {
|
|
|
35698
36243
|
title: this.title,
|
|
35699
36244
|
status: "warn",
|
|
35700
36245
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
35701
|
-
affected: orphans.map((o) =>
|
|
36246
|
+
affected: orphans.map((o) => resolve81(projectDir, o)),
|
|
35702
36247
|
autoFixable: false
|
|
35703
36248
|
});
|
|
35704
36249
|
}
|
|
@@ -35708,7 +36253,7 @@ var orphanFiles = {
|
|
|
35708
36253
|
};
|
|
35709
36254
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
35710
36255
|
async function newestAssignmentMtime(projectDir) {
|
|
35711
|
-
const assignmentsRoot =
|
|
36256
|
+
const assignmentsRoot = resolve81(projectDir, "assignments");
|
|
35712
36257
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
35713
36258
|
let newest = 0;
|
|
35714
36259
|
let entries;
|
|
@@ -35719,9 +36264,9 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
35719
36264
|
}
|
|
35720
36265
|
for (const e of entries) {
|
|
35721
36266
|
if (!e.isDirectory()) continue;
|
|
35722
|
-
const assignmentMd =
|
|
36267
|
+
const assignmentMd = resolve81(assignmentsRoot, e.name, "assignment.md");
|
|
35723
36268
|
try {
|
|
35724
|
-
const s = await
|
|
36269
|
+
const s = await stat11(assignmentMd);
|
|
35725
36270
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
35726
36271
|
} catch {
|
|
35727
36272
|
}
|
|
@@ -35744,8 +36289,8 @@ init_parser();
|
|
|
35744
36289
|
init_types();
|
|
35745
36290
|
init_paths();
|
|
35746
36291
|
init_assignment_walk();
|
|
35747
|
-
import { resolve as
|
|
35748
|
-
import { readFile as
|
|
36292
|
+
import { resolve as resolve82 } from "path";
|
|
36293
|
+
import { readFile as readFile54, readdir as readdir30 } from "fs/promises";
|
|
35749
36294
|
var CATEGORY4 = "assignment";
|
|
35750
36295
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
35751
36296
|
var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -35839,7 +36384,7 @@ var invalidStatus = {
|
|
|
35839
36384
|
const allowed = configuredStatuses(ctx);
|
|
35840
36385
|
const results = [];
|
|
35841
36386
|
for (const a of withAssignmentMd) {
|
|
35842
|
-
const path =
|
|
36387
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35843
36388
|
const parsed = await parseSafe4(path);
|
|
35844
36389
|
if (!parsed) continue;
|
|
35845
36390
|
if (!allowed.has(parsed.status)) {
|
|
@@ -35872,7 +36417,7 @@ var workspaceMissing = {
|
|
|
35872
36417
|
const terminal = terminalStatuses(ctx);
|
|
35873
36418
|
const results = [];
|
|
35874
36419
|
for (const a of withAssignmentMd) {
|
|
35875
|
-
const path =
|
|
36420
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
35876
36421
|
const parsed = await parseSafe4(path);
|
|
35877
36422
|
if (!parsed) continue;
|
|
35878
36423
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -35919,12 +36464,12 @@ var requiredFilesByStatus = {
|
|
|
35919
36464
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
35920
36465
|
const results = [];
|
|
35921
36466
|
for (const a of withAssignmentMd) {
|
|
35922
|
-
const assignmentPath =
|
|
36467
|
+
const assignmentPath = resolve82(a.assignmentDir, "assignment.md");
|
|
35923
36468
|
const parsed = await parseSafe4(assignmentPath);
|
|
35924
36469
|
if (!parsed) continue;
|
|
35925
36470
|
const missing = [];
|
|
35926
36471
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
35927
|
-
const handoffPath =
|
|
36472
|
+
const handoffPath = resolve82(a.assignmentDir, "handoff.md");
|
|
35928
36473
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
35929
36474
|
}
|
|
35930
36475
|
if (missing.length === 0) continue;
|
|
@@ -35934,7 +36479,7 @@ var requiredFilesByStatus = {
|
|
|
35934
36479
|
title: this.title,
|
|
35935
36480
|
status: "warn",
|
|
35936
36481
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
35937
|
-
affected: missing.map((m) =>
|
|
36482
|
+
affected: missing.map((m) => resolve82(a.assignmentDir, m)),
|
|
35938
36483
|
remediation: {
|
|
35939
36484
|
kind: "manual",
|
|
35940
36485
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -35957,7 +36502,7 @@ var companionFilesScaffolded = {
|
|
|
35957
36502
|
for (const a of withAssignmentMd) {
|
|
35958
36503
|
const missing = [];
|
|
35959
36504
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
35960
|
-
if (!await fileExists(
|
|
36505
|
+
if (!await fileExists(resolve82(a.assignmentDir, filename))) {
|
|
35961
36506
|
missing.push(filename);
|
|
35962
36507
|
}
|
|
35963
36508
|
}
|
|
@@ -35969,7 +36514,7 @@ var companionFilesScaffolded = {
|
|
|
35969
36514
|
title: this.title,
|
|
35970
36515
|
status: "warn",
|
|
35971
36516
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
35972
|
-
affected: missing.map((m) =>
|
|
36517
|
+
affected: missing.map((m) => resolve82(a.assignmentDir, m)),
|
|
35973
36518
|
remediation: {
|
|
35974
36519
|
kind: "manual",
|
|
35975
36520
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -36002,7 +36547,7 @@ var typeDefinition = {
|
|
|
36002
36547
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36003
36548
|
const results = [];
|
|
36004
36549
|
for (const a of withAssignmentMd) {
|
|
36005
|
-
const path =
|
|
36550
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36006
36551
|
const parsed = await parseSafe4(path);
|
|
36007
36552
|
if (!parsed) continue;
|
|
36008
36553
|
if (!parsed.type) continue;
|
|
@@ -36036,7 +36581,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
36036
36581
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36037
36582
|
const results = [];
|
|
36038
36583
|
for (const a of withAssignmentMd) {
|
|
36039
|
-
const path =
|
|
36584
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36040
36585
|
const parsed = await parseSafe4(path);
|
|
36041
36586
|
if (!parsed) continue;
|
|
36042
36587
|
if (a.standalone) {
|
|
@@ -36087,13 +36632,13 @@ var draftMissingObjective = {
|
|
|
36087
36632
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36088
36633
|
const results = [];
|
|
36089
36634
|
for (const a of withAssignmentMd) {
|
|
36090
|
-
const path =
|
|
36635
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36091
36636
|
const parsed = await parseSafe4(path);
|
|
36092
36637
|
if (!parsed) continue;
|
|
36093
36638
|
if (parsed.status !== "draft") continue;
|
|
36094
36639
|
let raw2;
|
|
36095
36640
|
try {
|
|
36096
|
-
raw2 = await
|
|
36641
|
+
raw2 = await readFile54(path, "utf-8");
|
|
36097
36642
|
} catch {
|
|
36098
36643
|
continue;
|
|
36099
36644
|
}
|
|
@@ -36126,7 +36671,7 @@ var readyToImplementMissingPlan = {
|
|
|
36126
36671
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
36127
36672
|
const results = [];
|
|
36128
36673
|
for (const a of withAssignmentMd) {
|
|
36129
|
-
const path =
|
|
36674
|
+
const path = resolve82(a.assignmentDir, "assignment.md");
|
|
36130
36675
|
const parsed = await parseSafe4(path);
|
|
36131
36676
|
if (!parsed) continue;
|
|
36132
36677
|
if (parsed.status !== "ready_to_implement") continue;
|
|
@@ -36135,7 +36680,7 @@ var readyToImplementMissingPlan = {
|
|
|
36135
36680
|
let hasPlanContent = false;
|
|
36136
36681
|
for (const f of planFiles) {
|
|
36137
36682
|
try {
|
|
36138
|
-
const c2 = await
|
|
36683
|
+
const c2 = await readFile54(resolve82(a.assignmentDir, f), "utf-8");
|
|
36139
36684
|
if (c2.trim().length > 0) {
|
|
36140
36685
|
hasPlanContent = true;
|
|
36141
36686
|
break;
|
|
@@ -36151,7 +36696,7 @@ var readyToImplementMissingPlan = {
|
|
|
36151
36696
|
title: this.title,
|
|
36152
36697
|
status: "warn",
|
|
36153
36698
|
detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
|
|
36154
|
-
affected: [
|
|
36699
|
+
affected: [resolve82(a.assignmentDir, "plan.md")],
|
|
36155
36700
|
remediation: {
|
|
36156
36701
|
kind: "manual",
|
|
36157
36702
|
suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
|
|
@@ -36178,7 +36723,7 @@ var assignmentChecks = [
|
|
|
36178
36723
|
];
|
|
36179
36724
|
async function parseSafe4(path) {
|
|
36180
36725
|
try {
|
|
36181
|
-
const content = await
|
|
36726
|
+
const content = await readFile54(path, "utf-8");
|
|
36182
36727
|
return parseAssignmentFull(content);
|
|
36183
36728
|
} catch {
|
|
36184
36729
|
return null;
|
|
@@ -36197,7 +36742,7 @@ function pass4(check, detail) {
|
|
|
36197
36742
|
|
|
36198
36743
|
// src/utils/doctor/checks/dashboard.ts
|
|
36199
36744
|
init_fs();
|
|
36200
|
-
import { resolve as
|
|
36745
|
+
import { resolve as resolve83 } from "path";
|
|
36201
36746
|
var CATEGORY5 = "dashboard";
|
|
36202
36747
|
var dbReachable = {
|
|
36203
36748
|
id: "dashboard.db-reachable",
|
|
@@ -36211,7 +36756,7 @@ var dbReachable = {
|
|
|
36211
36756
|
title: this.title,
|
|
36212
36757
|
status: "error",
|
|
36213
36758
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
36214
|
-
affected: [
|
|
36759
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36215
36760
|
remediation: {
|
|
36216
36761
|
kind: "manual",
|
|
36217
36762
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -36229,7 +36774,7 @@ var dbReachable = {
|
|
|
36229
36774
|
title: this.title,
|
|
36230
36775
|
status: "error",
|
|
36231
36776
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
36232
|
-
affected: [
|
|
36777
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36233
36778
|
autoFixable: false
|
|
36234
36779
|
};
|
|
36235
36780
|
}
|
|
@@ -36241,7 +36786,7 @@ var dbReachable = {
|
|
|
36241
36786
|
title: this.title,
|
|
36242
36787
|
status: "error",
|
|
36243
36788
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
36244
|
-
affected: [
|
|
36789
|
+
affected: [resolve83(ctx.syntaurRoot, "syntaur.db")],
|
|
36245
36790
|
autoFixable: false
|
|
36246
36791
|
};
|
|
36247
36792
|
}
|
|
@@ -36267,7 +36812,7 @@ var ghostSessions = {
|
|
|
36267
36812
|
const results = [];
|
|
36268
36813
|
for (const row of rows) {
|
|
36269
36814
|
if (!row.project_slug) continue;
|
|
36270
|
-
const projectPath =
|
|
36815
|
+
const projectPath = resolve83(projectsDir2, row.project_slug, "project.md");
|
|
36271
36816
|
if (!await fileExists(projectPath)) {
|
|
36272
36817
|
results.push({
|
|
36273
36818
|
id: this.id,
|
|
@@ -36286,7 +36831,7 @@ var ghostSessions = {
|
|
|
36286
36831
|
continue;
|
|
36287
36832
|
}
|
|
36288
36833
|
if (row.assignment_slug) {
|
|
36289
|
-
const assignmentPath =
|
|
36834
|
+
const assignmentPath = resolve83(
|
|
36290
36835
|
projectsDir2,
|
|
36291
36836
|
row.project_slug,
|
|
36292
36837
|
"assignments",
|
|
@@ -36338,8 +36883,8 @@ function skipped(check, reason) {
|
|
|
36338
36883
|
|
|
36339
36884
|
// src/utils/doctor/checks/integrations.ts
|
|
36340
36885
|
init_fs();
|
|
36341
|
-
import { resolve as
|
|
36342
|
-
import { readdir as readdir31, readFile as
|
|
36886
|
+
import { resolve as resolve84, dirname as dirname23, basename as basename9 } from "path";
|
|
36887
|
+
import { readdir as readdir31, readFile as readFile55 } from "fs/promises";
|
|
36343
36888
|
import { homedir as homedir14 } from "os";
|
|
36344
36889
|
var CATEGORY6 = "integrations";
|
|
36345
36890
|
var claudePluginLinked = {
|
|
@@ -36421,10 +36966,10 @@ var backupConfigured = {
|
|
|
36421
36966
|
}
|
|
36422
36967
|
};
|
|
36423
36968
|
async function readKnownMarketplaces() {
|
|
36424
|
-
const path =
|
|
36969
|
+
const path = resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json");
|
|
36425
36970
|
if (!await fileExists(path)) return {};
|
|
36426
36971
|
try {
|
|
36427
|
-
const raw2 = await
|
|
36972
|
+
const raw2 = await readFile55(path, "utf-8");
|
|
36428
36973
|
return JSON.parse(raw2);
|
|
36429
36974
|
} catch {
|
|
36430
36975
|
return {};
|
|
@@ -36441,7 +36986,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36441
36986
|
return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
|
|
36442
36987
|
}
|
|
36443
36988
|
const pluginsParent = dirname23(dir);
|
|
36444
|
-
if (
|
|
36989
|
+
if (basename9(pluginsParent) !== "plugins") {
|
|
36445
36990
|
return {
|
|
36446
36991
|
id: this.id,
|
|
36447
36992
|
category: this.category,
|
|
@@ -36458,7 +37003,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36458
37003
|
};
|
|
36459
37004
|
}
|
|
36460
37005
|
const marketplaceRoot = dirname23(pluginsParent);
|
|
36461
|
-
const marketplaceManifest =
|
|
37006
|
+
const marketplaceManifest = resolve84(marketplaceRoot, ".claude-plugin", "marketplace.json");
|
|
36462
37007
|
if (!await fileExists(marketplaceManifest)) {
|
|
36463
37008
|
return {
|
|
36464
37009
|
id: this.id,
|
|
@@ -36477,7 +37022,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36477
37022
|
}
|
|
36478
37023
|
let parsed = {};
|
|
36479
37024
|
try {
|
|
36480
|
-
parsed = JSON.parse(await
|
|
37025
|
+
parsed = JSON.parse(await readFile55(marketplaceManifest, "utf-8"));
|
|
36481
37026
|
} catch {
|
|
36482
37027
|
return {
|
|
36483
37028
|
id: this.id,
|
|
@@ -36489,7 +37034,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36489
37034
|
autoFixable: false
|
|
36490
37035
|
};
|
|
36491
37036
|
}
|
|
36492
|
-
const marketplaceName = parsed.name ??
|
|
37037
|
+
const marketplaceName = parsed.name ?? basename9(marketplaceRoot);
|
|
36493
37038
|
const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
|
|
36494
37039
|
const known = await readKnownMarketplaces();
|
|
36495
37040
|
const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
|
|
@@ -36509,7 +37054,7 @@ var claudeMarketplaceRegistered = {
|
|
|
36509
37054
|
title: this.title,
|
|
36510
37055
|
status: "error",
|
|
36511
37056
|
detail: issues.join("; "),
|
|
36512
|
-
affected: [marketplaceManifest,
|
|
37057
|
+
affected: [marketplaceManifest, resolve84(homedir14(), ".claude", "plugins", "known_marketplaces.json")],
|
|
36513
37058
|
remediation: {
|
|
36514
37059
|
kind: "manual",
|
|
36515
37060
|
suggestion: "Re-run install-plugin to ensure both files are in sync.",
|
|
@@ -36549,8 +37094,8 @@ function skipped2(check, reason) {
|
|
|
36549
37094
|
init_fs();
|
|
36550
37095
|
init_parser();
|
|
36551
37096
|
init_types();
|
|
36552
|
-
import { resolve as
|
|
36553
|
-
import { readFile as
|
|
37097
|
+
import { resolve as resolve85 } from "path";
|
|
37098
|
+
import { readFile as readFile56 } from "fs/promises";
|
|
36554
37099
|
var CATEGORY7 = "workspace";
|
|
36555
37100
|
var ASSIGNMENT_FIELDS2 = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
36556
37101
|
var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
|
|
@@ -36572,12 +37117,12 @@ function isStandaloneSession(ctx) {
|
|
|
36572
37117
|
return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && hasSessionMeta;
|
|
36573
37118
|
}
|
|
36574
37119
|
async function loadContext(ctx) {
|
|
36575
|
-
const path =
|
|
37120
|
+
const path = resolve85(ctx.cwd, ".syntaur", "context.json");
|
|
36576
37121
|
if (!await fileExists(path)) {
|
|
36577
37122
|
return { data: null, path, exists: false, parseError: null };
|
|
36578
37123
|
}
|
|
36579
37124
|
try {
|
|
36580
|
-
const raw2 = await
|
|
37125
|
+
const raw2 = await readFile56(path, "utf-8");
|
|
36581
37126
|
return { data: JSON.parse(raw2), path, exists: true, parseError: null };
|
|
36582
37127
|
} catch (err2) {
|
|
36583
37128
|
return {
|
|
@@ -36671,7 +37216,7 @@ var contextAssignmentResolves = {
|
|
|
36671
37216
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
36672
37217
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
|
|
36673
37218
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
36674
|
-
const assignmentMd =
|
|
37219
|
+
const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
|
|
36675
37220
|
if (!await fileExists(assignmentMd)) {
|
|
36676
37221
|
return {
|
|
36677
37222
|
id: this.id,
|
|
@@ -36701,10 +37246,10 @@ var contextTerminal = {
|
|
|
36701
37246
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
36702
37247
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
|
|
36703
37248
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
36704
|
-
const assignmentMd =
|
|
37249
|
+
const assignmentMd = resolve85(data.assignmentDir, "assignment.md");
|
|
36705
37250
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
36706
37251
|
try {
|
|
36707
|
-
const content = await
|
|
37252
|
+
const content = await readFile56(assignmentMd, "utf-8");
|
|
36708
37253
|
const parsed = parseAssignmentFull(content);
|
|
36709
37254
|
const terminal = terminalStatuses2(ctx);
|
|
36710
37255
|
if (terminal.has(parsed.status)) {
|
|
@@ -36850,15 +37395,15 @@ var agentChecks = [agentsResolvable];
|
|
|
36850
37395
|
// src/utils/doctor/checks/terminal.ts
|
|
36851
37396
|
init_config2();
|
|
36852
37397
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
36853
|
-
import { readFile as
|
|
36854
|
-
import { resolve as
|
|
37398
|
+
import { readFile as readFile57 } from "fs/promises";
|
|
37399
|
+
import { resolve as resolve86 } from "path";
|
|
36855
37400
|
init_paths();
|
|
36856
37401
|
init_fs();
|
|
36857
37402
|
var CATEGORY9 = "terminal";
|
|
36858
37403
|
async function readRawTerminalKey() {
|
|
36859
|
-
const configPath2 =
|
|
37404
|
+
const configPath2 = resolve86(syntaurRoot(), "config.md");
|
|
36860
37405
|
if (!await fileExists(configPath2)) return null;
|
|
36861
|
-
const content = await
|
|
37406
|
+
const content = await readFile57(configPath2, "utf-8");
|
|
36862
37407
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
36863
37408
|
if (!fmMatch) return null;
|
|
36864
37409
|
const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
|
|
@@ -37008,13 +37553,13 @@ var terminalChecks = [
|
|
|
37008
37553
|
|
|
37009
37554
|
// src/utils/doctor/checks/skills.ts
|
|
37010
37555
|
init_fs();
|
|
37011
|
-
import { resolve as
|
|
37012
|
-
import { readdir as readdir32, readFile as
|
|
37556
|
+
import { resolve as resolve87, join as join18 } from "path";
|
|
37557
|
+
import { readdir as readdir32, readFile as readFile58, lstat as lstat4 } from "fs/promises";
|
|
37013
37558
|
import { homedir as homedir15 } from "os";
|
|
37014
37559
|
var CATEGORY10 = "skills";
|
|
37015
37560
|
var skillTargets = [
|
|
37016
|
-
{ agent: "claude", dir:
|
|
37017
|
-
{ agent: "codex", dir:
|
|
37561
|
+
{ agent: "claude", dir: resolve87(homedir15(), ".claude", "skills"), label: "~/.claude/skills" },
|
|
37562
|
+
{ agent: "codex", dir: resolve87(homedir15(), ".codex", "skills"), label: "~/.codex/skills" }
|
|
37018
37563
|
];
|
|
37019
37564
|
var skillsDedupCheck = {
|
|
37020
37565
|
id: "skills.dedup",
|
|
@@ -37038,7 +37583,7 @@ var skillsDedupCheck = {
|
|
|
37038
37583
|
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
37039
37584
|
const skillMd = join18(dir, entry.name, "SKILL.md");
|
|
37040
37585
|
if (!await fileExists(skillMd)) continue;
|
|
37041
|
-
const content = await
|
|
37586
|
+
const content = await readFile58(skillMd, "utf-8").catch(() => "");
|
|
37042
37587
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
37043
37588
|
if (!match || match[1] !== entry.name) continue;
|
|
37044
37589
|
let isSymlink2 = false;
|
|
@@ -37088,12 +37633,12 @@ var skillsChecks = [skillsDedupCheck];
|
|
|
37088
37633
|
|
|
37089
37634
|
// src/utils/doctor/checks/cross-agent.ts
|
|
37090
37635
|
init_fs();
|
|
37091
|
-
import { join as join19, resolve as
|
|
37092
|
-
import { readFile as
|
|
37636
|
+
import { join as join19, resolve as resolve88 } from "path";
|
|
37637
|
+
import { readFile as readFile60 } from "fs/promises";
|
|
37093
37638
|
|
|
37094
37639
|
// src/utils/skill-frontmatter.ts
|
|
37095
37640
|
import { createHash as createHash4 } from "crypto";
|
|
37096
|
-
import { readFile as
|
|
37641
|
+
import { readFile as readFile59 } from "fs/promises";
|
|
37097
37642
|
function stripQuotes(raw2) {
|
|
37098
37643
|
const t = raw2.trim();
|
|
37099
37644
|
if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
|
|
@@ -37137,7 +37682,7 @@ function readSkillIdentity(skillMdText) {
|
|
|
37137
37682
|
return { name, hasDescription };
|
|
37138
37683
|
}
|
|
37139
37684
|
async function sha256File(path) {
|
|
37140
|
-
return createHash4("sha256").update(await
|
|
37685
|
+
return createHash4("sha256").update(await readFile59(path)).digest("hex");
|
|
37141
37686
|
}
|
|
37142
37687
|
|
|
37143
37688
|
// src/utils/doctor/checks/cross-agent.ts
|
|
@@ -37154,7 +37699,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
|
|
|
37154
37699
|
}
|
|
37155
37700
|
let text;
|
|
37156
37701
|
try {
|
|
37157
|
-
text = await
|
|
37702
|
+
text = await readFile60(installedPath, "utf-8");
|
|
37158
37703
|
} catch {
|
|
37159
37704
|
problems.push({ skill, kind: "invalid-frontmatter" });
|
|
37160
37705
|
continue;
|
|
@@ -37231,7 +37776,7 @@ var crossAgentSkillsCheck = {
|
|
|
37231
37776
|
}
|
|
37232
37777
|
if (recorded && t.instructions) {
|
|
37233
37778
|
for (const f of t.instructions.files) {
|
|
37234
|
-
const p =
|
|
37779
|
+
const p = resolve88(ctx.cwd, f.path);
|
|
37235
37780
|
if (!await fileExists(p)) {
|
|
37236
37781
|
problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
|
|
37237
37782
|
affected.push(p);
|
|
@@ -37307,7 +37852,7 @@ var crossAgentChecks = [crossAgentSkillsCheck];
|
|
|
37307
37852
|
// src/utils/doctor/checks/bundles.ts
|
|
37308
37853
|
init_fs();
|
|
37309
37854
|
init_paths();
|
|
37310
|
-
import { resolve as
|
|
37855
|
+
import { resolve as resolve89 } from "path";
|
|
37311
37856
|
import { readdir as readdir33 } from "fs/promises";
|
|
37312
37857
|
import { spawnSync as spawnSync10 } from "child_process";
|
|
37313
37858
|
init_parser2();
|
|
@@ -37337,7 +37882,7 @@ async function listScopes(ctx) {
|
|
|
37337
37882
|
if (!e.isDirectory()) continue;
|
|
37338
37883
|
const slug = e.name;
|
|
37339
37884
|
if (typeof slug !== "string" || slug.startsWith(".")) continue;
|
|
37340
|
-
const projectMd =
|
|
37885
|
+
const projectMd = resolve89(ctx.config.defaultProjectDir, slug, "project.md");
|
|
37341
37886
|
if (!await fileExists(projectMd)) continue;
|
|
37342
37887
|
out.push({
|
|
37343
37888
|
scopeLabel: `project:${slug}`,
|
|
@@ -37530,7 +38075,7 @@ var bundleChecks = [
|
|
|
37530
38075
|
];
|
|
37531
38076
|
|
|
37532
38077
|
// src/utils/doctor/checks/plugin.ts
|
|
37533
|
-
import { readFile as
|
|
38078
|
+
import { readFile as readFile61 } from "fs/promises";
|
|
37534
38079
|
import { dirname as dirname24, join as join20 } from "path";
|
|
37535
38080
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
37536
38081
|
var CATEGORY13 = "plugin";
|
|
@@ -37540,7 +38085,7 @@ async function readCliVersion() {
|
|
|
37540
38085
|
let dir = dirname24(fileURLToPath11(import.meta.url));
|
|
37541
38086
|
for (let i = 0; i < 8; i += 1) {
|
|
37542
38087
|
try {
|
|
37543
|
-
const parsed = JSON.parse(await
|
|
38088
|
+
const parsed = JSON.parse(await readFile61(join20(dir, "package.json"), "utf-8"));
|
|
37544
38089
|
if (typeof parsed.version === "string" && parsed.version.length > 0) return parsed.version;
|
|
37545
38090
|
} catch {
|
|
37546
38091
|
}
|
|
@@ -37648,6 +38193,68 @@ var deriveConfigValid = {
|
|
|
37648
38193
|
};
|
|
37649
38194
|
var deriveConfigChecks = [deriveConfigValid];
|
|
37650
38195
|
|
|
38196
|
+
// src/utils/doctor/checks/staleness.ts
|
|
38197
|
+
init_config2();
|
|
38198
|
+
import { resolve as resolve90 } from "path";
|
|
38199
|
+
import { readFile as readFile62 } from "fs/promises";
|
|
38200
|
+
var CATEGORY15 = "staleness";
|
|
38201
|
+
var stalenessConfigValid = {
|
|
38202
|
+
id: "staleness.valid",
|
|
38203
|
+
category: CATEGORY15,
|
|
38204
|
+
title: "Staleness threshold overrides are valid",
|
|
38205
|
+
async run(ctx) {
|
|
38206
|
+
let content;
|
|
38207
|
+
try {
|
|
38208
|
+
content = await readFile62(resolve90(ctx.syntaurRoot, "config.md"), "utf-8");
|
|
38209
|
+
} catch {
|
|
38210
|
+
return {
|
|
38211
|
+
id: this.id,
|
|
38212
|
+
category: this.category,
|
|
38213
|
+
title: this.title,
|
|
38214
|
+
status: "skipped",
|
|
38215
|
+
detail: "no config.md",
|
|
38216
|
+
autoFixable: false
|
|
38217
|
+
};
|
|
38218
|
+
}
|
|
38219
|
+
if (!/^\s*staleness:\s*$/m.test(content)) {
|
|
38220
|
+
return {
|
|
38221
|
+
id: this.id,
|
|
38222
|
+
category: this.category,
|
|
38223
|
+
title: this.title,
|
|
38224
|
+
status: "skipped",
|
|
38225
|
+
detail: "no staleness: block configured (using defaults)",
|
|
38226
|
+
autoFixable: false
|
|
38227
|
+
};
|
|
38228
|
+
}
|
|
38229
|
+
const problems = validateStalenessConfig(content);
|
|
38230
|
+
if (problems.length === 0) {
|
|
38231
|
+
return {
|
|
38232
|
+
id: this.id,
|
|
38233
|
+
category: this.category,
|
|
38234
|
+
title: this.title,
|
|
38235
|
+
status: "pass",
|
|
38236
|
+
detail: "staleness threshold overrides are valid",
|
|
38237
|
+
autoFixable: false
|
|
38238
|
+
};
|
|
38239
|
+
}
|
|
38240
|
+
return {
|
|
38241
|
+
id: this.id,
|
|
38242
|
+
category: this.category,
|
|
38243
|
+
title: this.title,
|
|
38244
|
+
status: "warn",
|
|
38245
|
+
detail: problems.join("; "),
|
|
38246
|
+
affected: problems,
|
|
38247
|
+
remediation: {
|
|
38248
|
+
kind: "manual",
|
|
38249
|
+
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.",
|
|
38250
|
+
command: null
|
|
38251
|
+
},
|
|
38252
|
+
autoFixable: false
|
|
38253
|
+
};
|
|
38254
|
+
}
|
|
38255
|
+
};
|
|
38256
|
+
var stalenessChecks = [stalenessConfigValid];
|
|
38257
|
+
|
|
37651
38258
|
// src/utils/doctor/registry.ts
|
|
37652
38259
|
function allChecks() {
|
|
37653
38260
|
return [
|
|
@@ -37664,7 +38271,8 @@ function allChecks() {
|
|
|
37664
38271
|
...crossAgentChecks,
|
|
37665
38272
|
...bundleChecks,
|
|
37666
38273
|
...pluginChecks,
|
|
37667
|
-
...deriveConfigChecks
|
|
38274
|
+
...deriveConfigChecks,
|
|
38275
|
+
...stalenessChecks
|
|
37668
38276
|
];
|
|
37669
38277
|
}
|
|
37670
38278
|
|
|
@@ -37750,7 +38358,7 @@ async function readVersion() {
|
|
|
37750
38358
|
let dir = dirname25(here);
|
|
37751
38359
|
for (let i = 0; i < 6; i++) {
|
|
37752
38360
|
try {
|
|
37753
|
-
const raw2 = await
|
|
38361
|
+
const raw2 = await readFile63(join21(dir, "package.json"), "utf-8");
|
|
37754
38362
|
const parsed = JSON.parse(raw2);
|
|
37755
38363
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
37756
38364
|
} catch {
|
|
@@ -37847,7 +38455,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
|
|
|
37847
38455
|
];
|
|
37848
38456
|
var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
37849
38457
|
async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
37850
|
-
const absolute = isAbsolute11(inputPath) ? inputPath :
|
|
38458
|
+
const absolute = isAbsolute11(inputPath) ? inputPath : resolve91(cwd, inputPath);
|
|
37851
38459
|
const errors = [];
|
|
37852
38460
|
const warnings = [];
|
|
37853
38461
|
if (!await fileExists(absolute)) {
|
|
@@ -37860,7 +38468,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
|
37860
38468
|
}
|
|
37861
38469
|
let content;
|
|
37862
38470
|
try {
|
|
37863
|
-
content = await
|
|
38471
|
+
content = await readFile64(absolute, "utf-8");
|
|
37864
38472
|
} catch (err2) {
|
|
37865
38473
|
return {
|
|
37866
38474
|
ok: false,
|
|
@@ -38227,8 +38835,8 @@ init_assignment_resolver();
|
|
|
38227
38835
|
init_frontmatter();
|
|
38228
38836
|
init_event_emit();
|
|
38229
38837
|
init_templates();
|
|
38230
|
-
import { resolve as
|
|
38231
|
-
import { readFile as
|
|
38838
|
+
import { resolve as resolve92 } from "path";
|
|
38839
|
+
import { readFile as readFile65 } from "fs/promises";
|
|
38232
38840
|
function shortId() {
|
|
38233
38841
|
return generateId().split("-")[0];
|
|
38234
38842
|
}
|
|
@@ -38259,7 +38867,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
38259
38867
|
if (!isValidSlug(target)) {
|
|
38260
38868
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
38261
38869
|
}
|
|
38262
|
-
assignmentDir =
|
|
38870
|
+
assignmentDir = resolve92(baseDir, options.project, "assignments", target);
|
|
38263
38871
|
assignmentRef = target;
|
|
38264
38872
|
projectSlug = options.project;
|
|
38265
38873
|
} else {
|
|
@@ -38271,13 +38879,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
38271
38879
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
38272
38880
|
projectSlug = resolved.projectSlug;
|
|
38273
38881
|
}
|
|
38274
|
-
const commentsPath =
|
|
38882
|
+
const commentsPath = resolve92(assignmentDir, "comments.md");
|
|
38275
38883
|
const timestamp = nowTimestamp();
|
|
38276
38884
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
38277
38885
|
let currentContent;
|
|
38278
38886
|
let currentCount = 0;
|
|
38279
38887
|
if (await fileExists(commentsPath)) {
|
|
38280
|
-
currentContent = await
|
|
38888
|
+
currentContent = await readFile65(commentsPath, "utf-8");
|
|
38281
38889
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
38282
38890
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
38283
38891
|
} else {
|
|
@@ -38305,9 +38913,9 @@ ${entry}`;
|
|
|
38305
38913
|
}
|
|
38306
38914
|
await writeFileForce(commentsPath, next);
|
|
38307
38915
|
try {
|
|
38308
|
-
const assignmentMd =
|
|
38916
|
+
const assignmentMd = resolve92(assignmentDir, "assignment.md");
|
|
38309
38917
|
if (await fileExists(assignmentMd)) {
|
|
38310
|
-
const fm = parseAssignmentFrontmatter(await
|
|
38918
|
+
const fm = parseAssignmentFrontmatter(await readFile65(assignmentMd, "utf-8"));
|
|
38311
38919
|
emitEvent({
|
|
38312
38920
|
assignmentId: fm.id,
|
|
38313
38921
|
projectSlug,
|
|
@@ -38331,170 +38939,15 @@ ${entry}`;
|
|
|
38331
38939
|
}
|
|
38332
38940
|
|
|
38333
38941
|
// src/commands/capture.ts
|
|
38334
|
-
import { resolve as
|
|
38335
|
-
import { copyFile as copyFile3, mkdir as mkdir12, realpath as realpath2, rm as rm14, stat as
|
|
38942
|
+
import { resolve as resolve95, relative as relative4, dirname as dirname26 } from "path";
|
|
38943
|
+
import { copyFile as copyFile3, mkdir as mkdir12, realpath as realpath2, rm as rm14, stat as stat14, writeFile as writeFile15 } from "fs/promises";
|
|
38336
38944
|
import { existsSync as existsSync7 } from "fs";
|
|
38337
|
-
|
|
38338
|
-
// src/utils/assignment-target.ts
|
|
38339
|
-
init_paths();
|
|
38340
|
-
init_fs();
|
|
38341
|
-
init_config2();
|
|
38342
|
-
init_slug();
|
|
38343
|
-
init_assignment_resolver();
|
|
38344
|
-
init_parser();
|
|
38345
|
-
import { resolve as resolve91 } from "path";
|
|
38346
|
-
import { readFile as readFile64 } from "fs/promises";
|
|
38347
|
-
var AssignmentTargetError = class extends Error {
|
|
38348
|
-
};
|
|
38349
|
-
function classifyContext(ctx) {
|
|
38350
|
-
if (!ctx) return "empty";
|
|
38351
|
-
const hasAssignment = Boolean(ctx.assignmentDir) || Boolean(ctx.assignmentSlug) || Boolean(ctx.projectSlug);
|
|
38352
|
-
if (hasAssignment) return "assignment";
|
|
38353
|
-
if (ctx.bundleId) return "bundle";
|
|
38354
|
-
if (ctx.sessionId || ctx.transcriptPath) return "standalone";
|
|
38355
|
-
return "empty";
|
|
38356
|
-
}
|
|
38357
|
-
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
38358
|
-
const path = resolve91(assignmentDir, "assignment.md");
|
|
38359
|
-
if (!await fileExists(path)) return null;
|
|
38360
|
-
try {
|
|
38361
|
-
const content = await readFile64(path, "utf-8");
|
|
38362
|
-
const [fm] = extractFrontmatter(content);
|
|
38363
|
-
return getField(fm, "id");
|
|
38364
|
-
} catch {
|
|
38365
|
-
return null;
|
|
38366
|
-
}
|
|
38367
|
-
}
|
|
38368
|
-
async function readContextJson(cwd) {
|
|
38369
|
-
const path = resolve91(cwd, ".syntaur", "context.json");
|
|
38370
|
-
if (!await fileExists(path)) return null;
|
|
38371
|
-
try {
|
|
38372
|
-
const raw2 = await readFile64(path, "utf-8");
|
|
38373
|
-
return JSON.parse(raw2);
|
|
38374
|
-
} catch {
|
|
38375
|
-
return null;
|
|
38376
|
-
}
|
|
38377
|
-
}
|
|
38378
|
-
async function resolveAssignmentTarget(input4, opts = {}) {
|
|
38379
|
-
const config = await readConfig();
|
|
38380
|
-
const baseDir = opts.dir ? expandHome(opts.dir) : config.defaultProjectDir;
|
|
38381
|
-
if (opts.project) {
|
|
38382
|
-
if (!input4) {
|
|
38383
|
-
throw new AssignmentTargetError(
|
|
38384
|
-
"--project requires an assignment slug as a positional argument."
|
|
38385
|
-
);
|
|
38386
|
-
}
|
|
38387
|
-
if (!isValidSlug(opts.project)) {
|
|
38388
|
-
throw new AssignmentTargetError(`Invalid project slug "${opts.project}".`);
|
|
38389
|
-
}
|
|
38390
|
-
if (!isValidSlug(input4)) {
|
|
38391
|
-
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
38392
|
-
}
|
|
38393
|
-
const projectDir = resolve91(baseDir, opts.project);
|
|
38394
|
-
const projectMdPath = resolve91(projectDir, "project.md");
|
|
38395
|
-
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
38396
|
-
throw new AssignmentTargetError(
|
|
38397
|
-
`Project "${opts.project}" not found at ${projectDir}.`
|
|
38398
|
-
);
|
|
38399
|
-
}
|
|
38400
|
-
const assignmentDir = resolve91(projectDir, "assignments", input4);
|
|
38401
|
-
const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
|
|
38402
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38403
|
-
throw new AssignmentTargetError(
|
|
38404
|
-
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
38405
|
-
);
|
|
38406
|
-
}
|
|
38407
|
-
const id = await readAssignmentFrontmatterId(assignmentDir) ?? input4;
|
|
38408
|
-
return {
|
|
38409
|
-
assignmentDir,
|
|
38410
|
-
projectSlug: opts.project,
|
|
38411
|
-
assignmentSlug: input4,
|
|
38412
|
-
id,
|
|
38413
|
-
standalone: false,
|
|
38414
|
-
workspaceGroup: null
|
|
38415
|
-
};
|
|
38416
|
-
}
|
|
38417
|
-
if (input4) {
|
|
38418
|
-
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), input4);
|
|
38419
|
-
if (!resolved) {
|
|
38420
|
-
throw new AssignmentTargetError(
|
|
38421
|
-
`Assignment "${input4}" not found. Provide --project <slug> + <slug> or a valid standalone UUID.`
|
|
38422
|
-
);
|
|
38423
|
-
}
|
|
38424
|
-
return resolved;
|
|
38425
|
-
}
|
|
38426
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
38427
|
-
const ctx = await readContextJson(cwd);
|
|
38428
|
-
if (!ctx) {
|
|
38429
|
-
throw new AssignmentTargetError(
|
|
38430
|
-
"No assignment specified. Provide an argument, --project + slug, or run from a directory with .syntaur/context.json."
|
|
38431
|
-
);
|
|
38432
|
-
}
|
|
38433
|
-
if (classifyContext(ctx) === "bundle" && ctx.bundleId) {
|
|
38434
|
-
throw new AssignmentTargetError(
|
|
38435
|
-
`Context is bound to bundle b:${ctx.bundleId}, not an assignment. Use \`syntaur todo bundle show ${ctx.bundleId}\` or the complete-bundle skill.`
|
|
38436
|
-
);
|
|
38437
|
-
}
|
|
38438
|
-
if (ctx.assignmentDir) {
|
|
38439
|
-
const dir = expandHome(ctx.assignmentDir);
|
|
38440
|
-
const assignmentMdPath2 = resolve91(dir, "assignment.md");
|
|
38441
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38442
|
-
throw new AssignmentTargetError(
|
|
38443
|
-
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
38444
|
-
);
|
|
38445
|
-
}
|
|
38446
|
-
const id = await readAssignmentFrontmatterId(dir);
|
|
38447
|
-
if (!id || id.trim() === "") {
|
|
38448
|
-
throw new AssignmentTargetError(
|
|
38449
|
-
`.syntaur/context.json points to an assignment with no frontmatter \`id\`: ${dir}.`
|
|
38450
|
-
);
|
|
38451
|
-
}
|
|
38452
|
-
const assignmentSlug = ctx.assignmentSlug ?? dir.split("/").pop() ?? "";
|
|
38453
|
-
const projectSlug = ctx.projectSlug ?? null;
|
|
38454
|
-
return {
|
|
38455
|
-
assignmentDir: dir,
|
|
38456
|
-
projectSlug,
|
|
38457
|
-
assignmentSlug,
|
|
38458
|
-
id,
|
|
38459
|
-
standalone: projectSlug === null,
|
|
38460
|
-
workspaceGroup: null
|
|
38461
|
-
};
|
|
38462
|
-
}
|
|
38463
|
-
if (ctx.projectSlug && ctx.assignmentSlug) {
|
|
38464
|
-
if (!isValidSlug(ctx.projectSlug) || !isValidSlug(ctx.assignmentSlug)) {
|
|
38465
|
-
throw new AssignmentTargetError(
|
|
38466
|
-
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
38467
|
-
);
|
|
38468
|
-
}
|
|
38469
|
-
const assignmentDir = resolve91(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
38470
|
-
const assignmentMdPath2 = resolve91(assignmentDir, "assignment.md");
|
|
38471
|
-
if (!await fileExists(assignmentMdPath2)) {
|
|
38472
|
-
throw new AssignmentTargetError(
|
|
38473
|
-
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
38474
|
-
);
|
|
38475
|
-
}
|
|
38476
|
-
const id = await readAssignmentFrontmatterId(assignmentDir) ?? ctx.assignmentSlug;
|
|
38477
|
-
return {
|
|
38478
|
-
assignmentDir,
|
|
38479
|
-
projectSlug: ctx.projectSlug,
|
|
38480
|
-
assignmentSlug: ctx.assignmentSlug,
|
|
38481
|
-
id,
|
|
38482
|
-
standalone: false,
|
|
38483
|
-
workspaceGroup: null
|
|
38484
|
-
};
|
|
38485
|
-
}
|
|
38486
|
-
throw new AssignmentTargetError(
|
|
38487
|
-
".syntaur/context.json exists but contains neither assignmentDir nor projectSlug+assignmentSlug."
|
|
38488
|
-
);
|
|
38489
|
-
}
|
|
38490
|
-
|
|
38491
|
-
// src/commands/capture.ts
|
|
38492
38945
|
init_paths();
|
|
38493
38946
|
init_fs();
|
|
38494
38947
|
|
|
38495
38948
|
// src/utils/screencapture.ts
|
|
38496
38949
|
import { spawn as spawn8 } from "child_process";
|
|
38497
|
-
import { mkdtemp as mkdtemp2, rm as rm10, stat as
|
|
38950
|
+
import { mkdtemp as mkdtemp2, rm as rm10, stat as stat12 } from "fs/promises";
|
|
38498
38951
|
import { tmpdir as tmpdir3 } from "os";
|
|
38499
38952
|
import { join as join22 } from "path";
|
|
38500
38953
|
function argsFor(mode, pngPath) {
|
|
@@ -38552,7 +39005,7 @@ async function captureScreenshot(mode) {
|
|
|
38552
39005
|
}
|
|
38553
39006
|
let size = 0;
|
|
38554
39007
|
try {
|
|
38555
|
-
size = (await
|
|
39008
|
+
size = (await stat12(pngPath)).size;
|
|
38556
39009
|
} catch {
|
|
38557
39010
|
throw new Error("screencapture exited 0 but produced no image.");
|
|
38558
39011
|
}
|
|
@@ -38568,7 +39021,7 @@ async function captureScreenshot(mode) {
|
|
|
38568
39021
|
|
|
38569
39022
|
// src/utils/asciinema.ts
|
|
38570
39023
|
import { spawn as spawn9 } from "child_process";
|
|
38571
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
39024
|
+
import { mkdtemp as mkdtemp3, readFile as readFile66, rm as rm11 } from "fs/promises";
|
|
38572
39025
|
import { tmpdir as tmpdir4 } from "os";
|
|
38573
39026
|
import { join as join23 } from "path";
|
|
38574
39027
|
var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
@@ -38631,7 +39084,7 @@ async function captureAsciinema(opts) {
|
|
|
38631
39084
|
}
|
|
38632
39085
|
throw err2;
|
|
38633
39086
|
}
|
|
38634
|
-
const text = await
|
|
39087
|
+
const text = await readFile66(castPath, "utf8").catch(() => null);
|
|
38635
39088
|
if (text === null) {
|
|
38636
39089
|
throw new Error(
|
|
38637
39090
|
`asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
|
|
@@ -38659,9 +39112,9 @@ async function captureAsciinema(opts) {
|
|
|
38659
39112
|
// src/utils/recording.ts
|
|
38660
39113
|
init_paths();
|
|
38661
39114
|
import { spawn as spawn10 } from "child_process";
|
|
38662
|
-
import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as
|
|
39115
|
+
import { mkdir as mkdir11, mkdtemp as mkdtemp4, open as open6, readFile as readFile67, rm as rm12, stat as stat13, unlink as unlink12, writeFile as writeFile14 } from "fs/promises";
|
|
38663
39116
|
import { tmpdir as tmpdir5 } from "os";
|
|
38664
|
-
import { join as join24, resolve as
|
|
39117
|
+
import { join as join24, resolve as resolve93 } from "path";
|
|
38665
39118
|
import { setTimeout as sleep } from "timers/promises";
|
|
38666
39119
|
function sigintPollIntervalMs() {
|
|
38667
39120
|
const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
|
|
@@ -38682,13 +39135,13 @@ function sigtermWaitMs() {
|
|
|
38682
39135
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
|
|
38683
39136
|
}
|
|
38684
39137
|
function pidfilePath() {
|
|
38685
|
-
return
|
|
39138
|
+
return resolve93(syntaurRoot(), "recording.pid");
|
|
38686
39139
|
}
|
|
38687
39140
|
function logPath2() {
|
|
38688
|
-
return
|
|
39141
|
+
return resolve93(syntaurRoot(), "recording.log");
|
|
38689
39142
|
}
|
|
38690
39143
|
function sidecarPath() {
|
|
38691
|
-
return
|
|
39144
|
+
return resolve93(syntaurRoot(), "recording.json");
|
|
38692
39145
|
}
|
|
38693
39146
|
function ffmpegArgs(device, fps, mp4Path) {
|
|
38694
39147
|
return [
|
|
@@ -38733,7 +39186,7 @@ async function acquirePidfile(pidfile) {
|
|
|
38733
39186
|
} catch (err2) {
|
|
38734
39187
|
if (err2.code !== "EEXIST") throw err2;
|
|
38735
39188
|
if (attempt === 1) throw err2;
|
|
38736
|
-
const existing = (await
|
|
39189
|
+
const existing = (await readFile67(pidfile, "utf-8").catch(() => "")).trim();
|
|
38737
39190
|
if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
|
|
38738
39191
|
const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
|
|
38739
39192
|
const parentPid = Number.parseInt(parentPidRaw, 10);
|
|
@@ -38835,7 +39288,7 @@ async function startRecording(input4) {
|
|
|
38835
39288
|
logHandle = null;
|
|
38836
39289
|
if (warmupMs > 0) await sleep(warmupMs);
|
|
38837
39290
|
if (!await isProcessAlive(pid)) {
|
|
38838
|
-
const tail = await
|
|
39291
|
+
const tail = await readFile67(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
|
|
38839
39292
|
acquiredPid = null;
|
|
38840
39293
|
throw new Error(
|
|
38841
39294
|
`ffmpeg exited during startup \u2014 likely macOS Screen Recording permission missing. Grant access to your terminal in System Settings \u2192 Privacy & Security \u2192 Screen Recording, then retry. Log: ${log}
|
|
@@ -38892,7 +39345,7 @@ ${tail}`
|
|
|
38892
39345
|
async function stopRecording() {
|
|
38893
39346
|
const pidfile = pidfilePath();
|
|
38894
39347
|
const sidecar = sidecarPath();
|
|
38895
|
-
const pidRaw = await
|
|
39348
|
+
const pidRaw = await readFile67(pidfile, "utf-8").catch(() => null);
|
|
38896
39349
|
if (pidRaw === null) {
|
|
38897
39350
|
throw new Error(
|
|
38898
39351
|
`No active recording found (no pidfile at ${pidfile}). Did you run --start?`
|
|
@@ -38902,7 +39355,7 @@ async function stopRecording() {
|
|
|
38902
39355
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
38903
39356
|
throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
|
|
38904
39357
|
}
|
|
38905
|
-
const sidecarRaw = await
|
|
39358
|
+
const sidecarRaw = await readFile67(sidecar, "utf-8").catch(() => null);
|
|
38906
39359
|
if (sidecarRaw === null) {
|
|
38907
39360
|
throw new Error(
|
|
38908
39361
|
`No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
|
|
@@ -38947,7 +39400,7 @@ async function stopRecording() {
|
|
|
38947
39400
|
if (alive) {
|
|
38948
39401
|
throw new Error(`ffmpeg (PID ${pid}) refused to die after SIGKILL`);
|
|
38949
39402
|
}
|
|
38950
|
-
const st = await
|
|
39403
|
+
const st = await stat13(sidecarData.mp4Path).catch(() => null);
|
|
38951
39404
|
if (st === null || st.size === 0) {
|
|
38952
39405
|
await unlink12(pidfile).catch(() => {
|
|
38953
39406
|
});
|
|
@@ -38967,7 +39420,7 @@ async function stopRecording() {
|
|
|
38967
39420
|
// src/db/proof-db.ts
|
|
38968
39421
|
init_paths();
|
|
38969
39422
|
import Database6 from "better-sqlite3";
|
|
38970
|
-
import { resolve as
|
|
39423
|
+
import { resolve as resolve94 } from "path";
|
|
38971
39424
|
var db5 = null;
|
|
38972
39425
|
var PROOF_SCHEMA_VERSION = "1";
|
|
38973
39426
|
var SCHEMA_SQL5 = `
|
|
@@ -38987,7 +39440,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
38987
39440
|
`;
|
|
38988
39441
|
function initProofDb(dbPath) {
|
|
38989
39442
|
if (db5) return db5;
|
|
38990
|
-
const finalPath = dbPath ??
|
|
39443
|
+
const finalPath = dbPath ?? resolve94(syntaurRoot(), "syntaur.db");
|
|
38991
39444
|
db5 = new Database6(finalPath);
|
|
38992
39445
|
db5.pragma("journal_mode = WAL");
|
|
38993
39446
|
db5.exec(SCHEMA_SQL5);
|
|
@@ -39035,7 +39488,7 @@ function listArtifactsByAssignment(assignmentId) {
|
|
|
39035
39488
|
|
|
39036
39489
|
// src/utils/transcribers/elevenlabs.ts
|
|
39037
39490
|
import { spawn as spawn11 } from "child_process";
|
|
39038
|
-
import { mkdtemp as mkdtemp5, readFile as
|
|
39491
|
+
import { mkdtemp as mkdtemp5, readFile as readFile68, rm as rm13 } from "fs/promises";
|
|
39039
39492
|
import { tmpdir as tmpdir6 } from "os";
|
|
39040
39493
|
import { join as join25 } from "path";
|
|
39041
39494
|
var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
|
|
@@ -39099,7 +39552,7 @@ async function extractAudio(videoAbsPath, wavOut) {
|
|
|
39099
39552
|
throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
|
|
39100
39553
|
}
|
|
39101
39554
|
async function callScribe(wavPath, apiKey, opts) {
|
|
39102
|
-
const audio = await
|
|
39555
|
+
const audio = await readFile68(wavPath);
|
|
39103
39556
|
const form = new FormData();
|
|
39104
39557
|
form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
|
|
39105
39558
|
form.set("model_id", "scribe_v1");
|
|
@@ -39415,7 +39868,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39415
39868
|
});
|
|
39416
39869
|
}
|
|
39417
39870
|
if (options.file) {
|
|
39418
|
-
const expanded = options.file.startsWith("~/") ?
|
|
39871
|
+
const expanded = options.file.startsWith("~/") ? resolve95(process.env.HOME ?? "", options.file.slice(2)) : resolve95(options.file);
|
|
39419
39872
|
if (!await fileExists(expanded)) {
|
|
39420
39873
|
throw new Error(`--file does not exist: ${options.file}`);
|
|
39421
39874
|
}
|
|
@@ -39427,7 +39880,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39427
39880
|
`--file is unreadable: ${options.file} (${e instanceof Error ? e.message : String(e)})`
|
|
39428
39881
|
);
|
|
39429
39882
|
}
|
|
39430
|
-
const st = await
|
|
39883
|
+
const st = await stat14(real);
|
|
39431
39884
|
if (!st.isFile()) {
|
|
39432
39885
|
throw new Error(`--file is not a regular file: ${options.file}`);
|
|
39433
39886
|
}
|
|
@@ -39456,7 +39909,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39456
39909
|
}
|
|
39457
39910
|
initProofDb();
|
|
39458
39911
|
const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
|
|
39459
|
-
const destDir =
|
|
39912
|
+
const destDir = resolve95(proofDir(resolved.assignmentDir), subdir);
|
|
39460
39913
|
if (resolvedSource) await mkdir12(destDir, { recursive: true });
|
|
39461
39914
|
const ext = resolvedSource ? extensionForKind(kind) : null;
|
|
39462
39915
|
let id = null;
|
|
@@ -39465,7 +39918,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39465
39918
|
let lastErr = null;
|
|
39466
39919
|
for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
|
|
39467
39920
|
const candidate = generateArtifactId();
|
|
39468
|
-
const candidateAbsPath = resolvedSource && ext ?
|
|
39921
|
+
const candidateAbsPath = resolvedSource && ext ? resolve95(destDir, `${candidate}.${ext}`) : null;
|
|
39469
39922
|
const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
|
|
39470
39923
|
try {
|
|
39471
39924
|
insertArtifact({
|
|
@@ -39509,7 +39962,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39509
39962
|
}
|
|
39510
39963
|
}
|
|
39511
39964
|
if (options.transcribe && kind === "video" && absPath && id) {
|
|
39512
|
-
const sidecarPath2 =
|
|
39965
|
+
const sidecarPath2 = resolve95(destDir, `${id}.transcript.md`);
|
|
39513
39966
|
if (existsSync7(sidecarPath2)) {
|
|
39514
39967
|
console.warn(
|
|
39515
39968
|
`transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
|
|
@@ -39541,7 +39994,7 @@ async function captureCommand(target, options = {}) {
|
|
|
39541
39994
|
const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
|
|
39542
39995
|
console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
|
|
39543
39996
|
if (relativeFilePath) {
|
|
39544
|
-
console.log(` file: ${
|
|
39997
|
+
console.log(` file: ${resolve95(resolved.assignmentDir, relativeFilePath)}`);
|
|
39545
39998
|
}
|
|
39546
39999
|
} catch (err2) {
|
|
39547
40000
|
if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
|
|
@@ -39564,8 +40017,8 @@ async function captureCommand(target, options = {}) {
|
|
|
39564
40017
|
|
|
39565
40018
|
// src/commands/proof.ts
|
|
39566
40019
|
import { Command as Command6 } from "commander";
|
|
39567
|
-
import { readFile as
|
|
39568
|
-
import { resolve as
|
|
40020
|
+
import { readFile as readFile69, writeFile as writeFile16, rename as rename10, stat as stat15 } from "fs/promises";
|
|
40021
|
+
import { resolve as resolve96, relative as relative5, isAbsolute as isAbsolute12, dirname as dirname27 } from "path";
|
|
39569
40022
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
39570
40023
|
|
|
39571
40024
|
// src/utils/acceptance-criteria-parse.ts
|
|
@@ -39805,11 +40258,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
|
|
|
39805
40258
|
|
|
39806
40259
|
// src/commands/proof.ts
|
|
39807
40260
|
async function readAssignmentMeta(assignmentDir) {
|
|
39808
|
-
const path =
|
|
40261
|
+
const path = resolve96(assignmentDir, "assignment.md");
|
|
39809
40262
|
if (!await fileExists(path)) {
|
|
39810
40263
|
return { title: "", body: "" };
|
|
39811
40264
|
}
|
|
39812
|
-
const content = await
|
|
40265
|
+
const content = await readFile69(path, "utf-8");
|
|
39813
40266
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
39814
40267
|
let title = "";
|
|
39815
40268
|
if (fmMatch) {
|
|
@@ -39856,7 +40309,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
39856
40309
|
for (const r of rows) {
|
|
39857
40310
|
if (!r.file_path) continue;
|
|
39858
40311
|
if (r.kind !== "http" && r.kind !== "text") continue;
|
|
39859
|
-
const abs =
|
|
40312
|
+
const abs = resolve96(assignmentDir, r.file_path);
|
|
39860
40313
|
if (!isWithin(proofRoot, abs)) {
|
|
39861
40314
|
out.set(r.file_path, null);
|
|
39862
40315
|
continue;
|
|
@@ -39865,13 +40318,13 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
39865
40318
|
out.set(r.file_path, null);
|
|
39866
40319
|
continue;
|
|
39867
40320
|
}
|
|
39868
|
-
const st = await
|
|
40321
|
+
const st = await stat15(abs);
|
|
39869
40322
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) {
|
|
39870
40323
|
out.set(r.file_path, null);
|
|
39871
40324
|
continue;
|
|
39872
40325
|
}
|
|
39873
40326
|
try {
|
|
39874
|
-
out.set(r.file_path, await
|
|
40327
|
+
out.set(r.file_path, await readFile69(abs, "utf-8"));
|
|
39875
40328
|
} catch {
|
|
39876
40329
|
out.set(r.file_path, null);
|
|
39877
40330
|
}
|
|
@@ -39883,14 +40336,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
|
|
|
39883
40336
|
const proofRoot = proofDir(assignmentDir);
|
|
39884
40337
|
for (const r of rows) {
|
|
39885
40338
|
if (r.kind !== "video" || !r.file_path) continue;
|
|
39886
|
-
const videoAbs =
|
|
39887
|
-
const sidecar =
|
|
40339
|
+
const videoAbs = resolve96(assignmentDir, r.file_path);
|
|
40340
|
+
const sidecar = resolve96(dirname27(videoAbs), `${r.id}.transcript.md`);
|
|
39888
40341
|
if (!isWithin(proofRoot, sidecar)) continue;
|
|
39889
40342
|
if (!await fileExists(sidecar)) continue;
|
|
39890
|
-
const st = await
|
|
40343
|
+
const st = await stat15(sidecar);
|
|
39891
40344
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
|
|
39892
40345
|
try {
|
|
39893
|
-
out.set(r.id, await
|
|
40346
|
+
out.set(r.id, await readFile69(sidecar, "utf-8"));
|
|
39894
40347
|
} catch {
|
|
39895
40348
|
}
|
|
39896
40349
|
}
|
|
@@ -39924,8 +40377,8 @@ async function proofBuildCommand(target, options = {}) {
|
|
|
39924
40377
|
};
|
|
39925
40378
|
const md = renderProofMarkdown(renderParams);
|
|
39926
40379
|
const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
|
|
39927
|
-
const mdPath =
|
|
39928
|
-
const htmlPath =
|
|
40380
|
+
const mdPath = resolve96(resolved.assignmentDir, "proof.md");
|
|
40381
|
+
const htmlPath = resolve96(resolved.assignmentDir, "proof.html");
|
|
39929
40382
|
await atomicWrite(mdPath, md);
|
|
39930
40383
|
await atomicWrite(htmlPath, html);
|
|
39931
40384
|
console.log(`Wrote ${htmlPath}`);
|
|
@@ -40468,7 +40921,7 @@ function isScheduledSessionLive(sessionId, launchPid) {
|
|
|
40468
40921
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
40469
40922
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync as rmSync2, realpathSync as realpathSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
40470
40923
|
import { homedir as homedir16, userInfo } from "os";
|
|
40471
|
-
import { dirname as dirname28, join as join26, resolve as
|
|
40924
|
+
import { dirname as dirname28, join as join26, resolve as resolve97 } from "path";
|
|
40472
40925
|
var LAUNCH_AGENT_LABEL = "com.syntaur.schedule.tick";
|
|
40473
40926
|
var LaunchAgentRefusalError = class extends Error {
|
|
40474
40927
|
constructor(message) {
|
|
@@ -40522,7 +40975,7 @@ function absolutize(p) {
|
|
|
40522
40975
|
try {
|
|
40523
40976
|
return realpathSync4(p);
|
|
40524
40977
|
} catch {
|
|
40525
|
-
return
|
|
40978
|
+
return resolve97(p);
|
|
40526
40979
|
}
|
|
40527
40980
|
}
|
|
40528
40981
|
function defaultRun(command, args) {
|
|
@@ -40583,7 +41036,7 @@ function uninstallLaunchAgent(deps = {}) {
|
|
|
40583
41036
|
// src/commands/schedule.ts
|
|
40584
41037
|
init_timestamp();
|
|
40585
41038
|
var DURATION_REGEX2 = /^(\d+)\s*(s|m|h|d)?$/i;
|
|
40586
|
-
function
|
|
41039
|
+
function parseDurationMs2(input4) {
|
|
40587
41040
|
const m = DURATION_REGEX2.exec(input4.trim());
|
|
40588
41041
|
if (!m) throw new Error(`invalid duration "${input4}" \u2014 use e.g. 30s, 5m, 2h, 1d`);
|
|
40589
41042
|
const n = Number.parseInt(m[1], 10);
|
|
@@ -40604,7 +41057,7 @@ function validateTerminalChoice(value) {
|
|
|
40604
41057
|
function buildTrigger(opts) {
|
|
40605
41058
|
const chosen = [];
|
|
40606
41059
|
if (opts.at) chosen.push({ kind: "at", at: opts.at });
|
|
40607
|
-
if (opts.in) chosen.push({ kind: "in", durationMs:
|
|
41060
|
+
if (opts.in) chosen.push({ kind: "in", durationMs: parseDurationMs2(opts.in), anchorIso: nowTimestamp() });
|
|
40608
41061
|
if (opts.cron) {
|
|
40609
41062
|
try {
|
|
40610
41063
|
new Cron2(opts.cron).nextRun();
|
|
@@ -40672,7 +41125,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
|
|
|
40672
41125
|
const unattended = !opts.interactive;
|
|
40673
41126
|
if (unattended) assertUnattendedTerminalSupported(terminal);
|
|
40674
41127
|
const limits = defaultLimits();
|
|
40675
|
-
if (opts.maxRuntime) limits.maxRuntimeMs =
|
|
41128
|
+
if (opts.maxRuntime) limits.maxRuntimeMs = parseDurationMs2(opts.maxRuntime);
|
|
40676
41129
|
if (opts.maxLaunchesPerDay) {
|
|
40677
41130
|
const n = Number(opts.maxLaunchesPerDay);
|
|
40678
41131
|
if (!Number.isInteger(n) || n <= 0) {
|
|
@@ -40680,7 +41133,7 @@ scheduleCommand.command("create").description("Create a scheduled job").required
|
|
|
40680
41133
|
}
|
|
40681
41134
|
limits.maxLaunchesPerDay = n;
|
|
40682
41135
|
}
|
|
40683
|
-
if (opts.cooldown) limits.cooldownMs =
|
|
41136
|
+
if (opts.cooldown) limits.cooldownMs = parseDurationMs2(opts.cooldown);
|
|
40684
41137
|
const now = nowTimestamp();
|
|
40685
41138
|
const job = {
|
|
40686
41139
|
id: newJobId(),
|
|
@@ -40898,8 +41351,8 @@ init_slug();
|
|
|
40898
41351
|
init_timestamp();
|
|
40899
41352
|
init_assignment_resolver();
|
|
40900
41353
|
init_assignment_todos();
|
|
40901
|
-
import { resolve as
|
|
40902
|
-
import { readFile as
|
|
41354
|
+
import { resolve as resolve98 } from "path";
|
|
41355
|
+
import { readFile as readFile70 } from "fs/promises";
|
|
40903
41356
|
async function requestCommand(target, text, options = {}) {
|
|
40904
41357
|
if (!text || !text.trim()) {
|
|
40905
41358
|
throw new Error("Request text cannot be empty.");
|
|
@@ -40915,7 +41368,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40915
41368
|
if (!isValidSlug(target)) {
|
|
40916
41369
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
40917
41370
|
}
|
|
40918
|
-
assignmentDir =
|
|
41371
|
+
assignmentDir = resolve98(baseDir, options.project, "assignments", target);
|
|
40919
41372
|
targetRef = target;
|
|
40920
41373
|
} else {
|
|
40921
41374
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -40925,12 +41378,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40925
41378
|
assignmentDir = resolved.assignmentDir;
|
|
40926
41379
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
40927
41380
|
}
|
|
40928
|
-
const assignmentMdPath2 =
|
|
41381
|
+
const assignmentMdPath2 = resolve98(assignmentDir, "assignment.md");
|
|
40929
41382
|
if (!await fileExists(assignmentMdPath2)) {
|
|
40930
41383
|
throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
|
|
40931
41384
|
}
|
|
40932
41385
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
40933
|
-
let content = await
|
|
41386
|
+
let content = await readFile70(assignmentMdPath2, "utf-8");
|
|
40934
41387
|
content = appendTodosToAssignmentBody(content, [
|
|
40935
41388
|
{ description: `${text.trim()} (from: ${source})` }
|
|
40936
41389
|
]);
|
|
@@ -40943,14 +41396,15 @@ async function requestCommand(target, text, options = {}) {
|
|
|
40943
41396
|
init_fs();
|
|
40944
41397
|
init_paths();
|
|
40945
41398
|
init_config2();
|
|
41399
|
+
init_recompute();
|
|
40946
41400
|
import { Command as Command10 } from "commander";
|
|
40947
|
-
import { readFile as
|
|
40948
|
-
import { resolve as
|
|
41401
|
+
import { readFile as readFile71, readdir as readdir34 } from "fs/promises";
|
|
41402
|
+
import { resolve as resolve99 } from "path";
|
|
40949
41403
|
async function readContextAssignmentDir(cwd) {
|
|
40950
|
-
const path =
|
|
41404
|
+
const path = resolve99(cwd, ".syntaur", "context.json");
|
|
40951
41405
|
if (!await fileExists(path)) return null;
|
|
40952
41406
|
try {
|
|
40953
|
-
const raw2 = await
|
|
41407
|
+
const raw2 = await readFile71(path, "utf-8");
|
|
40954
41408
|
const ctx = JSON.parse(raw2);
|
|
40955
41409
|
if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
|
|
40956
41410
|
return ctx.assignmentDir;
|
|
@@ -40964,9 +41418,9 @@ async function resolveAssignmentDir(opts) {
|
|
|
40964
41418
|
const cwd = opts.cwd ?? process.cwd();
|
|
40965
41419
|
if (opts.assignment) {
|
|
40966
41420
|
if (opts.project) {
|
|
40967
|
-
return
|
|
41421
|
+
return resolve99((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
|
|
40968
41422
|
}
|
|
40969
|
-
return
|
|
41423
|
+
return resolve99(assignmentsDir(), opts.assignment);
|
|
40970
41424
|
}
|
|
40971
41425
|
const fromCtx = await readContextAssignmentDir(cwd);
|
|
40972
41426
|
if (fromCtx) return fromCtx;
|
|
@@ -41162,17 +41616,17 @@ async function runPlanCreate(options) {
|
|
|
41162
41616
|
if (!await fileExists(assignmentDir)) {
|
|
41163
41617
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
41164
41618
|
}
|
|
41165
|
-
const assignmentMdPath2 =
|
|
41619
|
+
const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
|
|
41166
41620
|
if (!await fileExists(assignmentMdPath2)) {
|
|
41167
41621
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
41168
41622
|
}
|
|
41169
|
-
const planPath =
|
|
41623
|
+
const planPath = resolve99(assignmentDir, "plan.md");
|
|
41170
41624
|
if (await fileExists(planPath) && !options.force) {
|
|
41171
41625
|
throw new Error(
|
|
41172
41626
|
"plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
|
|
41173
41627
|
);
|
|
41174
41628
|
}
|
|
41175
|
-
const assignmentMd = await
|
|
41629
|
+
const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
|
|
41176
41630
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
41177
41631
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
41178
41632
|
await writeFileForce(planPath, buildInitialPlanStub(slug));
|
|
@@ -41186,13 +41640,14 @@ async function runPlanCreate(options) {
|
|
|
41186
41640
|
}
|
|
41187
41641
|
console.log(`Created ${planPath}`);
|
|
41188
41642
|
if (appended > 0) console.log(`Appended ${appended} plan todo(s) to assignment.md.`);
|
|
41643
|
+
await recomputeAssignmentDir(assignmentDir, "plan-create", null);
|
|
41189
41644
|
}
|
|
41190
41645
|
async function runPlanVersion(options) {
|
|
41191
41646
|
const assignmentDir = await resolveAssignmentDir(options);
|
|
41192
41647
|
if (!await fileExists(assignmentDir)) {
|
|
41193
41648
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
41194
41649
|
}
|
|
41195
|
-
const assignmentMdPath2 =
|
|
41650
|
+
const assignmentMdPath2 = resolve99(assignmentDir, "assignment.md");
|
|
41196
41651
|
if (!await fileExists(assignmentMdPath2)) {
|
|
41197
41652
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
41198
41653
|
}
|
|
@@ -41204,15 +41659,15 @@ async function runPlanVersion(options) {
|
|
|
41204
41659
|
}
|
|
41205
41660
|
const current = planFiles[planFiles.length - 1];
|
|
41206
41661
|
const next = nextPlanFileName(current.version);
|
|
41207
|
-
const newPath =
|
|
41662
|
+
const newPath = resolve99(assignmentDir, next.fileName);
|
|
41208
41663
|
if (await fileExists(newPath) && !options.force) {
|
|
41209
41664
|
throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
|
|
41210
41665
|
}
|
|
41211
|
-
const assignmentMd = await
|
|
41666
|
+
const assignmentMd = await readFile71(assignmentMdPath2, "utf-8");
|
|
41212
41667
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
41213
41668
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
41214
|
-
const oldPlanPath =
|
|
41215
|
-
const oldPlanContent = await
|
|
41669
|
+
const oldPlanPath = resolve99(assignmentDir, current.fileName);
|
|
41670
|
+
const oldPlanContent = await readFile71(oldPlanPath, "utf-8");
|
|
41216
41671
|
const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
|
|
41217
41672
|
const carriedTodos = extractUncheckedTodos(oldBody);
|
|
41218
41673
|
const stub = buildNewPlanStub({
|
|
@@ -41233,6 +41688,7 @@ async function runPlanVersion(options) {
|
|
|
41233
41688
|
);
|
|
41234
41689
|
console.log(`Path: ${newPath}`);
|
|
41235
41690
|
console.log(`Carried forward: ${carriedTodos.length} unchecked task(s).`);
|
|
41691
|
+
await recomputeAssignmentDir(assignmentDir, "plan-version", null);
|
|
41236
41692
|
}
|
|
41237
41693
|
var planCommand = new Command10("plan").description("Manage plan files for the active assignment");
|
|
41238
41694
|
planCommand.command("create").description("Create the initial plan.md and append the plan todo-cycle to assignment.md ## Todos").option("--assignment <slug>", "Assignment slug (UUID for standalone). Defaults to .syntaur/context.json").option("--project <slug>", "Project slug. Required when --assignment is given for a project-nested assignment").option("--force", "Overwrite an existing plan.md").action(async (options) => {
|
|
@@ -41282,28 +41738,28 @@ init_cwd();
|
|
|
41282
41738
|
init_session_db();
|
|
41283
41739
|
init_agent_sessions();
|
|
41284
41740
|
import { Command as Command11 } from "commander";
|
|
41285
|
-
import { readFile as
|
|
41286
|
-
import { resolve as
|
|
41741
|
+
import { readFile as readFile72, readdir as readdir35, stat as stat16 } from "fs/promises";
|
|
41742
|
+
import { resolve as resolve100 } from "path";
|
|
41287
41743
|
async function readContext(cwd) {
|
|
41288
|
-
const path =
|
|
41744
|
+
const path = resolve100(cwd, ".syntaur", "context.json");
|
|
41289
41745
|
if (!await fileExists(path)) return null;
|
|
41290
41746
|
try {
|
|
41291
|
-
const raw2 = await
|
|
41747
|
+
const raw2 = await readFile72(path, "utf-8");
|
|
41292
41748
|
return JSON.parse(raw2);
|
|
41293
41749
|
} catch {
|
|
41294
41750
|
return null;
|
|
41295
41751
|
}
|
|
41296
41752
|
}
|
|
41297
41753
|
async function findLatestSessionSummary(assignmentDir) {
|
|
41298
|
-
const sessionsRoot =
|
|
41754
|
+
const sessionsRoot = resolve100(assignmentDir, "sessions");
|
|
41299
41755
|
if (!await fileExists(sessionsRoot)) return null;
|
|
41300
41756
|
const entries = await readdir35(sessionsRoot, { withFileTypes: true });
|
|
41301
41757
|
let best = null;
|
|
41302
41758
|
for (const entry of entries) {
|
|
41303
41759
|
if (!entry.isDirectory()) continue;
|
|
41304
|
-
const summaryPath =
|
|
41760
|
+
const summaryPath = resolve100(sessionsRoot, entry.name, "summary.md");
|
|
41305
41761
|
if (!await fileExists(summaryPath)) continue;
|
|
41306
|
-
const st = await
|
|
41762
|
+
const st = await stat16(summaryPath);
|
|
41307
41763
|
if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
|
|
41308
41764
|
best = { sessionId: entry.name, path: summaryPath, mtime: st.mtime };
|
|
41309
41765
|
}
|
|
@@ -41311,9 +41767,9 @@ async function findLatestSessionSummary(assignmentDir) {
|
|
|
41311
41767
|
return best;
|
|
41312
41768
|
}
|
|
41313
41769
|
async function findOpenHandoff(assignmentDir) {
|
|
41314
|
-
const handoffPath =
|
|
41770
|
+
const handoffPath = resolve100(assignmentDir, "handoff.md");
|
|
41315
41771
|
if (!await fileExists(handoffPath)) return null;
|
|
41316
|
-
const content = await
|
|
41772
|
+
const content = await readFile72(handoffPath, "utf-8");
|
|
41317
41773
|
const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
|
|
41318
41774
|
if (body.length === 0) return null;
|
|
41319
41775
|
if (/^<!--[\s\S]*-->$/.test(body)) return null;
|
|
@@ -41408,7 +41864,7 @@ async function resolveSaveTarget(options, cwd) {
|
|
|
41408
41864
|
let slug;
|
|
41409
41865
|
const ctx = await readContext(cwd);
|
|
41410
41866
|
if (options.assignment) {
|
|
41411
|
-
assignmentDir = options.project ?
|
|
41867
|
+
assignmentDir = options.project ? resolve100((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve100(assignmentsDir(), options.assignment);
|
|
41412
41868
|
slug = options.assignment;
|
|
41413
41869
|
} else {
|
|
41414
41870
|
if (!ctx?.assignmentDir) {
|
|
@@ -41465,21 +41921,21 @@ function extractCreated(content) {
|
|
|
41465
41921
|
}
|
|
41466
41922
|
async function runSessionSave(options, cwd = process.cwd(), body) {
|
|
41467
41923
|
const { assignmentDir, slug, sessionId } = await resolveSaveTarget(options, cwd);
|
|
41468
|
-
if (!await fileExists(
|
|
41924
|
+
if (!await fileExists(resolve100(assignmentDir, "assignment.md"))) {
|
|
41469
41925
|
throw new Error(`No assignment found at ${assignmentDir} (missing assignment.md).`);
|
|
41470
41926
|
}
|
|
41471
|
-
const sessionDir =
|
|
41472
|
-
const summaryPath =
|
|
41927
|
+
const sessionDir = resolve100(assignmentDir, "sessions", sessionId);
|
|
41928
|
+
const summaryPath = resolve100(sessionDir, "summary.md");
|
|
41473
41929
|
const now = nowTimestamp();
|
|
41474
41930
|
let created = now;
|
|
41475
41931
|
if (await fileExists(summaryPath)) {
|
|
41476
|
-
const existing = await
|
|
41932
|
+
const existing = await readFile72(summaryPath, "utf-8");
|
|
41477
41933
|
created = extractCreated(existing) ?? now;
|
|
41478
41934
|
}
|
|
41479
41935
|
let sectionBody2 = body;
|
|
41480
41936
|
if (sectionBody2 === void 0) {
|
|
41481
41937
|
if (options.fromFile) {
|
|
41482
|
-
sectionBody2 = await
|
|
41938
|
+
sectionBody2 = await readFile72(resolve100(cwd, options.fromFile), "utf-8");
|
|
41483
41939
|
} else {
|
|
41484
41940
|
sectionBody2 = await readStdin();
|
|
41485
41941
|
}
|
|
@@ -41515,7 +41971,7 @@ async function runSessionRegister(rawStdin, options = {}, deps = {}) {
|
|
|
41515
41971
|
if (!isSafeSessionId(sessionId) || !cwd) return result;
|
|
41516
41972
|
result.sessionId = sessionId;
|
|
41517
41973
|
const transcriptPath = payload.transcript_path ?? "";
|
|
41518
|
-
const contextPath =
|
|
41974
|
+
const contextPath = resolve100(cwd, ".syntaur", "context.json");
|
|
41519
41975
|
const hasContextFile = await fileExists(contextPath);
|
|
41520
41976
|
const ctx = hasContextFile ? await readContext(cwd) : null;
|
|
41521
41977
|
if (ctx) {
|
|
@@ -41661,8 +42117,8 @@ sessionCommand.command("resolve-id").description(
|
|
|
41661
42117
|
// src/commands/worktree.ts
|
|
41662
42118
|
init_git_worktree();
|
|
41663
42119
|
import { Command as Command12 } from "commander";
|
|
41664
|
-
import { readFile as
|
|
41665
|
-
import { resolve as
|
|
42120
|
+
import { readFile as readFile73 } from "fs/promises";
|
|
42121
|
+
import { resolve as resolve102 } from "path";
|
|
41666
42122
|
init_fs();
|
|
41667
42123
|
init_paths();
|
|
41668
42124
|
init_config2();
|
|
@@ -41671,12 +42127,12 @@ init_state_machine();
|
|
|
41671
42127
|
|
|
41672
42128
|
// src/utils/path-canon.ts
|
|
41673
42129
|
import { realpathSync as realpathSync5 } from "fs";
|
|
41674
|
-
import { resolve as
|
|
42130
|
+
import { resolve as resolve101 } from "path";
|
|
41675
42131
|
function canonicalPath(p) {
|
|
41676
42132
|
try {
|
|
41677
|
-
return realpathSync5(
|
|
42133
|
+
return realpathSync5(resolve101(p));
|
|
41678
42134
|
} catch {
|
|
41679
|
-
return
|
|
42135
|
+
return resolve101(p).replace(/\/+$/, "");
|
|
41680
42136
|
}
|
|
41681
42137
|
}
|
|
41682
42138
|
|
|
@@ -41704,10 +42160,10 @@ function countSessionsByPath(dbPath, paths) {
|
|
|
41704
42160
|
init_timestamp();
|
|
41705
42161
|
init_frontmatter();
|
|
41706
42162
|
async function readContext2(cwd) {
|
|
41707
|
-
const path =
|
|
42163
|
+
const path = resolve102(cwd, ".syntaur", "context.json");
|
|
41708
42164
|
if (!await fileExists(path)) return null;
|
|
41709
42165
|
try {
|
|
41710
|
-
return JSON.parse(await
|
|
42166
|
+
return JSON.parse(await readFile73(path, "utf-8"));
|
|
41711
42167
|
} catch {
|
|
41712
42168
|
return null;
|
|
41713
42169
|
}
|
|
@@ -41716,12 +42172,12 @@ async function resolveAssignmentPath2(opts) {
|
|
|
41716
42172
|
if (opts.assignment) {
|
|
41717
42173
|
if (opts.project) {
|
|
41718
42174
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
41719
|
-
return
|
|
42175
|
+
return resolve102(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
41720
42176
|
}
|
|
41721
|
-
return
|
|
42177
|
+
return resolve102(assignmentsDir(), opts.assignment, "assignment.md");
|
|
41722
42178
|
}
|
|
41723
42179
|
const ctx = await readContext2(opts.cwd);
|
|
41724
|
-
if (ctx?.assignmentDir) return
|
|
42180
|
+
if (ctx?.assignmentDir) return resolve102(ctx.assignmentDir, "assignment.md");
|
|
41725
42181
|
throw new Error(
|
|
41726
42182
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
41727
42183
|
);
|
|
@@ -41732,7 +42188,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
|
|
|
41732
42188
|
}
|
|
41733
42189
|
const repository = options.repository ?? cwd;
|
|
41734
42190
|
const parentBranch = options.parentBranch ?? "main";
|
|
41735
|
-
const worktreePath = options.worktreePath ??
|
|
42191
|
+
const worktreePath = options.worktreePath ?? resolve102(repository, ".worktrees", options.branch);
|
|
41736
42192
|
const assignmentPath = await resolveAssignmentPath2({
|
|
41737
42193
|
assignment: options.assignment,
|
|
41738
42194
|
project: options.project,
|
|
@@ -41762,7 +42218,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
|
|
|
41762
42218
|
if (!await fileExists(assignmentPath)) {
|
|
41763
42219
|
throw new Error(`Assignment file not found: ${assignmentPath}`);
|
|
41764
42220
|
}
|
|
41765
|
-
const original = await
|
|
42221
|
+
const original = await readFile73(assignmentPath, "utf-8");
|
|
41766
42222
|
const fm = parseAssignmentFrontmatter(original);
|
|
41767
42223
|
const repository = options.repository ?? fm.workspace.repository ?? void 0;
|
|
41768
42224
|
const worktreePath = fm.workspace.worktreePath ?? void 0;
|
|
@@ -41828,7 +42284,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
|
|
|
41828
42284
|
const owners = /* @__PURE__ */ new Map();
|
|
41829
42285
|
for (const entry of walk.withAssignmentMd) {
|
|
41830
42286
|
try {
|
|
41831
|
-
const content = await
|
|
42287
|
+
const content = await readFile73(resolve102(entry.assignmentDir, "assignment.md"), "utf-8");
|
|
41832
42288
|
const fm = parseAssignmentFrontmatter(content);
|
|
41833
42289
|
const wp = fm.workspace?.worktreePath;
|
|
41834
42290
|
if (!wp) continue;
|
|
@@ -41850,7 +42306,7 @@ async function runWorktreeGc(options, cwd = process.cwd()) {
|
|
|
41850
42306
|
const repoTop = rt ? canonicalPath(rt) : null;
|
|
41851
42307
|
const cwdTop = ct ? canonicalPath(ct) : null;
|
|
41852
42308
|
const mainPath = entries.length > 0 ? canonicalPath(entries[0].worktreePath) : null;
|
|
41853
|
-
const dbPath =
|
|
42309
|
+
const dbPath = resolve102(syntaurRoot(), "syntaur.db");
|
|
41854
42310
|
const candidates = [];
|
|
41855
42311
|
for (const entry of entries) {
|
|
41856
42312
|
const canon = canonicalPath(entry.worktreePath);
|
|
@@ -42069,8 +42525,8 @@ worktreeCommand.command("gc").description(
|
|
|
42069
42525
|
// src/commands/open.ts
|
|
42070
42526
|
init_fs();
|
|
42071
42527
|
import { Command as Command13 } from "commander";
|
|
42072
|
-
import { readFile as
|
|
42073
|
-
import { resolve as
|
|
42528
|
+
import { readFile as readFile74 } from "fs/promises";
|
|
42529
|
+
import { resolve as resolve103 } from "path";
|
|
42074
42530
|
init_frontmatter();
|
|
42075
42531
|
init_config2();
|
|
42076
42532
|
init_paths();
|
|
@@ -42124,13 +42580,13 @@ function openInTerminal(path, config) {
|
|
|
42124
42580
|
// src/commands/open.ts
|
|
42125
42581
|
async function runOpen(assignmentArg, options) {
|
|
42126
42582
|
const resolved = options.id ? await resolveAssignmentTarget(options.id, {}) : await resolveAssignmentTarget(assignmentArg, { project: options.project });
|
|
42127
|
-
const assignmentPath =
|
|
42583
|
+
const assignmentPath = resolve103(resolved.assignmentDir, "assignment.md");
|
|
42128
42584
|
if (!await fileExists(assignmentPath)) {
|
|
42129
42585
|
throw new SyntaurError(`Assignment file not found: ${assignmentPath}`, {
|
|
42130
42586
|
remediation: "check the assignment slug or --id"
|
|
42131
42587
|
});
|
|
42132
42588
|
}
|
|
42133
|
-
const fm = parseAssignmentFrontmatter(await
|
|
42589
|
+
const fm = parseAssignmentFrontmatter(await readFile74(assignmentPath, "utf-8"));
|
|
42134
42590
|
const worktreePath = fm.workspace?.worktreePath;
|
|
42135
42591
|
if (!worktreePath) {
|
|
42136
42592
|
throw new SyntaurError("No worktree recorded for this assignment.", {
|
|
@@ -42195,14 +42651,14 @@ init_config2();
|
|
|
42195
42651
|
init_fs();
|
|
42196
42652
|
init_slug();
|
|
42197
42653
|
import { Command as Command14 } from "commander";
|
|
42198
|
-
import { resolve as
|
|
42199
|
-
import { readFile as
|
|
42654
|
+
import { resolve as resolve105 } from "path";
|
|
42655
|
+
import { readFile as readFile76, readdir as readdir37, rm as rm15 } from "fs/promises";
|
|
42200
42656
|
|
|
42201
42657
|
// src/utils/project-indexes.ts
|
|
42202
42658
|
init_parser();
|
|
42203
42659
|
init_fs();
|
|
42204
|
-
import { readdir as readdir36, readFile as
|
|
42205
|
-
import { resolve as
|
|
42660
|
+
import { readdir as readdir36, readFile as readFile75 } from "fs/promises";
|
|
42661
|
+
import { resolve as resolve104 } from "path";
|
|
42206
42662
|
function nowIso3() {
|
|
42207
42663
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
42208
42664
|
}
|
|
@@ -42221,7 +42677,7 @@ function joinList(items) {
|
|
|
42221
42677
|
return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
|
|
42222
42678
|
}
|
|
42223
42679
|
async function rebuildResourcesIndex(projectDir) {
|
|
42224
|
-
const dir =
|
|
42680
|
+
const dir = resolve104(projectDir, "resources");
|
|
42225
42681
|
await ensureDir(dir);
|
|
42226
42682
|
const files = await listSlugFiles(dir);
|
|
42227
42683
|
const slug = readProjectSlug(projectDir);
|
|
@@ -42237,7 +42693,7 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
42237
42693
|
lines.push("| Name | Category | Source | Related Assignments | Updated |");
|
|
42238
42694
|
lines.push("|------|----------|--------|---------------------|---------|");
|
|
42239
42695
|
for (const fileName of files) {
|
|
42240
|
-
const content = await
|
|
42696
|
+
const content = await readFile75(resolve104(dir, fileName), "utf-8");
|
|
42241
42697
|
const parsed = parseResource(content);
|
|
42242
42698
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
42243
42699
|
const name = parsed.name || slugBase;
|
|
@@ -42247,12 +42703,12 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
42247
42703
|
);
|
|
42248
42704
|
}
|
|
42249
42705
|
lines.push("");
|
|
42250
|
-
const indexPath =
|
|
42706
|
+
const indexPath = resolve104(dir, "_index.md");
|
|
42251
42707
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
42252
42708
|
return { total: files.length, path: indexPath };
|
|
42253
42709
|
}
|
|
42254
42710
|
async function rebuildMemoriesIndex(projectDir) {
|
|
42255
|
-
const dir =
|
|
42711
|
+
const dir = resolve104(projectDir, "memories");
|
|
42256
42712
|
await ensureDir(dir);
|
|
42257
42713
|
const files = await listSlugFiles(dir);
|
|
42258
42714
|
const slug = readProjectSlug(projectDir);
|
|
@@ -42268,7 +42724,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
42268
42724
|
lines.push("| Name | Source | Scope | Source Assignment | Updated |");
|
|
42269
42725
|
lines.push("|------|--------|-------|-------------------|---------|");
|
|
42270
42726
|
for (const fileName of files) {
|
|
42271
|
-
const content = await
|
|
42727
|
+
const content = await readFile75(resolve104(dir, fileName), "utf-8");
|
|
42272
42728
|
const parsed = parseMemory(content);
|
|
42273
42729
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
42274
42730
|
const name = parsed.name || slugBase;
|
|
@@ -42278,7 +42734,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
42278
42734
|
);
|
|
42279
42735
|
}
|
|
42280
42736
|
lines.push("");
|
|
42281
|
-
const indexPath =
|
|
42737
|
+
const indexPath = resolve104(dir, "_index.md");
|
|
42282
42738
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
42283
42739
|
return { total: files.length, path: indexPath };
|
|
42284
42740
|
}
|
|
@@ -42342,8 +42798,8 @@ ${opts.body ?? "<!-- Add notes about this resource here. -->"}
|
|
|
42342
42798
|
}
|
|
42343
42799
|
async function resolveProjectDir(project) {
|
|
42344
42800
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
42345
|
-
const projectDir =
|
|
42346
|
-
if (!await fileExists(
|
|
42801
|
+
const projectDir = resolve105((await readConfig()).defaultProjectDir, project);
|
|
42802
|
+
if (!await fileExists(resolve105(projectDir, "project.md"))) {
|
|
42347
42803
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
42348
42804
|
}
|
|
42349
42805
|
return projectDir;
|
|
@@ -42356,7 +42812,7 @@ async function runResourceAdd(options) {
|
|
|
42356
42812
|
if (!isValidSlug(slug)) {
|
|
42357
42813
|
throw new Error(`Invalid resource slug: "${slug}".`);
|
|
42358
42814
|
}
|
|
42359
|
-
const filePath =
|
|
42815
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42360
42816
|
if (await fileExists(filePath) && !options.force) {
|
|
42361
42817
|
throw new Error(
|
|
42362
42818
|
`Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -42373,7 +42829,7 @@ async function runResourceAdd(options) {
|
|
|
42373
42829
|
return { filePath, indexPath, total };
|
|
42374
42830
|
}
|
|
42375
42831
|
async function listResourceSlugs(projectDir) {
|
|
42376
|
-
const dir =
|
|
42832
|
+
const dir = resolve105(projectDir, "resources");
|
|
42377
42833
|
if (!await fileExists(dir)) return [];
|
|
42378
42834
|
const entries = await readdir37(dir, { withFileTypes: true });
|
|
42379
42835
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
@@ -42383,16 +42839,16 @@ async function runResourceList(project) {
|
|
|
42383
42839
|
const slugs = await listResourceSlugs(projectDir);
|
|
42384
42840
|
const out = [];
|
|
42385
42841
|
for (const slug of slugs) {
|
|
42386
|
-
const parsed = parseResource(await
|
|
42842
|
+
const parsed = parseResource(await readFile76(resolve105(projectDir, "resources", `${slug}.md`), "utf-8"));
|
|
42387
42843
|
out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
|
|
42388
42844
|
}
|
|
42389
42845
|
return out;
|
|
42390
42846
|
}
|
|
42391
42847
|
async function runResourceShow(project, slug) {
|
|
42392
42848
|
const projectDir = await resolveProjectDir(project);
|
|
42393
|
-
const filePath =
|
|
42849
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42394
42850
|
if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
|
|
42395
|
-
const parsed = parseResource(await
|
|
42851
|
+
const parsed = parseResource(await readFile76(filePath, "utf-8"));
|
|
42396
42852
|
return {
|
|
42397
42853
|
slug,
|
|
42398
42854
|
name: parsed.name,
|
|
@@ -42406,14 +42862,14 @@ async function runResourceShow(project, slug) {
|
|
|
42406
42862
|
}
|
|
42407
42863
|
async function runResourceUpdate(slug, options) {
|
|
42408
42864
|
const projectDir = await resolveProjectDir(options.project);
|
|
42409
|
-
const filePath =
|
|
42865
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42410
42866
|
if (!await fileExists(filePath)) {
|
|
42411
42867
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
42412
42868
|
}
|
|
42413
42869
|
if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
|
|
42414
42870
|
throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
|
|
42415
42871
|
}
|
|
42416
|
-
const original = await
|
|
42872
|
+
const original = await readFile76(filePath, "utf-8");
|
|
42417
42873
|
const content = editResourceFrontmatter(original, {
|
|
42418
42874
|
name: options.name,
|
|
42419
42875
|
category: options.category,
|
|
@@ -42426,7 +42882,7 @@ async function runResourceUpdate(slug, options) {
|
|
|
42426
42882
|
}
|
|
42427
42883
|
async function runResourceRemove(slug, options) {
|
|
42428
42884
|
const projectDir = await resolveProjectDir(options.project);
|
|
42429
|
-
const filePath =
|
|
42885
|
+
const filePath = resolve105(projectDir, "resources", `${slug}.md`);
|
|
42430
42886
|
if (!await fileExists(filePath)) {
|
|
42431
42887
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
42432
42888
|
}
|
|
@@ -42509,8 +42965,8 @@ init_config2();
|
|
|
42509
42965
|
init_fs();
|
|
42510
42966
|
init_slug();
|
|
42511
42967
|
import { Command as Command15 } from "commander";
|
|
42512
|
-
import { resolve as
|
|
42513
|
-
import { readFile as
|
|
42968
|
+
import { resolve as resolve106 } from "path";
|
|
42969
|
+
import { readFile as readFile77, readdir as readdir38, rm as rm16 } from "fs/promises";
|
|
42514
42970
|
init_parser();
|
|
42515
42971
|
function nowIso5() {
|
|
42516
42972
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -42574,8 +43030,8 @@ ${opts.body ?? "<!-- Capture the load-bearing context for this memory here. -->"
|
|
|
42574
43030
|
}
|
|
42575
43031
|
async function resolveProjectDir2(project) {
|
|
42576
43032
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
42577
|
-
const projectDir =
|
|
42578
|
-
if (!await fileExists(
|
|
43033
|
+
const projectDir = resolve106((await readConfig()).defaultProjectDir, project);
|
|
43034
|
+
if (!await fileExists(resolve106(projectDir, "project.md"))) {
|
|
42579
43035
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
42580
43036
|
}
|
|
42581
43037
|
return projectDir;
|
|
@@ -42588,7 +43044,7 @@ async function runMemoryAdd(options) {
|
|
|
42588
43044
|
if (!isValidSlug(slug)) {
|
|
42589
43045
|
throw new Error(`Invalid memory slug: "${slug}".`);
|
|
42590
43046
|
}
|
|
42591
|
-
const filePath =
|
|
43047
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42592
43048
|
if (await fileExists(filePath) && !options.force) {
|
|
42593
43049
|
throw new Error(
|
|
42594
43050
|
`Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -42606,7 +43062,7 @@ async function runMemoryAdd(options) {
|
|
|
42606
43062
|
return { filePath, indexPath, total };
|
|
42607
43063
|
}
|
|
42608
43064
|
async function listMemorySlugs(projectDir) {
|
|
42609
|
-
const dir =
|
|
43065
|
+
const dir = resolve106(projectDir, "memories");
|
|
42610
43066
|
if (!await fileExists(dir)) return [];
|
|
42611
43067
|
const entries = await readdir38(dir, { withFileTypes: true });
|
|
42612
43068
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
@@ -42616,16 +43072,16 @@ async function runMemoryList(project) {
|
|
|
42616
43072
|
const slugs = await listMemorySlugs(projectDir);
|
|
42617
43073
|
const out = [];
|
|
42618
43074
|
for (const slug of slugs) {
|
|
42619
|
-
const parsed = parseMemory(await
|
|
43075
|
+
const parsed = parseMemory(await readFile77(resolve106(projectDir, "memories", `${slug}.md`), "utf-8"));
|
|
42620
43076
|
out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
|
|
42621
43077
|
}
|
|
42622
43078
|
return out;
|
|
42623
43079
|
}
|
|
42624
43080
|
async function runMemoryShow(project, slug) {
|
|
42625
43081
|
const projectDir = await resolveProjectDir2(project);
|
|
42626
|
-
const filePath =
|
|
43082
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42627
43083
|
if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
|
|
42628
|
-
const parsed = parseMemory(await
|
|
43084
|
+
const parsed = parseMemory(await readFile77(filePath, "utf-8"));
|
|
42629
43085
|
return {
|
|
42630
43086
|
slug,
|
|
42631
43087
|
name: parsed.name,
|
|
@@ -42640,7 +43096,7 @@ async function runMemoryShow(project, slug) {
|
|
|
42640
43096
|
}
|
|
42641
43097
|
async function runMemoryUpdate(slug, options) {
|
|
42642
43098
|
const projectDir = await resolveProjectDir2(options.project);
|
|
42643
|
-
const filePath =
|
|
43099
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42644
43100
|
if (!await fileExists(filePath)) {
|
|
42645
43101
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
42646
43102
|
}
|
|
@@ -42649,7 +43105,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
42649
43105
|
"Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
|
|
42650
43106
|
);
|
|
42651
43107
|
}
|
|
42652
|
-
const original = await
|
|
43108
|
+
const original = await readFile77(filePath, "utf-8");
|
|
42653
43109
|
const content = editMemoryFrontmatter(original, {
|
|
42654
43110
|
name: options.name,
|
|
42655
43111
|
source: options.source,
|
|
@@ -42663,7 +43119,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
42663
43119
|
}
|
|
42664
43120
|
async function runMemoryRemove(slug, options) {
|
|
42665
43121
|
const projectDir = await resolveProjectDir2(options.project);
|
|
42666
|
-
const filePath =
|
|
43122
|
+
const filePath = resolve106(projectDir, "memories", `${slug}.md`);
|
|
42667
43123
|
if (!await fileExists(filePath)) {
|
|
42668
43124
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
42669
43125
|
}
|
|
@@ -42752,8 +43208,8 @@ init_derive();
|
|
|
42752
43208
|
init_recompute();
|
|
42753
43209
|
init_query();
|
|
42754
43210
|
import { Command as Command16 } from "commander";
|
|
42755
|
-
import { readFile as
|
|
42756
|
-
import { dirname as dirname29, resolve as
|
|
43211
|
+
import { readFile as readFile78 } from "fs/promises";
|
|
43212
|
+
import { dirname as dirname29, resolve as resolve107 } from "path";
|
|
42757
43213
|
var AGE_PATTERN = /^(\d+)([dhwm])$/i;
|
|
42758
43214
|
function parseAgeToCutoff(age) {
|
|
42759
43215
|
const match = age.match(AGE_PATTERN);
|
|
@@ -42772,7 +43228,7 @@ function parseAgeToCutoff(age) {
|
|
|
42772
43228
|
}
|
|
42773
43229
|
function assignmentMdPath(item) {
|
|
42774
43230
|
if (item.projectSlug) {
|
|
42775
|
-
return
|
|
43231
|
+
return resolve107(
|
|
42776
43232
|
defaultProjectDir(),
|
|
42777
43233
|
item.projectSlug,
|
|
42778
43234
|
"assignments",
|
|
@@ -42780,13 +43236,13 @@ function assignmentMdPath(item) {
|
|
|
42780
43236
|
"assignment.md"
|
|
42781
43237
|
);
|
|
42782
43238
|
}
|
|
42783
|
-
return
|
|
43239
|
+
return resolve107(assignmentsDir(), item.id, "assignment.md");
|
|
42784
43240
|
}
|
|
42785
43241
|
async function loadTags(item) {
|
|
42786
43242
|
const path = assignmentMdPath(item);
|
|
42787
43243
|
if (!await fileExists(path)) return [];
|
|
42788
43244
|
try {
|
|
42789
|
-
const content = await
|
|
43245
|
+
const content = await readFile78(path, "utf-8");
|
|
42790
43246
|
return parseAssignmentFrontmatter(content).tags;
|
|
42791
43247
|
} catch {
|
|
42792
43248
|
return [];
|
|
@@ -42850,11 +43306,11 @@ async function loadQueryItem(item, terminalStatuses3, now, declarations) {
|
|
|
42850
43306
|
const path = assignmentMdPath(item);
|
|
42851
43307
|
if (!await fileExists(path)) return null;
|
|
42852
43308
|
try {
|
|
42853
|
-
const content = await
|
|
43309
|
+
const content = await readFile78(path, "utf-8");
|
|
42854
43310
|
const fm = parseAssignmentFrontmatter(content);
|
|
42855
43311
|
const body = content.replace(/^---\n[\s\S]*?\n---/, "");
|
|
42856
43312
|
const assignmentDir = dirname29(path);
|
|
42857
|
-
const projectDir = item.projectSlug ?
|
|
43313
|
+
const projectDir = item.projectSlug ? resolve107(defaultProjectDir(), item.projectSlug) : null;
|
|
42858
43314
|
const facts = await computeFacts({ assignmentDir, frontmatter: fm, body, projectDir, terminalStatuses: terminalStatuses3, declarations });
|
|
42859
43315
|
const history = fm.statusHistory;
|
|
42860
43316
|
const lastHeadlineChange = [...history].reverse().find((e) => e.from !== e.to || e.from === null);
|
|
@@ -43624,8 +44080,8 @@ init_fs();
|
|
|
43624
44080
|
init_timestamp();
|
|
43625
44081
|
init_frontmatter();
|
|
43626
44082
|
import { Command as Command21 } from "commander";
|
|
43627
|
-
import { readFile as
|
|
43628
|
-
import { resolve as
|
|
44083
|
+
import { readFile as readFile79 } from "fs/promises";
|
|
44084
|
+
import { resolve as resolve108 } from "path";
|
|
43629
44085
|
function renameAssignmentStatusRefs(content, id, newId, now) {
|
|
43630
44086
|
const fm = parseAssignmentFrontmatter(content);
|
|
43631
44087
|
const updates = { updated: now };
|
|
@@ -43646,12 +44102,12 @@ function fail3(error) {
|
|
|
43646
44102
|
process.exit(1);
|
|
43647
44103
|
}
|
|
43648
44104
|
function configPath() {
|
|
43649
|
-
return
|
|
44105
|
+
return resolve108(syntaurRoot(), "config.md");
|
|
43650
44106
|
}
|
|
43651
44107
|
async function readStatusBlock() {
|
|
43652
44108
|
const p = configPath();
|
|
43653
44109
|
if (!await fileExists(p)) return null;
|
|
43654
|
-
const content = await
|
|
44110
|
+
const content = await readFile79(p, "utf-8");
|
|
43655
44111
|
return parseStatusConfig(content);
|
|
43656
44112
|
}
|
|
43657
44113
|
async function requireStatusBlock() {
|
|
@@ -43935,7 +44391,7 @@ async function runStatusRename(id, opts) {
|
|
|
43935
44391
|
printBlockDiff(before, after);
|
|
43936
44392
|
const now2 = nowTimestamp();
|
|
43937
44393
|
for (const a of affected) {
|
|
43938
|
-
const original = await
|
|
44394
|
+
const original = await readFile79(a.path, "utf-8");
|
|
43939
44395
|
const rewritten = renameAssignmentStatusRefs(original, id, newId, now2);
|
|
43940
44396
|
console.log(`
|
|
43941
44397
|
--- ${a.display}/assignment.md`);
|
|
@@ -43946,9 +44402,9 @@ async function runStatusRename(id, opts) {
|
|
|
43946
44402
|
}
|
|
43947
44403
|
const cfgPath = configPath();
|
|
43948
44404
|
const buffers = /* @__PURE__ */ new Map();
|
|
43949
|
-
buffers.set(cfgPath, await fileExists(cfgPath) ? await
|
|
44405
|
+
buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile79(cfgPath, "utf-8") : "");
|
|
43950
44406
|
for (const a of affected) {
|
|
43951
|
-
buffers.set(a.path, await
|
|
44407
|
+
buffers.set(a.path, await readFile79(a.path, "utf-8"));
|
|
43952
44408
|
}
|
|
43953
44409
|
const now = nowTimestamp();
|
|
43954
44410
|
try {
|
|
@@ -44150,13 +44606,13 @@ init_config2();
|
|
|
44150
44606
|
init_timestamp();
|
|
44151
44607
|
init_frontmatter();
|
|
44152
44608
|
import { Command as Command22 } from "commander";
|
|
44153
|
-
import { readFile as
|
|
44154
|
-
import { resolve as
|
|
44609
|
+
import { readFile as readFile80 } from "fs/promises";
|
|
44610
|
+
import { resolve as resolve109 } from "path";
|
|
44155
44611
|
async function readContext3(cwd) {
|
|
44156
|
-
const path =
|
|
44612
|
+
const path = resolve109(cwd, ".syntaur", "context.json");
|
|
44157
44613
|
if (!await fileExists(path)) return null;
|
|
44158
44614
|
try {
|
|
44159
|
-
return JSON.parse(await
|
|
44615
|
+
return JSON.parse(await readFile80(path, "utf-8"));
|
|
44160
44616
|
} catch {
|
|
44161
44617
|
return null;
|
|
44162
44618
|
}
|
|
@@ -44165,12 +44621,12 @@ async function resolveAssignmentPath3(opts) {
|
|
|
44165
44621
|
if (opts.assignment) {
|
|
44166
44622
|
if (opts.project) {
|
|
44167
44623
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
44168
|
-
return
|
|
44624
|
+
return resolve109(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
44169
44625
|
}
|
|
44170
|
-
return
|
|
44626
|
+
return resolve109(assignmentsDir(), opts.assignment, "assignment.md");
|
|
44171
44627
|
}
|
|
44172
44628
|
const ctx = await readContext3(opts.cwd);
|
|
44173
|
-
if (ctx?.assignmentDir) return
|
|
44629
|
+
if (ctx?.assignmentDir) return resolve109(ctx.assignmentDir, "assignment.md");
|
|
44174
44630
|
throw new Error(
|
|
44175
44631
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
44176
44632
|
);
|
|
@@ -44207,7 +44663,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
|
|
|
44207
44663
|
${pre.errors.map((e) => ` - ${e}`).join("\n")}`
|
|
44208
44664
|
);
|
|
44209
44665
|
}
|
|
44210
|
-
const original = await
|
|
44666
|
+
const original = await readFile80(path, "utf-8");
|
|
44211
44667
|
let next = updateAssignmentWorkspace(original, partial);
|
|
44212
44668
|
next = updateAssignmentFile(next, { updated: nowTimestamp() });
|
|
44213
44669
|
await writeFileForce(path, next);
|
|
@@ -44244,13 +44700,13 @@ init_config2();
|
|
|
44244
44700
|
init_timestamp();
|
|
44245
44701
|
init_templates();
|
|
44246
44702
|
import { Command as Command23 } from "commander";
|
|
44247
|
-
import { readFile as
|
|
44248
|
-
import { resolve as
|
|
44703
|
+
import { readFile as readFile81 } from "fs/promises";
|
|
44704
|
+
import { resolve as resolve110 } from "path";
|
|
44249
44705
|
async function readContext4(cwd) {
|
|
44250
|
-
const path =
|
|
44706
|
+
const path = resolve110(cwd, ".syntaur", "context.json");
|
|
44251
44707
|
if (!await fileExists(path)) return null;
|
|
44252
44708
|
try {
|
|
44253
|
-
return JSON.parse(await
|
|
44709
|
+
return JSON.parse(await readFile81(path, "utf-8"));
|
|
44254
44710
|
} catch {
|
|
44255
44711
|
return null;
|
|
44256
44712
|
}
|
|
@@ -44260,11 +44716,11 @@ async function resolveAssignmentDir2(opts) {
|
|
|
44260
44716
|
if (opts.project) {
|
|
44261
44717
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
44262
44718
|
return {
|
|
44263
|
-
dir:
|
|
44719
|
+
dir: resolve110(projectsDir2, opts.project, "assignments", opts.assignment),
|
|
44264
44720
|
slug: opts.assignment
|
|
44265
44721
|
};
|
|
44266
44722
|
}
|
|
44267
|
-
return { dir:
|
|
44723
|
+
return { dir: resolve110(assignmentsDir(), opts.assignment), slug: opts.assignment };
|
|
44268
44724
|
}
|
|
44269
44725
|
const ctx = await readContext4(opts.cwd);
|
|
44270
44726
|
if (ctx?.assignmentDir) {
|
|
@@ -44326,12 +44782,12 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
|
|
|
44326
44782
|
project: options.project,
|
|
44327
44783
|
cwd
|
|
44328
44784
|
});
|
|
44329
|
-
if (!await fileExists(
|
|
44785
|
+
if (!await fileExists(resolve110(dir, "assignment.md"))) {
|
|
44330
44786
|
throw new Error(`No assignment found at ${dir} (missing assignment.md).`);
|
|
44331
44787
|
}
|
|
44332
|
-
const path =
|
|
44788
|
+
const path = resolve110(dir, "progress.md");
|
|
44333
44789
|
const now = nowTimestamp();
|
|
44334
|
-
const content = await fileExists(path) ? await
|
|
44790
|
+
const content = await fileExists(path) ? await readFile81(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
|
|
44335
44791
|
const next = appendProgressEntry(content, text, now);
|
|
44336
44792
|
await writeFileForce(path, next);
|
|
44337
44793
|
return path;
|
|
@@ -44536,7 +44992,7 @@ program.command("attest").description("Record an attestation (agent reviewed a r
|
|
|
44536
44992
|
await attestCommand(assignment, fact, options);
|
|
44537
44993
|
})
|
|
44538
44994
|
);
|
|
44539
|
-
program.command("recompute").description("Recompute derived status for one assignment or --all (headless reconcile)").argument("[assignment]", "Assignment slug or standalone UUID").option("--all", "Recompute every assignment (projects + standalone)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").action(
|
|
44995
|
+
program.command("recompute").description("Recompute derived status for one assignment or --all (headless reconcile)").argument("[assignment]", "Assignment slug or standalone UUID").option("--all", "Recompute every assignment (projects + standalone)").option("--project <slug>", "Target project slug").option("--agent <name>", "Acting agent id").option("--dir <path>", "Override default project directory").option("--if-migrated", "No-op unless derive migration has run (for implicit triggers like session-end hooks)").action(
|
|
44540
44996
|
runCommand(async (assignment, options) => {
|
|
44541
44997
|
await recomputeCommand(assignment, options);
|
|
44542
44998
|
})
|