syntaur 0.16.2 → 0.17.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/dashboard/dist/assets/{_basePickBy-DTuHiZCP.js → _basePickBy-Dek5-ACb.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-BjbISCNN.js → _baseUniq-BeLddDKU.js} +1 -1
- package/dashboard/dist/assets/{arc-D9mCa8If.js → arc-kotdj4Uh.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-CLWheiZu.js → architectureDiagram-2XIMDMQ5-BaqFXQ-q.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-BBxrVTWB.js → blockDiagram-WCTKOSBZ-BSyGne6h.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-DnhDZ2W3.js → c4Diagram-IC4MRINW-iYjZFKOW.js} +1 -1
- package/dashboard/dist/assets/channel-eXdj7ayV.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-DeAfVeDa.js → chunk-4BX2VUAB-DvY9Y7jB.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-cKEeGDNI.js → chunk-55IACEB6-2vCDm2cJ.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-7RuZ6HEq.js → chunk-FMBD7UC4-BVPaoRtV.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-B5dwG0bv.js → chunk-JSJVCQXG-B9uJpMzd.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-DeyQYDVj.js → chunk-KX2RTZJC-CHcjjGaq.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-NYo4hSqA.js → chunk-NQ4KR5QH-B8n19pSs.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-CtJFICF8.js → chunk-QZHKN3VN-CF9ZYFGK.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-DZ3WYdab.js → chunk-WL4C6EOR-mY_1Vmdf.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-B-bBQACr.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-B-bBQACr.js +1 -0
- package/dashboard/dist/assets/clone-CL04pv9J.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-DaYOsf-M.js → cose-bilkent-S5V4N54A-DOYoG3gB.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-DzLc7ykF.js → dagre-KLK3FWXG-CZFrp-Yq.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-kpjEdOqb.js → diagram-E7M64L7V-F3rF5YwM.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-D3EHoh6j.js → diagram-IFDJBPK2-BynWv-Vo.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-Cp6Un2ys.js → diagram-P4PSJMXO-NIyzhm_M.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-DDjOUPWk.js → erDiagram-INFDFZHY-Ca9NbihE.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-CfO51xge.js → flowDiagram-PKNHOUZH-YhS0MOu4.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-BDzAtkcp.js → ganttDiagram-A5KZAMGK-G7P3qq_2.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-JPMFN2zF.js → gitGraphDiagram-K3NZZRJ6-BJAK7M7E.js} +1 -1
- package/dashboard/dist/assets/{graph-DXDryilu.js → graph-11geMy3f.js} +1 -1
- package/dashboard/dist/assets/index--qwybK3W.js +535 -0
- package/dashboard/dist/assets/index-DwrhlK8r.css +1 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-CYi5kkvz.js → infoDiagram-LFFYTUFH-COcQYcLf.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-DQl_IUe8.js → ishikawaDiagram-PHBUUO56-B7Jjyw8J.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-BXXGPcrS.js → journeyDiagram-4ABVD52K--snU-QAX.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-BqJestUY.js → kanban-definition-K7BYSVSG-BucLoc89.js} +1 -1
- package/dashboard/dist/assets/{layout-D5VdYmWn.js → layout-Bcgxdz8A.js} +1 -1
- package/dashboard/dist/assets/{linear-dZA_O_GN.js → linear-CRF8xwvk.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-B0Ixd1yP.js → mermaid.core-D5JD5uYg.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-CSJYdSMG.js → mindmap-definition-YRQLILUH-BqGcTcbk.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-DmYrRZHN.js → pieDiagram-SKSYHLDU-CnQ2EkyX.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-La3ce5kE.js → quadrantDiagram-337W2JSQ-CKCfmTRc.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-DPitIZQl.js → requirementDiagram-Z7DCOOCP-B9J4bvUK.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CAmCqGkr.js → sankeyDiagram-WA2Y5GQK-CMSzI5rg.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-6lEzNTWG.js → sequenceDiagram-2WXFIKYE-BFQDtxd0.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-DyGKCS2C.js → stateDiagram-RAJIS63D-DgWYKT02.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-Nmwft4cR.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-D9RxQE3j.js → timeline-definition-YZTLITO2-jANu0e7M.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-TuXbGNN4.js → treemap-KZPCXAKY-BFjWMkyM.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-Bly5knr-.js → vennDiagram-LZ73GAT5-DGwrAJYj.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-CA-5z7-4.js → xychartDiagram-JWTSCODW-BLCPfoFM.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +2220 -479
- package/dist/dashboard/server.js.map +1 -1
- package/dist/db/leases-db.js.map +1 -1
- package/dist/index.js +4499 -1127
- package/dist/index.js.map +1 -1
- package/dist/launch/index.js +127 -63
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/claude-code/skills/bundle-worktree/SKILL.md +107 -0
- package/platforms/claude-code/skills/complete-bundle/SKILL.md +113 -0
- package/platforms/claude-code/skills/grab-bundle/SKILL.md +111 -0
- package/platforms/claude-code/skills/plan-bundle/SKILL.md +88 -0
- package/platforms/codex/skills/bundle-worktree/SKILL.md +107 -0
- package/platforms/codex/skills/complete-bundle/SKILL.md +113 -0
- package/platforms/codex/skills/grab-bundle/SKILL.md +111 -0
- package/platforms/codex/skills/plan-bundle/SKILL.md +88 -0
- package/skills/bundle-worktree/SKILL.md +107 -0
- package/skills/complete-bundle/SKILL.md +113 -0
- package/skills/grab-bundle/SKILL.md +111 -0
- package/skills/plan-bundle/SKILL.md +88 -0
- package/dashboard/dist/assets/channel-CN5VmjNa.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-CqLb9GuU.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-CqLb9GuU.js +0 -1
- package/dashboard/dist/assets/clone-DwvrjCQs.js +0 -1
- package/dashboard/dist/assets/index-D7UtkCYM.js +0 -515
- package/dashboard/dist/assets/index-DTXSWSzH.css +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-BOmRICoY.js +0 -1
package/dist/launch/index.js
CHANGED
|
@@ -887,7 +887,20 @@ async function readConfig() {
|
|
|
887
887
|
};
|
|
888
888
|
}
|
|
889
889
|
function getAgents(config) {
|
|
890
|
-
|
|
890
|
+
if (config.agents === null) return BUILTIN_AGENTS;
|
|
891
|
+
const builtinById = new Map(BUILTIN_AGENTS.map((a) => [a.id, a]));
|
|
892
|
+
return config.agents.map((agent) => {
|
|
893
|
+
const builtin = builtinById.get(agent.id);
|
|
894
|
+
if (!builtin) return agent;
|
|
895
|
+
const resume = agent.resume ?? builtin.resume;
|
|
896
|
+
const fork = agent.fork ?? builtin.fork;
|
|
897
|
+
if (resume === agent.resume && fork === agent.fork) return agent;
|
|
898
|
+
return {
|
|
899
|
+
...agent,
|
|
900
|
+
...resume ? { resume } : {},
|
|
901
|
+
...fork ? { fork } : {}
|
|
902
|
+
};
|
|
903
|
+
});
|
|
891
904
|
}
|
|
892
905
|
function parseTerminalConfig(value) {
|
|
893
906
|
if (value === void 0 || value === null || value === "") return null;
|
|
@@ -1333,7 +1346,7 @@ function buildTransitionTable(transitions) {
|
|
|
1333
1346
|
return table;
|
|
1334
1347
|
}
|
|
1335
1348
|
function getTargetStatus(_from, command, table) {
|
|
1336
|
-
if (!table
|
|
1349
|
+
if (!table) {
|
|
1337
1350
|
return DEFAULT_COMMAND_TARGETS.get(command) ?? null;
|
|
1338
1351
|
}
|
|
1339
1352
|
return table.get(command) ?? table.get(`${_from}:${command}`) ?? null;
|
|
@@ -1715,6 +1728,10 @@ var init_overviewCopy = __esm({
|
|
|
1715
1728
|
// src/dashboard/api.ts
|
|
1716
1729
|
import { readdir as readdir6, readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
|
|
1717
1730
|
import { resolve as resolve11, dirname as dirname2, basename } from "path";
|
|
1731
|
+
function accumulatePhase(traces, label, ms) {
|
|
1732
|
+
if (!traces) return;
|
|
1733
|
+
traces.subPhases.set(label, (traces.subPhases.get(label) ?? 0) + ms);
|
|
1734
|
+
}
|
|
1718
1735
|
async function listStandaloneRecords(assignmentsDir) {
|
|
1719
1736
|
if (!assignmentsDir) return [];
|
|
1720
1737
|
if (!await fileExists(assignmentsDir)) return [];
|
|
@@ -1759,12 +1776,17 @@ async function getStatusConfig() {
|
|
|
1759
1776
|
const terminalSet = new Set(
|
|
1760
1777
|
config.statuses.statuses.filter((s) => s.terminal).map((s) => s.id)
|
|
1761
1778
|
);
|
|
1779
|
+
const hasCustomTransitions = config.statuses.transitions.length > 0;
|
|
1780
|
+
const effectiveTransitions = hasCustomTransitions ? config.statuses.transitions : Array.from(DEFAULT_TRANSITION_TABLE.entries()).map(([key, to]) => {
|
|
1781
|
+
const [from, command] = key.split(":");
|
|
1782
|
+
return { from, command, to };
|
|
1783
|
+
});
|
|
1762
1784
|
_cachedConfig = {
|
|
1763
1785
|
custom: true,
|
|
1764
1786
|
statuses: config.statuses.statuses,
|
|
1765
1787
|
order: config.statuses.order,
|
|
1766
|
-
transitions:
|
|
1767
|
-
transitionTable: buildTransitionTable(
|
|
1788
|
+
transitions: effectiveTransitions,
|
|
1789
|
+
transitionTable: buildTransitionTable(effectiveTransitions),
|
|
1768
1790
|
terminalStatuses: terminalSet.size > 0 ? terminalSet : /* @__PURE__ */ new Set(["completed", "failed"])
|
|
1769
1791
|
};
|
|
1770
1792
|
} else {
|
|
@@ -1792,16 +1814,17 @@ async function getStandaloneAvailableTransitions(assignment) {
|
|
|
1792
1814
|
const transitionDefs = getTransitionDefinitions(config);
|
|
1793
1815
|
const actions = [];
|
|
1794
1816
|
for (const definition of transitionDefs) {
|
|
1817
|
+
const target = getTargetStatus(assignment.status, definition.command, config.transitionTable);
|
|
1818
|
+
if (target === null) continue;
|
|
1795
1819
|
let warning = null;
|
|
1796
1820
|
if (definition.command === "start" && !assignment.assignee) {
|
|
1797
1821
|
warning = "No assignee set \u2014 consider assigning before starting.";
|
|
1798
1822
|
}
|
|
1799
|
-
const target = getTargetStatus(assignment.status, definition.command, config.transitionTable);
|
|
1800
1823
|
actions.push({
|
|
1801
1824
|
command: definition.command,
|
|
1802
1825
|
label: definition.label,
|
|
1803
1826
|
description: definition.description,
|
|
1804
|
-
targetStatus: target
|
|
1827
|
+
targetStatus: target,
|
|
1805
1828
|
disabled: false,
|
|
1806
1829
|
disabledReason: null,
|
|
1807
1830
|
warning,
|
|
@@ -1895,6 +1918,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
|
|
|
1895
1918
|
slug: assignment.slug || assignmentSlug,
|
|
1896
1919
|
title: assignment.title,
|
|
1897
1920
|
status: assignment.status,
|
|
1921
|
+
type: assignment.type,
|
|
1898
1922
|
priority: assignment.priority,
|
|
1899
1923
|
assignee: assignment.assignee,
|
|
1900
1924
|
dependsOn: assignment.dependsOn,
|
|
@@ -2143,6 +2167,7 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
2143
2167
|
slug: assignment.slug || resolved.id,
|
|
2144
2168
|
title: assignment.title,
|
|
2145
2169
|
status: assignment.status,
|
|
2170
|
+
type: assignment.type,
|
|
2146
2171
|
priority: assignment.priority,
|
|
2147
2172
|
assignee: assignment.assignee,
|
|
2148
2173
|
dependsOn: [],
|
|
@@ -2169,7 +2194,7 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
2169
2194
|
};
|
|
2170
2195
|
return detail;
|
|
2171
2196
|
}
|
|
2172
|
-
async function listProjectRecords(projectsDir) {
|
|
2197
|
+
async function listProjectRecords(projectsDir, traces) {
|
|
2173
2198
|
if (!await fileExists(projectsDir)) {
|
|
2174
2199
|
return [];
|
|
2175
2200
|
}
|
|
@@ -2179,61 +2204,75 @@ async function listProjectRecords(projectsDir) {
|
|
|
2179
2204
|
}
|
|
2180
2205
|
const entries = await readdir6(projectsDir, { withFileTypes: true });
|
|
2181
2206
|
const projectDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
2182
|
-
const
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
}
|
|
2189
|
-
const projectContent = await readFile8(projectMdPath, "utf-8");
|
|
2190
|
-
const project = parseProject(projectContent);
|
|
2191
|
-
const assignments = await listAssignmentRecords(projectPath);
|
|
2192
|
-
const rollup = await buildProjectRollup(projectPath, project, assignments);
|
|
2193
|
-
const updated = getProjectActivityTimestamp(project.updated, assignments);
|
|
2194
|
-
records.push({
|
|
2195
|
-
projectPath,
|
|
2196
|
-
project,
|
|
2197
|
-
assignments,
|
|
2198
|
-
dependencyGraph: await loadDependencyGraph(projectPath, assignments),
|
|
2199
|
-
summary: {
|
|
2200
|
-
slug: project.slug || entry.name,
|
|
2201
|
-
title: project.title,
|
|
2202
|
-
status: rollup.status,
|
|
2203
|
-
statusOverride: project.statusOverride,
|
|
2204
|
-
archived: project.archived,
|
|
2205
|
-
archivedAt: project.archivedAt,
|
|
2206
|
-
archivedReason: project.archivedReason,
|
|
2207
|
-
created: project.created,
|
|
2208
|
-
updated,
|
|
2209
|
-
tags: project.tags,
|
|
2210
|
-
progress: rollup.progress,
|
|
2211
|
-
needsAttention: rollup.needsAttention,
|
|
2212
|
-
workspace: project.workspace
|
|
2207
|
+
const maybeRecords = await Promise.all(
|
|
2208
|
+
projectDirs.map(async (entry) => {
|
|
2209
|
+
const projectPath = resolve11(projectsDir, entry.name);
|
|
2210
|
+
const projectMdPath = resolve11(projectPath, "project.md");
|
|
2211
|
+
if (!await fileExists(projectMdPath)) {
|
|
2212
|
+
return null;
|
|
2213
2213
|
}
|
|
2214
|
-
|
|
2215
|
-
|
|
2214
|
+
const t0 = traces ? performance.now() : 0;
|
|
2215
|
+
const projectContent = await readFile8(projectMdPath, "utf-8");
|
|
2216
|
+
const project = parseProject(projectContent);
|
|
2217
|
+
if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
|
|
2218
|
+
const t1 = traces ? performance.now() : 0;
|
|
2219
|
+
const assignments = await listAssignmentRecords(projectPath, traces);
|
|
2220
|
+
if (traces) accumulatePhase(traces, "list-assignments", performance.now() - t1);
|
|
2221
|
+
const t2 = traces ? performance.now() : 0;
|
|
2222
|
+
const rollup = await buildProjectRollup(projectPath, project, assignments, traces);
|
|
2223
|
+
if (traces) accumulatePhase(traces, "build-rollup", performance.now() - t2);
|
|
2224
|
+
const updated = getProjectActivityTimestamp(project.updated, assignments);
|
|
2225
|
+
const t3 = traces ? performance.now() : 0;
|
|
2226
|
+
const dependencyGraph = await loadDependencyGraph(projectPath, assignments);
|
|
2227
|
+
if (traces) accumulatePhase(traces, "load-dep-graph", performance.now() - t3);
|
|
2228
|
+
return {
|
|
2229
|
+
projectPath,
|
|
2230
|
+
project,
|
|
2231
|
+
assignments,
|
|
2232
|
+
dependencyGraph,
|
|
2233
|
+
summary: {
|
|
2234
|
+
slug: project.slug || entry.name,
|
|
2235
|
+
title: project.title,
|
|
2236
|
+
status: rollup.status,
|
|
2237
|
+
statusOverride: project.statusOverride,
|
|
2238
|
+
archived: project.archived,
|
|
2239
|
+
archivedAt: project.archivedAt,
|
|
2240
|
+
archivedReason: project.archivedReason,
|
|
2241
|
+
created: project.created,
|
|
2242
|
+
updated,
|
|
2243
|
+
tags: project.tags,
|
|
2244
|
+
progress: rollup.progress,
|
|
2245
|
+
needsAttention: rollup.needsAttention,
|
|
2246
|
+
workspace: project.workspace
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
})
|
|
2250
|
+
);
|
|
2251
|
+
const records = maybeRecords.filter((r) => r !== null);
|
|
2216
2252
|
records.sort((left, right) => compareTimestamps(right.summary.updated, left.summary.updated));
|
|
2217
2253
|
return records;
|
|
2218
2254
|
}
|
|
2219
|
-
async function listAssignmentRecords(projectPath) {
|
|
2255
|
+
async function listAssignmentRecords(projectPath, traces) {
|
|
2220
2256
|
const assignmentsDir = resolve11(projectPath, "assignments");
|
|
2221
2257
|
if (!await fileExists(assignmentsDir)) {
|
|
2222
2258
|
return [];
|
|
2223
2259
|
}
|
|
2224
2260
|
const entries = await readdir6(assignmentsDir, { withFileTypes: true });
|
|
2225
|
-
const
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2261
|
+
const dirEntries = entries.filter((entry) => entry.isDirectory());
|
|
2262
|
+
const maybeRecords = await Promise.all(
|
|
2263
|
+
dirEntries.map(async (entry) => {
|
|
2264
|
+
const assignmentMd = resolve11(assignmentsDir, entry.name, "assignment.md");
|
|
2265
|
+
if (!await fileExists(assignmentMd)) {
|
|
2266
|
+
return null;
|
|
2267
|
+
}
|
|
2268
|
+
const t0 = traces ? performance.now() : 0;
|
|
2269
|
+
const content = await readFile8(assignmentMd, "utf-8");
|
|
2270
|
+
const parsed = parseAssignmentFull(content);
|
|
2271
|
+
if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
|
|
2272
|
+
return parsed;
|
|
2273
|
+
})
|
|
2274
|
+
);
|
|
2275
|
+
const records = maybeRecords.filter((r) => r !== null);
|
|
2237
2276
|
records.sort((left, right) => compareTimestamps(right.updated, left.updated));
|
|
2238
2277
|
return records;
|
|
2239
2278
|
}
|
|
@@ -2249,13 +2288,20 @@ async function loadDependencyGraph(projectPath, assignments) {
|
|
|
2249
2288
|
}
|
|
2250
2289
|
return buildDependencyGraph(assignments);
|
|
2251
2290
|
}
|
|
2252
|
-
async function buildProjectRollup(projectPath, project, assignments) {
|
|
2291
|
+
async function buildProjectRollup(projectPath, project, assignments, traces) {
|
|
2253
2292
|
const progress = { total: assignments.length };
|
|
2293
|
+
const perAssignment = await Promise.all(
|
|
2294
|
+
assignments.map(async (assignment) => {
|
|
2295
|
+
const t0 = traces ? performance.now() : 0;
|
|
2296
|
+
const openQuestions2 = await countOpenQuestions(projectPath, assignment.slug);
|
|
2297
|
+
if (traces) accumulatePhase(traces, "count-open-questions", performance.now() - t0);
|
|
2298
|
+
return { status: assignment.status, openQuestions: openQuestions2 };
|
|
2299
|
+
})
|
|
2300
|
+
);
|
|
2254
2301
|
let openQuestions = 0;
|
|
2255
|
-
for (const
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
openQuestions += await countOpenQuestions(projectPath, assignment.slug);
|
|
2302
|
+
for (const entry of perAssignment) {
|
|
2303
|
+
progress[entry.status] = (progress[entry.status] ?? 0) + 1;
|
|
2304
|
+
openQuestions += entry.openQuestions;
|
|
2259
2305
|
}
|
|
2260
2306
|
const needsAttention = {
|
|
2261
2307
|
blockedCount: progress["blocked"] ?? 0,
|
|
@@ -2308,28 +2354,37 @@ function buildDependencyGraph(assignments) {
|
|
|
2308
2354
|
function findAssignmentStatus(assignments, slug) {
|
|
2309
2355
|
return assignments.find((assignment) => assignment.slug === slug)?.status ?? "pending";
|
|
2310
2356
|
}
|
|
2311
|
-
async function getAvailableTransitions(projectsDir, projectSlug, assignmentSlug, assignment) {
|
|
2357
|
+
async function getAvailableTransitions(projectsDir, projectSlug, assignmentSlug, assignment, options) {
|
|
2312
2358
|
const config = await getStatusConfig();
|
|
2313
2359
|
const transitionDefs = getTransitionDefinitions(config);
|
|
2314
2360
|
const actions = [];
|
|
2315
2361
|
const projectPath = resolve11(projectsDir, projectSlug);
|
|
2362
|
+
const traces = options?.traces;
|
|
2316
2363
|
for (const definition of transitionDefs) {
|
|
2364
|
+
const target = getTargetStatus(assignment.status, definition.command, config.transitionTable);
|
|
2365
|
+
if (target === null) continue;
|
|
2317
2366
|
let warning = null;
|
|
2318
2367
|
if (definition.command === "start" && !assignment.assignee) {
|
|
2319
2368
|
warning = "No assignee set \u2014 consider assigning before starting.";
|
|
2320
2369
|
}
|
|
2321
2370
|
if (definition.command === "start" && assignment.dependsOn.length > 0) {
|
|
2322
|
-
const
|
|
2371
|
+
const t0 = traces ? performance.now() : 0;
|
|
2372
|
+
const unmetDependencies = await getUnmetDependencies(
|
|
2373
|
+
projectPath,
|
|
2374
|
+
assignment.dependsOn,
|
|
2375
|
+
config.terminalStatuses,
|
|
2376
|
+
options?.dependencyStatusMap
|
|
2377
|
+
);
|
|
2378
|
+
if (traces) accumulatePhase(traces, "get-unmet-dependencies", performance.now() - t0);
|
|
2323
2379
|
if (unmetDependencies.length > 0) {
|
|
2324
2380
|
warning = `Unmet dependencies: ${unmetDependencies.join(", ")}.`;
|
|
2325
2381
|
}
|
|
2326
2382
|
}
|
|
2327
|
-
const target = getTargetStatus(assignment.status, definition.command, config.transitionTable);
|
|
2328
2383
|
actions.push({
|
|
2329
2384
|
command: definition.command,
|
|
2330
2385
|
label: definition.label,
|
|
2331
2386
|
description: definition.description,
|
|
2332
|
-
targetStatus: target
|
|
2387
|
+
targetStatus: target,
|
|
2333
2388
|
disabled: false,
|
|
2334
2389
|
disabledReason: null,
|
|
2335
2390
|
warning,
|
|
@@ -2338,10 +2393,19 @@ async function getAvailableTransitions(projectsDir, projectSlug, assignmentSlug,
|
|
|
2338
2393
|
}
|
|
2339
2394
|
return actions;
|
|
2340
2395
|
}
|
|
2341
|
-
async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses) {
|
|
2396
|
+
async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses, dependencyStatusMap) {
|
|
2342
2397
|
const terminals = terminalStatuses ?? /* @__PURE__ */ new Set(["completed"]);
|
|
2343
2398
|
const unmet = [];
|
|
2344
2399
|
for (const dependency of dependsOn) {
|
|
2400
|
+
if (dependencyStatusMap) {
|
|
2401
|
+
const mappedStatus = dependencyStatusMap.get(dependency);
|
|
2402
|
+
if (mappedStatus !== void 0) {
|
|
2403
|
+
if (!terminals.has(mappedStatus)) {
|
|
2404
|
+
unmet.push(`${dependency} (${mappedStatus})`);
|
|
2405
|
+
}
|
|
2406
|
+
continue;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2345
2409
|
const dependencyPath = resolve11(projectPath, "assignments", dependency, "assignment.md");
|
|
2346
2410
|
if (!await fileExists(dependencyPath)) {
|
|
2347
2411
|
unmet.push(`${dependency} (missing)`);
|