syntaur 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dashboard/server.js +164 -129
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +959 -545
- package/dist/index.js.map +1 -1
- package/dist/launch/index.d.ts +3 -0
- package/dist/launch/index.js +53 -40
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/README.md +29 -1
package/dist/index.js
CHANGED
|
@@ -93,9 +93,10 @@ __export(fs_exports, {
|
|
|
93
93
|
ensureDir: () => ensureDir,
|
|
94
94
|
fileExists: () => fileExists,
|
|
95
95
|
writeFileForce: () => writeFileForce,
|
|
96
|
+
writeFileReport: () => writeFileReport,
|
|
96
97
|
writeFileSafe: () => writeFileSafe
|
|
97
98
|
});
|
|
98
|
-
import { mkdir, writeFile, access, rename } from "fs/promises";
|
|
99
|
+
import { mkdir, writeFile, readFile, access, rename } from "fs/promises";
|
|
99
100
|
import { dirname, join } from "path";
|
|
100
101
|
async function ensureDir(dir) {
|
|
101
102
|
await mkdir(dir, { recursive: true });
|
|
@@ -126,6 +127,22 @@ async function writeFileForce(filePath, content) {
|
|
|
126
127
|
await writeFile(tempPath, content, "utf-8");
|
|
127
128
|
await rename(tempPath, filePath);
|
|
128
129
|
}
|
|
130
|
+
async function writeFileReport(filePath, content, options = {}) {
|
|
131
|
+
if (!await fileExists(filePath)) {
|
|
132
|
+
await ensureDir(dirname(filePath));
|
|
133
|
+
await writeFile(filePath, content, "utf-8");
|
|
134
|
+
return "written";
|
|
135
|
+
}
|
|
136
|
+
const current = await readFile(filePath, "utf-8").catch(() => null);
|
|
137
|
+
if (current === content) {
|
|
138
|
+
return "already-current";
|
|
139
|
+
}
|
|
140
|
+
if (!options.force) {
|
|
141
|
+
return "differs-preserved";
|
|
142
|
+
}
|
|
143
|
+
await writeFileForce(filePath, content);
|
|
144
|
+
return "overwritten";
|
|
145
|
+
}
|
|
129
146
|
var init_fs = __esm({
|
|
130
147
|
"src/utils/fs.ts"() {
|
|
131
148
|
"use strict";
|
|
@@ -471,7 +488,7 @@ var init_timestamp = __esm({
|
|
|
471
488
|
});
|
|
472
489
|
|
|
473
490
|
// src/utils/fs-migration.ts
|
|
474
|
-
import { readdir, readFile, rename as rename2, writeFile as writeFile2 } from "fs/promises";
|
|
491
|
+
import { readdir, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
|
|
475
492
|
import { resolve as resolve2 } from "path";
|
|
476
493
|
async function migrateLegacyProjectFiles(projectsDir2) {
|
|
477
494
|
const result = {
|
|
@@ -540,7 +557,7 @@ async function migrateLegacyArchivedProjects(projectsDir2) {
|
|
|
540
557
|
const projectMd = resolve2(projectsDir2, entry.name, "project.md");
|
|
541
558
|
try {
|
|
542
559
|
if (!await fileExists(projectMd)) continue;
|
|
543
|
-
const content = await
|
|
560
|
+
const content = await readFile2(projectMd, "utf-8");
|
|
544
561
|
if (readFrontmatterField(content, "statusOverride") !== "archived") continue;
|
|
545
562
|
let next = setFrontmatterField(content, "archived", true);
|
|
546
563
|
if (readFrontmatterField(content, "archivedAt") === null) {
|
|
@@ -565,7 +582,7 @@ async function migrateLegacyConfig(configPath) {
|
|
|
565
582
|
if (!await fileExists(configPath)) return result;
|
|
566
583
|
let content;
|
|
567
584
|
try {
|
|
568
|
-
content = await
|
|
585
|
+
content = await readFile2(configPath, "utf-8");
|
|
569
586
|
} catch {
|
|
570
587
|
return result;
|
|
571
588
|
}
|
|
@@ -822,7 +839,7 @@ __export(config_exports, {
|
|
|
822
839
|
writeTerminalConfig: () => writeTerminalConfig,
|
|
823
840
|
writeThemeConfig: () => writeThemeConfig
|
|
824
841
|
});
|
|
825
|
-
import { readFile as
|
|
842
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
826
843
|
import { spawnSync } from "child_process";
|
|
827
844
|
import { resolve as resolve3, isAbsolute } from "path";
|
|
828
845
|
function parseAgentCommand(value, agentId) {
|
|
@@ -950,6 +967,18 @@ function parseFrontmatter(content) {
|
|
|
950
967
|
}
|
|
951
968
|
return result;
|
|
952
969
|
}
|
|
970
|
+
function parseInstalledAgents(fm) {
|
|
971
|
+
const prefix = "integrations.installedAgents.";
|
|
972
|
+
const installedAgents = {};
|
|
973
|
+
for (const [key, value] of Object.entries(fm)) {
|
|
974
|
+
if (!key.startsWith(prefix)) continue;
|
|
975
|
+
const id = key.slice(prefix.length);
|
|
976
|
+
if (!id) continue;
|
|
977
|
+
const scope = value === "project" ? "project" : "global";
|
|
978
|
+
installedAgents[id] = { scope };
|
|
979
|
+
}
|
|
980
|
+
return Object.keys(installedAgents).length > 0 ? { installedAgents } : {};
|
|
981
|
+
}
|
|
953
982
|
function parseStatusConfig(content) {
|
|
954
983
|
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
955
984
|
if (!match) return null;
|
|
@@ -1079,6 +1108,11 @@ function serializeIntegrationConfig(integrations) {
|
|
|
1079
1108
|
if (integrations.codexMarketplacePath) {
|
|
1080
1109
|
lines.push(` codexMarketplacePath: ${integrations.codexMarketplacePath}`);
|
|
1081
1110
|
}
|
|
1111
|
+
if (integrations.installedAgents) {
|
|
1112
|
+
for (const [id, rec] of Object.entries(integrations.installedAgents)) {
|
|
1113
|
+
lines.push(` installedAgents.${id}: ${rec.scope}`);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1082
1116
|
if (lines.length === 0) {
|
|
1083
1117
|
return null;
|
|
1084
1118
|
}
|
|
@@ -1147,7 +1181,7 @@ async function updatePlaybooksConfig(playbooks) {
|
|
|
1147
1181
|
disabled: Array.from(new Set(playbooks.disabled ?? current.disabled))
|
|
1148
1182
|
};
|
|
1149
1183
|
const playbooksBlock = serializePlaybooksConfig(nextPlaybooks);
|
|
1150
|
-
const existing = await fileExists(configPath) ? await
|
|
1184
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1151
1185
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1152
1186
|
if (!fmMatch) {
|
|
1153
1187
|
const bodyBlock = playbooksBlock ? `${playbooksBlock}
|
|
@@ -1199,7 +1233,7 @@ function serializeThemeConfig(theme) {
|
|
|
1199
1233
|
async function writeThemeConfig(theme) {
|
|
1200
1234
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1201
1235
|
const themeBlock = serializeThemeConfig(theme);
|
|
1202
|
-
const existing = await fileExists(configPath) ? await
|
|
1236
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1203
1237
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1204
1238
|
if (!fmMatch) {
|
|
1205
1239
|
const content = `---
|
|
@@ -1225,7 +1259,7 @@ ${normalizedFm}
|
|
|
1225
1259
|
async function deleteThemeConfig() {
|
|
1226
1260
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1227
1261
|
if (!await fileExists(configPath)) return;
|
|
1228
|
-
const existing = await
|
|
1262
|
+
const existing = await readFile3(configPath, "utf-8");
|
|
1229
1263
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1230
1264
|
if (!fmMatch) return;
|
|
1231
1265
|
const fmBlock = fmMatch[2];
|
|
@@ -1245,7 +1279,7 @@ function stripTopLevelScalar(fmBlock, key) {
|
|
|
1245
1279
|
async function writeTerminalConfig(terminal) {
|
|
1246
1280
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1247
1281
|
const terminalLine = `terminal: ${terminal}`;
|
|
1248
|
-
const existing = await fileExists(configPath) ? await
|
|
1282
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1249
1283
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1250
1284
|
if (!fmMatch) {
|
|
1251
1285
|
const content = `---
|
|
@@ -1271,7 +1305,7 @@ ${normalizedFm}
|
|
|
1271
1305
|
async function deleteTerminalConfig() {
|
|
1272
1306
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1273
1307
|
if (!await fileExists(configPath)) return;
|
|
1274
|
-
const existing = await
|
|
1308
|
+
const existing = await readFile3(configPath, "utf-8");
|
|
1275
1309
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1276
1310
|
if (!fmMatch) return;
|
|
1277
1311
|
const fmBlock = fmMatch[2];
|
|
@@ -1340,7 +1374,7 @@ async function writeHotkeyBindingsConfig(cfg) {
|
|
|
1340
1374
|
}
|
|
1341
1375
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1342
1376
|
const block = serializeHotkeyBindingsConfig({ bindings: cleaned });
|
|
1343
|
-
const existing = await fileExists(configPath) ? await
|
|
1377
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1344
1378
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1345
1379
|
if (!fmMatch) {
|
|
1346
1380
|
const content = `---
|
|
@@ -1366,7 +1400,7 @@ ${normalizedFm}
|
|
|
1366
1400
|
async function deleteHotkeyBindingsConfig() {
|
|
1367
1401
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1368
1402
|
if (!await fileExists(configPath)) return;
|
|
1369
|
-
const existing = await
|
|
1403
|
+
const existing = await readFile3(configPath, "utf-8");
|
|
1370
1404
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1371
1405
|
if (!fmMatch) return;
|
|
1372
1406
|
const fmBlock = fmMatch[2];
|
|
@@ -1683,7 +1717,7 @@ async function writeAgentsConfig(agents) {
|
|
|
1683
1717
|
validateAgentList(agents);
|
|
1684
1718
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1685
1719
|
const agentsBlock = serializeAgentsConfig(agents);
|
|
1686
|
-
const existing = await fileExists(configPath) ? await
|
|
1720
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1687
1721
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1688
1722
|
if (!fmMatch) {
|
|
1689
1723
|
const content = `---
|
|
@@ -1708,7 +1742,7 @@ ${newFm}
|
|
|
1708
1742
|
async function deleteAgentsConfig() {
|
|
1709
1743
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1710
1744
|
if (!await fileExists(configPath)) return;
|
|
1711
|
-
const existing = await
|
|
1745
|
+
const existing = await readFile3(configPath, "utf-8");
|
|
1712
1746
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1713
1747
|
if (!fmMatch) return;
|
|
1714
1748
|
const fmBlock = fmMatch[2];
|
|
@@ -1732,7 +1766,7 @@ ${statusBlock}
|
|
|
1732
1766
|
await writeFileForce(configPath, content);
|
|
1733
1767
|
return;
|
|
1734
1768
|
}
|
|
1735
|
-
const existing = await
|
|
1769
|
+
const existing = await readFile3(configPath, "utf-8");
|
|
1736
1770
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1737
1771
|
if (!fmMatch) {
|
|
1738
1772
|
const content = `---
|
|
@@ -1776,7 +1810,7 @@ ${statusBlock}
|
|
|
1776
1810
|
async function deleteStatusConfig() {
|
|
1777
1811
|
const configPath = resolve3(syntaurRoot(), "config.md");
|
|
1778
1812
|
if (!await fileExists(configPath)) return;
|
|
1779
|
-
const existing = await
|
|
1813
|
+
const existing = await readFile3(configPath, "utf-8");
|
|
1780
1814
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1781
1815
|
if (!fmMatch) return;
|
|
1782
1816
|
const fmBlock = fmMatch[2];
|
|
@@ -1794,7 +1828,7 @@ async function updateIntegrationConfig(integrations) {
|
|
|
1794
1828
|
...integrations
|
|
1795
1829
|
};
|
|
1796
1830
|
const integrationBlock = serializeIntegrationConfig(nextIntegrations);
|
|
1797
|
-
const existing = await fileExists(configPath) ? await
|
|
1831
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1798
1832
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1799
1833
|
if (!fmMatch) {
|
|
1800
1834
|
const content = `---
|
|
@@ -1824,7 +1858,7 @@ async function updateOnboardingConfig(onboarding) {
|
|
|
1824
1858
|
...onboarding
|
|
1825
1859
|
};
|
|
1826
1860
|
const onboardingBlock = serializeOnboardingConfig(nextOnboarding);
|
|
1827
|
-
const existing = await fileExists(configPath) ? await
|
|
1861
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1828
1862
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1829
1863
|
if (!fmMatch) {
|
|
1830
1864
|
const content = `---
|
|
@@ -1858,7 +1892,7 @@ async function updateBackupConfig(backup) {
|
|
|
1858
1892
|
...backup
|
|
1859
1893
|
};
|
|
1860
1894
|
const backupBlock = serializeBackupConfig(nextBackup);
|
|
1861
|
-
const existing = await fileExists(configPath) ? await
|
|
1895
|
+
const existing = await fileExists(configPath) ? await readFile3(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
|
|
1862
1896
|
const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
|
|
1863
1897
|
if (!fmMatch) {
|
|
1864
1898
|
const content = `---
|
|
@@ -1890,7 +1924,7 @@ async function readConfig() {
|
|
|
1890
1924
|
migratedConfigPaths.add(configPath);
|
|
1891
1925
|
await migrateLegacyConfig(configPath);
|
|
1892
1926
|
}
|
|
1893
|
-
const content = await
|
|
1927
|
+
const content = await readFile3(configPath, "utf-8");
|
|
1894
1928
|
const fm = parseFrontmatter(content);
|
|
1895
1929
|
if (Object.keys(fm).length === 0) {
|
|
1896
1930
|
console.warn("Warning: ~/.syntaur/config.md has malformed frontmatter, using defaults");
|
|
@@ -1929,7 +1963,8 @@ async function readConfig() {
|
|
|
1929
1963
|
codexMarketplacePath: parseOptionalAbsolutePath(
|
|
1930
1964
|
fm["integrations.codexMarketplacePath"],
|
|
1931
1965
|
"integrations.codexMarketplacePath"
|
|
1932
|
-
)
|
|
1966
|
+
),
|
|
1967
|
+
...parseInstalledAgents(fm)
|
|
1933
1968
|
},
|
|
1934
1969
|
backup: fm["backup.repo"] || fm["backup.categories"] ? {
|
|
1935
1970
|
repo: fm["backup.repo"] && fm["backup.repo"] !== "null" ? fm["backup.repo"] : null,
|
|
@@ -2094,7 +2129,7 @@ var init_slug = __esm({
|
|
|
2094
2129
|
|
|
2095
2130
|
// src/utils/playbooks.ts
|
|
2096
2131
|
import { resolve as resolve4 } from "path";
|
|
2097
|
-
import { readdir as readdir2, readFile as
|
|
2132
|
+
import { readdir as readdir2, readFile as readFile4, unlink } from "fs/promises";
|
|
2098
2133
|
function escapeRegExp(value) {
|
|
2099
2134
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2100
2135
|
}
|
|
@@ -2120,7 +2155,7 @@ async function resolvePlaybookSlug(playbooksDir3, slug) {
|
|
|
2120
2155
|
for (const entry of entries) {
|
|
2121
2156
|
if (!isVisiblePlaybookFile(entry.name, entry.isFile())) continue;
|
|
2122
2157
|
const filePath = resolve4(playbooksDir3, entry.name);
|
|
2123
|
-
const raw = await
|
|
2158
|
+
const raw = await readFile4(filePath, "utf-8");
|
|
2124
2159
|
const parsed = parsePlaybook(raw);
|
|
2125
2160
|
const canonical = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
2126
2161
|
if (canonical === slug) {
|
|
@@ -2168,7 +2203,7 @@ async function rebuildPlaybookManifest(playbooksDir3) {
|
|
|
2168
2203
|
const rows = [];
|
|
2169
2204
|
for (const entry of entries) {
|
|
2170
2205
|
if (!isVisiblePlaybookFile(entry.name, entry.isFile())) continue;
|
|
2171
|
-
const raw = await
|
|
2206
|
+
const raw = await readFile4(resolve4(playbooksDir3, entry.name), "utf-8");
|
|
2172
2207
|
const parsed = parsePlaybook(raw);
|
|
2173
2208
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
2174
2209
|
if (disabledSet.has(slug)) continue;
|
|
@@ -2246,7 +2281,7 @@ async function renamePlaybook(playbooksDir3, oldSlug, newSlug) {
|
|
|
2246
2281
|
);
|
|
2247
2282
|
}
|
|
2248
2283
|
}
|
|
2249
|
-
const raw = await
|
|
2284
|
+
const raw = await readFile4(oldPath, "utf-8");
|
|
2250
2285
|
let next = setFrontmatterField2(raw, "slug", newSlug);
|
|
2251
2286
|
next = setFrontmatterField2(next, "updated", `"${nowTimestamp()}"`);
|
|
2252
2287
|
await writeFileForce(newPath, next);
|
|
@@ -3173,6 +3208,24 @@ var init_opencode_config = __esm({
|
|
|
3173
3208
|
}
|
|
3174
3209
|
});
|
|
3175
3210
|
|
|
3211
|
+
// src/templates/hermes-soul.ts
|
|
3212
|
+
function renderHermesSoul(params2) {
|
|
3213
|
+
const body = renderCodexAgents(params2);
|
|
3214
|
+
return `# SOUL -- Syntaur Protocol Operator
|
|
3215
|
+
|
|
3216
|
+
This agent follows the Syntaur protocol for multi-agent project coordination.
|
|
3217
|
+
Hermes loads this file as part of its identity / system context; treat the
|
|
3218
|
+
Write Boundary Rules and Lifecycle sections below as binding.
|
|
3219
|
+
|
|
3220
|
+
${body}`;
|
|
3221
|
+
}
|
|
3222
|
+
var init_hermes_soul = __esm({
|
|
3223
|
+
"src/templates/hermes-soul.ts"() {
|
|
3224
|
+
"use strict";
|
|
3225
|
+
init_codex_agents();
|
|
3226
|
+
}
|
|
3227
|
+
});
|
|
3228
|
+
|
|
3176
3229
|
// src/templates/index.ts
|
|
3177
3230
|
var init_templates = __esm({
|
|
3178
3231
|
"src/templates/index.ts"() {
|
|
@@ -3193,6 +3246,7 @@ var init_templates = __esm({
|
|
|
3193
3246
|
init_cursor_rules();
|
|
3194
3247
|
init_codex_agents();
|
|
3195
3248
|
init_opencode_config();
|
|
3249
|
+
init_hermes_soul();
|
|
3196
3250
|
}
|
|
3197
3251
|
});
|
|
3198
3252
|
|
|
@@ -3745,7 +3799,7 @@ __export(parser_exports, {
|
|
|
3745
3799
|
writeChecklist: () => writeChecklist
|
|
3746
3800
|
});
|
|
3747
3801
|
import { randomBytes } from "crypto";
|
|
3748
|
-
import { readFile as
|
|
3802
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
3749
3803
|
import { resolve as resolve8 } from "path";
|
|
3750
3804
|
function generateShortId() {
|
|
3751
3805
|
return randomBytes(2).toString("hex");
|
|
@@ -4042,7 +4096,7 @@ async function readChecklist(todosDir2, workspace) {
|
|
|
4042
4096
|
if (!await fileExists(path)) {
|
|
4043
4097
|
return { workspace, archiveInterval: "weekly", items: [] };
|
|
4044
4098
|
}
|
|
4045
|
-
const content = await
|
|
4099
|
+
const content = await readFile6(path, "utf-8");
|
|
4046
4100
|
return parseChecklist(content);
|
|
4047
4101
|
}
|
|
4048
4102
|
async function writeChecklist(todosDir2, checklist) {
|
|
@@ -4055,7 +4109,7 @@ async function readLog(todosDir2, workspace) {
|
|
|
4055
4109
|
if (!await fileExists(path)) {
|
|
4056
4110
|
return { workspace, entries: [] };
|
|
4057
4111
|
}
|
|
4058
|
-
const content = await
|
|
4112
|
+
const content = await readFile6(path, "utf-8");
|
|
4059
4113
|
return parseLog(content);
|
|
4060
4114
|
}
|
|
4061
4115
|
async function appendLogEntry(todosDir2, workspace, entry) {
|
|
@@ -4063,7 +4117,7 @@ async function appendLogEntry(todosDir2, workspace, entry) {
|
|
|
4063
4117
|
const path = logPath(todosDir2, workspace);
|
|
4064
4118
|
let content;
|
|
4065
4119
|
if (await fileExists(path)) {
|
|
4066
|
-
content = await
|
|
4120
|
+
content = await readFile6(path, "utf-8");
|
|
4067
4121
|
content = content.trimEnd() + "\n\n" + serializeLogEntry(entry) + "\n";
|
|
4068
4122
|
} else {
|
|
4069
4123
|
const fm = `---
|
|
@@ -4231,7 +4285,7 @@ var init_linked_todos = __esm({
|
|
|
4231
4285
|
|
|
4232
4286
|
// src/lifecycle/transitions.ts
|
|
4233
4287
|
import { resolve as resolve10 } from "path";
|
|
4234
|
-
import { readFile as
|
|
4288
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
4235
4289
|
function linkedAssignmentRef(frontmatter) {
|
|
4236
4290
|
return frontmatter.project ? `${frontmatter.project}/${frontmatter.slug}` : frontmatter.id;
|
|
4237
4291
|
}
|
|
@@ -4251,7 +4305,7 @@ async function readAssignment(filePath) {
|
|
|
4251
4305
|
if (!await fileExists(filePath)) {
|
|
4252
4306
|
throw new Error(`Assignment file not found: ${filePath}`);
|
|
4253
4307
|
}
|
|
4254
|
-
const content = await
|
|
4308
|
+
const content = await readFile7(filePath, "utf-8");
|
|
4255
4309
|
const frontmatter = parseAssignmentFrontmatter(content);
|
|
4256
4310
|
return { content, frontmatter };
|
|
4257
4311
|
}
|
|
@@ -4264,7 +4318,7 @@ async function checkDependencies(projectDir, dependsOn, terminalStatuses3) {
|
|
|
4264
4318
|
unmet.push(`${depSlug} (file not found)`);
|
|
4265
4319
|
continue;
|
|
4266
4320
|
}
|
|
4267
|
-
const depContent = await
|
|
4321
|
+
const depContent = await readFile7(depPath, "utf-8");
|
|
4268
4322
|
const depFrontmatter = parseAssignmentFrontmatter(depContent);
|
|
4269
4323
|
if (!terminals.has(depFrontmatter.status)) {
|
|
4270
4324
|
unmet.push(`${depSlug} (status: ${depFrontmatter.status})`);
|
|
@@ -4417,7 +4471,7 @@ var init_lifecycle = __esm({
|
|
|
4417
4471
|
|
|
4418
4472
|
// src/utils/assignment-resolver.ts
|
|
4419
4473
|
import { resolve as resolve11 } from "path";
|
|
4420
|
-
import { readdir as readdir5, readFile as
|
|
4474
|
+
import { readdir as readdir5, readFile as readFile8 } from "fs/promises";
|
|
4421
4475
|
async function resolveAssignmentById(projectsDir2, assignmentsDir2, id) {
|
|
4422
4476
|
let standaloneMatch = null;
|
|
4423
4477
|
let projectMatch = null;
|
|
@@ -4426,7 +4480,7 @@ async function resolveAssignmentById(projectsDir2, assignmentsDir2, id) {
|
|
|
4426
4480
|
if (await fileExists(standalonePath)) {
|
|
4427
4481
|
let workspaceGroup = null;
|
|
4428
4482
|
try {
|
|
4429
|
-
const content = await
|
|
4483
|
+
const content = await readFile8(standalonePath, "utf-8");
|
|
4430
4484
|
const [fm] = extractFrontmatter(content);
|
|
4431
4485
|
workspaceGroup = getField(fm, "workspaceGroup");
|
|
4432
4486
|
} catch {
|
|
@@ -4454,7 +4508,7 @@ async function resolveAssignmentById(projectsDir2, assignmentsDir2, id) {
|
|
|
4454
4508
|
const aPath = resolve11(assignmentsPath, a.name, "assignment.md");
|
|
4455
4509
|
if (!await fileExists(aPath)) continue;
|
|
4456
4510
|
try {
|
|
4457
|
-
const content = await
|
|
4511
|
+
const content = await readFile8(aPath, "utf-8");
|
|
4458
4512
|
const [fm] = extractFrontmatter(content);
|
|
4459
4513
|
const fileId = getField(fm, "id");
|
|
4460
4514
|
if (fileId === id) {
|
|
@@ -5106,8 +5160,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
5106
5160
|
return allSessions.length;
|
|
5107
5161
|
}
|
|
5108
5162
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
5109
|
-
const { readFile:
|
|
5110
|
-
const raw = await
|
|
5163
|
+
const { readFile: readFile56 } = await import("fs/promises");
|
|
5164
|
+
const raw = await readFile56(filePath, "utf-8");
|
|
5111
5165
|
const sessions = [];
|
|
5112
5166
|
const lines = raw.split("\n");
|
|
5113
5167
|
let inTable = false;
|
|
@@ -5177,7 +5231,7 @@ CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, ass
|
|
|
5177
5231
|
});
|
|
5178
5232
|
|
|
5179
5233
|
// src/dashboard/agent-sessions.ts
|
|
5180
|
-
import { readFile as
|
|
5234
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
5181
5235
|
import { resolve as resolve13 } from "path";
|
|
5182
5236
|
function rowToSession(row) {
|
|
5183
5237
|
return {
|
|
@@ -5265,7 +5319,7 @@ async function deleteSessions(sessionIds) {
|
|
|
5265
5319
|
}
|
|
5266
5320
|
async function readAssignmentStatusFromPath(assignmentMdPath2) {
|
|
5267
5321
|
if (!await fileExists(assignmentMdPath2)) return null;
|
|
5268
|
-
const raw = await
|
|
5322
|
+
const raw = await readFile9(assignmentMdPath2, "utf-8");
|
|
5269
5323
|
const match = raw.match(/^status:\s*(.+)$/m);
|
|
5270
5324
|
return match ? match[1].trim() : null;
|
|
5271
5325
|
}
|
|
@@ -5350,7 +5404,7 @@ var init_overviewCopy = __esm({
|
|
|
5350
5404
|
});
|
|
5351
5405
|
|
|
5352
5406
|
// src/dashboard/servers.ts
|
|
5353
|
-
import { readdir as readdir7, readFile as
|
|
5407
|
+
import { readdir as readdir7, readFile as readFile10, unlink as unlink2 } from "fs/promises";
|
|
5354
5408
|
import { resolve as resolve14 } from "path";
|
|
5355
5409
|
function sanitizeSessionName(name) {
|
|
5356
5410
|
return name.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
@@ -5410,7 +5464,7 @@ async function listSessionFiles(dir) {
|
|
|
5410
5464
|
async function readSessionFile(dir, name) {
|
|
5411
5465
|
const filePath = resolve14(dir, `${sanitizeSessionName(name)}.md`);
|
|
5412
5466
|
if (!await fileExists(filePath)) return null;
|
|
5413
|
-
const raw = await
|
|
5467
|
+
const raw = await readFile10(filePath, "utf-8");
|
|
5414
5468
|
const [frontmatter] = extractFrontmatter(raw);
|
|
5415
5469
|
if (!frontmatter) return null;
|
|
5416
5470
|
const session = getField(frontmatter, "session") ?? name;
|
|
@@ -5539,8 +5593,8 @@ function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
|
|
|
5539
5593
|
return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
|
|
5540
5594
|
}
|
|
5541
5595
|
function delay(ms) {
|
|
5542
|
-
return new Promise((
|
|
5543
|
-
const timer2 = setTimeout(
|
|
5596
|
+
return new Promise((resolve82) => {
|
|
5597
|
+
const timer2 = setTimeout(resolve82, ms);
|
|
5544
5598
|
if (typeof timer2.unref === "function") {
|
|
5545
5599
|
timer2.unref();
|
|
5546
5600
|
}
|
|
@@ -5983,7 +6037,7 @@ var init_scanner = __esm({
|
|
|
5983
6037
|
});
|
|
5984
6038
|
|
|
5985
6039
|
// src/dashboard/api.ts
|
|
5986
|
-
import { readdir as readdir8, readFile as
|
|
6040
|
+
import { readdir as readdir8, readFile as readFile11, writeFile as writeFile3 } from "fs/promises";
|
|
5987
6041
|
import { resolve as resolve16, dirname as dirname3, basename } from "path";
|
|
5988
6042
|
function clearFrontmatterField(content, key) {
|
|
5989
6043
|
const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
|
|
@@ -6071,7 +6125,7 @@ async function computeStandaloneRecords(assignmentsDir2) {
|
|
|
6071
6125
|
const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
|
|
6072
6126
|
if (!await fileExists(assignmentMdPath2)) continue;
|
|
6073
6127
|
try {
|
|
6074
|
-
const content = await
|
|
6128
|
+
const content = await readFile11(assignmentMdPath2, "utf-8");
|
|
6075
6129
|
const record = parseAssignmentFull(content);
|
|
6076
6130
|
records.push({ assignmentDir, id: entry.name, record });
|
|
6077
6131
|
} catch {
|
|
@@ -6147,7 +6201,7 @@ async function listProjects(projectsDir2) {
|
|
|
6147
6201
|
async function readWorkspaceRegistry(projectsDir2) {
|
|
6148
6202
|
const registryPath = resolve16(dirname3(projectsDir2), "workspaces.json");
|
|
6149
6203
|
try {
|
|
6150
|
-
const raw = await
|
|
6204
|
+
const raw = await readFile11(registryPath, "utf-8");
|
|
6151
6205
|
const parsed = JSON.parse(raw);
|
|
6152
6206
|
return Array.isArray(parsed) ? parsed.filter((w) => typeof w === "string") : [];
|
|
6153
6207
|
} catch {
|
|
@@ -6236,7 +6290,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
|
|
|
6236
6290
|
const timestamp = nowTimestamp();
|
|
6237
6291
|
for (const slug of projectsReferencing) {
|
|
6238
6292
|
const path = resolve16(projectsDir2, slug, "project.md");
|
|
6239
|
-
const raw = await
|
|
6293
|
+
const raw = await readFile11(path, "utf-8");
|
|
6240
6294
|
let next = clearFrontmatterField(raw, "workspace");
|
|
6241
6295
|
next = setUpdatedField(next, timestamp);
|
|
6242
6296
|
await writeFileForce(path, next);
|
|
@@ -6245,7 +6299,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
|
|
|
6245
6299
|
for (const id of standalonesReferencing) {
|
|
6246
6300
|
if (!opts.assignmentsDir) break;
|
|
6247
6301
|
const path = resolve16(opts.assignmentsDir, id, "assignment.md");
|
|
6248
|
-
const raw = await
|
|
6302
|
+
const raw = await readFile11(path, "utf-8");
|
|
6249
6303
|
let next = clearFrontmatterField(raw, "workspaceGroup");
|
|
6250
6304
|
next = setUpdatedField(next, timestamp);
|
|
6251
6305
|
await writeFileForce(path, next);
|
|
@@ -6482,7 +6536,7 @@ async function getEditableDocument(projectsDir2, documentType, projectSlug, assi
|
|
|
6482
6536
|
if (!filePath || !await fileExists(filePath)) {
|
|
6483
6537
|
return null;
|
|
6484
6538
|
}
|
|
6485
|
-
const content = await
|
|
6539
|
+
const content = await readFile11(filePath, "utf-8");
|
|
6486
6540
|
const title = getEditableDocumentTitle(documentType, projectSlug, assignmentSlug);
|
|
6487
6541
|
return {
|
|
6488
6542
|
documentType,
|
|
@@ -6508,7 +6562,7 @@ async function getEditableDocumentById(projectsDir2, assignmentsDir2, documentTy
|
|
|
6508
6562
|
if (!fileName) return null;
|
|
6509
6563
|
const filePath = resolve16(resolved.assignmentDir, fileName);
|
|
6510
6564
|
if (!await fileExists(filePath)) return null;
|
|
6511
|
-
const content = await
|
|
6565
|
+
const content = await readFile11(filePath, "utf-8");
|
|
6512
6566
|
const label = resolved.id;
|
|
6513
6567
|
const title = documentType === "assignment" ? `Edit Assignment: ${label}` : documentType === "plan" ? `Edit Plan: ${label}` : documentType === "scratchpad" ? `Edit Scratchpad: ${label}` : documentType === "handoff" ? `Append Handoff: ${label}` : `Append Decision: ${label}`;
|
|
6514
6568
|
return {
|
|
@@ -6527,7 +6581,7 @@ async function getProjectDetail(projectsDir2, slug) {
|
|
|
6527
6581
|
if (!await fileExists(projectMdPath)) {
|
|
6528
6582
|
return null;
|
|
6529
6583
|
}
|
|
6530
|
-
const projectContent = await
|
|
6584
|
+
const projectContent = await readFile11(projectMdPath, "utf-8");
|
|
6531
6585
|
const project = parseProject(projectContent);
|
|
6532
6586
|
const assignments = await listAssignmentRecords(projectPath);
|
|
6533
6587
|
const rollup = await buildProjectRollup(projectPath, project, assignments);
|
|
@@ -6564,18 +6618,18 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6564
6618
|
if (!await fileExists(assignmentMdPath2)) {
|
|
6565
6619
|
return null;
|
|
6566
6620
|
}
|
|
6567
|
-
const assignmentContent = await
|
|
6621
|
+
const assignmentContent = await readFile11(assignmentMdPath2, "utf-8");
|
|
6568
6622
|
const assignment = parseAssignmentFull(assignmentContent);
|
|
6569
6623
|
let projectWorkspace = null;
|
|
6570
6624
|
const projectMdPath = resolve16(projectsDir2, projectSlug, "project.md");
|
|
6571
6625
|
if (await fileExists(projectMdPath)) {
|
|
6572
|
-
const projectContent = await
|
|
6626
|
+
const projectContent = await readFile11(projectMdPath, "utf-8");
|
|
6573
6627
|
projectWorkspace = parseProject(projectContent).workspace;
|
|
6574
6628
|
}
|
|
6575
6629
|
let plan = null;
|
|
6576
6630
|
const planPath = resolve16(assignmentDir, "plan.md");
|
|
6577
6631
|
if (await fileExists(planPath)) {
|
|
6578
|
-
const planContent = await
|
|
6632
|
+
const planContent = await readFile11(planPath, "utf-8");
|
|
6579
6633
|
const parsed = parsePlan(planContent);
|
|
6580
6634
|
plan = {
|
|
6581
6635
|
status: parsed.status,
|
|
@@ -6586,7 +6640,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6586
6640
|
let scratchpad = null;
|
|
6587
6641
|
const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
|
|
6588
6642
|
if (await fileExists(scratchpadPath)) {
|
|
6589
|
-
const scratchpadContent = await
|
|
6643
|
+
const scratchpadContent = await readFile11(scratchpadPath, "utf-8");
|
|
6590
6644
|
const parsed = parseScratchpad(scratchpadContent);
|
|
6591
6645
|
scratchpad = {
|
|
6592
6646
|
updated: parsed.updated,
|
|
@@ -6596,7 +6650,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6596
6650
|
let handoff = null;
|
|
6597
6651
|
const handoffPath = resolve16(assignmentDir, "handoff.md");
|
|
6598
6652
|
if (await fileExists(handoffPath)) {
|
|
6599
|
-
const handoffContent = await
|
|
6653
|
+
const handoffContent = await readFile11(handoffPath, "utf-8");
|
|
6600
6654
|
const parsed = parseHandoff(handoffContent);
|
|
6601
6655
|
handoff = {
|
|
6602
6656
|
updated: parsed.updated,
|
|
@@ -6607,7 +6661,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6607
6661
|
let decisionRecord = null;
|
|
6608
6662
|
const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
|
|
6609
6663
|
if (await fileExists(decisionRecordPath)) {
|
|
6610
|
-
const decisionRecordContent = await
|
|
6664
|
+
const decisionRecordContent = await readFile11(decisionRecordPath, "utf-8");
|
|
6611
6665
|
const parsed = parseDecisionRecord(decisionRecordContent);
|
|
6612
6666
|
decisionRecord = {
|
|
6613
6667
|
updated: parsed.updated,
|
|
@@ -6618,7 +6672,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6618
6672
|
let progress = null;
|
|
6619
6673
|
const progressPath = resolve16(assignmentDir, "progress.md");
|
|
6620
6674
|
if (await fileExists(progressPath)) {
|
|
6621
|
-
const progressContent = await
|
|
6675
|
+
const progressContent = await readFile11(progressPath, "utf-8");
|
|
6622
6676
|
const parsed = parseProgress(progressContent);
|
|
6623
6677
|
progress = {
|
|
6624
6678
|
updated: parsed.updated,
|
|
@@ -6629,7 +6683,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6629
6683
|
let comments = null;
|
|
6630
6684
|
const commentsPath = resolve16(assignmentDir, "comments.md");
|
|
6631
6685
|
if (await fileExists(commentsPath)) {
|
|
6632
|
-
const commentsContent = await
|
|
6686
|
+
const commentsContent = await readFile11(commentsPath, "utf-8");
|
|
6633
6687
|
const parsed = parseComments(commentsContent);
|
|
6634
6688
|
comments = {
|
|
6635
6689
|
updated: parsed.updated,
|
|
@@ -6783,7 +6837,7 @@ async function countMentionsInAssignment(sourceDir, target) {
|
|
|
6783
6837
|
const bodies = [];
|
|
6784
6838
|
const assignmentMd = resolve16(sourceDir, "assignment.md");
|
|
6785
6839
|
if (await fileExists(assignmentMd)) {
|
|
6786
|
-
const content = await
|
|
6840
|
+
const content = await readFile11(assignmentMd, "utf-8");
|
|
6787
6841
|
const todosMatch = content.match(/^## Todos\s*$([\s\S]*?)(?=^## |$(?![\r\n]))/m);
|
|
6788
6842
|
if (todosMatch) bodies.push(todosMatch[1]);
|
|
6789
6843
|
}
|
|
@@ -6791,7 +6845,7 @@ async function countMentionsInAssignment(sourceDir, target) {
|
|
|
6791
6845
|
const path = resolve16(sourceDir, filename);
|
|
6792
6846
|
if (await fileExists(path)) {
|
|
6793
6847
|
try {
|
|
6794
|
-
bodies.push(await
|
|
6848
|
+
bodies.push(await readFile11(path, "utf-8"));
|
|
6795
6849
|
} catch {
|
|
6796
6850
|
}
|
|
6797
6851
|
}
|
|
@@ -6851,42 +6905,42 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
6851
6905
|
const assignmentDir = resolved.assignmentDir;
|
|
6852
6906
|
const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
|
|
6853
6907
|
if (!await fileExists(assignmentMdPath2)) return null;
|
|
6854
|
-
const assignmentContent = await
|
|
6908
|
+
const assignmentContent = await readFile11(assignmentMdPath2, "utf-8");
|
|
6855
6909
|
const assignment = parseAssignmentFull(assignmentContent);
|
|
6856
6910
|
let plan = null;
|
|
6857
6911
|
const planPath = resolve16(assignmentDir, "plan.md");
|
|
6858
6912
|
if (await fileExists(planPath)) {
|
|
6859
|
-
const parsed = parsePlan(await
|
|
6913
|
+
const parsed = parsePlan(await readFile11(planPath, "utf-8"));
|
|
6860
6914
|
plan = { status: parsed.status, updated: parsed.updated, body: parsed.body };
|
|
6861
6915
|
}
|
|
6862
6916
|
let scratchpad = null;
|
|
6863
6917
|
const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
|
|
6864
6918
|
if (await fileExists(scratchpadPath)) {
|
|
6865
|
-
const parsed = parseScratchpad(await
|
|
6919
|
+
const parsed = parseScratchpad(await readFile11(scratchpadPath, "utf-8"));
|
|
6866
6920
|
scratchpad = { updated: parsed.updated, body: parsed.body };
|
|
6867
6921
|
}
|
|
6868
6922
|
let handoff = null;
|
|
6869
6923
|
const handoffPath = resolve16(assignmentDir, "handoff.md");
|
|
6870
6924
|
if (await fileExists(handoffPath)) {
|
|
6871
|
-
const parsed = parseHandoff(await
|
|
6925
|
+
const parsed = parseHandoff(await readFile11(handoffPath, "utf-8"));
|
|
6872
6926
|
handoff = { updated: parsed.updated, handoffCount: parsed.handoffCount, body: parsed.body };
|
|
6873
6927
|
}
|
|
6874
6928
|
let decisionRecord = null;
|
|
6875
6929
|
const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
|
|
6876
6930
|
if (await fileExists(decisionRecordPath)) {
|
|
6877
|
-
const parsed = parseDecisionRecord(await
|
|
6931
|
+
const parsed = parseDecisionRecord(await readFile11(decisionRecordPath, "utf-8"));
|
|
6878
6932
|
decisionRecord = { updated: parsed.updated, decisionCount: parsed.decisionCount, body: parsed.body };
|
|
6879
6933
|
}
|
|
6880
6934
|
let progress = null;
|
|
6881
6935
|
const progressPath = resolve16(assignmentDir, "progress.md");
|
|
6882
6936
|
if (await fileExists(progressPath)) {
|
|
6883
|
-
const parsed = parseProgress(await
|
|
6937
|
+
const parsed = parseProgress(await readFile11(progressPath, "utf-8"));
|
|
6884
6938
|
progress = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
|
|
6885
6939
|
}
|
|
6886
6940
|
let comments = null;
|
|
6887
6941
|
const commentsPath = resolve16(assignmentDir, "comments.md");
|
|
6888
6942
|
if (await fileExists(commentsPath)) {
|
|
6889
|
-
const parsed = parseComments(await
|
|
6943
|
+
const parsed = parseComments(await readFile11(commentsPath, "utf-8"));
|
|
6890
6944
|
comments = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
|
|
6891
6945
|
}
|
|
6892
6946
|
const detail = {
|
|
@@ -6952,7 +7006,7 @@ async function computeProjectRecords(projectsDir2, traces) {
|
|
|
6952
7006
|
return null;
|
|
6953
7007
|
}
|
|
6954
7008
|
const t0 = traces ? performance.now() : 0;
|
|
6955
|
-
const projectContent = await
|
|
7009
|
+
const projectContent = await readFile11(projectMdPath, "utf-8");
|
|
6956
7010
|
const project = parseProject(projectContent);
|
|
6957
7011
|
if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
|
|
6958
7012
|
const t1 = traces ? performance.now() : 0;
|
|
@@ -7006,7 +7060,7 @@ async function listAssignmentRecords(projectPath, traces) {
|
|
|
7006
7060
|
return null;
|
|
7007
7061
|
}
|
|
7008
7062
|
const t0 = traces ? performance.now() : 0;
|
|
7009
|
-
const content = await
|
|
7063
|
+
const content = await readFile11(assignmentMd, "utf-8");
|
|
7010
7064
|
const parsed = parseAssignmentFull(content);
|
|
7011
7065
|
if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
|
|
7012
7066
|
return parsed;
|
|
@@ -7028,7 +7082,7 @@ async function listResources(projectPath) {
|
|
|
7028
7082
|
continue;
|
|
7029
7083
|
}
|
|
7030
7084
|
const filePath = resolve16(resourcesDir, entry.name);
|
|
7031
|
-
const content = await
|
|
7085
|
+
const content = await readFile11(filePath, "utf-8");
|
|
7032
7086
|
const parsed = parseResource(content);
|
|
7033
7087
|
results.push({
|
|
7034
7088
|
name: parsed.name,
|
|
@@ -7054,7 +7108,7 @@ async function listMemories(projectPath) {
|
|
|
7054
7108
|
continue;
|
|
7055
7109
|
}
|
|
7056
7110
|
const filePath = resolve16(memoriesDir, entry.name);
|
|
7057
|
-
const content = await
|
|
7111
|
+
const content = await readFile11(filePath, "utf-8");
|
|
7058
7112
|
const parsed = parseMemory(content);
|
|
7059
7113
|
results.push({
|
|
7060
7114
|
name: parsed.name,
|
|
@@ -7117,7 +7171,7 @@ async function getMemoryDetail(projectsDir2, projectSlug, itemSlug) {
|
|
|
7117
7171
|
if (!projectRecord) return null;
|
|
7118
7172
|
const filePath = resolve16(projectRecord.projectPath, "memories", `${itemSlug}.md`);
|
|
7119
7173
|
if (!await fileExists(filePath)) return null;
|
|
7120
|
-
const content = await
|
|
7174
|
+
const content = await readFile11(filePath, "utf-8");
|
|
7121
7175
|
const parsed = parseMemory(content);
|
|
7122
7176
|
return {
|
|
7123
7177
|
name: parsed.name,
|
|
@@ -7143,7 +7197,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
|
|
|
7143
7197
|
if (!projectRecord) return null;
|
|
7144
7198
|
const filePath = resolve16(projectRecord.projectPath, "resources", `${itemSlug}.md`);
|
|
7145
7199
|
if (!await fileExists(filePath)) return null;
|
|
7146
|
-
const content = await
|
|
7200
|
+
const content = await readFile11(filePath, "utf-8");
|
|
7147
7201
|
const parsed = parseResource(content);
|
|
7148
7202
|
return {
|
|
7149
7203
|
name: parsed.name,
|
|
@@ -7161,7 +7215,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
|
|
|
7161
7215
|
async function loadDependencyGraph(projectPath, assignments) {
|
|
7162
7216
|
const statusPath = resolve16(projectPath, "_status.md");
|
|
7163
7217
|
if (await fileExists(statusPath)) {
|
|
7164
|
-
const statusContent = await
|
|
7218
|
+
const statusContent = await readFile11(statusPath, "utf-8");
|
|
7165
7219
|
const parsed = parseStatus(statusContent);
|
|
7166
7220
|
const derivedGraph = extractMermaidGraph(parsed.body);
|
|
7167
7221
|
if (derivedGraph) {
|
|
@@ -7328,7 +7382,7 @@ async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses3, d
|
|
|
7328
7382
|
unmet.push(`${dependency} (missing)`);
|
|
7329
7383
|
continue;
|
|
7330
7384
|
}
|
|
7331
|
-
const content = await
|
|
7385
|
+
const content = await readFile11(dependencyPath, "utf-8");
|
|
7332
7386
|
const parsed = parseAssignmentFull(content);
|
|
7333
7387
|
if (!terminals.has(parsed.status)) {
|
|
7334
7388
|
unmet.push(`${dependency} (${parsed.status})`);
|
|
@@ -7623,7 +7677,7 @@ async function countOpenQuestions(projectPath, assignmentSlug) {
|
|
|
7623
7677
|
return 0;
|
|
7624
7678
|
}
|
|
7625
7679
|
try {
|
|
7626
|
-
const content = await
|
|
7680
|
+
const content = await readFile11(commentsPath, "utf-8");
|
|
7627
7681
|
const parsed = parseComments(content);
|
|
7628
7682
|
return parsed.entries.filter(
|
|
7629
7683
|
(e) => e.type === "question" && e.resolved !== true
|
|
@@ -7696,7 +7750,7 @@ async function listPlaybooks(playbooksDir3) {
|
|
|
7696
7750
|
for (const entry of entries) {
|
|
7697
7751
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_") || entry.name === "manifest.md") continue;
|
|
7698
7752
|
const filePath = resolve16(playbooksDir3, entry.name);
|
|
7699
|
-
const raw = await
|
|
7753
|
+
const raw = await readFile11(filePath, "utf-8");
|
|
7700
7754
|
const parsed = parsePlaybook(raw);
|
|
7701
7755
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
7702
7756
|
playbooks.push({
|
|
@@ -7881,7 +7935,7 @@ __export(git_worktree_exports, {
|
|
|
7881
7935
|
removeWorktree: () => removeWorktree
|
|
7882
7936
|
});
|
|
7883
7937
|
import { spawn } from "child_process";
|
|
7884
|
-
import { readFile as
|
|
7938
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
7885
7939
|
function run(command, args, cwd) {
|
|
7886
7940
|
return new Promise((resolvePromise) => {
|
|
7887
7941
|
const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -7959,7 +8013,7 @@ async function createWorktreeAndRecord(opts) {
|
|
|
7959
8013
|
const { assignmentPath, repository, branch, worktreePath, parentBranch } = opts;
|
|
7960
8014
|
await createWorktree({ repository, branch, worktreePath, parentBranch });
|
|
7961
8015
|
try {
|
|
7962
|
-
const content = await
|
|
8016
|
+
const content = await readFile14(assignmentPath, "utf-8");
|
|
7963
8017
|
const updated = updateAssignmentWorkspace(content, {
|
|
7964
8018
|
repository,
|
|
7965
8019
|
worktreePath,
|
|
@@ -8428,15 +8482,15 @@ import {
|
|
|
8428
8482
|
unlinkSync
|
|
8429
8483
|
} from "fs";
|
|
8430
8484
|
import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
|
|
8431
|
-
import { dirname as dirname16, resolve as
|
|
8432
|
-
import { homedir as
|
|
8433
|
-
import { spawnSync as
|
|
8485
|
+
import { dirname as dirname16, resolve as resolve49, join as join9 } from "path";
|
|
8486
|
+
import { homedir as homedir9, tmpdir as tmpdir2 } from "os";
|
|
8487
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
8434
8488
|
function syntaurRootMjs() {
|
|
8435
8489
|
const override = process.env.SYNTAUR_HOME;
|
|
8436
8490
|
if (override && override.length > 0) {
|
|
8437
8491
|
return override;
|
|
8438
8492
|
}
|
|
8439
|
-
return join9(
|
|
8493
|
+
return join9(homedir9(), ".syntaur");
|
|
8440
8494
|
}
|
|
8441
8495
|
async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
8442
8496
|
const { throwOnFailure } = options;
|
|
@@ -8450,7 +8504,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8450
8504
|
}
|
|
8451
8505
|
const stateRoot = syntaurRootMjs();
|
|
8452
8506
|
mkdirSync2(stateRoot, { recursive: true });
|
|
8453
|
-
const lockPath =
|
|
8507
|
+
const lockPath = resolve49(stateRoot, "install-url-handler.lock");
|
|
8454
8508
|
let lockFd;
|
|
8455
8509
|
try {
|
|
8456
8510
|
lockFd = openSync(lockPath, "wx");
|
|
@@ -8466,11 +8520,11 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8466
8520
|
throw err2;
|
|
8467
8521
|
}
|
|
8468
8522
|
try {
|
|
8469
|
-
const pkgRoot =
|
|
8470
|
-
const cliBin = realpathSync3(
|
|
8523
|
+
const pkgRoot = resolve49(dirname16(fileURLToPath9(import.meta.url)), "..");
|
|
8524
|
+
const cliBin = realpathSync3(resolve49(pkgRoot, "bin/syntaur.js"));
|
|
8471
8525
|
const nodeBin = process.execPath;
|
|
8472
8526
|
const bundleParent = join9(
|
|
8473
|
-
|
|
8527
|
+
homedir9(),
|
|
8474
8528
|
"Library",
|
|
8475
8529
|
"Application Support",
|
|
8476
8530
|
"Syntaur"
|
|
@@ -8490,7 +8544,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8490
8544
|
renderAppleScript({ nodeBin, cliBin, installedTerminals }),
|
|
8491
8545
|
"utf-8"
|
|
8492
8546
|
);
|
|
8493
|
-
const compile =
|
|
8547
|
+
const compile = spawnSync7(
|
|
8494
8548
|
OSACOMPILE,
|
|
8495
8549
|
["-o", bundlePath, scriptPath],
|
|
8496
8550
|
{ stdio: "pipe", encoding: "utf-8" }
|
|
@@ -8534,7 +8588,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8534
8588
|
["Add :CFBundleURLTypes:0:CFBundleURLSchemes array", null],
|
|
8535
8589
|
[`Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string ${URL_SCHEME}`, null]
|
|
8536
8590
|
]);
|
|
8537
|
-
const sign =
|
|
8591
|
+
const sign = spawnSync7(
|
|
8538
8592
|
"/usr/bin/codesign",
|
|
8539
8593
|
["--force", "--deep", "--sign", "-", bundlePath],
|
|
8540
8594
|
{ stdio: "pipe", encoding: "utf-8" }
|
|
@@ -8546,7 +8600,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8546
8600
|
}
|
|
8547
8601
|
console.warn(`syntaur: ${msg} \u2014 macOS may deny Automation permission.`);
|
|
8548
8602
|
}
|
|
8549
|
-
const ls =
|
|
8603
|
+
const ls = spawnSync7(LSREGISTER, ["-f", bundlePath], {
|
|
8550
8604
|
stdio: "pipe",
|
|
8551
8605
|
encoding: "utf-8"
|
|
8552
8606
|
});
|
|
@@ -8595,9 +8649,9 @@ function detectInstalledTerminals() {
|
|
|
8595
8649
|
ghostty: "Ghostty.app",
|
|
8596
8650
|
warp: "Warp.app"
|
|
8597
8651
|
};
|
|
8598
|
-
const appDirs = ["/Applications", join9(
|
|
8652
|
+
const appDirs = ["/Applications", join9(homedir9(), "Applications")];
|
|
8599
8653
|
for (const [id, bundleId] of Object.entries(bundleIds)) {
|
|
8600
|
-
const r =
|
|
8654
|
+
const r = spawnSync7(
|
|
8601
8655
|
"mdfind",
|
|
8602
8656
|
[`kMDItemCFBundleIdentifier == '${bundleId}'`],
|
|
8603
8657
|
{ encoding: "utf-8" }
|
|
@@ -8757,13 +8811,13 @@ function runPlistBuddy(plistPath, steps) {
|
|
|
8757
8811
|
for (const step of steps) {
|
|
8758
8812
|
if (Array.isArray(step)) {
|
|
8759
8813
|
const [primary, fallback] = step;
|
|
8760
|
-
const a =
|
|
8814
|
+
const a = spawnSync7("/usr/libexec/PlistBuddy", ["-c", primary, plistPath], {
|
|
8761
8815
|
stdio: "pipe",
|
|
8762
8816
|
encoding: "utf-8"
|
|
8763
8817
|
});
|
|
8764
8818
|
if (a.status === 0) continue;
|
|
8765
8819
|
if (fallback === null) continue;
|
|
8766
|
-
const b =
|
|
8820
|
+
const b = spawnSync7("/usr/libexec/PlistBuddy", ["-c", fallback, plistPath], {
|
|
8767
8821
|
stdio: "pipe",
|
|
8768
8822
|
encoding: "utf-8"
|
|
8769
8823
|
});
|
|
@@ -8773,7 +8827,7 @@ function runPlistBuddy(plistPath, steps) {
|
|
|
8773
8827
|
);
|
|
8774
8828
|
}
|
|
8775
8829
|
} else {
|
|
8776
|
-
const r =
|
|
8830
|
+
const r = spawnSync7("/usr/libexec/PlistBuddy", ["-c", step, plistPath], {
|
|
8777
8831
|
stdio: "pipe",
|
|
8778
8832
|
encoding: "utf-8"
|
|
8779
8833
|
});
|
|
@@ -9317,7 +9371,7 @@ init_fs();
|
|
|
9317
9371
|
init_config();
|
|
9318
9372
|
init_playbooks();
|
|
9319
9373
|
import { resolve as resolve5, dirname as dirname2 } from "path";
|
|
9320
|
-
import { readdir as readdir3, readFile as
|
|
9374
|
+
import { readdir as readdir3, readFile as readFile5 } from "fs/promises";
|
|
9321
9375
|
import { fileURLToPath } from "url";
|
|
9322
9376
|
async function initCommand(options) {
|
|
9323
9377
|
const root = syntaurRoot();
|
|
@@ -9369,7 +9423,7 @@ async function seedDefaultPlaybooks(playbooksDir3) {
|
|
|
9369
9423
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
9370
9424
|
const targetPath = resolve5(playbooksDir3, entry.name);
|
|
9371
9425
|
if (await fileExists(targetPath)) continue;
|
|
9372
|
-
const content = await
|
|
9426
|
+
const content = await readFile5(resolve5(examplesDir, entry.name), "utf-8");
|
|
9373
9427
|
await writeFileSafe(targetPath, content);
|
|
9374
9428
|
count++;
|
|
9375
9429
|
}
|
|
@@ -9931,7 +9985,7 @@ function mergePatch(current, patch) {
|
|
|
9931
9985
|
// src/utils/view-prefs.ts
|
|
9932
9986
|
init_paths();
|
|
9933
9987
|
init_fs();
|
|
9934
|
-
import { readFile as
|
|
9988
|
+
import { readFile as readFile12, rename as rename3, unlink as unlink3 } from "fs/promises";
|
|
9935
9989
|
import { resolve as resolve17 } from "path";
|
|
9936
9990
|
function corruptFilePath() {
|
|
9937
9991
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -9954,7 +10008,7 @@ async function readViewPrefsFile() {
|
|
|
9954
10008
|
}
|
|
9955
10009
|
let raw;
|
|
9956
10010
|
try {
|
|
9957
|
-
raw = await
|
|
10011
|
+
raw = await readFile12(path, "utf-8");
|
|
9958
10012
|
} catch {
|
|
9959
10013
|
return { ...DEFAULT_VIEW_PREFS_FILE };
|
|
9960
10014
|
}
|
|
@@ -10137,7 +10191,7 @@ function isDashboardSlot(value) {
|
|
|
10137
10191
|
// src/utils/saved-views.ts
|
|
10138
10192
|
init_paths();
|
|
10139
10193
|
init_fs();
|
|
10140
|
-
import { readFile as
|
|
10194
|
+
import { readFile as readFile13, rename as rename4, unlink as unlink4 } from "fs/promises";
|
|
10141
10195
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
10142
10196
|
import { resolve as resolve18 } from "path";
|
|
10143
10197
|
function corruptFilePath2() {
|
|
@@ -10167,7 +10221,7 @@ async function readSavedViewsFile() {
|
|
|
10167
10221
|
}
|
|
10168
10222
|
let raw;
|
|
10169
10223
|
try {
|
|
10170
|
-
raw = await
|
|
10224
|
+
raw = await readFile13(path, "utf-8");
|
|
10171
10225
|
} catch {
|
|
10172
10226
|
return cloneDefault();
|
|
10173
10227
|
}
|
|
@@ -10487,7 +10541,7 @@ init_fs();
|
|
|
10487
10541
|
init_git_worktree();
|
|
10488
10542
|
import { Router as Router2 } from "express";
|
|
10489
10543
|
import { resolve as resolve21, basename as basename3, isAbsolute as isAbsolute2 } from "path";
|
|
10490
|
-
import { rm, readFile as
|
|
10544
|
+
import { rm, readFile as readFile16, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
|
|
10491
10545
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10492
10546
|
|
|
10493
10547
|
// src/utils/worktree-defaults.ts
|
|
@@ -10563,7 +10617,7 @@ function validateBranchName(name) {
|
|
|
10563
10617
|
// src/dashboard/repository-candidates.ts
|
|
10564
10618
|
init_fs();
|
|
10565
10619
|
init_parser();
|
|
10566
|
-
import { readdir as readdir9, readFile as
|
|
10620
|
+
import { readdir as readdir9, readFile as readFile15 } from "fs/promises";
|
|
10567
10621
|
import { resolve as resolve20 } from "path";
|
|
10568
10622
|
function toSourceAssignment(parsed, fallbackId) {
|
|
10569
10623
|
const repository = parsed.workspace.repository?.trim();
|
|
@@ -10583,7 +10637,7 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
|
|
|
10583
10637
|
const out = [];
|
|
10584
10638
|
const projectPath = resolve20(projectsDir2, projectSlug, "project.md");
|
|
10585
10639
|
if (await fileExists(projectPath)) {
|
|
10586
|
-
const project = parseProject(await
|
|
10640
|
+
const project = parseProject(await readFile15(projectPath, "utf-8"));
|
|
10587
10641
|
for (const raw of project.repositories) {
|
|
10588
10642
|
const path = raw.trim();
|
|
10589
10643
|
if (!path) continue;
|
|
@@ -10600,7 +10654,7 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
|
|
|
10600
10654
|
if (!entry.isDirectory()) continue;
|
|
10601
10655
|
const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
|
|
10602
10656
|
if (!await fileExists(assignmentMd)) continue;
|
|
10603
|
-
const parsed = parseAssignmentFull(await
|
|
10657
|
+
const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
|
|
10604
10658
|
const repo = parsed.workspace.repository?.trim();
|
|
10605
10659
|
if (!repo) continue;
|
|
10606
10660
|
const abs = resolve20(repo);
|
|
@@ -10623,7 +10677,7 @@ async function getStandaloneRepositoryCandidates(assignmentsDir2, excludeAssignm
|
|
|
10623
10677
|
if (entry.name === excludeAssignmentId) continue;
|
|
10624
10678
|
const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
|
|
10625
10679
|
if (!await fileExists(assignmentMd)) continue;
|
|
10626
|
-
const parsed = parseAssignmentFull(await
|
|
10680
|
+
const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
|
|
10627
10681
|
const repo = parsed.workspace.repository?.trim();
|
|
10628
10682
|
if (!repo) continue;
|
|
10629
10683
|
const abs = resolve20(repo);
|
|
@@ -10644,7 +10698,7 @@ async function getProjectSourceAssignments(projectsDir2, projectSlug, excludeSlu
|
|
|
10644
10698
|
if (entry.name === excludeSlug) continue;
|
|
10645
10699
|
const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
|
|
10646
10700
|
if (!await fileExists(assignmentMd)) continue;
|
|
10647
|
-
const parsed = parseAssignmentFull(await
|
|
10701
|
+
const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
|
|
10648
10702
|
const source = toSourceAssignment(parsed, entry.name);
|
|
10649
10703
|
if (!source) continue;
|
|
10650
10704
|
if (seen.has(entry.name)) continue;
|
|
@@ -10663,7 +10717,7 @@ async function getStandaloneSourceAssignments(assignmentsDir2, excludeAssignment
|
|
|
10663
10717
|
if (entry.name === excludeAssignmentId) continue;
|
|
10664
10718
|
const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
|
|
10665
10719
|
if (!await fileExists(assignmentMd)) continue;
|
|
10666
|
-
const parsed = parseAssignmentFull(await
|
|
10720
|
+
const parsed = parseAssignmentFull(await readFile15(assignmentMd, "utf-8"));
|
|
10667
10721
|
const source = toSourceAssignment(parsed, entry.name);
|
|
10668
10722
|
if (!source) continue;
|
|
10669
10723
|
if (seen.has(entry.name)) continue;
|
|
@@ -10834,7 +10888,7 @@ async function readCurrentDocument(filePath) {
|
|
|
10834
10888
|
if (!await fileExists(filePath)) {
|
|
10835
10889
|
return null;
|
|
10836
10890
|
}
|
|
10837
|
-
return
|
|
10891
|
+
return readFile16(filePath, "utf-8");
|
|
10838
10892
|
}
|
|
10839
10893
|
var worktreeInFlight = /* @__PURE__ */ new Set();
|
|
10840
10894
|
async function assertRepoRoot(repoInput) {
|
|
@@ -10888,7 +10942,7 @@ async function handleWorktreeCreate(req, res, ctx) {
|
|
|
10888
10942
|
}
|
|
10889
10943
|
worktreeInFlight.add(ctx.assignmentPath);
|
|
10890
10944
|
try {
|
|
10891
|
-
const parsed = parseAssignmentFull(await
|
|
10945
|
+
const parsed = parseAssignmentFull(await readFile16(ctx.assignmentPath, "utf-8"));
|
|
10892
10946
|
if (parsed.workspace.worktreePath) {
|
|
10893
10947
|
res.status(409).json({ error: "Worktree already configured for this assignment" });
|
|
10894
10948
|
return;
|
|
@@ -11258,7 +11312,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
11258
11312
|
}
|
|
11259
11313
|
const nextContentRaw = requireContent(req, res);
|
|
11260
11314
|
if (!nextContentRaw) return;
|
|
11261
|
-
const currentContent = await
|
|
11315
|
+
const currentContent = await readFile16(filePath, "utf-8");
|
|
11262
11316
|
const frontmatterBlock = extractFrontmatterBlock(currentContent);
|
|
11263
11317
|
if (!frontmatterBlock) {
|
|
11264
11318
|
res.status(500).json({ error: `${kind} file is malformed (no frontmatter)` });
|
|
@@ -11709,7 +11763,7 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
|
|
|
11709
11763
|
let currentContent;
|
|
11710
11764
|
let currentCount = 0;
|
|
11711
11765
|
if (await fileExists(commentsPath)) {
|
|
11712
|
-
currentContent = await
|
|
11766
|
+
currentContent = await readFile16(commentsPath, "utf-8");
|
|
11713
11767
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
11714
11768
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
11715
11769
|
} else {
|
|
@@ -11766,7 +11820,7 @@ ${entry}`;
|
|
|
11766
11820
|
res.status(400).json({ error: "resolved (boolean) is required" });
|
|
11767
11821
|
return;
|
|
11768
11822
|
}
|
|
11769
|
-
const content = await
|
|
11823
|
+
const content = await readFile16(commentsPath, "utf-8");
|
|
11770
11824
|
const parsed = parseComments(content);
|
|
11771
11825
|
const target = parsed.entries.find((e) => e.id === commentId);
|
|
11772
11826
|
if (!target) {
|
|
@@ -11813,7 +11867,7 @@ ${entry}`;
|
|
|
11813
11867
|
});
|
|
11814
11868
|
return;
|
|
11815
11869
|
}
|
|
11816
|
-
let content = await
|
|
11870
|
+
let content = await readFile16(projectPath, "utf-8");
|
|
11817
11871
|
content = setTopLevelField(content, "workspace", workspace ?? null);
|
|
11818
11872
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11819
11873
|
await writeFileForce(projectPath, content);
|
|
@@ -11850,7 +11904,7 @@ ${entry}`;
|
|
|
11850
11904
|
return;
|
|
11851
11905
|
}
|
|
11852
11906
|
const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
|
|
11853
|
-
let content = await
|
|
11907
|
+
let content = await readFile16(assignmentPath, "utf-8");
|
|
11854
11908
|
content = setTopLevelField(content, "workspaceGroup", workspaceGroup ?? null);
|
|
11855
11909
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11856
11910
|
await writeFileForce(assignmentPath, content);
|
|
@@ -12067,7 +12121,7 @@ ${entry}`;
|
|
|
12067
12121
|
return;
|
|
12068
12122
|
}
|
|
12069
12123
|
const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
|
|
12070
|
-
const parsedForSlug = parseAssignmentFull(await
|
|
12124
|
+
const parsedForSlug = parseAssignmentFull(await readFile16(assignmentPath, "utf-8"));
|
|
12071
12125
|
const assignmentSlugForBranch = parsedForSlug.slug || resolved.id;
|
|
12072
12126
|
await handleWorktreeCreate(req, res, {
|
|
12073
12127
|
assignmentPath,
|
|
@@ -12096,7 +12150,7 @@ ${entry}`;
|
|
|
12096
12150
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
|
|
12097
12151
|
return;
|
|
12098
12152
|
}
|
|
12099
|
-
let content = await
|
|
12153
|
+
let content = await readFile16(projectPath, "utf-8");
|
|
12100
12154
|
content = setTopLevelField(content, "statusOverride", status ?? null);
|
|
12101
12155
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12102
12156
|
await writeFileForce(projectPath, content);
|
|
@@ -12129,7 +12183,7 @@ ${entry}`;
|
|
|
12129
12183
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
|
|
12130
12184
|
return;
|
|
12131
12185
|
}
|
|
12132
|
-
let content = await
|
|
12186
|
+
let content = await readFile16(assignmentPath, "utf-8");
|
|
12133
12187
|
content = setTopLevelField(content, "status", status);
|
|
12134
12188
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12135
12189
|
if (status !== "blocked") {
|
|
@@ -12155,7 +12209,7 @@ ${entry}`;
|
|
|
12155
12209
|
res.status(404).json({ error: `Project "${projectSlug}" not found` });
|
|
12156
12210
|
return;
|
|
12157
12211
|
}
|
|
12158
|
-
const content = await
|
|
12212
|
+
const content = await readFile16(projectPath, "utf-8");
|
|
12159
12213
|
await writeFileForce(projectPath, applyArchiveFields(content, true, archiveReason(req.body)));
|
|
12160
12214
|
const project = await getProjectDetail(projectsDir2, projectSlug);
|
|
12161
12215
|
res.json({ project });
|
|
@@ -12172,7 +12226,7 @@ ${entry}`;
|
|
|
12172
12226
|
res.status(404).json({ error: `Project "${projectSlug}" not found` });
|
|
12173
12227
|
return;
|
|
12174
12228
|
}
|
|
12175
|
-
const content = await
|
|
12229
|
+
const content = await readFile16(projectPath, "utf-8");
|
|
12176
12230
|
await writeFileForce(projectPath, applyArchiveFields(content, false, null));
|
|
12177
12231
|
const project = await getProjectDetail(projectsDir2, projectSlug);
|
|
12178
12232
|
res.json({ project });
|
|
@@ -12189,7 +12243,7 @@ ${entry}`;
|
|
|
12189
12243
|
res.status(404).json({ error: "Assignment not found" });
|
|
12190
12244
|
return;
|
|
12191
12245
|
}
|
|
12192
|
-
const content = await
|
|
12246
|
+
const content = await readFile16(assignmentPath, "utf-8");
|
|
12193
12247
|
await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
|
|
12194
12248
|
const assignment = await getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug);
|
|
12195
12249
|
res.json({ assignment });
|
|
@@ -12222,7 +12276,7 @@ ${entry}`;
|
|
|
12222
12276
|
return;
|
|
12223
12277
|
}
|
|
12224
12278
|
const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
|
|
12225
|
-
const content = await
|
|
12279
|
+
const content = await readFile16(assignmentPath, "utf-8");
|
|
12226
12280
|
await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
|
|
12227
12281
|
const assignment = await getAssignmentDetailById(projectsDir2, assignmentsDir2, id);
|
|
12228
12282
|
res.json({ assignment });
|
|
@@ -12263,7 +12317,7 @@ ${entry}`;
|
|
|
12263
12317
|
res.status(400).json({ error: validation.error });
|
|
12264
12318
|
return;
|
|
12265
12319
|
}
|
|
12266
|
-
let content = await
|
|
12320
|
+
let content = await readFile16(assignmentPath, "utf-8");
|
|
12267
12321
|
content = setTopLevelField(content, "assignee", validation.value);
|
|
12268
12322
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12269
12323
|
await writeFileForce(assignmentPath, content);
|
|
@@ -12294,7 +12348,7 @@ ${entry}`;
|
|
|
12294
12348
|
res.status(400).json({ error: validation.error });
|
|
12295
12349
|
return;
|
|
12296
12350
|
}
|
|
12297
|
-
let content = await
|
|
12351
|
+
let content = await readFile16(assignmentPath, "utf-8");
|
|
12298
12352
|
content = setTopLevelField(content, "title", validation.value);
|
|
12299
12353
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12300
12354
|
await writeFileForce(assignmentPath, content);
|
|
@@ -12812,7 +12866,7 @@ ${entry}`;
|
|
|
12812
12866
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
|
|
12813
12867
|
return;
|
|
12814
12868
|
}
|
|
12815
|
-
let content = await
|
|
12869
|
+
let content = await readFile16(assignmentPath, "utf-8");
|
|
12816
12870
|
content = setTopLevelField(content, "status", status);
|
|
12817
12871
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12818
12872
|
if (status !== "blocked") {
|
|
@@ -12848,7 +12902,7 @@ ${entry}`;
|
|
|
12848
12902
|
res.status(400).json({ error: validation.error });
|
|
12849
12903
|
return;
|
|
12850
12904
|
}
|
|
12851
|
-
let content = await
|
|
12905
|
+
let content = await readFile16(assignmentPath, "utf-8");
|
|
12852
12906
|
content = setTopLevelField(content, "assignee", validation.value);
|
|
12853
12907
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12854
12908
|
await writeFileForce(assignmentPath, content);
|
|
@@ -12881,7 +12935,7 @@ ${entry}`;
|
|
|
12881
12935
|
res.status(400).json({ error: validation.error });
|
|
12882
12936
|
return;
|
|
12883
12937
|
}
|
|
12884
|
-
let content = await
|
|
12938
|
+
let content = await readFile16(assignmentPath, "utf-8");
|
|
12885
12939
|
content = setTopLevelField(content, "title", validation.value);
|
|
12886
12940
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12887
12941
|
await writeFileForce(assignmentPath, content);
|
|
@@ -13018,7 +13072,7 @@ async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDet
|
|
|
13018
13072
|
let currentContent;
|
|
13019
13073
|
let currentCount = 0;
|
|
13020
13074
|
if (await fileExists(commentsPath)) {
|
|
13021
|
-
currentContent = await
|
|
13075
|
+
currentContent = await readFile16(commentsPath, "utf-8");
|
|
13022
13076
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
13023
13077
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
13024
13078
|
} else {
|
|
@@ -13058,7 +13112,7 @@ async function toggleCommentResolvedAt(assignmentDir, commentId, req, res, reloa
|
|
|
13058
13112
|
res.status(400).json({ error: "resolved (boolean) is required" });
|
|
13059
13113
|
return;
|
|
13060
13114
|
}
|
|
13061
|
-
const content = await
|
|
13115
|
+
const content = await readFile16(commentsPath, "utf-8");
|
|
13062
13116
|
const parsed = parseComments(content);
|
|
13063
13117
|
const target = parsed.entries.find((e) => e.id === commentId);
|
|
13064
13118
|
if (!target) {
|
|
@@ -13844,7 +13898,7 @@ import { Router as Router8 } from "express";
|
|
|
13844
13898
|
|
|
13845
13899
|
// src/utils/status-config-resolution.ts
|
|
13846
13900
|
init_frontmatter();
|
|
13847
|
-
import { readFile as
|
|
13901
|
+
import { readFile as readFile17, writeFile as writeFile4, rm as rm2 } from "fs/promises";
|
|
13848
13902
|
import { dirname as dirname5 } from "path";
|
|
13849
13903
|
|
|
13850
13904
|
// src/utils/assignment-walk.ts
|
|
@@ -13927,7 +13981,7 @@ async function scanAssignmentsByStatus(projectsDir2, standaloneDir, ids) {
|
|
|
13927
13981
|
const assignmentPath = `${entry.assignmentDir}/assignment.md`;
|
|
13928
13982
|
let content;
|
|
13929
13983
|
try {
|
|
13930
|
-
content = await
|
|
13984
|
+
content = await readFile17(assignmentPath, "utf-8");
|
|
13931
13985
|
} catch (err2) {
|
|
13932
13986
|
const code = err2?.code;
|
|
13933
13987
|
if (code === "ENOENT") {
|
|
@@ -13990,7 +14044,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
13990
14044
|
const list = affected.get(r.id) ?? [];
|
|
13991
14045
|
for (const a of list) {
|
|
13992
14046
|
try {
|
|
13993
|
-
const content = await
|
|
14047
|
+
const content = await readFile17(a.path, "utf-8");
|
|
13994
14048
|
buffer.set(a.path, content);
|
|
13995
14049
|
} catch (err2) {
|
|
13996
14050
|
const code = err2?.code;
|
|
@@ -14018,7 +14072,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
14018
14072
|
for (const a of list) {
|
|
14019
14073
|
let current;
|
|
14020
14074
|
try {
|
|
14021
|
-
current = await
|
|
14075
|
+
current = await readFile17(a.path, "utf-8");
|
|
14022
14076
|
} catch (err2) {
|
|
14023
14077
|
const code = err2?.code;
|
|
14024
14078
|
if (code === "ENOENT") {
|
|
@@ -14067,7 +14121,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
14067
14121
|
const list = affected.get(r.id) ?? [];
|
|
14068
14122
|
for (const a of list) {
|
|
14069
14123
|
try {
|
|
14070
|
-
const current = await
|
|
14124
|
+
const current = await readFile17(a.path, "utf-8");
|
|
14071
14125
|
const fm = parseAssignmentFrontmatter(current);
|
|
14072
14126
|
if (fm.status !== r.id) {
|
|
14073
14127
|
console.warn(
|
|
@@ -15456,7 +15510,7 @@ init_playbook();
|
|
|
15456
15510
|
init_playbooks();
|
|
15457
15511
|
import { Router as Router11 } from "express";
|
|
15458
15512
|
import { resolve as resolve26 } from "path";
|
|
15459
|
-
import { readFile as
|
|
15513
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
15460
15514
|
function statusForPlaybookError(code) {
|
|
15461
15515
|
switch (code) {
|
|
15462
15516
|
case "manifest":
|
|
@@ -15538,7 +15592,7 @@ function createPlaybooksRouter(playbooksDir3) {
|
|
|
15538
15592
|
return;
|
|
15539
15593
|
}
|
|
15540
15594
|
const filePath = resolve26(playbooksDir3, resolved.filename);
|
|
15541
|
-
const content = await
|
|
15595
|
+
const content = await readFile18(filePath, "utf-8");
|
|
15542
15596
|
res.json({
|
|
15543
15597
|
documentType: "playbook",
|
|
15544
15598
|
title: `Edit Playbook: ${resolved.slug}`,
|
|
@@ -15889,8 +15943,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
15889
15943
|
router.post("/:workspace/archive", async (req, res) => {
|
|
15890
15944
|
try {
|
|
15891
15945
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
15892
|
-
const { resolve:
|
|
15893
|
-
const { readFile:
|
|
15946
|
+
const { resolve: resolve82 } = await import("path");
|
|
15947
|
+
const { readFile: readFile56 } = await import("fs/promises");
|
|
15894
15948
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15895
15949
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
15896
15950
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
@@ -15906,10 +15960,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
15906
15960
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
15907
15961
|
);
|
|
15908
15962
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
15909
|
-
await ensureDir(
|
|
15963
|
+
await ensureDir(resolve82(todosDir2, "archive"));
|
|
15910
15964
|
let archContent = "";
|
|
15911
15965
|
if (await fileExists(archFile)) {
|
|
15912
|
-
archContent = await
|
|
15966
|
+
archContent = await readFile56(archFile, "utf-8");
|
|
15913
15967
|
archContent = archContent.trimEnd() + "\n\n";
|
|
15914
15968
|
} else {
|
|
15915
15969
|
archContent = `---
|
|
@@ -16191,7 +16245,7 @@ workspace: ${workspace}
|
|
|
16191
16245
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
16192
16246
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
16193
16247
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
16194
|
-
const { readFile:
|
|
16248
|
+
const { readFile: readFile56 } = await import("fs/promises");
|
|
16195
16249
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
16196
16250
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
16197
16251
|
let assignmentRef;
|
|
@@ -16212,7 +16266,7 @@ workspace: ${workspace}
|
|
|
16212
16266
|
}
|
|
16213
16267
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
16214
16268
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
16215
|
-
let content = await
|
|
16269
|
+
let content = await readFile56(assignmentMdPath2, "utf-8");
|
|
16216
16270
|
content = appendTodosToAssignmentBody2(
|
|
16217
16271
|
content,
|
|
16218
16272
|
items.map((it) => ({
|
|
@@ -16400,7 +16454,7 @@ init_fs();
|
|
|
16400
16454
|
init_paths();
|
|
16401
16455
|
init_slug();
|
|
16402
16456
|
import { Router as Router13 } from "express";
|
|
16403
|
-
import { mkdir as mkdir3, readFile as
|
|
16457
|
+
import { mkdir as mkdir3, readFile as readFile19, rename as rename6 } from "fs/promises";
|
|
16404
16458
|
import { resolve as resolve27, dirname as dirname7 } from "path";
|
|
16405
16459
|
init_promote_todos();
|
|
16406
16460
|
init_api();
|
|
@@ -16625,7 +16679,7 @@ function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
|
|
|
16625
16679
|
const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
|
|
16626
16680
|
let archContent = "";
|
|
16627
16681
|
if (await fileExists(archFile)) {
|
|
16628
|
-
archContent = await
|
|
16682
|
+
archContent = await readFile19(archFile, "utf-8");
|
|
16629
16683
|
archContent = archContent.trimEnd() + "\n\n";
|
|
16630
16684
|
} else {
|
|
16631
16685
|
archContent = `---
|
|
@@ -17082,7 +17136,7 @@ workspace: ${slug}
|
|
|
17082
17136
|
}
|
|
17083
17137
|
const assignmentMdPath2 = resolve27(assignmentDir, "assignment.md");
|
|
17084
17138
|
if (!await fileExists(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
17085
|
-
let content = await
|
|
17139
|
+
let content = await readFile19(assignmentMdPath2, "utf-8");
|
|
17086
17140
|
content = appendTodosToAssignmentBody2(
|
|
17087
17141
|
content,
|
|
17088
17142
|
items.map((it) => ({
|
|
@@ -17286,7 +17340,7 @@ init_fs();
|
|
|
17286
17340
|
init_paths();
|
|
17287
17341
|
init_parser2();
|
|
17288
17342
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
17289
|
-
import { readFile as
|
|
17343
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
17290
17344
|
var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
|
|
17291
17345
|
var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
|
|
17292
17346
|
var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
|
|
@@ -17389,7 +17443,7 @@ ${lines}
|
|
|
17389
17443
|
async function readBundles(todosDir2) {
|
|
17390
17444
|
const path = bundlesPath(todosDir2);
|
|
17391
17445
|
if (!await fileExists(path)) return [];
|
|
17392
|
-
const content = await
|
|
17446
|
+
const content = await readFile20(path, "utf-8");
|
|
17393
17447
|
return parseBundles(content).bundles;
|
|
17394
17448
|
}
|
|
17395
17449
|
async function writeBundles(todosDir2, bundles) {
|
|
@@ -17566,7 +17620,7 @@ init_fs();
|
|
|
17566
17620
|
init_config2();
|
|
17567
17621
|
import { execFile as execFile2 } from "child_process";
|
|
17568
17622
|
import { promisify as promisify2 } from "util";
|
|
17569
|
-
import { cp, mkdtemp, rm as rm3, readFile as
|
|
17623
|
+
import { cp, mkdtemp, rm as rm3, readFile as readFile21, writeFile as writeFile5, unlink as unlink5, stat, open as open2, rename as rename7 } from "fs/promises";
|
|
17570
17624
|
import { resolve as resolve29, join as join3 } from "path";
|
|
17571
17625
|
import { tmpdir } from "os";
|
|
17572
17626
|
var exec2 = promisify2(execFile2);
|
|
@@ -17627,7 +17681,7 @@ async function acquireLock() {
|
|
|
17627
17681
|
return lockPath;
|
|
17628
17682
|
} catch (err2) {
|
|
17629
17683
|
if (err2.code === "EEXIST") {
|
|
17630
|
-
const pid = await
|
|
17684
|
+
const pid = await readFile21(lockPath, "utf-8").catch(() => "");
|
|
17631
17685
|
throw new Error(
|
|
17632
17686
|
`Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
|
|
17633
17687
|
);
|
|
@@ -17674,7 +17728,7 @@ function resolveCategoriesStrict(csv) {
|
|
|
17674
17728
|
return parseCategoriesStrict(parts);
|
|
17675
17729
|
}
|
|
17676
17730
|
async function readSanitizedConfig(configPath) {
|
|
17677
|
-
const content = await
|
|
17731
|
+
const content = await readFile21(configPath, "utf-8");
|
|
17678
17732
|
return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
|
|
17679
17733
|
}
|
|
17680
17734
|
async function backupToGithub(overrides) {
|
|
@@ -19066,7 +19120,7 @@ init_frontmatter();
|
|
|
19066
19120
|
init_timestamp();
|
|
19067
19121
|
init_assignment_resolver();
|
|
19068
19122
|
import { resolve as resolve33 } from "path";
|
|
19069
|
-
import { readFile as
|
|
19123
|
+
import { readFile as readFile22, writeFile as writeFile7 } from "fs/promises";
|
|
19070
19124
|
async function resolveTarget(target, options) {
|
|
19071
19125
|
const config = await readConfig();
|
|
19072
19126
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
@@ -19098,7 +19152,7 @@ async function resolveTarget(target, options) {
|
|
|
19098
19152
|
return null;
|
|
19099
19153
|
}
|
|
19100
19154
|
async function writeArchiveState(filePath, archived, reason) {
|
|
19101
|
-
const content = await
|
|
19155
|
+
const content = await readFile22(filePath, "utf-8");
|
|
19102
19156
|
const updated = updateAssignmentFile(content, {
|
|
19103
19157
|
archived,
|
|
19104
19158
|
archivedAt: archived ? nowTimestamp() : null,
|
|
@@ -19167,7 +19221,7 @@ init_config2();
|
|
|
19167
19221
|
init_frontmatter();
|
|
19168
19222
|
init_timestamp();
|
|
19169
19223
|
import { resolve as resolve34 } from "path";
|
|
19170
|
-
import { readdir as readdir13, readFile as
|
|
19224
|
+
import { readdir as readdir13, readFile as readFile23 } from "fs/promises";
|
|
19171
19225
|
var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
19172
19226
|
function objectiveIsFleshedOut(content) {
|
|
19173
19227
|
const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
|
|
@@ -19193,7 +19247,7 @@ async function collectCandidates(baseDirs) {
|
|
|
19193
19247
|
if (await fileExists(directAssignmentMd)) {
|
|
19194
19248
|
const fm = await parseSafe(directAssignmentMd);
|
|
19195
19249
|
if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
|
|
19196
|
-
const content = await
|
|
19250
|
+
const content = await readFile23(directAssignmentMd, "utf-8");
|
|
19197
19251
|
if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
|
|
19198
19252
|
candidates.push({
|
|
19199
19253
|
projectSlug: null,
|
|
@@ -19216,7 +19270,7 @@ async function collectCandidates(baseDirs) {
|
|
|
19216
19270
|
if (!await fileExists(assignmentMd)) continue;
|
|
19217
19271
|
const fm = await parseSafe(assignmentMd);
|
|
19218
19272
|
if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
|
|
19219
|
-
const content = await
|
|
19273
|
+
const content = await readFile23(assignmentMd, "utf-8");
|
|
19220
19274
|
if (!objectiveIsFleshedOut(content)) continue;
|
|
19221
19275
|
if (!hasAcceptanceCriteria(content)) continue;
|
|
19222
19276
|
candidates.push({
|
|
@@ -19233,7 +19287,7 @@ async function collectCandidates(baseDirs) {
|
|
|
19233
19287
|
}
|
|
19234
19288
|
async function parseSafe(path) {
|
|
19235
19289
|
try {
|
|
19236
|
-
const content = await
|
|
19290
|
+
const content = await readFile23(path, "utf-8");
|
|
19237
19291
|
return parseAssignmentFrontmatter(content);
|
|
19238
19292
|
} catch {
|
|
19239
19293
|
return null;
|
|
@@ -19262,7 +19316,7 @@ async function migrateStatusesCommand(options) {
|
|
|
19262
19316
|
const now = nowTimestamp();
|
|
19263
19317
|
let migrated = 0;
|
|
19264
19318
|
for (const c2 of candidates) {
|
|
19265
|
-
const content = await
|
|
19319
|
+
const content = await readFile23(c2.assignmentMd, "utf-8");
|
|
19266
19320
|
const updated = updateAssignmentFile(content, {
|
|
19267
19321
|
status: c2.toStatus,
|
|
19268
19322
|
updated: now
|
|
@@ -19320,7 +19374,7 @@ import {
|
|
|
19320
19374
|
readdir as readdir14,
|
|
19321
19375
|
symlink,
|
|
19322
19376
|
lstat,
|
|
19323
|
-
readFile as
|
|
19377
|
+
readFile as readFile24,
|
|
19324
19378
|
readlink,
|
|
19325
19379
|
rename as rename8,
|
|
19326
19380
|
rm as rm4,
|
|
@@ -19362,8 +19416,8 @@ function getPluginManifestRelativePath(pluginKind) {
|
|
|
19362
19416
|
return pluginKind === "claude" ? ".claude-plugin/plugin.json" : ".codex-plugin/plugin.json";
|
|
19363
19417
|
}
|
|
19364
19418
|
function getDefaultPluginTargetDir(pluginKind) {
|
|
19365
|
-
const
|
|
19366
|
-
return pluginKind === "claude" ? resolve36(
|
|
19419
|
+
const home2 = homedir3();
|
|
19420
|
+
return pluginKind === "claude" ? resolve36(home2, ".claude", "plugins", "syntaur") : resolve36(home2, "plugins", "syntaur");
|
|
19367
19421
|
}
|
|
19368
19422
|
function getDefaultMarketplacePath() {
|
|
19369
19423
|
return resolve36(homedir3(), ".agents", "plugins", "marketplace.json");
|
|
@@ -19381,7 +19435,7 @@ function getInstallMarkerPath(targetDir) {
|
|
|
19381
19435
|
return resolve36(targetDir, INSTALL_MARKER_FILENAME);
|
|
19382
19436
|
}
|
|
19383
19437
|
async function readPackageManifest(packageRoot) {
|
|
19384
|
-
const raw = await
|
|
19438
|
+
const raw = await readFile24(resolve36(packageRoot, "package.json"), "utf-8");
|
|
19385
19439
|
return JSON.parse(raw);
|
|
19386
19440
|
}
|
|
19387
19441
|
async function readJsonFileIfExists(pathValue) {
|
|
@@ -19389,7 +19443,7 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
19389
19443
|
return null;
|
|
19390
19444
|
}
|
|
19391
19445
|
try {
|
|
19392
|
-
const raw = await
|
|
19446
|
+
const raw = await readFile24(pathValue, "utf-8");
|
|
19393
19447
|
return JSON.parse(raw);
|
|
19394
19448
|
} catch {
|
|
19395
19449
|
return null;
|
|
@@ -19405,7 +19459,7 @@ async function readPluginManifestName(targetDir, pluginKind) {
|
|
|
19405
19459
|
if (!await fileExists(manifestPath)) {
|
|
19406
19460
|
return void 0;
|
|
19407
19461
|
}
|
|
19408
|
-
const raw = await
|
|
19462
|
+
const raw = await readFile24(manifestPath, "utf-8");
|
|
19409
19463
|
const parsed = JSON.parse(raw);
|
|
19410
19464
|
return parsed.name;
|
|
19411
19465
|
}
|
|
@@ -19415,7 +19469,7 @@ async function readInstallMetadata(targetDir) {
|
|
|
19415
19469
|
return null;
|
|
19416
19470
|
}
|
|
19417
19471
|
try {
|
|
19418
|
-
const raw = await
|
|
19472
|
+
const raw = await readFile24(markerPath, "utf-8");
|
|
19419
19473
|
return JSON.parse(raw);
|
|
19420
19474
|
} catch {
|
|
19421
19475
|
return null;
|
|
@@ -19538,7 +19592,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
|
19538
19592
|
}
|
|
19539
19593
|
if (await fileExists(manifestPath)) {
|
|
19540
19594
|
try {
|
|
19541
|
-
const prev = await
|
|
19595
|
+
const prev = await readFile24(manifestPath, "utf-8");
|
|
19542
19596
|
JSON.parse(prev);
|
|
19543
19597
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19544
19598
|
await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
@@ -19686,7 +19740,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
19686
19740
|
const manifestPath = getClaudeKnownMarketplacesPath();
|
|
19687
19741
|
let existing = {};
|
|
19688
19742
|
if (await fileExists(manifestPath)) {
|
|
19689
|
-
const raw = await
|
|
19743
|
+
const raw = await readFile24(manifestPath, "utf-8");
|
|
19690
19744
|
try {
|
|
19691
19745
|
const parsed = JSON.parse(raw);
|
|
19692
19746
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -19713,7 +19767,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
19713
19767
|
existing[name].autoUpdate = true;
|
|
19714
19768
|
await ensureDir(dirname10(manifestPath));
|
|
19715
19769
|
if (await fileExists(manifestPath)) {
|
|
19716
|
-
const prev = await
|
|
19770
|
+
const prev = await readFile24(manifestPath, "utf-8");
|
|
19717
19771
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19718
19772
|
await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
19719
19773
|
}
|
|
@@ -19731,7 +19785,7 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
19731
19785
|
const key = `syntaur@${options.marketplaceName}`;
|
|
19732
19786
|
let parsed = {};
|
|
19733
19787
|
if (await fileExists(settingsPath)) {
|
|
19734
|
-
const raw = await
|
|
19788
|
+
const raw = await readFile24(settingsPath, "utf-8");
|
|
19735
19789
|
try {
|
|
19736
19790
|
parsed = JSON.parse(raw);
|
|
19737
19791
|
} catch {
|
|
@@ -19750,7 +19804,7 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
19750
19804
|
await ensureDir(dirname10(settingsPath));
|
|
19751
19805
|
if (await fileExists(settingsPath)) {
|
|
19752
19806
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19753
|
-
const prev = await
|
|
19807
|
+
const prev = await readFile24(settingsPath, "utf-8");
|
|
19754
19808
|
await writeFile8(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
|
|
19755
19809
|
}
|
|
19756
19810
|
const tmpPath = `${settingsPath}.tmp`;
|
|
@@ -19988,7 +20042,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
19988
20042
|
plugins: []
|
|
19989
20043
|
};
|
|
19990
20044
|
}
|
|
19991
|
-
const raw = await
|
|
20045
|
+
const raw = await readFile24(marketplacePath, "utf-8");
|
|
19992
20046
|
const parsed = JSON.parse(raw);
|
|
19993
20047
|
return {
|
|
19994
20048
|
name: parsed.name ?? "local",
|
|
@@ -20224,13 +20278,13 @@ async function textPrompt(question, defaultValue) {
|
|
|
20224
20278
|
|
|
20225
20279
|
// src/utils/install-skills.ts
|
|
20226
20280
|
init_fs();
|
|
20227
|
-
import { readFile as
|
|
20281
|
+
import { readFile as readFile26, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
|
|
20228
20282
|
import { resolve as resolve38, relative as relative3, join as join4 } from "path";
|
|
20229
20283
|
import { homedir as homedir5 } from "os";
|
|
20230
20284
|
|
|
20231
20285
|
// src/utils/plugin-state.ts
|
|
20232
20286
|
init_fs();
|
|
20233
|
-
import { readFile as
|
|
20287
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
20234
20288
|
import { resolve as resolve37 } from "path";
|
|
20235
20289
|
import { homedir as homedir4 } from "os";
|
|
20236
20290
|
function settingsPathFor(agent) {
|
|
@@ -20241,7 +20295,7 @@ async function readJsonOrNull(path) {
|
|
|
20241
20295
|
if (!path) return null;
|
|
20242
20296
|
if (!await fileExists(path)) return null;
|
|
20243
20297
|
try {
|
|
20244
|
-
const raw = await
|
|
20298
|
+
const raw = await readFile25(path, "utf-8");
|
|
20245
20299
|
return JSON.parse(raw);
|
|
20246
20300
|
} catch {
|
|
20247
20301
|
return null;
|
|
@@ -20315,7 +20369,7 @@ async function walkFiles(root) {
|
|
|
20315
20369
|
}
|
|
20316
20370
|
async function filesEqual(a, b) {
|
|
20317
20371
|
try {
|
|
20318
|
-
const [ba, bb] = await Promise.all([
|
|
20372
|
+
const [ba, bb] = await Promise.all([readFile26(a), readFile26(b)]);
|
|
20319
20373
|
if (ba.length !== bb.length) return false;
|
|
20320
20374
|
return ba.equals(bb);
|
|
20321
20375
|
} catch {
|
|
@@ -20416,6 +20470,24 @@ async function installSkillsWithReport(options) {
|
|
|
20416
20470
|
}
|
|
20417
20471
|
return { results };
|
|
20418
20472
|
}
|
|
20473
|
+
async function installSkillsToDir(options) {
|
|
20474
|
+
const source = options.sourceDir ?? await getSkillsDir();
|
|
20475
|
+
if (!await fileExists(source)) {
|
|
20476
|
+
throw new Error(
|
|
20477
|
+
`Syntaur skills not found at ${source}. Reinstall syntaur: npm install -g syntaur@latest`
|
|
20478
|
+
);
|
|
20479
|
+
}
|
|
20480
|
+
const force = options.force ?? false;
|
|
20481
|
+
const skillNames = await discoverSkillNames(source);
|
|
20482
|
+
await mkdir4(options.targetDir, { recursive: true });
|
|
20483
|
+
const results = [];
|
|
20484
|
+
for (const skill of skillNames) {
|
|
20485
|
+
const srcDir = join4(source, skill);
|
|
20486
|
+
const destDir = join4(options.targetDir, skill);
|
|
20487
|
+
results.push(await installSkillDir(srcDir, destDir, skill, force));
|
|
20488
|
+
}
|
|
20489
|
+
return results;
|
|
20490
|
+
}
|
|
20419
20491
|
async function uninstallSkills(options) {
|
|
20420
20492
|
const targetRoot = options.targetDir ?? defaultSkillTargetDir(options.target);
|
|
20421
20493
|
if (!await fileExists(targetRoot)) return [];
|
|
@@ -20437,7 +20509,7 @@ async function uninstallSkills(options) {
|
|
|
20437
20509
|
if (await isSymlink(destDir)) continue;
|
|
20438
20510
|
const skillMd = join4(destDir, "SKILL.md");
|
|
20439
20511
|
if (!await fileExists(skillMd)) continue;
|
|
20440
|
-
const content = await
|
|
20512
|
+
const content = await readFile26(skillMd, "utf-8").catch(() => "");
|
|
20441
20513
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
20442
20514
|
if (!match || match[1] !== skill) continue;
|
|
20443
20515
|
await rm5(destDir, { recursive: true, force: true });
|
|
@@ -20965,7 +21037,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20965
21037
|
`Spawn failed: ${msg}. Verify the terminal is installed and on PATH.`
|
|
20966
21038
|
);
|
|
20967
21039
|
}
|
|
20968
|
-
await new Promise((
|
|
21040
|
+
await new Promise((resolve82, reject) => {
|
|
20969
21041
|
let settled = false;
|
|
20970
21042
|
let stderr = "";
|
|
20971
21043
|
const finishOk = () => {
|
|
@@ -20975,7 +21047,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
20975
21047
|
child.unref();
|
|
20976
21048
|
} catch {
|
|
20977
21049
|
}
|
|
20978
|
-
|
|
21050
|
+
resolve82();
|
|
20979
21051
|
};
|
|
20980
21052
|
const finishErr = (remediation) => {
|
|
20981
21053
|
if (settled) return;
|
|
@@ -21130,7 +21202,7 @@ function normalizeSlashes(p) {
|
|
|
21130
21202
|
}
|
|
21131
21203
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
21132
21204
|
const realpath3 = opts.realpath ?? realpathSync.native;
|
|
21133
|
-
const
|
|
21205
|
+
const readFile56 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
|
|
21134
21206
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
21135
21207
|
const resolved = resolveScriptPath(scriptUrl, realpath3);
|
|
21136
21208
|
if (resolved === null) {
|
|
@@ -21151,7 +21223,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
21151
21223
|
const pkgJsonPath = join5(dir, "package.json");
|
|
21152
21224
|
let raw;
|
|
21153
21225
|
try {
|
|
21154
|
-
raw =
|
|
21226
|
+
raw = readFile56(pkgJsonPath);
|
|
21155
21227
|
} catch {
|
|
21156
21228
|
const parent2 = dirname11(dir);
|
|
21157
21229
|
if (parent2 === dir) break;
|
|
@@ -21236,20 +21308,20 @@ async function maybeNudgeForNpxInstall(scriptUrl) {
|
|
|
21236
21308
|
init_paths();
|
|
21237
21309
|
init_fs();
|
|
21238
21310
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
21239
|
-
import { readFile as
|
|
21311
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
21240
21312
|
import { dirname as dirname13, join as join7, resolve as resolve41 } from "path";
|
|
21241
21313
|
import { spawn as spawn5 } from "child_process";
|
|
21242
21314
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
21243
21315
|
|
|
21244
21316
|
// src/utils/version.ts
|
|
21245
21317
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
21246
|
-
import { readFile as
|
|
21318
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
21247
21319
|
import { dirname as dirname12, join as join6 } from "path";
|
|
21248
21320
|
async function readPackageVersion(scriptUrl) {
|
|
21249
21321
|
try {
|
|
21250
21322
|
const scriptPath = fileURLToPath5(scriptUrl);
|
|
21251
21323
|
const pkgRoot = dirname12(dirname12(scriptPath));
|
|
21252
|
-
const raw = await
|
|
21324
|
+
const raw = await readFile27(join6(pkgRoot, "package.json"), "utf-8");
|
|
21253
21325
|
const parsed = JSON.parse(raw);
|
|
21254
21326
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
21255
21327
|
} catch {
|
|
@@ -21279,7 +21351,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
21279
21351
|
async function readState() {
|
|
21280
21352
|
if (!await fileExists(STATE_FILE)) return null;
|
|
21281
21353
|
try {
|
|
21282
|
-
const raw = await
|
|
21354
|
+
const raw = await readFile28(STATE_FILE, "utf-8");
|
|
21283
21355
|
return JSON.parse(raw);
|
|
21284
21356
|
} catch {
|
|
21285
21357
|
return null;
|
|
@@ -21338,7 +21410,7 @@ async function readGlobalVersion() {
|
|
|
21338
21410
|
try {
|
|
21339
21411
|
const manifestPath = join7(rootPath, "syntaur", "package.json");
|
|
21340
21412
|
if (!await fileExists(manifestPath)) return null;
|
|
21341
|
-
const raw = await
|
|
21413
|
+
const raw = await readFile28(manifestPath, "utf-8");
|
|
21342
21414
|
const parsed = JSON.parse(raw);
|
|
21343
21415
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
21344
21416
|
} catch {
|
|
@@ -21696,7 +21768,7 @@ async function refreshPluginSkills(options, pm, runner, getManagedDir, resolveFr
|
|
|
21696
21768
|
// src/commands/install-statusline.ts
|
|
21697
21769
|
init_paths();
|
|
21698
21770
|
init_fs();
|
|
21699
|
-
import { readFile as
|
|
21771
|
+
import { readFile as readFile30, writeFile as writeFile11, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
|
|
21700
21772
|
import { resolve as resolve43, dirname as dirname15 } from "path";
|
|
21701
21773
|
import { homedir as homedir7 } from "os";
|
|
21702
21774
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
@@ -21704,7 +21776,7 @@ import { fileURLToPath as fileURLToPath8 } from "url";
|
|
|
21704
21776
|
// src/commands/configure-statusline.ts
|
|
21705
21777
|
init_paths();
|
|
21706
21778
|
init_fs();
|
|
21707
|
-
import { readFile as
|
|
21779
|
+
import { readFile as readFile29, writeFile as writeFile10 } from "fs/promises";
|
|
21708
21780
|
import { resolve as resolve42, dirname as dirname14 } from "path";
|
|
21709
21781
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
21710
21782
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
@@ -21731,7 +21803,7 @@ function getConfigPath(installRoot) {
|
|
|
21731
21803
|
async function readConfig2(path) {
|
|
21732
21804
|
if (!await fileExists(path)) return null;
|
|
21733
21805
|
try {
|
|
21734
|
-
const raw = await
|
|
21806
|
+
const raw = await readFile29(path, "utf-8");
|
|
21735
21807
|
const parsed = JSON.parse(raw);
|
|
21736
21808
|
if (!parsed || typeof parsed !== "object") return null;
|
|
21737
21809
|
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
@@ -21919,7 +21991,7 @@ function getPackageStatuslineSource() {
|
|
|
21919
21991
|
}
|
|
21920
21992
|
async function readSettingsJson(settingsPath) {
|
|
21921
21993
|
if (!await fileExists(settingsPath)) return {};
|
|
21922
|
-
const raw = await
|
|
21994
|
+
const raw = await readFile30(settingsPath, "utf-8");
|
|
21923
21995
|
if (raw.trim() === "") return {};
|
|
21924
21996
|
try {
|
|
21925
21997
|
const parsed = JSON.parse(raw);
|
|
@@ -22107,7 +22179,7 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
22107
22179
|
let restored = null;
|
|
22108
22180
|
if (await fileExists(backupPath)) {
|
|
22109
22181
|
try {
|
|
22110
|
-
const raw = await
|
|
22182
|
+
const raw = await readFile30(backupPath, "utf-8");
|
|
22111
22183
|
const parsed = JSON.parse(raw);
|
|
22112
22184
|
const prev = parsed?.previousStatusLine;
|
|
22113
22185
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -22284,6 +22356,353 @@ async function uninstallSkillsCommand(options) {
|
|
|
22284
22356
|
// src/commands/setup.ts
|
|
22285
22357
|
import { execSync } from "child_process";
|
|
22286
22358
|
init_config2();
|
|
22359
|
+
|
|
22360
|
+
// src/commands/cross-agent-install.ts
|
|
22361
|
+
init_fs();
|
|
22362
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
22363
|
+
import { resolve as resolve46 } from "path";
|
|
22364
|
+
import { readFile as readFile31 } from "fs/promises";
|
|
22365
|
+
|
|
22366
|
+
// src/commands/setup-adapter.ts
|
|
22367
|
+
init_paths();
|
|
22368
|
+
init_fs();
|
|
22369
|
+
init_config2();
|
|
22370
|
+
init_slug();
|
|
22371
|
+
import { resolve as resolve45 } from "path";
|
|
22372
|
+
|
|
22373
|
+
// src/targets/registry.ts
|
|
22374
|
+
init_fs();
|
|
22375
|
+
import { homedir as homedir8 } from "os";
|
|
22376
|
+
import { resolve as resolve44 } from "path";
|
|
22377
|
+
function home(...segments) {
|
|
22378
|
+
return resolve44(homedir8(), ...segments);
|
|
22379
|
+
}
|
|
22380
|
+
function hermesHome() {
|
|
22381
|
+
const env = process.env.HERMES_HOME;
|
|
22382
|
+
return env && env.length > 0 ? resolve44(env) : home(".hermes");
|
|
22383
|
+
}
|
|
22384
|
+
function hermesSkillsDir() {
|
|
22385
|
+
return resolve44(hermesHome(), "skills");
|
|
22386
|
+
}
|
|
22387
|
+
function isHermesHomeCustom() {
|
|
22388
|
+
return hermesHome() !== home(".hermes");
|
|
22389
|
+
}
|
|
22390
|
+
function codexHome() {
|
|
22391
|
+
const env = process.env.CODEX_HOME;
|
|
22392
|
+
return env && env.length > 0 ? resolve44(env) : home(".codex");
|
|
22393
|
+
}
|
|
22394
|
+
var detectDir = (dir) => () => fileExists(dir);
|
|
22395
|
+
var AGENT_TARGETS = [
|
|
22396
|
+
{
|
|
22397
|
+
id: "cursor",
|
|
22398
|
+
displayName: "Cursor",
|
|
22399
|
+
skillsShAgentId: "cursor",
|
|
22400
|
+
detect: detectDir(home(".cursor")),
|
|
22401
|
+
skillsDir: { global: home(".cursor", "skills") },
|
|
22402
|
+
instructions: {
|
|
22403
|
+
files: [
|
|
22404
|
+
{ path: ".cursor/rules/syntaur-protocol.mdc", renderer: "cursorProtocol" },
|
|
22405
|
+
{ path: ".cursor/rules/syntaur-assignment.mdc", renderer: "cursorAssignment" }
|
|
22406
|
+
]
|
|
22407
|
+
}
|
|
22408
|
+
},
|
|
22409
|
+
{
|
|
22410
|
+
// codex is BOTH an adapter (writes AGENTS.md) AND a native plugin.
|
|
22411
|
+
id: "codex",
|
|
22412
|
+
displayName: "Codex",
|
|
22413
|
+
skillsShAgentId: "codex",
|
|
22414
|
+
nativePlugin: "codex",
|
|
22415
|
+
detect: detectDir(codexHome()),
|
|
22416
|
+
skillsDir: { global: resolve44(codexHome(), "skills") },
|
|
22417
|
+
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
|
|
22418
|
+
},
|
|
22419
|
+
{
|
|
22420
|
+
id: "opencode",
|
|
22421
|
+
displayName: "OpenCode",
|
|
22422
|
+
skillsShAgentId: "opencode",
|
|
22423
|
+
detect: detectDir(home(".config", "opencode")),
|
|
22424
|
+
skillsDir: { global: home(".config", "opencode", "skills") },
|
|
22425
|
+
instructions: {
|
|
22426
|
+
files: [
|
|
22427
|
+
{ path: "AGENTS.md", renderer: "codexAgents" },
|
|
22428
|
+
{ path: "opencode.json", renderer: "openCodeConfig" }
|
|
22429
|
+
]
|
|
22430
|
+
}
|
|
22431
|
+
},
|
|
22432
|
+
{
|
|
22433
|
+
// claude has NO adapter today (not in the old SUPPORTED_FRAMEWORKS) — the
|
|
22434
|
+
// full plugin path owns its skills/hooks/commands. Native-plugin only.
|
|
22435
|
+
id: "claude",
|
|
22436
|
+
displayName: "Claude Code",
|
|
22437
|
+
skillsShAgentId: "claude-code",
|
|
22438
|
+
nativePlugin: "claude",
|
|
22439
|
+
detect: detectDir(home(".claude")),
|
|
22440
|
+
skillsDir: { global: home(".claude", "skills") }
|
|
22441
|
+
},
|
|
22442
|
+
{
|
|
22443
|
+
id: "pi",
|
|
22444
|
+
displayName: "Pi",
|
|
22445
|
+
skillsShAgentId: "pi",
|
|
22446
|
+
detect: detectDir(home(".pi")),
|
|
22447
|
+
skillsDir: { global: home(".pi", "agent", "skills") },
|
|
22448
|
+
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
|
|
22449
|
+
},
|
|
22450
|
+
{
|
|
22451
|
+
id: "openclaw",
|
|
22452
|
+
displayName: "OpenClaw",
|
|
22453
|
+
skillsShAgentId: "openclaw",
|
|
22454
|
+
detect: detectDir(home(".openclaw")),
|
|
22455
|
+
skillsDir: { global: home(".openclaw", "skills") },
|
|
22456
|
+
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
|
|
22457
|
+
},
|
|
22458
|
+
{
|
|
22459
|
+
id: "hermes",
|
|
22460
|
+
displayName: "Hermes Agent",
|
|
22461
|
+
skillsShAgentId: "hermes-agent",
|
|
22462
|
+
detect: () => fileExists(hermesHome()),
|
|
22463
|
+
skillsDir: { global: hermesSkillsDir() },
|
|
22464
|
+
instructions: { files: [{ path: "SOUL.md", renderer: "hermesSoul" }] }
|
|
22465
|
+
}
|
|
22466
|
+
];
|
|
22467
|
+
var AGENT_TARGETS_BY_ID = Object.fromEntries(
|
|
22468
|
+
AGENT_TARGETS.map((t) => [t.id, t])
|
|
22469
|
+
);
|
|
22470
|
+
function getAgentTarget(id) {
|
|
22471
|
+
return AGENT_TARGETS_BY_ID[id];
|
|
22472
|
+
}
|
|
22473
|
+
function agentTargetIds() {
|
|
22474
|
+
return AGENT_TARGETS.map((t) => t.id);
|
|
22475
|
+
}
|
|
22476
|
+
function adapterTargets() {
|
|
22477
|
+
return AGENT_TARGETS.filter((t) => t.instructions !== void 0);
|
|
22478
|
+
}
|
|
22479
|
+
|
|
22480
|
+
// src/targets/renderers.ts
|
|
22481
|
+
init_cursor_rules();
|
|
22482
|
+
init_codex_agents();
|
|
22483
|
+
init_opencode_config();
|
|
22484
|
+
init_hermes_soul();
|
|
22485
|
+
var RENDERERS = {
|
|
22486
|
+
codexAgents: (ctx) => renderCodexAgents(ctx),
|
|
22487
|
+
cursorProtocol: () => renderCursorProtocol(),
|
|
22488
|
+
cursorAssignment: (ctx) => renderCursorAssignment(ctx),
|
|
22489
|
+
openCodeConfig: (ctx) => renderOpenCodeConfig({ projectDir: ctx.projectDir }),
|
|
22490
|
+
hermesSoul: (ctx) => renderHermesSoul(ctx)
|
|
22491
|
+
};
|
|
22492
|
+
|
|
22493
|
+
// src/commands/setup-adapter.ts
|
|
22494
|
+
async function setupAdapterCommand(framework, options) {
|
|
22495
|
+
const target = getAgentTarget(framework);
|
|
22496
|
+
if (!target || !target.instructions) {
|
|
22497
|
+
const supported = adapterTargets().map((t) => t.id).join(", ");
|
|
22498
|
+
throw new Error(
|
|
22499
|
+
`Unsupported framework "${framework}". Supported: ${supported}`
|
|
22500
|
+
);
|
|
22501
|
+
}
|
|
22502
|
+
if (!options.project) {
|
|
22503
|
+
throw new Error("--project <slug> is required.");
|
|
22504
|
+
}
|
|
22505
|
+
if (!options.assignment) {
|
|
22506
|
+
throw new Error("--assignment <slug> is required.");
|
|
22507
|
+
}
|
|
22508
|
+
if (!isValidSlug(options.project)) {
|
|
22509
|
+
throw new Error(
|
|
22510
|
+
`Invalid project slug "${options.project}". Slugs must be lowercase, hyphen-separated, with no special characters.`
|
|
22511
|
+
);
|
|
22512
|
+
}
|
|
22513
|
+
if (!isValidSlug(options.assignment)) {
|
|
22514
|
+
throw new Error(
|
|
22515
|
+
`Invalid assignment slug "${options.assignment}". Slugs must be lowercase, hyphen-separated, with no special characters.`
|
|
22516
|
+
);
|
|
22517
|
+
}
|
|
22518
|
+
const config = await readConfig();
|
|
22519
|
+
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
22520
|
+
const projectDir = resolve45(baseDir, options.project);
|
|
22521
|
+
const assignmentDir = resolve45(projectDir, "assignments", options.assignment);
|
|
22522
|
+
const projectMdPath = resolve45(projectDir, "project.md");
|
|
22523
|
+
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
22524
|
+
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
22525
|
+
}
|
|
22526
|
+
const assignmentMdPath2 = resolve45(assignmentDir, "assignment.md");
|
|
22527
|
+
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
|
|
22528
|
+
throw new Error(
|
|
22529
|
+
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
22530
|
+
);
|
|
22531
|
+
}
|
|
22532
|
+
const cwd = process.cwd();
|
|
22533
|
+
const rendererParams = {
|
|
22534
|
+
projectSlug: options.project,
|
|
22535
|
+
assignmentSlug: options.assignment,
|
|
22536
|
+
projectDir,
|
|
22537
|
+
assignmentDir
|
|
22538
|
+
};
|
|
22539
|
+
const writtenFiles = [];
|
|
22540
|
+
const upToDateFiles = [];
|
|
22541
|
+
const skippedFiles = [];
|
|
22542
|
+
for (const file of target.instructions.files) {
|
|
22543
|
+
const filePath = resolve45(cwd, file.path);
|
|
22544
|
+
const content = RENDERERS[file.renderer](rendererParams);
|
|
22545
|
+
const status = await writeFileReport(filePath, content, {
|
|
22546
|
+
force: options.force
|
|
22547
|
+
});
|
|
22548
|
+
if (status === "differs-preserved") {
|
|
22549
|
+
skippedFiles.push(filePath);
|
|
22550
|
+
} else if (status === "already-current") {
|
|
22551
|
+
upToDateFiles.push(filePath);
|
|
22552
|
+
} else {
|
|
22553
|
+
writtenFiles.push(filePath);
|
|
22554
|
+
}
|
|
22555
|
+
}
|
|
22556
|
+
if (writtenFiles.length > 0) {
|
|
22557
|
+
console.log(`Generated ${target.id} adapter files:`);
|
|
22558
|
+
for (const f of writtenFiles) {
|
|
22559
|
+
console.log(` ${f}`);
|
|
22560
|
+
}
|
|
22561
|
+
}
|
|
22562
|
+
if (upToDateFiles.length > 0) {
|
|
22563
|
+
console.log(`Already up-to-date:`);
|
|
22564
|
+
for (const f of upToDateFiles) {
|
|
22565
|
+
console.log(` ${f}`);
|
|
22566
|
+
}
|
|
22567
|
+
}
|
|
22568
|
+
if (skippedFiles.length > 0) {
|
|
22569
|
+
console.log(`Skipped (exists with different content, use --force to overwrite):`);
|
|
22570
|
+
for (const f of skippedFiles) {
|
|
22571
|
+
console.log(` ${f}`);
|
|
22572
|
+
}
|
|
22573
|
+
}
|
|
22574
|
+
if (writtenFiles.length === 0 && skippedFiles.length === 0 && upToDateFiles.length > 0) {
|
|
22575
|
+
console.log(`No changes. All ${target.id} adapter files are already up-to-date.`);
|
|
22576
|
+
}
|
|
22577
|
+
}
|
|
22578
|
+
|
|
22579
|
+
// src/commands/cross-agent-install.ts
|
|
22580
|
+
init_config2();
|
|
22581
|
+
var DEFAULT_SKILLS_SOURCE = "prong-horn/syntaur";
|
|
22582
|
+
function parseTargetIds(options) {
|
|
22583
|
+
const raw = [options.target, options.agent].filter(Boolean).join(",");
|
|
22584
|
+
const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
22585
|
+
return [...new Set(ids)];
|
|
22586
|
+
}
|
|
22587
|
+
function isNpxAvailable() {
|
|
22588
|
+
try {
|
|
22589
|
+
const r = spawnSync6("npx", ["--version"], { stdio: "ignore" });
|
|
22590
|
+
return !r.error && r.status === 0;
|
|
22591
|
+
} catch {
|
|
22592
|
+
return false;
|
|
22593
|
+
}
|
|
22594
|
+
}
|
|
22595
|
+
async function readAssignmentContext() {
|
|
22596
|
+
const p = resolve46(process.cwd(), ".syntaur", "context.json");
|
|
22597
|
+
if (!await fileExists(p)) return null;
|
|
22598
|
+
try {
|
|
22599
|
+
return JSON.parse(await readFile31(p, "utf-8"));
|
|
22600
|
+
} catch {
|
|
22601
|
+
return null;
|
|
22602
|
+
}
|
|
22603
|
+
}
|
|
22604
|
+
async function crossAgentInstallCommand(options) {
|
|
22605
|
+
const ids = parseTargetIds(options);
|
|
22606
|
+
if (ids.length === 0) {
|
|
22607
|
+
throw new Error("No agents specified. Use --target <id> or --agent <id>.");
|
|
22608
|
+
}
|
|
22609
|
+
const targets = [];
|
|
22610
|
+
for (const id of ids) {
|
|
22611
|
+
const t = getAgentTarget(id);
|
|
22612
|
+
if (!t) {
|
|
22613
|
+
throw new Error(
|
|
22614
|
+
`Unknown agent "${id}". Known agents: ${agentTargetIds().join(", ")}`
|
|
22615
|
+
);
|
|
22616
|
+
}
|
|
22617
|
+
if (t.nativePlugin) {
|
|
22618
|
+
throw new Error(
|
|
22619
|
+
`"${id}" installs as a native Syntaur plugin, not via cross-agent install. Use \`syntaur setup --${t.nativePlugin}\` (or \`syntaur ${t.nativePlugin === "claude" ? "install-plugin" : "install-codex-plugin"}\`).`
|
|
22620
|
+
);
|
|
22621
|
+
}
|
|
22622
|
+
targets.push(t);
|
|
22623
|
+
}
|
|
22624
|
+
const dryRun = Boolean(options.dryRun);
|
|
22625
|
+
const force = Boolean(options.force);
|
|
22626
|
+
const scope = "global";
|
|
22627
|
+
const prefix = dryRun ? "[dry-run] " : "";
|
|
22628
|
+
if (!dryRun && !await isSyntaurDataInstalled()) {
|
|
22629
|
+
await initCommand({});
|
|
22630
|
+
}
|
|
22631
|
+
const skillsShIds = targets.map((t) => t.skillsShAgentId ?? t.id);
|
|
22632
|
+
const argv = ["skills", "add", DEFAULT_SKILLS_SOURCE, "--agent", ...skillsShIds];
|
|
22633
|
+
console.log(`${prefix}Tier 1 (skills): npx ${argv.join(" ")}`);
|
|
22634
|
+
let tier1Done = false;
|
|
22635
|
+
if (!dryRun) {
|
|
22636
|
+
if (isNpxAvailable()) {
|
|
22637
|
+
const r = spawnSync6("npx", argv, { stdio: "inherit" });
|
|
22638
|
+
tier1Done = !r.error && r.status === 0;
|
|
22639
|
+
if (!tier1Done) {
|
|
22640
|
+
console.log("`npx skills add` failed; falling back to offline copy.");
|
|
22641
|
+
}
|
|
22642
|
+
} else {
|
|
22643
|
+
console.log("npx not available; using offline skill copy.");
|
|
22644
|
+
}
|
|
22645
|
+
}
|
|
22646
|
+
for (const t of targets) {
|
|
22647
|
+
const globalDir = t.id === "hermes" ? hermesSkillsDir() : t.skillsDir?.global;
|
|
22648
|
+
if (!globalDir) continue;
|
|
22649
|
+
const offlineNeeded = !tier1Done;
|
|
22650
|
+
const hermesCustom = t.id === "hermes" && isHermesHomeCustom();
|
|
22651
|
+
if (!offlineNeeded && !hermesCustom) continue;
|
|
22652
|
+
if (dryRun) {
|
|
22653
|
+
const label = hermesCustom ? "offline copy (always \u2014 custom $HERMES_HOME)" : "offline copy (fallback if npx unavailable)";
|
|
22654
|
+
console.log(`${prefix}${label} -> ${globalDir}`);
|
|
22655
|
+
continue;
|
|
22656
|
+
}
|
|
22657
|
+
const reason = hermesCustom && tier1Done ? " ($HERMES_HOME reconcile)" : "";
|
|
22658
|
+
await installSkillsToDir({ targetDir: globalDir, force });
|
|
22659
|
+
console.log(`Copied skills -> ${globalDir}${reason}`);
|
|
22660
|
+
}
|
|
22661
|
+
const adapterTargets2 = targets.filter((t) => t.instructions);
|
|
22662
|
+
if (adapterTargets2.length > 0) {
|
|
22663
|
+
const ctx = await readAssignmentContext();
|
|
22664
|
+
const haveCtx = Boolean(ctx?.projectSlug && ctx?.assignmentSlug);
|
|
22665
|
+
for (const t of adapterTargets2) {
|
|
22666
|
+
if (dryRun) {
|
|
22667
|
+
for (const f of t.instructions.files) {
|
|
22668
|
+
console.log(
|
|
22669
|
+
`${prefix}Tier 2 (${t.id}): ${resolve46(process.cwd(), f.path)}`
|
|
22670
|
+
);
|
|
22671
|
+
}
|
|
22672
|
+
continue;
|
|
22673
|
+
}
|
|
22674
|
+
if (!haveCtx) {
|
|
22675
|
+
console.log(
|
|
22676
|
+
`No project-nested assignment context in cwd; skipping Tier-2 files for ${t.id}.`
|
|
22677
|
+
);
|
|
22678
|
+
continue;
|
|
22679
|
+
}
|
|
22680
|
+
try {
|
|
22681
|
+
await setupAdapterCommand(t.id, {
|
|
22682
|
+
project: ctx.projectSlug,
|
|
22683
|
+
assignment: ctx.assignmentSlug,
|
|
22684
|
+
force
|
|
22685
|
+
});
|
|
22686
|
+
} catch (err2) {
|
|
22687
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
22688
|
+
console.log(`Tier 2 for ${t.id} skipped: ${msg}`);
|
|
22689
|
+
}
|
|
22690
|
+
}
|
|
22691
|
+
}
|
|
22692
|
+
if (!dryRun) {
|
|
22693
|
+
const current = (await readConfig()).integrations.installedAgents ?? {};
|
|
22694
|
+
const next = { ...current };
|
|
22695
|
+
for (const t of targets) {
|
|
22696
|
+
next[t.id] = { scope };
|
|
22697
|
+
}
|
|
22698
|
+
await updateIntegrationConfig({ installedAgents: next });
|
|
22699
|
+
}
|
|
22700
|
+
console.log(
|
|
22701
|
+
`${prefix}Done. Agents: ${targets.map((t) => t.displayName).join(", ")}.`
|
|
22702
|
+
);
|
|
22703
|
+
}
|
|
22704
|
+
|
|
22705
|
+
// src/commands/setup.ts
|
|
22287
22706
|
function isCliInstalled(command) {
|
|
22288
22707
|
try {
|
|
22289
22708
|
execSync(`which ${command}`, { stdio: "ignore" });
|
|
@@ -22301,6 +22720,15 @@ function printNonInteractiveSetupHelp() {
|
|
|
22301
22720
|
console.error(` npx syntaur@latest setup --yes --dashboard`);
|
|
22302
22721
|
}
|
|
22303
22722
|
async function setupCommand(options) {
|
|
22723
|
+
if (options.dryRun && !options.target && !options.agent) {
|
|
22724
|
+
throw new Error(
|
|
22725
|
+
"--dry-run only applies to cross-agent install. Pass --target <id> or --agent <id>."
|
|
22726
|
+
);
|
|
22727
|
+
}
|
|
22728
|
+
if (options.target || options.agent) {
|
|
22729
|
+
await crossAgentInstallCommand(options);
|
|
22730
|
+
return;
|
|
22731
|
+
}
|
|
22304
22732
|
const initialized = await isSyntaurDataInstalled();
|
|
22305
22733
|
const interactive = isInteractiveTerminal();
|
|
22306
22734
|
if (!initialized) {
|
|
@@ -22385,7 +22813,7 @@ async function setupCommand(options) {
|
|
|
22385
22813
|
}
|
|
22386
22814
|
|
|
22387
22815
|
// src/commands/uninstall.ts
|
|
22388
|
-
import { resolve as
|
|
22816
|
+
import { resolve as resolve47 } from "path";
|
|
22389
22817
|
init_paths();
|
|
22390
22818
|
function expandTargets(options) {
|
|
22391
22819
|
if (options.all) {
|
|
@@ -22465,7 +22893,7 @@ async function uninstallCommand(options) {
|
|
|
22465
22893
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
22466
22894
|
await removeSyntaurData();
|
|
22467
22895
|
console.log(`Removed ${syntaurRoot()}`);
|
|
22468
|
-
if (configuredProjectDir &&
|
|
22896
|
+
if (configuredProjectDir && resolve47(configuredProjectDir) !== resolve47(syntaurRoot(), "projects")) {
|
|
22469
22897
|
console.warn(
|
|
22470
22898
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
22471
22899
|
);
|
|
@@ -22476,114 +22904,11 @@ async function uninstallCommand(options) {
|
|
|
22476
22904
|
}
|
|
22477
22905
|
}
|
|
22478
22906
|
|
|
22479
|
-
// src/commands/setup-adapter.ts
|
|
22480
|
-
init_paths();
|
|
22481
|
-
init_fs();
|
|
22482
|
-
init_config2();
|
|
22483
|
-
init_slug();
|
|
22484
|
-
init_cursor_rules();
|
|
22485
|
-
init_codex_agents();
|
|
22486
|
-
init_opencode_config();
|
|
22487
|
-
import { resolve as resolve45 } from "path";
|
|
22488
|
-
var SUPPORTED_FRAMEWORKS = ["cursor", "codex", "opencode"];
|
|
22489
|
-
async function setupAdapterCommand(framework, options) {
|
|
22490
|
-
if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
|
|
22491
|
-
throw new Error(
|
|
22492
|
-
`Unsupported framework "${framework}". Supported: ${SUPPORTED_FRAMEWORKS.join(", ")}`
|
|
22493
|
-
);
|
|
22494
|
-
}
|
|
22495
|
-
if (!options.project) {
|
|
22496
|
-
throw new Error("--project <slug> is required.");
|
|
22497
|
-
}
|
|
22498
|
-
if (!options.assignment) {
|
|
22499
|
-
throw new Error("--assignment <slug> is required.");
|
|
22500
|
-
}
|
|
22501
|
-
if (!isValidSlug(options.project)) {
|
|
22502
|
-
throw new Error(
|
|
22503
|
-
`Invalid project slug "${options.project}". Slugs must be lowercase, hyphen-separated, with no special characters.`
|
|
22504
|
-
);
|
|
22505
|
-
}
|
|
22506
|
-
if (!isValidSlug(options.assignment)) {
|
|
22507
|
-
throw new Error(
|
|
22508
|
-
`Invalid assignment slug "${options.assignment}". Slugs must be lowercase, hyphen-separated, with no special characters.`
|
|
22509
|
-
);
|
|
22510
|
-
}
|
|
22511
|
-
const config = await readConfig();
|
|
22512
|
-
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
22513
|
-
const projectDir = resolve45(baseDir, options.project);
|
|
22514
|
-
const assignmentDir = resolve45(
|
|
22515
|
-
projectDir,
|
|
22516
|
-
"assignments",
|
|
22517
|
-
options.assignment
|
|
22518
|
-
);
|
|
22519
|
-
const projectMdPath = resolve45(projectDir, "project.md");
|
|
22520
|
-
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
22521
|
-
throw new Error(
|
|
22522
|
-
`Project "${options.project}" not found at ${projectDir}.`
|
|
22523
|
-
);
|
|
22524
|
-
}
|
|
22525
|
-
const assignmentMdPath2 = resolve45(assignmentDir, "assignment.md");
|
|
22526
|
-
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
|
|
22527
|
-
throw new Error(
|
|
22528
|
-
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
22529
|
-
);
|
|
22530
|
-
}
|
|
22531
|
-
const cwd = process.cwd();
|
|
22532
|
-
const writtenFiles = [];
|
|
22533
|
-
const skippedFiles = [];
|
|
22534
|
-
const rendererParams = {
|
|
22535
|
-
projectSlug: options.project,
|
|
22536
|
-
assignmentSlug: options.assignment,
|
|
22537
|
-
projectDir,
|
|
22538
|
-
assignmentDir
|
|
22539
|
-
};
|
|
22540
|
-
async function writeAdapterFile(filePath, content) {
|
|
22541
|
-
if (options.force) {
|
|
22542
|
-
await writeFileForce(filePath, content);
|
|
22543
|
-
writtenFiles.push(filePath);
|
|
22544
|
-
} else {
|
|
22545
|
-
if (await writeFileSafe(filePath, content)) {
|
|
22546
|
-
writtenFiles.push(filePath);
|
|
22547
|
-
} else {
|
|
22548
|
-
skippedFiles.push(filePath);
|
|
22549
|
-
}
|
|
22550
|
-
}
|
|
22551
|
-
}
|
|
22552
|
-
if (framework === "cursor") {
|
|
22553
|
-
const protocolPath = resolve45(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
|
|
22554
|
-
const assignmentPath = resolve45(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
|
|
22555
|
-
await writeAdapterFile(protocolPath, renderCursorProtocol());
|
|
22556
|
-
await writeAdapterFile(assignmentPath, renderCursorAssignment(rendererParams));
|
|
22557
|
-
} else if (framework === "codex" || framework === "opencode") {
|
|
22558
|
-
const agentsPath = resolve45(cwd, "AGENTS.md");
|
|
22559
|
-
await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
|
|
22560
|
-
if (framework === "opencode") {
|
|
22561
|
-
const configPath = resolve45(cwd, "opencode.json");
|
|
22562
|
-
await writeAdapterFile(configPath, renderOpenCodeConfig({ projectDir }));
|
|
22563
|
-
}
|
|
22564
|
-
}
|
|
22565
|
-
if (writtenFiles.length > 0) {
|
|
22566
|
-
console.log(`Generated ${framework} adapter files:`);
|
|
22567
|
-
for (const f of writtenFiles) {
|
|
22568
|
-
console.log(` ${f}`);
|
|
22569
|
-
}
|
|
22570
|
-
}
|
|
22571
|
-
if (skippedFiles.length > 0) {
|
|
22572
|
-
console.log(`Skipped (already exist, use --force to overwrite):`);
|
|
22573
|
-
for (const f of skippedFiles) {
|
|
22574
|
-
console.log(` ${f}`);
|
|
22575
|
-
}
|
|
22576
|
-
}
|
|
22577
|
-
if (writtenFiles.length === 0 && skippedFiles.length > 0) {
|
|
22578
|
-
console.log(`No files written. All target files already exist.`);
|
|
22579
|
-
}
|
|
22580
|
-
}
|
|
22581
|
-
|
|
22582
22907
|
// src/commands/track-session.ts
|
|
22583
22908
|
init_paths();
|
|
22584
22909
|
init_fs();
|
|
22585
22910
|
init_config2();
|
|
22586
|
-
import { resolve as
|
|
22911
|
+
import { resolve as resolve48 } from "path";
|
|
22587
22912
|
init_session_db();
|
|
22588
22913
|
init_agent_sessions();
|
|
22589
22914
|
async function trackSessionCommand(options) {
|
|
@@ -22598,7 +22923,7 @@ async function trackSessionCommand(options) {
|
|
|
22598
22923
|
if (options.project) {
|
|
22599
22924
|
const config = await readConfig();
|
|
22600
22925
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
22601
|
-
const projectDir =
|
|
22926
|
+
const projectDir = resolve48(baseDir, options.project);
|
|
22602
22927
|
if (!await fileExists(projectDir)) {
|
|
22603
22928
|
throw new Error(
|
|
22604
22929
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -22730,8 +23055,8 @@ function formatInstallUrlHandlerError(err2) {
|
|
|
22730
23055
|
init_config2();
|
|
22731
23056
|
init_paths();
|
|
22732
23057
|
init_fs();
|
|
22733
|
-
import { resolve as
|
|
22734
|
-
import { readFile as
|
|
23058
|
+
import { resolve as resolve50, isAbsolute as isAbsolute7 } from "path";
|
|
23059
|
+
import { readFile as readFile32 } from "fs/promises";
|
|
22735
23060
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
22736
23061
|
async function browseCommand(options) {
|
|
22737
23062
|
const config = await readConfig();
|
|
@@ -22800,7 +23125,7 @@ async function pickAgent2(agents) {
|
|
|
22800
23125
|
return picked;
|
|
22801
23126
|
}
|
|
22802
23127
|
async function ensureWorktree(opts) {
|
|
22803
|
-
const assignmentPath =
|
|
23128
|
+
const assignmentPath = resolve50(
|
|
22804
23129
|
opts.projectsDir,
|
|
22805
23130
|
opts.projectSlug,
|
|
22806
23131
|
"assignments",
|
|
@@ -22810,7 +23135,7 @@ async function ensureWorktree(opts) {
|
|
|
22810
23135
|
if (!await fileExists(assignmentPath)) {
|
|
22811
23136
|
return void 0;
|
|
22812
23137
|
}
|
|
22813
|
-
const content = await
|
|
23138
|
+
const content = await readFile32(assignmentPath, "utf-8");
|
|
22814
23139
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
22815
23140
|
const fm = parseAssignmentFrontmatter2(content);
|
|
22816
23141
|
const { workspace } = fm;
|
|
@@ -22880,7 +23205,7 @@ async function ensureWorktree(opts) {
|
|
|
22880
23205
|
async function runCreate(opts) {
|
|
22881
23206
|
const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
22882
23207
|
const expandedWorktree = expandHome(opts.worktreePath);
|
|
22883
|
-
const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree :
|
|
23208
|
+
const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree : resolve50(expandedWorktree);
|
|
22884
23209
|
try {
|
|
22885
23210
|
await createWorktreeAndRecord2({
|
|
22886
23211
|
assignmentPath: opts.assignmentPath,
|
|
@@ -22909,7 +23234,7 @@ init_paths();
|
|
|
22909
23234
|
init_fs();
|
|
22910
23235
|
init_playbook();
|
|
22911
23236
|
init_playbooks();
|
|
22912
|
-
import { resolve as
|
|
23237
|
+
import { resolve as resolve51 } from "path";
|
|
22913
23238
|
async function createPlaybookCommand(name, options) {
|
|
22914
23239
|
if (!name.trim()) {
|
|
22915
23240
|
throw new Error("Playbook name cannot be empty.");
|
|
@@ -22922,7 +23247,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
22922
23247
|
}
|
|
22923
23248
|
const dir = playbooksDir();
|
|
22924
23249
|
await ensureDir(dir);
|
|
22925
|
-
const filePath =
|
|
23250
|
+
const filePath = resolve51(dir, `${slug}.md`);
|
|
22926
23251
|
if (await fileExists(filePath)) {
|
|
22927
23252
|
throw new Error(
|
|
22928
23253
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -22944,8 +23269,8 @@ init_paths();
|
|
|
22944
23269
|
init_fs();
|
|
22945
23270
|
init_parser();
|
|
22946
23271
|
init_config2();
|
|
22947
|
-
import { readdir as readdir16, readFile as
|
|
22948
|
-
import { resolve as
|
|
23272
|
+
import { readdir as readdir16, readFile as readFile33 } from "fs/promises";
|
|
23273
|
+
import { resolve as resolve52 } from "path";
|
|
22949
23274
|
async function listPlaybooksCommand(options = {}) {
|
|
22950
23275
|
const dir = playbooksDir();
|
|
22951
23276
|
if (!await fileExists(dir)) {
|
|
@@ -22960,8 +23285,8 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
22960
23285
|
);
|
|
22961
23286
|
const rows = [];
|
|
22962
23287
|
for (const entry of mdFiles) {
|
|
22963
|
-
const filePath =
|
|
22964
|
-
const raw = await
|
|
23288
|
+
const filePath = resolve52(dir, entry.name);
|
|
23289
|
+
const raw = await readFile33(filePath, "utf-8");
|
|
22965
23290
|
const parsed = parsePlaybook(raw);
|
|
22966
23291
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
22967
23292
|
const disabled = disabledSet.has(slug);
|
|
@@ -23084,14 +23409,14 @@ init_fs();
|
|
|
23084
23409
|
init_config2();
|
|
23085
23410
|
init_slug();
|
|
23086
23411
|
import { Command as Command2 } from "commander";
|
|
23087
|
-
import { readFile as
|
|
23088
|
-
import { resolve as
|
|
23412
|
+
import { readFile as readFile35 } from "fs/promises";
|
|
23413
|
+
import { resolve as resolve54 } from "path";
|
|
23089
23414
|
|
|
23090
23415
|
// src/commands/bundle.ts
|
|
23091
23416
|
init_paths();
|
|
23092
23417
|
import { Command } from "commander";
|
|
23093
|
-
import { mkdir as mkdir7, readFile as
|
|
23094
|
-
import { resolve as
|
|
23418
|
+
import { mkdir as mkdir7, readFile as readFile34, readdir as readdir17, rm as rm7, writeFile as writeFile12 } from "fs/promises";
|
|
23419
|
+
import { resolve as resolve53 } from "path";
|
|
23095
23420
|
init_parser2();
|
|
23096
23421
|
init_fs();
|
|
23097
23422
|
init_config2();
|
|
@@ -23109,7 +23434,7 @@ async function resolveBundleScope(options) {
|
|
|
23109
23434
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
23110
23435
|
}
|
|
23111
23436
|
const config = await readConfig();
|
|
23112
|
-
const projectMd =
|
|
23437
|
+
const projectMd = resolve53(config.defaultProjectDir, options.project, "project.md");
|
|
23113
23438
|
if (!await fileExists(projectMd)) {
|
|
23114
23439
|
throw new Error(`Project "${options.project}" not found.`);
|
|
23115
23440
|
}
|
|
@@ -23179,10 +23504,10 @@ function pickNextPlanFile(planDir, existingFiles) {
|
|
|
23179
23504
|
const m = f.match(/^plan-v(\d+)\.md$/);
|
|
23180
23505
|
if (m) versions.add(parseInt(m[1], 10));
|
|
23181
23506
|
}
|
|
23182
|
-
if (versions.size === 0) return { target:
|
|
23507
|
+
if (versions.size === 0) return { target: resolve53(planDir, "plan.md"), version: 1 };
|
|
23183
23508
|
let n = 2;
|
|
23184
23509
|
while (versions.has(n)) n++;
|
|
23185
|
-
return { target:
|
|
23510
|
+
return { target: resolve53(planDir, `plan-v${n}.md`), version: n };
|
|
23186
23511
|
}
|
|
23187
23512
|
function dedupePreserveOrder(ids) {
|
|
23188
23513
|
const out = [];
|
|
@@ -23266,7 +23591,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
|
|
|
23266
23591
|
if (options.plan) {
|
|
23267
23592
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
23268
23593
|
await ensureDir(planDir);
|
|
23269
|
-
const target =
|
|
23594
|
+
const target = resolve53(planDir, "plan.md");
|
|
23270
23595
|
const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
|
|
23271
23596
|
const stub = [
|
|
23272
23597
|
"---",
|
|
@@ -23442,7 +23767,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
23442
23767
|
}
|
|
23443
23768
|
const repository = options.repository ?? process.cwd();
|
|
23444
23769
|
const parentBranch = options.parentBranch ?? "main";
|
|
23445
|
-
const worktreePath = options.worktreePath ??
|
|
23770
|
+
const worktreePath = options.worktreePath ?? resolve53(repository, ".worktrees", options.branch);
|
|
23446
23771
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
23447
23772
|
for (const memberId of bundle.todoIds) {
|
|
23448
23773
|
const item = checklist.items.find((i) => i.id === memberId);
|
|
@@ -23452,8 +23777,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
23452
23777
|
}
|
|
23453
23778
|
const bundlesFilePath = bundlesPath(sc.todosPath);
|
|
23454
23779
|
const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
|
|
23455
|
-
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await
|
|
23456
|
-
const checklistSnapshot = await fileExists(checklistFilePath) ? await
|
|
23780
|
+
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile34(bundlesFilePath, "utf-8") : null;
|
|
23781
|
+
const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile34(checklistFilePath, "utf-8") : null;
|
|
23457
23782
|
const record = async () => {
|
|
23458
23783
|
bundle.branch = options.branch;
|
|
23459
23784
|
bundle.worktreePath = worktreePath;
|
|
@@ -23469,7 +23794,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
23469
23794
|
try {
|
|
23470
23795
|
await writeBundles(sc.todosPath, bundles);
|
|
23471
23796
|
await writeChecklist(sc.todosPath, checklist);
|
|
23472
|
-
const ctxDir =
|
|
23797
|
+
const ctxDir = resolve53(worktreePath, ".syntaur");
|
|
23473
23798
|
await mkdir7(ctxDir, { recursive: true });
|
|
23474
23799
|
const payload = {
|
|
23475
23800
|
bundleId: bundle.id,
|
|
@@ -23483,7 +23808,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
23483
23808
|
repository,
|
|
23484
23809
|
boundAt: nowISO()
|
|
23485
23810
|
};
|
|
23486
|
-
await writeFile12(
|
|
23811
|
+
await writeFile12(resolve53(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
|
|
23487
23812
|
} catch (err2) {
|
|
23488
23813
|
try {
|
|
23489
23814
|
if (bundlesSnapshot === null) {
|
|
@@ -23673,7 +23998,7 @@ async function resolveScope(options) {
|
|
|
23673
23998
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
23674
23999
|
}
|
|
23675
24000
|
const config = await readConfig();
|
|
23676
|
-
const projectMd =
|
|
24001
|
+
const projectMd = resolve54(config.defaultProjectDir, options.project, "project.md");
|
|
23677
24002
|
if (!await fileExists(projectMd)) {
|
|
23678
24003
|
throw new Error(`Project "${options.project}" not found.`);
|
|
23679
24004
|
}
|
|
@@ -23996,10 +24321,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
23996
24321
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
23997
24322
|
);
|
|
23998
24323
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
23999
|
-
await ensureDir(
|
|
24324
|
+
await ensureDir(resolve54(todosPath, "archive"));
|
|
24000
24325
|
let archContent = "";
|
|
24001
24326
|
if (await fileExists(archFile)) {
|
|
24002
|
-
archContent = await
|
|
24327
|
+
archContent = await readFile35(archFile, "utf-8");
|
|
24003
24328
|
archContent = archContent.trimEnd() + "\n\n";
|
|
24004
24329
|
} else {
|
|
24005
24330
|
archContent = `---
|
|
@@ -24176,12 +24501,12 @@ function describeScope(scope) {
|
|
|
24176
24501
|
}
|
|
24177
24502
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
24178
24503
|
const { resolve: resolvePath2 } = await import("path");
|
|
24179
|
-
const { readFile:
|
|
24504
|
+
const { readFile: readFile56 } = await import("fs/promises");
|
|
24180
24505
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
24181
24506
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
24182
24507
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
24183
24508
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
24184
|
-
let content = await
|
|
24509
|
+
let content = await readFile56(assignmentMdPath2, "utf-8");
|
|
24185
24510
|
content = appendTodosToAssignmentBody2(
|
|
24186
24511
|
content,
|
|
24187
24512
|
todos.map((t) => ({
|
|
@@ -24232,7 +24557,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
24232
24557
|
);
|
|
24233
24558
|
let target;
|
|
24234
24559
|
if (existingFiles.length === 0) {
|
|
24235
|
-
target =
|
|
24560
|
+
target = resolve54(planDir, "plan.md");
|
|
24236
24561
|
} else {
|
|
24237
24562
|
const versions = /* @__PURE__ */ new Set();
|
|
24238
24563
|
for (const f of existingFiles) {
|
|
@@ -24242,7 +24567,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
24242
24567
|
}
|
|
24243
24568
|
let n = 2;
|
|
24244
24569
|
while (versions.has(n)) n++;
|
|
24245
|
-
target =
|
|
24570
|
+
target = resolve54(planDir, `plan-v${n}.md`);
|
|
24246
24571
|
}
|
|
24247
24572
|
if (!await fileExists(target)) {
|
|
24248
24573
|
const stub = `---
|
|
@@ -24431,24 +24756,24 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
24431
24756
|
|
|
24432
24757
|
// src/commands/doctor.ts
|
|
24433
24758
|
import { Command as Command4 } from "commander";
|
|
24434
|
-
import { readFile as
|
|
24435
|
-
import { isAbsolute as isAbsolute10, resolve as
|
|
24759
|
+
import { readFile as readFile43 } from "fs/promises";
|
|
24760
|
+
import { isAbsolute as isAbsolute10, resolve as resolve67 } from "path";
|
|
24436
24761
|
|
|
24437
24762
|
// src/utils/doctor/index.ts
|
|
24438
24763
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
24439
|
-
import { readFile as
|
|
24440
|
-
import { dirname as dirname19, join as
|
|
24764
|
+
import { readFile as readFile42 } from "fs/promises";
|
|
24765
|
+
import { dirname as dirname19, join as join13 } from "path";
|
|
24441
24766
|
|
|
24442
24767
|
// src/utils/doctor/context.ts
|
|
24443
24768
|
init_config2();
|
|
24444
24769
|
init_paths();
|
|
24445
24770
|
init_fs();
|
|
24446
24771
|
import Database4 from "better-sqlite3";
|
|
24447
|
-
import { resolve as
|
|
24772
|
+
import { resolve as resolve55 } from "path";
|
|
24448
24773
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
24449
24774
|
const config = await readConfig();
|
|
24450
24775
|
const root = syntaurRoot();
|
|
24451
|
-
const dbPath =
|
|
24776
|
+
const dbPath = resolve55(root, "syntaur.db");
|
|
24452
24777
|
let db5 = null;
|
|
24453
24778
|
let dbError = null;
|
|
24454
24779
|
if (await fileExists(dbPath)) {
|
|
@@ -24482,8 +24807,8 @@ function closeCheckContext(ctx) {
|
|
|
24482
24807
|
// src/utils/doctor/checks/env.ts
|
|
24483
24808
|
init_fs();
|
|
24484
24809
|
init_paths();
|
|
24485
|
-
import { resolve as
|
|
24486
|
-
import { readFile as
|
|
24810
|
+
import { resolve as resolve56, isAbsolute as isAbsolute8 } from "path";
|
|
24811
|
+
import { readFile as readFile36, stat as stat3 } from "fs/promises";
|
|
24487
24812
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
24488
24813
|
import { dirname as dirname17, join as join10 } from "path";
|
|
24489
24814
|
var CATEGORY = "env";
|
|
@@ -24523,7 +24848,7 @@ var configValid = {
|
|
|
24523
24848
|
category: CATEGORY,
|
|
24524
24849
|
title: "~/.syntaur/config.md is valid",
|
|
24525
24850
|
async run(ctx) {
|
|
24526
|
-
const configPath =
|
|
24851
|
+
const configPath = resolve56(ctx.syntaurRoot, "config.md");
|
|
24527
24852
|
if (!await fileExists(configPath)) {
|
|
24528
24853
|
return {
|
|
24529
24854
|
id: this.id,
|
|
@@ -24540,7 +24865,7 @@ var configValid = {
|
|
|
24540
24865
|
autoFixable: false
|
|
24541
24866
|
};
|
|
24542
24867
|
}
|
|
24543
|
-
const content = await
|
|
24868
|
+
const content = await readFile36(configPath, "utf-8");
|
|
24544
24869
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
24545
24870
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
24546
24871
|
return {
|
|
@@ -24820,7 +25145,7 @@ async function readLocalPkg() {
|
|
|
24820
25145
|
for (let i = 0; i < 6; i++) {
|
|
24821
25146
|
const candidate = join10(dir, "package.json");
|
|
24822
25147
|
try {
|
|
24823
|
-
const text = await
|
|
25148
|
+
const text = await readFile36(candidate, "utf-8");
|
|
24824
25149
|
return JSON.parse(text);
|
|
24825
25150
|
} catch {
|
|
24826
25151
|
dir = dirname17(dir);
|
|
@@ -24872,7 +25197,7 @@ function versionGte(a, b) {
|
|
|
24872
25197
|
|
|
24873
25198
|
// src/utils/doctor/checks/structure.ts
|
|
24874
25199
|
init_fs();
|
|
24875
|
-
import { resolve as
|
|
25200
|
+
import { resolve as resolve57 } from "path";
|
|
24876
25201
|
import { readdir as readdir18, stat as stat4 } from "fs/promises";
|
|
24877
25202
|
var CATEGORY2 = "structure";
|
|
24878
25203
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
@@ -24892,7 +25217,7 @@ var projectsDir = {
|
|
|
24892
25217
|
category: CATEGORY2,
|
|
24893
25218
|
title: "projects/ directory exists",
|
|
24894
25219
|
async run(ctx) {
|
|
24895
|
-
const p =
|
|
25220
|
+
const p = resolve57(ctx.syntaurRoot, "projects");
|
|
24896
25221
|
if (!await fileExists(p)) {
|
|
24897
25222
|
return {
|
|
24898
25223
|
id: this.id,
|
|
@@ -24917,7 +25242,7 @@ var playbooksDir2 = {
|
|
|
24917
25242
|
category: CATEGORY2,
|
|
24918
25243
|
title: "playbooks/ directory exists",
|
|
24919
25244
|
async run(ctx) {
|
|
24920
|
-
const p =
|
|
25245
|
+
const p = resolve57(ctx.syntaurRoot, "playbooks");
|
|
24921
25246
|
if (!await fileExists(p)) {
|
|
24922
25247
|
return {
|
|
24923
25248
|
id: this.id,
|
|
@@ -24942,7 +25267,7 @@ var todosDirValid = {
|
|
|
24942
25267
|
category: CATEGORY2,
|
|
24943
25268
|
title: "todos/ directory is readable (if present)",
|
|
24944
25269
|
async run(ctx) {
|
|
24945
|
-
const p =
|
|
25270
|
+
const p = resolve57(ctx.syntaurRoot, "todos");
|
|
24946
25271
|
if (!await fileExists(p)) {
|
|
24947
25272
|
return {
|
|
24948
25273
|
id: this.id,
|
|
@@ -24973,7 +25298,7 @@ var serversDirValid = {
|
|
|
24973
25298
|
category: CATEGORY2,
|
|
24974
25299
|
title: "servers/ directory is readable (if present)",
|
|
24975
25300
|
async run(ctx) {
|
|
24976
|
-
const p =
|
|
25301
|
+
const p = resolve57(ctx.syntaurRoot, "servers");
|
|
24977
25302
|
if (!await fileExists(p)) {
|
|
24978
25303
|
return {
|
|
24979
25304
|
id: this.id,
|
|
@@ -25018,7 +25343,7 @@ var knownFilesRecognized = {
|
|
|
25018
25343
|
title: this.title,
|
|
25019
25344
|
status: "warn",
|
|
25020
25345
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
25021
|
-
affected: unexpected.map((n) =>
|
|
25346
|
+
affected: unexpected.map((n) => resolve57(ctx.syntaurRoot, n)),
|
|
25022
25347
|
remediation: {
|
|
25023
25348
|
kind: "manual",
|
|
25024
25349
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -25047,7 +25372,7 @@ function pass2(check) {
|
|
|
25047
25372
|
|
|
25048
25373
|
// src/utils/doctor/checks/project.ts
|
|
25049
25374
|
init_fs();
|
|
25050
|
-
import { resolve as
|
|
25375
|
+
import { resolve as resolve58 } from "path";
|
|
25051
25376
|
import { readdir as readdir19, stat as stat5 } from "fs/promises";
|
|
25052
25377
|
var CATEGORY3 = "project";
|
|
25053
25378
|
var REQUIRED_PROJECT_FILES = [
|
|
@@ -25077,10 +25402,10 @@ async function listProjects2(ctx) {
|
|
|
25077
25402
|
for (const e of entries) {
|
|
25078
25403
|
if (!e.isDirectory()) continue;
|
|
25079
25404
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
25080
|
-
const projectDir =
|
|
25405
|
+
const projectDir = resolve58(dir, e.name);
|
|
25081
25406
|
let looksLikeProject = false;
|
|
25082
25407
|
for (const marker of PROJECT_MARKERS) {
|
|
25083
|
-
if (await fileExists(
|
|
25408
|
+
if (await fileExists(resolve58(projectDir, marker))) {
|
|
25084
25409
|
looksLikeProject = true;
|
|
25085
25410
|
break;
|
|
25086
25411
|
}
|
|
@@ -25099,7 +25424,7 @@ var requiredFiles = {
|
|
|
25099
25424
|
for (const projectDir of projects) {
|
|
25100
25425
|
const missing = [];
|
|
25101
25426
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
25102
|
-
const p =
|
|
25427
|
+
const p = resolve58(projectDir, rel);
|
|
25103
25428
|
if (!await fileExists(p)) missing.push(rel);
|
|
25104
25429
|
}
|
|
25105
25430
|
if (missing.length === 0) continue;
|
|
@@ -25109,7 +25434,7 @@ var requiredFiles = {
|
|
|
25109
25434
|
title: this.title,
|
|
25110
25435
|
status: "error",
|
|
25111
25436
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
25112
|
-
affected: missing.map((m) =>
|
|
25437
|
+
affected: missing.map((m) => resolve58(projectDir, m)),
|
|
25113
25438
|
remediation: {
|
|
25114
25439
|
kind: "manual",
|
|
25115
25440
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -25132,7 +25457,7 @@ var manifestStale = {
|
|
|
25132
25457
|
const projects = await listProjects2(ctx);
|
|
25133
25458
|
const results = [];
|
|
25134
25459
|
for (const projectDir of projects) {
|
|
25135
|
-
const manifestPath =
|
|
25460
|
+
const manifestPath = resolve58(projectDir, "manifest.md");
|
|
25136
25461
|
if (!await fileExists(manifestPath)) continue;
|
|
25137
25462
|
const manifestMtime = (await stat5(manifestPath)).mtimeMs;
|
|
25138
25463
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
@@ -25181,7 +25506,7 @@ var orphanFiles = {
|
|
|
25181
25506
|
title: this.title,
|
|
25182
25507
|
status: "warn",
|
|
25183
25508
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
25184
|
-
affected: orphans.map((o) =>
|
|
25509
|
+
affected: orphans.map((o) => resolve58(projectDir, o)),
|
|
25185
25510
|
autoFixable: false
|
|
25186
25511
|
});
|
|
25187
25512
|
}
|
|
@@ -25191,7 +25516,7 @@ var orphanFiles = {
|
|
|
25191
25516
|
};
|
|
25192
25517
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
25193
25518
|
async function newestAssignmentMtime(projectDir) {
|
|
25194
|
-
const assignmentsRoot =
|
|
25519
|
+
const assignmentsRoot = resolve58(projectDir, "assignments");
|
|
25195
25520
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
25196
25521
|
let newest = 0;
|
|
25197
25522
|
let entries;
|
|
@@ -25202,7 +25527,7 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
25202
25527
|
}
|
|
25203
25528
|
for (const e of entries) {
|
|
25204
25529
|
if (!e.isDirectory()) continue;
|
|
25205
|
-
const assignmentMd =
|
|
25530
|
+
const assignmentMd = resolve58(assignmentsRoot, e.name, "assignment.md");
|
|
25206
25531
|
try {
|
|
25207
25532
|
const s = await stat5(assignmentMd);
|
|
25208
25533
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
@@ -25226,8 +25551,8 @@ init_fs();
|
|
|
25226
25551
|
init_parser();
|
|
25227
25552
|
init_types();
|
|
25228
25553
|
init_paths();
|
|
25229
|
-
import { resolve as
|
|
25230
|
-
import { readFile as
|
|
25554
|
+
import { resolve as resolve59 } from "path";
|
|
25555
|
+
import { readFile as readFile37, readdir as readdir20 } from "fs/promises";
|
|
25231
25556
|
var CATEGORY4 = "assignment";
|
|
25232
25557
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
25233
25558
|
var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -25321,7 +25646,7 @@ var invalidStatus = {
|
|
|
25321
25646
|
const allowed = configuredStatuses(ctx);
|
|
25322
25647
|
const results = [];
|
|
25323
25648
|
for (const a of withAssignmentMd) {
|
|
25324
|
-
const path =
|
|
25649
|
+
const path = resolve59(a.assignmentDir, "assignment.md");
|
|
25325
25650
|
const parsed = await parseSafe2(path);
|
|
25326
25651
|
if (!parsed) continue;
|
|
25327
25652
|
if (!allowed.has(parsed.status)) {
|
|
@@ -25354,7 +25679,7 @@ var workspaceMissing = {
|
|
|
25354
25679
|
const terminal = terminalStatuses(ctx);
|
|
25355
25680
|
const results = [];
|
|
25356
25681
|
for (const a of withAssignmentMd) {
|
|
25357
|
-
const path =
|
|
25682
|
+
const path = resolve59(a.assignmentDir, "assignment.md");
|
|
25358
25683
|
const parsed = await parseSafe2(path);
|
|
25359
25684
|
if (!parsed) continue;
|
|
25360
25685
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -25401,12 +25726,12 @@ var requiredFilesByStatus = {
|
|
|
25401
25726
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
25402
25727
|
const results = [];
|
|
25403
25728
|
for (const a of withAssignmentMd) {
|
|
25404
|
-
const assignmentPath =
|
|
25729
|
+
const assignmentPath = resolve59(a.assignmentDir, "assignment.md");
|
|
25405
25730
|
const parsed = await parseSafe2(assignmentPath);
|
|
25406
25731
|
if (!parsed) continue;
|
|
25407
25732
|
const missing = [];
|
|
25408
25733
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
25409
|
-
const handoffPath =
|
|
25734
|
+
const handoffPath = resolve59(a.assignmentDir, "handoff.md");
|
|
25410
25735
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
25411
25736
|
}
|
|
25412
25737
|
if (missing.length === 0) continue;
|
|
@@ -25416,7 +25741,7 @@ var requiredFilesByStatus = {
|
|
|
25416
25741
|
title: this.title,
|
|
25417
25742
|
status: "warn",
|
|
25418
25743
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
25419
|
-
affected: missing.map((m) =>
|
|
25744
|
+
affected: missing.map((m) => resolve59(a.assignmentDir, m)),
|
|
25420
25745
|
remediation: {
|
|
25421
25746
|
kind: "manual",
|
|
25422
25747
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -25439,7 +25764,7 @@ var companionFilesScaffolded = {
|
|
|
25439
25764
|
for (const a of withAssignmentMd) {
|
|
25440
25765
|
const missing = [];
|
|
25441
25766
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
25442
|
-
if (!await fileExists(
|
|
25767
|
+
if (!await fileExists(resolve59(a.assignmentDir, filename))) {
|
|
25443
25768
|
missing.push(filename);
|
|
25444
25769
|
}
|
|
25445
25770
|
}
|
|
@@ -25451,7 +25776,7 @@ var companionFilesScaffolded = {
|
|
|
25451
25776
|
title: this.title,
|
|
25452
25777
|
status: "warn",
|
|
25453
25778
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
25454
|
-
affected: missing.map((m) =>
|
|
25779
|
+
affected: missing.map((m) => resolve59(a.assignmentDir, m)),
|
|
25455
25780
|
remediation: {
|
|
25456
25781
|
kind: "manual",
|
|
25457
25782
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -25484,7 +25809,7 @@ var typeDefinition = {
|
|
|
25484
25809
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
25485
25810
|
const results = [];
|
|
25486
25811
|
for (const a of withAssignmentMd) {
|
|
25487
|
-
const path =
|
|
25812
|
+
const path = resolve59(a.assignmentDir, "assignment.md");
|
|
25488
25813
|
const parsed = await parseSafe2(path);
|
|
25489
25814
|
if (!parsed) continue;
|
|
25490
25815
|
if (!parsed.type) continue;
|
|
@@ -25518,7 +25843,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
25518
25843
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
25519
25844
|
const results = [];
|
|
25520
25845
|
for (const a of withAssignmentMd) {
|
|
25521
|
-
const path =
|
|
25846
|
+
const path = resolve59(a.assignmentDir, "assignment.md");
|
|
25522
25847
|
const parsed = await parseSafe2(path);
|
|
25523
25848
|
if (!parsed) continue;
|
|
25524
25849
|
if (a.standalone) {
|
|
@@ -25569,13 +25894,13 @@ var draftMissingObjective = {
|
|
|
25569
25894
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
25570
25895
|
const results = [];
|
|
25571
25896
|
for (const a of withAssignmentMd) {
|
|
25572
|
-
const path =
|
|
25897
|
+
const path = resolve59(a.assignmentDir, "assignment.md");
|
|
25573
25898
|
const parsed = await parseSafe2(path);
|
|
25574
25899
|
if (!parsed) continue;
|
|
25575
25900
|
if (parsed.status !== "draft") continue;
|
|
25576
25901
|
let raw;
|
|
25577
25902
|
try {
|
|
25578
|
-
raw = await
|
|
25903
|
+
raw = await readFile37(path, "utf-8");
|
|
25579
25904
|
} catch {
|
|
25580
25905
|
continue;
|
|
25581
25906
|
}
|
|
@@ -25608,7 +25933,7 @@ var readyToImplementMissingPlan = {
|
|
|
25608
25933
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
25609
25934
|
const results = [];
|
|
25610
25935
|
for (const a of withAssignmentMd) {
|
|
25611
|
-
const path =
|
|
25936
|
+
const path = resolve59(a.assignmentDir, "assignment.md");
|
|
25612
25937
|
const parsed = await parseSafe2(path);
|
|
25613
25938
|
if (!parsed) continue;
|
|
25614
25939
|
if (parsed.status !== "ready_to_implement") continue;
|
|
@@ -25617,7 +25942,7 @@ var readyToImplementMissingPlan = {
|
|
|
25617
25942
|
let hasPlanContent = false;
|
|
25618
25943
|
for (const f of planFiles) {
|
|
25619
25944
|
try {
|
|
25620
|
-
const c2 = await
|
|
25945
|
+
const c2 = await readFile37(resolve59(a.assignmentDir, f), "utf-8");
|
|
25621
25946
|
if (c2.trim().length > 0) {
|
|
25622
25947
|
hasPlanContent = true;
|
|
25623
25948
|
break;
|
|
@@ -25633,7 +25958,7 @@ var readyToImplementMissingPlan = {
|
|
|
25633
25958
|
title: this.title,
|
|
25634
25959
|
status: "warn",
|
|
25635
25960
|
detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
|
|
25636
|
-
affected: [
|
|
25961
|
+
affected: [resolve59(a.assignmentDir, "plan.md")],
|
|
25637
25962
|
remediation: {
|
|
25638
25963
|
kind: "manual",
|
|
25639
25964
|
suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
|
|
@@ -25660,7 +25985,7 @@ var assignmentChecks = [
|
|
|
25660
25985
|
];
|
|
25661
25986
|
async function parseSafe2(path) {
|
|
25662
25987
|
try {
|
|
25663
|
-
const content = await
|
|
25988
|
+
const content = await readFile37(path, "utf-8");
|
|
25664
25989
|
return parseAssignmentFull(content);
|
|
25665
25990
|
} catch {
|
|
25666
25991
|
return null;
|
|
@@ -25679,7 +26004,7 @@ function pass4(check, detail) {
|
|
|
25679
26004
|
|
|
25680
26005
|
// src/utils/doctor/checks/dashboard.ts
|
|
25681
26006
|
init_fs();
|
|
25682
|
-
import { resolve as
|
|
26007
|
+
import { resolve as resolve60 } from "path";
|
|
25683
26008
|
var CATEGORY5 = "dashboard";
|
|
25684
26009
|
var dbReachable = {
|
|
25685
26010
|
id: "dashboard.db-reachable",
|
|
@@ -25693,7 +26018,7 @@ var dbReachable = {
|
|
|
25693
26018
|
title: this.title,
|
|
25694
26019
|
status: "error",
|
|
25695
26020
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
25696
|
-
affected: [
|
|
26021
|
+
affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
|
|
25697
26022
|
remediation: {
|
|
25698
26023
|
kind: "manual",
|
|
25699
26024
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -25711,7 +26036,7 @@ var dbReachable = {
|
|
|
25711
26036
|
title: this.title,
|
|
25712
26037
|
status: "error",
|
|
25713
26038
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
25714
|
-
affected: [
|
|
26039
|
+
affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
|
|
25715
26040
|
autoFixable: false
|
|
25716
26041
|
};
|
|
25717
26042
|
}
|
|
@@ -25723,7 +26048,7 @@ var dbReachable = {
|
|
|
25723
26048
|
title: this.title,
|
|
25724
26049
|
status: "error",
|
|
25725
26050
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
25726
|
-
affected: [
|
|
26051
|
+
affected: [resolve60(ctx.syntaurRoot, "syntaur.db")],
|
|
25727
26052
|
autoFixable: false
|
|
25728
26053
|
};
|
|
25729
26054
|
}
|
|
@@ -25749,7 +26074,7 @@ var ghostSessions = {
|
|
|
25749
26074
|
const results = [];
|
|
25750
26075
|
for (const row of rows) {
|
|
25751
26076
|
if (!row.project_slug) continue;
|
|
25752
|
-
const projectPath =
|
|
26077
|
+
const projectPath = resolve60(projectsDir2, row.project_slug, "project.md");
|
|
25753
26078
|
if (!await fileExists(projectPath)) {
|
|
25754
26079
|
results.push({
|
|
25755
26080
|
id: this.id,
|
|
@@ -25768,7 +26093,7 @@ var ghostSessions = {
|
|
|
25768
26093
|
continue;
|
|
25769
26094
|
}
|
|
25770
26095
|
if (row.assignment_slug) {
|
|
25771
|
-
const assignmentPath =
|
|
26096
|
+
const assignmentPath = resolve60(
|
|
25772
26097
|
projectsDir2,
|
|
25773
26098
|
row.project_slug,
|
|
25774
26099
|
"assignments",
|
|
@@ -25820,9 +26145,9 @@ function skipped(check, reason) {
|
|
|
25820
26145
|
|
|
25821
26146
|
// src/utils/doctor/checks/integrations.ts
|
|
25822
26147
|
init_fs();
|
|
25823
|
-
import { resolve as
|
|
25824
|
-
import { readdir as readdir21, readFile as
|
|
25825
|
-
import { homedir as
|
|
26148
|
+
import { resolve as resolve61, dirname as dirname18, basename as basename5 } from "path";
|
|
26149
|
+
import { readdir as readdir21, readFile as readFile38 } from "fs/promises";
|
|
26150
|
+
import { homedir as homedir10 } from "os";
|
|
25826
26151
|
var CATEGORY6 = "integrations";
|
|
25827
26152
|
var claudePluginLinked = {
|
|
25828
26153
|
id: "integrations.claude-plugin-linked",
|
|
@@ -25903,10 +26228,10 @@ var backupConfigured = {
|
|
|
25903
26228
|
}
|
|
25904
26229
|
};
|
|
25905
26230
|
async function readKnownMarketplaces() {
|
|
25906
|
-
const path =
|
|
26231
|
+
const path = resolve61(homedir10(), ".claude", "plugins", "known_marketplaces.json");
|
|
25907
26232
|
if (!await fileExists(path)) return {};
|
|
25908
26233
|
try {
|
|
25909
|
-
const raw = await
|
|
26234
|
+
const raw = await readFile38(path, "utf-8");
|
|
25910
26235
|
return JSON.parse(raw);
|
|
25911
26236
|
} catch {
|
|
25912
26237
|
return {};
|
|
@@ -25940,7 +26265,7 @@ var claudeMarketplaceRegistered = {
|
|
|
25940
26265
|
};
|
|
25941
26266
|
}
|
|
25942
26267
|
const marketplaceRoot = dirname18(pluginsParent);
|
|
25943
|
-
const marketplaceManifest =
|
|
26268
|
+
const marketplaceManifest = resolve61(marketplaceRoot, ".claude-plugin", "marketplace.json");
|
|
25944
26269
|
if (!await fileExists(marketplaceManifest)) {
|
|
25945
26270
|
return {
|
|
25946
26271
|
id: this.id,
|
|
@@ -25959,7 +26284,7 @@ var claudeMarketplaceRegistered = {
|
|
|
25959
26284
|
}
|
|
25960
26285
|
let parsed = {};
|
|
25961
26286
|
try {
|
|
25962
|
-
parsed = JSON.parse(await
|
|
26287
|
+
parsed = JSON.parse(await readFile38(marketplaceManifest, "utf-8"));
|
|
25963
26288
|
} catch {
|
|
25964
26289
|
return {
|
|
25965
26290
|
id: this.id,
|
|
@@ -25991,7 +26316,7 @@ var claudeMarketplaceRegistered = {
|
|
|
25991
26316
|
title: this.title,
|
|
25992
26317
|
status: "error",
|
|
25993
26318
|
detail: issues.join("; "),
|
|
25994
|
-
affected: [marketplaceManifest,
|
|
26319
|
+
affected: [marketplaceManifest, resolve61(homedir10(), ".claude", "plugins", "known_marketplaces.json")],
|
|
25995
26320
|
remediation: {
|
|
25996
26321
|
kind: "manual",
|
|
25997
26322
|
suggestion: "Re-run install-plugin to ensure both files are in sync.",
|
|
@@ -26031,8 +26356,8 @@ function skipped2(check, reason) {
|
|
|
26031
26356
|
init_fs();
|
|
26032
26357
|
init_parser();
|
|
26033
26358
|
init_types();
|
|
26034
|
-
import { resolve as
|
|
26035
|
-
import { readFile as
|
|
26359
|
+
import { resolve as resolve62 } from "path";
|
|
26360
|
+
import { readFile as readFile39 } from "fs/promises";
|
|
26036
26361
|
var CATEGORY7 = "workspace";
|
|
26037
26362
|
var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
26038
26363
|
var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
|
|
@@ -26053,12 +26378,12 @@ function isStandaloneSession(ctx) {
|
|
|
26053
26378
|
return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
|
|
26054
26379
|
}
|
|
26055
26380
|
async function loadContext(ctx) {
|
|
26056
|
-
const path =
|
|
26381
|
+
const path = resolve62(ctx.cwd, ".syntaur", "context.json");
|
|
26057
26382
|
if (!await fileExists(path)) {
|
|
26058
26383
|
return { data: null, path, exists: false, parseError: null };
|
|
26059
26384
|
}
|
|
26060
26385
|
try {
|
|
26061
|
-
const raw = await
|
|
26386
|
+
const raw = await readFile39(path, "utf-8");
|
|
26062
26387
|
return { data: JSON.parse(raw), path, exists: true, parseError: null };
|
|
26063
26388
|
} catch (err2) {
|
|
26064
26389
|
return {
|
|
@@ -26152,7 +26477,7 @@ var contextAssignmentResolves = {
|
|
|
26152
26477
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
26153
26478
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
|
|
26154
26479
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
26155
|
-
const assignmentMd =
|
|
26480
|
+
const assignmentMd = resolve62(data.assignmentDir, "assignment.md");
|
|
26156
26481
|
if (!await fileExists(assignmentMd)) {
|
|
26157
26482
|
return {
|
|
26158
26483
|
id: this.id,
|
|
@@ -26182,10 +26507,10 @@ var contextTerminal = {
|
|
|
26182
26507
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
26183
26508
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
|
|
26184
26509
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
26185
|
-
const assignmentMd =
|
|
26510
|
+
const assignmentMd = resolve62(data.assignmentDir, "assignment.md");
|
|
26186
26511
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
26187
26512
|
try {
|
|
26188
|
-
const content = await
|
|
26513
|
+
const content = await readFile39(assignmentMd, "utf-8");
|
|
26189
26514
|
const parsed = parseAssignmentFull(content);
|
|
26190
26515
|
const terminal = terminalStatuses2(ctx);
|
|
26191
26516
|
if (terminal.has(parsed.status)) {
|
|
@@ -26241,7 +26566,7 @@ function skipped3(check, reason) {
|
|
|
26241
26566
|
init_config2();
|
|
26242
26567
|
import { isAbsolute as isAbsolute9 } from "path";
|
|
26243
26568
|
import { access as access2, constants as fsConstants } from "fs/promises";
|
|
26244
|
-
import { spawnSync as
|
|
26569
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
26245
26570
|
var CATEGORY8 = "agents";
|
|
26246
26571
|
var agentsResolvable = {
|
|
26247
26572
|
id: "agents.commands-resolvable",
|
|
@@ -26305,7 +26630,7 @@ async function checkAgent(agent) {
|
|
|
26305
26630
|
};
|
|
26306
26631
|
}
|
|
26307
26632
|
}
|
|
26308
|
-
const result =
|
|
26633
|
+
const result = spawnSync8("which", [agent.command], { encoding: "utf-8" });
|
|
26309
26634
|
if (result.status === 0 && result.stdout.trim().length > 0) {
|
|
26310
26635
|
return {
|
|
26311
26636
|
...base,
|
|
@@ -26330,16 +26655,16 @@ var agentChecks = [agentsResolvable];
|
|
|
26330
26655
|
|
|
26331
26656
|
// src/utils/doctor/checks/terminal.ts
|
|
26332
26657
|
init_config2();
|
|
26333
|
-
import { spawnSync as
|
|
26334
|
-
import { readFile as
|
|
26335
|
-
import { resolve as
|
|
26658
|
+
import { spawnSync as spawnSync9 } from "child_process";
|
|
26659
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
26660
|
+
import { resolve as resolve63 } from "path";
|
|
26336
26661
|
init_paths();
|
|
26337
26662
|
init_fs();
|
|
26338
26663
|
var CATEGORY9 = "terminal";
|
|
26339
26664
|
async function readRawTerminalKey() {
|
|
26340
|
-
const configPath =
|
|
26665
|
+
const configPath = resolve63(syntaurRoot(), "config.md");
|
|
26341
26666
|
if (!await fileExists(configPath)) return null;
|
|
26342
|
-
const content = await
|
|
26667
|
+
const content = await readFile40(configPath, "utf-8");
|
|
26343
26668
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
26344
26669
|
if (!fmMatch) return null;
|
|
26345
26670
|
const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
|
|
@@ -26452,7 +26777,7 @@ var kittyRemoteControl = {
|
|
|
26452
26777
|
autoFixable: false
|
|
26453
26778
|
};
|
|
26454
26779
|
}
|
|
26455
|
-
const result =
|
|
26780
|
+
const result = spawnSync9("kitty", ["@", "ls"], {
|
|
26456
26781
|
encoding: "utf-8",
|
|
26457
26782
|
timeout: 2e3
|
|
26458
26783
|
});
|
|
@@ -26489,13 +26814,13 @@ var terminalChecks = [
|
|
|
26489
26814
|
|
|
26490
26815
|
// src/utils/doctor/checks/skills.ts
|
|
26491
26816
|
init_fs();
|
|
26492
|
-
import { resolve as
|
|
26493
|
-
import { readdir as readdir22, readFile as
|
|
26494
|
-
import { homedir as
|
|
26817
|
+
import { resolve as resolve64, join as join11 } from "path";
|
|
26818
|
+
import { readdir as readdir22, readFile as readFile41, lstat as lstat4 } from "fs/promises";
|
|
26819
|
+
import { homedir as homedir11 } from "os";
|
|
26495
26820
|
var CATEGORY10 = "skills";
|
|
26496
26821
|
var skillTargets = [
|
|
26497
|
-
{ agent: "claude", dir:
|
|
26498
|
-
{ agent: "codex", dir:
|
|
26822
|
+
{ agent: "claude", dir: resolve64(homedir11(), ".claude", "skills"), label: "~/.claude/skills" },
|
|
26823
|
+
{ agent: "codex", dir: resolve64(homedir11(), ".codex", "skills"), label: "~/.codex/skills" }
|
|
26499
26824
|
];
|
|
26500
26825
|
var skillsDedupCheck = {
|
|
26501
26826
|
id: "skills.dedup",
|
|
@@ -26519,7 +26844,7 @@ var skillsDedupCheck = {
|
|
|
26519
26844
|
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
26520
26845
|
const skillMd = join11(dir, entry.name, "SKILL.md");
|
|
26521
26846
|
if (!await fileExists(skillMd)) continue;
|
|
26522
|
-
const content = await
|
|
26847
|
+
const content = await readFile41(skillMd, "utf-8").catch(() => "");
|
|
26523
26848
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
26524
26849
|
if (!match || match[1] !== entry.name) continue;
|
|
26525
26850
|
let isSymlink2 = false;
|
|
@@ -26567,14 +26892,101 @@ var skillsDedupCheck = {
|
|
|
26567
26892
|
};
|
|
26568
26893
|
var skillsChecks = [skillsDedupCheck];
|
|
26569
26894
|
|
|
26895
|
+
// src/utils/doctor/checks/cross-agent.ts
|
|
26896
|
+
init_fs();
|
|
26897
|
+
import { join as join12, resolve as resolve65 } from "path";
|
|
26898
|
+
var CATEGORY11 = "cross-agent";
|
|
26899
|
+
async function countSyntaurSkills(dir) {
|
|
26900
|
+
if (!await fileExists(dir)) return 0;
|
|
26901
|
+
let n = 0;
|
|
26902
|
+
for (const skill of KNOWN_SKILLS) {
|
|
26903
|
+
if (await fileExists(join12(dir, skill, "SKILL.md"))) n++;
|
|
26904
|
+
}
|
|
26905
|
+
return n;
|
|
26906
|
+
}
|
|
26907
|
+
var crossAgentSkillsCheck = {
|
|
26908
|
+
id: "cross-agent.skills",
|
|
26909
|
+
category: CATEGORY11,
|
|
26910
|
+
title: "Cross-agent targets have Syntaur skills + protocol files",
|
|
26911
|
+
async run(ctx) {
|
|
26912
|
+
const installed = ctx.config.integrations.installedAgents ?? {};
|
|
26913
|
+
const total = KNOWN_SKILLS.length;
|
|
26914
|
+
const lines = [];
|
|
26915
|
+
const problems = [];
|
|
26916
|
+
const affected = [];
|
|
26917
|
+
let considered = 0;
|
|
26918
|
+
for (const t of AGENT_TARGETS) {
|
|
26919
|
+
if (t.nativePlugin) continue;
|
|
26920
|
+
const dir = t.skillsDir?.global;
|
|
26921
|
+
if (!dir) continue;
|
|
26922
|
+
const recorded = Boolean(installed[t.id]);
|
|
26923
|
+
const detected = await t.detect();
|
|
26924
|
+
if (!recorded && !detected) continue;
|
|
26925
|
+
considered++;
|
|
26926
|
+
const present = await countSyntaurSkills(dir);
|
|
26927
|
+
lines.push(`${t.displayName}: ${present}/${total} skills (${dir})`);
|
|
26928
|
+
if (recorded && present < total) {
|
|
26929
|
+
problems.push(
|
|
26930
|
+
`${t.displayName}: ${present === 0 ? "no Syntaur skills" : `incomplete skills (${present}/${total})`}`
|
|
26931
|
+
);
|
|
26932
|
+
affected.push(dir);
|
|
26933
|
+
}
|
|
26934
|
+
if (recorded && t.instructions) {
|
|
26935
|
+
for (const f of t.instructions.files) {
|
|
26936
|
+
const p = resolve65(ctx.cwd, f.path);
|
|
26937
|
+
if (!await fileExists(p)) {
|
|
26938
|
+
problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
|
|
26939
|
+
affected.push(p);
|
|
26940
|
+
}
|
|
26941
|
+
}
|
|
26942
|
+
}
|
|
26943
|
+
}
|
|
26944
|
+
if (considered === 0) {
|
|
26945
|
+
return {
|
|
26946
|
+
id: this.id,
|
|
26947
|
+
category: this.category,
|
|
26948
|
+
title: this.title,
|
|
26949
|
+
status: "skipped",
|
|
26950
|
+
detail: "No cross-agent targets detected or recorded.",
|
|
26951
|
+
autoFixable: false
|
|
26952
|
+
};
|
|
26953
|
+
}
|
|
26954
|
+
if (problems.length > 0) {
|
|
26955
|
+
return {
|
|
26956
|
+
id: this.id,
|
|
26957
|
+
category: this.category,
|
|
26958
|
+
title: this.title,
|
|
26959
|
+
status: "warn",
|
|
26960
|
+
detail: `${problems.join("; ")}. (${lines.join("; ")})`,
|
|
26961
|
+
affected,
|
|
26962
|
+
remediation: {
|
|
26963
|
+
kind: "manual",
|
|
26964
|
+
suggestion: "Re-run `syntaur setup --target <id>` (from the assignment workspace, to also write protocol files) to complete the install.",
|
|
26965
|
+
command: null
|
|
26966
|
+
},
|
|
26967
|
+
autoFixable: false
|
|
26968
|
+
};
|
|
26969
|
+
}
|
|
26970
|
+
return {
|
|
26971
|
+
id: this.id,
|
|
26972
|
+
category: this.category,
|
|
26973
|
+
title: this.title,
|
|
26974
|
+
status: "pass",
|
|
26975
|
+
detail: lines.join("; "),
|
|
26976
|
+
autoFixable: false
|
|
26977
|
+
};
|
|
26978
|
+
}
|
|
26979
|
+
};
|
|
26980
|
+
var crossAgentChecks = [crossAgentSkillsCheck];
|
|
26981
|
+
|
|
26570
26982
|
// src/utils/doctor/checks/bundles.ts
|
|
26571
26983
|
init_fs();
|
|
26572
26984
|
init_paths();
|
|
26573
|
-
import { resolve as
|
|
26985
|
+
import { resolve as resolve66 } from "path";
|
|
26574
26986
|
import { readdir as readdir23 } from "fs/promises";
|
|
26575
|
-
import { spawnSync as
|
|
26987
|
+
import { spawnSync as spawnSync10 } from "child_process";
|
|
26576
26988
|
init_parser2();
|
|
26577
|
-
var
|
|
26989
|
+
var CATEGORY12 = "bundles";
|
|
26578
26990
|
async function listScopes(ctx) {
|
|
26579
26991
|
const out = [];
|
|
26580
26992
|
const td = todosDir();
|
|
@@ -26600,7 +27012,7 @@ async function listScopes(ctx) {
|
|
|
26600
27012
|
if (!e.isDirectory()) continue;
|
|
26601
27013
|
const slug = e.name;
|
|
26602
27014
|
if (typeof slug !== "string" || slug.startsWith(".")) continue;
|
|
26603
|
-
const projectMd =
|
|
27015
|
+
const projectMd = resolve66(ctx.config.defaultProjectDir, slug, "project.md");
|
|
26604
27016
|
if (!await fileExists(projectMd)) continue;
|
|
26605
27017
|
out.push({
|
|
26606
27018
|
scopeLabel: `project:${slug}`,
|
|
@@ -26655,7 +27067,7 @@ async function gatherBundlesByScope(scopes) {
|
|
|
26655
27067
|
}
|
|
26656
27068
|
var orphanBundleId = {
|
|
26657
27069
|
id: "bundles.orphan-bundleid",
|
|
26658
|
-
category:
|
|
27070
|
+
category: CATEGORY12,
|
|
26659
27071
|
title: "Every todo with a bundleId points at an existing bundle in the same scope",
|
|
26660
27072
|
async run(ctx) {
|
|
26661
27073
|
const scopes = await listScopes(ctx);
|
|
@@ -26678,7 +27090,7 @@ var orphanBundleId = {
|
|
|
26678
27090
|
};
|
|
26679
27091
|
var missingMembers = {
|
|
26680
27092
|
id: "bundles.missing-members",
|
|
26681
|
-
category:
|
|
27093
|
+
category: CATEGORY12,
|
|
26682
27094
|
title: "Every bundle member exists in the bundle's scope checklist",
|
|
26683
27095
|
async run(ctx) {
|
|
26684
27096
|
const scopes = await listScopes(ctx);
|
|
@@ -26697,7 +27109,7 @@ var missingMembers = {
|
|
|
26697
27109
|
};
|
|
26698
27110
|
var scopeMismatch = {
|
|
26699
27111
|
id: "bundles.scope-mismatch",
|
|
26700
|
-
category:
|
|
27112
|
+
category: CATEGORY12,
|
|
26701
27113
|
title: "Every bundle member's bundleId matches the bundle id",
|
|
26702
27114
|
async run(ctx) {
|
|
26703
27115
|
const scopes = await listScopes(ctx);
|
|
@@ -26715,7 +27127,7 @@ var scopeMismatch = {
|
|
|
26715
27127
|
};
|
|
26716
27128
|
var minMembers = {
|
|
26717
27129
|
id: "bundles.min-members",
|
|
26718
|
-
category:
|
|
27130
|
+
category: CATEGORY12,
|
|
26719
27131
|
title: "Every bundle has at least 2 members",
|
|
26720
27132
|
async run(ctx) {
|
|
26721
27133
|
const scopes = await listScopes(ctx);
|
|
@@ -26731,7 +27143,7 @@ var minMembers = {
|
|
|
26731
27143
|
};
|
|
26732
27144
|
var stalePlanDir = {
|
|
26733
27145
|
id: "bundles.stale-plan-dir",
|
|
26734
|
-
category:
|
|
27146
|
+
category: CATEGORY12,
|
|
26735
27147
|
title: "Every bundle's persisted planDir still exists on disk",
|
|
26736
27148
|
async run(ctx) {
|
|
26737
27149
|
const scopes = await listScopes(ctx);
|
|
@@ -26755,7 +27167,7 @@ var stalePlanDir = {
|
|
|
26755
27167
|
};
|
|
26756
27168
|
var staleWorktree = {
|
|
26757
27169
|
id: "bundles.stale-worktree",
|
|
26758
|
-
category:
|
|
27170
|
+
category: CATEGORY12,
|
|
26759
27171
|
title: "Every bundle's persisted worktree still exists in the repo",
|
|
26760
27172
|
async run(ctx) {
|
|
26761
27173
|
const scopes = await listScopes(ctx);
|
|
@@ -26765,7 +27177,7 @@ var staleWorktree = {
|
|
|
26765
27177
|
if (bs.bundle.worktreePath === null || bs.bundle.repository === null) continue;
|
|
26766
27178
|
const onDisk = await fileExists(bs.bundle.worktreePath);
|
|
26767
27179
|
let gitKnowsIt = false;
|
|
26768
|
-
const gitOut =
|
|
27180
|
+
const gitOut = spawnSync10("git", ["-C", bs.bundle.repository, "worktree", "list", "--porcelain"], { encoding: "utf-8" });
|
|
26769
27181
|
if (gitOut.status === 0) {
|
|
26770
27182
|
gitKnowsIt = gitOut.stdout.split("\n").some((l) => l.trim() === `worktree ${bs.bundle.worktreePath}`);
|
|
26771
27183
|
}
|
|
@@ -26805,6 +27217,7 @@ function allChecks() {
|
|
|
26805
27217
|
...agentChecks,
|
|
26806
27218
|
...terminalChecks,
|
|
26807
27219
|
...skillsChecks,
|
|
27220
|
+
...crossAgentChecks,
|
|
26808
27221
|
...bundleChecks
|
|
26809
27222
|
];
|
|
26810
27223
|
}
|
|
@@ -26891,7 +27304,7 @@ async function readVersion() {
|
|
|
26891
27304
|
let dir = dirname19(here);
|
|
26892
27305
|
for (let i = 0; i < 6; i++) {
|
|
26893
27306
|
try {
|
|
26894
|
-
const raw = await
|
|
27307
|
+
const raw = await readFile42(join13(dir, "package.json"), "utf-8");
|
|
26895
27308
|
const parsed = JSON.parse(raw);
|
|
26896
27309
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
26897
27310
|
} catch {
|
|
@@ -26988,7 +27401,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
|
|
|
26988
27401
|
];
|
|
26989
27402
|
var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
26990
27403
|
async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
26991
|
-
const absolute = isAbsolute10(inputPath) ? inputPath :
|
|
27404
|
+
const absolute = isAbsolute10(inputPath) ? inputPath : resolve67(cwd, inputPath);
|
|
26992
27405
|
const errors = [];
|
|
26993
27406
|
const warnings = [];
|
|
26994
27407
|
if (!await fileExists(absolute)) {
|
|
@@ -27001,7 +27414,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
|
27001
27414
|
}
|
|
27002
27415
|
let content;
|
|
27003
27416
|
try {
|
|
27004
|
-
content = await
|
|
27417
|
+
content = await readFile43(absolute, "utf-8");
|
|
27005
27418
|
} catch (err2) {
|
|
27006
27419
|
return {
|
|
27007
27420
|
ok: false,
|
|
@@ -27325,8 +27738,8 @@ init_uuid();
|
|
|
27325
27738
|
init_timestamp();
|
|
27326
27739
|
init_assignment_resolver();
|
|
27327
27740
|
init_templates();
|
|
27328
|
-
import { resolve as
|
|
27329
|
-
import { readFile as
|
|
27741
|
+
import { resolve as resolve68 } from "path";
|
|
27742
|
+
import { readFile as readFile44 } from "fs/promises";
|
|
27330
27743
|
function shortId() {
|
|
27331
27744
|
return generateId().split("-")[0];
|
|
27332
27745
|
}
|
|
@@ -27356,7 +27769,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
27356
27769
|
if (!isValidSlug(target)) {
|
|
27357
27770
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
27358
27771
|
}
|
|
27359
|
-
assignmentDir =
|
|
27772
|
+
assignmentDir = resolve68(baseDir, options.project, "assignments", target);
|
|
27360
27773
|
assignmentRef = target;
|
|
27361
27774
|
} else {
|
|
27362
27775
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -27366,13 +27779,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
27366
27779
|
assignmentDir = resolved.assignmentDir;
|
|
27367
27780
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
27368
27781
|
}
|
|
27369
|
-
const commentsPath =
|
|
27782
|
+
const commentsPath = resolve68(assignmentDir, "comments.md");
|
|
27370
27783
|
const timestamp = nowTimestamp();
|
|
27371
27784
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
27372
27785
|
let currentContent;
|
|
27373
27786
|
let currentCount = 0;
|
|
27374
27787
|
if (await fileExists(commentsPath)) {
|
|
27375
|
-
currentContent = await
|
|
27788
|
+
currentContent = await readFile44(commentsPath, "utf-8");
|
|
27376
27789
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
27377
27790
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
27378
27791
|
} else {
|
|
@@ -27406,7 +27819,7 @@ ${entry}`;
|
|
|
27406
27819
|
}
|
|
27407
27820
|
|
|
27408
27821
|
// src/commands/capture.ts
|
|
27409
|
-
import { resolve as
|
|
27822
|
+
import { resolve as resolve72, relative as relative4, dirname as dirname20 } from "path";
|
|
27410
27823
|
import { copyFile as copyFile3, mkdir as mkdir9, realpath as realpath2, rm as rm12, stat as stat8, writeFile as writeFile14 } from "fs/promises";
|
|
27411
27824
|
import { existsSync as existsSync6 } from "fs";
|
|
27412
27825
|
|
|
@@ -27417,8 +27830,8 @@ init_config2();
|
|
|
27417
27830
|
init_slug();
|
|
27418
27831
|
init_assignment_resolver();
|
|
27419
27832
|
init_parser();
|
|
27420
|
-
import { resolve as
|
|
27421
|
-
import { readFile as
|
|
27833
|
+
import { resolve as resolve69 } from "path";
|
|
27834
|
+
import { readFile as readFile45 } from "fs/promises";
|
|
27422
27835
|
var AssignmentTargetError = class extends Error {
|
|
27423
27836
|
};
|
|
27424
27837
|
function classifyContext(ctx) {
|
|
@@ -27430,10 +27843,10 @@ function classifyContext(ctx) {
|
|
|
27430
27843
|
return "empty";
|
|
27431
27844
|
}
|
|
27432
27845
|
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
27433
|
-
const path =
|
|
27846
|
+
const path = resolve69(assignmentDir, "assignment.md");
|
|
27434
27847
|
if (!await fileExists(path)) return null;
|
|
27435
27848
|
try {
|
|
27436
|
-
const content = await
|
|
27849
|
+
const content = await readFile45(path, "utf-8");
|
|
27437
27850
|
const [fm] = extractFrontmatter(content);
|
|
27438
27851
|
return getField(fm, "id");
|
|
27439
27852
|
} catch {
|
|
@@ -27441,10 +27854,10 @@ async function readAssignmentFrontmatterId(assignmentDir) {
|
|
|
27441
27854
|
}
|
|
27442
27855
|
}
|
|
27443
27856
|
async function readContextJson(cwd) {
|
|
27444
|
-
const path =
|
|
27857
|
+
const path = resolve69(cwd, ".syntaur", "context.json");
|
|
27445
27858
|
if (!await fileExists(path)) return null;
|
|
27446
27859
|
try {
|
|
27447
|
-
const raw = await
|
|
27860
|
+
const raw = await readFile45(path, "utf-8");
|
|
27448
27861
|
return JSON.parse(raw);
|
|
27449
27862
|
} catch {
|
|
27450
27863
|
return null;
|
|
@@ -27465,15 +27878,15 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
27465
27878
|
if (!isValidSlug(input4)) {
|
|
27466
27879
|
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
27467
27880
|
}
|
|
27468
|
-
const projectDir =
|
|
27469
|
-
const projectMdPath =
|
|
27881
|
+
const projectDir = resolve69(baseDir, opts.project);
|
|
27882
|
+
const projectMdPath = resolve69(projectDir, "project.md");
|
|
27470
27883
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
27471
27884
|
throw new AssignmentTargetError(
|
|
27472
27885
|
`Project "${opts.project}" not found at ${projectDir}.`
|
|
27473
27886
|
);
|
|
27474
27887
|
}
|
|
27475
|
-
const assignmentDir =
|
|
27476
|
-
const assignmentMdPath2 =
|
|
27888
|
+
const assignmentDir = resolve69(projectDir, "assignments", input4);
|
|
27889
|
+
const assignmentMdPath2 = resolve69(assignmentDir, "assignment.md");
|
|
27477
27890
|
if (!await fileExists(assignmentMdPath2)) {
|
|
27478
27891
|
throw new AssignmentTargetError(
|
|
27479
27892
|
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
@@ -27512,7 +27925,7 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
27512
27925
|
}
|
|
27513
27926
|
if (ctx.assignmentDir) {
|
|
27514
27927
|
const dir = expandHome(ctx.assignmentDir);
|
|
27515
|
-
const assignmentMdPath2 =
|
|
27928
|
+
const assignmentMdPath2 = resolve69(dir, "assignment.md");
|
|
27516
27929
|
if (!await fileExists(assignmentMdPath2)) {
|
|
27517
27930
|
throw new AssignmentTargetError(
|
|
27518
27931
|
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
@@ -27541,8 +27954,8 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
27541
27954
|
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
27542
27955
|
);
|
|
27543
27956
|
}
|
|
27544
|
-
const assignmentDir =
|
|
27545
|
-
const assignmentMdPath2 =
|
|
27957
|
+
const assignmentDir = resolve69(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
27958
|
+
const assignmentMdPath2 = resolve69(assignmentDir, "assignment.md");
|
|
27546
27959
|
if (!await fileExists(assignmentMdPath2)) {
|
|
27547
27960
|
throw new AssignmentTargetError(
|
|
27548
27961
|
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
@@ -27605,7 +28018,7 @@ init_fs();
|
|
|
27605
28018
|
import { spawn as spawn7 } from "child_process";
|
|
27606
28019
|
import { mkdtemp as mkdtemp2, rm as rm8, stat as stat6 } from "fs/promises";
|
|
27607
28020
|
import { tmpdir as tmpdir3 } from "os";
|
|
27608
|
-
import { join as
|
|
28021
|
+
import { join as join14 } from "path";
|
|
27609
28022
|
function argsFor(mode, pngPath) {
|
|
27610
28023
|
switch (mode) {
|
|
27611
28024
|
case "interactive":
|
|
@@ -27638,8 +28051,8 @@ async function captureScreenshot(mode) {
|
|
|
27638
28051
|
"screencapture is only available on macOS. Use --file <path> to attach an existing image."
|
|
27639
28052
|
);
|
|
27640
28053
|
}
|
|
27641
|
-
const tmpDir = await mkdtemp2(
|
|
27642
|
-
const pngPath =
|
|
28054
|
+
const tmpDir = await mkdtemp2(join14(tmpdir3(), "syntaur-screenshot-"));
|
|
28055
|
+
const pngPath = join14(tmpDir, "shot.png");
|
|
27643
28056
|
const cleanup = async () => {
|
|
27644
28057
|
await rm8(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
27645
28058
|
});
|
|
@@ -27677,9 +28090,9 @@ async function captureScreenshot(mode) {
|
|
|
27677
28090
|
|
|
27678
28091
|
// src/utils/asciinema.ts
|
|
27679
28092
|
import { spawn as spawn8 } from "child_process";
|
|
27680
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
28093
|
+
import { mkdtemp as mkdtemp3, readFile as readFile46, rm as rm9 } from "fs/promises";
|
|
27681
28094
|
import { tmpdir as tmpdir4 } from "os";
|
|
27682
|
-
import { join as
|
|
28095
|
+
import { join as join15 } from "path";
|
|
27683
28096
|
var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
27684
28097
|
function shellQuote2(s) {
|
|
27685
28098
|
if (s.length === 0) return `''`;
|
|
@@ -27716,8 +28129,8 @@ function runAsciinema(args, stdio) {
|
|
|
27716
28129
|
});
|
|
27717
28130
|
}
|
|
27718
28131
|
async function captureAsciinema(opts) {
|
|
27719
|
-
const tmpDir = await mkdtemp3(
|
|
27720
|
-
const castPath =
|
|
28132
|
+
const tmpDir = await mkdtemp3(join15(tmpdir4(), "syntaur-asciinema-"));
|
|
28133
|
+
const castPath = join15(tmpDir, "session.cast");
|
|
27721
28134
|
const cleanup = async () => {
|
|
27722
28135
|
await rm9(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
27723
28136
|
});
|
|
@@ -27740,7 +28153,7 @@ async function captureAsciinema(opts) {
|
|
|
27740
28153
|
}
|
|
27741
28154
|
throw err2;
|
|
27742
28155
|
}
|
|
27743
|
-
const text = await
|
|
28156
|
+
const text = await readFile46(castPath, "utf8").catch(() => null);
|
|
27744
28157
|
if (text === null) {
|
|
27745
28158
|
throw new Error(
|
|
27746
28159
|
`asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
|
|
@@ -27768,9 +28181,9 @@ async function captureAsciinema(opts) {
|
|
|
27768
28181
|
// src/utils/recording.ts
|
|
27769
28182
|
init_paths();
|
|
27770
28183
|
import { spawn as spawn9 } from "child_process";
|
|
27771
|
-
import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as
|
|
28184
|
+
import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile47, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile13 } from "fs/promises";
|
|
27772
28185
|
import { tmpdir as tmpdir5 } from "os";
|
|
27773
|
-
import { join as
|
|
28186
|
+
import { join as join16, resolve as resolve70 } from "path";
|
|
27774
28187
|
import { setTimeout as sleep } from "timers/promises";
|
|
27775
28188
|
function sigintPollIntervalMs() {
|
|
27776
28189
|
const raw = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
|
|
@@ -27791,13 +28204,13 @@ function sigtermWaitMs() {
|
|
|
27791
28204
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
|
|
27792
28205
|
}
|
|
27793
28206
|
function pidfilePath() {
|
|
27794
|
-
return
|
|
28207
|
+
return resolve70(syntaurRoot(), "recording.pid");
|
|
27795
28208
|
}
|
|
27796
28209
|
function logPath2() {
|
|
27797
|
-
return
|
|
28210
|
+
return resolve70(syntaurRoot(), "recording.log");
|
|
27798
28211
|
}
|
|
27799
28212
|
function sidecarPath() {
|
|
27800
|
-
return
|
|
28213
|
+
return resolve70(syntaurRoot(), "recording.json");
|
|
27801
28214
|
}
|
|
27802
28215
|
function ffmpegArgs(device, fps, mp4Path) {
|
|
27803
28216
|
return [
|
|
@@ -27842,7 +28255,7 @@ async function acquirePidfile(pidfile) {
|
|
|
27842
28255
|
} catch (err2) {
|
|
27843
28256
|
if (err2.code !== "EEXIST") throw err2;
|
|
27844
28257
|
if (attempt === 1) throw err2;
|
|
27845
|
-
const existing = (await
|
|
28258
|
+
const existing = (await readFile47(pidfile, "utf-8").catch(() => "")).trim();
|
|
27846
28259
|
if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
|
|
27847
28260
|
const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
|
|
27848
28261
|
const parentPid = Number.parseInt(parentPidRaw, 10);
|
|
@@ -27895,8 +28308,8 @@ async function startRecording(input4) {
|
|
|
27895
28308
|
let acquiredPid = null;
|
|
27896
28309
|
try {
|
|
27897
28310
|
logHandle = await open3(log, "w");
|
|
27898
|
-
tmpDir = await mkdtemp4(
|
|
27899
|
-
const mp4Path =
|
|
28311
|
+
tmpDir = await mkdtemp4(join16(tmpdir5(), "syntaur-recording-"));
|
|
28312
|
+
const mp4Path = join16(tmpDir, "recording.mp4");
|
|
27900
28313
|
let child;
|
|
27901
28314
|
try {
|
|
27902
28315
|
child = spawn9("ffmpeg", ffmpegArgs(input4.device, input4.fps, mp4Path), {
|
|
@@ -27944,7 +28357,7 @@ async function startRecording(input4) {
|
|
|
27944
28357
|
logHandle = null;
|
|
27945
28358
|
if (warmupMs > 0) await sleep(warmupMs);
|
|
27946
28359
|
if (!await isProcessAlive(pid)) {
|
|
27947
|
-
const tail = await
|
|
28360
|
+
const tail = await readFile47(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
|
|
27948
28361
|
acquiredPid = null;
|
|
27949
28362
|
throw new Error(
|
|
27950
28363
|
`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}
|
|
@@ -28001,7 +28414,7 @@ ${tail}`
|
|
|
28001
28414
|
async function stopRecording() {
|
|
28002
28415
|
const pidfile = pidfilePath();
|
|
28003
28416
|
const sidecar = sidecarPath();
|
|
28004
|
-
const pidRaw = await
|
|
28417
|
+
const pidRaw = await readFile47(pidfile, "utf-8").catch(() => null);
|
|
28005
28418
|
if (pidRaw === null) {
|
|
28006
28419
|
throw new Error(
|
|
28007
28420
|
`No active recording found (no pidfile at ${pidfile}). Did you run --start?`
|
|
@@ -28011,7 +28424,7 @@ async function stopRecording() {
|
|
|
28011
28424
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
28012
28425
|
throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
|
|
28013
28426
|
}
|
|
28014
|
-
const sidecarRaw = await
|
|
28427
|
+
const sidecarRaw = await readFile47(sidecar, "utf-8").catch(() => null);
|
|
28015
28428
|
if (sidecarRaw === null) {
|
|
28016
28429
|
throw new Error(
|
|
28017
28430
|
`No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
|
|
@@ -28076,7 +28489,7 @@ async function stopRecording() {
|
|
|
28076
28489
|
// src/db/proof-db.ts
|
|
28077
28490
|
init_paths();
|
|
28078
28491
|
import Database5 from "better-sqlite3";
|
|
28079
|
-
import { resolve as
|
|
28492
|
+
import { resolve as resolve71 } from "path";
|
|
28080
28493
|
var db4 = null;
|
|
28081
28494
|
var PROOF_SCHEMA_VERSION = "1";
|
|
28082
28495
|
var SCHEMA_SQL4 = `
|
|
@@ -28096,7 +28509,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
28096
28509
|
`;
|
|
28097
28510
|
function initProofDb(dbPath) {
|
|
28098
28511
|
if (db4) return db4;
|
|
28099
|
-
const finalPath = dbPath ??
|
|
28512
|
+
const finalPath = dbPath ?? resolve71(syntaurRoot(), "syntaur.db");
|
|
28100
28513
|
db4 = new Database5(finalPath);
|
|
28101
28514
|
db4.pragma("journal_mode = WAL");
|
|
28102
28515
|
db4.exec(SCHEMA_SQL4);
|
|
@@ -28144,9 +28557,9 @@ function listArtifactsByAssignment(assignmentId) {
|
|
|
28144
28557
|
|
|
28145
28558
|
// src/utils/transcribers/elevenlabs.ts
|
|
28146
28559
|
import { spawn as spawn10 } from "child_process";
|
|
28147
|
-
import { mkdtemp as mkdtemp5, readFile as
|
|
28560
|
+
import { mkdtemp as mkdtemp5, readFile as readFile48, rm as rm11 } from "fs/promises";
|
|
28148
28561
|
import { tmpdir as tmpdir6 } from "os";
|
|
28149
|
-
import { join as
|
|
28562
|
+
import { join as join17 } from "path";
|
|
28150
28563
|
var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
|
|
28151
28564
|
var NO_AUDIO_MARKERS = [
|
|
28152
28565
|
"Stream map '0:a:0' matches no streams",
|
|
@@ -28208,7 +28621,7 @@ async function extractAudio(videoAbsPath, wavOut) {
|
|
|
28208
28621
|
throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
|
|
28209
28622
|
}
|
|
28210
28623
|
async function callScribe(wavPath, apiKey, opts) {
|
|
28211
|
-
const audio = await
|
|
28624
|
+
const audio = await readFile48(wavPath);
|
|
28212
28625
|
const form = new FormData();
|
|
28213
28626
|
form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
|
|
28214
28627
|
form.set("model_id", "scribe_v1");
|
|
@@ -28236,8 +28649,8 @@ var elevenLabsScribe = {
|
|
|
28236
28649
|
"ELEVENLABS_API_KEY is not set. Export it (e.g. `export ELEVENLABS_API_KEY=\u2026`) and re-run. A config-file slot will land later."
|
|
28237
28650
|
);
|
|
28238
28651
|
}
|
|
28239
|
-
const tmp = await mkdtemp5(
|
|
28240
|
-
const wav =
|
|
28652
|
+
const tmp = await mkdtemp5(join17(tmpdir6(), "syntaur-transcribe-"));
|
|
28653
|
+
const wav = join17(tmp, "audio.wav");
|
|
28241
28654
|
try {
|
|
28242
28655
|
await extractAudio(videoAbsPath, wav);
|
|
28243
28656
|
return await callScribe(wav, apiKey, opts);
|
|
@@ -28524,7 +28937,7 @@ async function captureCommand(target, options = {}) {
|
|
|
28524
28937
|
});
|
|
28525
28938
|
}
|
|
28526
28939
|
if (options.file) {
|
|
28527
|
-
const expanded = options.file.startsWith("~/") ?
|
|
28940
|
+
const expanded = options.file.startsWith("~/") ? resolve72(process.env.HOME ?? "", options.file.slice(2)) : resolve72(options.file);
|
|
28528
28941
|
if (!await fileExists(expanded)) {
|
|
28529
28942
|
throw new Error(`--file does not exist: ${options.file}`);
|
|
28530
28943
|
}
|
|
@@ -28565,7 +28978,7 @@ async function captureCommand(target, options = {}) {
|
|
|
28565
28978
|
}
|
|
28566
28979
|
initProofDb();
|
|
28567
28980
|
const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
|
|
28568
|
-
const destDir =
|
|
28981
|
+
const destDir = resolve72(proofDir(resolved.assignmentDir), subdir);
|
|
28569
28982
|
if (resolvedSource) await mkdir9(destDir, { recursive: true });
|
|
28570
28983
|
const ext = resolvedSource ? extensionForKind(kind) : null;
|
|
28571
28984
|
let id = null;
|
|
@@ -28574,7 +28987,7 @@ async function captureCommand(target, options = {}) {
|
|
|
28574
28987
|
let lastErr = null;
|
|
28575
28988
|
for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
|
|
28576
28989
|
const candidate = generateArtifactId();
|
|
28577
|
-
const candidateAbsPath = resolvedSource && ext ?
|
|
28990
|
+
const candidateAbsPath = resolvedSource && ext ? resolve72(destDir, `${candidate}.${ext}`) : null;
|
|
28578
28991
|
const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
|
|
28579
28992
|
try {
|
|
28580
28993
|
insertArtifact({
|
|
@@ -28618,7 +29031,7 @@ async function captureCommand(target, options = {}) {
|
|
|
28618
29031
|
}
|
|
28619
29032
|
}
|
|
28620
29033
|
if (options.transcribe && kind === "video" && absPath && id) {
|
|
28621
|
-
const sidecarPath2 =
|
|
29034
|
+
const sidecarPath2 = resolve72(destDir, `${id}.transcript.md`);
|
|
28622
29035
|
if (existsSync6(sidecarPath2)) {
|
|
28623
29036
|
console.warn(
|
|
28624
29037
|
`transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
|
|
@@ -28650,7 +29063,7 @@ async function captureCommand(target, options = {}) {
|
|
|
28650
29063
|
const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
|
|
28651
29064
|
console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
|
|
28652
29065
|
if (relativeFilePath) {
|
|
28653
|
-
console.log(` file: ${
|
|
29066
|
+
console.log(` file: ${resolve72(resolved.assignmentDir, relativeFilePath)}`);
|
|
28654
29067
|
}
|
|
28655
29068
|
} catch (err2) {
|
|
28656
29069
|
if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
|
|
@@ -28673,8 +29086,8 @@ async function captureCommand(target, options = {}) {
|
|
|
28673
29086
|
|
|
28674
29087
|
// src/commands/proof.ts
|
|
28675
29088
|
import { Command as Command6 } from "commander";
|
|
28676
|
-
import { readFile as
|
|
28677
|
-
import { resolve as
|
|
29089
|
+
import { readFile as readFile49, writeFile as writeFile15, rename as rename9, stat as stat9 } from "fs/promises";
|
|
29090
|
+
import { resolve as resolve73, relative as relative5, isAbsolute as isAbsolute11, dirname as dirname21 } from "path";
|
|
28678
29091
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
28679
29092
|
|
|
28680
29093
|
// src/utils/acceptance-criteria-parse.ts
|
|
@@ -28914,11 +29327,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
|
|
|
28914
29327
|
|
|
28915
29328
|
// src/commands/proof.ts
|
|
28916
29329
|
async function readAssignmentMeta(assignmentDir) {
|
|
28917
|
-
const path =
|
|
29330
|
+
const path = resolve73(assignmentDir, "assignment.md");
|
|
28918
29331
|
if (!await fileExists(path)) {
|
|
28919
29332
|
return { title: "", body: "" };
|
|
28920
29333
|
}
|
|
28921
|
-
const content = await
|
|
29334
|
+
const content = await readFile49(path, "utf-8");
|
|
28922
29335
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
28923
29336
|
let title = "";
|
|
28924
29337
|
if (fmMatch) {
|
|
@@ -28965,7 +29378,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
28965
29378
|
for (const r of rows) {
|
|
28966
29379
|
if (!r.file_path) continue;
|
|
28967
29380
|
if (r.kind !== "http" && r.kind !== "text") continue;
|
|
28968
|
-
const abs =
|
|
29381
|
+
const abs = resolve73(assignmentDir, r.file_path);
|
|
28969
29382
|
if (!isWithin(proofRoot, abs)) {
|
|
28970
29383
|
out.set(r.file_path, null);
|
|
28971
29384
|
continue;
|
|
@@ -28980,7 +29393,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
28980
29393
|
continue;
|
|
28981
29394
|
}
|
|
28982
29395
|
try {
|
|
28983
|
-
out.set(r.file_path, await
|
|
29396
|
+
out.set(r.file_path, await readFile49(abs, "utf-8"));
|
|
28984
29397
|
} catch {
|
|
28985
29398
|
out.set(r.file_path, null);
|
|
28986
29399
|
}
|
|
@@ -28992,14 +29405,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
|
|
|
28992
29405
|
const proofRoot = proofDir(assignmentDir);
|
|
28993
29406
|
for (const r of rows) {
|
|
28994
29407
|
if (r.kind !== "video" || !r.file_path) continue;
|
|
28995
|
-
const videoAbs =
|
|
28996
|
-
const sidecar =
|
|
29408
|
+
const videoAbs = resolve73(assignmentDir, r.file_path);
|
|
29409
|
+
const sidecar = resolve73(dirname21(videoAbs), `${r.id}.transcript.md`);
|
|
28997
29410
|
if (!isWithin(proofRoot, sidecar)) continue;
|
|
28998
29411
|
if (!await fileExists(sidecar)) continue;
|
|
28999
29412
|
const st = await stat9(sidecar);
|
|
29000
29413
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
|
|
29001
29414
|
try {
|
|
29002
|
-
out.set(r.id, await
|
|
29415
|
+
out.set(r.id, await readFile49(sidecar, "utf-8"));
|
|
29003
29416
|
} catch {
|
|
29004
29417
|
}
|
|
29005
29418
|
}
|
|
@@ -29033,8 +29446,8 @@ async function proofBuildCommand(target, options = {}) {
|
|
|
29033
29446
|
};
|
|
29034
29447
|
const md = renderProofMarkdown(renderParams);
|
|
29035
29448
|
const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
|
|
29036
|
-
const mdPath =
|
|
29037
|
-
const htmlPath =
|
|
29449
|
+
const mdPath = resolve73(resolved.assignmentDir, "proof.md");
|
|
29450
|
+
const htmlPath = resolve73(resolved.assignmentDir, "proof.html");
|
|
29038
29451
|
await atomicWrite(mdPath, md);
|
|
29039
29452
|
await atomicWrite(htmlPath, html);
|
|
29040
29453
|
console.log(`Wrote ${htmlPath}`);
|
|
@@ -29699,7 +30112,7 @@ async function runCcusage(opts = {}) {
|
|
|
29699
30112
|
};
|
|
29700
30113
|
}
|
|
29701
30114
|
function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
29702
|
-
return new Promise((
|
|
30115
|
+
return new Promise((resolve82) => {
|
|
29703
30116
|
const child = spawn11(binary, args, {
|
|
29704
30117
|
env: env ?? process.env,
|
|
29705
30118
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -29713,7 +30126,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
|
29713
30126
|
if (settled) return;
|
|
29714
30127
|
settled = true;
|
|
29715
30128
|
clearTimeout(timer2);
|
|
29716
|
-
|
|
30129
|
+
resolve82(result);
|
|
29717
30130
|
};
|
|
29718
30131
|
const timer2 = setTimeout(() => {
|
|
29719
30132
|
timedOut = true;
|
|
@@ -29758,8 +30171,8 @@ function isoToCcusageDate(iso) {
|
|
|
29758
30171
|
// src/usage/cwd-extractor.ts
|
|
29759
30172
|
init_paths();
|
|
29760
30173
|
import { open as open4, readdir as readdir24, stat as stat10 } from "fs/promises";
|
|
29761
|
-
import { join as
|
|
29762
|
-
import { homedir as
|
|
30174
|
+
import { join as join18 } from "path";
|
|
30175
|
+
import { homedir as homedir12 } from "os";
|
|
29763
30176
|
var SCAN_LINE_CAP = 50;
|
|
29764
30177
|
var TAIL_READ_BYTES = 8 * 1024;
|
|
29765
30178
|
var TAIL_READ_BYTES_MAX = 64 * 1024;
|
|
@@ -29833,12 +30246,12 @@ async function* walkClaudeProjects(opts = {}) {
|
|
|
29833
30246
|
const dirs = await listDirSafe(root);
|
|
29834
30247
|
for (const dirent of dirs) {
|
|
29835
30248
|
if (!dirent.isDirectory) continue;
|
|
29836
|
-
const dirPath =
|
|
30249
|
+
const dirPath = join18(root, dirent.name);
|
|
29837
30250
|
const files = await listDirSafe(dirPath);
|
|
29838
30251
|
let cachedCwd = null;
|
|
29839
30252
|
for (const f of files) {
|
|
29840
30253
|
if (!f.isFile || !f.name.endsWith(".jsonl")) continue;
|
|
29841
|
-
const filePath =
|
|
30254
|
+
const filePath = join18(dirPath, f.name);
|
|
29842
30255
|
if (opts.sinceMtimeMs !== void 0) {
|
|
29843
30256
|
const mtime = await mtimeMs(filePath);
|
|
29844
30257
|
if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
|
|
@@ -29875,8 +30288,8 @@ function resolveCodexSessionsRoot(override) {
|
|
|
29875
30288
|
const fromSessionsEnv = process.env.CODEX_SESSIONS_DIR;
|
|
29876
30289
|
if (fromSessionsEnv && fromSessionsEnv.length > 0) return expandHome(fromSessionsEnv);
|
|
29877
30290
|
const fromHomeEnv = process.env.CODEX_HOME;
|
|
29878
|
-
if (fromHomeEnv && fromHomeEnv.length > 0) return
|
|
29879
|
-
return
|
|
30291
|
+
if (fromHomeEnv && fromHomeEnv.length > 0) return join18(expandHome(fromHomeEnv), "sessions");
|
|
30292
|
+
return join18(homedir12(), ".codex", "sessions");
|
|
29880
30293
|
}
|
|
29881
30294
|
async function listDirSafe(path) {
|
|
29882
30295
|
try {
|
|
@@ -29896,7 +30309,7 @@ async function* walkJsonlRecursive(root) {
|
|
|
29896
30309
|
const current = stack.pop();
|
|
29897
30310
|
const entries = await listDirSafe(current);
|
|
29898
30311
|
for (const e of entries) {
|
|
29899
|
-
const full =
|
|
30312
|
+
const full = join18(current, e.name);
|
|
29900
30313
|
if (e.isDirectory) {
|
|
29901
30314
|
stack.push(full);
|
|
29902
30315
|
} else if (e.isFile && e.name.endsWith(".jsonl")) {
|
|
@@ -30235,8 +30648,8 @@ init_slug();
|
|
|
30235
30648
|
init_timestamp();
|
|
30236
30649
|
init_assignment_resolver();
|
|
30237
30650
|
init_assignment_todos();
|
|
30238
|
-
import { resolve as
|
|
30239
|
-
import { readFile as
|
|
30651
|
+
import { resolve as resolve74 } from "path";
|
|
30652
|
+
import { readFile as readFile50 } from "fs/promises";
|
|
30240
30653
|
async function requestCommand(target, text, options = {}) {
|
|
30241
30654
|
if (!text || !text.trim()) {
|
|
30242
30655
|
throw new Error("Request text cannot be empty.");
|
|
@@ -30252,7 +30665,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
30252
30665
|
if (!isValidSlug(target)) {
|
|
30253
30666
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
30254
30667
|
}
|
|
30255
|
-
assignmentDir =
|
|
30668
|
+
assignmentDir = resolve74(baseDir, options.project, "assignments", target);
|
|
30256
30669
|
targetRef = target;
|
|
30257
30670
|
} else {
|
|
30258
30671
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -30262,12 +30675,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
30262
30675
|
assignmentDir = resolved.assignmentDir;
|
|
30263
30676
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
30264
30677
|
}
|
|
30265
|
-
const assignmentMdPath2 =
|
|
30678
|
+
const assignmentMdPath2 = resolve74(assignmentDir, "assignment.md");
|
|
30266
30679
|
if (!await fileExists(assignmentMdPath2)) {
|
|
30267
30680
|
throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
|
|
30268
30681
|
}
|
|
30269
30682
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
30270
|
-
let content = await
|
|
30683
|
+
let content = await readFile50(assignmentMdPath2, "utf-8");
|
|
30271
30684
|
content = appendTodosToAssignmentBody(content, [
|
|
30272
30685
|
{ description: `${text.trim()} (from: ${source})` }
|
|
30273
30686
|
]);
|
|
@@ -30280,13 +30693,13 @@ async function requestCommand(target, text, options = {}) {
|
|
|
30280
30693
|
init_fs();
|
|
30281
30694
|
init_paths();
|
|
30282
30695
|
import { Command as Command9 } from "commander";
|
|
30283
|
-
import { readFile as
|
|
30284
|
-
import { resolve as
|
|
30696
|
+
import { readFile as readFile51, readdir as readdir25 } from "fs/promises";
|
|
30697
|
+
import { resolve as resolve75 } from "path";
|
|
30285
30698
|
async function readContextAssignmentDir(cwd) {
|
|
30286
|
-
const path =
|
|
30699
|
+
const path = resolve75(cwd, ".syntaur", "context.json");
|
|
30287
30700
|
if (!await fileExists(path)) return null;
|
|
30288
30701
|
try {
|
|
30289
|
-
const raw = await
|
|
30702
|
+
const raw = await readFile51(path, "utf-8");
|
|
30290
30703
|
const ctx = JSON.parse(raw);
|
|
30291
30704
|
if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
|
|
30292
30705
|
return ctx.assignmentDir;
|
|
@@ -30300,9 +30713,9 @@ async function resolveAssignmentDir(opts) {
|
|
|
30300
30713
|
const cwd = opts.cwd ?? process.cwd();
|
|
30301
30714
|
if (opts.assignment) {
|
|
30302
30715
|
if (opts.project) {
|
|
30303
|
-
return
|
|
30716
|
+
return resolve75(defaultProjectDir(), opts.project, "assignments", opts.assignment);
|
|
30304
30717
|
}
|
|
30305
|
-
return
|
|
30718
|
+
return resolve75(assignmentsDir(), opts.assignment);
|
|
30306
30719
|
}
|
|
30307
30720
|
const fromCtx = await readContextAssignmentDir(cwd);
|
|
30308
30721
|
if (fromCtx) return fromCtx;
|
|
@@ -30445,7 +30858,7 @@ async function runPlanVersion(options) {
|
|
|
30445
30858
|
if (!await fileExists(assignmentDir)) {
|
|
30446
30859
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
30447
30860
|
}
|
|
30448
|
-
const assignmentMdPath2 =
|
|
30861
|
+
const assignmentMdPath2 = resolve75(assignmentDir, "assignment.md");
|
|
30449
30862
|
if (!await fileExists(assignmentMdPath2)) {
|
|
30450
30863
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
30451
30864
|
}
|
|
@@ -30457,15 +30870,15 @@ async function runPlanVersion(options) {
|
|
|
30457
30870
|
}
|
|
30458
30871
|
const current = planFiles[planFiles.length - 1];
|
|
30459
30872
|
const next = nextPlanFileName(current.version);
|
|
30460
|
-
const newPath =
|
|
30873
|
+
const newPath = resolve75(assignmentDir, next.fileName);
|
|
30461
30874
|
if (await fileExists(newPath) && !options.force) {
|
|
30462
30875
|
throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
|
|
30463
30876
|
}
|
|
30464
|
-
const assignmentMd = await
|
|
30877
|
+
const assignmentMd = await readFile51(assignmentMdPath2, "utf-8");
|
|
30465
30878
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
30466
30879
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
30467
|
-
const oldPlanPath =
|
|
30468
|
-
const oldPlanContent = await
|
|
30880
|
+
const oldPlanPath = resolve75(assignmentDir, current.fileName);
|
|
30881
|
+
const oldPlanContent = await readFile51(oldPlanPath, "utf-8");
|
|
30469
30882
|
const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
|
|
30470
30883
|
const carriedTodos = extractUncheckedTodos(oldBody);
|
|
30471
30884
|
const stub = buildNewPlanStub({
|
|
@@ -30502,26 +30915,26 @@ planCommand.command("version").description(
|
|
|
30502
30915
|
// src/commands/session.ts
|
|
30503
30916
|
init_fs();
|
|
30504
30917
|
import { Command as Command10 } from "commander";
|
|
30505
|
-
import { readFile as
|
|
30506
|
-
import { resolve as
|
|
30918
|
+
import { readFile as readFile52, readdir as readdir26, stat as stat11 } from "fs/promises";
|
|
30919
|
+
import { resolve as resolve76 } from "path";
|
|
30507
30920
|
async function readContext(cwd) {
|
|
30508
|
-
const path =
|
|
30921
|
+
const path = resolve76(cwd, ".syntaur", "context.json");
|
|
30509
30922
|
if (!await fileExists(path)) return null;
|
|
30510
30923
|
try {
|
|
30511
|
-
const raw = await
|
|
30924
|
+
const raw = await readFile52(path, "utf-8");
|
|
30512
30925
|
return JSON.parse(raw);
|
|
30513
30926
|
} catch {
|
|
30514
30927
|
return null;
|
|
30515
30928
|
}
|
|
30516
30929
|
}
|
|
30517
30930
|
async function findLatestSessionSummary(assignmentDir) {
|
|
30518
|
-
const sessionsRoot =
|
|
30931
|
+
const sessionsRoot = resolve76(assignmentDir, "sessions");
|
|
30519
30932
|
if (!await fileExists(sessionsRoot)) return null;
|
|
30520
30933
|
const entries = await readdir26(sessionsRoot, { withFileTypes: true });
|
|
30521
30934
|
let best = null;
|
|
30522
30935
|
for (const entry of entries) {
|
|
30523
30936
|
if (!entry.isDirectory()) continue;
|
|
30524
|
-
const summaryPath =
|
|
30937
|
+
const summaryPath = resolve76(sessionsRoot, entry.name, "summary.md");
|
|
30525
30938
|
if (!await fileExists(summaryPath)) continue;
|
|
30526
30939
|
const st = await stat11(summaryPath);
|
|
30527
30940
|
if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
|
|
@@ -30531,9 +30944,9 @@ async function findLatestSessionSummary(assignmentDir) {
|
|
|
30531
30944
|
return best;
|
|
30532
30945
|
}
|
|
30533
30946
|
async function findOpenHandoff(assignmentDir) {
|
|
30534
|
-
const handoffPath =
|
|
30947
|
+
const handoffPath = resolve76(assignmentDir, "handoff.md");
|
|
30535
30948
|
if (!await fileExists(handoffPath)) return null;
|
|
30536
|
-
const content = await
|
|
30949
|
+
const content = await readFile52(handoffPath, "utf-8");
|
|
30537
30950
|
const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
|
|
30538
30951
|
if (body.length === 0) return null;
|
|
30539
30952
|
if (/^<!--[\s\S]*-->$/.test(body)) return null;
|
|
@@ -30641,13 +31054,13 @@ init_git_worktree();
|
|
|
30641
31054
|
init_fs();
|
|
30642
31055
|
init_paths();
|
|
30643
31056
|
import { Command as Command11 } from "commander";
|
|
30644
|
-
import { readFile as
|
|
30645
|
-
import { resolve as
|
|
31057
|
+
import { readFile as readFile53 } from "fs/promises";
|
|
31058
|
+
import { resolve as resolve77 } from "path";
|
|
30646
31059
|
async function readContext2(cwd) {
|
|
30647
|
-
const path =
|
|
31060
|
+
const path = resolve77(cwd, ".syntaur", "context.json");
|
|
30648
31061
|
if (!await fileExists(path)) return null;
|
|
30649
31062
|
try {
|
|
30650
|
-
return JSON.parse(await
|
|
31063
|
+
return JSON.parse(await readFile53(path, "utf-8"));
|
|
30651
31064
|
} catch {
|
|
30652
31065
|
return null;
|
|
30653
31066
|
}
|
|
@@ -30655,7 +31068,7 @@ async function readContext2(cwd) {
|
|
|
30655
31068
|
async function resolveAssignmentPath2(opts) {
|
|
30656
31069
|
if (opts.assignment) {
|
|
30657
31070
|
if (opts.project) {
|
|
30658
|
-
return
|
|
31071
|
+
return resolve77(
|
|
30659
31072
|
defaultProjectDir(),
|
|
30660
31073
|
opts.project,
|
|
30661
31074
|
"assignments",
|
|
@@ -30663,10 +31076,10 @@ async function resolveAssignmentPath2(opts) {
|
|
|
30663
31076
|
"assignment.md"
|
|
30664
31077
|
);
|
|
30665
31078
|
}
|
|
30666
|
-
return
|
|
31079
|
+
return resolve77(assignmentsDir(), opts.assignment, "assignment.md");
|
|
30667
31080
|
}
|
|
30668
31081
|
const ctx = await readContext2(opts.cwd);
|
|
30669
|
-
if (ctx?.assignmentDir) return
|
|
31082
|
+
if (ctx?.assignmentDir) return resolve77(ctx.assignmentDir, "assignment.md");
|
|
30670
31083
|
throw new Error(
|
|
30671
31084
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
30672
31085
|
);
|
|
@@ -30677,7 +31090,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
|
|
|
30677
31090
|
}
|
|
30678
31091
|
const repository = options.repository ?? cwd;
|
|
30679
31092
|
const parentBranch = options.parentBranch ?? "main";
|
|
30680
|
-
const worktreePath = options.worktreePath ??
|
|
31093
|
+
const worktreePath = options.worktreePath ?? resolve77(repository, ".worktrees", options.branch);
|
|
30681
31094
|
const assignmentPath = await resolveAssignmentPath2({
|
|
30682
31095
|
assignment: options.assignment,
|
|
30683
31096
|
project: options.project,
|
|
@@ -30714,13 +31127,13 @@ init_paths();
|
|
|
30714
31127
|
init_fs();
|
|
30715
31128
|
init_slug();
|
|
30716
31129
|
import { Command as Command12 } from "commander";
|
|
30717
|
-
import { resolve as
|
|
31130
|
+
import { resolve as resolve79 } from "path";
|
|
30718
31131
|
|
|
30719
31132
|
// src/utils/project-indexes.ts
|
|
30720
31133
|
init_parser();
|
|
30721
31134
|
init_fs();
|
|
30722
|
-
import { readdir as readdir27, readFile as
|
|
30723
|
-
import { resolve as
|
|
31135
|
+
import { readdir as readdir27, readFile as readFile54 } from "fs/promises";
|
|
31136
|
+
import { resolve as resolve78 } from "path";
|
|
30724
31137
|
function nowIso3() {
|
|
30725
31138
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
30726
31139
|
}
|
|
@@ -30739,7 +31152,7 @@ function joinList(items) {
|
|
|
30739
31152
|
return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
|
|
30740
31153
|
}
|
|
30741
31154
|
async function rebuildResourcesIndex(projectDir) {
|
|
30742
|
-
const dir =
|
|
31155
|
+
const dir = resolve78(projectDir, "resources");
|
|
30743
31156
|
await ensureDir(dir);
|
|
30744
31157
|
const files = await listSlugFiles(dir);
|
|
30745
31158
|
const slug = readProjectSlug(projectDir);
|
|
@@ -30755,7 +31168,7 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
30755
31168
|
lines.push("| Name | Category | Source | Related Assignments | Updated |");
|
|
30756
31169
|
lines.push("|------|----------|--------|---------------------|---------|");
|
|
30757
31170
|
for (const fileName of files) {
|
|
30758
|
-
const content = await
|
|
31171
|
+
const content = await readFile54(resolve78(dir, fileName), "utf-8");
|
|
30759
31172
|
const parsed = parseResource(content);
|
|
30760
31173
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
30761
31174
|
const name = parsed.name || slugBase;
|
|
@@ -30765,12 +31178,12 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
30765
31178
|
);
|
|
30766
31179
|
}
|
|
30767
31180
|
lines.push("");
|
|
30768
|
-
const indexPath =
|
|
31181
|
+
const indexPath = resolve78(dir, "_index.md");
|
|
30769
31182
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
30770
31183
|
return { total: files.length, path: indexPath };
|
|
30771
31184
|
}
|
|
30772
31185
|
async function rebuildMemoriesIndex(projectDir) {
|
|
30773
|
-
const dir =
|
|
31186
|
+
const dir = resolve78(projectDir, "memories");
|
|
30774
31187
|
await ensureDir(dir);
|
|
30775
31188
|
const files = await listSlugFiles(dir);
|
|
30776
31189
|
const slug = readProjectSlug(projectDir);
|
|
@@ -30786,7 +31199,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
30786
31199
|
lines.push("| Name | Source | Scope | Source Assignment | Updated |");
|
|
30787
31200
|
lines.push("|------|--------|-------|-------------------|---------|");
|
|
30788
31201
|
for (const fileName of files) {
|
|
30789
|
-
const content = await
|
|
31202
|
+
const content = await readFile54(resolve78(dir, fileName), "utf-8");
|
|
30790
31203
|
const parsed = parseMemory(content);
|
|
30791
31204
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
30792
31205
|
const name = parsed.name || slugBase;
|
|
@@ -30796,7 +31209,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
30796
31209
|
);
|
|
30797
31210
|
}
|
|
30798
31211
|
lines.push("");
|
|
30799
|
-
const indexPath =
|
|
31212
|
+
const indexPath = resolve78(dir, "_index.md");
|
|
30800
31213
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
30801
31214
|
return { total: files.length, path: indexPath };
|
|
30802
31215
|
}
|
|
@@ -30834,8 +31247,8 @@ async function runResourceAdd(options) {
|
|
|
30834
31247
|
if (!isValidSlug(options.project)) {
|
|
30835
31248
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
30836
31249
|
}
|
|
30837
|
-
const projectDir =
|
|
30838
|
-
if (!await fileExists(
|
|
31250
|
+
const projectDir = resolve79(defaultProjectDir(), options.project);
|
|
31251
|
+
if (!await fileExists(resolve79(projectDir, "project.md"))) {
|
|
30839
31252
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
30840
31253
|
}
|
|
30841
31254
|
if (!options.name) throw new Error("--name is required.");
|
|
@@ -30844,7 +31257,7 @@ async function runResourceAdd(options) {
|
|
|
30844
31257
|
if (!isValidSlug(slug)) {
|
|
30845
31258
|
throw new Error(`Invalid resource slug: "${slug}".`);
|
|
30846
31259
|
}
|
|
30847
|
-
const filePath =
|
|
31260
|
+
const filePath = resolve79(projectDir, "resources", `${slug}.md`);
|
|
30848
31261
|
if (await fileExists(filePath) && !options.force) {
|
|
30849
31262
|
throw new Error(
|
|
30850
31263
|
`Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -30879,7 +31292,7 @@ init_paths();
|
|
|
30879
31292
|
init_fs();
|
|
30880
31293
|
init_slug();
|
|
30881
31294
|
import { Command as Command13 } from "commander";
|
|
30882
|
-
import { resolve as
|
|
31295
|
+
import { resolve as resolve80 } from "path";
|
|
30883
31296
|
function nowIso5() {
|
|
30884
31297
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
30885
31298
|
}
|
|
@@ -30914,8 +31327,8 @@ async function runMemoryAdd(options) {
|
|
|
30914
31327
|
if (!isValidSlug(options.project)) {
|
|
30915
31328
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
30916
31329
|
}
|
|
30917
|
-
const projectDir =
|
|
30918
|
-
if (!await fileExists(
|
|
31330
|
+
const projectDir = resolve80(defaultProjectDir(), options.project);
|
|
31331
|
+
if (!await fileExists(resolve80(projectDir, "project.md"))) {
|
|
30919
31332
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
30920
31333
|
}
|
|
30921
31334
|
if (!options.name) throw new Error("--name is required.");
|
|
@@ -30924,7 +31337,7 @@ async function runMemoryAdd(options) {
|
|
|
30924
31337
|
if (!isValidSlug(slug)) {
|
|
30925
31338
|
throw new Error(`Invalid memory slug: "${slug}".`);
|
|
30926
31339
|
}
|
|
30927
|
-
const filePath =
|
|
31340
|
+
const filePath = resolve80(projectDir, "memories", `${slug}.md`);
|
|
30928
31341
|
if (await fileExists(filePath) && !options.force) {
|
|
30929
31342
|
throw new Error(
|
|
30930
31343
|
`Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -30961,8 +31374,8 @@ init_paths();
|
|
|
30961
31374
|
init_fs();
|
|
30962
31375
|
init_frontmatter();
|
|
30963
31376
|
import { Command as Command14 } from "commander";
|
|
30964
|
-
import { readFile as
|
|
30965
|
-
import { resolve as
|
|
31377
|
+
import { readFile as readFile55 } from "fs/promises";
|
|
31378
|
+
import { resolve as resolve81 } from "path";
|
|
30966
31379
|
var AGE_PATTERN = /^(\d+)([dhwm])$/i;
|
|
30967
31380
|
function parseAgeToCutoff(age) {
|
|
30968
31381
|
const match = age.match(AGE_PATTERN);
|
|
@@ -30981,7 +31394,7 @@ function parseAgeToCutoff(age) {
|
|
|
30981
31394
|
}
|
|
30982
31395
|
function assignmentMdPath(item) {
|
|
30983
31396
|
if (item.projectSlug) {
|
|
30984
|
-
return
|
|
31397
|
+
return resolve81(
|
|
30985
31398
|
defaultProjectDir(),
|
|
30986
31399
|
item.projectSlug,
|
|
30987
31400
|
"assignments",
|
|
@@ -30989,13 +31402,13 @@ function assignmentMdPath(item) {
|
|
|
30989
31402
|
"assignment.md"
|
|
30990
31403
|
);
|
|
30991
31404
|
}
|
|
30992
|
-
return
|
|
31405
|
+
return resolve81(assignmentsDir(), item.id, "assignment.md");
|
|
30993
31406
|
}
|
|
30994
31407
|
async function loadTags(item) {
|
|
30995
31408
|
const path = assignmentMdPath(item);
|
|
30996
31409
|
if (!await fileExists(path)) return [];
|
|
30997
31410
|
try {
|
|
30998
|
-
const content = await
|
|
31411
|
+
const content = await readFile55(path, "utf-8");
|
|
30999
31412
|
return parseAssignmentFrontmatter(content).tags;
|
|
31000
31413
|
} catch {
|
|
31001
31414
|
return [];
|
|
@@ -31467,7 +31880,8 @@ function spliceDashDashFromArgv(argv) {
|
|
|
31467
31880
|
// src/index.ts
|
|
31468
31881
|
{
|
|
31469
31882
|
const sub = process.argv[2];
|
|
31470
|
-
|
|
31883
|
+
const isDryRunSetup = sub === "setup" && process.argv.slice(3).includes("--dry-run");
|
|
31884
|
+
if (sub !== "update" && sub !== "upgrade" && !isDryRunSetup) {
|
|
31471
31885
|
await maybePromptInstall(import.meta.url);
|
|
31472
31886
|
await maybeNudgeForNpxInstall(import.meta.url);
|
|
31473
31887
|
}
|
|
@@ -31712,7 +32126,7 @@ program.command("reopen").description("Reopen a completed or failed assignment")
|
|
|
31712
32126
|
process.exit(1);
|
|
31713
32127
|
}
|
|
31714
32128
|
});
|
|
31715
|
-
program.command("setup").description("Initialize Syntaur and optionally install plugins or launch the dashboard").option("--yes", "Skip interactive prompts and perform only the requested flags").option("--claude", "Install the Claude Code plugin").option("--codex", "Install the Codex plugin").option("--claude-dir <path>", "Install the Claude Code plugin at a specific path").option("--codex-dir <path>", "Install the Codex plugin at a specific path").option("--codex-marketplace-path <path>", "Write the Codex marketplace entry to a specific file").option("--dashboard", "Launch the dashboard after setup").action(async (options) => {
|
|
32129
|
+
program.command("setup").description("Initialize Syntaur and optionally install plugins or launch the dashboard").option("--yes", "Skip interactive prompts and perform only the requested flags").option("--claude", "Install the Claude Code plugin").option("--codex", "Install the Codex plugin").option("--claude-dir <path>", "Install the Claude Code plugin at a specific path").option("--codex-dir <path>", "Install the Codex plugin at a specific path").option("--codex-marketplace-path <path>", "Write the Codex marketplace entry to a specific file").option("--dashboard", "Launch the dashboard after setup").option("--target <id>", "Install Syntaur into a cross-agent target (pi, hermes, openclaw, cursor, opencode); comma-separated for several").option("--agent <id>", "Alias for --target; cross-agent target id(s) to install into").option("--force", "Overwrite existing cross-agent protocol files / skills").option("--dry-run", "Print the cross-agent install actions without writing anything").action(async (options) => {
|
|
31716
32130
|
try {
|
|
31717
32131
|
await setupCommand(options);
|
|
31718
32132
|
} catch (error) {
|