syntaur 0.32.0 → 0.33.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/index.js +763 -451
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/README.md +38 -7
- package/platforms/hermes/plugins/syntaur/README.md +37 -0
- package/platforms/hermes/plugins/syntaur/__init__.py +151 -0
- package/platforms/hermes/plugins/syntaur/__pycache__/__init__.cpython-312.pyc +0 -0
- package/platforms/hermes/plugins/syntaur/__pycache__/boundary.cpython-312.pyc +0 -0
- package/platforms/hermes/plugins/syntaur/boundary.py +99 -0
- package/platforms/hermes/plugins/syntaur/plugin.yaml +6 -0
- package/platforms/pi/extensions/syntaur/README.md +34 -0
- package/platforms/pi/extensions/syntaur/index.ts +265 -0
package/dist/index.js
CHANGED
|
@@ -2760,9 +2760,9 @@ function normalizeAgentsFromConfig(agents) {
|
|
|
2760
2760
|
validateAgentList(normalized);
|
|
2761
2761
|
return normalized;
|
|
2762
2762
|
} catch (err2) {
|
|
2763
|
-
const
|
|
2763
|
+
const msg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
2764
2764
|
console.warn(
|
|
2765
|
-
`Warning: ~/.syntaur/config.md agents block is invalid (${
|
|
2765
|
+
`Warning: ~/.syntaur/config.md agents block is invalid (${msg2}) \u2014 using built-in defaults`
|
|
2766
2766
|
);
|
|
2767
2767
|
return null;
|
|
2768
2768
|
}
|
|
@@ -3152,8 +3152,8 @@ async function readConfig() {
|
|
|
3152
3152
|
try {
|
|
3153
3153
|
return parseTerminalConfig(fm["terminal"]);
|
|
3154
3154
|
} catch (err2) {
|
|
3155
|
-
const
|
|
3156
|
-
console.warn(`Warning: ${
|
|
3155
|
+
const msg2 = err2 instanceof TerminalConfigError ? err2.message : String(err2);
|
|
3156
|
+
console.warn(`Warning: ${msg2} \u2014 falling back to default`);
|
|
3157
3157
|
return null;
|
|
3158
3158
|
}
|
|
3159
3159
|
})(),
|
|
@@ -5382,8 +5382,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
5382
5382
|
return allSessions.length;
|
|
5383
5383
|
}
|
|
5384
5384
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
5385
|
-
const { readFile:
|
|
5386
|
-
const raw2 = await
|
|
5385
|
+
const { readFile: readFile64 } = await import("fs/promises");
|
|
5386
|
+
const raw2 = await readFile64(filePath, "utf-8");
|
|
5387
5387
|
const sessions = [];
|
|
5388
5388
|
const lines = raw2.split("\n");
|
|
5389
5389
|
let inTable = false;
|
|
@@ -5819,8 +5819,8 @@ function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
|
|
|
5819
5819
|
return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
|
|
5820
5820
|
}
|
|
5821
5821
|
function delay(ms) {
|
|
5822
|
-
return new Promise((
|
|
5823
|
-
const timer2 = setTimeout(
|
|
5822
|
+
return new Promise((resolve87) => {
|
|
5823
|
+
const timer2 = setTimeout(resolve87, ms);
|
|
5824
5824
|
if (typeof timer2.unref === "function") {
|
|
5825
5825
|
timer2.unref();
|
|
5826
5826
|
}
|
|
@@ -8802,7 +8802,7 @@ import {
|
|
|
8802
8802
|
unlinkSync
|
|
8803
8803
|
} from "fs";
|
|
8804
8804
|
import { fileURLToPath as fileURLToPath9, pathToFileURL } from "url";
|
|
8805
|
-
import { dirname as
|
|
8805
|
+
import { dirname as dirname18, resolve as resolve51, join as join10 } from "path";
|
|
8806
8806
|
import { homedir as homedir9, tmpdir as tmpdir2 } from "os";
|
|
8807
8807
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
8808
8808
|
function syntaurRootMjs() {
|
|
@@ -8810,7 +8810,7 @@ function syntaurRootMjs() {
|
|
|
8810
8810
|
if (override && override.length > 0) {
|
|
8811
8811
|
return override;
|
|
8812
8812
|
}
|
|
8813
|
-
return
|
|
8813
|
+
return join10(homedir9(), ".syntaur");
|
|
8814
8814
|
}
|
|
8815
8815
|
async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
8816
8816
|
const { throwOnFailure } = options;
|
|
@@ -8824,38 +8824,38 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8824
8824
|
}
|
|
8825
8825
|
const stateRoot = syntaurRootMjs();
|
|
8826
8826
|
mkdirSync2(stateRoot, { recursive: true });
|
|
8827
|
-
const lockPath =
|
|
8827
|
+
const lockPath = resolve51(stateRoot, "install-url-handler.lock");
|
|
8828
8828
|
let lockFd;
|
|
8829
8829
|
try {
|
|
8830
8830
|
lockFd = openSync(lockPath, "wx");
|
|
8831
8831
|
} catch (err2) {
|
|
8832
8832
|
if (err2 && err2.code === "EEXIST") {
|
|
8833
|
-
const
|
|
8833
|
+
const msg2 = `Another syntaur:// handler registration is in progress (lock at ${lockPath}). Wait for it to finish or remove the stale lock.`;
|
|
8834
8834
|
if (throwOnFailure) {
|
|
8835
|
-
throw new Error(
|
|
8835
|
+
throw new Error(msg2);
|
|
8836
8836
|
}
|
|
8837
|
-
console.warn(`syntaur: skipping macOS URL-handler registration (${
|
|
8837
|
+
console.warn(`syntaur: skipping macOS URL-handler registration (${msg2})`);
|
|
8838
8838
|
return { bundlePath: "" };
|
|
8839
8839
|
}
|
|
8840
8840
|
throw err2;
|
|
8841
8841
|
}
|
|
8842
8842
|
try {
|
|
8843
|
-
const pkgRoot =
|
|
8844
|
-
const cliBin = realpathSync3(
|
|
8843
|
+
const pkgRoot = resolve51(dirname18(fileURLToPath9(import.meta.url)), "..");
|
|
8844
|
+
const cliBin = realpathSync3(resolve51(pkgRoot, "bin/syntaur.js"));
|
|
8845
8845
|
const nodeBin = process.execPath;
|
|
8846
|
-
const bundleParent =
|
|
8846
|
+
const bundleParent = join10(
|
|
8847
8847
|
homedir9(),
|
|
8848
8848
|
"Library",
|
|
8849
8849
|
"Application Support",
|
|
8850
8850
|
"Syntaur"
|
|
8851
8851
|
);
|
|
8852
|
-
const bundlePath =
|
|
8852
|
+
const bundlePath = join10(bundleParent, "syntaur-url.app");
|
|
8853
8853
|
mkdirSync2(bundleParent, { recursive: true });
|
|
8854
8854
|
if (existsSync5(bundlePath)) {
|
|
8855
8855
|
rmSync(bundlePath, { recursive: true, force: true });
|
|
8856
8856
|
}
|
|
8857
8857
|
const installedTerminals = detectInstalledTerminals();
|
|
8858
|
-
const scriptPath =
|
|
8858
|
+
const scriptPath = join10(
|
|
8859
8859
|
tmpdir2(),
|
|
8860
8860
|
`syntaur-url-handler-${process.pid}.applescript`
|
|
8861
8861
|
);
|
|
@@ -8874,7 +8874,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8874
8874
|
`osacompile exited with code ${compile.status}: ${compile.stderr || compile.stdout}`
|
|
8875
8875
|
);
|
|
8876
8876
|
}
|
|
8877
|
-
const infoPlistPath =
|
|
8877
|
+
const infoPlistPath = join10(bundlePath, "Contents", "Info.plist");
|
|
8878
8878
|
if (!existsSync5(infoPlistPath)) {
|
|
8879
8879
|
throw new Error(`osacompile did not produce ${infoPlistPath}`);
|
|
8880
8880
|
}
|
|
@@ -8914,23 +8914,23 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8914
8914
|
{ stdio: "pipe", encoding: "utf-8" }
|
|
8915
8915
|
);
|
|
8916
8916
|
if (sign.status !== 0) {
|
|
8917
|
-
const
|
|
8917
|
+
const msg2 = `codesign returned ${sign.status} while re-signing ${bundlePath}: ${sign.stderr || sign.stdout || "(no output)"}`;
|
|
8918
8918
|
if (throwOnFailure) {
|
|
8919
|
-
throw new Error(
|
|
8919
|
+
throw new Error(msg2);
|
|
8920
8920
|
}
|
|
8921
|
-
console.warn(`syntaur: ${
|
|
8921
|
+
console.warn(`syntaur: ${msg2} \u2014 macOS may deny Automation permission.`);
|
|
8922
8922
|
}
|
|
8923
8923
|
const ls = spawnSync7(LSREGISTER, ["-f", bundlePath], {
|
|
8924
8924
|
stdio: "pipe",
|
|
8925
8925
|
encoding: "utf-8"
|
|
8926
8926
|
});
|
|
8927
8927
|
if (ls.status !== 0) {
|
|
8928
|
-
const
|
|
8928
|
+
const msg2 = `lsregister returned ${ls.status} while registering ${bundlePath}: ${ls.stderr || ls.stdout || "(no output)"}`;
|
|
8929
8929
|
if (throwOnFailure) {
|
|
8930
|
-
throw new Error(
|
|
8930
|
+
throw new Error(msg2);
|
|
8931
8931
|
}
|
|
8932
8932
|
console.warn(
|
|
8933
|
-
`syntaur: ${
|
|
8933
|
+
`syntaur: ${msg2} \u2014 \`open syntaur://...\` may not route through the CLI handler until you run \`${LSREGISTER} -f ${bundlePath}\` manually.`
|
|
8934
8934
|
);
|
|
8935
8935
|
}
|
|
8936
8936
|
try {
|
|
@@ -8953,8 +8953,8 @@ async function main() {
|
|
|
8953
8953
|
try {
|
|
8954
8954
|
await registerMacosUrlHandler({ throwOnFailure: false });
|
|
8955
8955
|
} catch (err2) {
|
|
8956
|
-
const
|
|
8957
|
-
console.warn(`syntaur: skipping macOS URL-handler registration (${
|
|
8956
|
+
const msg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
8957
|
+
console.warn(`syntaur: skipping macOS URL-handler registration (${msg2})`);
|
|
8958
8958
|
}
|
|
8959
8959
|
}
|
|
8960
8960
|
function detectInstalledTerminals() {
|
|
@@ -8969,7 +8969,7 @@ function detectInstalledTerminals() {
|
|
|
8969
8969
|
ghostty: "Ghostty.app",
|
|
8970
8970
|
warp: "Warp.app"
|
|
8971
8971
|
};
|
|
8972
|
-
const appDirs = ["/Applications",
|
|
8972
|
+
const appDirs = ["/Applications", join10(homedir9(), "Applications")];
|
|
8973
8973
|
for (const [id, bundleId] of Object.entries(bundleIds)) {
|
|
8974
8974
|
const r = spawnSync7(
|
|
8975
8975
|
"mdfind",
|
|
@@ -8981,7 +8981,7 @@ function detectInstalledTerminals() {
|
|
|
8981
8981
|
continue;
|
|
8982
8982
|
}
|
|
8983
8983
|
const name = bundleNames[id];
|
|
8984
|
-
if (name && appDirs.some((dir) => existsSync5(
|
|
8984
|
+
if (name && appDirs.some((dir) => existsSync5(join10(dir, name)))) {
|
|
8985
8985
|
installed.add(id);
|
|
8986
8986
|
}
|
|
8987
8987
|
}
|
|
@@ -16160,12 +16160,12 @@ function createPlaybooksRouter(playbooksDir3) {
|
|
|
16160
16160
|
const result = await setPlaybookEnabled(playbooksDir3, req.params.slug, true);
|
|
16161
16161
|
res.json({ slug: result.slug, enabled: result.enabled, changed: result.changed });
|
|
16162
16162
|
} catch (error) {
|
|
16163
|
-
const
|
|
16164
|
-
if (
|
|
16165
|
-
res.status(404).json({ error:
|
|
16163
|
+
const msg2 = error instanceof Error ? error.message : "Failed to enable playbook";
|
|
16164
|
+
if (msg2.startsWith("Playbook ")) {
|
|
16165
|
+
res.status(404).json({ error: msg2 });
|
|
16166
16166
|
return;
|
|
16167
16167
|
}
|
|
16168
|
-
res.status(500).json({ error:
|
|
16168
|
+
res.status(500).json({ error: msg2 });
|
|
16169
16169
|
}
|
|
16170
16170
|
});
|
|
16171
16171
|
router.post("/:slug/disable", async (req, res) => {
|
|
@@ -16173,12 +16173,12 @@ function createPlaybooksRouter(playbooksDir3) {
|
|
|
16173
16173
|
const result = await setPlaybookEnabled(playbooksDir3, req.params.slug, false);
|
|
16174
16174
|
res.json({ slug: result.slug, enabled: result.enabled, changed: result.changed });
|
|
16175
16175
|
} catch (error) {
|
|
16176
|
-
const
|
|
16177
|
-
if (
|
|
16178
|
-
res.status(404).json({ error:
|
|
16176
|
+
const msg2 = error instanceof Error ? error.message : "Failed to disable playbook";
|
|
16177
|
+
if (msg2.startsWith("Playbook ")) {
|
|
16178
|
+
res.status(404).json({ error: msg2 });
|
|
16179
16179
|
return;
|
|
16180
16180
|
}
|
|
16181
|
-
res.status(500).json({ error:
|
|
16181
|
+
res.status(500).json({ error: msg2 });
|
|
16182
16182
|
}
|
|
16183
16183
|
});
|
|
16184
16184
|
router.get("/:slug", async (req, res) => {
|
|
@@ -16916,8 +16916,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
16916
16916
|
router.post("/:workspace/archive", async (req, res) => {
|
|
16917
16917
|
try {
|
|
16918
16918
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
16919
|
-
const { resolve:
|
|
16920
|
-
const { readFile:
|
|
16919
|
+
const { resolve: resolve87 } = await import("path");
|
|
16920
|
+
const { readFile: readFile64 } = await import("fs/promises");
|
|
16921
16921
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
16922
16922
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
16923
16923
|
const outcome = await wsLock(workspace, async () => {
|
|
@@ -16933,10 +16933,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
16933
16933
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
16934
16934
|
);
|
|
16935
16935
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
16936
|
-
await ensureDir(
|
|
16936
|
+
await ensureDir(resolve87(todosDir2, "archive"));
|
|
16937
16937
|
let archContent = "";
|
|
16938
16938
|
if (await fileExists(archFile)) {
|
|
16939
|
-
archContent = await
|
|
16939
|
+
archContent = await readFile64(archFile, "utf-8");
|
|
16940
16940
|
archContent = archContent.trimEnd() + "\n\n";
|
|
16941
16941
|
} else {
|
|
16942
16942
|
archContent = `---
|
|
@@ -17225,7 +17225,7 @@ workspace: ${workspace}
|
|
|
17225
17225
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
17226
17226
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
17227
17227
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
17228
|
-
const { readFile:
|
|
17228
|
+
const { readFile: readFile64 } = await import("fs/promises");
|
|
17229
17229
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
17230
17230
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
17231
17231
|
let assignmentRef;
|
|
@@ -17246,7 +17246,7 @@ workspace: ${workspace}
|
|
|
17246
17246
|
}
|
|
17247
17247
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
17248
17248
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
17249
|
-
let content = await
|
|
17249
|
+
let content = await readFile64(assignmentMdPath2, "utf-8");
|
|
17250
17250
|
content = appendTodosToAssignmentBody2(
|
|
17251
17251
|
content,
|
|
17252
17252
|
items.map((it) => ({
|
|
@@ -18920,8 +18920,8 @@ async function restoreFromGithub(overrides) {
|
|
|
18920
18920
|
await safeRestoreCategory(localPath, repoSrcPath, isFile);
|
|
18921
18921
|
restored.push(category);
|
|
18922
18922
|
} catch (error) {
|
|
18923
|
-
const
|
|
18924
|
-
console.error(`Failed to restore "${category}": ${
|
|
18923
|
+
const msg2 = error instanceof Error ? error.message : String(error);
|
|
18924
|
+
console.error(`Failed to restore "${category}": ${msg2}`);
|
|
18925
18925
|
failed.push(category);
|
|
18926
18926
|
}
|
|
18927
18927
|
}
|
|
@@ -22083,13 +22083,13 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
22083
22083
|
env: plan.env
|
|
22084
22084
|
});
|
|
22085
22085
|
} catch (err2) {
|
|
22086
|
-
const
|
|
22086
|
+
const msg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
22087
22087
|
throw new TerminalNotFoundError(
|
|
22088
22088
|
plan.terminal,
|
|
22089
|
-
`Spawn failed: ${
|
|
22089
|
+
`Spawn failed: ${msg2}. Verify the terminal is installed and on PATH.`
|
|
22090
22090
|
);
|
|
22091
22091
|
}
|
|
22092
|
-
await new Promise((
|
|
22092
|
+
await new Promise((resolve87, reject) => {
|
|
22093
22093
|
let settled = false;
|
|
22094
22094
|
let stderr = "";
|
|
22095
22095
|
const finishOk = () => {
|
|
@@ -22099,7 +22099,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
22099
22099
|
child.unref();
|
|
22100
22100
|
} catch {
|
|
22101
22101
|
}
|
|
22102
|
-
|
|
22102
|
+
resolve87();
|
|
22103
22103
|
};
|
|
22104
22104
|
const finishErr = (remediation) => {
|
|
22105
22105
|
if (settled) return;
|
|
@@ -22270,7 +22270,7 @@ function normalizeSlashes(p) {
|
|
|
22270
22270
|
}
|
|
22271
22271
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
22272
22272
|
const realpath3 = opts.realpath ?? realpathSync.native;
|
|
22273
|
-
const
|
|
22273
|
+
const readFile64 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
|
|
22274
22274
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
22275
22275
|
const resolved = resolveScriptPath(scriptUrl, realpath3);
|
|
22276
22276
|
if (resolved === null) {
|
|
@@ -22291,7 +22291,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
22291
22291
|
const pkgJsonPath = join5(dir, "package.json");
|
|
22292
22292
|
let raw2;
|
|
22293
22293
|
try {
|
|
22294
|
-
raw2 =
|
|
22294
|
+
raw2 = readFile64(pkgJsonPath);
|
|
22295
22295
|
} catch {
|
|
22296
22296
|
const parent2 = dirname12(dir);
|
|
22297
22297
|
if (parent2 === dir) break;
|
|
@@ -23428,36 +23428,267 @@ init_config2();
|
|
|
23428
23428
|
// src/commands/cross-agent-install.ts
|
|
23429
23429
|
init_fs();
|
|
23430
23430
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
23431
|
-
import { resolve as
|
|
23432
|
-
import { readFile as
|
|
23431
|
+
import { dirname as dirname17, join as join9, resolve as resolve48 } from "path";
|
|
23432
|
+
import { cp as cp4, mkdir as mkdir8, readFile as readFile32 } from "fs/promises";
|
|
23433
23433
|
|
|
23434
23434
|
// src/commands/setup-adapter.ts
|
|
23435
23435
|
init_paths();
|
|
23436
23436
|
init_fs();
|
|
23437
23437
|
init_config2();
|
|
23438
23438
|
init_slug();
|
|
23439
|
-
import { resolve as
|
|
23439
|
+
import { resolve as resolve47 } from "path";
|
|
23440
23440
|
|
|
23441
23441
|
// src/targets/registry.ts
|
|
23442
23442
|
init_fs();
|
|
23443
23443
|
import { homedir as homedir8 } from "os";
|
|
23444
|
+
import { resolve as resolve46 } from "path";
|
|
23445
|
+
|
|
23446
|
+
// src/targets/user-descriptors.ts
|
|
23447
|
+
init_fs();
|
|
23448
|
+
init_paths();
|
|
23444
23449
|
import { resolve as resolve45 } from "path";
|
|
23450
|
+
import { readFile as readFile31, readdir as readdir17 } from "fs/promises";
|
|
23451
|
+
|
|
23452
|
+
// src/targets/renderers.ts
|
|
23453
|
+
init_cursor_rules();
|
|
23454
|
+
init_codex_agents();
|
|
23455
|
+
init_opencode_config();
|
|
23456
|
+
init_hermes_soul();
|
|
23457
|
+
var RENDERERS = {
|
|
23458
|
+
codexAgents: (ctx) => renderCodexAgents(ctx),
|
|
23459
|
+
cursorProtocol: () => renderCursorProtocol(),
|
|
23460
|
+
cursorAssignment: (ctx) => renderCursorAssignment(ctx),
|
|
23461
|
+
openCodeConfig: (ctx) => renderOpenCodeConfig({ projectDir: ctx.projectDir }),
|
|
23462
|
+
hermesSoul: (ctx) => renderHermesSoul(ctx)
|
|
23463
|
+
};
|
|
23464
|
+
|
|
23465
|
+
// src/targets/user-descriptors.ts
|
|
23466
|
+
function userTargetsDir() {
|
|
23467
|
+
return resolve45(syntaurRoot(), "targets");
|
|
23468
|
+
}
|
|
23469
|
+
var ID_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
|
23470
|
+
var VALID_RENDERER_KEYS = new Set(Object.keys(RENDERERS));
|
|
23471
|
+
var ALLOWED_TOP_KEYS = /* @__PURE__ */ new Set([
|
|
23472
|
+
"id",
|
|
23473
|
+
"displayName",
|
|
23474
|
+
"skillsShAgentId",
|
|
23475
|
+
"detect",
|
|
23476
|
+
"skillsDir",
|
|
23477
|
+
"instructions"
|
|
23478
|
+
]);
|
|
23479
|
+
function msg(err2) {
|
|
23480
|
+
return err2 instanceof Error ? err2.message : String(err2);
|
|
23481
|
+
}
|
|
23482
|
+
function expandHomeAndEnv(p) {
|
|
23483
|
+
const envExpanded = p.replace(
|
|
23484
|
+
/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,
|
|
23485
|
+
(_m, braced, bare) => {
|
|
23486
|
+
const name = braced ?? bare ?? "";
|
|
23487
|
+
const v = process.env[name];
|
|
23488
|
+
return v ?? "";
|
|
23489
|
+
}
|
|
23490
|
+
);
|
|
23491
|
+
return resolve45(expandHome(envExpanded));
|
|
23492
|
+
}
|
|
23493
|
+
function compileDetect(spec) {
|
|
23494
|
+
switch (spec.kind) {
|
|
23495
|
+
case "pathExists": {
|
|
23496
|
+
const p = expandHomeAndEnv(spec.path);
|
|
23497
|
+
return () => fileExists(p);
|
|
23498
|
+
}
|
|
23499
|
+
case "anyPathExists": {
|
|
23500
|
+
const ps = spec.paths.map(expandHomeAndEnv);
|
|
23501
|
+
return async () => {
|
|
23502
|
+
for (const p of ps) {
|
|
23503
|
+
if (await fileExists(p)) return true;
|
|
23504
|
+
}
|
|
23505
|
+
return false;
|
|
23506
|
+
};
|
|
23507
|
+
}
|
|
23508
|
+
case "envSet": {
|
|
23509
|
+
const env = spec.env;
|
|
23510
|
+
return async () => {
|
|
23511
|
+
const v = process.env[env];
|
|
23512
|
+
return typeof v === "string" && v.length > 0;
|
|
23513
|
+
};
|
|
23514
|
+
}
|
|
23515
|
+
}
|
|
23516
|
+
}
|
|
23517
|
+
function isPlainObject(v) {
|
|
23518
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
23519
|
+
}
|
|
23520
|
+
function validateDetect(detect, errors) {
|
|
23521
|
+
if (!isPlainObject(detect)) {
|
|
23522
|
+
errors.push("detect must be an object");
|
|
23523
|
+
return;
|
|
23524
|
+
}
|
|
23525
|
+
switch (detect.kind) {
|
|
23526
|
+
case "pathExists":
|
|
23527
|
+
if (typeof detect.path !== "string" || detect.path.length === 0)
|
|
23528
|
+
errors.push('detect.path must be a non-empty string for kind "pathExists"');
|
|
23529
|
+
break;
|
|
23530
|
+
case "anyPathExists":
|
|
23531
|
+
if (!Array.isArray(detect.paths) || detect.paths.length === 0 || !detect.paths.every((p) => typeof p === "string" && p.length > 0))
|
|
23532
|
+
errors.push('detect.paths must be a non-empty string[] for kind "anyPathExists"');
|
|
23533
|
+
break;
|
|
23534
|
+
case "envSet":
|
|
23535
|
+
if (typeof detect.env !== "string" || detect.env.length === 0)
|
|
23536
|
+
errors.push('detect.env must be a non-empty string for kind "envSet"');
|
|
23537
|
+
break;
|
|
23538
|
+
default:
|
|
23539
|
+
errors.push(
|
|
23540
|
+
`detect.kind must be one of pathExists|anyPathExists|envSet (got ${JSON.stringify(detect.kind)})`
|
|
23541
|
+
);
|
|
23542
|
+
}
|
|
23543
|
+
}
|
|
23544
|
+
function validateInstructions(instructions, errors) {
|
|
23545
|
+
if (!isPlainObject(instructions)) {
|
|
23546
|
+
errors.push("instructions must be an object");
|
|
23547
|
+
return;
|
|
23548
|
+
}
|
|
23549
|
+
if (!Array.isArray(instructions.files) || instructions.files.length === 0) {
|
|
23550
|
+
errors.push("instructions.files must be a non-empty array");
|
|
23551
|
+
return;
|
|
23552
|
+
}
|
|
23553
|
+
instructions.files.forEach((f, i) => {
|
|
23554
|
+
if (!isPlainObject(f)) {
|
|
23555
|
+
errors.push(`instructions.files[${i}] must be an object`);
|
|
23556
|
+
return;
|
|
23557
|
+
}
|
|
23558
|
+
if (typeof f.path !== "string" || f.path.length === 0)
|
|
23559
|
+
errors.push(`instructions.files[${i}].path must be a non-empty string`);
|
|
23560
|
+
if (typeof f.renderer !== "string" || !VALID_RENDERER_KEYS.has(f.renderer))
|
|
23561
|
+
errors.push(
|
|
23562
|
+
`instructions.files[${i}].renderer ${JSON.stringify(f.renderer)} is not a known renderer (valid: ${[...VALID_RENDERER_KEYS].join(", ")})`
|
|
23563
|
+
);
|
|
23564
|
+
});
|
|
23565
|
+
}
|
|
23566
|
+
function validateUserDescriptor(data, builtinIds) {
|
|
23567
|
+
const errors = [];
|
|
23568
|
+
if (!isPlainObject(data)) {
|
|
23569
|
+
return { ok: false, errors: ["descriptor must be a JSON object"] };
|
|
23570
|
+
}
|
|
23571
|
+
for (const k of Object.keys(data)) {
|
|
23572
|
+
if (!ALLOWED_TOP_KEYS.has(k)) errors.push(`unknown field "${k}"`);
|
|
23573
|
+
}
|
|
23574
|
+
if (typeof data.id !== "string" || data.id.length === 0) {
|
|
23575
|
+
errors.push("id is required and must be a non-empty string");
|
|
23576
|
+
} else if (!ID_RE.test(data.id)) {
|
|
23577
|
+
errors.push(`id "${data.id}" must match ${ID_RE.source}`);
|
|
23578
|
+
} else if (builtinIds.has(data.id)) {
|
|
23579
|
+
errors.push(
|
|
23580
|
+
`id "${data.id}" collides with a built-in agent (built-ins cannot be overridden)`
|
|
23581
|
+
);
|
|
23582
|
+
}
|
|
23583
|
+
if (typeof data.displayName !== "string" || data.displayName.trim().length === 0) {
|
|
23584
|
+
errors.push("displayName is required and must be a non-empty string");
|
|
23585
|
+
}
|
|
23586
|
+
if (data.skillsShAgentId !== void 0 && (typeof data.skillsShAgentId !== "string" || data.skillsShAgentId.length === 0)) {
|
|
23587
|
+
errors.push("skillsShAgentId must be a non-empty string when present");
|
|
23588
|
+
}
|
|
23589
|
+
if (data.detect === void 0) {
|
|
23590
|
+
errors.push("detect is required");
|
|
23591
|
+
} else {
|
|
23592
|
+
validateDetect(data.detect, errors);
|
|
23593
|
+
}
|
|
23594
|
+
if (data.skillsDir !== void 0) {
|
|
23595
|
+
if (!isPlainObject(data.skillsDir)) {
|
|
23596
|
+
errors.push("skillsDir must be an object");
|
|
23597
|
+
} else {
|
|
23598
|
+
for (const k of Object.keys(data.skillsDir)) {
|
|
23599
|
+
if (k !== "project" && k !== "global") errors.push(`skillsDir.${k} is not a valid key`);
|
|
23600
|
+
}
|
|
23601
|
+
if (data.skillsDir.project !== void 0 && typeof data.skillsDir.project !== "string")
|
|
23602
|
+
errors.push("skillsDir.project must be a string");
|
|
23603
|
+
if (data.skillsDir.global !== void 0 && typeof data.skillsDir.global !== "string")
|
|
23604
|
+
errors.push("skillsDir.global must be a string");
|
|
23605
|
+
}
|
|
23606
|
+
}
|
|
23607
|
+
if (data.instructions !== void 0) {
|
|
23608
|
+
validateInstructions(data.instructions, errors);
|
|
23609
|
+
}
|
|
23610
|
+
if (errors.length > 0) return { ok: false, errors };
|
|
23611
|
+
return { ok: true, value: data };
|
|
23612
|
+
}
|
|
23613
|
+
function compileDescriptor(desc) {
|
|
23614
|
+
const target = {
|
|
23615
|
+
id: desc.id,
|
|
23616
|
+
displayName: desc.displayName,
|
|
23617
|
+
detect: compileDetect(desc.detect)
|
|
23618
|
+
};
|
|
23619
|
+
if (desc.skillsShAgentId) target.skillsShAgentId = desc.skillsShAgentId;
|
|
23620
|
+
if (desc.skillsDir) {
|
|
23621
|
+
const skillsDir = {};
|
|
23622
|
+
if (desc.skillsDir.global) skillsDir.global = expandHomeAndEnv(desc.skillsDir.global);
|
|
23623
|
+
if (desc.skillsDir.project) skillsDir.project = desc.skillsDir.project;
|
|
23624
|
+
target.skillsDir = skillsDir;
|
|
23625
|
+
}
|
|
23626
|
+
if (desc.instructions) target.instructions = desc.instructions;
|
|
23627
|
+
return target;
|
|
23628
|
+
}
|
|
23629
|
+
async function loadUserDescriptors(opts = {}) {
|
|
23630
|
+
const dir = opts.dir ?? userTargetsDir();
|
|
23631
|
+
const builtinIds = opts.builtinIds ?? /* @__PURE__ */ new Set();
|
|
23632
|
+
const warnings = [];
|
|
23633
|
+
if (!await fileExists(dir)) return { targets: [], warnings };
|
|
23634
|
+
let files;
|
|
23635
|
+
try {
|
|
23636
|
+
files = (await readdir17(dir)).filter((f) => f.endsWith(".json")).sort();
|
|
23637
|
+
} catch (err2) {
|
|
23638
|
+
return { targets: [], warnings: [`could not read ${dir}: ${msg(err2)}`] };
|
|
23639
|
+
}
|
|
23640
|
+
const targets = [];
|
|
23641
|
+
const seen = /* @__PURE__ */ new Set();
|
|
23642
|
+
for (const file of files) {
|
|
23643
|
+
const full = resolve45(dir, file);
|
|
23644
|
+
let raw2;
|
|
23645
|
+
try {
|
|
23646
|
+
raw2 = await readFile31(full, "utf-8");
|
|
23647
|
+
} catch (err2) {
|
|
23648
|
+
warnings.push(`skipped ${file}: ${msg(err2)}`);
|
|
23649
|
+
continue;
|
|
23650
|
+
}
|
|
23651
|
+
let parsed;
|
|
23652
|
+
try {
|
|
23653
|
+
parsed = JSON.parse(raw2);
|
|
23654
|
+
} catch (err2) {
|
|
23655
|
+
warnings.push(`skipped ${file}: invalid JSON: ${msg(err2)}`);
|
|
23656
|
+
continue;
|
|
23657
|
+
}
|
|
23658
|
+
const result = validateUserDescriptor(parsed, builtinIds);
|
|
23659
|
+
if (!result.ok) {
|
|
23660
|
+
warnings.push(`skipped ${file}: ${result.errors.join("; ")}`);
|
|
23661
|
+
continue;
|
|
23662
|
+
}
|
|
23663
|
+
if (seen.has(result.value.id)) {
|
|
23664
|
+
warnings.push(
|
|
23665
|
+
`skipped ${file}: duplicate id "${result.value.id}" (already loaded from an earlier file)`
|
|
23666
|
+
);
|
|
23667
|
+
continue;
|
|
23668
|
+
}
|
|
23669
|
+
seen.add(result.value.id);
|
|
23670
|
+
targets.push(compileDescriptor(result.value));
|
|
23671
|
+
}
|
|
23672
|
+
return { targets, warnings };
|
|
23673
|
+
}
|
|
23674
|
+
|
|
23675
|
+
// src/targets/registry.ts
|
|
23445
23676
|
function home(...segments) {
|
|
23446
|
-
return
|
|
23677
|
+
return resolve46(homedir8(), ...segments);
|
|
23447
23678
|
}
|
|
23448
23679
|
function hermesHome() {
|
|
23449
23680
|
const env = process.env.HERMES_HOME;
|
|
23450
|
-
return env && env.length > 0 ?
|
|
23681
|
+
return env && env.length > 0 ? resolve46(env) : home(".hermes");
|
|
23451
23682
|
}
|
|
23452
23683
|
function hermesSkillsDir() {
|
|
23453
|
-
return
|
|
23684
|
+
return resolve46(hermesHome(), "skills");
|
|
23454
23685
|
}
|
|
23455
23686
|
function isHermesHomeCustom() {
|
|
23456
23687
|
return hermesHome() !== home(".hermes");
|
|
23457
23688
|
}
|
|
23458
23689
|
function codexHome() {
|
|
23459
23690
|
const env = process.env.CODEX_HOME;
|
|
23460
|
-
return env && env.length > 0 ?
|
|
23691
|
+
return env && env.length > 0 ? resolve46(env) : home(".codex");
|
|
23461
23692
|
}
|
|
23462
23693
|
var detectDir = (dir) => () => fileExists(dir);
|
|
23463
23694
|
var AGENT_TARGETS = [
|
|
@@ -23481,7 +23712,7 @@ var AGENT_TARGETS = [
|
|
|
23481
23712
|
skillsShAgentId: "codex",
|
|
23482
23713
|
nativePlugin: "codex",
|
|
23483
23714
|
detect: detectDir(codexHome()),
|
|
23484
|
-
skillsDir: { global:
|
|
23715
|
+
skillsDir: { global: resolve46(codexHome(), "skills") },
|
|
23485
23716
|
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
|
|
23486
23717
|
},
|
|
23487
23718
|
{
|
|
@@ -23513,7 +23744,13 @@ var AGENT_TARGETS = [
|
|
|
23513
23744
|
skillsShAgentId: "pi",
|
|
23514
23745
|
detect: detectDir(home(".pi")),
|
|
23515
23746
|
skillsDir: { global: home(".pi", "agent", "skills") },
|
|
23516
|
-
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
|
|
23747
|
+
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
|
|
23748
|
+
tier3: {
|
|
23749
|
+
kind: "pi-extension",
|
|
23750
|
+
source: "platforms/pi/extensions/syntaur",
|
|
23751
|
+
installDir: () => home(".pi", "agent", "extensions", "syntaur"),
|
|
23752
|
+
entry: "index.ts"
|
|
23753
|
+
}
|
|
23517
23754
|
},
|
|
23518
23755
|
{
|
|
23519
23756
|
id: "openclaw",
|
|
@@ -23521,7 +23758,15 @@ var AGENT_TARGETS = [
|
|
|
23521
23758
|
skillsShAgentId: "openclaw",
|
|
23522
23759
|
detect: detectDir(home(".openclaw")),
|
|
23523
23760
|
skillsDir: { global: home(".openclaw", "skills") },
|
|
23524
|
-
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] }
|
|
23761
|
+
instructions: { files: [{ path: "AGENTS.md", renderer: "codexAgents" }] },
|
|
23762
|
+
// OpenClaw runs on pi-coding-agent (design memo), so it reuses the pi
|
|
23763
|
+
// extension SOURCE; only the install dir differs.
|
|
23764
|
+
tier3: {
|
|
23765
|
+
kind: "pi-extension",
|
|
23766
|
+
source: "platforms/pi/extensions/syntaur",
|
|
23767
|
+
installDir: () => home(".openclaw", "extensions", "syntaur"),
|
|
23768
|
+
entry: "index.ts"
|
|
23769
|
+
}
|
|
23525
23770
|
},
|
|
23526
23771
|
{
|
|
23527
23772
|
id: "hermes",
|
|
@@ -23529,42 +23774,34 @@ var AGENT_TARGETS = [
|
|
|
23529
23774
|
skillsShAgentId: "hermes-agent",
|
|
23530
23775
|
detect: () => fileExists(hermesHome()),
|
|
23531
23776
|
skillsDir: { global: hermesSkillsDir() },
|
|
23532
|
-
instructions: { files: [{ path: "SOUL.md", renderer: "hermesSoul" }] }
|
|
23777
|
+
instructions: { files: [{ path: "SOUL.md", renderer: "hermesSoul" }] },
|
|
23778
|
+
tier3: {
|
|
23779
|
+
kind: "hermes-plugin",
|
|
23780
|
+
source: "platforms/hermes/plugins/syntaur",
|
|
23781
|
+
installDir: () => resolve46(hermesHome(), "plugins", "syntaur"),
|
|
23782
|
+
entry: "plugin.yaml"
|
|
23783
|
+
}
|
|
23533
23784
|
}
|
|
23534
23785
|
];
|
|
23535
23786
|
var AGENT_TARGETS_BY_ID = Object.fromEntries(
|
|
23536
23787
|
AGENT_TARGETS.map((t) => [t.id, t])
|
|
23537
23788
|
);
|
|
23538
|
-
function
|
|
23539
|
-
|
|
23540
|
-
|
|
23541
|
-
|
|
23542
|
-
return AGENT_TARGETS
|
|
23543
|
-
}
|
|
23544
|
-
function adapterTargets() {
|
|
23545
|
-
return AGENT_TARGETS.filter((t) => t.instructions !== void 0);
|
|
23789
|
+
async function resolveAgentTargets() {
|
|
23790
|
+
const { targets: user, warnings } = await loadUserDescriptors({
|
|
23791
|
+
builtinIds: new Set(AGENT_TARGETS.map((t) => t.id))
|
|
23792
|
+
});
|
|
23793
|
+
return { targets: [...AGENT_TARGETS, ...user], warnings };
|
|
23546
23794
|
}
|
|
23547
23795
|
|
|
23548
|
-
// src/targets/renderers.ts
|
|
23549
|
-
init_cursor_rules();
|
|
23550
|
-
init_codex_agents();
|
|
23551
|
-
init_opencode_config();
|
|
23552
|
-
init_hermes_soul();
|
|
23553
|
-
var RENDERERS = {
|
|
23554
|
-
codexAgents: (ctx) => renderCodexAgents(ctx),
|
|
23555
|
-
cursorProtocol: () => renderCursorProtocol(),
|
|
23556
|
-
cursorAssignment: (ctx) => renderCursorAssignment(ctx),
|
|
23557
|
-
openCodeConfig: (ctx) => renderOpenCodeConfig({ projectDir: ctx.projectDir }),
|
|
23558
|
-
hermesSoul: (ctx) => renderHermesSoul(ctx)
|
|
23559
|
-
};
|
|
23560
|
-
|
|
23561
23796
|
// src/commands/setup-adapter.ts
|
|
23562
23797
|
async function setupAdapterCommand(framework, options) {
|
|
23563
|
-
const
|
|
23798
|
+
const { targets: known, warnings } = await resolveAgentTargets();
|
|
23799
|
+
const target = known.find((t) => t.id === framework);
|
|
23564
23800
|
if (!target || !target.instructions) {
|
|
23565
|
-
const supported =
|
|
23801
|
+
const supported = known.filter((t) => t.instructions !== void 0).map((t) => t.id).join(", ");
|
|
23802
|
+
const warn2 = warnings.length ? ` (descriptor warnings: ${warnings.join("; ")})` : "";
|
|
23566
23803
|
throw new Error(
|
|
23567
|
-
`Unsupported framework "${framework}". Supported: ${supported}`
|
|
23804
|
+
`Unsupported framework "${framework}". Supported: ${supported}.${warn2}`
|
|
23568
23805
|
);
|
|
23569
23806
|
}
|
|
23570
23807
|
if (!options.project) {
|
|
@@ -23585,13 +23822,13 @@ async function setupAdapterCommand(framework, options) {
|
|
|
23585
23822
|
}
|
|
23586
23823
|
const config = await readConfig();
|
|
23587
23824
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
23588
|
-
const projectDir =
|
|
23589
|
-
const assignmentDir =
|
|
23590
|
-
const projectMdPath =
|
|
23825
|
+
const projectDir = resolve47(baseDir, options.project);
|
|
23826
|
+
const assignmentDir = resolve47(projectDir, "assignments", options.assignment);
|
|
23827
|
+
const projectMdPath = resolve47(projectDir, "project.md");
|
|
23591
23828
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
23592
23829
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
23593
23830
|
}
|
|
23594
|
-
const assignmentMdPath2 =
|
|
23831
|
+
const assignmentMdPath2 = resolve47(assignmentDir, "assignment.md");
|
|
23595
23832
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
|
|
23596
23833
|
throw new Error(
|
|
23597
23834
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -23608,7 +23845,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
23608
23845
|
const upToDateFiles = [];
|
|
23609
23846
|
const skippedFiles = [];
|
|
23610
23847
|
for (const file of target.instructions.files) {
|
|
23611
|
-
const filePath =
|
|
23848
|
+
const filePath = resolve47(cwd, file.path);
|
|
23612
23849
|
const content = RENDERERS[file.renderer](rendererParams);
|
|
23613
23850
|
const status = await writeFileReport(filePath, content, {
|
|
23614
23851
|
force: options.force
|
|
@@ -23647,6 +23884,33 @@ async function setupAdapterCommand(framework, options) {
|
|
|
23647
23884
|
// src/commands/cross-agent-install.ts
|
|
23648
23885
|
init_config2();
|
|
23649
23886
|
var DEFAULT_SKILLS_SOURCE = "prong-horn/syntaur";
|
|
23887
|
+
async function installTier3Plugin(t, opts = {}) {
|
|
23888
|
+
if (!t.tier3) return "none";
|
|
23889
|
+
const plugin = t.tier3;
|
|
23890
|
+
const installDir = plugin.installDir();
|
|
23891
|
+
const prefix = opts.prefix ?? "";
|
|
23892
|
+
if (opts.dryRun) {
|
|
23893
|
+
console.log(`${prefix}Tier 3 (${t.id}): ${plugin.source} -> ${installDir}`);
|
|
23894
|
+
return "dry-run";
|
|
23895
|
+
}
|
|
23896
|
+
if (await fileExists(join9(installDir, plugin.entry)) && !opts.force) {
|
|
23897
|
+
console.log(
|
|
23898
|
+
`Tier 3 (${t.id}): already installed at ${installDir} (use --force to overwrite).`
|
|
23899
|
+
);
|
|
23900
|
+
return "already-present";
|
|
23901
|
+
}
|
|
23902
|
+
try {
|
|
23903
|
+
const sourceDir = resolve48(await findPackageRoot(plugin.source), plugin.source);
|
|
23904
|
+
await mkdir8(dirname17(installDir), { recursive: true });
|
|
23905
|
+
await cp4(sourceDir, installDir, { recursive: true, force: true });
|
|
23906
|
+
console.log(`Tier 3 (${t.id}): installed ${plugin.kind} -> ${installDir}`);
|
|
23907
|
+
return "installed";
|
|
23908
|
+
} catch (err2) {
|
|
23909
|
+
const msg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
23910
|
+
console.log(`Tier 3 for ${t.id} FAILED: ${msg2}`);
|
|
23911
|
+
return "failed";
|
|
23912
|
+
}
|
|
23913
|
+
}
|
|
23650
23914
|
function parseTargetIds(options) {
|
|
23651
23915
|
const raw2 = [options.target, options.agent].filter(Boolean).join(",");
|
|
23652
23916
|
const ids = raw2.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -23661,10 +23925,10 @@ function isNpxAvailable() {
|
|
|
23661
23925
|
}
|
|
23662
23926
|
}
|
|
23663
23927
|
async function readAssignmentContext() {
|
|
23664
|
-
const p =
|
|
23928
|
+
const p = resolve48(process.cwd(), ".syntaur", "context.json");
|
|
23665
23929
|
if (!await fileExists(p)) return null;
|
|
23666
23930
|
try {
|
|
23667
|
-
return JSON.parse(await
|
|
23931
|
+
return JSON.parse(await readFile32(p, "utf-8"));
|
|
23668
23932
|
} catch {
|
|
23669
23933
|
return null;
|
|
23670
23934
|
}
|
|
@@ -23674,12 +23938,15 @@ async function crossAgentInstallCommand(options) {
|
|
|
23674
23938
|
if (ids.length === 0) {
|
|
23675
23939
|
throw new Error("No agents specified. Use --target <id> or --agent <id>.");
|
|
23676
23940
|
}
|
|
23941
|
+
const { targets: known, warnings } = await resolveAgentTargets();
|
|
23942
|
+
for (const w of warnings) console.log(`Warning (user target): ${w}`);
|
|
23943
|
+
const byId = new Map(known.map((t) => [t.id, t]));
|
|
23677
23944
|
const targets = [];
|
|
23678
23945
|
for (const id of ids) {
|
|
23679
|
-
const t =
|
|
23946
|
+
const t = byId.get(id);
|
|
23680
23947
|
if (!t) {
|
|
23681
23948
|
throw new Error(
|
|
23682
|
-
`Unknown agent "${id}". Known agents: ${
|
|
23949
|
+
`Unknown agent "${id}". Known agents: ${known.map((k) => k.id).join(", ")}`
|
|
23683
23950
|
);
|
|
23684
23951
|
}
|
|
23685
23952
|
if (t.nativePlugin) {
|
|
@@ -23713,8 +23980,15 @@ async function crossAgentInstallCommand(options) {
|
|
|
23713
23980
|
}
|
|
23714
23981
|
for (const t of targets) {
|
|
23715
23982
|
const globalDir = t.id === "hermes" ? hermesSkillsDir() : t.skillsDir?.global;
|
|
23716
|
-
if (!globalDir) continue;
|
|
23717
23983
|
const offlineNeeded = !tier1Done;
|
|
23984
|
+
if (!globalDir) {
|
|
23985
|
+
if (offlineNeeded && !dryRun) {
|
|
23986
|
+
console.log(
|
|
23987
|
+
`Warning: skills NOT installed for ${t.displayName} \u2014 Tier-1 (npx skills add) was unavailable/failed and the descriptor has no skillsDir.global for an offline copy.`
|
|
23988
|
+
);
|
|
23989
|
+
}
|
|
23990
|
+
continue;
|
|
23991
|
+
}
|
|
23718
23992
|
const hermesCustom = t.id === "hermes" && isHermesHomeCustom();
|
|
23719
23993
|
if (!offlineNeeded && !hermesCustom) continue;
|
|
23720
23994
|
if (dryRun) {
|
|
@@ -23726,15 +24000,15 @@ async function crossAgentInstallCommand(options) {
|
|
|
23726
24000
|
await installSkillsToDir({ targetDir: globalDir, force });
|
|
23727
24001
|
console.log(`Copied skills -> ${globalDir}${reason}`);
|
|
23728
24002
|
}
|
|
23729
|
-
const
|
|
23730
|
-
if (
|
|
24003
|
+
const adapterTargets = targets.filter((t) => t.instructions);
|
|
24004
|
+
if (adapterTargets.length > 0) {
|
|
23731
24005
|
const ctx = await readAssignmentContext();
|
|
23732
24006
|
const haveCtx = Boolean(ctx?.projectSlug && ctx?.assignmentSlug);
|
|
23733
|
-
for (const t of
|
|
24007
|
+
for (const t of adapterTargets) {
|
|
23734
24008
|
if (dryRun) {
|
|
23735
24009
|
for (const f of t.instructions.files) {
|
|
23736
24010
|
console.log(
|
|
23737
|
-
`${prefix}Tier 2 (${t.id}): ${
|
|
24011
|
+
`${prefix}Tier 2 (${t.id}): ${resolve48(process.cwd(), f.path)}`
|
|
23738
24012
|
);
|
|
23739
24013
|
}
|
|
23740
24014
|
continue;
|
|
@@ -23752,11 +24026,21 @@ async function crossAgentInstallCommand(options) {
|
|
|
23752
24026
|
force
|
|
23753
24027
|
});
|
|
23754
24028
|
} catch (err2) {
|
|
23755
|
-
const
|
|
23756
|
-
console.log(`Tier 2 for ${t.id} skipped: ${
|
|
24029
|
+
const msg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
24030
|
+
console.log(`Tier 2 for ${t.id} skipped: ${msg2}`);
|
|
23757
24031
|
}
|
|
23758
24032
|
}
|
|
23759
24033
|
}
|
|
24034
|
+
const tier3Failures = [];
|
|
24035
|
+
for (const t of targets.filter((x) => x.tier3)) {
|
|
24036
|
+
const result = await installTier3Plugin(t, { dryRun, force, prefix });
|
|
24037
|
+
if (result === "failed") tier3Failures.push(t.displayName);
|
|
24038
|
+
}
|
|
24039
|
+
if (tier3Failures.length > 0) {
|
|
24040
|
+
throw new Error(
|
|
24041
|
+
`Tier-3 enforcement plugin failed to install for: ${tier3Failures.join(", ")}. Enforcement is NOT active for ${tier3Failures.length > 1 ? "those agents" : "that agent"} \u2014 check permissions and re-run \`syntaur setup --target <id> --force\`.`
|
|
24042
|
+
);
|
|
24043
|
+
}
|
|
23760
24044
|
if (!dryRun) {
|
|
23761
24045
|
const current = (await readConfig()).integrations.installedAgents ?? {};
|
|
23762
24046
|
const next = { ...current };
|
|
@@ -23881,7 +24165,7 @@ async function setupCommand(options) {
|
|
|
23881
24165
|
}
|
|
23882
24166
|
|
|
23883
24167
|
// src/commands/uninstall.ts
|
|
23884
|
-
import { resolve as
|
|
24168
|
+
import { resolve as resolve49 } from "path";
|
|
23885
24169
|
init_paths();
|
|
23886
24170
|
function expandTargets(options) {
|
|
23887
24171
|
if (options.all) {
|
|
@@ -23961,7 +24245,7 @@ async function uninstallCommand(options) {
|
|
|
23961
24245
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
23962
24246
|
await removeSyntaurData();
|
|
23963
24247
|
console.log(`Removed ${syntaurRoot()}`);
|
|
23964
|
-
if (configuredProjectDir &&
|
|
24248
|
+
if (configuredProjectDir && resolve49(configuredProjectDir) !== resolve49(syntaurRoot(), "projects")) {
|
|
23965
24249
|
console.warn(
|
|
23966
24250
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
23967
24251
|
);
|
|
@@ -23976,7 +24260,7 @@ async function uninstallCommand(options) {
|
|
|
23976
24260
|
init_paths();
|
|
23977
24261
|
init_fs();
|
|
23978
24262
|
init_config2();
|
|
23979
|
-
import { resolve as
|
|
24263
|
+
import { resolve as resolve50 } from "path";
|
|
23980
24264
|
init_git_worktree();
|
|
23981
24265
|
init_cwd();
|
|
23982
24266
|
init_session_db();
|
|
@@ -23993,7 +24277,7 @@ async function trackSessionCommand(options) {
|
|
|
23993
24277
|
if (options.project) {
|
|
23994
24278
|
const config = await readConfig();
|
|
23995
24279
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
23996
|
-
const projectDir =
|
|
24280
|
+
const projectDir = resolve50(baseDir, options.project);
|
|
23997
24281
|
if (!await fileExists(projectDir)) {
|
|
23998
24282
|
throw new Error(
|
|
23999
24283
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -24127,8 +24411,8 @@ function formatInstallUrlHandlerError(err2) {
|
|
|
24127
24411
|
init_config2();
|
|
24128
24412
|
init_paths();
|
|
24129
24413
|
init_fs();
|
|
24130
|
-
import { resolve as
|
|
24131
|
-
import { readFile as
|
|
24414
|
+
import { resolve as resolve52, isAbsolute as isAbsolute8 } from "path";
|
|
24415
|
+
import { readFile as readFile33 } from "fs/promises";
|
|
24132
24416
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
24133
24417
|
async function browseCommand(options) {
|
|
24134
24418
|
const config = await readConfig();
|
|
@@ -24197,7 +24481,7 @@ async function pickAgent2(agents) {
|
|
|
24197
24481
|
return picked;
|
|
24198
24482
|
}
|
|
24199
24483
|
async function ensureWorktree(opts) {
|
|
24200
|
-
const assignmentPath =
|
|
24484
|
+
const assignmentPath = resolve52(
|
|
24201
24485
|
opts.projectsDir,
|
|
24202
24486
|
opts.projectSlug,
|
|
24203
24487
|
"assignments",
|
|
@@ -24207,7 +24491,7 @@ async function ensureWorktree(opts) {
|
|
|
24207
24491
|
if (!await fileExists(assignmentPath)) {
|
|
24208
24492
|
return void 0;
|
|
24209
24493
|
}
|
|
24210
|
-
const content = await
|
|
24494
|
+
const content = await readFile33(assignmentPath, "utf-8");
|
|
24211
24495
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
24212
24496
|
const fm = parseAssignmentFrontmatter2(content);
|
|
24213
24497
|
const { workspace } = fm;
|
|
@@ -24277,7 +24561,7 @@ async function ensureWorktree(opts) {
|
|
|
24277
24561
|
async function runCreate(opts) {
|
|
24278
24562
|
const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
24279
24563
|
const expandedWorktree = expandHome(opts.worktreePath);
|
|
24280
|
-
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree :
|
|
24564
|
+
const absWorktree = isAbsolute8(expandedWorktree) ? expandedWorktree : resolve52(expandedWorktree);
|
|
24281
24565
|
try {
|
|
24282
24566
|
await createWorktreeAndRecord2({
|
|
24283
24567
|
assignmentPath: opts.assignmentPath,
|
|
@@ -24289,11 +24573,11 @@ async function runCreate(opts) {
|
|
|
24289
24573
|
console.log(`syntaur: created worktree at ${absWorktree} on branch ${opts.branch}`);
|
|
24290
24574
|
return absWorktree;
|
|
24291
24575
|
} catch (err2) {
|
|
24292
|
-
const
|
|
24576
|
+
const msg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
24293
24577
|
if (err2 instanceof GitWorktreeError2) {
|
|
24294
|
-
console.error(`syntaur: ${
|
|
24578
|
+
console.error(`syntaur: ${msg2}`);
|
|
24295
24579
|
} else {
|
|
24296
|
-
console.error(`syntaur: ${
|
|
24580
|
+
console.error(`syntaur: ${msg2}`);
|
|
24297
24581
|
}
|
|
24298
24582
|
process.exit(1);
|
|
24299
24583
|
}
|
|
@@ -24306,7 +24590,7 @@ init_paths();
|
|
|
24306
24590
|
init_fs();
|
|
24307
24591
|
init_playbook();
|
|
24308
24592
|
init_playbooks();
|
|
24309
|
-
import { resolve as
|
|
24593
|
+
import { resolve as resolve53 } from "path";
|
|
24310
24594
|
async function createPlaybookCommand(name, options) {
|
|
24311
24595
|
if (!name.trim()) {
|
|
24312
24596
|
throw new Error("Playbook name cannot be empty.");
|
|
@@ -24319,7 +24603,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
24319
24603
|
}
|
|
24320
24604
|
const dir = playbooksDir();
|
|
24321
24605
|
await ensureDir(dir);
|
|
24322
|
-
const filePath =
|
|
24606
|
+
const filePath = resolve53(dir, `${slug}.md`);
|
|
24323
24607
|
if (await fileExists(filePath)) {
|
|
24324
24608
|
throw new Error(
|
|
24325
24609
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -24341,8 +24625,8 @@ init_paths();
|
|
|
24341
24625
|
init_fs();
|
|
24342
24626
|
init_parser();
|
|
24343
24627
|
init_config2();
|
|
24344
|
-
import { readdir as
|
|
24345
|
-
import { resolve as
|
|
24628
|
+
import { readdir as readdir18, readFile as readFile34 } from "fs/promises";
|
|
24629
|
+
import { resolve as resolve54 } from "path";
|
|
24346
24630
|
async function listPlaybooksCommand(options = {}) {
|
|
24347
24631
|
const dir = playbooksDir();
|
|
24348
24632
|
if (!await fileExists(dir)) {
|
|
@@ -24351,14 +24635,14 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
24351
24635
|
}
|
|
24352
24636
|
const config = await readConfig();
|
|
24353
24637
|
const disabledSet = new Set(config.playbooks.disabled);
|
|
24354
|
-
const entries = await
|
|
24638
|
+
const entries = await readdir18(dir, { withFileTypes: true });
|
|
24355
24639
|
const mdFiles = entries.filter(
|
|
24356
24640
|
(e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_") && e.name !== "manifest.md"
|
|
24357
24641
|
);
|
|
24358
24642
|
const rows = [];
|
|
24359
24643
|
for (const entry of mdFiles) {
|
|
24360
|
-
const filePath =
|
|
24361
|
-
const raw2 = await
|
|
24644
|
+
const filePath = resolve54(dir, entry.name);
|
|
24645
|
+
const raw2 = await readFile34(filePath, "utf-8");
|
|
24362
24646
|
const parsed = parsePlaybook(raw2);
|
|
24363
24647
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
24364
24648
|
const disabled = disabledSet.has(slug);
|
|
@@ -24481,14 +24765,14 @@ init_fs();
|
|
|
24481
24765
|
init_config2();
|
|
24482
24766
|
init_slug();
|
|
24483
24767
|
import { Command as Command2 } from "commander";
|
|
24484
|
-
import { readFile as
|
|
24485
|
-
import { resolve as
|
|
24768
|
+
import { readFile as readFile36 } from "fs/promises";
|
|
24769
|
+
import { resolve as resolve56 } from "path";
|
|
24486
24770
|
|
|
24487
24771
|
// src/commands/bundle.ts
|
|
24488
24772
|
init_paths();
|
|
24489
24773
|
import { Command } from "commander";
|
|
24490
|
-
import { mkdir as
|
|
24491
|
-
import { resolve as
|
|
24774
|
+
import { mkdir as mkdir9, readFile as readFile35, readdir as readdir19, rm as rm8, writeFile as writeFile13 } from "fs/promises";
|
|
24775
|
+
import { resolve as resolve55 } from "path";
|
|
24492
24776
|
init_parser2();
|
|
24493
24777
|
init_fs();
|
|
24494
24778
|
init_config2();
|
|
@@ -24506,7 +24790,7 @@ async function resolveBundleScope(options) {
|
|
|
24506
24790
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
24507
24791
|
}
|
|
24508
24792
|
const config = await readConfig();
|
|
24509
|
-
const projectMd =
|
|
24793
|
+
const projectMd = resolve55(config.defaultProjectDir, options.project, "project.md");
|
|
24510
24794
|
if (!await fileExists(projectMd)) {
|
|
24511
24795
|
throw new Error(`Project "${options.project}" not found.`);
|
|
24512
24796
|
}
|
|
@@ -24576,10 +24860,10 @@ function pickNextPlanFile(planDir, existingFiles) {
|
|
|
24576
24860
|
const m = f.match(/^plan-v(\d+)\.md$/);
|
|
24577
24861
|
if (m) versions.add(parseInt(m[1], 10));
|
|
24578
24862
|
}
|
|
24579
|
-
if (versions.size === 0) return { target:
|
|
24863
|
+
if (versions.size === 0) return { target: resolve55(planDir, "plan.md"), version: 1 };
|
|
24580
24864
|
let n = 2;
|
|
24581
24865
|
while (versions.has(n)) n++;
|
|
24582
|
-
return { target:
|
|
24866
|
+
return { target: resolve55(planDir, `plan-v${n}.md`), version: n };
|
|
24583
24867
|
}
|
|
24584
24868
|
function dedupePreserveOrder(ids) {
|
|
24585
24869
|
const out = [];
|
|
@@ -24663,7 +24947,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
|
|
|
24663
24947
|
if (options.plan) {
|
|
24664
24948
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
24665
24949
|
await ensureDir(planDir);
|
|
24666
|
-
const target =
|
|
24950
|
+
const target = resolve55(planDir, "plan.md");
|
|
24667
24951
|
const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
|
|
24668
24952
|
const stub = [
|
|
24669
24953
|
"---",
|
|
@@ -24780,7 +25064,7 @@ bundleCommand.command("plan").description("Create or open the bundle's shared pl
|
|
|
24780
25064
|
}
|
|
24781
25065
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
24782
25066
|
await ensureDir(planDir);
|
|
24783
|
-
const existing = (await
|
|
25067
|
+
const existing = (await readdir19(planDir).catch(() => [])).filter((f) => /^plan(?:-v\d+)?\.md$/.test(f));
|
|
24784
25068
|
const { target } = pickNextPlanFile(planDir, existing);
|
|
24785
25069
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
24786
25070
|
if (!await fileExists(target)) {
|
|
@@ -24839,7 +25123,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
24839
25123
|
}
|
|
24840
25124
|
const repository = options.repository ?? process.cwd();
|
|
24841
25125
|
const parentBranch = options.parentBranch ?? "main";
|
|
24842
|
-
const worktreePath = options.worktreePath ??
|
|
25126
|
+
const worktreePath = options.worktreePath ?? resolve55(repository, ".worktrees", options.branch);
|
|
24843
25127
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
24844
25128
|
for (const memberId of bundle.todoIds) {
|
|
24845
25129
|
const item = checklist.items.find((i) => i.id === memberId);
|
|
@@ -24849,8 +25133,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
24849
25133
|
}
|
|
24850
25134
|
const bundlesFilePath = bundlesPath(sc.todosPath);
|
|
24851
25135
|
const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
|
|
24852
|
-
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await
|
|
24853
|
-
const checklistSnapshot = await fileExists(checklistFilePath) ? await
|
|
25136
|
+
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile35(bundlesFilePath, "utf-8") : null;
|
|
25137
|
+
const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile35(checklistFilePath, "utf-8") : null;
|
|
24854
25138
|
const record = async () => {
|
|
24855
25139
|
bundle.branch = options.branch;
|
|
24856
25140
|
bundle.worktreePath = worktreePath;
|
|
@@ -24866,8 +25150,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
24866
25150
|
try {
|
|
24867
25151
|
await writeBundles(sc.todosPath, bundles);
|
|
24868
25152
|
await writeChecklist(sc.todosPath, checklist);
|
|
24869
|
-
const ctxDir =
|
|
24870
|
-
await
|
|
25153
|
+
const ctxDir = resolve55(worktreePath, ".syntaur");
|
|
25154
|
+
await mkdir9(ctxDir, { recursive: true });
|
|
24871
25155
|
const payload = {
|
|
24872
25156
|
bundleId: bundle.id,
|
|
24873
25157
|
bundleSlug: bundle.slug,
|
|
@@ -24880,7 +25164,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
24880
25164
|
repository,
|
|
24881
25165
|
boundAt: nowISO()
|
|
24882
25166
|
};
|
|
24883
|
-
await writeFile13(
|
|
25167
|
+
await writeFile13(resolve55(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
|
|
24884
25168
|
} catch (err2) {
|
|
24885
25169
|
try {
|
|
24886
25170
|
if (bundlesSnapshot === null) {
|
|
@@ -25070,7 +25354,7 @@ async function resolveScope(options) {
|
|
|
25070
25354
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
25071
25355
|
}
|
|
25072
25356
|
const config = await readConfig();
|
|
25073
|
-
const projectMd =
|
|
25357
|
+
const projectMd = resolve56(config.defaultProjectDir, options.project, "project.md");
|
|
25074
25358
|
if (!await fileExists(projectMd)) {
|
|
25075
25359
|
throw new Error(`Project "${options.project}" not found.`);
|
|
25076
25360
|
}
|
|
@@ -25393,10 +25677,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
25393
25677
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
25394
25678
|
);
|
|
25395
25679
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
25396
|
-
await ensureDir(
|
|
25680
|
+
await ensureDir(resolve56(todosPath, "archive"));
|
|
25397
25681
|
let archContent = "";
|
|
25398
25682
|
if (await fileExists(archFile)) {
|
|
25399
|
-
archContent = await
|
|
25683
|
+
archContent = await readFile36(archFile, "utf-8");
|
|
25400
25684
|
archContent = archContent.trimEnd() + "\n\n";
|
|
25401
25685
|
} else {
|
|
25402
25686
|
archContent = `---
|
|
@@ -25573,12 +25857,12 @@ function describeScope(scope) {
|
|
|
25573
25857
|
}
|
|
25574
25858
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
25575
25859
|
const { resolve: resolvePath2 } = await import("path");
|
|
25576
|
-
const { readFile:
|
|
25860
|
+
const { readFile: readFile64 } = await import("fs/promises");
|
|
25577
25861
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
25578
25862
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
25579
25863
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
25580
25864
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
25581
|
-
let content = await
|
|
25865
|
+
let content = await readFile64(assignmentMdPath2, "utf-8");
|
|
25582
25866
|
content = appendTodosToAssignmentBody2(
|
|
25583
25867
|
content,
|
|
25584
25868
|
todos.map((t) => ({
|
|
@@ -25623,13 +25907,13 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
25623
25907
|
}
|
|
25624
25908
|
const planDir = todoPlanDir(todosPath, workspace, id);
|
|
25625
25909
|
await ensureDir(planDir);
|
|
25626
|
-
const { readdir:
|
|
25627
|
-
const existingFiles = (await
|
|
25910
|
+
const { readdir: readdir33 } = await import("fs/promises");
|
|
25911
|
+
const existingFiles = (await readdir33(planDir).catch(() => [])).filter(
|
|
25628
25912
|
(f) => /^plan(?:-v\d+)?\.md$/.test(f)
|
|
25629
25913
|
);
|
|
25630
25914
|
let target;
|
|
25631
25915
|
if (existingFiles.length === 0) {
|
|
25632
|
-
target =
|
|
25916
|
+
target = resolve56(planDir, "plan.md");
|
|
25633
25917
|
} else {
|
|
25634
25918
|
const versions = /* @__PURE__ */ new Set();
|
|
25635
25919
|
for (const f of existingFiles) {
|
|
@@ -25639,7 +25923,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
25639
25923
|
}
|
|
25640
25924
|
let n = 2;
|
|
25641
25925
|
while (versions.has(n)) n++;
|
|
25642
|
-
target =
|
|
25926
|
+
target = resolve56(planDir, `plan-v${n}.md`);
|
|
25643
25927
|
}
|
|
25644
25928
|
if (!await fileExists(target)) {
|
|
25645
25929
|
const stub = `---
|
|
@@ -25709,9 +25993,9 @@ async function moveTodo(id, options) {
|
|
|
25709
25993
|
if (await fileExists(newPlanDir)) {
|
|
25710
25994
|
throw new Error(`Plan directory already exists at target: ${newPlanDir}; refusing to move.`);
|
|
25711
25995
|
}
|
|
25712
|
-
const { rename: rename11, mkdir:
|
|
25713
|
-
const { dirname:
|
|
25714
|
-
await
|
|
25996
|
+
const { rename: rename11, mkdir: mkdir12 } = await import("fs/promises");
|
|
25997
|
+
const { dirname: dirname24 } = await import("path");
|
|
25998
|
+
await mkdir12(dirname24(newPlanDir), { recursive: true });
|
|
25715
25999
|
await rename11(item.planDir, newPlanDir);
|
|
25716
26000
|
item.planDir = newPlanDir;
|
|
25717
26001
|
}
|
|
@@ -25828,24 +26112,24 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
25828
26112
|
|
|
25829
26113
|
// src/commands/doctor.ts
|
|
25830
26114
|
import { Command as Command4 } from "commander";
|
|
25831
|
-
import { readFile as
|
|
25832
|
-
import { isAbsolute as isAbsolute11, resolve as
|
|
26115
|
+
import { readFile as readFile46 } from "fs/promises";
|
|
26116
|
+
import { isAbsolute as isAbsolute11, resolve as resolve69 } from "path";
|
|
25833
26117
|
|
|
25834
26118
|
// src/utils/doctor/index.ts
|
|
25835
26119
|
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
25836
|
-
import { readFile as
|
|
25837
|
-
import { dirname as
|
|
26120
|
+
import { readFile as readFile45 } from "fs/promises";
|
|
26121
|
+
import { dirname as dirname21, join as join14 } from "path";
|
|
25838
26122
|
|
|
25839
26123
|
// src/utils/doctor/context.ts
|
|
25840
26124
|
init_config2();
|
|
25841
26125
|
init_paths();
|
|
25842
26126
|
init_fs();
|
|
25843
26127
|
import Database4 from "better-sqlite3";
|
|
25844
|
-
import { resolve as
|
|
26128
|
+
import { resolve as resolve57 } from "path";
|
|
25845
26129
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
25846
26130
|
const config = await readConfig();
|
|
25847
26131
|
const root = syntaurRoot();
|
|
25848
|
-
const dbPath =
|
|
26132
|
+
const dbPath = resolve57(root, "syntaur.db");
|
|
25849
26133
|
let db5 = null;
|
|
25850
26134
|
let dbError = null;
|
|
25851
26135
|
if (await fileExists(dbPath)) {
|
|
@@ -25879,10 +26163,10 @@ function closeCheckContext(ctx) {
|
|
|
25879
26163
|
// src/utils/doctor/checks/env.ts
|
|
25880
26164
|
init_fs();
|
|
25881
26165
|
init_paths();
|
|
25882
|
-
import { resolve as
|
|
25883
|
-
import { readFile as
|
|
26166
|
+
import { resolve as resolve58, isAbsolute as isAbsolute9 } from "path";
|
|
26167
|
+
import { readFile as readFile37, stat as stat4 } from "fs/promises";
|
|
25884
26168
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
25885
|
-
import { dirname as
|
|
26169
|
+
import { dirname as dirname19, join as join11 } from "path";
|
|
25886
26170
|
var CATEGORY = "env";
|
|
25887
26171
|
var syntaurRootExists = {
|
|
25888
26172
|
id: "env.syntaur-root-exists",
|
|
@@ -25920,7 +26204,7 @@ var configValid = {
|
|
|
25920
26204
|
category: CATEGORY,
|
|
25921
26205
|
title: "~/.syntaur/config.md is valid",
|
|
25922
26206
|
async run(ctx) {
|
|
25923
|
-
const configPath2 =
|
|
26207
|
+
const configPath2 = resolve58(ctx.syntaurRoot, "config.md");
|
|
25924
26208
|
if (!await fileExists(configPath2)) {
|
|
25925
26209
|
return {
|
|
25926
26210
|
id: this.id,
|
|
@@ -25937,7 +26221,7 @@ var configValid = {
|
|
|
25937
26221
|
autoFixable: false
|
|
25938
26222
|
};
|
|
25939
26223
|
}
|
|
25940
|
-
const content = await
|
|
26224
|
+
const content = await readFile37(configPath2, "utf-8");
|
|
25941
26225
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
25942
26226
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
25943
26227
|
return {
|
|
@@ -26213,14 +26497,14 @@ async function readLocalVersion() {
|
|
|
26213
26497
|
async function readLocalPkg() {
|
|
26214
26498
|
try {
|
|
26215
26499
|
const here = fileURLToPath10(import.meta.url);
|
|
26216
|
-
let dir =
|
|
26500
|
+
let dir = dirname19(here);
|
|
26217
26501
|
for (let i = 0; i < 6; i++) {
|
|
26218
|
-
const candidate =
|
|
26502
|
+
const candidate = join11(dir, "package.json");
|
|
26219
26503
|
try {
|
|
26220
|
-
const text = await
|
|
26504
|
+
const text = await readFile37(candidate, "utf-8");
|
|
26221
26505
|
return JSON.parse(text);
|
|
26222
26506
|
} catch {
|
|
26223
|
-
dir =
|
|
26507
|
+
dir = dirname19(dir);
|
|
26224
26508
|
}
|
|
26225
26509
|
}
|
|
26226
26510
|
return null;
|
|
@@ -26269,8 +26553,8 @@ function versionGte(a, b) {
|
|
|
26269
26553
|
|
|
26270
26554
|
// src/utils/doctor/checks/structure.ts
|
|
26271
26555
|
init_fs();
|
|
26272
|
-
import { resolve as
|
|
26273
|
-
import { readdir as
|
|
26556
|
+
import { resolve as resolve59 } from "path";
|
|
26557
|
+
import { readdir as readdir20, stat as stat5 } from "fs/promises";
|
|
26274
26558
|
var CATEGORY2 = "structure";
|
|
26275
26559
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
26276
26560
|
"projects",
|
|
@@ -26289,7 +26573,7 @@ var projectsDir = {
|
|
|
26289
26573
|
category: CATEGORY2,
|
|
26290
26574
|
title: "projects/ directory exists",
|
|
26291
26575
|
async run(ctx) {
|
|
26292
|
-
const p =
|
|
26576
|
+
const p = resolve59(ctx.syntaurRoot, "projects");
|
|
26293
26577
|
if (!await fileExists(p)) {
|
|
26294
26578
|
return {
|
|
26295
26579
|
id: this.id,
|
|
@@ -26314,7 +26598,7 @@ var playbooksDir2 = {
|
|
|
26314
26598
|
category: CATEGORY2,
|
|
26315
26599
|
title: "playbooks/ directory exists",
|
|
26316
26600
|
async run(ctx) {
|
|
26317
|
-
const p =
|
|
26601
|
+
const p = resolve59(ctx.syntaurRoot, "playbooks");
|
|
26318
26602
|
if (!await fileExists(p)) {
|
|
26319
26603
|
return {
|
|
26320
26604
|
id: this.id,
|
|
@@ -26339,7 +26623,7 @@ var todosDirValid = {
|
|
|
26339
26623
|
category: CATEGORY2,
|
|
26340
26624
|
title: "todos/ directory is readable (if present)",
|
|
26341
26625
|
async run(ctx) {
|
|
26342
|
-
const p =
|
|
26626
|
+
const p = resolve59(ctx.syntaurRoot, "todos");
|
|
26343
26627
|
if (!await fileExists(p)) {
|
|
26344
26628
|
return {
|
|
26345
26629
|
id: this.id,
|
|
@@ -26370,7 +26654,7 @@ var serversDirValid = {
|
|
|
26370
26654
|
category: CATEGORY2,
|
|
26371
26655
|
title: "servers/ directory is readable (if present)",
|
|
26372
26656
|
async run(ctx) {
|
|
26373
|
-
const p =
|
|
26657
|
+
const p = resolve59(ctx.syntaurRoot, "servers");
|
|
26374
26658
|
if (!await fileExists(p)) {
|
|
26375
26659
|
return {
|
|
26376
26660
|
id: this.id,
|
|
@@ -26401,7 +26685,7 @@ var knownFilesRecognized = {
|
|
|
26401
26685
|
category: CATEGORY2,
|
|
26402
26686
|
title: "No unexpected top-level entries under ~/.syntaur/",
|
|
26403
26687
|
async run(ctx) {
|
|
26404
|
-
const entries = await
|
|
26688
|
+
const entries = await readdir20(ctx.syntaurRoot, { withFileTypes: true });
|
|
26405
26689
|
const unexpected = [];
|
|
26406
26690
|
for (const e of entries) {
|
|
26407
26691
|
if (e.name.startsWith(".")) continue;
|
|
@@ -26415,7 +26699,7 @@ var knownFilesRecognized = {
|
|
|
26415
26699
|
title: this.title,
|
|
26416
26700
|
status: "warn",
|
|
26417
26701
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
26418
|
-
affected: unexpected.map((n) =>
|
|
26702
|
+
affected: unexpected.map((n) => resolve59(ctx.syntaurRoot, n)),
|
|
26419
26703
|
remediation: {
|
|
26420
26704
|
kind: "manual",
|
|
26421
26705
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -26444,8 +26728,8 @@ function pass2(check) {
|
|
|
26444
26728
|
|
|
26445
26729
|
// src/utils/doctor/checks/project.ts
|
|
26446
26730
|
init_fs();
|
|
26447
|
-
import { resolve as
|
|
26448
|
-
import { readdir as
|
|
26731
|
+
import { resolve as resolve60 } from "path";
|
|
26732
|
+
import { readdir as readdir21, stat as stat6 } from "fs/promises";
|
|
26449
26733
|
var CATEGORY3 = "project";
|
|
26450
26734
|
var REQUIRED_PROJECT_FILES = [
|
|
26451
26735
|
"project.md",
|
|
@@ -26469,15 +26753,15 @@ var PROJECT_MARKERS = ["project.md", "manifest.md", "assignments"];
|
|
|
26469
26753
|
async function listProjects2(ctx) {
|
|
26470
26754
|
const dir = ctx.config.defaultProjectDir;
|
|
26471
26755
|
if (!await fileExists(dir)) return [];
|
|
26472
|
-
const entries = await
|
|
26756
|
+
const entries = await readdir21(dir, { withFileTypes: true });
|
|
26473
26757
|
const result = [];
|
|
26474
26758
|
for (const e of entries) {
|
|
26475
26759
|
if (!e.isDirectory()) continue;
|
|
26476
26760
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
26477
|
-
const projectDir =
|
|
26761
|
+
const projectDir = resolve60(dir, e.name);
|
|
26478
26762
|
let looksLikeProject = false;
|
|
26479
26763
|
for (const marker of PROJECT_MARKERS) {
|
|
26480
|
-
if (await fileExists(
|
|
26764
|
+
if (await fileExists(resolve60(projectDir, marker))) {
|
|
26481
26765
|
looksLikeProject = true;
|
|
26482
26766
|
break;
|
|
26483
26767
|
}
|
|
@@ -26496,7 +26780,7 @@ var requiredFiles = {
|
|
|
26496
26780
|
for (const projectDir of projects) {
|
|
26497
26781
|
const missing = [];
|
|
26498
26782
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
26499
|
-
const p =
|
|
26783
|
+
const p = resolve60(projectDir, rel);
|
|
26500
26784
|
if (!await fileExists(p)) missing.push(rel);
|
|
26501
26785
|
}
|
|
26502
26786
|
if (missing.length === 0) continue;
|
|
@@ -26506,7 +26790,7 @@ var requiredFiles = {
|
|
|
26506
26790
|
title: this.title,
|
|
26507
26791
|
status: "error",
|
|
26508
26792
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
26509
|
-
affected: missing.map((m) =>
|
|
26793
|
+
affected: missing.map((m) => resolve60(projectDir, m)),
|
|
26510
26794
|
remediation: {
|
|
26511
26795
|
kind: "manual",
|
|
26512
26796
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -26529,7 +26813,7 @@ var manifestStale = {
|
|
|
26529
26813
|
const projects = await listProjects2(ctx);
|
|
26530
26814
|
const results = [];
|
|
26531
26815
|
for (const projectDir of projects) {
|
|
26532
|
-
const manifestPath =
|
|
26816
|
+
const manifestPath = resolve60(projectDir, "manifest.md");
|
|
26533
26817
|
if (!await fileExists(manifestPath)) continue;
|
|
26534
26818
|
const manifestMtime = (await stat6(manifestPath)).mtimeMs;
|
|
26535
26819
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
@@ -26563,7 +26847,7 @@ var orphanFiles = {
|
|
|
26563
26847
|
const projects = await listProjects2(ctx);
|
|
26564
26848
|
const results = [];
|
|
26565
26849
|
for (const projectDir of projects) {
|
|
26566
|
-
const entries = await
|
|
26850
|
+
const entries = await readdir21(projectDir, { withFileTypes: true });
|
|
26567
26851
|
const orphans = [];
|
|
26568
26852
|
for (const e of entries) {
|
|
26569
26853
|
if (e.name.startsWith(".")) continue;
|
|
@@ -26578,7 +26862,7 @@ var orphanFiles = {
|
|
|
26578
26862
|
title: this.title,
|
|
26579
26863
|
status: "warn",
|
|
26580
26864
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
26581
|
-
affected: orphans.map((o) =>
|
|
26865
|
+
affected: orphans.map((o) => resolve60(projectDir, o)),
|
|
26582
26866
|
autoFixable: false
|
|
26583
26867
|
});
|
|
26584
26868
|
}
|
|
@@ -26588,18 +26872,18 @@ var orphanFiles = {
|
|
|
26588
26872
|
};
|
|
26589
26873
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
26590
26874
|
async function newestAssignmentMtime(projectDir) {
|
|
26591
|
-
const assignmentsRoot =
|
|
26875
|
+
const assignmentsRoot = resolve60(projectDir, "assignments");
|
|
26592
26876
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
26593
26877
|
let newest = 0;
|
|
26594
26878
|
let entries;
|
|
26595
26879
|
try {
|
|
26596
|
-
entries = await
|
|
26880
|
+
entries = await readdir21(assignmentsRoot, { withFileTypes: true });
|
|
26597
26881
|
} catch {
|
|
26598
26882
|
return 0;
|
|
26599
26883
|
}
|
|
26600
26884
|
for (const e of entries) {
|
|
26601
26885
|
if (!e.isDirectory()) continue;
|
|
26602
|
-
const assignmentMd =
|
|
26886
|
+
const assignmentMd = resolve60(assignmentsRoot, e.name, "assignment.md");
|
|
26603
26887
|
try {
|
|
26604
26888
|
const s = await stat6(assignmentMd);
|
|
26605
26889
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
@@ -26623,8 +26907,8 @@ init_fs();
|
|
|
26623
26907
|
init_parser();
|
|
26624
26908
|
init_types();
|
|
26625
26909
|
init_paths();
|
|
26626
|
-
import { resolve as
|
|
26627
|
-
import { readFile as
|
|
26910
|
+
import { resolve as resolve61 } from "path";
|
|
26911
|
+
import { readFile as readFile38, readdir as readdir22 } from "fs/promises";
|
|
26628
26912
|
var CATEGORY4 = "assignment";
|
|
26629
26913
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
26630
26914
|
var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -26718,7 +27002,7 @@ var invalidStatus = {
|
|
|
26718
27002
|
const allowed = configuredStatuses(ctx);
|
|
26719
27003
|
const results = [];
|
|
26720
27004
|
for (const a of withAssignmentMd) {
|
|
26721
|
-
const path =
|
|
27005
|
+
const path = resolve61(a.assignmentDir, "assignment.md");
|
|
26722
27006
|
const parsed = await parseSafe2(path);
|
|
26723
27007
|
if (!parsed) continue;
|
|
26724
27008
|
if (!allowed.has(parsed.status)) {
|
|
@@ -26751,7 +27035,7 @@ var workspaceMissing = {
|
|
|
26751
27035
|
const terminal = terminalStatuses(ctx);
|
|
26752
27036
|
const results = [];
|
|
26753
27037
|
for (const a of withAssignmentMd) {
|
|
26754
|
-
const path =
|
|
27038
|
+
const path = resolve61(a.assignmentDir, "assignment.md");
|
|
26755
27039
|
const parsed = await parseSafe2(path);
|
|
26756
27040
|
if (!parsed) continue;
|
|
26757
27041
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -26798,12 +27082,12 @@ var requiredFilesByStatus = {
|
|
|
26798
27082
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
26799
27083
|
const results = [];
|
|
26800
27084
|
for (const a of withAssignmentMd) {
|
|
26801
|
-
const assignmentPath =
|
|
27085
|
+
const assignmentPath = resolve61(a.assignmentDir, "assignment.md");
|
|
26802
27086
|
const parsed = await parseSafe2(assignmentPath);
|
|
26803
27087
|
if (!parsed) continue;
|
|
26804
27088
|
const missing = [];
|
|
26805
27089
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
26806
|
-
const handoffPath =
|
|
27090
|
+
const handoffPath = resolve61(a.assignmentDir, "handoff.md");
|
|
26807
27091
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
26808
27092
|
}
|
|
26809
27093
|
if (missing.length === 0) continue;
|
|
@@ -26813,7 +27097,7 @@ var requiredFilesByStatus = {
|
|
|
26813
27097
|
title: this.title,
|
|
26814
27098
|
status: "warn",
|
|
26815
27099
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
26816
|
-
affected: missing.map((m) =>
|
|
27100
|
+
affected: missing.map((m) => resolve61(a.assignmentDir, m)),
|
|
26817
27101
|
remediation: {
|
|
26818
27102
|
kind: "manual",
|
|
26819
27103
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -26836,7 +27120,7 @@ var companionFilesScaffolded = {
|
|
|
26836
27120
|
for (const a of withAssignmentMd) {
|
|
26837
27121
|
const missing = [];
|
|
26838
27122
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
26839
|
-
if (!await fileExists(
|
|
27123
|
+
if (!await fileExists(resolve61(a.assignmentDir, filename))) {
|
|
26840
27124
|
missing.push(filename);
|
|
26841
27125
|
}
|
|
26842
27126
|
}
|
|
@@ -26848,7 +27132,7 @@ var companionFilesScaffolded = {
|
|
|
26848
27132
|
title: this.title,
|
|
26849
27133
|
status: "warn",
|
|
26850
27134
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
26851
|
-
affected: missing.map((m) =>
|
|
27135
|
+
affected: missing.map((m) => resolve61(a.assignmentDir, m)),
|
|
26852
27136
|
remediation: {
|
|
26853
27137
|
kind: "manual",
|
|
26854
27138
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -26881,7 +27165,7 @@ var typeDefinition = {
|
|
|
26881
27165
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
26882
27166
|
const results = [];
|
|
26883
27167
|
for (const a of withAssignmentMd) {
|
|
26884
|
-
const path =
|
|
27168
|
+
const path = resolve61(a.assignmentDir, "assignment.md");
|
|
26885
27169
|
const parsed = await parseSafe2(path);
|
|
26886
27170
|
if (!parsed) continue;
|
|
26887
27171
|
if (!parsed.type) continue;
|
|
@@ -26915,7 +27199,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
26915
27199
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
26916
27200
|
const results = [];
|
|
26917
27201
|
for (const a of withAssignmentMd) {
|
|
26918
|
-
const path =
|
|
27202
|
+
const path = resolve61(a.assignmentDir, "assignment.md");
|
|
26919
27203
|
const parsed = await parseSafe2(path);
|
|
26920
27204
|
if (!parsed) continue;
|
|
26921
27205
|
if (a.standalone) {
|
|
@@ -26966,13 +27250,13 @@ var draftMissingObjective = {
|
|
|
26966
27250
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
26967
27251
|
const results = [];
|
|
26968
27252
|
for (const a of withAssignmentMd) {
|
|
26969
|
-
const path =
|
|
27253
|
+
const path = resolve61(a.assignmentDir, "assignment.md");
|
|
26970
27254
|
const parsed = await parseSafe2(path);
|
|
26971
27255
|
if (!parsed) continue;
|
|
26972
27256
|
if (parsed.status !== "draft") continue;
|
|
26973
27257
|
let raw2;
|
|
26974
27258
|
try {
|
|
26975
|
-
raw2 = await
|
|
27259
|
+
raw2 = await readFile38(path, "utf-8");
|
|
26976
27260
|
} catch {
|
|
26977
27261
|
continue;
|
|
26978
27262
|
}
|
|
@@ -27005,16 +27289,16 @@ var readyToImplementMissingPlan = {
|
|
|
27005
27289
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
27006
27290
|
const results = [];
|
|
27007
27291
|
for (const a of withAssignmentMd) {
|
|
27008
|
-
const path =
|
|
27292
|
+
const path = resolve61(a.assignmentDir, "assignment.md");
|
|
27009
27293
|
const parsed = await parseSafe2(path);
|
|
27010
27294
|
if (!parsed) continue;
|
|
27011
27295
|
if (parsed.status !== "ready_to_implement") continue;
|
|
27012
|
-
const entries = await
|
|
27296
|
+
const entries = await readdir22(a.assignmentDir).catch(() => []);
|
|
27013
27297
|
const planFiles = entries.filter((f) => /^plan(?:-v\d+)?\.md$/i.test(f));
|
|
27014
27298
|
let hasPlanContent = false;
|
|
27015
27299
|
for (const f of planFiles) {
|
|
27016
27300
|
try {
|
|
27017
|
-
const c2 = await
|
|
27301
|
+
const c2 = await readFile38(resolve61(a.assignmentDir, f), "utf-8");
|
|
27018
27302
|
if (c2.trim().length > 0) {
|
|
27019
27303
|
hasPlanContent = true;
|
|
27020
27304
|
break;
|
|
@@ -27030,7 +27314,7 @@ var readyToImplementMissingPlan = {
|
|
|
27030
27314
|
title: this.title,
|
|
27031
27315
|
status: "warn",
|
|
27032
27316
|
detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
|
|
27033
|
-
affected: [
|
|
27317
|
+
affected: [resolve61(a.assignmentDir, "plan.md")],
|
|
27034
27318
|
remediation: {
|
|
27035
27319
|
kind: "manual",
|
|
27036
27320
|
suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
|
|
@@ -27057,7 +27341,7 @@ var assignmentChecks = [
|
|
|
27057
27341
|
];
|
|
27058
27342
|
async function parseSafe2(path) {
|
|
27059
27343
|
try {
|
|
27060
|
-
const content = await
|
|
27344
|
+
const content = await readFile38(path, "utf-8");
|
|
27061
27345
|
return parseAssignmentFull(content);
|
|
27062
27346
|
} catch {
|
|
27063
27347
|
return null;
|
|
@@ -27076,7 +27360,7 @@ function pass4(check, detail) {
|
|
|
27076
27360
|
|
|
27077
27361
|
// src/utils/doctor/checks/dashboard.ts
|
|
27078
27362
|
init_fs();
|
|
27079
|
-
import { resolve as
|
|
27363
|
+
import { resolve as resolve62 } from "path";
|
|
27080
27364
|
var CATEGORY5 = "dashboard";
|
|
27081
27365
|
var dbReachable = {
|
|
27082
27366
|
id: "dashboard.db-reachable",
|
|
@@ -27090,7 +27374,7 @@ var dbReachable = {
|
|
|
27090
27374
|
title: this.title,
|
|
27091
27375
|
status: "error",
|
|
27092
27376
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
27093
|
-
affected: [
|
|
27377
|
+
affected: [resolve62(ctx.syntaurRoot, "syntaur.db")],
|
|
27094
27378
|
remediation: {
|
|
27095
27379
|
kind: "manual",
|
|
27096
27380
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -27108,7 +27392,7 @@ var dbReachable = {
|
|
|
27108
27392
|
title: this.title,
|
|
27109
27393
|
status: "error",
|
|
27110
27394
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
27111
|
-
affected: [
|
|
27395
|
+
affected: [resolve62(ctx.syntaurRoot, "syntaur.db")],
|
|
27112
27396
|
autoFixable: false
|
|
27113
27397
|
};
|
|
27114
27398
|
}
|
|
@@ -27120,7 +27404,7 @@ var dbReachable = {
|
|
|
27120
27404
|
title: this.title,
|
|
27121
27405
|
status: "error",
|
|
27122
27406
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
27123
|
-
affected: [
|
|
27407
|
+
affected: [resolve62(ctx.syntaurRoot, "syntaur.db")],
|
|
27124
27408
|
autoFixable: false
|
|
27125
27409
|
};
|
|
27126
27410
|
}
|
|
@@ -27146,7 +27430,7 @@ var ghostSessions = {
|
|
|
27146
27430
|
const results = [];
|
|
27147
27431
|
for (const row of rows) {
|
|
27148
27432
|
if (!row.project_slug) continue;
|
|
27149
|
-
const projectPath =
|
|
27433
|
+
const projectPath = resolve62(projectsDir2, row.project_slug, "project.md");
|
|
27150
27434
|
if (!await fileExists(projectPath)) {
|
|
27151
27435
|
results.push({
|
|
27152
27436
|
id: this.id,
|
|
@@ -27165,7 +27449,7 @@ var ghostSessions = {
|
|
|
27165
27449
|
continue;
|
|
27166
27450
|
}
|
|
27167
27451
|
if (row.assignment_slug) {
|
|
27168
|
-
const assignmentPath =
|
|
27452
|
+
const assignmentPath = resolve62(
|
|
27169
27453
|
projectsDir2,
|
|
27170
27454
|
row.project_slug,
|
|
27171
27455
|
"assignments",
|
|
@@ -27217,8 +27501,8 @@ function skipped(check, reason) {
|
|
|
27217
27501
|
|
|
27218
27502
|
// src/utils/doctor/checks/integrations.ts
|
|
27219
27503
|
init_fs();
|
|
27220
|
-
import { resolve as
|
|
27221
|
-
import { readdir as
|
|
27504
|
+
import { resolve as resolve63, dirname as dirname20, basename as basename6 } from "path";
|
|
27505
|
+
import { readdir as readdir23, readFile as readFile39 } from "fs/promises";
|
|
27222
27506
|
import { homedir as homedir10 } from "os";
|
|
27223
27507
|
var CATEGORY6 = "integrations";
|
|
27224
27508
|
var claudePluginLinked = {
|
|
@@ -27281,7 +27565,7 @@ var backupConfigured = {
|
|
|
27281
27565
|
if (ctx.config.backup?.repo) return pass6(this);
|
|
27282
27566
|
const projectsDir2 = ctx.config.defaultProjectDir;
|
|
27283
27567
|
if (!await fileExists(projectsDir2)) return skipped2(this, "no projects dir");
|
|
27284
|
-
const entries = await
|
|
27568
|
+
const entries = await readdir23(projectsDir2, { withFileTypes: true });
|
|
27285
27569
|
const hasProjects = entries.some((e) => e.isDirectory() && !e.name.startsWith(".") && !e.name.startsWith("_"));
|
|
27286
27570
|
if (!hasProjects) return skipped2(this, "no projects yet");
|
|
27287
27571
|
return {
|
|
@@ -27300,10 +27584,10 @@ var backupConfigured = {
|
|
|
27300
27584
|
}
|
|
27301
27585
|
};
|
|
27302
27586
|
async function readKnownMarketplaces() {
|
|
27303
|
-
const path =
|
|
27587
|
+
const path = resolve63(homedir10(), ".claude", "plugins", "known_marketplaces.json");
|
|
27304
27588
|
if (!await fileExists(path)) return {};
|
|
27305
27589
|
try {
|
|
27306
|
-
const raw2 = await
|
|
27590
|
+
const raw2 = await readFile39(path, "utf-8");
|
|
27307
27591
|
return JSON.parse(raw2);
|
|
27308
27592
|
} catch {
|
|
27309
27593
|
return {};
|
|
@@ -27319,7 +27603,7 @@ var claudeMarketplaceRegistered = {
|
|
|
27319
27603
|
if (!await fileExists(dir)) {
|
|
27320
27604
|
return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
|
|
27321
27605
|
}
|
|
27322
|
-
const pluginsParent =
|
|
27606
|
+
const pluginsParent = dirname20(dir);
|
|
27323
27607
|
if (basename6(pluginsParent) !== "plugins") {
|
|
27324
27608
|
return {
|
|
27325
27609
|
id: this.id,
|
|
@@ -27336,8 +27620,8 @@ var claudeMarketplaceRegistered = {
|
|
|
27336
27620
|
autoFixable: false
|
|
27337
27621
|
};
|
|
27338
27622
|
}
|
|
27339
|
-
const marketplaceRoot =
|
|
27340
|
-
const marketplaceManifest =
|
|
27623
|
+
const marketplaceRoot = dirname20(pluginsParent);
|
|
27624
|
+
const marketplaceManifest = resolve63(marketplaceRoot, ".claude-plugin", "marketplace.json");
|
|
27341
27625
|
if (!await fileExists(marketplaceManifest)) {
|
|
27342
27626
|
return {
|
|
27343
27627
|
id: this.id,
|
|
@@ -27356,7 +27640,7 @@ var claudeMarketplaceRegistered = {
|
|
|
27356
27640
|
}
|
|
27357
27641
|
let parsed = {};
|
|
27358
27642
|
try {
|
|
27359
|
-
parsed = JSON.parse(await
|
|
27643
|
+
parsed = JSON.parse(await readFile39(marketplaceManifest, "utf-8"));
|
|
27360
27644
|
} catch {
|
|
27361
27645
|
return {
|
|
27362
27646
|
id: this.id,
|
|
@@ -27388,7 +27672,7 @@ var claudeMarketplaceRegistered = {
|
|
|
27388
27672
|
title: this.title,
|
|
27389
27673
|
status: "error",
|
|
27390
27674
|
detail: issues.join("; "),
|
|
27391
|
-
affected: [marketplaceManifest,
|
|
27675
|
+
affected: [marketplaceManifest, resolve63(homedir10(), ".claude", "plugins", "known_marketplaces.json")],
|
|
27392
27676
|
remediation: {
|
|
27393
27677
|
kind: "manual",
|
|
27394
27678
|
suggestion: "Re-run install-plugin to ensure both files are in sync.",
|
|
@@ -27428,8 +27712,8 @@ function skipped2(check, reason) {
|
|
|
27428
27712
|
init_fs();
|
|
27429
27713
|
init_parser();
|
|
27430
27714
|
init_types();
|
|
27431
|
-
import { resolve as
|
|
27432
|
-
import { readFile as
|
|
27715
|
+
import { resolve as resolve64 } from "path";
|
|
27716
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
27433
27717
|
var CATEGORY7 = "workspace";
|
|
27434
27718
|
var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
27435
27719
|
var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
|
|
@@ -27450,12 +27734,12 @@ function isStandaloneSession(ctx) {
|
|
|
27450
27734
|
return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
|
|
27451
27735
|
}
|
|
27452
27736
|
async function loadContext(ctx) {
|
|
27453
|
-
const path =
|
|
27737
|
+
const path = resolve64(ctx.cwd, ".syntaur", "context.json");
|
|
27454
27738
|
if (!await fileExists(path)) {
|
|
27455
27739
|
return { data: null, path, exists: false, parseError: null };
|
|
27456
27740
|
}
|
|
27457
27741
|
try {
|
|
27458
|
-
const raw2 = await
|
|
27742
|
+
const raw2 = await readFile40(path, "utf-8");
|
|
27459
27743
|
return { data: JSON.parse(raw2), path, exists: true, parseError: null };
|
|
27460
27744
|
} catch (err2) {
|
|
27461
27745
|
return {
|
|
@@ -27549,7 +27833,7 @@ var contextAssignmentResolves = {
|
|
|
27549
27833
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
27550
27834
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
|
|
27551
27835
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
27552
|
-
const assignmentMd =
|
|
27836
|
+
const assignmentMd = resolve64(data.assignmentDir, "assignment.md");
|
|
27553
27837
|
if (!await fileExists(assignmentMd)) {
|
|
27554
27838
|
return {
|
|
27555
27839
|
id: this.id,
|
|
@@ -27579,10 +27863,10 @@ var contextTerminal = {
|
|
|
27579
27863
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
27580
27864
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
|
|
27581
27865
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
27582
|
-
const assignmentMd =
|
|
27866
|
+
const assignmentMd = resolve64(data.assignmentDir, "assignment.md");
|
|
27583
27867
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
27584
27868
|
try {
|
|
27585
|
-
const content = await
|
|
27869
|
+
const content = await readFile40(assignmentMd, "utf-8");
|
|
27586
27870
|
const parsed = parseAssignmentFull(content);
|
|
27587
27871
|
const terminal = terminalStatuses2(ctx);
|
|
27588
27872
|
if (terminal.has(parsed.status)) {
|
|
@@ -27728,15 +28012,15 @@ var agentChecks = [agentsResolvable];
|
|
|
27728
28012
|
// src/utils/doctor/checks/terminal.ts
|
|
27729
28013
|
init_config2();
|
|
27730
28014
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
27731
|
-
import { readFile as
|
|
27732
|
-
import { resolve as
|
|
28015
|
+
import { readFile as readFile41 } from "fs/promises";
|
|
28016
|
+
import { resolve as resolve65 } from "path";
|
|
27733
28017
|
init_paths();
|
|
27734
28018
|
init_fs();
|
|
27735
28019
|
var CATEGORY9 = "terminal";
|
|
27736
28020
|
async function readRawTerminalKey() {
|
|
27737
|
-
const configPath2 =
|
|
28021
|
+
const configPath2 = resolve65(syntaurRoot(), "config.md");
|
|
27738
28022
|
if (!await fileExists(configPath2)) return null;
|
|
27739
|
-
const content = await
|
|
28023
|
+
const content = await readFile41(configPath2, "utf-8");
|
|
27740
28024
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
27741
28025
|
if (!fmMatch) return null;
|
|
27742
28026
|
const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
|
|
@@ -27886,13 +28170,13 @@ var terminalChecks = [
|
|
|
27886
28170
|
|
|
27887
28171
|
// src/utils/doctor/checks/skills.ts
|
|
27888
28172
|
init_fs();
|
|
27889
|
-
import { resolve as
|
|
27890
|
-
import { readdir as
|
|
28173
|
+
import { resolve as resolve66, join as join12 } from "path";
|
|
28174
|
+
import { readdir as readdir24, readFile as readFile42, lstat as lstat4 } from "fs/promises";
|
|
27891
28175
|
import { homedir as homedir11 } from "os";
|
|
27892
28176
|
var CATEGORY10 = "skills";
|
|
27893
28177
|
var skillTargets = [
|
|
27894
|
-
{ agent: "claude", dir:
|
|
27895
|
-
{ agent: "codex", dir:
|
|
28178
|
+
{ agent: "claude", dir: resolve66(homedir11(), ".claude", "skills"), label: "~/.claude/skills" },
|
|
28179
|
+
{ agent: "codex", dir: resolve66(homedir11(), ".codex", "skills"), label: "~/.codex/skills" }
|
|
27896
28180
|
];
|
|
27897
28181
|
var skillsDedupCheck = {
|
|
27898
28182
|
id: "skills.dedup",
|
|
@@ -27907,21 +28191,21 @@ var skillsDedupCheck = {
|
|
|
27907
28191
|
const present = [];
|
|
27908
28192
|
let entries;
|
|
27909
28193
|
try {
|
|
27910
|
-
entries = await
|
|
28194
|
+
entries = await readdir24(dir, { withFileTypes: true });
|
|
27911
28195
|
} catch {
|
|
27912
28196
|
continue;
|
|
27913
28197
|
}
|
|
27914
28198
|
for (const entry of entries) {
|
|
27915
28199
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
27916
28200
|
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
27917
|
-
const skillMd =
|
|
28201
|
+
const skillMd = join12(dir, entry.name, "SKILL.md");
|
|
27918
28202
|
if (!await fileExists(skillMd)) continue;
|
|
27919
|
-
const content = await
|
|
28203
|
+
const content = await readFile42(skillMd, "utf-8").catch(() => "");
|
|
27920
28204
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
27921
28205
|
if (!match || match[1] !== entry.name) continue;
|
|
27922
28206
|
let isSymlink2 = false;
|
|
27923
28207
|
try {
|
|
27924
|
-
isSymlink2 = (await lstat4(
|
|
28208
|
+
isSymlink2 = (await lstat4(join12(dir, entry.name))).isSymbolicLink();
|
|
27925
28209
|
} catch {
|
|
27926
28210
|
}
|
|
27927
28211
|
present.push({ name: entry.name, isSymlink: isSymlink2 });
|
|
@@ -27933,7 +28217,7 @@ var skillsDedupCheck = {
|
|
|
27933
28217
|
findings.push(
|
|
27934
28218
|
`${label}: ${nonSymlink.length} syntaur skill(s) installed globally while the syntaur plugin is enabled (${agent}) \u2014 duplicate registrations`
|
|
27935
28219
|
);
|
|
27936
|
-
for (const p of nonSymlink) affected.push(
|
|
28220
|
+
for (const p of nonSymlink) affected.push(join12(dir, p.name));
|
|
27937
28221
|
}
|
|
27938
28222
|
}
|
|
27939
28223
|
}
|
|
@@ -27966,12 +28250,12 @@ var skillsChecks = [skillsDedupCheck];
|
|
|
27966
28250
|
|
|
27967
28251
|
// src/utils/doctor/checks/cross-agent.ts
|
|
27968
28252
|
init_fs();
|
|
27969
|
-
import { join as
|
|
27970
|
-
import { readFile as
|
|
28253
|
+
import { join as join13, resolve as resolve67 } from "path";
|
|
28254
|
+
import { readFile as readFile44 } from "fs/promises";
|
|
27971
28255
|
|
|
27972
28256
|
// src/utils/skill-frontmatter.ts
|
|
27973
28257
|
import { createHash } from "crypto";
|
|
27974
|
-
import { readFile as
|
|
28258
|
+
import { readFile as readFile43 } from "fs/promises";
|
|
27975
28259
|
function stripQuotes(raw2) {
|
|
27976
28260
|
const t = raw2.trim();
|
|
27977
28261
|
if (t.length >= 2 && (t.startsWith('"') && t.endsWith('"') || t.startsWith("'") && t.endsWith("'"))) {
|
|
@@ -28015,7 +28299,7 @@ function readSkillIdentity(skillMdText) {
|
|
|
28015
28299
|
return { name, hasDescription };
|
|
28016
28300
|
}
|
|
28017
28301
|
async function sha256File(path) {
|
|
28018
|
-
return createHash("sha256").update(await
|
|
28302
|
+
return createHash("sha256").update(await readFile43(path)).digest("hex");
|
|
28019
28303
|
}
|
|
28020
28304
|
|
|
28021
28305
|
// src/utils/doctor/checks/cross-agent.ts
|
|
@@ -28024,14 +28308,14 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
|
|
|
28024
28308
|
const problems = [];
|
|
28025
28309
|
let valid = 0;
|
|
28026
28310
|
for (const skill of knownSkills) {
|
|
28027
|
-
const installedPath =
|
|
28311
|
+
const installedPath = join13(installedDir, skill, "SKILL.md");
|
|
28028
28312
|
if (!await fileExists(installedPath)) {
|
|
28029
28313
|
problems.push({ skill, kind: "missing" });
|
|
28030
28314
|
continue;
|
|
28031
28315
|
}
|
|
28032
28316
|
let text;
|
|
28033
28317
|
try {
|
|
28034
|
-
text = await
|
|
28318
|
+
text = await readFile44(installedPath, "utf-8");
|
|
28035
28319
|
} catch {
|
|
28036
28320
|
problems.push({ skill, kind: "invalid-frontmatter" });
|
|
28037
28321
|
continue;
|
|
@@ -28042,7 +28326,7 @@ async function checkTargetSkillsIntegrity(installedDir, canonicalSkillsDir, know
|
|
|
28042
28326
|
continue;
|
|
28043
28327
|
}
|
|
28044
28328
|
valid++;
|
|
28045
|
-
const canonicalPath =
|
|
28329
|
+
const canonicalPath = join13(canonicalSkillsDir, skill, "SKILL.md");
|
|
28046
28330
|
if (await fileExists(canonicalPath)) {
|
|
28047
28331
|
const [installedHash, canonicalHash] = await Promise.all([
|
|
28048
28332
|
sha256File(installedPath),
|
|
@@ -28075,6 +28359,7 @@ var crossAgentSkillsCheck = {
|
|
|
28075
28359
|
title: "Cross-agent targets have Syntaur skills + protocol files",
|
|
28076
28360
|
async run(ctx) {
|
|
28077
28361
|
const installed = ctx.config.integrations.installedAgents ?? {};
|
|
28362
|
+
const { targets: resolvedTargets, warnings: descriptorWarnings } = await resolveAgentTargets();
|
|
28078
28363
|
const canonicalSkillsDir = await getSkillsDir();
|
|
28079
28364
|
let knownSkills;
|
|
28080
28365
|
try {
|
|
@@ -28088,7 +28373,7 @@ var crossAgentSkillsCheck = {
|
|
|
28088
28373
|
const problems = [];
|
|
28089
28374
|
const affected = [];
|
|
28090
28375
|
let considered = 0;
|
|
28091
|
-
for (const t of
|
|
28376
|
+
for (const t of resolvedTargets) {
|
|
28092
28377
|
if (t.nativePlugin) continue;
|
|
28093
28378
|
const dir = t.skillsDir?.global;
|
|
28094
28379
|
if (!dir) continue;
|
|
@@ -28107,15 +28392,41 @@ var crossAgentSkillsCheck = {
|
|
|
28107
28392
|
}
|
|
28108
28393
|
if (recorded && t.instructions) {
|
|
28109
28394
|
for (const f of t.instructions.files) {
|
|
28110
|
-
const p =
|
|
28395
|
+
const p = resolve67(ctx.cwd, f.path);
|
|
28111
28396
|
if (!await fileExists(p)) {
|
|
28112
28397
|
problems.push(`${t.displayName}: missing protocol file ${f.path} in cwd`);
|
|
28113
28398
|
affected.push(p);
|
|
28114
28399
|
}
|
|
28115
28400
|
}
|
|
28116
28401
|
}
|
|
28402
|
+
if (t.tier3) {
|
|
28403
|
+
const installDir = t.tier3.installDir();
|
|
28404
|
+
const installed2 = await fileExists(join13(installDir, t.tier3.entry));
|
|
28405
|
+
lines.push(
|
|
28406
|
+
`${t.displayName}: Tier-3 ${t.tier3.kind} ${installed2 ? "installed" : "absent"} (${installDir})`
|
|
28407
|
+
);
|
|
28408
|
+
if (recorded && !installed2) {
|
|
28409
|
+
problems.push(`${t.displayName}: Tier-3 ${t.tier3.kind} not installed`);
|
|
28410
|
+
affected.push(installDir);
|
|
28411
|
+
}
|
|
28412
|
+
}
|
|
28117
28413
|
}
|
|
28118
28414
|
if (considered === 0) {
|
|
28415
|
+
if (descriptorWarnings.length > 0) {
|
|
28416
|
+
return {
|
|
28417
|
+
id: this.id,
|
|
28418
|
+
category: this.category,
|
|
28419
|
+
title: this.title,
|
|
28420
|
+
status: "warn",
|
|
28421
|
+
detail: `User target descriptor issues: ${descriptorWarnings.join("; ")}`,
|
|
28422
|
+
remediation: {
|
|
28423
|
+
kind: "manual",
|
|
28424
|
+
suggestion: `Fix or remove the offending file(s) in ~/.syntaur/targets/ (see references/user-targets.md).`,
|
|
28425
|
+
command: null
|
|
28426
|
+
},
|
|
28427
|
+
autoFixable: false
|
|
28428
|
+
};
|
|
28429
|
+
}
|
|
28119
28430
|
return {
|
|
28120
28431
|
id: this.id,
|
|
28121
28432
|
category: this.category,
|
|
@@ -28125,6 +28436,7 @@ var crossAgentSkillsCheck = {
|
|
|
28125
28436
|
autoFixable: false
|
|
28126
28437
|
};
|
|
28127
28438
|
}
|
|
28439
|
+
for (const w of descriptorWarnings) problems.push(`user target descriptor: ${w}`);
|
|
28128
28440
|
if (problems.length > 0) {
|
|
28129
28441
|
return {
|
|
28130
28442
|
id: this.id,
|
|
@@ -28156,8 +28468,8 @@ var crossAgentChecks = [crossAgentSkillsCheck];
|
|
|
28156
28468
|
// src/utils/doctor/checks/bundles.ts
|
|
28157
28469
|
init_fs();
|
|
28158
28470
|
init_paths();
|
|
28159
|
-
import { resolve as
|
|
28160
|
-
import { readdir as
|
|
28471
|
+
import { resolve as resolve68 } from "path";
|
|
28472
|
+
import { readdir as readdir25 } from "fs/promises";
|
|
28161
28473
|
import { spawnSync as spawnSync10 } from "child_process";
|
|
28162
28474
|
init_parser2();
|
|
28163
28475
|
var CATEGORY12 = "bundles";
|
|
@@ -28165,7 +28477,7 @@ async function listScopes(ctx) {
|
|
|
28165
28477
|
const out = [];
|
|
28166
28478
|
const td = todosDir();
|
|
28167
28479
|
if (await fileExists(td)) {
|
|
28168
|
-
const entries = await
|
|
28480
|
+
const entries = await readdir25(td).catch(() => []);
|
|
28169
28481
|
for (const f of entries) {
|
|
28170
28482
|
if (typeof f !== "string") continue;
|
|
28171
28483
|
if (!f.endsWith(".md") || f.endsWith("-log.md")) continue;
|
|
@@ -28181,12 +28493,12 @@ async function listScopes(ctx) {
|
|
|
28181
28493
|
}
|
|
28182
28494
|
}
|
|
28183
28495
|
if (await fileExists(ctx.config.defaultProjectDir)) {
|
|
28184
|
-
const projectEntries = await
|
|
28496
|
+
const projectEntries = await readdir25(ctx.config.defaultProjectDir, { withFileTypes: true }).catch(() => []);
|
|
28185
28497
|
for (const e of projectEntries) {
|
|
28186
28498
|
if (!e.isDirectory()) continue;
|
|
28187
28499
|
const slug = e.name;
|
|
28188
28500
|
if (typeof slug !== "string" || slug.startsWith(".")) continue;
|
|
28189
|
-
const projectMd =
|
|
28501
|
+
const projectMd = resolve68(ctx.config.defaultProjectDir, slug, "project.md");
|
|
28190
28502
|
if (!await fileExists(projectMd)) continue;
|
|
28191
28503
|
out.push({
|
|
28192
28504
|
scopeLabel: `project:${slug}`,
|
|
@@ -28475,14 +28787,14 @@ async function finalize(checks) {
|
|
|
28475
28787
|
async function readVersion() {
|
|
28476
28788
|
try {
|
|
28477
28789
|
const here = fileURLToPath11(import.meta.url);
|
|
28478
|
-
let dir =
|
|
28790
|
+
let dir = dirname21(here);
|
|
28479
28791
|
for (let i = 0; i < 6; i++) {
|
|
28480
28792
|
try {
|
|
28481
|
-
const raw2 = await
|
|
28793
|
+
const raw2 = await readFile45(join14(dir, "package.json"), "utf-8");
|
|
28482
28794
|
const parsed = JSON.parse(raw2);
|
|
28483
28795
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
28484
28796
|
} catch {
|
|
28485
|
-
dir =
|
|
28797
|
+
dir = dirname21(dir);
|
|
28486
28798
|
}
|
|
28487
28799
|
}
|
|
28488
28800
|
return null;
|
|
@@ -28575,7 +28887,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
|
|
|
28575
28887
|
];
|
|
28576
28888
|
var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
28577
28889
|
async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
28578
|
-
const absolute = isAbsolute11(inputPath) ? inputPath :
|
|
28890
|
+
const absolute = isAbsolute11(inputPath) ? inputPath : resolve69(cwd, inputPath);
|
|
28579
28891
|
const errors = [];
|
|
28580
28892
|
const warnings = [];
|
|
28581
28893
|
if (!await fileExists(absolute)) {
|
|
@@ -28588,7 +28900,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
|
28588
28900
|
}
|
|
28589
28901
|
let content;
|
|
28590
28902
|
try {
|
|
28591
|
-
content = await
|
|
28903
|
+
content = await readFile46(absolute, "utf-8");
|
|
28592
28904
|
} catch (err2) {
|
|
28593
28905
|
return {
|
|
28594
28906
|
ok: false,
|
|
@@ -28654,20 +28966,20 @@ var doctorCommand = new Command4("doctor").description("Diagnose Syntaur state a
|
|
|
28654
28966
|
process.exit(result.ok ? 0 : 1);
|
|
28655
28967
|
}
|
|
28656
28968
|
if (!await fileExists(syntaurRoot())) {
|
|
28657
|
-
const
|
|
28969
|
+
const msg2 = "~/.syntaur/ does not exist. Run `syntaur init` first.";
|
|
28658
28970
|
if (options.json) {
|
|
28659
28971
|
process.stdout.write(
|
|
28660
28972
|
JSON.stringify(
|
|
28661
28973
|
{
|
|
28662
28974
|
version: "1.0",
|
|
28663
|
-
error:
|
|
28975
|
+
error: msg2
|
|
28664
28976
|
},
|
|
28665
28977
|
null,
|
|
28666
28978
|
2
|
|
28667
28979
|
) + "\n"
|
|
28668
28980
|
);
|
|
28669
28981
|
} else {
|
|
28670
|
-
process.stderr.write(
|
|
28982
|
+
process.stderr.write(msg2 + "\n");
|
|
28671
28983
|
}
|
|
28672
28984
|
process.exit(2);
|
|
28673
28985
|
}
|
|
@@ -28686,11 +28998,11 @@ var doctorCommand = new Command4("doctor").description("Diagnose Syntaur state a
|
|
|
28686
28998
|
const hasError = report.summary.error > 0;
|
|
28687
28999
|
process.exit(hasError ? 1 : 0);
|
|
28688
29000
|
} catch (err2) {
|
|
28689
|
-
const
|
|
29001
|
+
const msg2 = err2 instanceof Error ? err2.message : String(err2);
|
|
28690
29002
|
if (options.json) {
|
|
28691
|
-
process.stdout.write(JSON.stringify({ version: "1.0", error:
|
|
29003
|
+
process.stdout.write(JSON.stringify({ version: "1.0", error: msg2 }, null, 2) + "\n");
|
|
28692
29004
|
} else {
|
|
28693
|
-
process.stderr.write(`doctor itself failed: ${
|
|
29005
|
+
process.stderr.write(`doctor itself failed: ${msg2}
|
|
28694
29006
|
`);
|
|
28695
29007
|
}
|
|
28696
29008
|
process.exit(2);
|
|
@@ -28912,8 +29224,8 @@ init_uuid();
|
|
|
28912
29224
|
init_timestamp();
|
|
28913
29225
|
init_assignment_resolver();
|
|
28914
29226
|
init_templates();
|
|
28915
|
-
import { resolve as
|
|
28916
|
-
import { readFile as
|
|
29227
|
+
import { resolve as resolve70 } from "path";
|
|
29228
|
+
import { readFile as readFile47 } from "fs/promises";
|
|
28917
29229
|
function shortId() {
|
|
28918
29230
|
return generateId().split("-")[0];
|
|
28919
29231
|
}
|
|
@@ -28943,7 +29255,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
28943
29255
|
if (!isValidSlug(target)) {
|
|
28944
29256
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
28945
29257
|
}
|
|
28946
|
-
assignmentDir =
|
|
29258
|
+
assignmentDir = resolve70(baseDir, options.project, "assignments", target);
|
|
28947
29259
|
assignmentRef = target;
|
|
28948
29260
|
} else {
|
|
28949
29261
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -28953,13 +29265,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
28953
29265
|
assignmentDir = resolved.assignmentDir;
|
|
28954
29266
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
28955
29267
|
}
|
|
28956
|
-
const commentsPath =
|
|
29268
|
+
const commentsPath = resolve70(assignmentDir, "comments.md");
|
|
28957
29269
|
const timestamp = nowTimestamp();
|
|
28958
29270
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
28959
29271
|
let currentContent;
|
|
28960
29272
|
let currentCount = 0;
|
|
28961
29273
|
if (await fileExists(commentsPath)) {
|
|
28962
|
-
currentContent = await
|
|
29274
|
+
currentContent = await readFile47(commentsPath, "utf-8");
|
|
28963
29275
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
28964
29276
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
28965
29277
|
} else {
|
|
@@ -28993,8 +29305,8 @@ ${entry}`;
|
|
|
28993
29305
|
}
|
|
28994
29306
|
|
|
28995
29307
|
// src/commands/capture.ts
|
|
28996
|
-
import { resolve as
|
|
28997
|
-
import { copyFile as copyFile3, mkdir as
|
|
29308
|
+
import { resolve as resolve74, relative as relative4, dirname as dirname22 } from "path";
|
|
29309
|
+
import { copyFile as copyFile3, mkdir as mkdir11, realpath as realpath2, rm as rm13, stat as stat9, writeFile as writeFile15 } from "fs/promises";
|
|
28998
29310
|
import { existsSync as existsSync6 } from "fs";
|
|
28999
29311
|
|
|
29000
29312
|
// src/utils/assignment-target.ts
|
|
@@ -29004,8 +29316,8 @@ init_config2();
|
|
|
29004
29316
|
init_slug();
|
|
29005
29317
|
init_assignment_resolver();
|
|
29006
29318
|
init_parser();
|
|
29007
|
-
import { resolve as
|
|
29008
|
-
import { readFile as
|
|
29319
|
+
import { resolve as resolve71 } from "path";
|
|
29320
|
+
import { readFile as readFile48 } from "fs/promises";
|
|
29009
29321
|
var AssignmentTargetError = class extends Error {
|
|
29010
29322
|
};
|
|
29011
29323
|
function classifyContext(ctx) {
|
|
@@ -29017,10 +29329,10 @@ function classifyContext(ctx) {
|
|
|
29017
29329
|
return "empty";
|
|
29018
29330
|
}
|
|
29019
29331
|
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
29020
|
-
const path =
|
|
29332
|
+
const path = resolve71(assignmentDir, "assignment.md");
|
|
29021
29333
|
if (!await fileExists(path)) return null;
|
|
29022
29334
|
try {
|
|
29023
|
-
const content = await
|
|
29335
|
+
const content = await readFile48(path, "utf-8");
|
|
29024
29336
|
const [fm] = extractFrontmatter(content);
|
|
29025
29337
|
return getField(fm, "id");
|
|
29026
29338
|
} catch {
|
|
@@ -29028,10 +29340,10 @@ async function readAssignmentFrontmatterId(assignmentDir) {
|
|
|
29028
29340
|
}
|
|
29029
29341
|
}
|
|
29030
29342
|
async function readContextJson(cwd) {
|
|
29031
|
-
const path =
|
|
29343
|
+
const path = resolve71(cwd, ".syntaur", "context.json");
|
|
29032
29344
|
if (!await fileExists(path)) return null;
|
|
29033
29345
|
try {
|
|
29034
|
-
const raw2 = await
|
|
29346
|
+
const raw2 = await readFile48(path, "utf-8");
|
|
29035
29347
|
return JSON.parse(raw2);
|
|
29036
29348
|
} catch {
|
|
29037
29349
|
return null;
|
|
@@ -29052,15 +29364,15 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
29052
29364
|
if (!isValidSlug(input4)) {
|
|
29053
29365
|
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
29054
29366
|
}
|
|
29055
|
-
const projectDir =
|
|
29056
|
-
const projectMdPath =
|
|
29367
|
+
const projectDir = resolve71(baseDir, opts.project);
|
|
29368
|
+
const projectMdPath = resolve71(projectDir, "project.md");
|
|
29057
29369
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
29058
29370
|
throw new AssignmentTargetError(
|
|
29059
29371
|
`Project "${opts.project}" not found at ${projectDir}.`
|
|
29060
29372
|
);
|
|
29061
29373
|
}
|
|
29062
|
-
const assignmentDir =
|
|
29063
|
-
const assignmentMdPath2 =
|
|
29374
|
+
const assignmentDir = resolve71(projectDir, "assignments", input4);
|
|
29375
|
+
const assignmentMdPath2 = resolve71(assignmentDir, "assignment.md");
|
|
29064
29376
|
if (!await fileExists(assignmentMdPath2)) {
|
|
29065
29377
|
throw new AssignmentTargetError(
|
|
29066
29378
|
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
@@ -29099,7 +29411,7 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
29099
29411
|
}
|
|
29100
29412
|
if (ctx.assignmentDir) {
|
|
29101
29413
|
const dir = expandHome(ctx.assignmentDir);
|
|
29102
|
-
const assignmentMdPath2 =
|
|
29414
|
+
const assignmentMdPath2 = resolve71(dir, "assignment.md");
|
|
29103
29415
|
if (!await fileExists(assignmentMdPath2)) {
|
|
29104
29416
|
throw new AssignmentTargetError(
|
|
29105
29417
|
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
@@ -29128,8 +29440,8 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
29128
29440
|
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
29129
29441
|
);
|
|
29130
29442
|
}
|
|
29131
|
-
const assignmentDir =
|
|
29132
|
-
const assignmentMdPath2 =
|
|
29443
|
+
const assignmentDir = resolve71(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
29444
|
+
const assignmentMdPath2 = resolve71(assignmentDir, "assignment.md");
|
|
29133
29445
|
if (!await fileExists(assignmentMdPath2)) {
|
|
29134
29446
|
throw new AssignmentTargetError(
|
|
29135
29447
|
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
@@ -29158,7 +29470,7 @@ init_fs();
|
|
|
29158
29470
|
import { spawn as spawn7 } from "child_process";
|
|
29159
29471
|
import { mkdtemp as mkdtemp2, rm as rm9, stat as stat7 } from "fs/promises";
|
|
29160
29472
|
import { tmpdir as tmpdir3 } from "os";
|
|
29161
|
-
import { join as
|
|
29473
|
+
import { join as join15 } from "path";
|
|
29162
29474
|
function argsFor(mode, pngPath) {
|
|
29163
29475
|
switch (mode) {
|
|
29164
29476
|
case "interactive":
|
|
@@ -29191,8 +29503,8 @@ async function captureScreenshot(mode) {
|
|
|
29191
29503
|
"screencapture is only available on macOS. Use --file <path> to attach an existing image."
|
|
29192
29504
|
);
|
|
29193
29505
|
}
|
|
29194
|
-
const tmpDir = await mkdtemp2(
|
|
29195
|
-
const pngPath =
|
|
29506
|
+
const tmpDir = await mkdtemp2(join15(tmpdir3(), "syntaur-screenshot-"));
|
|
29507
|
+
const pngPath = join15(tmpDir, "shot.png");
|
|
29196
29508
|
const cleanup = async () => {
|
|
29197
29509
|
await rm9(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
29198
29510
|
});
|
|
@@ -29230,9 +29542,9 @@ async function captureScreenshot(mode) {
|
|
|
29230
29542
|
|
|
29231
29543
|
// src/utils/asciinema.ts
|
|
29232
29544
|
import { spawn as spawn8 } from "child_process";
|
|
29233
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
29545
|
+
import { mkdtemp as mkdtemp3, readFile as readFile49, rm as rm10 } from "fs/promises";
|
|
29234
29546
|
import { tmpdir as tmpdir4 } from "os";
|
|
29235
|
-
import { join as
|
|
29547
|
+
import { join as join16 } from "path";
|
|
29236
29548
|
var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
29237
29549
|
function shellQuote2(s) {
|
|
29238
29550
|
if (s.length === 0) return `''`;
|
|
@@ -29269,8 +29581,8 @@ function runAsciinema(args, stdio) {
|
|
|
29269
29581
|
});
|
|
29270
29582
|
}
|
|
29271
29583
|
async function captureAsciinema(opts) {
|
|
29272
|
-
const tmpDir = await mkdtemp3(
|
|
29273
|
-
const castPath =
|
|
29584
|
+
const tmpDir = await mkdtemp3(join16(tmpdir4(), "syntaur-asciinema-"));
|
|
29585
|
+
const castPath = join16(tmpDir, "session.cast");
|
|
29274
29586
|
const cleanup = async () => {
|
|
29275
29587
|
await rm10(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
29276
29588
|
});
|
|
@@ -29293,7 +29605,7 @@ async function captureAsciinema(opts) {
|
|
|
29293
29605
|
}
|
|
29294
29606
|
throw err2;
|
|
29295
29607
|
}
|
|
29296
|
-
const text = await
|
|
29608
|
+
const text = await readFile49(castPath, "utf8").catch(() => null);
|
|
29297
29609
|
if (text === null) {
|
|
29298
29610
|
throw new Error(
|
|
29299
29611
|
`asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
|
|
@@ -29321,9 +29633,9 @@ async function captureAsciinema(opts) {
|
|
|
29321
29633
|
// src/utils/recording.ts
|
|
29322
29634
|
init_paths();
|
|
29323
29635
|
import { spawn as spawn9 } from "child_process";
|
|
29324
|
-
import { mkdir as
|
|
29636
|
+
import { mkdir as mkdir10, mkdtemp as mkdtemp4, open as open3, readFile as readFile50, rm as rm11, stat as stat8, unlink as unlink10, writeFile as writeFile14 } from "fs/promises";
|
|
29325
29637
|
import { tmpdir as tmpdir5 } from "os";
|
|
29326
|
-
import { join as
|
|
29638
|
+
import { join as join17, resolve as resolve72 } from "path";
|
|
29327
29639
|
import { setTimeout as sleep } from "timers/promises";
|
|
29328
29640
|
function sigintPollIntervalMs() {
|
|
29329
29641
|
const raw2 = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
|
|
@@ -29344,13 +29656,13 @@ function sigtermWaitMs() {
|
|
|
29344
29656
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
|
|
29345
29657
|
}
|
|
29346
29658
|
function pidfilePath() {
|
|
29347
|
-
return
|
|
29659
|
+
return resolve72(syntaurRoot(), "recording.pid");
|
|
29348
29660
|
}
|
|
29349
29661
|
function logPath2() {
|
|
29350
|
-
return
|
|
29662
|
+
return resolve72(syntaurRoot(), "recording.log");
|
|
29351
29663
|
}
|
|
29352
29664
|
function sidecarPath() {
|
|
29353
|
-
return
|
|
29665
|
+
return resolve72(syntaurRoot(), "recording.json");
|
|
29354
29666
|
}
|
|
29355
29667
|
function ffmpegArgs(device, fps, mp4Path) {
|
|
29356
29668
|
return [
|
|
@@ -29395,7 +29707,7 @@ async function acquirePidfile(pidfile) {
|
|
|
29395
29707
|
} catch (err2) {
|
|
29396
29708
|
if (err2.code !== "EEXIST") throw err2;
|
|
29397
29709
|
if (attempt === 1) throw err2;
|
|
29398
|
-
const existing = (await
|
|
29710
|
+
const existing = (await readFile50(pidfile, "utf-8").catch(() => "")).trim();
|
|
29399
29711
|
if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
|
|
29400
29712
|
const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
|
|
29401
29713
|
const parentPid = Number.parseInt(parentPidRaw, 10);
|
|
@@ -29437,7 +29749,7 @@ async function startRecording(input4) {
|
|
|
29437
29749
|
);
|
|
29438
29750
|
}
|
|
29439
29751
|
const root = syntaurRoot();
|
|
29440
|
-
await
|
|
29752
|
+
await mkdir10(root, { recursive: true });
|
|
29441
29753
|
const pidfile = pidfilePath();
|
|
29442
29754
|
const log = logPath2();
|
|
29443
29755
|
const sidecar = sidecarPath();
|
|
@@ -29448,8 +29760,8 @@ async function startRecording(input4) {
|
|
|
29448
29760
|
let acquiredPid = null;
|
|
29449
29761
|
try {
|
|
29450
29762
|
logHandle = await open3(log, "w");
|
|
29451
|
-
tmpDir = await mkdtemp4(
|
|
29452
|
-
const mp4Path =
|
|
29763
|
+
tmpDir = await mkdtemp4(join17(tmpdir5(), "syntaur-recording-"));
|
|
29764
|
+
const mp4Path = join17(tmpDir, "recording.mp4");
|
|
29453
29765
|
let child;
|
|
29454
29766
|
try {
|
|
29455
29767
|
child = spawn9("ffmpeg", ffmpegArgs(input4.device, input4.fps, mp4Path), {
|
|
@@ -29497,7 +29809,7 @@ async function startRecording(input4) {
|
|
|
29497
29809
|
logHandle = null;
|
|
29498
29810
|
if (warmupMs > 0) await sleep(warmupMs);
|
|
29499
29811
|
if (!await isProcessAlive(pid)) {
|
|
29500
|
-
const tail = await
|
|
29812
|
+
const tail = await readFile50(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
|
|
29501
29813
|
acquiredPid = null;
|
|
29502
29814
|
throw new Error(
|
|
29503
29815
|
`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}
|
|
@@ -29554,7 +29866,7 @@ ${tail}`
|
|
|
29554
29866
|
async function stopRecording() {
|
|
29555
29867
|
const pidfile = pidfilePath();
|
|
29556
29868
|
const sidecar = sidecarPath();
|
|
29557
|
-
const pidRaw = await
|
|
29869
|
+
const pidRaw = await readFile50(pidfile, "utf-8").catch(() => null);
|
|
29558
29870
|
if (pidRaw === null) {
|
|
29559
29871
|
throw new Error(
|
|
29560
29872
|
`No active recording found (no pidfile at ${pidfile}). Did you run --start?`
|
|
@@ -29564,7 +29876,7 @@ async function stopRecording() {
|
|
|
29564
29876
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
29565
29877
|
throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
|
|
29566
29878
|
}
|
|
29567
|
-
const sidecarRaw = await
|
|
29879
|
+
const sidecarRaw = await readFile50(sidecar, "utf-8").catch(() => null);
|
|
29568
29880
|
if (sidecarRaw === null) {
|
|
29569
29881
|
throw new Error(
|
|
29570
29882
|
`No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
|
|
@@ -29629,7 +29941,7 @@ async function stopRecording() {
|
|
|
29629
29941
|
// src/db/proof-db.ts
|
|
29630
29942
|
init_paths();
|
|
29631
29943
|
import Database5 from "better-sqlite3";
|
|
29632
|
-
import { resolve as
|
|
29944
|
+
import { resolve as resolve73 } from "path";
|
|
29633
29945
|
var db4 = null;
|
|
29634
29946
|
var PROOF_SCHEMA_VERSION = "1";
|
|
29635
29947
|
var SCHEMA_SQL4 = `
|
|
@@ -29649,7 +29961,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
29649
29961
|
`;
|
|
29650
29962
|
function initProofDb(dbPath) {
|
|
29651
29963
|
if (db4) return db4;
|
|
29652
|
-
const finalPath = dbPath ??
|
|
29964
|
+
const finalPath = dbPath ?? resolve73(syntaurRoot(), "syntaur.db");
|
|
29653
29965
|
db4 = new Database5(finalPath);
|
|
29654
29966
|
db4.pragma("journal_mode = WAL");
|
|
29655
29967
|
db4.exec(SCHEMA_SQL4);
|
|
@@ -29697,9 +30009,9 @@ function listArtifactsByAssignment(assignmentId) {
|
|
|
29697
30009
|
|
|
29698
30010
|
// src/utils/transcribers/elevenlabs.ts
|
|
29699
30011
|
import { spawn as spawn10 } from "child_process";
|
|
29700
|
-
import { mkdtemp as mkdtemp5, readFile as
|
|
30012
|
+
import { mkdtemp as mkdtemp5, readFile as readFile51, rm as rm12 } from "fs/promises";
|
|
29701
30013
|
import { tmpdir as tmpdir6 } from "os";
|
|
29702
|
-
import { join as
|
|
30014
|
+
import { join as join18 } from "path";
|
|
29703
30015
|
var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
|
|
29704
30016
|
var NO_AUDIO_MARKERS = [
|
|
29705
30017
|
"Stream map '0:a:0' matches no streams",
|
|
@@ -29761,7 +30073,7 @@ async function extractAudio(videoAbsPath, wavOut) {
|
|
|
29761
30073
|
throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
|
|
29762
30074
|
}
|
|
29763
30075
|
async function callScribe(wavPath, apiKey, opts) {
|
|
29764
|
-
const audio = await
|
|
30076
|
+
const audio = await readFile51(wavPath);
|
|
29765
30077
|
const form = new FormData();
|
|
29766
30078
|
form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
|
|
29767
30079
|
form.set("model_id", "scribe_v1");
|
|
@@ -29789,8 +30101,8 @@ var elevenLabsScribe = {
|
|
|
29789
30101
|
"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."
|
|
29790
30102
|
);
|
|
29791
30103
|
}
|
|
29792
|
-
const tmp = await mkdtemp5(
|
|
29793
|
-
const wav =
|
|
30104
|
+
const tmp = await mkdtemp5(join18(tmpdir6(), "syntaur-transcribe-"));
|
|
30105
|
+
const wav = join18(tmp, "audio.wav");
|
|
29794
30106
|
try {
|
|
29795
30107
|
await extractAudio(videoAbsPath, wav);
|
|
29796
30108
|
return await callScribe(wav, apiKey, opts);
|
|
@@ -30064,7 +30376,7 @@ async function captureCommand(target, options = {}) {
|
|
|
30064
30376
|
criterionIndex = sidecar.criterionIndex;
|
|
30065
30377
|
stopNote = sidecar.note;
|
|
30066
30378
|
resolvedSource = mp4Path;
|
|
30067
|
-
const mp4TmpDir =
|
|
30379
|
+
const mp4TmpDir = dirname22(mp4Path);
|
|
30068
30380
|
shelloutCleanup = async () => {
|
|
30069
30381
|
await rm13(mp4TmpDir, { recursive: true, force: true }).catch(() => {
|
|
30070
30382
|
});
|
|
@@ -30077,7 +30389,7 @@ async function captureCommand(target, options = {}) {
|
|
|
30077
30389
|
});
|
|
30078
30390
|
}
|
|
30079
30391
|
if (options.file) {
|
|
30080
|
-
const expanded = options.file.startsWith("~/") ?
|
|
30392
|
+
const expanded = options.file.startsWith("~/") ? resolve74(process.env.HOME ?? "", options.file.slice(2)) : resolve74(options.file);
|
|
30081
30393
|
if (!await fileExists(expanded)) {
|
|
30082
30394
|
throw new Error(`--file does not exist: ${options.file}`);
|
|
30083
30395
|
}
|
|
@@ -30118,8 +30430,8 @@ async function captureCommand(target, options = {}) {
|
|
|
30118
30430
|
}
|
|
30119
30431
|
initProofDb();
|
|
30120
30432
|
const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
|
|
30121
|
-
const destDir =
|
|
30122
|
-
if (resolvedSource) await
|
|
30433
|
+
const destDir = resolve74(proofDir(resolved.assignmentDir), subdir);
|
|
30434
|
+
if (resolvedSource) await mkdir11(destDir, { recursive: true });
|
|
30123
30435
|
const ext = resolvedSource ? extensionForKind(kind) : null;
|
|
30124
30436
|
let id = null;
|
|
30125
30437
|
let relativeFilePath = null;
|
|
@@ -30127,7 +30439,7 @@ async function captureCommand(target, options = {}) {
|
|
|
30127
30439
|
let lastErr = null;
|
|
30128
30440
|
for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
|
|
30129
30441
|
const candidate = generateArtifactId();
|
|
30130
|
-
const candidateAbsPath = resolvedSource && ext ?
|
|
30442
|
+
const candidateAbsPath = resolvedSource && ext ? resolve74(destDir, `${candidate}.${ext}`) : null;
|
|
30131
30443
|
const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
|
|
30132
30444
|
try {
|
|
30133
30445
|
insertArtifact({
|
|
@@ -30171,7 +30483,7 @@ async function captureCommand(target, options = {}) {
|
|
|
30171
30483
|
}
|
|
30172
30484
|
}
|
|
30173
30485
|
if (options.transcribe && kind === "video" && absPath && id) {
|
|
30174
|
-
const sidecarPath2 =
|
|
30486
|
+
const sidecarPath2 = resolve74(destDir, `${id}.transcript.md`);
|
|
30175
30487
|
if (existsSync6(sidecarPath2)) {
|
|
30176
30488
|
console.warn(
|
|
30177
30489
|
`transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
|
|
@@ -30203,7 +30515,7 @@ async function captureCommand(target, options = {}) {
|
|
|
30203
30515
|
const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
|
|
30204
30516
|
console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
|
|
30205
30517
|
if (relativeFilePath) {
|
|
30206
|
-
console.log(` file: ${
|
|
30518
|
+
console.log(` file: ${resolve74(resolved.assignmentDir, relativeFilePath)}`);
|
|
30207
30519
|
}
|
|
30208
30520
|
} catch (err2) {
|
|
30209
30521
|
if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
|
|
@@ -30226,8 +30538,8 @@ async function captureCommand(target, options = {}) {
|
|
|
30226
30538
|
|
|
30227
30539
|
// src/commands/proof.ts
|
|
30228
30540
|
import { Command as Command6 } from "commander";
|
|
30229
|
-
import { readFile as
|
|
30230
|
-
import { resolve as
|
|
30541
|
+
import { readFile as readFile52, writeFile as writeFile16, rename as rename10, stat as stat10 } from "fs/promises";
|
|
30542
|
+
import { resolve as resolve75, relative as relative5, isAbsolute as isAbsolute12, dirname as dirname23 } from "path";
|
|
30231
30543
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
30232
30544
|
|
|
30233
30545
|
// src/utils/acceptance-criteria-parse.ts
|
|
@@ -30467,11 +30779,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
|
|
|
30467
30779
|
|
|
30468
30780
|
// src/commands/proof.ts
|
|
30469
30781
|
async function readAssignmentMeta(assignmentDir) {
|
|
30470
|
-
const path =
|
|
30782
|
+
const path = resolve75(assignmentDir, "assignment.md");
|
|
30471
30783
|
if (!await fileExists(path)) {
|
|
30472
30784
|
return { title: "", body: "" };
|
|
30473
30785
|
}
|
|
30474
|
-
const content = await
|
|
30786
|
+
const content = await readFile52(path, "utf-8");
|
|
30475
30787
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
30476
30788
|
let title = "";
|
|
30477
30789
|
if (fmMatch) {
|
|
@@ -30518,7 +30830,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
30518
30830
|
for (const r of rows) {
|
|
30519
30831
|
if (!r.file_path) continue;
|
|
30520
30832
|
if (r.kind !== "http" && r.kind !== "text") continue;
|
|
30521
|
-
const abs =
|
|
30833
|
+
const abs = resolve75(assignmentDir, r.file_path);
|
|
30522
30834
|
if (!isWithin(proofRoot, abs)) {
|
|
30523
30835
|
out.set(r.file_path, null);
|
|
30524
30836
|
continue;
|
|
@@ -30533,7 +30845,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
30533
30845
|
continue;
|
|
30534
30846
|
}
|
|
30535
30847
|
try {
|
|
30536
|
-
out.set(r.file_path, await
|
|
30848
|
+
out.set(r.file_path, await readFile52(abs, "utf-8"));
|
|
30537
30849
|
} catch {
|
|
30538
30850
|
out.set(r.file_path, null);
|
|
30539
30851
|
}
|
|
@@ -30545,14 +30857,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
|
|
|
30545
30857
|
const proofRoot = proofDir(assignmentDir);
|
|
30546
30858
|
for (const r of rows) {
|
|
30547
30859
|
if (r.kind !== "video" || !r.file_path) continue;
|
|
30548
|
-
const videoAbs =
|
|
30549
|
-
const sidecar =
|
|
30860
|
+
const videoAbs = resolve75(assignmentDir, r.file_path);
|
|
30861
|
+
const sidecar = resolve75(dirname23(videoAbs), `${r.id}.transcript.md`);
|
|
30550
30862
|
if (!isWithin(proofRoot, sidecar)) continue;
|
|
30551
30863
|
if (!await fileExists(sidecar)) continue;
|
|
30552
30864
|
const st = await stat10(sidecar);
|
|
30553
30865
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
|
|
30554
30866
|
try {
|
|
30555
|
-
out.set(r.id, await
|
|
30867
|
+
out.set(r.id, await readFile52(sidecar, "utf-8"));
|
|
30556
30868
|
} catch {
|
|
30557
30869
|
}
|
|
30558
30870
|
}
|
|
@@ -30586,8 +30898,8 @@ async function proofBuildCommand(target, options = {}) {
|
|
|
30586
30898
|
};
|
|
30587
30899
|
const md = renderProofMarkdown(renderParams);
|
|
30588
30900
|
const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
|
|
30589
|
-
const mdPath =
|
|
30590
|
-
const htmlPath =
|
|
30901
|
+
const mdPath = resolve75(resolved.assignmentDir, "proof.md");
|
|
30902
|
+
const htmlPath = resolve75(resolved.assignmentDir, "proof.html");
|
|
30591
30903
|
await atomicWrite(mdPath, md);
|
|
30592
30904
|
await atomicWrite(htmlPath, html);
|
|
30593
30905
|
console.log(`Wrote ${htmlPath}`);
|
|
@@ -31252,7 +31564,7 @@ async function runCcusage(opts = {}) {
|
|
|
31252
31564
|
};
|
|
31253
31565
|
}
|
|
31254
31566
|
function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
31255
|
-
return new Promise((
|
|
31567
|
+
return new Promise((resolve87) => {
|
|
31256
31568
|
const child = spawn11(binary, args, {
|
|
31257
31569
|
env: env ?? process.env,
|
|
31258
31570
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -31266,7 +31578,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
|
31266
31578
|
if (settled) return;
|
|
31267
31579
|
settled = true;
|
|
31268
31580
|
clearTimeout(timer2);
|
|
31269
|
-
|
|
31581
|
+
resolve87(result);
|
|
31270
31582
|
};
|
|
31271
31583
|
const timer2 = setTimeout(() => {
|
|
31272
31584
|
timedOut = true;
|
|
@@ -31310,8 +31622,8 @@ function isoToCcusageDate(iso) {
|
|
|
31310
31622
|
|
|
31311
31623
|
// src/usage/cwd-extractor.ts
|
|
31312
31624
|
init_paths();
|
|
31313
|
-
import { open as open4, readdir as
|
|
31314
|
-
import { join as
|
|
31625
|
+
import { open as open4, readdir as readdir26, stat as stat11 } from "fs/promises";
|
|
31626
|
+
import { join as join19 } from "path";
|
|
31315
31627
|
import { homedir as homedir12 } from "os";
|
|
31316
31628
|
var SCAN_LINE_CAP = 50;
|
|
31317
31629
|
var TAIL_READ_BYTES = 8 * 1024;
|
|
@@ -31386,12 +31698,12 @@ async function* walkClaudeProjects(opts = {}) {
|
|
|
31386
31698
|
const dirs = await listDirSafe(root);
|
|
31387
31699
|
for (const dirent of dirs) {
|
|
31388
31700
|
if (!dirent.isDirectory) continue;
|
|
31389
|
-
const dirPath =
|
|
31701
|
+
const dirPath = join19(root, dirent.name);
|
|
31390
31702
|
const files = await listDirSafe(dirPath);
|
|
31391
31703
|
let cachedCwd = null;
|
|
31392
31704
|
for (const f of files) {
|
|
31393
31705
|
if (!f.isFile || !f.name.endsWith(".jsonl")) continue;
|
|
31394
|
-
const filePath =
|
|
31706
|
+
const filePath = join19(dirPath, f.name);
|
|
31395
31707
|
if (opts.sinceMtimeMs !== void 0) {
|
|
31396
31708
|
const mtime = await mtimeMs(filePath);
|
|
31397
31709
|
if (mtime !== null && mtime < opts.sinceMtimeMs) continue;
|
|
@@ -31428,12 +31740,12 @@ function resolveCodexSessionsRoot(override) {
|
|
|
31428
31740
|
const fromSessionsEnv = process.env.CODEX_SESSIONS_DIR;
|
|
31429
31741
|
if (fromSessionsEnv && fromSessionsEnv.length > 0) return expandHome(fromSessionsEnv);
|
|
31430
31742
|
const fromHomeEnv = process.env.CODEX_HOME;
|
|
31431
|
-
if (fromHomeEnv && fromHomeEnv.length > 0) return
|
|
31432
|
-
return
|
|
31743
|
+
if (fromHomeEnv && fromHomeEnv.length > 0) return join19(expandHome(fromHomeEnv), "sessions");
|
|
31744
|
+
return join19(homedir12(), ".codex", "sessions");
|
|
31433
31745
|
}
|
|
31434
31746
|
async function listDirSafe(path) {
|
|
31435
31747
|
try {
|
|
31436
|
-
const entries = await
|
|
31748
|
+
const entries = await readdir26(path, { withFileTypes: true });
|
|
31437
31749
|
return entries.map((e) => ({
|
|
31438
31750
|
name: e.name,
|
|
31439
31751
|
isFile: e.isFile(),
|
|
@@ -31449,7 +31761,7 @@ async function* walkJsonlRecursive(root) {
|
|
|
31449
31761
|
const current = stack.pop();
|
|
31450
31762
|
const entries = await listDirSafe(current);
|
|
31451
31763
|
for (const e of entries) {
|
|
31452
|
-
const full =
|
|
31764
|
+
const full = join19(current, e.name);
|
|
31453
31765
|
if (e.isDirectory) {
|
|
31454
31766
|
stack.push(full);
|
|
31455
31767
|
} else if (e.isFile && e.name.endsWith(".jsonl")) {
|
|
@@ -31788,8 +32100,8 @@ init_slug();
|
|
|
31788
32100
|
init_timestamp();
|
|
31789
32101
|
init_assignment_resolver();
|
|
31790
32102
|
init_assignment_todos();
|
|
31791
|
-
import { resolve as
|
|
31792
|
-
import { readFile as
|
|
32103
|
+
import { resolve as resolve76 } from "path";
|
|
32104
|
+
import { readFile as readFile53 } from "fs/promises";
|
|
31793
32105
|
async function requestCommand(target, text, options = {}) {
|
|
31794
32106
|
if (!text || !text.trim()) {
|
|
31795
32107
|
throw new Error("Request text cannot be empty.");
|
|
@@ -31805,7 +32117,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
31805
32117
|
if (!isValidSlug(target)) {
|
|
31806
32118
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
31807
32119
|
}
|
|
31808
|
-
assignmentDir =
|
|
32120
|
+
assignmentDir = resolve76(baseDir, options.project, "assignments", target);
|
|
31809
32121
|
targetRef = target;
|
|
31810
32122
|
} else {
|
|
31811
32123
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -31815,12 +32127,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
31815
32127
|
assignmentDir = resolved.assignmentDir;
|
|
31816
32128
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
31817
32129
|
}
|
|
31818
|
-
const assignmentMdPath2 =
|
|
32130
|
+
const assignmentMdPath2 = resolve76(assignmentDir, "assignment.md");
|
|
31819
32131
|
if (!await fileExists(assignmentMdPath2)) {
|
|
31820
32132
|
throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
|
|
31821
32133
|
}
|
|
31822
32134
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
31823
|
-
let content = await
|
|
32135
|
+
let content = await readFile53(assignmentMdPath2, "utf-8");
|
|
31824
32136
|
content = appendTodosToAssignmentBody(content, [
|
|
31825
32137
|
{ description: `${text.trim()} (from: ${source})` }
|
|
31826
32138
|
]);
|
|
@@ -31834,13 +32146,13 @@ init_fs();
|
|
|
31834
32146
|
init_paths();
|
|
31835
32147
|
init_config2();
|
|
31836
32148
|
import { Command as Command9 } from "commander";
|
|
31837
|
-
import { readFile as
|
|
31838
|
-
import { resolve as
|
|
32149
|
+
import { readFile as readFile54, readdir as readdir27 } from "fs/promises";
|
|
32150
|
+
import { resolve as resolve77 } from "path";
|
|
31839
32151
|
async function readContextAssignmentDir(cwd) {
|
|
31840
|
-
const path =
|
|
32152
|
+
const path = resolve77(cwd, ".syntaur", "context.json");
|
|
31841
32153
|
if (!await fileExists(path)) return null;
|
|
31842
32154
|
try {
|
|
31843
|
-
const raw2 = await
|
|
32155
|
+
const raw2 = await readFile54(path, "utf-8");
|
|
31844
32156
|
const ctx = JSON.parse(raw2);
|
|
31845
32157
|
if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
|
|
31846
32158
|
return ctx.assignmentDir;
|
|
@@ -31854,9 +32166,9 @@ async function resolveAssignmentDir(opts) {
|
|
|
31854
32166
|
const cwd = opts.cwd ?? process.cwd();
|
|
31855
32167
|
if (opts.assignment) {
|
|
31856
32168
|
if (opts.project) {
|
|
31857
|
-
return
|
|
32169
|
+
return resolve77((await readConfig()).defaultProjectDir, opts.project, "assignments", opts.assignment);
|
|
31858
32170
|
}
|
|
31859
|
-
return
|
|
32171
|
+
return resolve77(assignmentsDir(), opts.assignment);
|
|
31860
32172
|
}
|
|
31861
32173
|
const fromCtx = await readContextAssignmentDir(cwd);
|
|
31862
32174
|
if (fromCtx) return fromCtx;
|
|
@@ -31867,7 +32179,7 @@ async function resolveAssignmentDir(opts) {
|
|
|
31867
32179
|
var PLAN_PATTERN = /^plan(?:-v(\d+))?\.md$/;
|
|
31868
32180
|
async function listPlanFiles(assignmentDir) {
|
|
31869
32181
|
if (!await fileExists(assignmentDir)) return [];
|
|
31870
|
-
const entries = await
|
|
32182
|
+
const entries = await readdir27(assignmentDir, { withFileTypes: true });
|
|
31871
32183
|
const out = [];
|
|
31872
32184
|
for (const e of entries) {
|
|
31873
32185
|
if (!e.isFile()) continue;
|
|
@@ -32052,17 +32364,17 @@ async function runPlanCreate(options) {
|
|
|
32052
32364
|
if (!await fileExists(assignmentDir)) {
|
|
32053
32365
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
32054
32366
|
}
|
|
32055
|
-
const assignmentMdPath2 =
|
|
32367
|
+
const assignmentMdPath2 = resolve77(assignmentDir, "assignment.md");
|
|
32056
32368
|
if (!await fileExists(assignmentMdPath2)) {
|
|
32057
32369
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
32058
32370
|
}
|
|
32059
|
-
const planPath =
|
|
32371
|
+
const planPath = resolve77(assignmentDir, "plan.md");
|
|
32060
32372
|
if (await fileExists(planPath) && !options.force) {
|
|
32061
32373
|
throw new Error(
|
|
32062
32374
|
"plan.md already exists. Use --force to overwrite, or `syntaur plan version` to create the next version."
|
|
32063
32375
|
);
|
|
32064
32376
|
}
|
|
32065
|
-
const assignmentMd = await
|
|
32377
|
+
const assignmentMd = await readFile54(assignmentMdPath2, "utf-8");
|
|
32066
32378
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
32067
32379
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
32068
32380
|
await writeFileForce(planPath, buildInitialPlanStub(slug));
|
|
@@ -32082,7 +32394,7 @@ async function runPlanVersion(options) {
|
|
|
32082
32394
|
if (!await fileExists(assignmentDir)) {
|
|
32083
32395
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
32084
32396
|
}
|
|
32085
|
-
const assignmentMdPath2 =
|
|
32397
|
+
const assignmentMdPath2 = resolve77(assignmentDir, "assignment.md");
|
|
32086
32398
|
if (!await fileExists(assignmentMdPath2)) {
|
|
32087
32399
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
32088
32400
|
}
|
|
@@ -32094,15 +32406,15 @@ async function runPlanVersion(options) {
|
|
|
32094
32406
|
}
|
|
32095
32407
|
const current = planFiles[planFiles.length - 1];
|
|
32096
32408
|
const next = nextPlanFileName(current.version);
|
|
32097
|
-
const newPath =
|
|
32409
|
+
const newPath = resolve77(assignmentDir, next.fileName);
|
|
32098
32410
|
if (await fileExists(newPath) && !options.force) {
|
|
32099
32411
|
throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
|
|
32100
32412
|
}
|
|
32101
|
-
const assignmentMd = await
|
|
32413
|
+
const assignmentMd = await readFile54(assignmentMdPath2, "utf-8");
|
|
32102
32414
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
32103
32415
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
32104
|
-
const oldPlanPath =
|
|
32105
|
-
const oldPlanContent = await
|
|
32416
|
+
const oldPlanPath = resolve77(assignmentDir, current.fileName);
|
|
32417
|
+
const oldPlanContent = await readFile54(oldPlanPath, "utf-8");
|
|
32106
32418
|
const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
|
|
32107
32419
|
const carriedTodos = extractUncheckedTodos(oldBody);
|
|
32108
32420
|
const stub = buildNewPlanStub({
|
|
@@ -32150,26 +32462,26 @@ init_paths();
|
|
|
32150
32462
|
init_config2();
|
|
32151
32463
|
init_timestamp();
|
|
32152
32464
|
import { Command as Command10 } from "commander";
|
|
32153
|
-
import { readFile as
|
|
32154
|
-
import { resolve as
|
|
32465
|
+
import { readFile as readFile55, readdir as readdir28, stat as stat12 } from "fs/promises";
|
|
32466
|
+
import { resolve as resolve78 } from "path";
|
|
32155
32467
|
async function readContext(cwd) {
|
|
32156
|
-
const path =
|
|
32468
|
+
const path = resolve78(cwd, ".syntaur", "context.json");
|
|
32157
32469
|
if (!await fileExists(path)) return null;
|
|
32158
32470
|
try {
|
|
32159
|
-
const raw2 = await
|
|
32471
|
+
const raw2 = await readFile55(path, "utf-8");
|
|
32160
32472
|
return JSON.parse(raw2);
|
|
32161
32473
|
} catch {
|
|
32162
32474
|
return null;
|
|
32163
32475
|
}
|
|
32164
32476
|
}
|
|
32165
32477
|
async function findLatestSessionSummary(assignmentDir) {
|
|
32166
|
-
const sessionsRoot =
|
|
32478
|
+
const sessionsRoot = resolve78(assignmentDir, "sessions");
|
|
32167
32479
|
if (!await fileExists(sessionsRoot)) return null;
|
|
32168
|
-
const entries = await
|
|
32480
|
+
const entries = await readdir28(sessionsRoot, { withFileTypes: true });
|
|
32169
32481
|
let best = null;
|
|
32170
32482
|
for (const entry of entries) {
|
|
32171
32483
|
if (!entry.isDirectory()) continue;
|
|
32172
|
-
const summaryPath =
|
|
32484
|
+
const summaryPath = resolve78(sessionsRoot, entry.name, "summary.md");
|
|
32173
32485
|
if (!await fileExists(summaryPath)) continue;
|
|
32174
32486
|
const st = await stat12(summaryPath);
|
|
32175
32487
|
if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
|
|
@@ -32179,9 +32491,9 @@ async function findLatestSessionSummary(assignmentDir) {
|
|
|
32179
32491
|
return best;
|
|
32180
32492
|
}
|
|
32181
32493
|
async function findOpenHandoff(assignmentDir) {
|
|
32182
|
-
const handoffPath =
|
|
32494
|
+
const handoffPath = resolve78(assignmentDir, "handoff.md");
|
|
32183
32495
|
if (!await fileExists(handoffPath)) return null;
|
|
32184
|
-
const content = await
|
|
32496
|
+
const content = await readFile55(handoffPath, "utf-8");
|
|
32185
32497
|
const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
|
|
32186
32498
|
if (body.length === 0) return null;
|
|
32187
32499
|
if (/^<!--[\s\S]*-->$/.test(body)) return null;
|
|
@@ -32276,7 +32588,7 @@ async function resolveSaveTarget(options, cwd) {
|
|
|
32276
32588
|
let slug;
|
|
32277
32589
|
const ctx = await readContext(cwd);
|
|
32278
32590
|
if (options.assignment) {
|
|
32279
|
-
assignmentDir = options.project ?
|
|
32591
|
+
assignmentDir = options.project ? resolve78((await readConfig()).defaultProjectDir, options.project, "assignments", options.assignment) : resolve78(assignmentsDir(), options.assignment);
|
|
32280
32592
|
slug = options.assignment;
|
|
32281
32593
|
} else {
|
|
32282
32594
|
if (!ctx?.assignmentDir) {
|
|
@@ -32329,21 +32641,21 @@ function extractCreated(content) {
|
|
|
32329
32641
|
}
|
|
32330
32642
|
async function runSessionSave(options, cwd = process.cwd(), body) {
|
|
32331
32643
|
const { assignmentDir, slug, sessionId } = await resolveSaveTarget(options, cwd);
|
|
32332
|
-
if (!await fileExists(
|
|
32644
|
+
if (!await fileExists(resolve78(assignmentDir, "assignment.md"))) {
|
|
32333
32645
|
throw new Error(`No assignment found at ${assignmentDir} (missing assignment.md).`);
|
|
32334
32646
|
}
|
|
32335
|
-
const sessionDir =
|
|
32336
|
-
const summaryPath =
|
|
32647
|
+
const sessionDir = resolve78(assignmentDir, "sessions", sessionId);
|
|
32648
|
+
const summaryPath = resolve78(sessionDir, "summary.md");
|
|
32337
32649
|
const now = nowTimestamp();
|
|
32338
32650
|
let created = now;
|
|
32339
32651
|
if (await fileExists(summaryPath)) {
|
|
32340
|
-
const existing = await
|
|
32652
|
+
const existing = await readFile55(summaryPath, "utf-8");
|
|
32341
32653
|
created = extractCreated(existing) ?? now;
|
|
32342
32654
|
}
|
|
32343
32655
|
let sectionBody = body;
|
|
32344
32656
|
if (sectionBody === void 0) {
|
|
32345
32657
|
if (options.fromFile) {
|
|
32346
|
-
sectionBody = await
|
|
32658
|
+
sectionBody = await readFile55(resolve78(cwd, options.fromFile), "utf-8");
|
|
32347
32659
|
} else {
|
|
32348
32660
|
sectionBody = await readStdin();
|
|
32349
32661
|
}
|
|
@@ -32391,13 +32703,13 @@ init_config2();
|
|
|
32391
32703
|
init_timestamp();
|
|
32392
32704
|
init_frontmatter();
|
|
32393
32705
|
import { Command as Command11 } from "commander";
|
|
32394
|
-
import { readFile as
|
|
32395
|
-
import { resolve as
|
|
32706
|
+
import { readFile as readFile56 } from "fs/promises";
|
|
32707
|
+
import { resolve as resolve79 } from "path";
|
|
32396
32708
|
async function readContext2(cwd) {
|
|
32397
|
-
const path =
|
|
32709
|
+
const path = resolve79(cwd, ".syntaur", "context.json");
|
|
32398
32710
|
if (!await fileExists(path)) return null;
|
|
32399
32711
|
try {
|
|
32400
|
-
return JSON.parse(await
|
|
32712
|
+
return JSON.parse(await readFile56(path, "utf-8"));
|
|
32401
32713
|
} catch {
|
|
32402
32714
|
return null;
|
|
32403
32715
|
}
|
|
@@ -32406,12 +32718,12 @@ async function resolveAssignmentPath2(opts) {
|
|
|
32406
32718
|
if (opts.assignment) {
|
|
32407
32719
|
if (opts.project) {
|
|
32408
32720
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
32409
|
-
return
|
|
32721
|
+
return resolve79(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
32410
32722
|
}
|
|
32411
|
-
return
|
|
32723
|
+
return resolve79(assignmentsDir(), opts.assignment, "assignment.md");
|
|
32412
32724
|
}
|
|
32413
32725
|
const ctx = await readContext2(opts.cwd);
|
|
32414
|
-
if (ctx?.assignmentDir) return
|
|
32726
|
+
if (ctx?.assignmentDir) return resolve79(ctx.assignmentDir, "assignment.md");
|
|
32415
32727
|
throw new Error(
|
|
32416
32728
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
32417
32729
|
);
|
|
@@ -32422,7 +32734,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
|
|
|
32422
32734
|
}
|
|
32423
32735
|
const repository = options.repository ?? cwd;
|
|
32424
32736
|
const parentBranch = options.parentBranch ?? "main";
|
|
32425
|
-
const worktreePath = options.worktreePath ??
|
|
32737
|
+
const worktreePath = options.worktreePath ?? resolve79(repository, ".worktrees", options.branch);
|
|
32426
32738
|
const assignmentPath = await resolveAssignmentPath2({
|
|
32427
32739
|
assignment: options.assignment,
|
|
32428
32740
|
project: options.project,
|
|
@@ -32452,7 +32764,7 @@ async function runWorktreeRemove(options, cwd = process.cwd()) {
|
|
|
32452
32764
|
if (!await fileExists(assignmentPath)) {
|
|
32453
32765
|
throw new Error(`Assignment file not found: ${assignmentPath}`);
|
|
32454
32766
|
}
|
|
32455
|
-
const original = await
|
|
32767
|
+
const original = await readFile56(assignmentPath, "utf-8");
|
|
32456
32768
|
const fm = parseAssignmentFrontmatter(original);
|
|
32457
32769
|
const repository = options.repository ?? fm.workspace.repository ?? void 0;
|
|
32458
32770
|
const worktreePath = fm.workspace.worktreePath ?? void 0;
|
|
@@ -32551,14 +32863,14 @@ init_config2();
|
|
|
32551
32863
|
init_fs();
|
|
32552
32864
|
init_slug();
|
|
32553
32865
|
import { Command as Command12 } from "commander";
|
|
32554
|
-
import { resolve as
|
|
32555
|
-
import { readFile as
|
|
32866
|
+
import { resolve as resolve81 } from "path";
|
|
32867
|
+
import { readFile as readFile58, readdir as readdir30, rm as rm14 } from "fs/promises";
|
|
32556
32868
|
|
|
32557
32869
|
// src/utils/project-indexes.ts
|
|
32558
32870
|
init_parser();
|
|
32559
32871
|
init_fs();
|
|
32560
|
-
import { readdir as
|
|
32561
|
-
import { resolve as
|
|
32872
|
+
import { readdir as readdir29, readFile as readFile57 } from "fs/promises";
|
|
32873
|
+
import { resolve as resolve80 } from "path";
|
|
32562
32874
|
function nowIso3() {
|
|
32563
32875
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
32564
32876
|
}
|
|
@@ -32567,7 +32879,7 @@ function readProjectSlug(projectDir) {
|
|
|
32567
32879
|
}
|
|
32568
32880
|
async function listSlugFiles(dir) {
|
|
32569
32881
|
if (!await fileExists(dir)) return [];
|
|
32570
|
-
const entries = await
|
|
32882
|
+
const entries = await readdir29(dir, { withFileTypes: true });
|
|
32571
32883
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_")).map((e) => e.name).sort();
|
|
32572
32884
|
}
|
|
32573
32885
|
function escapeCell(value) {
|
|
@@ -32577,7 +32889,7 @@ function joinList(items) {
|
|
|
32577
32889
|
return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
|
|
32578
32890
|
}
|
|
32579
32891
|
async function rebuildResourcesIndex(projectDir) {
|
|
32580
|
-
const dir =
|
|
32892
|
+
const dir = resolve80(projectDir, "resources");
|
|
32581
32893
|
await ensureDir(dir);
|
|
32582
32894
|
const files = await listSlugFiles(dir);
|
|
32583
32895
|
const slug = readProjectSlug(projectDir);
|
|
@@ -32593,7 +32905,7 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
32593
32905
|
lines.push("| Name | Category | Source | Related Assignments | Updated |");
|
|
32594
32906
|
lines.push("|------|----------|--------|---------------------|---------|");
|
|
32595
32907
|
for (const fileName of files) {
|
|
32596
|
-
const content = await
|
|
32908
|
+
const content = await readFile57(resolve80(dir, fileName), "utf-8");
|
|
32597
32909
|
const parsed = parseResource(content);
|
|
32598
32910
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
32599
32911
|
const name = parsed.name || slugBase;
|
|
@@ -32603,12 +32915,12 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
32603
32915
|
);
|
|
32604
32916
|
}
|
|
32605
32917
|
lines.push("");
|
|
32606
|
-
const indexPath =
|
|
32918
|
+
const indexPath = resolve80(dir, "_index.md");
|
|
32607
32919
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
32608
32920
|
return { total: files.length, path: indexPath };
|
|
32609
32921
|
}
|
|
32610
32922
|
async function rebuildMemoriesIndex(projectDir) {
|
|
32611
|
-
const dir =
|
|
32923
|
+
const dir = resolve80(projectDir, "memories");
|
|
32612
32924
|
await ensureDir(dir);
|
|
32613
32925
|
const files = await listSlugFiles(dir);
|
|
32614
32926
|
const slug = readProjectSlug(projectDir);
|
|
@@ -32624,7 +32936,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
32624
32936
|
lines.push("| Name | Source | Scope | Source Assignment | Updated |");
|
|
32625
32937
|
lines.push("|------|--------|-------|-------------------|---------|");
|
|
32626
32938
|
for (const fileName of files) {
|
|
32627
|
-
const content = await
|
|
32939
|
+
const content = await readFile57(resolve80(dir, fileName), "utf-8");
|
|
32628
32940
|
const parsed = parseMemory(content);
|
|
32629
32941
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
32630
32942
|
const name = parsed.name || slugBase;
|
|
@@ -32634,7 +32946,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
32634
32946
|
);
|
|
32635
32947
|
}
|
|
32636
32948
|
lines.push("");
|
|
32637
|
-
const indexPath =
|
|
32949
|
+
const indexPath = resolve80(dir, "_index.md");
|
|
32638
32950
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
32639
32951
|
return { total: files.length, path: indexPath };
|
|
32640
32952
|
}
|
|
@@ -32698,8 +33010,8 @@ ${opts.body ?? "<!-- Add notes about this resource here. -->"}
|
|
|
32698
33010
|
}
|
|
32699
33011
|
async function resolveProjectDir(project) {
|
|
32700
33012
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
32701
|
-
const projectDir =
|
|
32702
|
-
if (!await fileExists(
|
|
33013
|
+
const projectDir = resolve81((await readConfig()).defaultProjectDir, project);
|
|
33014
|
+
if (!await fileExists(resolve81(projectDir, "project.md"))) {
|
|
32703
33015
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
32704
33016
|
}
|
|
32705
33017
|
return projectDir;
|
|
@@ -32712,7 +33024,7 @@ async function runResourceAdd(options) {
|
|
|
32712
33024
|
if (!isValidSlug(slug)) {
|
|
32713
33025
|
throw new Error(`Invalid resource slug: "${slug}".`);
|
|
32714
33026
|
}
|
|
32715
|
-
const filePath =
|
|
33027
|
+
const filePath = resolve81(projectDir, "resources", `${slug}.md`);
|
|
32716
33028
|
if (await fileExists(filePath) && !options.force) {
|
|
32717
33029
|
throw new Error(
|
|
32718
33030
|
`Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -32729,9 +33041,9 @@ async function runResourceAdd(options) {
|
|
|
32729
33041
|
return { filePath, indexPath, total };
|
|
32730
33042
|
}
|
|
32731
33043
|
async function listResourceSlugs(projectDir) {
|
|
32732
|
-
const dir =
|
|
33044
|
+
const dir = resolve81(projectDir, "resources");
|
|
32733
33045
|
if (!await fileExists(dir)) return [];
|
|
32734
|
-
const entries = await
|
|
33046
|
+
const entries = await readdir30(dir, { withFileTypes: true });
|
|
32735
33047
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
32736
33048
|
}
|
|
32737
33049
|
async function runResourceList(project) {
|
|
@@ -32739,16 +33051,16 @@ async function runResourceList(project) {
|
|
|
32739
33051
|
const slugs = await listResourceSlugs(projectDir);
|
|
32740
33052
|
const out = [];
|
|
32741
33053
|
for (const slug of slugs) {
|
|
32742
|
-
const parsed = parseResource(await
|
|
33054
|
+
const parsed = parseResource(await readFile58(resolve81(projectDir, "resources", `${slug}.md`), "utf-8"));
|
|
32743
33055
|
out.push({ slug, name: parsed.name, category: parsed.category, source: parsed.source, updated: parsed.updated });
|
|
32744
33056
|
}
|
|
32745
33057
|
return out;
|
|
32746
33058
|
}
|
|
32747
33059
|
async function runResourceShow(project, slug) {
|
|
32748
33060
|
const projectDir = await resolveProjectDir(project);
|
|
32749
|
-
const filePath =
|
|
33061
|
+
const filePath = resolve81(projectDir, "resources", `${slug}.md`);
|
|
32750
33062
|
if (!await fileExists(filePath)) throw new Error(`Resource "${slug}" not found in project "${project}".`);
|
|
32751
|
-
const parsed = parseResource(await
|
|
33063
|
+
const parsed = parseResource(await readFile58(filePath, "utf-8"));
|
|
32752
33064
|
return {
|
|
32753
33065
|
slug,
|
|
32754
33066
|
name: parsed.name,
|
|
@@ -32762,14 +33074,14 @@ async function runResourceShow(project, slug) {
|
|
|
32762
33074
|
}
|
|
32763
33075
|
async function runResourceUpdate(slug, options) {
|
|
32764
33076
|
const projectDir = await resolveProjectDir(options.project);
|
|
32765
|
-
const filePath =
|
|
33077
|
+
const filePath = resolve81(projectDir, "resources", `${slug}.md`);
|
|
32766
33078
|
if (!await fileExists(filePath)) {
|
|
32767
33079
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
32768
33080
|
}
|
|
32769
33081
|
if (options.name === void 0 && options.source === void 0 && options.category === void 0 && options.relatedAssignments === void 0) {
|
|
32770
33082
|
throw new Error("Provide at least one of --name, --source, --category, --related-assignments.");
|
|
32771
33083
|
}
|
|
32772
|
-
const original = await
|
|
33084
|
+
const original = await readFile58(filePath, "utf-8");
|
|
32773
33085
|
const content = editResourceFrontmatter(original, {
|
|
32774
33086
|
name: options.name,
|
|
32775
33087
|
category: options.category,
|
|
@@ -32782,7 +33094,7 @@ async function runResourceUpdate(slug, options) {
|
|
|
32782
33094
|
}
|
|
32783
33095
|
async function runResourceRemove(slug, options) {
|
|
32784
33096
|
const projectDir = await resolveProjectDir(options.project);
|
|
32785
|
-
const filePath =
|
|
33097
|
+
const filePath = resolve81(projectDir, "resources", `${slug}.md`);
|
|
32786
33098
|
if (!await fileExists(filePath)) {
|
|
32787
33099
|
throw new Error(`Resource "${slug}" not found in project "${options.project}".`);
|
|
32788
33100
|
}
|
|
@@ -32865,8 +33177,8 @@ init_config2();
|
|
|
32865
33177
|
init_fs();
|
|
32866
33178
|
init_slug();
|
|
32867
33179
|
import { Command as Command13 } from "commander";
|
|
32868
|
-
import { resolve as
|
|
32869
|
-
import { readFile as
|
|
33180
|
+
import { resolve as resolve82 } from "path";
|
|
33181
|
+
import { readFile as readFile59, readdir as readdir31, rm as rm15 } from "fs/promises";
|
|
32870
33182
|
init_parser();
|
|
32871
33183
|
function nowIso5() {
|
|
32872
33184
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -32930,8 +33242,8 @@ ${opts.body ?? "<!-- Capture the load-bearing context for this memory here. -->"
|
|
|
32930
33242
|
}
|
|
32931
33243
|
async function resolveProjectDir2(project) {
|
|
32932
33244
|
if (!isValidSlug(project)) throw new Error(`Invalid project slug: "${project}".`);
|
|
32933
|
-
const projectDir =
|
|
32934
|
-
if (!await fileExists(
|
|
33245
|
+
const projectDir = resolve82((await readConfig()).defaultProjectDir, project);
|
|
33246
|
+
if (!await fileExists(resolve82(projectDir, "project.md"))) {
|
|
32935
33247
|
throw new Error(`Project "${project}" not found at ${projectDir}.`);
|
|
32936
33248
|
}
|
|
32937
33249
|
return projectDir;
|
|
@@ -32944,7 +33256,7 @@ async function runMemoryAdd(options) {
|
|
|
32944
33256
|
if (!isValidSlug(slug)) {
|
|
32945
33257
|
throw new Error(`Invalid memory slug: "${slug}".`);
|
|
32946
33258
|
}
|
|
32947
|
-
const filePath =
|
|
33259
|
+
const filePath = resolve82(projectDir, "memories", `${slug}.md`);
|
|
32948
33260
|
if (await fileExists(filePath) && !options.force) {
|
|
32949
33261
|
throw new Error(
|
|
32950
33262
|
`Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -32962,9 +33274,9 @@ async function runMemoryAdd(options) {
|
|
|
32962
33274
|
return { filePath, indexPath, total };
|
|
32963
33275
|
}
|
|
32964
33276
|
async function listMemorySlugs(projectDir) {
|
|
32965
|
-
const dir =
|
|
33277
|
+
const dir = resolve82(projectDir, "memories");
|
|
32966
33278
|
if (!await fileExists(dir)) return [];
|
|
32967
|
-
const entries = await
|
|
33279
|
+
const entries = await readdir31(dir, { withFileTypes: true });
|
|
32968
33280
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "_index.md").map((e) => e.name.slice(0, -3)).sort();
|
|
32969
33281
|
}
|
|
32970
33282
|
async function runMemoryList(project) {
|
|
@@ -32972,16 +33284,16 @@ async function runMemoryList(project) {
|
|
|
32972
33284
|
const slugs = await listMemorySlugs(projectDir);
|
|
32973
33285
|
const out = [];
|
|
32974
33286
|
for (const slug of slugs) {
|
|
32975
|
-
const parsed = parseMemory(await
|
|
33287
|
+
const parsed = parseMemory(await readFile59(resolve82(projectDir, "memories", `${slug}.md`), "utf-8"));
|
|
32976
33288
|
out.push({ slug, name: parsed.name, scope: parsed.scope, source: parsed.source, updated: parsed.updated });
|
|
32977
33289
|
}
|
|
32978
33290
|
return out;
|
|
32979
33291
|
}
|
|
32980
33292
|
async function runMemoryShow(project, slug) {
|
|
32981
33293
|
const projectDir = await resolveProjectDir2(project);
|
|
32982
|
-
const filePath =
|
|
33294
|
+
const filePath = resolve82(projectDir, "memories", `${slug}.md`);
|
|
32983
33295
|
if (!await fileExists(filePath)) throw new Error(`Memory "${slug}" not found in project "${project}".`);
|
|
32984
|
-
const parsed = parseMemory(await
|
|
33296
|
+
const parsed = parseMemory(await readFile59(filePath, "utf-8"));
|
|
32985
33297
|
return {
|
|
32986
33298
|
slug,
|
|
32987
33299
|
name: parsed.name,
|
|
@@ -32996,7 +33308,7 @@ async function runMemoryShow(project, slug) {
|
|
|
32996
33308
|
}
|
|
32997
33309
|
async function runMemoryUpdate(slug, options) {
|
|
32998
33310
|
const projectDir = await resolveProjectDir2(options.project);
|
|
32999
|
-
const filePath =
|
|
33311
|
+
const filePath = resolve82(projectDir, "memories", `${slug}.md`);
|
|
33000
33312
|
if (!await fileExists(filePath)) {
|
|
33001
33313
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
33002
33314
|
}
|
|
@@ -33005,7 +33317,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
33005
33317
|
"Provide at least one of --name, --source, --scope, --source-assignment, --related-assignments."
|
|
33006
33318
|
);
|
|
33007
33319
|
}
|
|
33008
|
-
const original = await
|
|
33320
|
+
const original = await readFile59(filePath, "utf-8");
|
|
33009
33321
|
const content = editMemoryFrontmatter(original, {
|
|
33010
33322
|
name: options.name,
|
|
33011
33323
|
source: options.source,
|
|
@@ -33019,7 +33331,7 @@ async function runMemoryUpdate(slug, options) {
|
|
|
33019
33331
|
}
|
|
33020
33332
|
async function runMemoryRemove(slug, options) {
|
|
33021
33333
|
const projectDir = await resolveProjectDir2(options.project);
|
|
33022
|
-
const filePath =
|
|
33334
|
+
const filePath = resolve82(projectDir, "memories", `${slug}.md`);
|
|
33023
33335
|
if (!await fileExists(filePath)) {
|
|
33024
33336
|
throw new Error(`Memory "${slug}" not found in project "${options.project}".`);
|
|
33025
33337
|
}
|
|
@@ -33104,8 +33416,8 @@ init_paths();
|
|
|
33104
33416
|
init_fs();
|
|
33105
33417
|
init_frontmatter();
|
|
33106
33418
|
import { Command as Command14 } from "commander";
|
|
33107
|
-
import { readFile as
|
|
33108
|
-
import { resolve as
|
|
33419
|
+
import { readFile as readFile60 } from "fs/promises";
|
|
33420
|
+
import { resolve as resolve83 } from "path";
|
|
33109
33421
|
var AGE_PATTERN = /^(\d+)([dhwm])$/i;
|
|
33110
33422
|
function parseAgeToCutoff(age) {
|
|
33111
33423
|
const match = age.match(AGE_PATTERN);
|
|
@@ -33124,7 +33436,7 @@ function parseAgeToCutoff(age) {
|
|
|
33124
33436
|
}
|
|
33125
33437
|
function assignmentMdPath(item) {
|
|
33126
33438
|
if (item.projectSlug) {
|
|
33127
|
-
return
|
|
33439
|
+
return resolve83(
|
|
33128
33440
|
defaultProjectDir(),
|
|
33129
33441
|
item.projectSlug,
|
|
33130
33442
|
"assignments",
|
|
@@ -33132,13 +33444,13 @@ function assignmentMdPath(item) {
|
|
|
33132
33444
|
"assignment.md"
|
|
33133
33445
|
);
|
|
33134
33446
|
}
|
|
33135
|
-
return
|
|
33447
|
+
return resolve83(assignmentsDir(), item.id, "assignment.md");
|
|
33136
33448
|
}
|
|
33137
33449
|
async function loadTags(item) {
|
|
33138
33450
|
const path = assignmentMdPath(item);
|
|
33139
33451
|
if (!await fileExists(path)) return [];
|
|
33140
33452
|
try {
|
|
33141
|
-
const content = await
|
|
33453
|
+
const content = await readFile60(path, "utf-8");
|
|
33142
33454
|
return parseAssignmentFrontmatter(content).tags;
|
|
33143
33455
|
} catch {
|
|
33144
33456
|
return [];
|
|
@@ -33567,8 +33879,8 @@ init_fs();
|
|
|
33567
33879
|
init_timestamp();
|
|
33568
33880
|
init_frontmatter();
|
|
33569
33881
|
import { Command as Command16 } from "commander";
|
|
33570
|
-
import { readFile as
|
|
33571
|
-
import { resolve as
|
|
33882
|
+
import { readFile as readFile61 } from "fs/promises";
|
|
33883
|
+
import { resolve as resolve84 } from "path";
|
|
33572
33884
|
async function scanDirs() {
|
|
33573
33885
|
const config = await readConfig();
|
|
33574
33886
|
return { projectsDir: config.defaultProjectDir, standaloneDir: assignmentsDir() };
|
|
@@ -33578,12 +33890,12 @@ function fail3(error) {
|
|
|
33578
33890
|
process.exit(1);
|
|
33579
33891
|
}
|
|
33580
33892
|
function configPath() {
|
|
33581
|
-
return
|
|
33893
|
+
return resolve84(syntaurRoot(), "config.md");
|
|
33582
33894
|
}
|
|
33583
33895
|
async function readStatusBlock() {
|
|
33584
33896
|
const p = configPath();
|
|
33585
33897
|
if (!await fileExists(p)) return null;
|
|
33586
|
-
const content = await
|
|
33898
|
+
const content = await readFile61(p, "utf-8");
|
|
33587
33899
|
return parseStatusConfig(content);
|
|
33588
33900
|
}
|
|
33589
33901
|
async function requireStatusBlock() {
|
|
@@ -33832,7 +34144,7 @@ async function runStatusRename(id, opts) {
|
|
|
33832
34144
|
printBlockDiff(before, after);
|
|
33833
34145
|
const now2 = nowTimestamp();
|
|
33834
34146
|
for (const a of affected) {
|
|
33835
|
-
const original = await
|
|
34147
|
+
const original = await readFile61(a.path, "utf-8");
|
|
33836
34148
|
const rewritten = updateAssignmentFile(original, { status: newId, updated: now2 });
|
|
33837
34149
|
console.log(`
|
|
33838
34150
|
--- ${a.display}/assignment.md`);
|
|
@@ -33843,9 +34155,9 @@ async function runStatusRename(id, opts) {
|
|
|
33843
34155
|
}
|
|
33844
34156
|
const cfgPath = configPath();
|
|
33845
34157
|
const buffers = /* @__PURE__ */ new Map();
|
|
33846
|
-
buffers.set(cfgPath, await fileExists(cfgPath) ? await
|
|
34158
|
+
buffers.set(cfgPath, await fileExists(cfgPath) ? await readFile61(cfgPath, "utf-8") : "");
|
|
33847
34159
|
for (const a of affected) {
|
|
33848
|
-
buffers.set(a.path, await
|
|
34160
|
+
buffers.set(a.path, await readFile61(a.path, "utf-8"));
|
|
33849
34161
|
}
|
|
33850
34162
|
const now = nowTimestamp();
|
|
33851
34163
|
try {
|
|
@@ -34025,13 +34337,13 @@ init_config2();
|
|
|
34025
34337
|
init_timestamp();
|
|
34026
34338
|
init_frontmatter();
|
|
34027
34339
|
import { Command as Command17 } from "commander";
|
|
34028
|
-
import { readFile as
|
|
34029
|
-
import { resolve as
|
|
34340
|
+
import { readFile as readFile62 } from "fs/promises";
|
|
34341
|
+
import { resolve as resolve85 } from "path";
|
|
34030
34342
|
async function readContext3(cwd) {
|
|
34031
|
-
const path =
|
|
34343
|
+
const path = resolve85(cwd, ".syntaur", "context.json");
|
|
34032
34344
|
if (!await fileExists(path)) return null;
|
|
34033
34345
|
try {
|
|
34034
|
-
return JSON.parse(await
|
|
34346
|
+
return JSON.parse(await readFile62(path, "utf-8"));
|
|
34035
34347
|
} catch {
|
|
34036
34348
|
return null;
|
|
34037
34349
|
}
|
|
@@ -34040,12 +34352,12 @@ async function resolveAssignmentPath3(opts) {
|
|
|
34040
34352
|
if (opts.assignment) {
|
|
34041
34353
|
if (opts.project) {
|
|
34042
34354
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
34043
|
-
return
|
|
34355
|
+
return resolve85(projectsDir2, opts.project, "assignments", opts.assignment, "assignment.md");
|
|
34044
34356
|
}
|
|
34045
|
-
return
|
|
34357
|
+
return resolve85(assignmentsDir(), opts.assignment, "assignment.md");
|
|
34046
34358
|
}
|
|
34047
34359
|
const ctx = await readContext3(opts.cwd);
|
|
34048
|
-
if (ctx?.assignmentDir) return
|
|
34360
|
+
if (ctx?.assignmentDir) return resolve85(ctx.assignmentDir, "assignment.md");
|
|
34049
34361
|
throw new Error(
|
|
34050
34362
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
34051
34363
|
);
|
|
@@ -34082,7 +34394,7 @@ async function runWorkspaceSet(options, cwd = process.cwd()) {
|
|
|
34082
34394
|
${pre.errors.map((e) => ` - ${e}`).join("\n")}`
|
|
34083
34395
|
);
|
|
34084
34396
|
}
|
|
34085
|
-
const original = await
|
|
34397
|
+
const original = await readFile62(path, "utf-8");
|
|
34086
34398
|
let next = updateAssignmentWorkspace(original, partial);
|
|
34087
34399
|
next = updateAssignmentFile(next, { updated: nowTimestamp() });
|
|
34088
34400
|
await writeFileForce(path, next);
|
|
@@ -34119,13 +34431,13 @@ init_config2();
|
|
|
34119
34431
|
init_timestamp();
|
|
34120
34432
|
init_templates();
|
|
34121
34433
|
import { Command as Command18 } from "commander";
|
|
34122
|
-
import { readFile as
|
|
34123
|
-
import { resolve as
|
|
34434
|
+
import { readFile as readFile63 } from "fs/promises";
|
|
34435
|
+
import { resolve as resolve86 } from "path";
|
|
34124
34436
|
async function readContext4(cwd) {
|
|
34125
|
-
const path =
|
|
34437
|
+
const path = resolve86(cwd, ".syntaur", "context.json");
|
|
34126
34438
|
if (!await fileExists(path)) return null;
|
|
34127
34439
|
try {
|
|
34128
|
-
return JSON.parse(await
|
|
34440
|
+
return JSON.parse(await readFile63(path, "utf-8"));
|
|
34129
34441
|
} catch {
|
|
34130
34442
|
return null;
|
|
34131
34443
|
}
|
|
@@ -34135,11 +34447,11 @@ async function resolveAssignmentDir2(opts) {
|
|
|
34135
34447
|
if (opts.project) {
|
|
34136
34448
|
const projectsDir2 = (await readConfig()).defaultProjectDir;
|
|
34137
34449
|
return {
|
|
34138
|
-
dir:
|
|
34450
|
+
dir: resolve86(projectsDir2, opts.project, "assignments", opts.assignment),
|
|
34139
34451
|
slug: opts.assignment
|
|
34140
34452
|
};
|
|
34141
34453
|
}
|
|
34142
|
-
return { dir:
|
|
34454
|
+
return { dir: resolve86(assignmentsDir(), opts.assignment), slug: opts.assignment };
|
|
34143
34455
|
}
|
|
34144
34456
|
const ctx = await readContext4(opts.cwd);
|
|
34145
34457
|
if (ctx?.assignmentDir) {
|
|
@@ -34201,12 +34513,12 @@ async function runProgressLog(text, options, cwd = process.cwd()) {
|
|
|
34201
34513
|
project: options.project,
|
|
34202
34514
|
cwd
|
|
34203
34515
|
});
|
|
34204
|
-
if (!await fileExists(
|
|
34516
|
+
if (!await fileExists(resolve86(dir, "assignment.md"))) {
|
|
34205
34517
|
throw new Error(`No assignment found at ${dir} (missing assignment.md).`);
|
|
34206
34518
|
}
|
|
34207
|
-
const path =
|
|
34519
|
+
const path = resolve86(dir, "progress.md");
|
|
34208
34520
|
const now = nowTimestamp();
|
|
34209
|
-
const content = await fileExists(path) ? await
|
|
34521
|
+
const content = await fileExists(path) ? await readFile63(path, "utf-8") : renderProgress({ assignment: slug, timestamp: now });
|
|
34210
34522
|
const next = appendProgressEntry(content, text, now);
|
|
34211
34523
|
await writeFileForce(path, next);
|
|
34212
34524
|
return path;
|
|
@@ -34226,10 +34538,10 @@ progressCommand.command("log").description("Append a timestamped entry to the as
|
|
|
34226
34538
|
|
|
34227
34539
|
// src/cli-default-command.ts
|
|
34228
34540
|
init_config2();
|
|
34229
|
-
import { readdir as
|
|
34541
|
+
import { readdir as readdir32 } from "fs/promises";
|
|
34230
34542
|
async function hasAnyProjectContent(projectsDir2) {
|
|
34231
34543
|
try {
|
|
34232
|
-
const entries = await
|
|
34544
|
+
const entries = await readdir32(projectsDir2, { withFileTypes: true });
|
|
34233
34545
|
return entries.some((entry) => entry.isDirectory());
|
|
34234
34546
|
} catch {
|
|
34235
34547
|
return false;
|
|
@@ -34531,7 +34843,7 @@ program.command("reopen").description("Reopen a completed or failed assignment")
|
|
|
34531
34843
|
process.exit(1);
|
|
34532
34844
|
}
|
|
34533
34845
|
});
|
|
34534
|
-
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
|
|
34846
|
+
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. Built-in ids: pi, hermes, openclaw, cursor, opencode (plus any user descriptors in ~/.syntaur/targets/). 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) => {
|
|
34535
34847
|
try {
|
|
34536
34848
|
await setupCommand(options);
|
|
34537
34849
|
} catch (error) {
|
|
@@ -34640,7 +34952,7 @@ program.command("uninstall").description("Remove Syntaur integrations and option
|
|
|
34640
34952
|
process.exit(1);
|
|
34641
34953
|
}
|
|
34642
34954
|
});
|
|
34643
|
-
program.command("setup-adapter").description("Generate adapter instruction files for a framework in the current directory").argument("<framework>", "Target framework
|
|
34955
|
+
program.command("setup-adapter").description("Generate adapter instruction files for a framework in the current directory").argument("<framework>", "Target framework: built-in ids cursor, codex, opencode, pi, openclaw, hermes (plus any user descriptor with an instructions adapter in ~/.syntaur/targets/)").option("--project <slug>", "Target project slug (required)").option("--assignment <slug>", "Target assignment slug (required)").option("--force", "Overwrite existing adapter files").option("--dir <path>", "Override default project directory").action(async (framework, options) => {
|
|
34644
34956
|
try {
|
|
34645
34957
|
await setupAdapterCommand(framework, options);
|
|
34646
34958
|
} catch (error) {
|