syntaur 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +23 -0
- package/README.md +64 -24
- package/dashboard/dist/assets/{_basePickBy-DTYUlCEg.js → _basePickBy-CIZortGD.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-C0Y4HRd5.js → _baseUniq-BHP5NBQ7.js} +1 -1
- package/dashboard/dist/assets/{arc-BFx2eqN9.js → arc-BLtQy7Cl.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-Erol1JD6.js → architectureDiagram-2XIMDMQ5-0mhIBjWy.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-kSkh6VkS.js → blockDiagram-WCTKOSBZ-CM7K5Tf5.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-C04oKzvX.js → c4Diagram-IC4MRINW-gsbeygvS.js} +1 -1
- package/dashboard/dist/assets/channel-FWLgstBg.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-C3t0tXt-.js → chunk-4BX2VUAB-CneRz9Nd.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-2cnyEL0b.js → chunk-55IACEB6-uetANWHd.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-DIY9MTNi.js → chunk-FMBD7UC4-3-tNszoF.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-Cw8fpqpE.js → chunk-JSJVCQXG-DOO88rZD.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-BAhd66XV.js → chunk-KX2RTZJC-Bnc1CFy2.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-RzXwoxk3.js → chunk-NQ4KR5QH-BfViC1Ms.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-Dgri4sGz.js → chunk-QZHKN3VN-CgjyAt2l.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-DYLj9JRa.js → chunk-WL4C6EOR-BYFermSR.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-BAvQTSn_.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BAvQTSn_.js +1 -0
- package/dashboard/dist/assets/clone-rSEllQ1P.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-DfY_Fnfu.js → cose-bilkent-S5V4N54A-CE-k5SLv.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-CyTKIVSK.js → dagre-KLK3FWXG-CX7Br8LQ.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-Krub7Xxo.js → diagram-E7M64L7V-t-xhWjCU.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-giUl9uHz.js → diagram-IFDJBPK2-BVhcTXsJ.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-oAtnO3C9.js → diagram-P4PSJMXO-fxt8ZSca.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-eYaVjXqo.js → erDiagram-INFDFZHY-C20k2bxX.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-or5S0_Sb.js → flowDiagram-PKNHOUZH-CdeygexM.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-C9R1lsme.js → ganttDiagram-A5KZAMGK-CPsoOAV-.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-BQwsDzvp.js → gitGraphDiagram-K3NZZRJ6-t0tCTIZy.js} +1 -1
- package/dashboard/dist/assets/{graph-EQOX1wg8.js → graph-Bcqd31wL.js} +1 -1
- package/dashboard/dist/assets/{index-u80fISp0.css → index-bLWoSCLL.css} +1 -1
- package/dashboard/dist/assets/{index-Cy7yjuqO.js → index-eS0wtDO0.js} +87 -87
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-BjLlQWxk.js → infoDiagram-LFFYTUFH-DgoKvH3N.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BNjydh4j.js → ishikawaDiagram-PHBUUO56-CGr4P8do.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DNzE7TgQ.js → journeyDiagram-4ABVD52K-CHYRi6wF.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-FbNvGx6i.js → kanban-definition-K7BYSVSG-kwzqrFcW.js} +1 -1
- package/dashboard/dist/assets/{layout-B2yZvlWs.js → layout-wud47qGs.js} +1 -1
- package/dashboard/dist/assets/{linear-p68yY_14.js → linear-DG5js90X.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-D558akcW.js → mermaid.core-BDw69uC2.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-CZPgesSK.js → mindmap-definition-YRQLILUH-Dw_uyg1-.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CdXMWspp.js → pieDiagram-SKSYHLDU-C7NBi6tK.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-D7tq22ZY.js → quadrantDiagram-337W2JSQ-_N_BSBd4.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-ByZxUSmd.js → requirementDiagram-Z7DCOOCP-BqUiqfP1.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CZon9rRY.js → sankeyDiagram-WA2Y5GQK-C2Qq5ULW.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-nbELB6rb.js → sequenceDiagram-2WXFIKYE-BOCF2z8f.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-D_OPKr5B.js → stateDiagram-RAJIS63D-BvDyTxC5.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-TaLLhLWD.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-Cxuvk1D2.js → timeline-definition-YZTLITO2-DNNZxSrG.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-C0CRpL92.js → treemap-KZPCXAKY-C6hOdiGH.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-DijCj6M3.js → vennDiagram-LZ73GAT5-FmXl6puP.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-CdpE0oRi.js → xychartDiagram-JWTSCODW-Bjav12WG.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +12 -14
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +644 -268
- package/dist/index.js.map +1 -1
- package/package.json +6 -8
- package/platforms/claude-code/.claude-plugin/plugin.json +15 -2
- package/{vendor/syntaur-skills → platforms/claude-code}/skills/complete-assignment/SKILL.md +0 -2
- package/{vendor/syntaur-skills → platforms/claude-code}/skills/grab-assignment/SKILL.md +2 -7
- package/{vendor/syntaur-skills → platforms/claude-code}/skills/plan-assignment/SKILL.md +1 -3
- package/{vendor/syntaur-skills → platforms/claude-code}/skills/syntaur-protocol/SKILL.md +4 -23
- package/{vendor/syntaur-skills → platforms/claude-code}/skills/syntaur-protocol/references/file-ownership.md +1 -2
- package/{vendor/syntaur-skills → platforms/claude-code}/skills/syntaur-protocol/references/protocol-summary.md +1 -6
- package/platforms/claude-code/skills/track-server/SKILL.md +49 -0
- package/platforms/codex/.codex-plugin/plugin.json +2 -2
- package/platforms/codex/skills/clear-assignment/SKILL.md +111 -0
- package/platforms/codex/skills/complete-assignment/SKILL.md +146 -0
- package/platforms/codex/skills/create-assignment/SKILL.md +73 -0
- package/platforms/codex/skills/create-project/SKILL.md +56 -0
- package/platforms/codex/skills/grab-assignment/SKILL.md +158 -0
- package/platforms/codex/skills/manage-statuses/SKILL.md +72 -0
- package/platforms/codex/skills/plan-assignment/SKILL.md +137 -0
- package/platforms/codex/skills/save-session-summary/SKILL.md +113 -0
- package/platforms/codex/skills/syntaur-protocol/SKILL.md +119 -0
- package/platforms/codex/skills/syntaur-protocol/references/file-ownership.md +67 -0
- package/platforms/codex/skills/syntaur-protocol/references/protocol-summary.md +82 -0
- package/platforms/codex/skills/track-server/SKILL.md +49 -0
- package/platforms/codex/skills/track-session/SKILL.md +63 -26
- package/skills/clear-assignment/SKILL.md +111 -0
- package/skills/complete-assignment/SKILL.md +146 -0
- package/skills/create-assignment/SKILL.md +73 -0
- package/skills/create-project/SKILL.md +56 -0
- package/skills/grab-assignment/SKILL.md +158 -0
- package/skills/manage-statuses/SKILL.md +72 -0
- package/skills/plan-assignment/SKILL.md +137 -0
- package/skills/save-session-summary/SKILL.md +113 -0
- package/skills/syntaur-protocol/SKILL.md +119 -0
- package/skills/syntaur-protocol/references/file-ownership.md +67 -0
- package/skills/syntaur-protocol/references/protocol-summary.md +82 -0
- package/skills/track-server/SKILL.md +49 -0
- package/skills/track-session/SKILL.md +86 -0
- package/dashboard/dist/assets/channel-C82tBKZ7.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-STOZ51tg.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-STOZ51tg.js +0 -1
- package/dashboard/dist/assets/clone-TzhWk-Bj.js +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-4pLM5B3m.js +0 -1
- package/scripts/postinstall-submodules.mjs +0 -40
- package/vendor/syntaur-skills/LICENSE +0 -21
- package/vendor/syntaur-skills/README.md +0 -57
- /package/{vendor/syntaur-skills → platforms/claude-code}/skills/clear-assignment/SKILL.md +0 -0
- /package/{vendor/syntaur-skills → platforms/claude-code}/skills/create-assignment/SKILL.md +0 -0
- /package/{vendor/syntaur-skills → platforms/claude-code}/skills/create-project/SKILL.md +0 -0
- /package/{vendor/syntaur-skills → platforms/claude-code}/skills/manage-statuses/SKILL.md +0 -0
- /package/{vendor/syntaur-skills → platforms/claude-code}/skills/save-session-summary/SKILL.md +0 -0
package/dist/index.js
CHANGED
|
@@ -148,7 +148,7 @@ function extractFrontmatter(fileContent) {
|
|
|
148
148
|
}
|
|
149
149
|
function parseSimpleValue(raw) {
|
|
150
150
|
const trimmed = raw.trim();
|
|
151
|
-
if (trimmed === "null" || trimmed === "") return null;
|
|
151
|
+
if (trimmed === "null" || trimmed === "~" || trimmed === "") return null;
|
|
152
152
|
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
153
153
|
return trimmed.slice(1, -1);
|
|
154
154
|
}
|
|
@@ -199,6 +199,7 @@ function parseProject(fileContent) {
|
|
|
199
199
|
updated: getField(fm, "updated") ?? "",
|
|
200
200
|
tags: parseListField(fm, "tags"),
|
|
201
201
|
workspace: getField(fm, "workspace"),
|
|
202
|
+
externalIds: parseExternalIds(fm),
|
|
202
203
|
body
|
|
203
204
|
};
|
|
204
205
|
}
|
|
@@ -243,16 +244,14 @@ function parseExternalIds(frontmatter) {
|
|
|
243
244
|
const colonIdx = line.indexOf(":");
|
|
244
245
|
if (colonIdx < 0) continue;
|
|
245
246
|
const key = line.slice(0, colonIdx).trim().replace(/^-\s+/, "");
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
entry[key] = value;
|
|
249
|
-
}
|
|
247
|
+
if (!key) continue;
|
|
248
|
+
entry[key] = parseSimpleValue(line.slice(colonIdx + 1));
|
|
250
249
|
}
|
|
251
|
-
if (entry["system"] && entry["id"]
|
|
250
|
+
if (entry["system"] && entry["id"]) {
|
|
252
251
|
results.push({
|
|
253
252
|
system: entry["system"],
|
|
254
253
|
id: entry["id"],
|
|
255
|
-
url: entry["url"]
|
|
254
|
+
url: entry["url"] || null
|
|
256
255
|
});
|
|
257
256
|
}
|
|
258
257
|
}
|
|
@@ -3021,7 +3020,7 @@ function extractFrontmatter2(fileContent) {
|
|
|
3021
3020
|
}
|
|
3022
3021
|
function parseSimpleValue2(raw) {
|
|
3023
3022
|
const trimmed = raw.trim();
|
|
3024
|
-
if (trimmed === "null") return null;
|
|
3023
|
+
if (trimmed === "null" || trimmed === "~" || trimmed === "") return null;
|
|
3025
3024
|
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
3026
3025
|
return trimmed.slice(1, -1);
|
|
3027
3026
|
}
|
|
@@ -3069,16 +3068,14 @@ function parseExternalIds2(frontmatter) {
|
|
|
3069
3068
|
const colonIdx = line.indexOf(":");
|
|
3070
3069
|
if (colonIdx < 0) continue;
|
|
3071
3070
|
const key = line.slice(0, colonIdx).trim().replace(/^-\s+/, "");
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
entry[key] = value;
|
|
3075
|
-
}
|
|
3071
|
+
if (!key) continue;
|
|
3072
|
+
entry[key] = parseSimpleValue2(line.slice(colonIdx + 1));
|
|
3076
3073
|
}
|
|
3077
|
-
if (entry["system"] && entry["id"]
|
|
3074
|
+
if (entry["system"] && entry["id"]) {
|
|
3078
3075
|
results.push({
|
|
3079
3076
|
system: entry["system"],
|
|
3080
3077
|
id: entry["id"],
|
|
3081
|
-
url: entry["url"]
|
|
3078
|
+
url: entry["url"] || null
|
|
3082
3079
|
});
|
|
3083
3080
|
}
|
|
3084
3081
|
}
|
|
@@ -4822,6 +4819,7 @@ async function getProjectDetail(projectsDir2, slug) {
|
|
|
4822
4819
|
created: project.created,
|
|
4823
4820
|
updated,
|
|
4824
4821
|
tags: project.tags,
|
|
4822
|
+
externalIds: project.externalIds,
|
|
4825
4823
|
body: project.body,
|
|
4826
4824
|
progress: rollup.progress,
|
|
4827
4825
|
needsAttention: rollup.needsAttention,
|
|
@@ -6708,7 +6706,7 @@ __export(launch_exports, {
|
|
|
6708
6706
|
});
|
|
6709
6707
|
import { spawn as spawn2 } from "child_process";
|
|
6710
6708
|
import { mkdir as mkdir6, writeFile as writeFile9 } from "fs/promises";
|
|
6711
|
-
import { isAbsolute as isAbsolute3, resolve as
|
|
6709
|
+
import { isAbsolute as isAbsolute3, resolve as resolve33 } from "path";
|
|
6712
6710
|
function formatFallbackCwdWarning(opts) {
|
|
6713
6711
|
const missing = [];
|
|
6714
6712
|
if (!opts.worktreePath) missing.push("worktreePath");
|
|
@@ -6752,8 +6750,8 @@ async function launchAgent(options) {
|
|
|
6752
6750
|
console.error(`Assignment not found: ${projectSlug}/${assignmentSlug}`);
|
|
6753
6751
|
process.exit(1);
|
|
6754
6752
|
}
|
|
6755
|
-
const projectDir =
|
|
6756
|
-
const assignmentDir =
|
|
6753
|
+
const projectDir = resolve33(projectsDir2, projectSlug);
|
|
6754
|
+
const assignmentDir = resolve33(projectDir, "assignments", assignmentSlug);
|
|
6757
6755
|
const resolvedFromWorkspace = cwdOverride ?? detail.workspace.worktreePath ?? (detail.workspace.repository?.startsWith("/") ? detail.workspace.repository : null);
|
|
6758
6756
|
const workspaceDir = resolvedFromWorkspace ?? process.cwd();
|
|
6759
6757
|
if (!cwdOverride) {
|
|
@@ -6765,7 +6763,7 @@ async function launchAgent(options) {
|
|
|
6765
6763
|
});
|
|
6766
6764
|
if (warning) console.warn(warning);
|
|
6767
6765
|
}
|
|
6768
|
-
const contextDir =
|
|
6766
|
+
const contextDir = resolve33(workspaceDir, ".syntaur");
|
|
6769
6767
|
await mkdir6(contextDir, { recursive: true });
|
|
6770
6768
|
const context = {
|
|
6771
6769
|
projectSlug,
|
|
@@ -6778,7 +6776,7 @@ async function launchAgent(options) {
|
|
|
6778
6776
|
grabbedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6779
6777
|
};
|
|
6780
6778
|
await writeFile9(
|
|
6781
|
-
|
|
6779
|
+
resolve33(contextDir, "context.json"),
|
|
6782
6780
|
JSON.stringify(context, null, 2) + "\n"
|
|
6783
6781
|
);
|
|
6784
6782
|
const { argv, shellFallbackWarning } = buildAgentArgv(
|
|
@@ -6837,7 +6835,7 @@ __export(git_worktree_exports, {
|
|
|
6837
6835
|
removeWorktree: () => removeWorktree
|
|
6838
6836
|
});
|
|
6839
6837
|
import { spawn as spawn3 } from "child_process";
|
|
6840
|
-
import { readFile as
|
|
6838
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
6841
6839
|
function run(command, args, cwd) {
|
|
6842
6840
|
return new Promise((resolvePromise) => {
|
|
6843
6841
|
const child = spawn3(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -6881,7 +6879,7 @@ async function createWorktreeAndRecord(opts) {
|
|
|
6881
6879
|
const { assignmentPath, repository, branch, worktreePath, parentBranch } = opts;
|
|
6882
6880
|
await createWorktree({ repository, branch, worktreePath, parentBranch });
|
|
6883
6881
|
try {
|
|
6884
|
-
const content = await
|
|
6882
|
+
const content = await readFile21(assignmentPath, "utf-8");
|
|
6885
6883
|
const updated = updateAssignmentWorkspace(content, {
|
|
6886
6884
|
repository,
|
|
6887
6885
|
worktreePath,
|
|
@@ -7265,8 +7263,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
7265
7263
|
return allSessions.length;
|
|
7266
7264
|
}
|
|
7267
7265
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
7268
|
-
const { readFile:
|
|
7269
|
-
const raw = await
|
|
7266
|
+
const { readFile: readFile35 } = await import("fs/promises");
|
|
7267
|
+
const raw = await readFile35(filePath, "utf-8");
|
|
7270
7268
|
const sessions = [];
|
|
7271
7269
|
const lines = raw.split("\n");
|
|
7272
7270
|
let inTable = false;
|
|
@@ -9738,8 +9736,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
9738
9736
|
router.post("/:workspace/archive", async (req, res) => {
|
|
9739
9737
|
try {
|
|
9740
9738
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
9741
|
-
const { resolve:
|
|
9742
|
-
const { readFile:
|
|
9739
|
+
const { resolve: resolve50 } = await import("path");
|
|
9740
|
+
const { readFile: readFile35 } = await import("fs/promises");
|
|
9743
9741
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
9744
9742
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
9745
9743
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
@@ -9755,10 +9753,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
9755
9753
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
9756
9754
|
);
|
|
9757
9755
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
9758
|
-
await ensureDir(
|
|
9756
|
+
await ensureDir(resolve50(todosDir2, "archive"));
|
|
9759
9757
|
let archContent = "";
|
|
9760
9758
|
if (await fileExists(archFile)) {
|
|
9761
|
-
archContent = await
|
|
9759
|
+
archContent = await readFile35(archFile, "utf-8");
|
|
9762
9760
|
archContent = archContent.trimEnd() + "\n\n";
|
|
9763
9761
|
} else {
|
|
9764
9762
|
archContent = `---
|
|
@@ -10017,7 +10015,7 @@ workspace: ${workspace}
|
|
|
10017
10015
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
10018
10016
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
10019
10017
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
10020
|
-
const { readFile:
|
|
10018
|
+
const { readFile: readFile35 } = await import("fs/promises");
|
|
10021
10019
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
10022
10020
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
10023
10021
|
let assignmentRef;
|
|
@@ -10055,7 +10053,7 @@ workspace: ${workspace}
|
|
|
10055
10053
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
10056
10054
|
}
|
|
10057
10055
|
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
10058
|
-
let content = await
|
|
10056
|
+
let content = await readFile35(assignmentMdPath, "utf-8");
|
|
10059
10057
|
content = appendTodosToAssignmentBody2(
|
|
10060
10058
|
content,
|
|
10061
10059
|
items.map((it) => ({
|
|
@@ -12449,6 +12447,7 @@ import {
|
|
|
12449
12447
|
lstat,
|
|
12450
12448
|
readFile as readFile16,
|
|
12451
12449
|
readlink,
|
|
12450
|
+
rename as rename6,
|
|
12452
12451
|
rm as rm3,
|
|
12453
12452
|
unlink as unlink5,
|
|
12454
12453
|
writeFile as writeFile6
|
|
@@ -12655,8 +12654,29 @@ async function readClaudeMarketplaceFile(manifestPath) {
|
|
|
12655
12654
|
}
|
|
12656
12655
|
async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
12657
12656
|
await ensureDir(dirname8(manifestPath));
|
|
12658
|
-
|
|
12657
|
+
if (Array.isArray(marketplace.plugins)) {
|
|
12658
|
+
marketplace.plugins = [...marketplace.plugins].sort((a, b) => {
|
|
12659
|
+
const an = a?.name ?? "";
|
|
12660
|
+
const bn = b?.name ?? "";
|
|
12661
|
+
return an.localeCompare(bn);
|
|
12662
|
+
});
|
|
12663
|
+
}
|
|
12664
|
+
if (await fileExists(manifestPath)) {
|
|
12665
|
+
try {
|
|
12666
|
+
const prev = await readFile16(manifestPath, "utf-8");
|
|
12667
|
+
JSON.parse(prev);
|
|
12668
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
12669
|
+
await writeFile6(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
12670
|
+
} catch {
|
|
12671
|
+
throw new Error(
|
|
12672
|
+
`Refusing to overwrite ${manifestPath}: existing file is not valid JSON. Inspect and remove or repair it manually before re-running.`
|
|
12673
|
+
);
|
|
12674
|
+
}
|
|
12675
|
+
}
|
|
12676
|
+
const tmpPath = `${manifestPath}.tmp`;
|
|
12677
|
+
await writeFile6(tmpPath, `${JSON.stringify(marketplace, null, 2)}
|
|
12659
12678
|
`, "utf-8");
|
|
12679
|
+
await rename6(tmpPath, manifestPath);
|
|
12660
12680
|
}
|
|
12661
12681
|
function buildClaudeMarketplaceSourcePath(pluginTargetDir, marketplaceRootDir) {
|
|
12662
12682
|
const relPath = relative2(marketplaceRootDir, pluginTargetDir).replaceAll("\\", "/");
|
|
@@ -12789,11 +12809,25 @@ async function getPreferredClaudeMarketplace() {
|
|
|
12789
12809
|
}
|
|
12790
12810
|
async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
12791
12811
|
const manifestPath = getClaudeKnownMarketplacesPath();
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12812
|
+
let existing = {};
|
|
12813
|
+
if (await fileExists(manifestPath)) {
|
|
12814
|
+
const raw = await readFile16(manifestPath, "utf-8");
|
|
12815
|
+
try {
|
|
12816
|
+
const parsed = JSON.parse(raw);
|
|
12817
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
12818
|
+
existing = parsed;
|
|
12819
|
+
} else {
|
|
12820
|
+
throw new Error("not a JSON object");
|
|
12821
|
+
}
|
|
12822
|
+
} catch (err2) {
|
|
12823
|
+
throw new Error(
|
|
12824
|
+
`Refusing to update ${manifestPath}: existing file is not a valid JSON object (${err2 instanceof Error ? err2.message : String(err2)}). Inspect and repair (or delete) it before re-running install-plugin.`
|
|
12825
|
+
);
|
|
12826
|
+
}
|
|
12827
|
+
}
|
|
12828
|
+
const had = Object.prototype.hasOwnProperty.call(existing, name);
|
|
12829
|
+
if (had && existing[name]?.installLocation === rootDir) {
|
|
12830
|
+
return { added: false, updated: false };
|
|
12797
12831
|
}
|
|
12798
12832
|
existing[name] = {
|
|
12799
12833
|
...existing[name] ?? {},
|
|
@@ -12803,8 +12837,52 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
12803
12837
|
existing[name].lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
12804
12838
|
existing[name].autoUpdate = true;
|
|
12805
12839
|
await ensureDir(dirname8(manifestPath));
|
|
12806
|
-
await
|
|
12840
|
+
if (await fileExists(manifestPath)) {
|
|
12841
|
+
const prev = await readFile16(manifestPath, "utf-8");
|
|
12842
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
12843
|
+
await writeFile6(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
12844
|
+
}
|
|
12845
|
+
const tmpPath = `${manifestPath}.tmp`;
|
|
12846
|
+
await writeFile6(tmpPath, `${JSON.stringify(existing, null, 2)}
|
|
12807
12847
|
`, "utf-8");
|
|
12848
|
+
await rename6(tmpPath, manifestPath);
|
|
12849
|
+
return { added: !had, updated: had };
|
|
12850
|
+
}
|
|
12851
|
+
async function ensureKnownClaudeMarketplaceForRoot(options) {
|
|
12852
|
+
return registerKnownClaudeMarketplace(options.name, options.rootDir);
|
|
12853
|
+
}
|
|
12854
|
+
async function setSyntaurPluginEnabled(options) {
|
|
12855
|
+
const settingsPath = resolve25(homedir2(), ".claude", "settings.json");
|
|
12856
|
+
const key = `syntaur@${options.marketplaceName}`;
|
|
12857
|
+
let parsed = {};
|
|
12858
|
+
if (await fileExists(settingsPath)) {
|
|
12859
|
+
const raw = await readFile16(settingsPath, "utf-8");
|
|
12860
|
+
try {
|
|
12861
|
+
parsed = JSON.parse(raw);
|
|
12862
|
+
} catch {
|
|
12863
|
+
throw new Error(
|
|
12864
|
+
`Cannot toggle plugin: ${settingsPath} is not valid JSON. Inspect and repair it before retrying.`
|
|
12865
|
+
);
|
|
12866
|
+
}
|
|
12867
|
+
}
|
|
12868
|
+
const enabledPlugins = parsed.enabledPlugins ?? {};
|
|
12869
|
+
const previous = typeof enabledPlugins[key] === "boolean" ? enabledPlugins[key] : void 0;
|
|
12870
|
+
if (previous === options.enabled) {
|
|
12871
|
+
return { key, previous, current: options.enabled, changed: false };
|
|
12872
|
+
}
|
|
12873
|
+
enabledPlugins[key] = options.enabled;
|
|
12874
|
+
parsed.enabledPlugins = enabledPlugins;
|
|
12875
|
+
await ensureDir(dirname8(settingsPath));
|
|
12876
|
+
if (await fileExists(settingsPath)) {
|
|
12877
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
12878
|
+
const prev = await readFile16(settingsPath, "utf-8");
|
|
12879
|
+
await writeFile6(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
|
|
12880
|
+
}
|
|
12881
|
+
const tmpPath = `${settingsPath}.tmp`;
|
|
12882
|
+
await writeFile6(tmpPath, `${JSON.stringify(parsed, null, 2)}
|
|
12883
|
+
`, "utf-8");
|
|
12884
|
+
await rename6(tmpPath, settingsPath);
|
|
12885
|
+
return { key, previous, current: options.enabled, changed: true };
|
|
12808
12886
|
}
|
|
12809
12887
|
async function ensureClaudeUserMarketplace() {
|
|
12810
12888
|
const existing = await getPreferredClaudeMarketplace();
|
|
@@ -13271,31 +13349,63 @@ async function textPrompt(question, defaultValue) {
|
|
|
13271
13349
|
|
|
13272
13350
|
// src/utils/install-skills.ts
|
|
13273
13351
|
init_fs();
|
|
13274
|
-
import { readFile as
|
|
13275
|
-
import {
|
|
13276
|
-
import {
|
|
13352
|
+
import { readFile as readFile18, readdir as readdir11, mkdir as mkdir4, copyFile, rm as rm4, lstat as lstat2 } from "fs/promises";
|
|
13353
|
+
import { resolve as resolve27, relative as relative3, join as join3 } from "path";
|
|
13354
|
+
import { homedir as homedir4 } from "os";
|
|
13355
|
+
|
|
13356
|
+
// src/utils/plugin-state.ts
|
|
13357
|
+
init_fs();
|
|
13358
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
13359
|
+
import { resolve as resolve26 } from "path";
|
|
13277
13360
|
import { homedir as homedir3 } from "os";
|
|
13278
|
-
|
|
13361
|
+
function settingsPathFor(agent) {
|
|
13362
|
+
if (agent === "claude") return resolve26(homedir3(), ".claude", "settings.json");
|
|
13363
|
+
return null;
|
|
13364
|
+
}
|
|
13365
|
+
async function readJsonOrNull(path) {
|
|
13366
|
+
if (!path) return null;
|
|
13367
|
+
if (!await fileExists(path)) return null;
|
|
13368
|
+
try {
|
|
13369
|
+
const raw = await readFile17(path, "utf-8");
|
|
13370
|
+
return JSON.parse(raw);
|
|
13371
|
+
} catch {
|
|
13372
|
+
return null;
|
|
13373
|
+
}
|
|
13374
|
+
}
|
|
13375
|
+
async function isSyntaurPluginEnabledFor(agent) {
|
|
13376
|
+
const settings = await readJsonOrNull(settingsPathFor(agent));
|
|
13377
|
+
const enabled = settings?.enabledPlugins ?? {};
|
|
13378
|
+
for (const [key, value] of Object.entries(enabled)) {
|
|
13379
|
+
if (value !== true) continue;
|
|
13380
|
+
const atIndex = key.lastIndexOf("@");
|
|
13381
|
+
const pluginName = atIndex > 0 ? key.slice(0, atIndex) : key;
|
|
13382
|
+
if (pluginName === "syntaur") return true;
|
|
13383
|
+
}
|
|
13384
|
+
return false;
|
|
13385
|
+
}
|
|
13386
|
+
|
|
13387
|
+
// src/utils/install-skills.ts
|
|
13388
|
+
var KNOWN_SKILL_NAMES = [
|
|
13279
13389
|
"syntaur-protocol",
|
|
13280
13390
|
"grab-assignment",
|
|
13281
13391
|
"plan-assignment",
|
|
13282
13392
|
"complete-assignment",
|
|
13283
13393
|
"create-assignment",
|
|
13284
13394
|
"create-project",
|
|
13285
|
-
"
|
|
13395
|
+
"manage-statuses",
|
|
13396
|
+
"clear-assignment",
|
|
13397
|
+
"save-session-summary",
|
|
13398
|
+
"track-session",
|
|
13399
|
+
"track-server"
|
|
13286
13400
|
];
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
function getPlatformSkillsDir(target) {
|
|
13292
|
-
const here = dirname9(fileURLToPath4(import.meta.url));
|
|
13293
|
-
const kind = target === "claude" ? "claude-code" : "codex";
|
|
13294
|
-
return resolve26(here, "..", "platforms", kind, "skills");
|
|
13401
|
+
var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
|
|
13402
|
+
async function getSkillsDir() {
|
|
13403
|
+
const packageRoot = await findPackageRoot("skills");
|
|
13404
|
+
return resolve27(packageRoot, "skills");
|
|
13295
13405
|
}
|
|
13296
13406
|
function defaultSkillTargetDir(target) {
|
|
13297
|
-
if (target === "claude") return
|
|
13298
|
-
return
|
|
13407
|
+
if (target === "claude") return resolve27(homedir4(), ".claude", "skills");
|
|
13408
|
+
return resolve27(homedir4(), ".codex", "skills");
|
|
13299
13409
|
}
|
|
13300
13410
|
async function walkFiles(root) {
|
|
13301
13411
|
const out = [];
|
|
@@ -13315,7 +13425,7 @@ async function walkFiles(root) {
|
|
|
13315
13425
|
}
|
|
13316
13426
|
async function filesEqual(a, b) {
|
|
13317
13427
|
try {
|
|
13318
|
-
const [ba, bb] = await Promise.all([
|
|
13428
|
+
const [ba, bb] = await Promise.all([readFile18(a), readFile18(b)]);
|
|
13319
13429
|
if (ba.length !== bb.length) return false;
|
|
13320
13430
|
return ba.equals(bb);
|
|
13321
13431
|
} catch {
|
|
@@ -13347,70 +13457,97 @@ async function skillMatches(srcDir, destDir) {
|
|
|
13347
13457
|
if (destFiles.length !== srcFiles.length) return false;
|
|
13348
13458
|
return true;
|
|
13349
13459
|
}
|
|
13350
|
-
async function
|
|
13460
|
+
async function isSymlink(path) {
|
|
13461
|
+
try {
|
|
13462
|
+
const stat6 = await lstat2(path);
|
|
13463
|
+
return stat6.isSymbolicLink();
|
|
13464
|
+
} catch {
|
|
13465
|
+
return false;
|
|
13466
|
+
}
|
|
13467
|
+
}
|
|
13468
|
+
async function installSkillDir(srcDir, destDir, skillName, force) {
|
|
13469
|
+
if (await isSymlink(destDir)) {
|
|
13470
|
+
return {
|
|
13471
|
+
skill: skillName,
|
|
13472
|
+
status: "skipped-symlink",
|
|
13473
|
+
targetPath: destDir
|
|
13474
|
+
};
|
|
13475
|
+
}
|
|
13351
13476
|
if (!await fileExists(destDir)) {
|
|
13352
13477
|
await copyDir(srcDir, destDir);
|
|
13353
|
-
return { skill: skillName, status: "installed", targetPath: destDir
|
|
13478
|
+
return { skill: skillName, status: "installed", targetPath: destDir };
|
|
13354
13479
|
}
|
|
13355
13480
|
if (await skillMatches(srcDir, destDir)) {
|
|
13356
|
-
return { skill: skillName, status: "already-current", targetPath: destDir
|
|
13481
|
+
return { skill: skillName, status: "already-current", targetPath: destDir };
|
|
13357
13482
|
}
|
|
13358
13483
|
if (force) {
|
|
13359
13484
|
await rm4(destDir, { recursive: true, force: true });
|
|
13360
13485
|
await copyDir(srcDir, destDir);
|
|
13361
|
-
return { skill: skillName, status: "overwritten", targetPath: destDir
|
|
13486
|
+
return { skill: skillName, status: "overwritten", targetPath: destDir };
|
|
13487
|
+
}
|
|
13488
|
+
return { skill: skillName, status: "differs-preserved", targetPath: destDir };
|
|
13489
|
+
}
|
|
13490
|
+
async function discoverSkillNames(sourceDir) {
|
|
13491
|
+
const entries = await readdir11(sourceDir, { withFileTypes: true });
|
|
13492
|
+
const names = [];
|
|
13493
|
+
for (const entry of entries) {
|
|
13494
|
+
if (!entry.isDirectory()) continue;
|
|
13495
|
+
if (entry.name.startsWith(".")) continue;
|
|
13496
|
+
if (await fileExists(join3(sourceDir, entry.name, "SKILL.md"))) {
|
|
13497
|
+
names.push(entry.name);
|
|
13498
|
+
}
|
|
13362
13499
|
}
|
|
13363
|
-
|
|
13500
|
+
const pinnedSet = new Set(KNOWN_SKILL_NAMES);
|
|
13501
|
+
const pinned = KNOWN_SKILL_NAMES.filter((name) => names.includes(name));
|
|
13502
|
+
const extras = names.filter((name) => !pinnedSet.has(name)).sort();
|
|
13503
|
+
return [...pinned, ...extras];
|
|
13364
13504
|
}
|
|
13365
|
-
async function
|
|
13366
|
-
|
|
13367
|
-
|
|
13505
|
+
async function installSkillsWithReport(options) {
|
|
13506
|
+
if (!options.ignorePluginActive && options.targetDir === void 0) {
|
|
13507
|
+
if (await isSyntaurPluginEnabledFor(options.target)) {
|
|
13508
|
+
return { results: [], skippedReason: "plugin-active" };
|
|
13509
|
+
}
|
|
13510
|
+
}
|
|
13511
|
+
const source = options.sourceDir ?? await getSkillsDir();
|
|
13368
13512
|
const targetRoot = options.targetDir ?? defaultSkillTargetDir(options.target);
|
|
13369
13513
|
const force = options.force ?? false;
|
|
13370
13514
|
if (!await fileExists(source)) {
|
|
13371
13515
|
throw new Error(
|
|
13372
|
-
`
|
|
13516
|
+
`Syntaur skills not found at ${source}. Reinstall syntaur: npm install -g syntaur@latest`
|
|
13373
13517
|
);
|
|
13374
13518
|
}
|
|
13519
|
+
const skillNames = await discoverSkillNames(source);
|
|
13375
13520
|
const results = [];
|
|
13376
13521
|
await mkdir4(targetRoot, { recursive: true });
|
|
13377
|
-
for (const skill of
|
|
13522
|
+
for (const skill of skillNames) {
|
|
13378
13523
|
const srcDir = join3(source, skill);
|
|
13379
|
-
if (!await fileExists(srcDir)) continue;
|
|
13380
13524
|
const destDir = join3(targetRoot, skill);
|
|
13381
|
-
results.push(await installSkillDir(srcDir, destDir, skill,
|
|
13382
|
-
}
|
|
13383
|
-
if (options.target === "claude" && await fileExists(platformSource)) {
|
|
13384
|
-
const entries = await readdir11(platformSource, { withFileTypes: true });
|
|
13385
|
-
for (const entry of entries) {
|
|
13386
|
-
if (!entry.isDirectory()) continue;
|
|
13387
|
-
const skill = entry.name;
|
|
13388
|
-
if (REQUIRED_SKILLS.includes(skill)) continue;
|
|
13389
|
-
const srcDir = join3(platformSource, skill);
|
|
13390
|
-
const destDir = join3(targetRoot, skill);
|
|
13391
|
-
results.push(await installSkillDir(srcDir, destDir, skill, "platform", force));
|
|
13392
|
-
}
|
|
13525
|
+
results.push(await installSkillDir(srcDir, destDir, skill, force));
|
|
13393
13526
|
}
|
|
13394
|
-
return results;
|
|
13527
|
+
return { results };
|
|
13395
13528
|
}
|
|
13396
13529
|
async function uninstallSkills(options) {
|
|
13397
13530
|
const targetRoot = options.targetDir ?? defaultSkillTargetDir(options.target);
|
|
13398
13531
|
if (!await fileExists(targetRoot)) return [];
|
|
13399
|
-
const
|
|
13400
|
-
const
|
|
13401
|
-
if (
|
|
13402
|
-
const
|
|
13403
|
-
|
|
13404
|
-
|
|
13532
|
+
const sourceDir = options.sourceDir ?? await getSkillsDir();
|
|
13533
|
+
const known = /* @__PURE__ */ new Set();
|
|
13534
|
+
if (await fileExists(sourceDir)) {
|
|
13535
|
+
for (const name of await discoverSkillNames(sourceDir)) {
|
|
13536
|
+
known.add(name);
|
|
13537
|
+
}
|
|
13538
|
+
} else {
|
|
13539
|
+
for (const name of KNOWN_SKILL_NAMES) {
|
|
13540
|
+
known.add(name);
|
|
13405
13541
|
}
|
|
13406
13542
|
}
|
|
13407
13543
|
const removed = [];
|
|
13408
13544
|
for (const skill of known) {
|
|
13409
13545
|
const destDir = join3(targetRoot, skill);
|
|
13410
13546
|
if (!await fileExists(destDir)) continue;
|
|
13547
|
+
if (await isSymlink(destDir)) continue;
|
|
13411
13548
|
const skillMd = join3(destDir, "SKILL.md");
|
|
13412
13549
|
if (!await fileExists(skillMd)) continue;
|
|
13413
|
-
const content = await
|
|
13550
|
+
const content = await readFile18(skillMd, "utf-8").catch(() => "");
|
|
13414
13551
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
13415
13552
|
if (!match || match[1] !== skill) continue;
|
|
13416
13553
|
await rm4(destDir, { recursive: true, force: true });
|
|
@@ -13418,11 +13555,22 @@ async function uninstallSkills(options) {
|
|
|
13418
13555
|
}
|
|
13419
13556
|
return removed;
|
|
13420
13557
|
}
|
|
13421
|
-
function formatInstallReport(
|
|
13558
|
+
function formatInstallReport(resultsOrReport, target) {
|
|
13559
|
+
const report = Array.isArray(resultsOrReport) ? { results: resultsOrReport } : resultsOrReport;
|
|
13560
|
+
const { results, skippedReason } = report;
|
|
13422
13561
|
const lines = [];
|
|
13423
13562
|
lines.push(`Skill install (${target}):`);
|
|
13563
|
+
if (skippedReason === "plugin-active") {
|
|
13564
|
+
lines.push(
|
|
13565
|
+
" Skipped \u2014 syntaur plugin is enabled for this agent and already loads skills from its manifest."
|
|
13566
|
+
);
|
|
13567
|
+
lines.push(
|
|
13568
|
+
" Run with --force-skills to install globally anyway (creates duplicates with the plugin)."
|
|
13569
|
+
);
|
|
13570
|
+
return lines.join("\n");
|
|
13571
|
+
}
|
|
13424
13572
|
for (const r of results) {
|
|
13425
|
-
const marker = r.status === "installed" ? "+" : r.status === "overwritten" ? "!" : r.status === "differs-preserved" ? "?" : "=";
|
|
13573
|
+
const marker = r.status === "installed" ? "+" : r.status === "overwritten" ? "!" : r.status === "differs-preserved" ? "?" : r.status === "skipped-symlink" ? "~" : "=";
|
|
13426
13574
|
lines.push(` ${marker} ${r.skill} (${r.status})`);
|
|
13427
13575
|
}
|
|
13428
13576
|
const diffs = results.filter((r) => r.status === "differs-preserved");
|
|
@@ -13432,7 +13580,14 @@ function formatInstallReport(results, target) {
|
|
|
13432
13580
|
` Note: ${diffs.length} skill(s) already exist with different content and were preserved.`
|
|
13433
13581
|
);
|
|
13434
13582
|
lines.push(
|
|
13435
|
-
" Run with --force-skills to overwrite with the
|
|
13583
|
+
" Run with --force-skills to overwrite with the syntaur version."
|
|
13584
|
+
);
|
|
13585
|
+
}
|
|
13586
|
+
const symlinked = results.filter((r) => r.status === "skipped-symlink");
|
|
13587
|
+
if (symlinked.length > 0) {
|
|
13588
|
+
lines.push("");
|
|
13589
|
+
lines.push(
|
|
13590
|
+
` Note: ${symlinked.length} skill(s) were skipped because the target is a symlink (likely managed by skills.sh).`
|
|
13436
13591
|
);
|
|
13437
13592
|
}
|
|
13438
13593
|
return lines.join("\n");
|
|
@@ -13450,11 +13605,12 @@ async function promptForInstallPath(question, recommendedPath) {
|
|
|
13450
13605
|
}
|
|
13451
13606
|
}
|
|
13452
13607
|
async function installPluginCommand(options) {
|
|
13608
|
+
const envOverride = process.env.SYNTAUR_PLUGIN_TARGET?.trim();
|
|
13453
13609
|
const shouldPromptForTarget = Boolean(
|
|
13454
|
-
options.promptForTarget !== false && isInteractiveTerminal() && !options.targetDir
|
|
13610
|
+
options.promptForTarget !== false && isInteractiveTerminal() && !options.targetDir && !envOverride
|
|
13455
13611
|
);
|
|
13456
13612
|
const recommendedTargetDir = await recommendPluginTargetDir("claude");
|
|
13457
|
-
const targetDir = options.targetDir ? normalizeAbsoluteInstallPath(options.targetDir, "Claude plugin target") : shouldPromptForTarget ? await promptForInstallPath("Claude plugin directory", recommendedTargetDir) : recommendedTargetDir;
|
|
13613
|
+
const targetDir = options.targetDir ? normalizeAbsoluteInstallPath(options.targetDir, "Claude plugin target") : envOverride ? normalizeAbsoluteInstallPath(envOverride, "SYNTAUR_PLUGIN_TARGET") : shouldPromptForTarget ? await promptForInstallPath("Claude plugin directory", recommendedTargetDir) : recommendedTargetDir;
|
|
13458
13614
|
const previousTargetDir = await getConfiguredOrLegacyManagedPluginDir("claude");
|
|
13459
13615
|
const migrating = Boolean(previousTargetDir && previousTargetDir !== targetDir);
|
|
13460
13616
|
let previousInstall = previousTargetDir ? await inspectInstallPath("claude", previousTargetDir) : null;
|
|
@@ -13487,6 +13643,7 @@ async function installPluginCommand(options) {
|
|
|
13487
13643
|
targetDir
|
|
13488
13644
|
});
|
|
13489
13645
|
const currentMarketplace = await detectClaudeMarketplaceForTarget(result.targetDir);
|
|
13646
|
+
let knownMarketplaceState = null;
|
|
13490
13647
|
if (currentMarketplace) {
|
|
13491
13648
|
await ensureClaudeMarketplaceEntry({
|
|
13492
13649
|
marketplaceRootDir: currentMarketplace.rootDir,
|
|
@@ -13494,6 +13651,10 @@ async function installPluginCommand(options) {
|
|
|
13494
13651
|
pluginTargetDir: result.targetDir,
|
|
13495
13652
|
expectedExistingPluginTargetDir: previousMarketplace && previousMarketplace.manifestPath === currentMarketplace.manifestPath ? previousTargetDir : null
|
|
13496
13653
|
});
|
|
13654
|
+
knownMarketplaceState = await ensureKnownClaudeMarketplaceForRoot({
|
|
13655
|
+
name: currentMarketplace.name,
|
|
13656
|
+
rootDir: currentMarketplace.rootDir
|
|
13657
|
+
});
|
|
13497
13658
|
} else {
|
|
13498
13659
|
console.warn(
|
|
13499
13660
|
`Warning: ${result.targetDir} is not inside a Claude Code marketplace (expected parent path of the form ~/.claude/plugins/marketplaces/<name>/plugins/syntaur). The plugin files were copied, but Claude Code will not discover them until you place them inside a marketplace.`
|
|
@@ -13517,21 +13678,49 @@ async function installPluginCommand(options) {
|
|
|
13517
13678
|
}
|
|
13518
13679
|
previousInstall = null;
|
|
13519
13680
|
}
|
|
13681
|
+
let enableResult = null;
|
|
13682
|
+
if (options.enable && currentMarketplace) {
|
|
13683
|
+
try {
|
|
13684
|
+
enableResult = await setSyntaurPluginEnabled({
|
|
13685
|
+
marketplaceName: currentMarketplace.name,
|
|
13686
|
+
enabled: true
|
|
13687
|
+
});
|
|
13688
|
+
} catch (error) {
|
|
13689
|
+
console.warn(
|
|
13690
|
+
`Warning: could not enable plugin \u2014 ${error instanceof Error ? error.message : String(error)}`
|
|
13691
|
+
);
|
|
13692
|
+
}
|
|
13693
|
+
}
|
|
13520
13694
|
console.log("Installed Syntaur plugin:");
|
|
13521
13695
|
console.log(` target: ${result.targetDir}`);
|
|
13522
13696
|
console.log(` source: ${result.sourceDir}`);
|
|
13523
13697
|
console.log(` mode: ${result.mode}`);
|
|
13524
13698
|
if (currentMarketplace) {
|
|
13525
13699
|
console.log(` marketplace: ${currentMarketplace.manifestPath}`);
|
|
13700
|
+
if (knownMarketplaceState) {
|
|
13701
|
+
const tag = knownMarketplaceState.added ? "registered (added)" : knownMarketplaceState.updated ? "registered (updated)" : "already registered";
|
|
13702
|
+
console.log(` known_marketplaces.json: ${tag}`);
|
|
13703
|
+
}
|
|
13704
|
+
const enabledKey = `syntaur@${currentMarketplace.name}`;
|
|
13705
|
+
if (enableResult) {
|
|
13706
|
+
console.log(
|
|
13707
|
+
` enabledPlugins: ${enabledKey} = ${enableResult.current}` + (enableResult.changed ? "" : " (already)")
|
|
13708
|
+
);
|
|
13709
|
+
} else {
|
|
13710
|
+
console.log(
|
|
13711
|
+
` enabledPlugins: ${enabledKey} not modified \u2014 run /plugin to enable, or pass --enable next time`
|
|
13712
|
+
);
|
|
13713
|
+
}
|
|
13526
13714
|
}
|
|
13527
13715
|
if (!options.skipSkills) {
|
|
13528
13716
|
try {
|
|
13529
|
-
const
|
|
13717
|
+
const skillReport = await installSkillsWithReport({
|
|
13530
13718
|
target: "claude",
|
|
13531
|
-
force: options.forceSkills
|
|
13719
|
+
force: options.forceSkills,
|
|
13720
|
+
ignorePluginActive: options.forceSkills
|
|
13532
13721
|
});
|
|
13533
13722
|
console.log("");
|
|
13534
|
-
console.log(formatInstallReport(
|
|
13723
|
+
console.log(formatInstallReport(skillReport, "claude"));
|
|
13535
13724
|
} catch (error) {
|
|
13536
13725
|
console.warn(
|
|
13537
13726
|
`Warning: skill install failed \u2014 ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -13539,25 +13728,24 @@ async function installPluginCommand(options) {
|
|
|
13539
13728
|
}
|
|
13540
13729
|
}
|
|
13541
13730
|
console.log("\nThe plugin is now available in Claude Code.");
|
|
13542
|
-
console.log(" Slash commands: /grab-assignment, /plan-assignment, /complete-assignment, /create-assignment, /create-project, /track-session, /
|
|
13731
|
+
console.log(" Slash commands: /grab-assignment, /plan-assignment, /complete-assignment, /create-assignment, /create-project, /track-session, /clear-assignment, /manage-statuses");
|
|
13543
13732
|
console.log(" Background: syntaur-protocol skill (auto-invoked)");
|
|
13544
|
-
console.log("
|
|
13545
|
-
console.log(" Hook: write boundary enforcement (PreToolUse) + SessionStart/End + PreCompact (prompts /save-session-summary)");
|
|
13733
|
+
console.log(" Hook: write boundary enforcement (PreToolUse) + SessionStart/End");
|
|
13546
13734
|
}
|
|
13547
13735
|
|
|
13548
13736
|
// src/commands/install-statusline.ts
|
|
13549
13737
|
init_paths();
|
|
13550
13738
|
init_fs();
|
|
13551
|
-
import { readFile as
|
|
13552
|
-
import { resolve as
|
|
13553
|
-
import { homedir as
|
|
13554
|
-
import { fileURLToPath as
|
|
13739
|
+
import { readFile as readFile20, writeFile as writeFile8, copyFile as copyFile2, rm as rm5, stat as stat2, symlink as symlink2, unlink as unlink6, lstat as lstat3 } from "fs/promises";
|
|
13740
|
+
import { resolve as resolve29, dirname as dirname10 } from "path";
|
|
13741
|
+
import { homedir as homedir5 } from "os";
|
|
13742
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13555
13743
|
|
|
13556
13744
|
// src/commands/configure-statusline.ts
|
|
13557
13745
|
init_paths();
|
|
13558
13746
|
init_fs();
|
|
13559
|
-
import { readFile as
|
|
13560
|
-
import { resolve as
|
|
13747
|
+
import { readFile as readFile19, writeFile as writeFile7 } from "fs/promises";
|
|
13748
|
+
import { resolve as resolve28, dirname as dirname9 } from "path";
|
|
13561
13749
|
import { spawnSync } from "child_process";
|
|
13562
13750
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
13563
13751
|
var AVAILABLE_SEGMENTS = [
|
|
@@ -13578,12 +13766,12 @@ var PRESETS = {
|
|
|
13578
13766
|
tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
|
|
13579
13767
|
};
|
|
13580
13768
|
function getConfigPath(installRoot) {
|
|
13581
|
-
return
|
|
13769
|
+
return resolve28(installRoot, "statusline.config.json");
|
|
13582
13770
|
}
|
|
13583
13771
|
async function readConfig2(path) {
|
|
13584
13772
|
if (!await fileExists(path)) return null;
|
|
13585
13773
|
try {
|
|
13586
|
-
const raw = await
|
|
13774
|
+
const raw = await readFile19(path, "utf-8");
|
|
13587
13775
|
const parsed = JSON.parse(raw);
|
|
13588
13776
|
if (!parsed || typeof parsed !== "object") return null;
|
|
13589
13777
|
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
@@ -13678,7 +13866,7 @@ function renderPreview(config, statuslineScript, cwd) {
|
|
|
13678
13866
|
env: {
|
|
13679
13867
|
...process.env,
|
|
13680
13868
|
// Force the child to pick up the freshly-written config from install root.
|
|
13681
|
-
HOME:
|
|
13869
|
+
HOME: dirname9(dirname9(statuslineScript))
|
|
13682
13870
|
}
|
|
13683
13871
|
});
|
|
13684
13872
|
if (res.status !== 0) return null;
|
|
@@ -13729,14 +13917,14 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
13729
13917
|
console.log("(preview mode \u2014 config NOT written)");
|
|
13730
13918
|
return;
|
|
13731
13919
|
}
|
|
13732
|
-
await ensureDir(
|
|
13920
|
+
await ensureDir(dirname9(configPath));
|
|
13733
13921
|
await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
13734
13922
|
console.log("Wrote statusline config:");
|
|
13735
13923
|
console.log(` path: ${configPath}`);
|
|
13736
13924
|
console.log(` segments: ${config.segments.join(", ")}`);
|
|
13737
13925
|
console.log(` separator: ${JSON.stringify(config.separator)}`);
|
|
13738
13926
|
if (config.wrap) console.log(` wrap: ${config.wrap}`);
|
|
13739
|
-
const script = options.statuslineScript ??
|
|
13927
|
+
const script = options.statuslineScript ?? resolve28(installRoot, "statusline.sh");
|
|
13740
13928
|
if (await fileExists(script)) {
|
|
13741
13929
|
console.log("");
|
|
13742
13930
|
console.log("Live preview:");
|
|
@@ -13756,7 +13944,7 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
13756
13944
|
async function writeDefaultConfigIfMissing(installRoot) {
|
|
13757
13945
|
const path = getConfigPath(installRoot);
|
|
13758
13946
|
if (await fileExists(path)) return;
|
|
13759
|
-
await ensureDir(
|
|
13947
|
+
await ensureDir(dirname9(path));
|
|
13760
13948
|
const defaultConfig = {
|
|
13761
13949
|
segments: ["git", "assignment", "session"],
|
|
13762
13950
|
separator: " \xB7 "
|
|
@@ -13766,12 +13954,12 @@ async function writeDefaultConfigIfMissing(installRoot) {
|
|
|
13766
13954
|
|
|
13767
13955
|
// src/commands/install-statusline.ts
|
|
13768
13956
|
function getPackageStatuslineSource() {
|
|
13769
|
-
const here =
|
|
13770
|
-
return
|
|
13957
|
+
const here = dirname10(fileURLToPath4(import.meta.url));
|
|
13958
|
+
return resolve29(here, "..", "statusline", "statusline.sh");
|
|
13771
13959
|
}
|
|
13772
13960
|
async function readSettingsJson(settingsPath) {
|
|
13773
13961
|
if (!await fileExists(settingsPath)) return {};
|
|
13774
|
-
const raw = await
|
|
13962
|
+
const raw = await readFile20(settingsPath, "utf-8");
|
|
13775
13963
|
if (raw.trim() === "") return {};
|
|
13776
13964
|
try {
|
|
13777
13965
|
const parsed = JSON.parse(raw);
|
|
@@ -13783,7 +13971,7 @@ async function readSettingsJson(settingsPath) {
|
|
|
13783
13971
|
}
|
|
13784
13972
|
}
|
|
13785
13973
|
async function writeSettingsJson(settingsPath, data) {
|
|
13786
|
-
await ensureDir(
|
|
13974
|
+
await ensureDir(dirname10(settingsPath));
|
|
13787
13975
|
await writeFile8(settingsPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
13788
13976
|
}
|
|
13789
13977
|
async function resolveMode(mode, existingCommand, ourCommand) {
|
|
@@ -13818,7 +14006,7 @@ function extractExistingCommand(settings) {
|
|
|
13818
14006
|
};
|
|
13819
14007
|
}
|
|
13820
14008
|
async function backupSettings(settingsSnapshot, backupPath) {
|
|
13821
|
-
await ensureDir(
|
|
14009
|
+
await ensureDir(dirname10(backupPath));
|
|
13822
14010
|
await writeFile8(
|
|
13823
14011
|
backupPath,
|
|
13824
14012
|
JSON.stringify(
|
|
@@ -13835,9 +14023,9 @@ async function backupSettings(settingsSnapshot, backupPath) {
|
|
|
13835
14023
|
);
|
|
13836
14024
|
}
|
|
13837
14025
|
async function installScript(sourceScript, destScript, link) {
|
|
13838
|
-
await ensureDir(
|
|
14026
|
+
await ensureDir(dirname10(destScript));
|
|
13839
14027
|
try {
|
|
13840
|
-
const s = await
|
|
14028
|
+
const s = await lstat3(destScript);
|
|
13841
14029
|
if (s.isSymbolicLink() || s.isFile()) {
|
|
13842
14030
|
await unlink6(destScript);
|
|
13843
14031
|
}
|
|
@@ -13851,12 +14039,12 @@ async function installScript(sourceScript, destScript, link) {
|
|
|
13851
14039
|
}
|
|
13852
14040
|
async function installStatuslineCommand(options = {}) {
|
|
13853
14041
|
const mode = options.mode ?? "ask";
|
|
13854
|
-
const settingsPath = options.settingsPath ??
|
|
14042
|
+
const settingsPath = options.settingsPath ?? resolve29(homedir5(), ".claude", "settings.json");
|
|
13855
14043
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
13856
14044
|
const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
|
|
13857
|
-
const destScript =
|
|
13858
|
-
const confPath =
|
|
13859
|
-
const backupPath =
|
|
14045
|
+
const destScript = resolve29(installRoot, "statusline.sh");
|
|
14046
|
+
const confPath = resolve29(installRoot, "statusline.conf");
|
|
14047
|
+
const backupPath = resolve29(installRoot, "statusline.backup.json");
|
|
13860
14048
|
if (!await fileExists(sourceScript)) {
|
|
13861
14049
|
throw new Error(
|
|
13862
14050
|
`Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
|
|
@@ -13892,7 +14080,7 @@ async function installStatuslineCommand(options = {}) {
|
|
|
13892
14080
|
if (parsed) {
|
|
13893
14081
|
wrapTarget = parsed;
|
|
13894
14082
|
} else {
|
|
13895
|
-
const wrapperPath =
|
|
14083
|
+
const wrapperPath = resolve29(installRoot, "statusline-wrapped.sh");
|
|
13896
14084
|
const wrapperBody = `#!/usr/bin/env bash
|
|
13897
14085
|
# Auto-generated by syntaur install-statusline.
|
|
13898
14086
|
# Executes the previously configured statusLine command.
|
|
@@ -13903,7 +14091,7 @@ exec ${existingCommand}
|
|
|
13903
14091
|
wrapTarget = wrapperPath;
|
|
13904
14092
|
}
|
|
13905
14093
|
}
|
|
13906
|
-
await ensureDir(
|
|
14094
|
+
await ensureDir(dirname10(confPath));
|
|
13907
14095
|
await writeFile8(
|
|
13908
14096
|
confPath,
|
|
13909
14097
|
wrapTarget ? `# Wrap target \u2014 the command below is invoked with the same stdin; its
|
|
@@ -13941,25 +14129,25 @@ function parseWrapCommand(command) {
|
|
|
13941
14129
|
async function chmodExec(path) {
|
|
13942
14130
|
const fs = await import("fs/promises");
|
|
13943
14131
|
try {
|
|
13944
|
-
const s = await
|
|
14132
|
+
const s = await stat2(path);
|
|
13945
14133
|
await fs.chmod(path, s.mode | 73);
|
|
13946
14134
|
} catch {
|
|
13947
14135
|
}
|
|
13948
14136
|
}
|
|
13949
14137
|
async function uninstallStatuslineCommand(options = {}) {
|
|
13950
|
-
const settingsPath = options.settingsPath ??
|
|
14138
|
+
const settingsPath = options.settingsPath ?? resolve29(homedir5(), ".claude", "settings.json");
|
|
13951
14139
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
13952
|
-
const destScript =
|
|
13953
|
-
const confPath =
|
|
13954
|
-
const backupPath =
|
|
13955
|
-
const wrapperPath =
|
|
14140
|
+
const destScript = resolve29(installRoot, "statusline.sh");
|
|
14141
|
+
const confPath = resolve29(installRoot, "statusline.conf");
|
|
14142
|
+
const backupPath = resolve29(installRoot, "statusline.backup.json");
|
|
14143
|
+
const wrapperPath = resolve29(installRoot, "statusline-wrapped.sh");
|
|
13956
14144
|
const settings = await readSettingsJson(settingsPath);
|
|
13957
14145
|
const existing = extractExistingCommand(settings);
|
|
13958
14146
|
const ourCommand = `bash ${destScript}`;
|
|
13959
14147
|
let restored = null;
|
|
13960
14148
|
if (await fileExists(backupPath)) {
|
|
13961
14149
|
try {
|
|
13962
|
-
const raw = await
|
|
14150
|
+
const raw = await readFile20(backupPath, "utf-8");
|
|
13963
14151
|
const parsed = JSON.parse(raw);
|
|
13964
14152
|
const prev = parsed?.previousStatusLine;
|
|
13965
14153
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -13980,7 +14168,7 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
13980
14168
|
await writeSettingsJson(settingsPath, settings);
|
|
13981
14169
|
}
|
|
13982
14170
|
if (!options.keepScript) {
|
|
13983
|
-
const configPath =
|
|
14171
|
+
const configPath = resolve29(installRoot, "statusline.config.json");
|
|
13984
14172
|
for (const path of [destScript, confPath, backupPath, wrapperPath, configPath]) {
|
|
13985
14173
|
try {
|
|
13986
14174
|
await rm5(path, { force: true });
|
|
@@ -14078,12 +14266,13 @@ async function installCodexPluginCommand(options) {
|
|
|
14078
14266
|
console.log(` marketplace: ${marketplace.marketplacePath}`);
|
|
14079
14267
|
if (!options.skipSkills) {
|
|
14080
14268
|
try {
|
|
14081
|
-
const
|
|
14269
|
+
const skillReport = await installSkillsWithReport({
|
|
14082
14270
|
target: "codex",
|
|
14083
|
-
force: options.forceSkills
|
|
14271
|
+
force: options.forceSkills,
|
|
14272
|
+
ignorePluginActive: options.forceSkills
|
|
14084
14273
|
});
|
|
14085
14274
|
console.log("");
|
|
14086
|
-
console.log(formatInstallReport(
|
|
14275
|
+
console.log(formatInstallReport(skillReport, "codex"));
|
|
14087
14276
|
} catch (error) {
|
|
14088
14277
|
console.warn(
|
|
14089
14278
|
`Warning: skill install failed \u2014 ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -14236,7 +14425,7 @@ async function setupCommand(options) {
|
|
|
14236
14425
|
}
|
|
14237
14426
|
|
|
14238
14427
|
// src/commands/uninstall.ts
|
|
14239
|
-
import { resolve as
|
|
14428
|
+
import { resolve as resolve30 } from "path";
|
|
14240
14429
|
init_paths();
|
|
14241
14430
|
function expandTargets(options) {
|
|
14242
14431
|
if (options.all) {
|
|
@@ -14316,7 +14505,7 @@ async function uninstallCommand(options) {
|
|
|
14316
14505
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
14317
14506
|
await removeSyntaurData();
|
|
14318
14507
|
console.log(`Removed ${syntaurRoot()}`);
|
|
14319
|
-
if (configuredProjectDir &&
|
|
14508
|
+
if (configuredProjectDir && resolve30(configuredProjectDir) !== resolve30(syntaurRoot(), "projects")) {
|
|
14320
14509
|
console.warn(
|
|
14321
14510
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
14322
14511
|
);
|
|
@@ -14335,7 +14524,7 @@ init_slug();
|
|
|
14335
14524
|
init_cursor_rules();
|
|
14336
14525
|
init_codex_agents();
|
|
14337
14526
|
init_opencode_config();
|
|
14338
|
-
import { resolve as
|
|
14527
|
+
import { resolve as resolve31 } from "path";
|
|
14339
14528
|
var SUPPORTED_FRAMEWORKS = ["cursor", "codex", "opencode"];
|
|
14340
14529
|
async function setupAdapterCommand(framework, options) {
|
|
14341
14530
|
if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
|
|
@@ -14361,19 +14550,19 @@ async function setupAdapterCommand(framework, options) {
|
|
|
14361
14550
|
}
|
|
14362
14551
|
const config = await readConfig();
|
|
14363
14552
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
14364
|
-
const projectDir =
|
|
14365
|
-
const assignmentDir =
|
|
14553
|
+
const projectDir = resolve31(baseDir, options.project);
|
|
14554
|
+
const assignmentDir = resolve31(
|
|
14366
14555
|
projectDir,
|
|
14367
14556
|
"assignments",
|
|
14368
14557
|
options.assignment
|
|
14369
14558
|
);
|
|
14370
|
-
const projectMdPath =
|
|
14559
|
+
const projectMdPath = resolve31(projectDir, "project.md");
|
|
14371
14560
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
14372
14561
|
throw new Error(
|
|
14373
14562
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
14374
14563
|
);
|
|
14375
14564
|
}
|
|
14376
|
-
const assignmentMdPath =
|
|
14565
|
+
const assignmentMdPath = resolve31(assignmentDir, "assignment.md");
|
|
14377
14566
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath)) {
|
|
14378
14567
|
throw new Error(
|
|
14379
14568
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -14401,15 +14590,15 @@ async function setupAdapterCommand(framework, options) {
|
|
|
14401
14590
|
}
|
|
14402
14591
|
}
|
|
14403
14592
|
if (framework === "cursor") {
|
|
14404
|
-
const protocolPath =
|
|
14405
|
-
const assignmentPath =
|
|
14593
|
+
const protocolPath = resolve31(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
|
|
14594
|
+
const assignmentPath = resolve31(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
|
|
14406
14595
|
await writeAdapterFile(protocolPath, renderCursorProtocol());
|
|
14407
14596
|
await writeAdapterFile(assignmentPath, renderCursorAssignment(rendererParams));
|
|
14408
14597
|
} else if (framework === "codex" || framework === "opencode") {
|
|
14409
|
-
const agentsPath =
|
|
14598
|
+
const agentsPath = resolve31(cwd, "AGENTS.md");
|
|
14410
14599
|
await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
|
|
14411
14600
|
if (framework === "opencode") {
|
|
14412
|
-
const configPath =
|
|
14601
|
+
const configPath = resolve31(cwd, "opencode.json");
|
|
14413
14602
|
await writeAdapterFile(configPath, renderOpenCodeConfig({ projectDir }));
|
|
14414
14603
|
}
|
|
14415
14604
|
}
|
|
@@ -14434,7 +14623,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
14434
14623
|
init_paths();
|
|
14435
14624
|
init_fs();
|
|
14436
14625
|
init_config2();
|
|
14437
|
-
import { resolve as
|
|
14626
|
+
import { resolve as resolve32 } from "path";
|
|
14438
14627
|
async function trackSessionCommand(options) {
|
|
14439
14628
|
if (!options.agent) {
|
|
14440
14629
|
throw new Error("--agent <name> is required.");
|
|
@@ -14447,7 +14636,7 @@ async function trackSessionCommand(options) {
|
|
|
14447
14636
|
if (options.project) {
|
|
14448
14637
|
const config = await readConfig();
|
|
14449
14638
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
14450
|
-
const projectDir =
|
|
14639
|
+
const projectDir = resolve32(baseDir, options.project);
|
|
14451
14640
|
if (!await fileExists(projectDir)) {
|
|
14452
14641
|
throw new Error(
|
|
14453
14642
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -14481,8 +14670,8 @@ init_config2();
|
|
|
14481
14670
|
init_paths();
|
|
14482
14671
|
init_fs();
|
|
14483
14672
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
14484
|
-
import { resolve as
|
|
14485
|
-
import { readFile as
|
|
14673
|
+
import { resolve as resolve34, isAbsolute as isAbsolute4 } from "path";
|
|
14674
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
14486
14675
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
14487
14676
|
async function browseCommand(options) {
|
|
14488
14677
|
const config = await readConfig();
|
|
@@ -14551,7 +14740,7 @@ async function pickAgent(agents) {
|
|
|
14551
14740
|
return picked;
|
|
14552
14741
|
}
|
|
14553
14742
|
async function ensureWorktree(opts) {
|
|
14554
|
-
const assignmentPath =
|
|
14743
|
+
const assignmentPath = resolve34(
|
|
14555
14744
|
opts.projectsDir,
|
|
14556
14745
|
opts.projectSlug,
|
|
14557
14746
|
"assignments",
|
|
@@ -14561,7 +14750,7 @@ async function ensureWorktree(opts) {
|
|
|
14561
14750
|
if (!await fileExists(assignmentPath)) {
|
|
14562
14751
|
return void 0;
|
|
14563
14752
|
}
|
|
14564
|
-
const content = await
|
|
14753
|
+
const content = await readFile22(assignmentPath, "utf-8");
|
|
14565
14754
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
14566
14755
|
const fm = parseAssignmentFrontmatter2(content);
|
|
14567
14756
|
const { workspace } = fm;
|
|
@@ -14632,7 +14821,7 @@ function computeWorktreeDefaults(opts) {
|
|
|
14632
14821
|
const repository = opts.existing.repository ?? detectCurrentGitRoot();
|
|
14633
14822
|
const branch = opts.projectSlug ? `syntaur/${opts.projectSlug}/${opts.assignmentSlug}` : `syntaur/${opts.assignmentSlug}`;
|
|
14634
14823
|
const parentBranch = opts.existing.parentBranch ?? detectCurrentBranch() ?? "main";
|
|
14635
|
-
const worktreeBase =
|
|
14824
|
+
const worktreeBase = resolve34(
|
|
14636
14825
|
syntaurRoot(),
|
|
14637
14826
|
"worktrees",
|
|
14638
14827
|
opts.projectSlug || "standalone",
|
|
@@ -14665,7 +14854,7 @@ function detectCurrentBranch() {
|
|
|
14665
14854
|
async function runCreate(opts) {
|
|
14666
14855
|
const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
14667
14856
|
const expandedWorktree = expandHome(opts.worktreePath);
|
|
14668
|
-
const absWorktree = isAbsolute4(expandedWorktree) ? expandedWorktree :
|
|
14857
|
+
const absWorktree = isAbsolute4(expandedWorktree) ? expandedWorktree : resolve34(expandedWorktree);
|
|
14669
14858
|
try {
|
|
14670
14859
|
await createWorktreeAndRecord2({
|
|
14671
14860
|
assignmentPath: opts.assignmentPath,
|
|
@@ -14694,7 +14883,7 @@ init_paths();
|
|
|
14694
14883
|
init_fs();
|
|
14695
14884
|
init_playbook();
|
|
14696
14885
|
init_playbooks();
|
|
14697
|
-
import { resolve as
|
|
14886
|
+
import { resolve as resolve35 } from "path";
|
|
14698
14887
|
async function createPlaybookCommand(name, options) {
|
|
14699
14888
|
if (!name.trim()) {
|
|
14700
14889
|
throw new Error("Playbook name cannot be empty.");
|
|
@@ -14707,7 +14896,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
14707
14896
|
}
|
|
14708
14897
|
const dir = playbooksDir();
|
|
14709
14898
|
await ensureDir(dir);
|
|
14710
|
-
const filePath =
|
|
14899
|
+
const filePath = resolve35(dir, `${slug}.md`);
|
|
14711
14900
|
if (await fileExists(filePath)) {
|
|
14712
14901
|
throw new Error(
|
|
14713
14902
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -14729,8 +14918,8 @@ init_paths();
|
|
|
14729
14918
|
init_fs();
|
|
14730
14919
|
init_parser();
|
|
14731
14920
|
init_config2();
|
|
14732
|
-
import { readdir as readdir12, readFile as
|
|
14733
|
-
import { resolve as
|
|
14921
|
+
import { readdir as readdir12, readFile as readFile23 } from "fs/promises";
|
|
14922
|
+
import { resolve as resolve36 } from "path";
|
|
14734
14923
|
async function listPlaybooksCommand(options = {}) {
|
|
14735
14924
|
const dir = playbooksDir();
|
|
14736
14925
|
if (!await fileExists(dir)) {
|
|
@@ -14745,8 +14934,8 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
14745
14934
|
);
|
|
14746
14935
|
const rows = [];
|
|
14747
14936
|
for (const entry of mdFiles) {
|
|
14748
|
-
const filePath =
|
|
14749
|
-
const raw = await
|
|
14937
|
+
const filePath = resolve36(dir, entry.name);
|
|
14938
|
+
const raw = await readFile23(filePath, "utf-8");
|
|
14750
14939
|
const parsed = parsePlaybook(raw);
|
|
14751
14940
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
14752
14941
|
const disabled = disabledSet.has(slug);
|
|
@@ -14844,8 +15033,8 @@ init_fs();
|
|
|
14844
15033
|
init_config2();
|
|
14845
15034
|
init_slug();
|
|
14846
15035
|
import { Command } from "commander";
|
|
14847
|
-
import { readFile as
|
|
14848
|
-
import { resolve as
|
|
15036
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
15037
|
+
import { resolve as resolve37 } from "path";
|
|
14849
15038
|
var WORKSPACE_REGEX3 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
14850
15039
|
async function resolveScope(options) {
|
|
14851
15040
|
const flagCount = [Boolean(options.project), Boolean(options.workspace), Boolean(options.global)].filter(Boolean).length;
|
|
@@ -14857,7 +15046,7 @@ async function resolveScope(options) {
|
|
|
14857
15046
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
14858
15047
|
}
|
|
14859
15048
|
const config = await readConfig();
|
|
14860
|
-
const projectMd =
|
|
15049
|
+
const projectMd = resolve37(config.defaultProjectDir, options.project, "project.md");
|
|
14861
15050
|
if (!await fileExists(projectMd)) {
|
|
14862
15051
|
throw new Error(`Project "${options.project}" not found.`);
|
|
14863
15052
|
}
|
|
@@ -15176,10 +15365,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
15176
15365
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
15177
15366
|
);
|
|
15178
15367
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
15179
|
-
await ensureDir(
|
|
15368
|
+
await ensureDir(resolve37(todosPath, "archive"));
|
|
15180
15369
|
let archContent = "";
|
|
15181
15370
|
if (await fileExists(archFile)) {
|
|
15182
|
-
archContent = await
|
|
15371
|
+
archContent = await readFile24(archFile, "utf-8");
|
|
15183
15372
|
archContent = archContent.trimEnd() + "\n\n";
|
|
15184
15373
|
} else {
|
|
15185
15374
|
archContent = `---
|
|
@@ -15352,12 +15541,12 @@ function describeScope(scope) {
|
|
|
15352
15541
|
}
|
|
15353
15542
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
15354
15543
|
const { resolve: resolvePath2 } = await import("path");
|
|
15355
|
-
const { readFile:
|
|
15544
|
+
const { readFile: readFile35 } = await import("fs/promises");
|
|
15356
15545
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15357
15546
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
15358
15547
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
15359
15548
|
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
15360
|
-
let content = await
|
|
15549
|
+
let content = await readFile35(assignmentMdPath, "utf-8");
|
|
15361
15550
|
content = appendTodosToAssignmentBody2(
|
|
15362
15551
|
content,
|
|
15363
15552
|
todos.map((t) => ({
|
|
@@ -15402,13 +15591,13 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
15402
15591
|
}
|
|
15403
15592
|
const planDir = todoPlanDir(todosPath, workspace, id);
|
|
15404
15593
|
await ensureDir(planDir);
|
|
15405
|
-
const { readdir:
|
|
15406
|
-
const existingFiles = (await
|
|
15594
|
+
const { readdir: readdir19 } = await import("fs/promises");
|
|
15595
|
+
const existingFiles = (await readdir19(planDir).catch(() => [])).filter(
|
|
15407
15596
|
(f) => /^plan(?:-v\d+)?\.md$/.test(f)
|
|
15408
15597
|
);
|
|
15409
15598
|
let target;
|
|
15410
15599
|
if (existingFiles.length === 0) {
|
|
15411
|
-
target =
|
|
15600
|
+
target = resolve37(planDir, "plan.md");
|
|
15412
15601
|
} else {
|
|
15413
15602
|
const versions = /* @__PURE__ */ new Set();
|
|
15414
15603
|
for (const f of existingFiles) {
|
|
@@ -15418,7 +15607,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
15418
15607
|
}
|
|
15419
15608
|
let n = 2;
|
|
15420
15609
|
while (versions.has(n)) n++;
|
|
15421
|
-
target =
|
|
15610
|
+
target = resolve37(planDir, `plan-v${n}.md`);
|
|
15422
15611
|
}
|
|
15423
15612
|
if (!await fileExists(target)) {
|
|
15424
15613
|
const stub = `---
|
|
@@ -15488,10 +15677,10 @@ async function moveTodo(id, options) {
|
|
|
15488
15677
|
if (await fileExists(newPlanDir)) {
|
|
15489
15678
|
throw new Error(`Plan directory already exists at target: ${newPlanDir}; refusing to move.`);
|
|
15490
15679
|
}
|
|
15491
|
-
const { rename:
|
|
15680
|
+
const { rename: rename7, mkdir: mkdir7 } = await import("fs/promises");
|
|
15492
15681
|
const { dirname: dirname16 } = await import("path");
|
|
15493
15682
|
await mkdir7(dirname16(newPlanDir), { recursive: true });
|
|
15494
|
-
await
|
|
15683
|
+
await rename7(item.planDir, newPlanDir);
|
|
15495
15684
|
item.planDir = newPlanDir;
|
|
15496
15685
|
}
|
|
15497
15686
|
sourceChecklist.items.splice(idx, 1);
|
|
@@ -15609,20 +15798,20 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
15609
15798
|
import { Command as Command3 } from "commander";
|
|
15610
15799
|
|
|
15611
15800
|
// src/utils/doctor/index.ts
|
|
15612
|
-
import { fileURLToPath as
|
|
15613
|
-
import { readFile as
|
|
15614
|
-
import { dirname as dirname13, join as
|
|
15801
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
15802
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
15803
|
+
import { dirname as dirname13, join as join6 } from "path";
|
|
15615
15804
|
|
|
15616
15805
|
// src/utils/doctor/context.ts
|
|
15617
15806
|
init_config2();
|
|
15618
15807
|
init_paths();
|
|
15619
15808
|
init_fs();
|
|
15620
15809
|
import Database2 from "better-sqlite3";
|
|
15621
|
-
import { resolve as
|
|
15810
|
+
import { resolve as resolve38 } from "path";
|
|
15622
15811
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
15623
15812
|
const config = await readConfig();
|
|
15624
15813
|
const root = syntaurRoot();
|
|
15625
|
-
const dbPath =
|
|
15814
|
+
const dbPath = resolve38(root, "syntaur.db");
|
|
15626
15815
|
let db2 = null;
|
|
15627
15816
|
let dbError = null;
|
|
15628
15817
|
if (await fileExists(dbPath)) {
|
|
@@ -15656,10 +15845,10 @@ function closeCheckContext(ctx) {
|
|
|
15656
15845
|
// src/utils/doctor/checks/env.ts
|
|
15657
15846
|
init_fs();
|
|
15658
15847
|
init_paths();
|
|
15659
|
-
import { resolve as
|
|
15660
|
-
import { readFile as
|
|
15661
|
-
import { fileURLToPath as
|
|
15662
|
-
import { dirname as
|
|
15848
|
+
import { resolve as resolve39, isAbsolute as isAbsolute5 } from "path";
|
|
15849
|
+
import { readFile as readFile25, stat as stat3 } from "fs/promises";
|
|
15850
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
15851
|
+
import { dirname as dirname11, join as join4 } from "path";
|
|
15663
15852
|
var CATEGORY = "env";
|
|
15664
15853
|
var syntaurRootExists = {
|
|
15665
15854
|
id: "env.syntaur-root-exists",
|
|
@@ -15667,7 +15856,7 @@ var syntaurRootExists = {
|
|
|
15667
15856
|
title: "~/.syntaur/ directory exists",
|
|
15668
15857
|
async run(ctx) {
|
|
15669
15858
|
try {
|
|
15670
|
-
const s = await
|
|
15859
|
+
const s = await stat3(ctx.syntaurRoot);
|
|
15671
15860
|
if (!s.isDirectory()) {
|
|
15672
15861
|
return err(this, `${ctx.syntaurRoot} exists but is not a directory`, [
|
|
15673
15862
|
ctx.syntaurRoot
|
|
@@ -15697,7 +15886,7 @@ var configValid = {
|
|
|
15697
15886
|
category: CATEGORY,
|
|
15698
15887
|
title: "~/.syntaur/config.md is valid",
|
|
15699
15888
|
async run(ctx) {
|
|
15700
|
-
const configPath =
|
|
15889
|
+
const configPath = resolve39(ctx.syntaurRoot, "config.md");
|
|
15701
15890
|
if (!await fileExists(configPath)) {
|
|
15702
15891
|
return {
|
|
15703
15892
|
id: this.id,
|
|
@@ -15714,7 +15903,7 @@ var configValid = {
|
|
|
15714
15903
|
autoFixable: false
|
|
15715
15904
|
};
|
|
15716
15905
|
}
|
|
15717
|
-
const content = await
|
|
15906
|
+
const content = await readFile25(configPath, "utf-8");
|
|
15718
15907
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
15719
15908
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
15720
15909
|
return {
|
|
@@ -15989,15 +16178,15 @@ async function readLocalVersion() {
|
|
|
15989
16178
|
}
|
|
15990
16179
|
async function readLocalPkg() {
|
|
15991
16180
|
try {
|
|
15992
|
-
const here =
|
|
15993
|
-
let dir =
|
|
16181
|
+
const here = fileURLToPath5(import.meta.url);
|
|
16182
|
+
let dir = dirname11(here);
|
|
15994
16183
|
for (let i = 0; i < 6; i++) {
|
|
15995
16184
|
const candidate = join4(dir, "package.json");
|
|
15996
16185
|
try {
|
|
15997
|
-
const text = await
|
|
16186
|
+
const text = await readFile25(candidate, "utf-8");
|
|
15998
16187
|
return JSON.parse(text);
|
|
15999
16188
|
} catch {
|
|
16000
|
-
dir =
|
|
16189
|
+
dir = dirname11(dir);
|
|
16001
16190
|
}
|
|
16002
16191
|
}
|
|
16003
16192
|
return null;
|
|
@@ -16046,8 +16235,8 @@ function versionGte(a, b) {
|
|
|
16046
16235
|
|
|
16047
16236
|
// src/utils/doctor/checks/structure.ts
|
|
16048
16237
|
init_fs();
|
|
16049
|
-
import { resolve as
|
|
16050
|
-
import { readdir as readdir13, stat as
|
|
16238
|
+
import { resolve as resolve40 } from "path";
|
|
16239
|
+
import { readdir as readdir13, stat as stat4 } from "fs/promises";
|
|
16051
16240
|
var CATEGORY2 = "structure";
|
|
16052
16241
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
16053
16242
|
"projects",
|
|
@@ -16066,7 +16255,7 @@ var projectsDir = {
|
|
|
16066
16255
|
category: CATEGORY2,
|
|
16067
16256
|
title: "projects/ directory exists",
|
|
16068
16257
|
async run(ctx) {
|
|
16069
|
-
const p =
|
|
16258
|
+
const p = resolve40(ctx.syntaurRoot, "projects");
|
|
16070
16259
|
if (!await fileExists(p)) {
|
|
16071
16260
|
return {
|
|
16072
16261
|
id: this.id,
|
|
@@ -16091,7 +16280,7 @@ var playbooksDir2 = {
|
|
|
16091
16280
|
category: CATEGORY2,
|
|
16092
16281
|
title: "playbooks/ directory exists",
|
|
16093
16282
|
async run(ctx) {
|
|
16094
|
-
const p =
|
|
16283
|
+
const p = resolve40(ctx.syntaurRoot, "playbooks");
|
|
16095
16284
|
if (!await fileExists(p)) {
|
|
16096
16285
|
return {
|
|
16097
16286
|
id: this.id,
|
|
@@ -16116,7 +16305,7 @@ var todosDirValid = {
|
|
|
16116
16305
|
category: CATEGORY2,
|
|
16117
16306
|
title: "todos/ directory is readable (if present)",
|
|
16118
16307
|
async run(ctx) {
|
|
16119
|
-
const p =
|
|
16308
|
+
const p = resolve40(ctx.syntaurRoot, "todos");
|
|
16120
16309
|
if (!await fileExists(p)) {
|
|
16121
16310
|
return {
|
|
16122
16311
|
id: this.id,
|
|
@@ -16127,7 +16316,7 @@ var todosDirValid = {
|
|
|
16127
16316
|
autoFixable: false
|
|
16128
16317
|
};
|
|
16129
16318
|
}
|
|
16130
|
-
const s = await
|
|
16319
|
+
const s = await stat4(p);
|
|
16131
16320
|
if (!s.isDirectory()) {
|
|
16132
16321
|
return {
|
|
16133
16322
|
id: this.id,
|
|
@@ -16147,7 +16336,7 @@ var serversDirValid = {
|
|
|
16147
16336
|
category: CATEGORY2,
|
|
16148
16337
|
title: "servers/ directory is readable (if present)",
|
|
16149
16338
|
async run(ctx) {
|
|
16150
|
-
const p =
|
|
16339
|
+
const p = resolve40(ctx.syntaurRoot, "servers");
|
|
16151
16340
|
if (!await fileExists(p)) {
|
|
16152
16341
|
return {
|
|
16153
16342
|
id: this.id,
|
|
@@ -16158,7 +16347,7 @@ var serversDirValid = {
|
|
|
16158
16347
|
autoFixable: false
|
|
16159
16348
|
};
|
|
16160
16349
|
}
|
|
16161
|
-
const s = await
|
|
16350
|
+
const s = await stat4(p);
|
|
16162
16351
|
if (!s.isDirectory()) {
|
|
16163
16352
|
return {
|
|
16164
16353
|
id: this.id,
|
|
@@ -16192,7 +16381,7 @@ var knownFilesRecognized = {
|
|
|
16192
16381
|
title: this.title,
|
|
16193
16382
|
status: "warn",
|
|
16194
16383
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
16195
|
-
affected: unexpected.map((n) =>
|
|
16384
|
+
affected: unexpected.map((n) => resolve40(ctx.syntaurRoot, n)),
|
|
16196
16385
|
remediation: {
|
|
16197
16386
|
kind: "manual",
|
|
16198
16387
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -16221,8 +16410,8 @@ function pass2(check) {
|
|
|
16221
16410
|
|
|
16222
16411
|
// src/utils/doctor/checks/project.ts
|
|
16223
16412
|
init_fs();
|
|
16224
|
-
import { resolve as
|
|
16225
|
-
import { readdir as readdir14, stat as
|
|
16413
|
+
import { resolve as resolve41 } from "path";
|
|
16414
|
+
import { readdir as readdir14, stat as stat5 } from "fs/promises";
|
|
16226
16415
|
var CATEGORY3 = "project";
|
|
16227
16416
|
var REQUIRED_PROJECT_FILES = [
|
|
16228
16417
|
"project.md",
|
|
@@ -16251,10 +16440,10 @@ async function listProjects2(ctx) {
|
|
|
16251
16440
|
for (const e of entries) {
|
|
16252
16441
|
if (!e.isDirectory()) continue;
|
|
16253
16442
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
16254
|
-
const projectDir =
|
|
16443
|
+
const projectDir = resolve41(dir, e.name);
|
|
16255
16444
|
let looksLikeProject = false;
|
|
16256
16445
|
for (const marker of PROJECT_MARKERS) {
|
|
16257
|
-
if (await fileExists(
|
|
16446
|
+
if (await fileExists(resolve41(projectDir, marker))) {
|
|
16258
16447
|
looksLikeProject = true;
|
|
16259
16448
|
break;
|
|
16260
16449
|
}
|
|
@@ -16273,7 +16462,7 @@ var requiredFiles = {
|
|
|
16273
16462
|
for (const projectDir of projects) {
|
|
16274
16463
|
const missing = [];
|
|
16275
16464
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
16276
|
-
const p =
|
|
16465
|
+
const p = resolve41(projectDir, rel);
|
|
16277
16466
|
if (!await fileExists(p)) missing.push(rel);
|
|
16278
16467
|
}
|
|
16279
16468
|
if (missing.length === 0) continue;
|
|
@@ -16283,7 +16472,7 @@ var requiredFiles = {
|
|
|
16283
16472
|
title: this.title,
|
|
16284
16473
|
status: "error",
|
|
16285
16474
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
16286
|
-
affected: missing.map((m) =>
|
|
16475
|
+
affected: missing.map((m) => resolve41(projectDir, m)),
|
|
16287
16476
|
remediation: {
|
|
16288
16477
|
kind: "manual",
|
|
16289
16478
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -16306,9 +16495,9 @@ var manifestStale = {
|
|
|
16306
16495
|
const projects = await listProjects2(ctx);
|
|
16307
16496
|
const results = [];
|
|
16308
16497
|
for (const projectDir of projects) {
|
|
16309
|
-
const manifestPath =
|
|
16498
|
+
const manifestPath = resolve41(projectDir, "manifest.md");
|
|
16310
16499
|
if (!await fileExists(manifestPath)) continue;
|
|
16311
|
-
const manifestMtime = (await
|
|
16500
|
+
const manifestMtime = (await stat5(manifestPath)).mtimeMs;
|
|
16312
16501
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
16313
16502
|
if (newestAssignment === 0) continue;
|
|
16314
16503
|
if (newestAssignment > manifestMtime) {
|
|
@@ -16355,7 +16544,7 @@ var orphanFiles = {
|
|
|
16355
16544
|
title: this.title,
|
|
16356
16545
|
status: "warn",
|
|
16357
16546
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
16358
|
-
affected: orphans.map((o) =>
|
|
16547
|
+
affected: orphans.map((o) => resolve41(projectDir, o)),
|
|
16359
16548
|
autoFixable: false
|
|
16360
16549
|
});
|
|
16361
16550
|
}
|
|
@@ -16365,7 +16554,7 @@ var orphanFiles = {
|
|
|
16365
16554
|
};
|
|
16366
16555
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
16367
16556
|
async function newestAssignmentMtime(projectDir) {
|
|
16368
|
-
const assignmentsRoot =
|
|
16557
|
+
const assignmentsRoot = resolve41(projectDir, "assignments");
|
|
16369
16558
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
16370
16559
|
let newest = 0;
|
|
16371
16560
|
let entries;
|
|
@@ -16376,9 +16565,9 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
16376
16565
|
}
|
|
16377
16566
|
for (const e of entries) {
|
|
16378
16567
|
if (!e.isDirectory()) continue;
|
|
16379
|
-
const assignmentMd =
|
|
16568
|
+
const assignmentMd = resolve41(assignmentsRoot, e.name, "assignment.md");
|
|
16380
16569
|
try {
|
|
16381
|
-
const s = await
|
|
16570
|
+
const s = await stat5(assignmentMd);
|
|
16382
16571
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
16383
16572
|
} catch {
|
|
16384
16573
|
}
|
|
@@ -16400,8 +16589,8 @@ init_fs();
|
|
|
16400
16589
|
init_parser();
|
|
16401
16590
|
init_types();
|
|
16402
16591
|
init_paths();
|
|
16403
|
-
import { resolve as
|
|
16404
|
-
import { readdir as readdir15, readFile as
|
|
16592
|
+
import { resolve as resolve42 } from "path";
|
|
16593
|
+
import { readdir as readdir15, readFile as readFile26 } from "fs/promises";
|
|
16405
16594
|
var CATEGORY4 = "assignment";
|
|
16406
16595
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
16407
16596
|
async function listAssignments(ctx) {
|
|
@@ -16412,16 +16601,16 @@ async function listAssignments(ctx) {
|
|
|
16412
16601
|
for (const m of projects) {
|
|
16413
16602
|
if (!m.isDirectory()) continue;
|
|
16414
16603
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
16415
|
-
const assignmentsDir2 =
|
|
16604
|
+
const assignmentsDir2 = resolve42(projectsDir2, m.name, "assignments");
|
|
16416
16605
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
16417
16606
|
const entries = await readdir15(assignmentsDir2, { withFileTypes: true });
|
|
16418
16607
|
for (const a of entries) {
|
|
16419
16608
|
if (!a.isDirectory()) continue;
|
|
16420
16609
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
16421
|
-
const assignmentDir =
|
|
16422
|
-
const assignmentMd =
|
|
16610
|
+
const assignmentDir = resolve42(assignmentsDir2, a.name);
|
|
16611
|
+
const assignmentMd = resolve42(assignmentDir, "assignment.md");
|
|
16423
16612
|
const entry = {
|
|
16424
|
-
projectDir:
|
|
16613
|
+
projectDir: resolve42(projectsDir2, m.name),
|
|
16425
16614
|
projectSlug: m.name,
|
|
16426
16615
|
assignmentDir,
|
|
16427
16616
|
assignmentSlug: a.name,
|
|
@@ -16441,8 +16630,8 @@ async function listAssignments(ctx) {
|
|
|
16441
16630
|
for (const a of entries) {
|
|
16442
16631
|
if (!a.isDirectory()) continue;
|
|
16443
16632
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
16444
|
-
const assignmentDir =
|
|
16445
|
-
const assignmentMd =
|
|
16633
|
+
const assignmentDir = resolve42(standaloneRoot, a.name);
|
|
16634
|
+
const assignmentMd = resolve42(assignmentDir, "assignment.md");
|
|
16446
16635
|
const entry = {
|
|
16447
16636
|
projectDir: standaloneRoot,
|
|
16448
16637
|
projectSlug: null,
|
|
@@ -16520,7 +16709,7 @@ var invalidStatus = {
|
|
|
16520
16709
|
const allowed = configuredStatuses(ctx);
|
|
16521
16710
|
const results = [];
|
|
16522
16711
|
for (const a of withAssignmentMd) {
|
|
16523
|
-
const path =
|
|
16712
|
+
const path = resolve42(a.assignmentDir, "assignment.md");
|
|
16524
16713
|
const parsed = await parseSafe(path);
|
|
16525
16714
|
if (!parsed) continue;
|
|
16526
16715
|
if (!allowed.has(parsed.status)) {
|
|
@@ -16553,7 +16742,7 @@ var workspaceMissing = {
|
|
|
16553
16742
|
const terminal = terminalStatuses(ctx);
|
|
16554
16743
|
const results = [];
|
|
16555
16744
|
for (const a of withAssignmentMd) {
|
|
16556
|
-
const path =
|
|
16745
|
+
const path = resolve42(a.assignmentDir, "assignment.md");
|
|
16557
16746
|
const parsed = await parseSafe(path);
|
|
16558
16747
|
if (!parsed) continue;
|
|
16559
16748
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -16600,12 +16789,12 @@ var requiredFilesByStatus = {
|
|
|
16600
16789
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
16601
16790
|
const results = [];
|
|
16602
16791
|
for (const a of withAssignmentMd) {
|
|
16603
|
-
const assignmentPath =
|
|
16792
|
+
const assignmentPath = resolve42(a.assignmentDir, "assignment.md");
|
|
16604
16793
|
const parsed = await parseSafe(assignmentPath);
|
|
16605
16794
|
if (!parsed) continue;
|
|
16606
16795
|
const missing = [];
|
|
16607
16796
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
16608
|
-
const handoffPath =
|
|
16797
|
+
const handoffPath = resolve42(a.assignmentDir, "handoff.md");
|
|
16609
16798
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
16610
16799
|
}
|
|
16611
16800
|
if (missing.length === 0) continue;
|
|
@@ -16615,7 +16804,7 @@ var requiredFilesByStatus = {
|
|
|
16615
16804
|
title: this.title,
|
|
16616
16805
|
status: "warn",
|
|
16617
16806
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
16618
|
-
affected: missing.map((m) =>
|
|
16807
|
+
affected: missing.map((m) => resolve42(a.assignmentDir, m)),
|
|
16619
16808
|
remediation: {
|
|
16620
16809
|
kind: "manual",
|
|
16621
16810
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -16638,7 +16827,7 @@ var companionFilesScaffolded = {
|
|
|
16638
16827
|
for (const a of withAssignmentMd) {
|
|
16639
16828
|
const missing = [];
|
|
16640
16829
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
16641
|
-
if (!await fileExists(
|
|
16830
|
+
if (!await fileExists(resolve42(a.assignmentDir, filename))) {
|
|
16642
16831
|
missing.push(filename);
|
|
16643
16832
|
}
|
|
16644
16833
|
}
|
|
@@ -16650,7 +16839,7 @@ var companionFilesScaffolded = {
|
|
|
16650
16839
|
title: this.title,
|
|
16651
16840
|
status: "warn",
|
|
16652
16841
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
16653
|
-
affected: missing.map((m) =>
|
|
16842
|
+
affected: missing.map((m) => resolve42(a.assignmentDir, m)),
|
|
16654
16843
|
remediation: {
|
|
16655
16844
|
kind: "manual",
|
|
16656
16845
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -16683,7 +16872,7 @@ var typeDefinition = {
|
|
|
16683
16872
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
16684
16873
|
const results = [];
|
|
16685
16874
|
for (const a of withAssignmentMd) {
|
|
16686
|
-
const path =
|
|
16875
|
+
const path = resolve42(a.assignmentDir, "assignment.md");
|
|
16687
16876
|
const parsed = await parseSafe(path);
|
|
16688
16877
|
if (!parsed) continue;
|
|
16689
16878
|
if (!parsed.type) continue;
|
|
@@ -16717,7 +16906,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
16717
16906
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
16718
16907
|
const results = [];
|
|
16719
16908
|
for (const a of withAssignmentMd) {
|
|
16720
|
-
const path =
|
|
16909
|
+
const path = resolve42(a.assignmentDir, "assignment.md");
|
|
16721
16910
|
const parsed = await parseSafe(path);
|
|
16722
16911
|
if (!parsed) continue;
|
|
16723
16912
|
if (a.standalone) {
|
|
@@ -16772,7 +16961,7 @@ var assignmentChecks = [
|
|
|
16772
16961
|
];
|
|
16773
16962
|
async function parseSafe(path) {
|
|
16774
16963
|
try {
|
|
16775
|
-
const content = await
|
|
16964
|
+
const content = await readFile26(path, "utf-8");
|
|
16776
16965
|
return parseAssignmentFull(content);
|
|
16777
16966
|
} catch {
|
|
16778
16967
|
return null;
|
|
@@ -16791,7 +16980,7 @@ function pass4(check, detail) {
|
|
|
16791
16980
|
|
|
16792
16981
|
// src/utils/doctor/checks/dashboard.ts
|
|
16793
16982
|
init_fs();
|
|
16794
|
-
import { resolve as
|
|
16983
|
+
import { resolve as resolve43 } from "path";
|
|
16795
16984
|
var CATEGORY5 = "dashboard";
|
|
16796
16985
|
var dbReachable = {
|
|
16797
16986
|
id: "dashboard.db-reachable",
|
|
@@ -16805,7 +16994,7 @@ var dbReachable = {
|
|
|
16805
16994
|
title: this.title,
|
|
16806
16995
|
status: "error",
|
|
16807
16996
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
16808
|
-
affected: [
|
|
16997
|
+
affected: [resolve43(ctx.syntaurRoot, "syntaur.db")],
|
|
16809
16998
|
remediation: {
|
|
16810
16999
|
kind: "manual",
|
|
16811
17000
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -16823,7 +17012,7 @@ var dbReachable = {
|
|
|
16823
17012
|
title: this.title,
|
|
16824
17013
|
status: "error",
|
|
16825
17014
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
16826
|
-
affected: [
|
|
17015
|
+
affected: [resolve43(ctx.syntaurRoot, "syntaur.db")],
|
|
16827
17016
|
autoFixable: false
|
|
16828
17017
|
};
|
|
16829
17018
|
}
|
|
@@ -16835,7 +17024,7 @@ var dbReachable = {
|
|
|
16835
17024
|
title: this.title,
|
|
16836
17025
|
status: "error",
|
|
16837
17026
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
16838
|
-
affected: [
|
|
17027
|
+
affected: [resolve43(ctx.syntaurRoot, "syntaur.db")],
|
|
16839
17028
|
autoFixable: false
|
|
16840
17029
|
};
|
|
16841
17030
|
}
|
|
@@ -16861,7 +17050,7 @@ var ghostSessions = {
|
|
|
16861
17050
|
const results = [];
|
|
16862
17051
|
for (const row of rows) {
|
|
16863
17052
|
if (!row.project_slug) continue;
|
|
16864
|
-
const projectPath =
|
|
17053
|
+
const projectPath = resolve43(projectsDir2, row.project_slug, "project.md");
|
|
16865
17054
|
if (!await fileExists(projectPath)) {
|
|
16866
17055
|
results.push({
|
|
16867
17056
|
id: this.id,
|
|
@@ -16880,7 +17069,7 @@ var ghostSessions = {
|
|
|
16880
17069
|
continue;
|
|
16881
17070
|
}
|
|
16882
17071
|
if (row.assignment_slug) {
|
|
16883
|
-
const assignmentPath =
|
|
17072
|
+
const assignmentPath = resolve43(
|
|
16884
17073
|
projectsDir2,
|
|
16885
17074
|
row.project_slug,
|
|
16886
17075
|
"assignments",
|
|
@@ -16932,7 +17121,9 @@ function skipped(check, reason) {
|
|
|
16932
17121
|
|
|
16933
17122
|
// src/utils/doctor/checks/integrations.ts
|
|
16934
17123
|
init_fs();
|
|
16935
|
-
import {
|
|
17124
|
+
import { resolve as resolve44, dirname as dirname12, basename as basename2 } from "path";
|
|
17125
|
+
import { readdir as readdir16, readFile as readFile27 } from "fs/promises";
|
|
17126
|
+
import { homedir as homedir6 } from "os";
|
|
16936
17127
|
var CATEGORY6 = "integrations";
|
|
16937
17128
|
var claudePluginLinked = {
|
|
16938
17129
|
id: "integrations.claude-plugin-linked",
|
|
@@ -17012,7 +17203,111 @@ var backupConfigured = {
|
|
|
17012
17203
|
};
|
|
17013
17204
|
}
|
|
17014
17205
|
};
|
|
17015
|
-
|
|
17206
|
+
async function readKnownMarketplaces() {
|
|
17207
|
+
const path = resolve44(homedir6(), ".claude", "plugins", "known_marketplaces.json");
|
|
17208
|
+
if (!await fileExists(path)) return {};
|
|
17209
|
+
try {
|
|
17210
|
+
const raw = await readFile27(path, "utf-8");
|
|
17211
|
+
return JSON.parse(raw);
|
|
17212
|
+
} catch {
|
|
17213
|
+
return {};
|
|
17214
|
+
}
|
|
17215
|
+
}
|
|
17216
|
+
var claudeMarketplaceRegistered = {
|
|
17217
|
+
id: "integrations.claude-marketplace-registered",
|
|
17218
|
+
category: CATEGORY6,
|
|
17219
|
+
title: "Claude marketplace containing syntaur is registered in known_marketplaces.json",
|
|
17220
|
+
async run(ctx) {
|
|
17221
|
+
const dir = ctx.config.integrations.claudePluginDir;
|
|
17222
|
+
if (!dir) return skipped2(this, "claudePluginDir not configured");
|
|
17223
|
+
if (!await fileExists(dir)) {
|
|
17224
|
+
return skipped2(this, "claudePluginDir does not exist (run install-plugin)");
|
|
17225
|
+
}
|
|
17226
|
+
const pluginsParent = dirname12(dir);
|
|
17227
|
+
if (basename2(pluginsParent) !== "plugins") {
|
|
17228
|
+
return {
|
|
17229
|
+
id: this.id,
|
|
17230
|
+
category: this.category,
|
|
17231
|
+
title: this.title,
|
|
17232
|
+
status: "error",
|
|
17233
|
+
detail: `claudePluginDir is ${dir}, which is not under a Claude marketplace (expected path of the form <marketplace-root>/plugins/syntaur). Claude Code will not show this plugin until it lives inside a marketplace.`,
|
|
17234
|
+
affected: [dir],
|
|
17235
|
+
remediation: {
|
|
17236
|
+
kind: "manual",
|
|
17237
|
+
suggestion: "Re-run install-plugin to relocate into a marketplace, or pass --target-dir <marketplace>/plugins/syntaur.",
|
|
17238
|
+
command: "syntaur install-plugin"
|
|
17239
|
+
},
|
|
17240
|
+
autoFixable: false
|
|
17241
|
+
};
|
|
17242
|
+
}
|
|
17243
|
+
const marketplaceRoot = dirname12(pluginsParent);
|
|
17244
|
+
const marketplaceManifest = resolve44(marketplaceRoot, ".claude-plugin", "marketplace.json");
|
|
17245
|
+
if (!await fileExists(marketplaceManifest)) {
|
|
17246
|
+
return {
|
|
17247
|
+
id: this.id,
|
|
17248
|
+
category: this.category,
|
|
17249
|
+
title: this.title,
|
|
17250
|
+
status: "error",
|
|
17251
|
+
detail: `${marketplaceManifest} does not exist \u2014 Claude won't see this plugin.`,
|
|
17252
|
+
affected: [marketplaceManifest],
|
|
17253
|
+
remediation: {
|
|
17254
|
+
kind: "manual",
|
|
17255
|
+
suggestion: "Re-run install-plugin to repair the marketplace files.",
|
|
17256
|
+
command: "syntaur install-plugin"
|
|
17257
|
+
},
|
|
17258
|
+
autoFixable: false
|
|
17259
|
+
};
|
|
17260
|
+
}
|
|
17261
|
+
let parsed = {};
|
|
17262
|
+
try {
|
|
17263
|
+
parsed = JSON.parse(await readFile27(marketplaceManifest, "utf-8"));
|
|
17264
|
+
} catch {
|
|
17265
|
+
return {
|
|
17266
|
+
id: this.id,
|
|
17267
|
+
category: this.category,
|
|
17268
|
+
title: this.title,
|
|
17269
|
+
status: "error",
|
|
17270
|
+
detail: `${marketplaceManifest} is not valid JSON.`,
|
|
17271
|
+
affected: [marketplaceManifest],
|
|
17272
|
+
autoFixable: false
|
|
17273
|
+
};
|
|
17274
|
+
}
|
|
17275
|
+
const marketplaceName = parsed.name ?? basename2(marketplaceRoot);
|
|
17276
|
+
const hasSyntaurEntry = (parsed.plugins ?? []).some((p) => p?.name === "syntaur");
|
|
17277
|
+
const known = await readKnownMarketplaces();
|
|
17278
|
+
const registered = known[marketplaceName]?.installLocation === marketplaceRoot || Object.values(known).some((v) => v.installLocation === marketplaceRoot);
|
|
17279
|
+
const issues = [];
|
|
17280
|
+
if (!hasSyntaurEntry) {
|
|
17281
|
+
issues.push(`marketplace.json at ${marketplaceManifest} does not list a "syntaur" plugin`);
|
|
17282
|
+
}
|
|
17283
|
+
if (!registered) {
|
|
17284
|
+
issues.push(
|
|
17285
|
+
`known_marketplaces.json does not register ${marketplaceName} \u2192 ${marketplaceRoot} (Claude will not show this plugin)`
|
|
17286
|
+
);
|
|
17287
|
+
}
|
|
17288
|
+
if (issues.length === 0) return pass6(this);
|
|
17289
|
+
return {
|
|
17290
|
+
id: this.id,
|
|
17291
|
+
category: this.category,
|
|
17292
|
+
title: this.title,
|
|
17293
|
+
status: "error",
|
|
17294
|
+
detail: issues.join("; "),
|
|
17295
|
+
affected: [marketplaceManifest, resolve44(homedir6(), ".claude", "plugins", "known_marketplaces.json")],
|
|
17296
|
+
remediation: {
|
|
17297
|
+
kind: "manual",
|
|
17298
|
+
suggestion: "Re-run install-plugin to ensure both files are in sync.",
|
|
17299
|
+
command: "syntaur install-plugin"
|
|
17300
|
+
},
|
|
17301
|
+
autoFixable: false
|
|
17302
|
+
};
|
|
17303
|
+
}
|
|
17304
|
+
};
|
|
17305
|
+
var integrationChecks = [
|
|
17306
|
+
claudePluginLinked,
|
|
17307
|
+
claudeMarketplaceRegistered,
|
|
17308
|
+
codexPluginLinked,
|
|
17309
|
+
backupConfigured
|
|
17310
|
+
];
|
|
17016
17311
|
function pass6(check) {
|
|
17017
17312
|
return {
|
|
17018
17313
|
id: check.id,
|
|
@@ -17037,8 +17332,8 @@ function skipped2(check, reason) {
|
|
|
17037
17332
|
init_fs();
|
|
17038
17333
|
init_parser();
|
|
17039
17334
|
init_types();
|
|
17040
|
-
import { resolve as
|
|
17041
|
-
import { readFile as
|
|
17335
|
+
import { resolve as resolve45 } from "path";
|
|
17336
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
17042
17337
|
var CATEGORY7 = "workspace";
|
|
17043
17338
|
var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
17044
17339
|
function hasAnyAssignmentField(ctx) {
|
|
@@ -17050,12 +17345,12 @@ function isStandaloneSession(ctx) {
|
|
|
17050
17345
|
return !hasAnyAssignmentField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
|
|
17051
17346
|
}
|
|
17052
17347
|
async function loadContext(ctx) {
|
|
17053
|
-
const path =
|
|
17348
|
+
const path = resolve45(ctx.cwd, ".syntaur", "context.json");
|
|
17054
17349
|
if (!await fileExists(path)) {
|
|
17055
17350
|
return { data: null, path, exists: false, parseError: null };
|
|
17056
17351
|
}
|
|
17057
17352
|
try {
|
|
17058
|
-
const raw = await
|
|
17353
|
+
const raw = await readFile28(path, "utf-8");
|
|
17059
17354
|
return { data: JSON.parse(raw), path, exists: true, parseError: null };
|
|
17060
17355
|
} catch (err2) {
|
|
17061
17356
|
return {
|
|
@@ -17130,7 +17425,7 @@ var contextAssignmentResolves = {
|
|
|
17130
17425
|
if (!exists) return skipped3(this, "no context to resolve");
|
|
17131
17426
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
17132
17427
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
17133
|
-
const assignmentMd =
|
|
17428
|
+
const assignmentMd = resolve45(data.assignmentDir, "assignment.md");
|
|
17134
17429
|
if (!await fileExists(assignmentMd)) {
|
|
17135
17430
|
return {
|
|
17136
17431
|
id: this.id,
|
|
@@ -17159,10 +17454,10 @@ var contextTerminal = {
|
|
|
17159
17454
|
if (!exists) return skipped3(this, "no context to check");
|
|
17160
17455
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
17161
17456
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
17162
|
-
const assignmentMd =
|
|
17457
|
+
const assignmentMd = resolve45(data.assignmentDir, "assignment.md");
|
|
17163
17458
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
17164
17459
|
try {
|
|
17165
|
-
const content = await
|
|
17460
|
+
const content = await readFile28(assignmentMd, "utf-8");
|
|
17166
17461
|
const parsed = parseAssignmentFull(content);
|
|
17167
17462
|
const terminal = terminalStatuses2(ctx);
|
|
17168
17463
|
if (terminal.has(parsed.status)) {
|
|
@@ -17305,6 +17600,86 @@ async function checkAgent(agent) {
|
|
|
17305
17600
|
}
|
|
17306
17601
|
var agentChecks = [agentsResolvable];
|
|
17307
17602
|
|
|
17603
|
+
// src/utils/doctor/checks/skills.ts
|
|
17604
|
+
init_fs();
|
|
17605
|
+
import { resolve as resolve46, join as join5 } from "path";
|
|
17606
|
+
import { readdir as readdir17, readFile as readFile29, lstat as lstat4 } from "fs/promises";
|
|
17607
|
+
import { homedir as homedir7 } from "os";
|
|
17608
|
+
var CATEGORY9 = "skills";
|
|
17609
|
+
var skillTargets = [
|
|
17610
|
+
{ agent: "claude", dir: resolve46(homedir7(), ".claude", "skills"), label: "~/.claude/skills" },
|
|
17611
|
+
{ agent: "codex", dir: resolve46(homedir7(), ".codex", "skills"), label: "~/.codex/skills" }
|
|
17612
|
+
];
|
|
17613
|
+
var skillsDedupCheck = {
|
|
17614
|
+
id: "skills.dedup",
|
|
17615
|
+
category: CATEGORY9,
|
|
17616
|
+
title: "Syntaur skills are not duplicated across install paths",
|
|
17617
|
+
async run() {
|
|
17618
|
+
const findings = [];
|
|
17619
|
+
const affected = [];
|
|
17620
|
+
for (const { agent, dir, label } of skillTargets) {
|
|
17621
|
+
if (!await fileExists(dir)) continue;
|
|
17622
|
+
const pluginEnabled = await isSyntaurPluginEnabledFor(agent);
|
|
17623
|
+
const present = [];
|
|
17624
|
+
let entries;
|
|
17625
|
+
try {
|
|
17626
|
+
entries = await readdir17(dir, { withFileTypes: true });
|
|
17627
|
+
} catch {
|
|
17628
|
+
continue;
|
|
17629
|
+
}
|
|
17630
|
+
for (const entry of entries) {
|
|
17631
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
17632
|
+
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
17633
|
+
const skillMd = join5(dir, entry.name, "SKILL.md");
|
|
17634
|
+
if (!await fileExists(skillMd)) continue;
|
|
17635
|
+
const content = await readFile29(skillMd, "utf-8").catch(() => "");
|
|
17636
|
+
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
17637
|
+
if (!match || match[1] !== entry.name) continue;
|
|
17638
|
+
let isSymlink2 = false;
|
|
17639
|
+
try {
|
|
17640
|
+
isSymlink2 = (await lstat4(join5(dir, entry.name))).isSymbolicLink();
|
|
17641
|
+
} catch {
|
|
17642
|
+
}
|
|
17643
|
+
present.push({ name: entry.name, isSymlink: isSymlink2 });
|
|
17644
|
+
}
|
|
17645
|
+
if (present.length === 0) continue;
|
|
17646
|
+
if (pluginEnabled) {
|
|
17647
|
+
const nonSymlink = present.filter((p) => !p.isSymlink);
|
|
17648
|
+
if (nonSymlink.length > 0) {
|
|
17649
|
+
findings.push(
|
|
17650
|
+
`${label}: ${nonSymlink.length} syntaur skill(s) installed globally while the syntaur plugin is enabled (${agent}) \u2014 duplicate registrations`
|
|
17651
|
+
);
|
|
17652
|
+
for (const p of nonSymlink) affected.push(join5(dir, p.name));
|
|
17653
|
+
}
|
|
17654
|
+
}
|
|
17655
|
+
}
|
|
17656
|
+
if (findings.length === 0) {
|
|
17657
|
+
return {
|
|
17658
|
+
id: this.id,
|
|
17659
|
+
category: this.category,
|
|
17660
|
+
title: this.title,
|
|
17661
|
+
status: "pass",
|
|
17662
|
+
autoFixable: false
|
|
17663
|
+
};
|
|
17664
|
+
}
|
|
17665
|
+
return {
|
|
17666
|
+
id: this.id,
|
|
17667
|
+
category: this.category,
|
|
17668
|
+
title: this.title,
|
|
17669
|
+
status: "warn",
|
|
17670
|
+
detail: findings.join("; "),
|
|
17671
|
+
affected,
|
|
17672
|
+
remediation: {
|
|
17673
|
+
kind: "manual",
|
|
17674
|
+
suggestion: "Either disable the plugin or remove the global skill copies. Recommended: keep the plugin path and remove the global copies via `syntaur uninstall --skills` (preserves user-authored skills with non-syntaur frontmatter).",
|
|
17675
|
+
command: null
|
|
17676
|
+
},
|
|
17677
|
+
autoFixable: false
|
|
17678
|
+
};
|
|
17679
|
+
}
|
|
17680
|
+
};
|
|
17681
|
+
var skillsChecks = [skillsDedupCheck];
|
|
17682
|
+
|
|
17308
17683
|
// src/utils/doctor/registry.ts
|
|
17309
17684
|
function allChecks() {
|
|
17310
17685
|
return [
|
|
@@ -17315,7 +17690,8 @@ function allChecks() {
|
|
|
17315
17690
|
...dashboardChecks,
|
|
17316
17691
|
...integrationChecks,
|
|
17317
17692
|
...workspaceChecks,
|
|
17318
|
-
...agentChecks
|
|
17693
|
+
...agentChecks,
|
|
17694
|
+
...skillsChecks
|
|
17319
17695
|
];
|
|
17320
17696
|
}
|
|
17321
17697
|
|
|
@@ -17397,11 +17773,11 @@ async function finalize(checks) {
|
|
|
17397
17773
|
}
|
|
17398
17774
|
async function readVersion() {
|
|
17399
17775
|
try {
|
|
17400
|
-
const here =
|
|
17776
|
+
const here = fileURLToPath6(import.meta.url);
|
|
17401
17777
|
let dir = dirname13(here);
|
|
17402
17778
|
for (let i = 0; i < 6; i++) {
|
|
17403
17779
|
try {
|
|
17404
|
-
const raw = await
|
|
17780
|
+
const raw = await readFile30(join6(dir, "package.json"), "utf-8");
|
|
17405
17781
|
const parsed = JSON.parse(raw);
|
|
17406
17782
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
17407
17783
|
} catch {
|
|
@@ -17749,8 +18125,8 @@ init_uuid();
|
|
|
17749
18125
|
init_timestamp();
|
|
17750
18126
|
init_assignment_resolver();
|
|
17751
18127
|
init_templates();
|
|
17752
|
-
import { resolve as
|
|
17753
|
-
import { readFile as
|
|
18128
|
+
import { resolve as resolve47 } from "path";
|
|
18129
|
+
import { readFile as readFile31 } from "fs/promises";
|
|
17754
18130
|
function shortId() {
|
|
17755
18131
|
return generateId().split("-")[0];
|
|
17756
18132
|
}
|
|
@@ -17780,7 +18156,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
17780
18156
|
if (!isValidSlug(target)) {
|
|
17781
18157
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
17782
18158
|
}
|
|
17783
|
-
assignmentDir =
|
|
18159
|
+
assignmentDir = resolve47(baseDir, options.project, "assignments", target);
|
|
17784
18160
|
assignmentRef = target;
|
|
17785
18161
|
} else {
|
|
17786
18162
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -17790,13 +18166,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
17790
18166
|
assignmentDir = resolved.assignmentDir;
|
|
17791
18167
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
17792
18168
|
}
|
|
17793
|
-
const commentsPath =
|
|
18169
|
+
const commentsPath = resolve47(assignmentDir, "comments.md");
|
|
17794
18170
|
const timestamp = nowTimestamp();
|
|
17795
18171
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
17796
18172
|
let currentContent;
|
|
17797
18173
|
let currentCount = 0;
|
|
17798
18174
|
if (await fileExists(commentsPath)) {
|
|
17799
|
-
currentContent = await
|
|
18175
|
+
currentContent = await readFile31(commentsPath, "utf-8");
|
|
17800
18176
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
17801
18177
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
17802
18178
|
} else {
|
|
@@ -17837,8 +18213,8 @@ init_slug();
|
|
|
17837
18213
|
init_timestamp();
|
|
17838
18214
|
init_assignment_resolver();
|
|
17839
18215
|
init_assignment_todos();
|
|
17840
|
-
import { resolve as
|
|
17841
|
-
import { readFile as
|
|
18216
|
+
import { resolve as resolve48 } from "path";
|
|
18217
|
+
import { readFile as readFile32 } from "fs/promises";
|
|
17842
18218
|
async function requestCommand(target, text, options = {}) {
|
|
17843
18219
|
if (!text || !text.trim()) {
|
|
17844
18220
|
throw new Error("Request text cannot be empty.");
|
|
@@ -17854,7 +18230,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
17854
18230
|
if (!isValidSlug(target)) {
|
|
17855
18231
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
17856
18232
|
}
|
|
17857
|
-
assignmentDir =
|
|
18233
|
+
assignmentDir = resolve48(baseDir, options.project, "assignments", target);
|
|
17858
18234
|
targetRef = target;
|
|
17859
18235
|
} else {
|
|
17860
18236
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -17864,12 +18240,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
17864
18240
|
assignmentDir = resolved.assignmentDir;
|
|
17865
18241
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
17866
18242
|
}
|
|
17867
|
-
const assignmentMdPath =
|
|
18243
|
+
const assignmentMdPath = resolve48(assignmentDir, "assignment.md");
|
|
17868
18244
|
if (!await fileExists(assignmentMdPath)) {
|
|
17869
18245
|
throw new Error(`assignment.md not found at ${assignmentMdPath}`);
|
|
17870
18246
|
}
|
|
17871
18247
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
17872
|
-
let content = await
|
|
18248
|
+
let content = await readFile32(assignmentMdPath, "utf-8");
|
|
17873
18249
|
content = appendTodosToAssignmentBody(content, [
|
|
17874
18250
|
{ description: `${text.trim()} (from: ${source})` }
|
|
17875
18251
|
]);
|
|
@@ -17880,10 +18256,10 @@ async function requestCommand(target, text, options = {}) {
|
|
|
17880
18256
|
|
|
17881
18257
|
// src/cli-default-command.ts
|
|
17882
18258
|
init_config2();
|
|
17883
|
-
import { readdir as
|
|
18259
|
+
import { readdir as readdir18 } from "fs/promises";
|
|
17884
18260
|
async function hasAnyProjectContent(projectsDir2) {
|
|
17885
18261
|
try {
|
|
17886
|
-
const entries = await
|
|
18262
|
+
const entries = await readdir18(projectsDir2, { withFileTypes: true });
|
|
17887
18263
|
return entries.some((entry) => entry.isDirectory());
|
|
17888
18264
|
} catch {
|
|
17889
18265
|
return false;
|
|
@@ -17919,21 +18295,21 @@ async function getDefaultCommandName() {
|
|
|
17919
18295
|
// src/utils/npx-prompt.ts
|
|
17920
18296
|
init_paths();
|
|
17921
18297
|
init_fs();
|
|
17922
|
-
import { fileURLToPath as
|
|
17923
|
-
import { readFile as
|
|
17924
|
-
import { dirname as dirname15, join as
|
|
18298
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
18299
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
18300
|
+
import { dirname as dirname15, join as join8, resolve as resolve49 } from "path";
|
|
17925
18301
|
import { spawn as spawn4 } from "child_process";
|
|
17926
18302
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
17927
18303
|
|
|
17928
18304
|
// src/utils/version.ts
|
|
17929
|
-
import { fileURLToPath as
|
|
17930
|
-
import { readFile as
|
|
17931
|
-
import { dirname as dirname14, join as
|
|
18305
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
18306
|
+
import { readFile as readFile33 } from "fs/promises";
|
|
18307
|
+
import { dirname as dirname14, join as join7 } from "path";
|
|
17932
18308
|
async function readPackageVersion(scriptUrl) {
|
|
17933
18309
|
try {
|
|
17934
|
-
const scriptPath =
|
|
18310
|
+
const scriptPath = fileURLToPath7(scriptUrl);
|
|
17935
18311
|
const pkgRoot = dirname14(dirname14(scriptPath));
|
|
17936
|
-
const raw = await
|
|
18312
|
+
const raw = await readFile33(join7(pkgRoot, "package.json"), "utf-8");
|
|
17937
18313
|
const parsed = JSON.parse(raw);
|
|
17938
18314
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
17939
18315
|
} catch {
|
|
@@ -17942,13 +18318,13 @@ async function readPackageVersion(scriptUrl) {
|
|
|
17942
18318
|
}
|
|
17943
18319
|
|
|
17944
18320
|
// src/utils/npx-prompt.ts
|
|
17945
|
-
var STATE_FILE =
|
|
18321
|
+
var STATE_FILE = resolve49(syntaurRoot(), "npx-install.json");
|
|
17946
18322
|
var META_ARGS = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
|
|
17947
18323
|
var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
|
|
17948
18324
|
function isRunningViaNpx(scriptUrl) {
|
|
17949
18325
|
let scriptPath;
|
|
17950
18326
|
try {
|
|
17951
|
-
scriptPath =
|
|
18327
|
+
scriptPath = fileURLToPath8(scriptUrl);
|
|
17952
18328
|
} catch {
|
|
17953
18329
|
return false;
|
|
17954
18330
|
}
|
|
@@ -17963,7 +18339,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
17963
18339
|
async function readState() {
|
|
17964
18340
|
if (!await fileExists(STATE_FILE)) return null;
|
|
17965
18341
|
try {
|
|
17966
|
-
const raw = await
|
|
18342
|
+
const raw = await readFile34(STATE_FILE, "utf-8");
|
|
17967
18343
|
return JSON.parse(raw);
|
|
17968
18344
|
} catch {
|
|
17969
18345
|
return null;
|
|
@@ -17977,7 +18353,7 @@ async function resolveNpmBin() {
|
|
|
17977
18353
|
const nodeDir = dirname15(process.execPath);
|
|
17978
18354
|
const isWin = process.platform === "win32";
|
|
17979
18355
|
const npmName = isWin ? "npm.cmd" : "npm";
|
|
17980
|
-
const nearNode =
|
|
18356
|
+
const nearNode = join8(nodeDir, npmName);
|
|
17981
18357
|
if (await fileExists(nearNode)) {
|
|
17982
18358
|
return { cmd: nearNode, shell: false };
|
|
17983
18359
|
}
|
|
@@ -18020,9 +18396,9 @@ async function readGlobalVersion() {
|
|
|
18020
18396
|
});
|
|
18021
18397
|
if (!rootPath) return null;
|
|
18022
18398
|
try {
|
|
18023
|
-
const manifestPath =
|
|
18399
|
+
const manifestPath = join8(rootPath, "syntaur", "package.json");
|
|
18024
18400
|
if (!await fileExists(manifestPath)) return null;
|
|
18025
|
-
const raw = await
|
|
18401
|
+
const raw = await readFile34(manifestPath, "utf-8");
|
|
18026
18402
|
const parsed = JSON.parse(raw);
|
|
18027
18403
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
18028
18404
|
} catch {
|
|
@@ -18317,7 +18693,7 @@ program.command("setup").description("Initialize Syntaur and optionally install
|
|
|
18317
18693
|
process.exit(1);
|
|
18318
18694
|
}
|
|
18319
18695
|
});
|
|
18320
|
-
program.command("install-plugin").description("Install the Syntaur Claude Code plugin").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.claude/skills").option("--skip-skills", "Do not install protocol skills into ~/.claude/skills").action(async (options) => {
|
|
18696
|
+
program.command("install-plugin").description("Install the Syntaur Claude Code plugin").option("--force", "Overwrite an existing Syntaur-managed install").option("--target-dir <path>", "Install the plugin at a specific directory").option("--link", "Use a symlink instead of copying files (repo-local dev only)").option("--force-skills", "Overwrite user-edited skills in ~/.claude/skills").option("--skip-skills", "Do not install protocol skills into ~/.claude/skills").option("--enable", "Enable the plugin in ~/.claude/settings.json after install").action(async (options) => {
|
|
18321
18697
|
try {
|
|
18322
18698
|
await installPluginCommand({ ...options, promptForTarget: true });
|
|
18323
18699
|
} catch (error) {
|