@versatly/workgraph 0.2.0 → 0.3.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/README.md +28 -0
- package/dist/{chunk-XUMA4O2Z.js → chunk-65ZMX2WM.js} +1049 -1020
- package/dist/chunk-E3QU5Y53.js +1062 -0
- package/dist/cli.js +135 -10
- package/dist/index.d.ts +136 -3
- package/dist/index.js +22 -8
- package/dist/mcp-server-fU6U6ht8.d.ts +20 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +8 -0
- package/package.json +9 -3
|
@@ -4,28 +4,32 @@ var __export = (target, all) => {
|
|
|
4
4
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
// src/
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
7
|
+
// src/mcp-server.ts
|
|
8
|
+
var mcp_server_exports = {};
|
|
9
|
+
__export(mcp_server_exports, {
|
|
10
|
+
createWorkgraphMcpServer: () => createWorkgraphMcpServer,
|
|
11
|
+
startWorkgraphMcpServer: () => startWorkgraphMcpServer
|
|
12
|
+
});
|
|
13
|
+
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import { z } from "zod";
|
|
15
16
|
|
|
16
|
-
// src/
|
|
17
|
-
var
|
|
18
|
-
__export(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
// src/dispatch.ts
|
|
18
|
+
var dispatch_exports = {};
|
|
19
|
+
__export(dispatch_exports, {
|
|
20
|
+
createAndExecuteRun: () => createAndExecuteRun,
|
|
21
|
+
createRun: () => createRun,
|
|
22
|
+
executeRun: () => executeRun,
|
|
23
|
+
followup: () => followup,
|
|
24
|
+
listRuns: () => listRuns,
|
|
25
|
+
logs: () => logs,
|
|
26
|
+
markRun: () => markRun,
|
|
27
|
+
status: () => status,
|
|
28
|
+
stop: () => stop
|
|
26
29
|
});
|
|
27
|
-
import
|
|
28
|
-
import
|
|
30
|
+
import fs6 from "fs";
|
|
31
|
+
import path6 from "path";
|
|
32
|
+
import { randomUUID } from "crypto";
|
|
29
33
|
|
|
30
34
|
// src/ledger.ts
|
|
31
35
|
var ledger_exports = {};
|
|
@@ -359,7 +363,37 @@ function stableStringify(value) {
|
|
|
359
363
|
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`).join(",")}}`;
|
|
360
364
|
}
|
|
361
365
|
|
|
366
|
+
// src/store.ts
|
|
367
|
+
var store_exports = {};
|
|
368
|
+
__export(store_exports, {
|
|
369
|
+
activeThreads: () => activeThreads,
|
|
370
|
+
blockedThreads: () => blockedThreads,
|
|
371
|
+
create: () => create,
|
|
372
|
+
findByField: () => findByField,
|
|
373
|
+
list: () => list,
|
|
374
|
+
openThreads: () => openThreads,
|
|
375
|
+
read: () => read,
|
|
376
|
+
remove: () => remove,
|
|
377
|
+
threadsInSpace: () => threadsInSpace,
|
|
378
|
+
update: () => update
|
|
379
|
+
});
|
|
380
|
+
import fs5 from "fs";
|
|
381
|
+
import path5 from "path";
|
|
382
|
+
import matter from "gray-matter";
|
|
383
|
+
|
|
362
384
|
// src/registry.ts
|
|
385
|
+
var registry_exports = {};
|
|
386
|
+
__export(registry_exports, {
|
|
387
|
+
defineType: () => defineType,
|
|
388
|
+
extendType: () => extendType,
|
|
389
|
+
getType: () => getType,
|
|
390
|
+
listTypes: () => listTypes,
|
|
391
|
+
loadRegistry: () => loadRegistry,
|
|
392
|
+
registryPath: () => registryPath,
|
|
393
|
+
saveRegistry: () => saveRegistry
|
|
394
|
+
});
|
|
395
|
+
import fs2 from "fs";
|
|
396
|
+
import path2 from "path";
|
|
363
397
|
var REGISTRY_FILE = ".workgraph/registry.json";
|
|
364
398
|
var CURRENT_VERSION = 1;
|
|
365
399
|
var BUILT_IN_TYPES = [
|
|
@@ -690,24 +724,6 @@ function ensureBuiltIns(registry) {
|
|
|
690
724
|
return registry;
|
|
691
725
|
}
|
|
692
726
|
|
|
693
|
-
// src/store.ts
|
|
694
|
-
var store_exports = {};
|
|
695
|
-
__export(store_exports, {
|
|
696
|
-
activeThreads: () => activeThreads,
|
|
697
|
-
blockedThreads: () => blockedThreads,
|
|
698
|
-
create: () => create,
|
|
699
|
-
findByField: () => findByField,
|
|
700
|
-
list: () => list,
|
|
701
|
-
openThreads: () => openThreads,
|
|
702
|
-
read: () => read,
|
|
703
|
-
remove: () => remove,
|
|
704
|
-
threadsInSpace: () => threadsInSpace,
|
|
705
|
-
update: () => update
|
|
706
|
-
});
|
|
707
|
-
import fs5 from "fs";
|
|
708
|
-
import path5 from "path";
|
|
709
|
-
import matter from "gray-matter";
|
|
710
|
-
|
|
711
727
|
// src/graph.ts
|
|
712
728
|
var graph_exports = {};
|
|
713
729
|
__export(graph_exports, {
|
|
@@ -1270,6 +1286,81 @@ function validateRefValue(workspacePath, rawRef, allowedTypes) {
|
|
|
1270
1286
|
return { ok: true };
|
|
1271
1287
|
}
|
|
1272
1288
|
|
|
1289
|
+
// src/orientation.ts
|
|
1290
|
+
var orientation_exports = {};
|
|
1291
|
+
__export(orientation_exports, {
|
|
1292
|
+
brief: () => brief,
|
|
1293
|
+
checkpoint: () => checkpoint,
|
|
1294
|
+
intake: () => intake,
|
|
1295
|
+
statusSnapshot: () => statusSnapshot
|
|
1296
|
+
});
|
|
1297
|
+
|
|
1298
|
+
// src/query.ts
|
|
1299
|
+
var query_exports = {};
|
|
1300
|
+
__export(query_exports, {
|
|
1301
|
+
keywordSearch: () => keywordSearch,
|
|
1302
|
+
queryPrimitives: () => queryPrimitives
|
|
1303
|
+
});
|
|
1304
|
+
function queryPrimitives(workspacePath, filters = {}) {
|
|
1305
|
+
const typeNames = filters.type ? [filters.type] : listTypes(workspacePath).map((type) => type.name);
|
|
1306
|
+
const all = typeNames.flatMap((typeName) => list(workspacePath, typeName));
|
|
1307
|
+
const matched = all.filter((instance) => matchesFilters(instance, filters));
|
|
1308
|
+
const offset = Math.max(0, filters.offset ?? 0);
|
|
1309
|
+
const limited = filters.limit && filters.limit >= 0 ? matched.slice(offset, offset + filters.limit) : matched.slice(offset);
|
|
1310
|
+
return limited;
|
|
1311
|
+
}
|
|
1312
|
+
function keywordSearch(workspacePath, text, filters = {}) {
|
|
1313
|
+
return queryPrimitives(workspacePath, {
|
|
1314
|
+
...filters,
|
|
1315
|
+
text
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
function matchesFilters(instance, filters) {
|
|
1319
|
+
if (filters.status && String(instance.fields.status ?? "") !== filters.status) return false;
|
|
1320
|
+
if (filters.owner && String(instance.fields.owner ?? "") !== filters.owner) return false;
|
|
1321
|
+
if (filters.tag && !hasTag(instance, filters.tag)) return false;
|
|
1322
|
+
if (filters.pathIncludes && !instance.path.includes(filters.pathIncludes)) return false;
|
|
1323
|
+
if (filters.updatedAfter && !isDateOnOrAfter(instance.fields.updated, filters.updatedAfter)) return false;
|
|
1324
|
+
if (filters.updatedBefore && !isDateOnOrBefore(instance.fields.updated, filters.updatedBefore)) return false;
|
|
1325
|
+
if (filters.createdAfter && !isDateOnOrAfter(instance.fields.created, filters.createdAfter)) return false;
|
|
1326
|
+
if (filters.createdBefore && !isDateOnOrBefore(instance.fields.created, filters.createdBefore)) return false;
|
|
1327
|
+
if (filters.text && !containsText(instance, filters.text)) return false;
|
|
1328
|
+
return true;
|
|
1329
|
+
}
|
|
1330
|
+
function hasTag(instance, tag) {
|
|
1331
|
+
const tags = instance.fields.tags;
|
|
1332
|
+
if (!Array.isArray(tags)) return false;
|
|
1333
|
+
return tags.map((value) => String(value)).includes(tag);
|
|
1334
|
+
}
|
|
1335
|
+
function containsText(instance, text) {
|
|
1336
|
+
const haystack = [
|
|
1337
|
+
instance.path,
|
|
1338
|
+
instance.type,
|
|
1339
|
+
stringifyFields(instance.fields),
|
|
1340
|
+
instance.body
|
|
1341
|
+
].join("\n").toLowerCase();
|
|
1342
|
+
return haystack.includes(text.toLowerCase());
|
|
1343
|
+
}
|
|
1344
|
+
function stringifyFields(fields) {
|
|
1345
|
+
try {
|
|
1346
|
+
return JSON.stringify(fields);
|
|
1347
|
+
} catch {
|
|
1348
|
+
return "";
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function isDateOnOrAfter(value, thresholdIso) {
|
|
1352
|
+
const ts = Date.parse(String(value ?? ""));
|
|
1353
|
+
const threshold = Date.parse(thresholdIso);
|
|
1354
|
+
if (!Number.isFinite(ts) || !Number.isFinite(threshold)) return false;
|
|
1355
|
+
return ts >= threshold;
|
|
1356
|
+
}
|
|
1357
|
+
function isDateOnOrBefore(value, thresholdIso) {
|
|
1358
|
+
const ts = Date.parse(String(value ?? ""));
|
|
1359
|
+
const threshold = Date.parse(thresholdIso);
|
|
1360
|
+
if (!Number.isFinite(ts) || !Number.isFinite(threshold)) return false;
|
|
1361
|
+
return ts <= threshold;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1273
1364
|
// src/thread.ts
|
|
1274
1365
|
var thread_exports = {};
|
|
1275
1366
|
__export(thread_exports, {
|
|
@@ -1289,6 +1380,17 @@ __export(thread_exports, {
|
|
|
1289
1380
|
release: () => release,
|
|
1290
1381
|
unblock: () => unblock
|
|
1291
1382
|
});
|
|
1383
|
+
|
|
1384
|
+
// src/types.ts
|
|
1385
|
+
var THREAD_STATUS_TRANSITIONS = {
|
|
1386
|
+
open: ["active", "cancelled"],
|
|
1387
|
+
active: ["blocked", "done", "cancelled", "open"],
|
|
1388
|
+
blocked: ["active", "cancelled"],
|
|
1389
|
+
done: [],
|
|
1390
|
+
cancelled: ["open"]
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
// src/thread.ts
|
|
1292
1394
|
function createThread(workspacePath, title, goal, actor, opts = {}) {
|
|
1293
1395
|
const normalizedSpace = opts.space ? normalizeWorkspaceRef(opts.space) : void 0;
|
|
1294
1396
|
const contextRefs = opts.context_refs ?? [];
|
|
@@ -1531,632 +1633,50 @@ function normalizeWorkspaceRef(value) {
|
|
|
1531
1633
|
return unwrapped.endsWith(".md") ? unwrapped : `${unwrapped}.md`;
|
|
1532
1634
|
}
|
|
1533
1635
|
|
|
1534
|
-
// src/
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
// src/bases.ts
|
|
1545
|
-
var bases_exports = {};
|
|
1546
|
-
__export(bases_exports, {
|
|
1547
|
-
generateBasesFromPrimitiveRegistry: () => generateBasesFromPrimitiveRegistry,
|
|
1548
|
-
primitiveRegistryManifestPath: () => primitiveRegistryManifestPath,
|
|
1549
|
-
readPrimitiveRegistryManifest: () => readPrimitiveRegistryManifest,
|
|
1550
|
-
syncPrimitiveRegistryManifest: () => syncPrimitiveRegistryManifest
|
|
1551
|
-
});
|
|
1552
|
-
import fs6 from "fs";
|
|
1553
|
-
import path6 from "path";
|
|
1554
|
-
import YAML from "yaml";
|
|
1555
|
-
var REGISTRY_MANIFEST_FILE = ".workgraph/primitive-registry.yaml";
|
|
1556
|
-
var DEFAULT_BASES_DIR = ".workgraph/bases";
|
|
1557
|
-
function primitiveRegistryManifestPath(workspacePath) {
|
|
1558
|
-
return path6.join(workspacePath, REGISTRY_MANIFEST_FILE);
|
|
1559
|
-
}
|
|
1560
|
-
function readPrimitiveRegistryManifest(workspacePath) {
|
|
1561
|
-
const manifestPath = primitiveRegistryManifestPath(workspacePath);
|
|
1562
|
-
if (!fs6.existsSync(manifestPath)) {
|
|
1563
|
-
throw new Error(`Primitive registry manifest not found: ${manifestPath}`);
|
|
1564
|
-
}
|
|
1565
|
-
const raw = fs6.readFileSync(manifestPath, "utf-8");
|
|
1566
|
-
return YAML.parse(raw);
|
|
1567
|
-
}
|
|
1568
|
-
function syncPrimitiveRegistryManifest(workspacePath) {
|
|
1569
|
-
const registry = loadRegistry(workspacePath);
|
|
1570
|
-
const manifest = {
|
|
1571
|
-
version: 1,
|
|
1572
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1573
|
-
primitives: Object.values(registry.types).map((primitive) => ({
|
|
1574
|
-
name: primitive.name,
|
|
1575
|
-
directory: primitive.directory,
|
|
1576
|
-
canonical: primitive.builtIn,
|
|
1577
|
-
builtIn: primitive.builtIn,
|
|
1578
|
-
fields: Object.entries(primitive.fields).map(([name, field]) => ({
|
|
1579
|
-
name,
|
|
1580
|
-
type: field.type,
|
|
1581
|
-
...field.required ? { required: true } : {},
|
|
1582
|
-
...field.description ? { description: field.description } : {}
|
|
1583
|
-
}))
|
|
1584
|
-
})).sort((a, b) => a.name.localeCompare(b.name))
|
|
1585
|
-
};
|
|
1586
|
-
const manifestPath = primitiveRegistryManifestPath(workspacePath);
|
|
1587
|
-
ensureDirectory(path6.dirname(manifestPath));
|
|
1588
|
-
fs6.writeFileSync(manifestPath, YAML.stringify(manifest), "utf-8");
|
|
1589
|
-
return manifest;
|
|
1590
|
-
}
|
|
1591
|
-
function generateBasesFromPrimitiveRegistry(workspacePath, options = {}) {
|
|
1592
|
-
const manifest = readPrimitiveRegistryManifest(workspacePath);
|
|
1593
|
-
const includeNonCanonical = options.includeNonCanonical === true;
|
|
1594
|
-
const outputDirectory = path6.join(workspacePath, options.outputDirectory ?? DEFAULT_BASES_DIR);
|
|
1595
|
-
ensureDirectory(outputDirectory);
|
|
1596
|
-
const generated = [];
|
|
1597
|
-
const primitives = manifest.primitives.filter(
|
|
1598
|
-
(primitive) => includeNonCanonical ? true : primitive.canonical
|
|
1599
|
-
);
|
|
1600
|
-
for (const primitive of primitives) {
|
|
1601
|
-
const relBasePath = `${primitive.name}.base`;
|
|
1602
|
-
const absBasePath = path6.join(outputDirectory, relBasePath);
|
|
1603
|
-
const content = renderBaseFile(primitive);
|
|
1604
|
-
fs6.writeFileSync(absBasePath, content, "utf-8");
|
|
1605
|
-
generated.push(path6.relative(workspacePath, absBasePath).replace(/\\/g, "/"));
|
|
1606
|
-
}
|
|
1636
|
+
// src/orientation.ts
|
|
1637
|
+
function statusSnapshot(workspacePath) {
|
|
1638
|
+
const threads = list(workspacePath, "thread");
|
|
1639
|
+
const allPrimitives = queryPrimitives(workspacePath);
|
|
1640
|
+
const byType = allPrimitives.reduce((acc, instance) => {
|
|
1641
|
+
acc[instance.type] = (acc[instance.type] ?? 0) + 1;
|
|
1642
|
+
return acc;
|
|
1643
|
+
}, {});
|
|
1644
|
+
const claims = allClaims(workspacePath);
|
|
1645
|
+
const ready = listReadyThreads(workspacePath);
|
|
1607
1646
|
return {
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
source: {
|
|
1618
|
-
type: "folder",
|
|
1619
|
-
path: primitive.directory,
|
|
1620
|
-
extension: "md"
|
|
1647
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1648
|
+
threads: {
|
|
1649
|
+
total: threads.length,
|
|
1650
|
+
open: threads.filter((item) => item.fields.status === "open").length,
|
|
1651
|
+
active: threads.filter((item) => item.fields.status === "active").length,
|
|
1652
|
+
blocked: threads.filter((item) => item.fields.status === "blocked").length,
|
|
1653
|
+
done: threads.filter((item) => item.fields.status === "done").length,
|
|
1654
|
+
cancelled: threads.filter((item) => item.fields.status === "cancelled").length,
|
|
1655
|
+
ready: ready.length
|
|
1621
1656
|
},
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
}
|
|
1629
|
-
]
|
|
1630
|
-
};
|
|
1631
|
-
return YAML.stringify(baseDoc);
|
|
1632
|
-
}
|
|
1633
|
-
function ensureDirectory(dirPath) {
|
|
1634
|
-
if (!fs6.existsSync(dirPath)) fs6.mkdirSync(dirPath, { recursive: true });
|
|
1635
|
-
}
|
|
1636
|
-
function titleCase(value) {
|
|
1637
|
-
return value.split(/[-_]/g).filter(Boolean).map((segment) => segment[0].toUpperCase() + segment.slice(1)).join(" ");
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
|
-
// src/workspace.ts
|
|
1641
|
-
var WORKGRAPH_CONFIG_FILE = ".workgraph.json";
|
|
1642
|
-
function workspaceConfigPath(workspacePath) {
|
|
1643
|
-
return path7.join(workspacePath, WORKGRAPH_CONFIG_FILE);
|
|
1644
|
-
}
|
|
1645
|
-
function isWorkgraphWorkspace(workspacePath) {
|
|
1646
|
-
return fs7.existsSync(workspaceConfigPath(workspacePath));
|
|
1647
|
-
}
|
|
1648
|
-
function initWorkspace(targetPath, options = {}) {
|
|
1649
|
-
const resolvedPath = path7.resolve(targetPath);
|
|
1650
|
-
const configPath = workspaceConfigPath(resolvedPath);
|
|
1651
|
-
if (fs7.existsSync(configPath)) {
|
|
1652
|
-
throw new Error(`Workgraph workspace already initialized at ${resolvedPath}`);
|
|
1653
|
-
}
|
|
1654
|
-
const createdDirectories = [];
|
|
1655
|
-
ensureDir(resolvedPath, createdDirectories);
|
|
1656
|
-
ensureDir(path7.join(resolvedPath, ".workgraph"), createdDirectories);
|
|
1657
|
-
const registry = loadRegistry(resolvedPath);
|
|
1658
|
-
saveRegistry(resolvedPath, registry);
|
|
1659
|
-
syncPrimitiveRegistryManifest(resolvedPath);
|
|
1660
|
-
if (options.createTypeDirs !== false) {
|
|
1661
|
-
const types = listTypes(resolvedPath);
|
|
1662
|
-
for (const typeDef of types) {
|
|
1663
|
-
ensureDir(path7.join(resolvedPath, typeDef.directory), createdDirectories);
|
|
1657
|
+
claims: {
|
|
1658
|
+
active: claims.size
|
|
1659
|
+
},
|
|
1660
|
+
primitives: {
|
|
1661
|
+
total: allPrimitives.length,
|
|
1662
|
+
byType
|
|
1664
1663
|
}
|
|
1665
|
-
}
|
|
1666
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1667
|
-
const config = {
|
|
1668
|
-
name: options.name ?? path7.basename(resolvedPath),
|
|
1669
|
-
version: "1.0.0",
|
|
1670
|
-
mode: "workgraph",
|
|
1671
|
-
createdAt: now,
|
|
1672
|
-
updatedAt: now
|
|
1673
1664
|
};
|
|
1674
|
-
fs7.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1675
|
-
if (options.createReadme !== false) {
|
|
1676
|
-
writeReadmeIfMissing(resolvedPath, config.name);
|
|
1677
|
-
}
|
|
1678
|
-
const bases = options.createBases === false ? { generated: [] } : generateBasesFromPrimitiveRegistry(resolvedPath);
|
|
1679
|
-
loadPolicyRegistry(resolvedPath);
|
|
1680
|
-
refreshWikiLinkGraphIndex(resolvedPath);
|
|
1681
|
-
return {
|
|
1682
|
-
workspacePath: resolvedPath,
|
|
1683
|
-
configPath,
|
|
1684
|
-
config,
|
|
1685
|
-
createdDirectories,
|
|
1686
|
-
seededTypes: listTypes(resolvedPath).map((t) => t.name),
|
|
1687
|
-
generatedBases: bases.generated,
|
|
1688
|
-
primitiveRegistryManifestPath: ".workgraph/primitive-registry.yaml"
|
|
1689
|
-
};
|
|
1690
|
-
}
|
|
1691
|
-
function ensureDir(dirPath, createdDirectories) {
|
|
1692
|
-
if (fs7.existsSync(dirPath)) return;
|
|
1693
|
-
fs7.mkdirSync(dirPath, { recursive: true });
|
|
1694
|
-
createdDirectories.push(dirPath);
|
|
1695
1665
|
}
|
|
1696
|
-
function
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
## Quickstart
|
|
1704
|
-
|
|
1705
|
-
\`\`\`bash
|
|
1706
|
-
workgraph thread list --json
|
|
1707
|
-
workgraph thread next --claim --actor agent-a --json
|
|
1708
|
-
workgraph ledger show --count 20 --json
|
|
1709
|
-
\`\`\`
|
|
1710
|
-
`;
|
|
1711
|
-
fs7.writeFileSync(readmePath, content, "utf-8");
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
// src/command-center.ts
|
|
1715
|
-
var command_center_exports = {};
|
|
1716
|
-
__export(command_center_exports, {
|
|
1717
|
-
generateCommandCenter: () => generateCommandCenter
|
|
1718
|
-
});
|
|
1719
|
-
import fs8 from "fs";
|
|
1720
|
-
import path8 from "path";
|
|
1721
|
-
function generateCommandCenter(workspacePath, options = {}) {
|
|
1722
|
-
const actor = options.actor ?? "system";
|
|
1723
|
-
const recentCount = options.recentCount ?? 15;
|
|
1724
|
-
const relOutputPath = options.outputPath ?? "Command Center.md";
|
|
1725
|
-
const absOutputPath = resolvePathWithinWorkspace(workspacePath, relOutputPath);
|
|
1726
|
-
const normalizedOutputPath = path8.relative(workspacePath, absOutputPath).replace(/\\/g, "/");
|
|
1727
|
-
const allThreads = list(workspacePath, "thread");
|
|
1728
|
-
const openThreads2 = allThreads.filter((thread) => thread.fields.status === "open");
|
|
1729
|
-
const activeThreads2 = allThreads.filter((thread) => thread.fields.status === "active");
|
|
1730
|
-
const blockedThreads2 = allThreads.filter((thread) => thread.fields.status === "blocked");
|
|
1731
|
-
const doneThreads = allThreads.filter((thread) => thread.fields.status === "done");
|
|
1732
|
-
const claims = allClaims(workspacePath);
|
|
1733
|
-
const recentEvents = recent(workspacePath, recentCount);
|
|
1734
|
-
const content = renderCommandCenter({
|
|
1666
|
+
function brief(workspacePath, actor, options = {}) {
|
|
1667
|
+
const myClaims = [...allClaims(workspacePath).entries()].filter(([, owner]) => owner === actor).map(([target]) => read(workspacePath, target)).filter((instance) => instance !== null);
|
|
1668
|
+
const myOpenThreads = queryPrimitives(workspacePath, {
|
|
1669
|
+
type: "thread",
|
|
1670
|
+
owner: actor
|
|
1671
|
+
}).filter((instance) => ["open", "active"].includes(String(instance.fields.status)));
|
|
1672
|
+
return {
|
|
1735
1673
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1736
|
-
openThreads: openThreads2,
|
|
1737
|
-
activeThreads: activeThreads2,
|
|
1738
|
-
blockedThreads: blockedThreads2,
|
|
1739
|
-
doneThreads,
|
|
1740
|
-
claims: [...claims.entries()].map(([target, owner]) => ({ target, owner })),
|
|
1741
|
-
recentEvents
|
|
1742
|
-
});
|
|
1743
|
-
const parentDir = path8.dirname(absOutputPath);
|
|
1744
|
-
if (!fs8.existsSync(parentDir)) fs8.mkdirSync(parentDir, { recursive: true });
|
|
1745
|
-
const existed = fs8.existsSync(absOutputPath);
|
|
1746
|
-
fs8.writeFileSync(absOutputPath, content, "utf-8");
|
|
1747
|
-
append(
|
|
1748
|
-
workspacePath,
|
|
1749
1674
|
actor,
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
open_threads: openThreads2.length,
|
|
1756
|
-
active_claims: claims.size,
|
|
1757
|
-
recent_events: recentEvents.length
|
|
1758
|
-
}
|
|
1759
|
-
);
|
|
1760
|
-
return {
|
|
1761
|
-
outputPath: normalizedOutputPath,
|
|
1762
|
-
stats: {
|
|
1763
|
-
totalThreads: allThreads.length,
|
|
1764
|
-
openThreads: openThreads2.length,
|
|
1765
|
-
activeThreads: activeThreads2.length,
|
|
1766
|
-
blockedThreads: blockedThreads2.length,
|
|
1767
|
-
doneThreads: doneThreads.length,
|
|
1768
|
-
activeClaims: claims.size,
|
|
1769
|
-
recentEvents: recentEvents.length
|
|
1770
|
-
},
|
|
1771
|
-
content
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
function resolvePathWithinWorkspace(workspacePath, outputPath) {
|
|
1775
|
-
const base = path8.resolve(workspacePath);
|
|
1776
|
-
const resolved = path8.resolve(base, outputPath);
|
|
1777
|
-
if (!resolved.startsWith(base + path8.sep) && resolved !== base) {
|
|
1778
|
-
throw new Error(`Invalid command-center output path: ${outputPath}`);
|
|
1779
|
-
}
|
|
1780
|
-
return resolved;
|
|
1781
|
-
}
|
|
1782
|
-
function renderCommandCenter(input) {
|
|
1783
|
-
const header = [
|
|
1784
|
-
"# Workgraph Command Center",
|
|
1785
|
-
"",
|
|
1786
|
-
`Generated: ${input.generatedAt}`,
|
|
1787
|
-
""
|
|
1788
|
-
];
|
|
1789
|
-
const statusBlock = [
|
|
1790
|
-
"## Thread Status",
|
|
1791
|
-
"",
|
|
1792
|
-
`- Open: ${input.openThreads.length}`,
|
|
1793
|
-
`- Active: ${input.activeThreads.length}`,
|
|
1794
|
-
`- Blocked: ${input.blockedThreads.length}`,
|
|
1795
|
-
`- Done: ${input.doneThreads.length}`,
|
|
1796
|
-
""
|
|
1797
|
-
];
|
|
1798
|
-
const openTable = [
|
|
1799
|
-
"## Open Threads",
|
|
1800
|
-
"",
|
|
1801
|
-
"| Priority | Title | Path |",
|
|
1802
|
-
"|---|---|---|",
|
|
1803
|
-
...input.openThreads.length > 0 ? input.openThreads.map((thread) => `| ${String(thread.fields.priority ?? "medium")} | ${String(thread.fields.title ?? "Untitled")} | \`${thread.path}\` |`) : ["| - | None | - |"],
|
|
1804
|
-
""
|
|
1805
|
-
];
|
|
1806
|
-
const claimsSection = [
|
|
1807
|
-
"## Active Claims",
|
|
1808
|
-
"",
|
|
1809
|
-
...input.claims.length > 0 ? input.claims.map((claim2) => `- ${claim2.owner} -> \`${claim2.target}\``) : ["- None"],
|
|
1810
|
-
""
|
|
1811
|
-
];
|
|
1812
|
-
const blockedSection = [
|
|
1813
|
-
"## Blocked Threads",
|
|
1814
|
-
"",
|
|
1815
|
-
...input.blockedThreads.length > 0 ? input.blockedThreads.map((thread) => {
|
|
1816
|
-
const deps = Array.isArray(thread.fields.deps) ? thread.fields.deps.join(", ") : "";
|
|
1817
|
-
return `- ${String(thread.fields.title ?? thread.path)} (\`${thread.path}\`)${deps ? ` blocked by: ${deps}` : ""}`;
|
|
1818
|
-
}) : ["- None"],
|
|
1819
|
-
""
|
|
1820
|
-
];
|
|
1821
|
-
const recentSection = [
|
|
1822
|
-
"## Recent Ledger Activity",
|
|
1823
|
-
"",
|
|
1824
|
-
...input.recentEvents.length > 0 ? input.recentEvents.map((event) => `- ${event.ts} ${event.op} ${event.actor} -> \`${event.target}\``) : ["- No activity"],
|
|
1825
|
-
""
|
|
1826
|
-
];
|
|
1827
|
-
return [
|
|
1828
|
-
...header,
|
|
1829
|
-
...statusBlock,
|
|
1830
|
-
...openTable,
|
|
1831
|
-
...claimsSection,
|
|
1832
|
-
...blockedSection,
|
|
1833
|
-
...recentSection
|
|
1834
|
-
].join("\n");
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
// src/skill.ts
|
|
1838
|
-
var skill_exports = {};
|
|
1839
|
-
__export(skill_exports, {
|
|
1840
|
-
listSkills: () => listSkills,
|
|
1841
|
-
loadSkill: () => loadSkill,
|
|
1842
|
-
promoteSkill: () => promoteSkill,
|
|
1843
|
-
proposeSkill: () => proposeSkill,
|
|
1844
|
-
skillDiff: () => skillDiff,
|
|
1845
|
-
skillHistory: () => skillHistory,
|
|
1846
|
-
writeSkill: () => writeSkill
|
|
1847
|
-
});
|
|
1848
|
-
import fs9 from "fs";
|
|
1849
|
-
import path9 from "path";
|
|
1850
|
-
function writeSkill(workspacePath, title, body, actor, options = {}) {
|
|
1851
|
-
const slug = skillSlug(title);
|
|
1852
|
-
const bundleSkillPath = folderSkillPath(slug);
|
|
1853
|
-
const legacyPath = legacySkillPath(slug);
|
|
1854
|
-
const existing = read(workspacePath, bundleSkillPath) ?? read(workspacePath, legacyPath);
|
|
1855
|
-
const status2 = options.status ?? existing?.fields.status ?? "draft";
|
|
1856
|
-
if (existing && options.expectedUpdatedAt) {
|
|
1857
|
-
const currentUpdatedAt = String(existing.fields.updated ?? "");
|
|
1858
|
-
if (currentUpdatedAt !== options.expectedUpdatedAt) {
|
|
1859
|
-
throw new Error(`Concurrent skill update detected for ${existing.path}. Expected updated="${options.expectedUpdatedAt}" but found "${currentUpdatedAt}".`);
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1862
|
-
if (!existing) {
|
|
1863
|
-
ensureSkillBundleScaffold(workspacePath, slug);
|
|
1864
|
-
const created = create(workspacePath, "skill", {
|
|
1865
|
-
title,
|
|
1866
|
-
owner: options.owner ?? actor,
|
|
1867
|
-
version: options.version ?? "0.1.0",
|
|
1868
|
-
status: status2,
|
|
1869
|
-
distribution: options.distribution ?? "tailscale-shared-vault",
|
|
1870
|
-
tailscale_path: options.tailscalePath,
|
|
1871
|
-
reviewers: options.reviewers ?? [],
|
|
1872
|
-
depends_on: options.dependsOn ?? [],
|
|
1873
|
-
tags: options.tags ?? []
|
|
1874
|
-
}, body, actor, {
|
|
1875
|
-
pathOverride: bundleSkillPath
|
|
1876
|
-
});
|
|
1877
|
-
writeSkillManifest(workspacePath, slug, created, actor);
|
|
1878
|
-
return created;
|
|
1879
|
-
}
|
|
1880
|
-
const updated = update(workspacePath, existing.path, {
|
|
1881
|
-
title,
|
|
1882
|
-
owner: options.owner ?? existing.fields.owner ?? actor,
|
|
1883
|
-
version: options.version ?? existing.fields.version ?? "0.1.0",
|
|
1884
|
-
status: status2,
|
|
1885
|
-
distribution: options.distribution ?? existing.fields.distribution ?? "tailscale-shared-vault",
|
|
1886
|
-
tailscale_path: options.tailscalePath ?? existing.fields.tailscale_path,
|
|
1887
|
-
reviewers: options.reviewers ?? existing.fields.reviewers ?? [],
|
|
1888
|
-
depends_on: options.dependsOn ?? existing.fields.depends_on ?? [],
|
|
1889
|
-
tags: options.tags ?? existing.fields.tags ?? []
|
|
1890
|
-
}, body, actor);
|
|
1891
|
-
writeSkillManifest(workspacePath, slug, updated, actor);
|
|
1892
|
-
return updated;
|
|
1893
|
-
}
|
|
1894
|
-
function loadSkill(workspacePath, skillRef) {
|
|
1895
|
-
const normalizedCandidates = normalizeSkillRefCandidates(skillRef);
|
|
1896
|
-
const skill = normalizedCandidates.map((candidate) => read(workspacePath, candidate)).find((entry) => entry !== null);
|
|
1897
|
-
if (!skill) throw new Error(`Skill not found: ${skillRef}`);
|
|
1898
|
-
if (skill.type !== "skill") throw new Error(`Target is not a skill primitive: ${skillRef}`);
|
|
1899
|
-
return skill;
|
|
1900
|
-
}
|
|
1901
|
-
function listSkills(workspacePath, options = {}) {
|
|
1902
|
-
let skills = list(workspacePath, "skill");
|
|
1903
|
-
if (options.status) {
|
|
1904
|
-
skills = skills.filter((skill) => skill.fields.status === options.status);
|
|
1905
|
-
}
|
|
1906
|
-
if (options.updatedSince) {
|
|
1907
|
-
const threshold = Date.parse(options.updatedSince);
|
|
1908
|
-
if (Number.isFinite(threshold)) {
|
|
1909
|
-
skills = skills.filter((skill) => {
|
|
1910
|
-
const updatedAt = Date.parse(String(skill.fields.updated ?? ""));
|
|
1911
|
-
return Number.isFinite(updatedAt) && updatedAt >= threshold;
|
|
1912
|
-
});
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
return skills;
|
|
1916
|
-
}
|
|
1917
|
-
function proposeSkill(workspacePath, skillRef, actor, options = {}) {
|
|
1918
|
-
const skill = loadSkill(workspacePath, skillRef);
|
|
1919
|
-
const slug = skillSlug(String(skill.fields.title ?? skillRef));
|
|
1920
|
-
let proposalThread = options.proposalThread;
|
|
1921
|
-
if (!proposalThread && options.createThreadIfMissing !== false) {
|
|
1922
|
-
const createdThread = createThread(
|
|
1923
|
-
workspacePath,
|
|
1924
|
-
`Review skill: ${String(skill.fields.title)}`,
|
|
1925
|
-
`Review and approve skill ${skill.path} for activation.`,
|
|
1926
|
-
actor,
|
|
1927
|
-
{
|
|
1928
|
-
priority: "medium",
|
|
1929
|
-
space: options.space,
|
|
1930
|
-
context_refs: [skill.path]
|
|
1931
|
-
}
|
|
1932
|
-
);
|
|
1933
|
-
proposalThread = createdThread.path;
|
|
1934
|
-
}
|
|
1935
|
-
const updated = update(workspacePath, skill.path, {
|
|
1936
|
-
status: "proposed",
|
|
1937
|
-
proposal_thread: proposalThread ?? skill.fields.proposal_thread,
|
|
1938
|
-
proposed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1939
|
-
reviewers: options.reviewers ?? skill.fields.reviewers ?? []
|
|
1940
|
-
}, void 0, actor);
|
|
1941
|
-
writeSkillManifest(workspacePath, slug, updated, actor);
|
|
1942
|
-
return updated;
|
|
1943
|
-
}
|
|
1944
|
-
function skillHistory(workspacePath, skillRef, options = {}) {
|
|
1945
|
-
const skill = loadSkill(workspacePath, skillRef);
|
|
1946
|
-
const entries = historyOf(workspacePath, skill.path);
|
|
1947
|
-
if (options.limit && options.limit > 0) {
|
|
1948
|
-
return entries.slice(-options.limit);
|
|
1949
|
-
}
|
|
1950
|
-
return entries;
|
|
1951
|
-
}
|
|
1952
|
-
function skillDiff(workspacePath, skillRef) {
|
|
1953
|
-
const skill = loadSkill(workspacePath, skillRef);
|
|
1954
|
-
const entries = historyOf(workspacePath, skill.path).filter((entry) => entry.op === "create" || entry.op === "update");
|
|
1955
|
-
const latest = entries.length > 0 ? entries[entries.length - 1] : null;
|
|
1956
|
-
const previous = entries.length > 1 ? entries[entries.length - 2] : null;
|
|
1957
|
-
const changedFields = Array.isArray(latest?.data?.changed) ? latest.data.changed.map((value) => String(value)) : latest?.op === "create" ? Object.keys(skill.fields) : [];
|
|
1958
|
-
return {
|
|
1959
|
-
path: skill.path,
|
|
1960
|
-
latestEntryTs: latest?.ts ?? null,
|
|
1961
|
-
previousEntryTs: previous?.ts ?? null,
|
|
1962
|
-
changedFields
|
|
1963
|
-
};
|
|
1964
|
-
}
|
|
1965
|
-
function promoteSkill(workspacePath, skillRef, actor, options = {}) {
|
|
1966
|
-
const skill = loadSkill(workspacePath, skillRef);
|
|
1967
|
-
const slug = skillSlug(String(skill.fields.title ?? skillRef));
|
|
1968
|
-
const currentVersion = String(skill.fields.version ?? "0.1.0");
|
|
1969
|
-
const nextVersion = options.version ?? bumpPatchVersion(currentVersion);
|
|
1970
|
-
const updated = update(workspacePath, skill.path, {
|
|
1971
|
-
status: "active",
|
|
1972
|
-
version: nextVersion,
|
|
1973
|
-
promoted_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1974
|
-
}, void 0, actor);
|
|
1975
|
-
writeSkillManifest(workspacePath, slug, updated, actor);
|
|
1976
|
-
return updated;
|
|
1977
|
-
}
|
|
1978
|
-
function skillSlug(title) {
|
|
1979
|
-
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
|
|
1980
|
-
}
|
|
1981
|
-
function normalizeSkillRefCandidates(skillRef) {
|
|
1982
|
-
const raw = skillRef.trim();
|
|
1983
|
-
if (!raw) return [];
|
|
1984
|
-
if (raw.includes("/")) {
|
|
1985
|
-
const normalized = raw.endsWith(".md") ? raw : `${raw}.md`;
|
|
1986
|
-
if (normalized.endsWith("/SKILL.md")) return [normalized];
|
|
1987
|
-
if (normalized.endsWith("/SKILL")) return [`${normalized}.md`];
|
|
1988
|
-
if (normalized.endsWith(".md")) {
|
|
1989
|
-
const noExt = normalized.slice(0, -3);
|
|
1990
|
-
return [normalized, `${noExt}/SKILL.md`];
|
|
1991
|
-
}
|
|
1992
|
-
return [normalized, `${normalized}/SKILL.md`];
|
|
1993
|
-
}
|
|
1994
|
-
const slug = skillSlug(raw);
|
|
1995
|
-
return [folderSkillPath(slug), legacySkillPath(slug)];
|
|
1996
|
-
}
|
|
1997
|
-
function bumpPatchVersion(version) {
|
|
1998
|
-
const match = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
1999
|
-
if (!match) return "0.1.0";
|
|
2000
|
-
const major = Number.parseInt(match[1], 10);
|
|
2001
|
-
const minor = Number.parseInt(match[2], 10);
|
|
2002
|
-
const patch = Number.parseInt(match[3], 10) + 1;
|
|
2003
|
-
return `${major}.${minor}.${patch}`;
|
|
2004
|
-
}
|
|
2005
|
-
function folderSkillPath(slug) {
|
|
2006
|
-
return `skills/${slug}/SKILL.md`;
|
|
2007
|
-
}
|
|
2008
|
-
function legacySkillPath(slug) {
|
|
2009
|
-
return `skills/${slug}.md`;
|
|
2010
|
-
}
|
|
2011
|
-
function ensureSkillBundleScaffold(workspacePath, slug) {
|
|
2012
|
-
const skillRoot = path9.join(workspacePath, "skills", slug);
|
|
2013
|
-
fs9.mkdirSync(skillRoot, { recursive: true });
|
|
2014
|
-
for (const subdir of ["scripts", "examples", "tests", "assets"]) {
|
|
2015
|
-
fs9.mkdirSync(path9.join(skillRoot, subdir), { recursive: true });
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
function writeSkillManifest(workspacePath, slug, skill, actor) {
|
|
2019
|
-
const manifestPath = path9.join(workspacePath, "skills", slug, "skill-manifest.json");
|
|
2020
|
-
const dir = path9.dirname(manifestPath);
|
|
2021
|
-
fs9.mkdirSync(dir, { recursive: true });
|
|
2022
|
-
const manifest = {
|
|
2023
|
-
version: 1,
|
|
2024
|
-
slug,
|
|
2025
|
-
title: String(skill.fields.title ?? slug),
|
|
2026
|
-
primitivePath: skill.path,
|
|
2027
|
-
owner: String(skill.fields.owner ?? actor),
|
|
2028
|
-
skillVersion: String(skill.fields.version ?? "0.1.0"),
|
|
2029
|
-
status: String(skill.fields.status ?? "draft"),
|
|
2030
|
-
dependsOn: Array.isArray(skill.fields.depends_on) ? skill.fields.depends_on.map((value) => String(value)) : [],
|
|
2031
|
-
components: {
|
|
2032
|
-
skillDoc: "SKILL.md",
|
|
2033
|
-
scriptsDir: "scripts/",
|
|
2034
|
-
examplesDir: "examples/",
|
|
2035
|
-
testsDir: "tests/",
|
|
2036
|
-
assetsDir: "assets/"
|
|
2037
|
-
},
|
|
2038
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2039
|
-
};
|
|
2040
|
-
fs9.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
// src/query.ts
|
|
2044
|
-
var query_exports = {};
|
|
2045
|
-
__export(query_exports, {
|
|
2046
|
-
keywordSearch: () => keywordSearch,
|
|
2047
|
-
queryPrimitives: () => queryPrimitives
|
|
2048
|
-
});
|
|
2049
|
-
function queryPrimitives(workspacePath, filters = {}) {
|
|
2050
|
-
const typeNames = filters.type ? [filters.type] : listTypes(workspacePath).map((type) => type.name);
|
|
2051
|
-
const all = typeNames.flatMap((typeName) => list(workspacePath, typeName));
|
|
2052
|
-
const matched = all.filter((instance) => matchesFilters(instance, filters));
|
|
2053
|
-
const offset = Math.max(0, filters.offset ?? 0);
|
|
2054
|
-
const limited = filters.limit && filters.limit >= 0 ? matched.slice(offset, offset + filters.limit) : matched.slice(offset);
|
|
2055
|
-
return limited;
|
|
2056
|
-
}
|
|
2057
|
-
function keywordSearch(workspacePath, text, filters = {}) {
|
|
2058
|
-
return queryPrimitives(workspacePath, {
|
|
2059
|
-
...filters,
|
|
2060
|
-
text
|
|
2061
|
-
});
|
|
2062
|
-
}
|
|
2063
|
-
function matchesFilters(instance, filters) {
|
|
2064
|
-
if (filters.status && String(instance.fields.status ?? "") !== filters.status) return false;
|
|
2065
|
-
if (filters.owner && String(instance.fields.owner ?? "") !== filters.owner) return false;
|
|
2066
|
-
if (filters.tag && !hasTag(instance, filters.tag)) return false;
|
|
2067
|
-
if (filters.pathIncludes && !instance.path.includes(filters.pathIncludes)) return false;
|
|
2068
|
-
if (filters.updatedAfter && !isDateOnOrAfter(instance.fields.updated, filters.updatedAfter)) return false;
|
|
2069
|
-
if (filters.updatedBefore && !isDateOnOrBefore(instance.fields.updated, filters.updatedBefore)) return false;
|
|
2070
|
-
if (filters.createdAfter && !isDateOnOrAfter(instance.fields.created, filters.createdAfter)) return false;
|
|
2071
|
-
if (filters.createdBefore && !isDateOnOrBefore(instance.fields.created, filters.createdBefore)) return false;
|
|
2072
|
-
if (filters.text && !containsText(instance, filters.text)) return false;
|
|
2073
|
-
return true;
|
|
2074
|
-
}
|
|
2075
|
-
function hasTag(instance, tag) {
|
|
2076
|
-
const tags = instance.fields.tags;
|
|
2077
|
-
if (!Array.isArray(tags)) return false;
|
|
2078
|
-
return tags.map((value) => String(value)).includes(tag);
|
|
2079
|
-
}
|
|
2080
|
-
function containsText(instance, text) {
|
|
2081
|
-
const haystack = [
|
|
2082
|
-
instance.path,
|
|
2083
|
-
instance.type,
|
|
2084
|
-
stringifyFields(instance.fields),
|
|
2085
|
-
instance.body
|
|
2086
|
-
].join("\n").toLowerCase();
|
|
2087
|
-
return haystack.includes(text.toLowerCase());
|
|
2088
|
-
}
|
|
2089
|
-
function stringifyFields(fields) {
|
|
2090
|
-
try {
|
|
2091
|
-
return JSON.stringify(fields);
|
|
2092
|
-
} catch {
|
|
2093
|
-
return "";
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
function isDateOnOrAfter(value, thresholdIso) {
|
|
2097
|
-
const ts = Date.parse(String(value ?? ""));
|
|
2098
|
-
const threshold = Date.parse(thresholdIso);
|
|
2099
|
-
if (!Number.isFinite(ts) || !Number.isFinite(threshold)) return false;
|
|
2100
|
-
return ts >= threshold;
|
|
2101
|
-
}
|
|
2102
|
-
function isDateOnOrBefore(value, thresholdIso) {
|
|
2103
|
-
const ts = Date.parse(String(value ?? ""));
|
|
2104
|
-
const threshold = Date.parse(thresholdIso);
|
|
2105
|
-
if (!Number.isFinite(ts) || !Number.isFinite(threshold)) return false;
|
|
2106
|
-
return ts <= threshold;
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
// src/orientation.ts
|
|
2110
|
-
var orientation_exports = {};
|
|
2111
|
-
__export(orientation_exports, {
|
|
2112
|
-
brief: () => brief,
|
|
2113
|
-
checkpoint: () => checkpoint,
|
|
2114
|
-
intake: () => intake,
|
|
2115
|
-
statusSnapshot: () => statusSnapshot
|
|
2116
|
-
});
|
|
2117
|
-
function statusSnapshot(workspacePath) {
|
|
2118
|
-
const threads = list(workspacePath, "thread");
|
|
2119
|
-
const allPrimitives = queryPrimitives(workspacePath);
|
|
2120
|
-
const byType = allPrimitives.reduce((acc, instance) => {
|
|
2121
|
-
acc[instance.type] = (acc[instance.type] ?? 0) + 1;
|
|
2122
|
-
return acc;
|
|
2123
|
-
}, {});
|
|
2124
|
-
const claims = allClaims(workspacePath);
|
|
2125
|
-
const ready = listReadyThreads(workspacePath);
|
|
2126
|
-
return {
|
|
2127
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2128
|
-
threads: {
|
|
2129
|
-
total: threads.length,
|
|
2130
|
-
open: threads.filter((item) => item.fields.status === "open").length,
|
|
2131
|
-
active: threads.filter((item) => item.fields.status === "active").length,
|
|
2132
|
-
blocked: threads.filter((item) => item.fields.status === "blocked").length,
|
|
2133
|
-
done: threads.filter((item) => item.fields.status === "done").length,
|
|
2134
|
-
cancelled: threads.filter((item) => item.fields.status === "cancelled").length,
|
|
2135
|
-
ready: ready.length
|
|
2136
|
-
},
|
|
2137
|
-
claims: {
|
|
2138
|
-
active: claims.size
|
|
2139
|
-
},
|
|
2140
|
-
primitives: {
|
|
2141
|
-
total: allPrimitives.length,
|
|
2142
|
-
byType
|
|
2143
|
-
}
|
|
2144
|
-
};
|
|
2145
|
-
}
|
|
2146
|
-
function brief(workspacePath, actor, options = {}) {
|
|
2147
|
-
const myClaims = [...allClaims(workspacePath).entries()].filter(([, owner]) => owner === actor).map(([target]) => read(workspacePath, target)).filter((instance) => instance !== null);
|
|
2148
|
-
const myOpenThreads = queryPrimitives(workspacePath, {
|
|
2149
|
-
type: "thread",
|
|
2150
|
-
owner: actor
|
|
2151
|
-
}).filter((instance) => ["open", "active"].includes(String(instance.fields.status)));
|
|
2152
|
-
return {
|
|
2153
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2154
|
-
actor,
|
|
2155
|
-
myClaims,
|
|
2156
|
-
myOpenThreads,
|
|
2157
|
-
blockedThreads: blockedThreads(workspacePath),
|
|
2158
|
-
nextReadyThreads: listReadyThreads(workspacePath).slice(0, options.nextCount ?? 5),
|
|
2159
|
-
recentActivity: recent(workspacePath, options.recentCount ?? 12)
|
|
1675
|
+
myClaims,
|
|
1676
|
+
myOpenThreads,
|
|
1677
|
+
blockedThreads: blockedThreads(workspacePath),
|
|
1678
|
+
nextReadyThreads: listReadyThreads(workspacePath).slice(0, options.nextCount ?? 5),
|
|
1679
|
+
recentActivity: recent(workspacePath, options.recentCount ?? 12)
|
|
2160
1680
|
};
|
|
2161
1681
|
}
|
|
2162
1682
|
function checkpoint(workspacePath, actor, summary, options = {}) {
|
|
@@ -2196,155 +1716,269 @@ function intake(workspacePath, actor, observation, options = {}) {
|
|
|
2196
1716
|
});
|
|
2197
1717
|
}
|
|
2198
1718
|
|
|
2199
|
-
// src/
|
|
2200
|
-
var
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
{
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
{
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
}
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
active: [],
|
|
2246
|
-
blocked: [],
|
|
2247
|
-
done: [],
|
|
2248
|
-
cancelled: []
|
|
2249
|
-
};
|
|
2250
|
-
for (const thread of threads) {
|
|
2251
|
-
const status2 = String(thread.fields.status ?? "open");
|
|
2252
|
-
switch (status2) {
|
|
2253
|
-
case "active":
|
|
2254
|
-
groups.active.push(thread);
|
|
2255
|
-
break;
|
|
2256
|
-
case "blocked":
|
|
2257
|
-
groups.blocked.push(thread);
|
|
2258
|
-
break;
|
|
2259
|
-
case "done":
|
|
2260
|
-
groups.done.push(thread);
|
|
2261
|
-
break;
|
|
2262
|
-
case "cancelled":
|
|
2263
|
-
groups.cancelled.push(thread);
|
|
2264
|
-
break;
|
|
2265
|
-
case "open":
|
|
2266
|
-
default:
|
|
2267
|
-
groups.open.push(thread);
|
|
1719
|
+
// src/adapter-cursor-cloud.ts
|
|
1720
|
+
var DEFAULT_MAX_STEPS = 200;
|
|
1721
|
+
var DEFAULT_STEP_DELAY_MS = 25;
|
|
1722
|
+
var DEFAULT_AGENT_COUNT = 3;
|
|
1723
|
+
var CursorCloudAdapter = class {
|
|
1724
|
+
name = "cursor-cloud";
|
|
1725
|
+
async create(_input) {
|
|
1726
|
+
return {
|
|
1727
|
+
runId: "adapter-managed",
|
|
1728
|
+
status: "queued"
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
async status(runId) {
|
|
1732
|
+
return { runId, status: "running" };
|
|
1733
|
+
}
|
|
1734
|
+
async followup(runId, _actor, _input) {
|
|
1735
|
+
return { runId, status: "running" };
|
|
1736
|
+
}
|
|
1737
|
+
async stop(runId, _actor) {
|
|
1738
|
+
return { runId, status: "cancelled" };
|
|
1739
|
+
}
|
|
1740
|
+
async logs(_runId) {
|
|
1741
|
+
return [];
|
|
1742
|
+
}
|
|
1743
|
+
async execute(input) {
|
|
1744
|
+
const start = Date.now();
|
|
1745
|
+
const logs2 = [];
|
|
1746
|
+
const agentPool = normalizeAgents(input.agents, input.actor);
|
|
1747
|
+
const maxSteps = normalizeInt(input.maxSteps, DEFAULT_MAX_STEPS, 1, 5e3);
|
|
1748
|
+
const stepDelayMs = normalizeInt(input.stepDelayMs, DEFAULT_STEP_DELAY_MS, 0, 5e3);
|
|
1749
|
+
const claimedByAgent = {};
|
|
1750
|
+
const completedByAgent = {};
|
|
1751
|
+
let stepsExecuted = 0;
|
|
1752
|
+
let completionCount = 0;
|
|
1753
|
+
let failureCount = 0;
|
|
1754
|
+
let cancelled = false;
|
|
1755
|
+
for (const agent of agentPool) {
|
|
1756
|
+
claimedByAgent[agent] = 0;
|
|
1757
|
+
completedByAgent[agent] = 0;
|
|
1758
|
+
}
|
|
1759
|
+
pushLog(logs2, "info", `Run ${input.runId} started with agents: ${agentPool.join(", ")}`);
|
|
1760
|
+
pushLog(logs2, "info", `Objective: ${input.objective}`);
|
|
1761
|
+
while (stepsExecuted < maxSteps) {
|
|
1762
|
+
if (input.isCancelled?.()) {
|
|
1763
|
+
cancelled = true;
|
|
1764
|
+
pushLog(logs2, "warn", `Run ${input.runId} received cancellation signal.`);
|
|
2268
1765
|
break;
|
|
1766
|
+
}
|
|
1767
|
+
const claimedThisRound = [];
|
|
1768
|
+
for (const agent of agentPool) {
|
|
1769
|
+
try {
|
|
1770
|
+
const claimed = input.space ? claimNextReadyInSpace(input.workspacePath, agent, input.space) : claimNextReady(input.workspacePath, agent);
|
|
1771
|
+
if (!claimed) {
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1774
|
+
const path7 = claimed.path;
|
|
1775
|
+
const goal = String(claimed.fields.goal ?? claimed.fields.title ?? path7);
|
|
1776
|
+
claimedThisRound.push({ agent, threadPath: path7, goal });
|
|
1777
|
+
claimedByAgent[agent] += 1;
|
|
1778
|
+
pushLog(logs2, "info", `${agent} claimed ${path7}`);
|
|
1779
|
+
} catch (error) {
|
|
1780
|
+
pushLog(logs2, "warn", `${agent} claim skipped: ${errorMessage(error)}`);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
if (claimedThisRound.length === 0) {
|
|
1784
|
+
const readyRemaining = listReady(input.workspacePath, input.space).length;
|
|
1785
|
+
if (readyRemaining === 0) {
|
|
1786
|
+
pushLog(logs2, "info", "No ready threads remaining; autonomous loop complete.");
|
|
1787
|
+
break;
|
|
1788
|
+
}
|
|
1789
|
+
if (stepDelayMs > 0) {
|
|
1790
|
+
await sleep(stepDelayMs);
|
|
1791
|
+
}
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
await Promise.all(claimedThisRound.map(async (claimed) => {
|
|
1795
|
+
if (input.isCancelled?.()) {
|
|
1796
|
+
cancelled = true;
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
if (stepDelayMs > 0) {
|
|
1800
|
+
await sleep(stepDelayMs);
|
|
1801
|
+
}
|
|
1802
|
+
try {
|
|
1803
|
+
done(
|
|
1804
|
+
input.workspacePath,
|
|
1805
|
+
claimed.threadPath,
|
|
1806
|
+
claimed.agent,
|
|
1807
|
+
`Completed by ${claimed.agent} during dispatch run ${input.runId}. Goal: ${claimed.goal}`
|
|
1808
|
+
);
|
|
1809
|
+
completionCount += 1;
|
|
1810
|
+
completedByAgent[claimed.agent] += 1;
|
|
1811
|
+
pushLog(logs2, "info", `${claimed.agent} completed ${claimed.threadPath}`);
|
|
1812
|
+
} catch (error) {
|
|
1813
|
+
failureCount += 1;
|
|
1814
|
+
pushLog(logs2, "error", `${claimed.agent} failed to complete ${claimed.threadPath}: ${errorMessage(error)}`);
|
|
1815
|
+
}
|
|
1816
|
+
}));
|
|
1817
|
+
stepsExecuted += claimedThisRound.length;
|
|
1818
|
+
if (cancelled) break;
|
|
2269
1819
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
const
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
1820
|
+
const readyAfter = listReady(input.workspacePath, input.space);
|
|
1821
|
+
const activeAfter = input.space ? threadsInSpace(input.workspacePath, input.space).filter((candidate) => candidate.fields.status === "active") : activeThreads(input.workspacePath);
|
|
1822
|
+
const openAfter = input.space ? threadsInSpace(input.workspacePath, input.space).filter((candidate) => candidate.fields.status === "open") : openThreads(input.workspacePath);
|
|
1823
|
+
const blockedAfter = input.space ? threadsInSpace(input.workspacePath, input.space).filter((candidate) => candidate.fields.status === "blocked") : blockedThreads(input.workspacePath);
|
|
1824
|
+
const elapsedMs = Date.now() - start;
|
|
1825
|
+
const summary = renderSummary({
|
|
1826
|
+
objective: input.objective,
|
|
1827
|
+
runId: input.runId,
|
|
1828
|
+
completed: completionCount,
|
|
1829
|
+
failed: failureCount,
|
|
1830
|
+
stepsExecuted,
|
|
1831
|
+
readyRemaining: readyAfter.length,
|
|
1832
|
+
openRemaining: openAfter.length,
|
|
1833
|
+
blockedRemaining: blockedAfter.length,
|
|
1834
|
+
activeRemaining: activeAfter.length,
|
|
1835
|
+
elapsedMs,
|
|
1836
|
+
claimedByAgent,
|
|
1837
|
+
completedByAgent,
|
|
1838
|
+
cancelled
|
|
1839
|
+
});
|
|
1840
|
+
if (input.createCheckpoint !== false) {
|
|
1841
|
+
try {
|
|
1842
|
+
checkpoint(
|
|
1843
|
+
input.workspacePath,
|
|
1844
|
+
input.actor,
|
|
1845
|
+
`Dispatch run ${input.runId} completed autonomous execution.`,
|
|
1846
|
+
{
|
|
1847
|
+
next: readyAfter.slice(0, 10).map((entry) => entry.path),
|
|
1848
|
+
blocked: blockedAfter.slice(0, 10).map((entry) => entry.path),
|
|
1849
|
+
tags: ["dispatch", "autonomous-run"]
|
|
1850
|
+
}
|
|
1851
|
+
);
|
|
1852
|
+
pushLog(logs2, "info", `Checkpoint recorded for run ${input.runId}.`);
|
|
1853
|
+
} catch (error) {
|
|
1854
|
+
pushLog(logs2, "warn", `Checkpoint creation skipped: ${errorMessage(error)}`);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
if (cancelled) {
|
|
1858
|
+
return {
|
|
1859
|
+
status: "cancelled",
|
|
1860
|
+
output: summary,
|
|
1861
|
+
logs: logs2,
|
|
1862
|
+
metrics: {
|
|
1863
|
+
completed: completionCount,
|
|
1864
|
+
failed: failureCount,
|
|
1865
|
+
readyRemaining: readyAfter.length,
|
|
1866
|
+
openRemaining: openAfter.length,
|
|
1867
|
+
blockedRemaining: blockedAfter.length,
|
|
1868
|
+
elapsedMs,
|
|
1869
|
+
claimedByAgent,
|
|
1870
|
+
completedByAgent
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
if (failureCount > 0) {
|
|
1875
|
+
return {
|
|
1876
|
+
status: "failed",
|
|
1877
|
+
error: summary,
|
|
1878
|
+
logs: logs2,
|
|
1879
|
+
metrics: {
|
|
1880
|
+
completed: completionCount,
|
|
1881
|
+
failed: failureCount,
|
|
1882
|
+
readyRemaining: readyAfter.length,
|
|
1883
|
+
openRemaining: openAfter.length,
|
|
1884
|
+
blockedRemaining: blockedAfter.length,
|
|
1885
|
+
elapsedMs,
|
|
1886
|
+
claimedByAgent,
|
|
1887
|
+
completedByAgent
|
|
1888
|
+
}
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
const status2 = readyAfter.length === 0 && activeAfter.length === 0 ? "succeeded" : "failed";
|
|
1892
|
+
if (status2 === "failed") {
|
|
1893
|
+
pushLog(logs2, "warn", "Execution stopped with actionable work still remaining.");
|
|
1894
|
+
}
|
|
1895
|
+
return {
|
|
1896
|
+
status: status2,
|
|
1897
|
+
output: summary,
|
|
1898
|
+
logs: logs2,
|
|
1899
|
+
metrics: {
|
|
1900
|
+
completed: completionCount,
|
|
1901
|
+
failed: failureCount,
|
|
1902
|
+
readyRemaining: readyAfter.length,
|
|
1903
|
+
openRemaining: openAfter.length,
|
|
1904
|
+
blockedRemaining: blockedAfter.length,
|
|
1905
|
+
elapsedMs,
|
|
1906
|
+
claimedByAgent,
|
|
1907
|
+
completedByAgent
|
|
2284
1908
|
}
|
|
2285
1909
|
};
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
1910
|
+
}
|
|
1911
|
+
};
|
|
1912
|
+
function normalizeAgents(agents, actor) {
|
|
1913
|
+
const fromInput = (agents ?? []).map((entry) => String(entry).trim()).filter(Boolean);
|
|
1914
|
+
if (fromInput.length > 0) return [...new Set(fromInput)];
|
|
1915
|
+
return Array.from({ length: DEFAULT_AGENT_COUNT }, (_, idx) => `${actor}-worker-${idx + 1}`);
|
|
1916
|
+
}
|
|
1917
|
+
function normalizeInt(rawValue, fallback, min, max) {
|
|
1918
|
+
const value = Number.isFinite(rawValue) ? Number(rawValue) : fallback;
|
|
1919
|
+
return Math.min(max, Math.max(min, Math.trunc(value)));
|
|
1920
|
+
}
|
|
1921
|
+
function pushLog(target, level, message) {
|
|
1922
|
+
target.push({
|
|
1923
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1924
|
+
level,
|
|
1925
|
+
message
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
function listReady(workspacePath, space) {
|
|
1929
|
+
return space ? listReadyThreadsInSpace(workspacePath, space) : listReadyThreads(workspacePath);
|
|
1930
|
+
}
|
|
1931
|
+
function errorMessage(error) {
|
|
1932
|
+
return error instanceof Error ? error.message : String(error);
|
|
1933
|
+
}
|
|
1934
|
+
function sleep(ms) {
|
|
1935
|
+
return new Promise((resolve) => {
|
|
1936
|
+
setTimeout(resolve, ms);
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
function renderSummary(data) {
|
|
2299
1940
|
const lines = [
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
1941
|
+
`Autonomous dispatch summary for ${data.runId}`,
|
|
1942
|
+
`Objective: ${data.objective}`,
|
|
1943
|
+
`Completed threads: ${data.completed}`,
|
|
1944
|
+
`Failed completions: ${data.failed}`,
|
|
1945
|
+
`Scheduler steps executed: ${data.stepsExecuted}`,
|
|
1946
|
+
`Ready remaining: ${data.readyRemaining}`,
|
|
1947
|
+
`Open remaining: ${data.openRemaining}`,
|
|
1948
|
+
`Blocked remaining: ${data.blockedRemaining}`,
|
|
1949
|
+
`Active remaining: ${data.activeRemaining}`,
|
|
1950
|
+
`Elapsed ms: ${data.elapsedMs}`,
|
|
1951
|
+
`Cancelled: ${data.cancelled ? "yes" : "no"}`,
|
|
1952
|
+
"",
|
|
1953
|
+
"Claims by agent:",
|
|
1954
|
+
...Object.entries(data.claimedByAgent).map(([agent, count]) => `- ${agent}: ${count}`),
|
|
1955
|
+
"",
|
|
1956
|
+
"Completions by agent:",
|
|
1957
|
+
...Object.entries(data.completedByAgent).map(([agent, count]) => `- ${agent}: ${count}`)
|
|
2304
1958
|
];
|
|
2305
|
-
for (const lane of lanes) {
|
|
2306
|
-
lines.push(`## ${lane.title}`);
|
|
2307
|
-
lines.push("");
|
|
2308
|
-
for (const thread of lane.items) {
|
|
2309
|
-
const title = String(thread.fields.title ?? thread.path);
|
|
2310
|
-
const priority = String(thread.fields.priority ?? "medium");
|
|
2311
|
-
lines.push(`- [${lane.checkChar}] [[${thread.path}|${title}]] (#${priority})`);
|
|
2312
|
-
}
|
|
2313
|
-
lines.push("");
|
|
2314
|
-
lines.push("");
|
|
2315
|
-
lines.push("");
|
|
2316
|
-
}
|
|
2317
|
-
lines.push("%% kanban:settings");
|
|
2318
|
-
lines.push("```");
|
|
2319
|
-
lines.push(JSON.stringify(settings));
|
|
2320
|
-
lines.push("```");
|
|
2321
|
-
lines.push("%%");
|
|
2322
|
-
lines.push("");
|
|
2323
1959
|
return lines.join("\n");
|
|
2324
1960
|
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
1961
|
+
|
|
1962
|
+
// src/runtime-adapter-registry.ts
|
|
1963
|
+
var adapterFactories = /* @__PURE__ */ new Map([
|
|
1964
|
+
["cursor-cloud", () => new CursorCloudAdapter()]
|
|
1965
|
+
]);
|
|
1966
|
+
function resolveDispatchAdapter(name) {
|
|
1967
|
+
const safeName = normalizeName(name);
|
|
1968
|
+
const factory = adapterFactories.get(safeName);
|
|
1969
|
+
if (!factory) {
|
|
1970
|
+
throw new Error(`Unknown dispatch adapter "${name}". Registered adapters: ${listDispatchAdapters().join(", ") || "none"}.`);
|
|
2330
1971
|
}
|
|
2331
|
-
return
|
|
1972
|
+
return factory();
|
|
1973
|
+
}
|
|
1974
|
+
function listDispatchAdapters() {
|
|
1975
|
+
return [...adapterFactories.keys()].sort((a, b) => a.localeCompare(b));
|
|
1976
|
+
}
|
|
1977
|
+
function normalizeName(name) {
|
|
1978
|
+
return String(name || "").trim().toLowerCase();
|
|
2332
1979
|
}
|
|
2333
1980
|
|
|
2334
1981
|
// src/dispatch.ts
|
|
2335
|
-
var dispatch_exports = {};
|
|
2336
|
-
__export(dispatch_exports, {
|
|
2337
|
-
createRun: () => createRun,
|
|
2338
|
-
followup: () => followup,
|
|
2339
|
-
listRuns: () => listRuns,
|
|
2340
|
-
logs: () => logs,
|
|
2341
|
-
markRun: () => markRun,
|
|
2342
|
-
status: () => status,
|
|
2343
|
-
stop: () => stop
|
|
2344
|
-
});
|
|
2345
|
-
import fs11 from "fs";
|
|
2346
|
-
import path11 from "path";
|
|
2347
|
-
import { randomUUID } from "crypto";
|
|
2348
1982
|
var RUNS_FILE = ".workgraph/dispatch-runs.json";
|
|
2349
1983
|
function createRun(workspacePath, input) {
|
|
2350
1984
|
const state = loadRuns(workspacePath);
|
|
@@ -2410,11 +2044,18 @@ function markRun(workspacePath, runId, actor, nextStatus, options = {}) {
|
|
|
2410
2044
|
const run = setStatus(workspacePath, runId, actor, nextStatus, `Run moved to ${nextStatus}.`);
|
|
2411
2045
|
if (options.output) run.output = options.output;
|
|
2412
2046
|
if (options.error) run.error = options.error;
|
|
2047
|
+
if (options.contextPatch && Object.keys(options.contextPatch).length > 0) {
|
|
2048
|
+
run.context = {
|
|
2049
|
+
...run.context ?? {},
|
|
2050
|
+
...options.contextPatch
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2413
2053
|
const state = loadRuns(workspacePath);
|
|
2414
2054
|
const target = state.runs.find((entry) => entry.id === runId);
|
|
2415
2055
|
if (target) {
|
|
2416
2056
|
target.output = run.output;
|
|
2417
2057
|
target.error = run.error;
|
|
2058
|
+
target.context = run.context;
|
|
2418
2059
|
target.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2419
2060
|
saveRuns(workspacePath, state);
|
|
2420
2061
|
syncRunPrimitive(workspacePath, target, actor);
|
|
@@ -2431,6 +2072,62 @@ function listRuns(workspacePath, options = {}) {
|
|
|
2431
2072
|
}
|
|
2432
2073
|
return runs;
|
|
2433
2074
|
}
|
|
2075
|
+
async function executeRun(workspacePath, runId, input) {
|
|
2076
|
+
const existing = status(workspacePath, runId);
|
|
2077
|
+
if (!["queued", "running"].includes(existing.status)) {
|
|
2078
|
+
throw new Error(`Run ${runId} is in terminal status "${existing.status}" and cannot be executed.`);
|
|
2079
|
+
}
|
|
2080
|
+
const adapter = resolveDispatchAdapter(existing.adapter);
|
|
2081
|
+
if (!adapter.execute) {
|
|
2082
|
+
throw new Error(`Dispatch adapter "${existing.adapter}" does not implement execute().`);
|
|
2083
|
+
}
|
|
2084
|
+
if (existing.status === "queued") {
|
|
2085
|
+
setStatus(workspacePath, runId, input.actor, "running", `Run started on adapter "${existing.adapter}".`);
|
|
2086
|
+
}
|
|
2087
|
+
const execution = await adapter.execute({
|
|
2088
|
+
workspacePath,
|
|
2089
|
+
runId,
|
|
2090
|
+
actor: input.actor,
|
|
2091
|
+
objective: existing.objective,
|
|
2092
|
+
context: existing.context,
|
|
2093
|
+
agents: input.agents,
|
|
2094
|
+
maxSteps: input.maxSteps,
|
|
2095
|
+
stepDelayMs: input.stepDelayMs,
|
|
2096
|
+
space: input.space,
|
|
2097
|
+
createCheckpoint: input.createCheckpoint,
|
|
2098
|
+
isCancelled: () => status(workspacePath, runId).status === "cancelled"
|
|
2099
|
+
});
|
|
2100
|
+
appendRunLogs(workspacePath, runId, input.actor, execution.logs);
|
|
2101
|
+
const finalStatus = execution.status;
|
|
2102
|
+
if (finalStatus === "queued" || finalStatus === "running") {
|
|
2103
|
+
throw new Error(`Adapter returned invalid terminal status "${finalStatus}" for execute().`);
|
|
2104
|
+
}
|
|
2105
|
+
return markRun(workspacePath, runId, input.actor, finalStatus, {
|
|
2106
|
+
output: execution.output,
|
|
2107
|
+
error: execution.error,
|
|
2108
|
+
contextPatch: execution.metrics ? { adapter_metrics: execution.metrics } : void 0
|
|
2109
|
+
});
|
|
2110
|
+
}
|
|
2111
|
+
async function createAndExecuteRun(workspacePath, createInput, executeInput = {}) {
|
|
2112
|
+
const run = createRun(workspacePath, createInput);
|
|
2113
|
+
return executeRun(workspacePath, run.id, {
|
|
2114
|
+
actor: createInput.actor,
|
|
2115
|
+
...executeInput
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
function appendRunLogs(workspacePath, runId, actor, logEntries) {
|
|
2119
|
+
if (logEntries.length === 0) return;
|
|
2120
|
+
const state = loadRuns(workspacePath);
|
|
2121
|
+
const run = state.runs.find((entry) => entry.id === runId);
|
|
2122
|
+
if (!run) throw new Error(`Run not found: ${runId}`);
|
|
2123
|
+
run.logs.push(...logEntries);
|
|
2124
|
+
run.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2125
|
+
saveRuns(workspacePath, state);
|
|
2126
|
+
append(workspacePath, actor, "update", `.workgraph/runs/${run.id}`, "run", {
|
|
2127
|
+
log_append_count: logEntries.length
|
|
2128
|
+
});
|
|
2129
|
+
syncRunPrimitive(workspacePath, run, actor);
|
|
2130
|
+
}
|
|
2434
2131
|
function setStatus(workspacePath, runId, actor, statusValue, logMessage) {
|
|
2435
2132
|
const state = loadRuns(workspacePath);
|
|
2436
2133
|
const run = state.runs.find((entry) => entry.id === runId);
|
|
@@ -2448,16 +2145,16 @@ function setStatus(workspacePath, runId, actor, statusValue, logMessage) {
|
|
|
2448
2145
|
return run;
|
|
2449
2146
|
}
|
|
2450
2147
|
function runsPath(workspacePath) {
|
|
2451
|
-
return
|
|
2148
|
+
return path6.join(workspacePath, RUNS_FILE);
|
|
2452
2149
|
}
|
|
2453
2150
|
function loadRuns(workspacePath) {
|
|
2454
2151
|
const rPath = runsPath(workspacePath);
|
|
2455
|
-
if (!
|
|
2152
|
+
if (!fs6.existsSync(rPath)) {
|
|
2456
2153
|
const seeded = { version: 1, runs: [] };
|
|
2457
2154
|
saveRuns(workspacePath, seeded);
|
|
2458
2155
|
return seeded;
|
|
2459
2156
|
}
|
|
2460
|
-
const raw =
|
|
2157
|
+
const raw = fs6.readFileSync(rPath, "utf-8");
|
|
2461
2158
|
const parsed = JSON.parse(raw);
|
|
2462
2159
|
return {
|
|
2463
2160
|
version: parsed.version ?? 1,
|
|
@@ -2466,9 +2163,9 @@ function loadRuns(workspacePath) {
|
|
|
2466
2163
|
}
|
|
2467
2164
|
function saveRuns(workspacePath, value) {
|
|
2468
2165
|
const rPath = runsPath(workspacePath);
|
|
2469
|
-
const dir =
|
|
2470
|
-
if (!
|
|
2471
|
-
|
|
2166
|
+
const dir = path6.dirname(rPath);
|
|
2167
|
+
if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
|
|
2168
|
+
fs6.writeFileSync(rPath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
2472
2169
|
}
|
|
2473
2170
|
function getRun(workspacePath, runId) {
|
|
2474
2171
|
const state = loadRuns(workspacePath);
|
|
@@ -2476,6 +2173,9 @@ function getRun(workspacePath, runId) {
|
|
|
2476
2173
|
}
|
|
2477
2174
|
function ensureRunPrimitive(workspacePath, run, actor) {
|
|
2478
2175
|
const safeTitle = `${run.objective} (${run.id.slice(0, 8)})`;
|
|
2176
|
+
const runPrimitivePath = `runs/${run.id}.md`;
|
|
2177
|
+
const existing = read(workspacePath, runPrimitivePath);
|
|
2178
|
+
if (existing) return;
|
|
2479
2179
|
create(
|
|
2480
2180
|
workspacePath,
|
|
2481
2181
|
"run",
|
|
@@ -2492,7 +2192,8 @@ function ensureRunPrimitive(workspacePath, run, actor) {
|
|
|
2492
2192
|
|
|
2493
2193
|
${run.objective}
|
|
2494
2194
|
`,
|
|
2495
|
-
actor
|
|
2195
|
+
actor,
|
|
2196
|
+
{ pathOverride: runPrimitivePath }
|
|
2496
2197
|
);
|
|
2497
2198
|
}
|
|
2498
2199
|
function syncRunPrimitive(workspacePath, run, actor) {
|
|
@@ -2539,6 +2240,14 @@ function renderRunBody(run) {
|
|
|
2539
2240
|
lines.push(run.error);
|
|
2540
2241
|
lines.push("");
|
|
2541
2242
|
}
|
|
2243
|
+
if (run.context && Object.keys(run.context).length > 0) {
|
|
2244
|
+
lines.push("## Context");
|
|
2245
|
+
lines.push("");
|
|
2246
|
+
lines.push("```json");
|
|
2247
|
+
lines.push(JSON.stringify(run.context, null, 2));
|
|
2248
|
+
lines.push("```");
|
|
2249
|
+
lines.push("");
|
|
2250
|
+
}
|
|
2542
2251
|
return lines.join("\n");
|
|
2543
2252
|
}
|
|
2544
2253
|
var RUN_STATUS_TRANSITIONS = {
|
|
@@ -2556,262 +2265,582 @@ function assertRunStatusTransition(from, to, runId) {
|
|
|
2556
2265
|
}
|
|
2557
2266
|
}
|
|
2558
2267
|
|
|
2559
|
-
// src/
|
|
2560
|
-
var
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
const spaces = options.spaces && options.spaces.length > 0 ? options.spaces : ["platform", "product", "operations"];
|
|
2567
|
-
const spacesCreated = [];
|
|
2568
|
-
for (const space of spaces) {
|
|
2569
|
-
const title = titleCase2(space);
|
|
2570
|
-
const created = create(
|
|
2571
|
-
workspacePath,
|
|
2572
|
-
"space",
|
|
2573
|
-
{
|
|
2574
|
-
title,
|
|
2575
|
-
description: `${title} workspace lane`,
|
|
2576
|
-
members: [options.actor],
|
|
2577
|
-
tags: ["onboarded"]
|
|
2578
|
-
},
|
|
2579
|
-
`# ${title}
|
|
2580
|
-
|
|
2581
|
-
Auto-created during onboarding.
|
|
2582
|
-
`,
|
|
2583
|
-
options.actor
|
|
2584
|
-
);
|
|
2585
|
-
spacesCreated.push(created.path);
|
|
2586
|
-
}
|
|
2587
|
-
const threadsCreated = [];
|
|
2588
|
-
if (options.createDemoThreads !== false) {
|
|
2589
|
-
const templates = [
|
|
2590
|
-
{ title: "Review workspace policy gates", goal: "Validate sensitive transitions are governed.", space: spacesCreated[0] },
|
|
2591
|
-
{ title: "Configure board sync cadence", goal: "Set board update expectations for all agents.", space: spacesCreated[1] ?? spacesCreated[0] },
|
|
2592
|
-
{ title: "Establish daily checkpoint routine", goal: "Agents leave actionable hand-off notes.", space: spacesCreated[2] ?? spacesCreated[0] }
|
|
2593
|
-
];
|
|
2594
|
-
for (const template of templates) {
|
|
2595
|
-
const created = create(
|
|
2596
|
-
workspacePath,
|
|
2597
|
-
"thread",
|
|
2598
|
-
{
|
|
2599
|
-
title: template.title,
|
|
2600
|
-
goal: template.goal,
|
|
2601
|
-
status: "open",
|
|
2602
|
-
priority: "medium",
|
|
2603
|
-
space: template.space,
|
|
2604
|
-
context_refs: [template.space],
|
|
2605
|
-
tags: ["onboarding"]
|
|
2606
|
-
},
|
|
2607
|
-
`## Goal
|
|
2608
|
-
|
|
2609
|
-
${template.goal}
|
|
2610
|
-
`,
|
|
2611
|
-
options.actor
|
|
2612
|
-
);
|
|
2613
|
-
threadsCreated.push(created.path);
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
const boardResult = generateKanbanBoard(workspacePath, { outputPath: "ops/Onboarding Board.md" });
|
|
2617
|
-
const commandCenterResult = generateCommandCenter(workspacePath, {
|
|
2618
|
-
outputPath: "ops/Onboarding Command Center.md",
|
|
2619
|
-
actor: options.actor
|
|
2268
|
+
// src/mcp-server.ts
|
|
2269
|
+
var DEFAULT_SERVER_NAME = "workgraph-mcp-server";
|
|
2270
|
+
var DEFAULT_SERVER_VERSION = "0.1.0";
|
|
2271
|
+
function createWorkgraphMcpServer(options) {
|
|
2272
|
+
const server = new McpServer({
|
|
2273
|
+
name: options.name ?? DEFAULT_SERVER_NAME,
|
|
2274
|
+
version: options.version ?? DEFAULT_SERVER_VERSION
|
|
2620
2275
|
});
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2276
|
+
registerResources(server, options);
|
|
2277
|
+
registerTools(server, options);
|
|
2278
|
+
return server;
|
|
2279
|
+
}
|
|
2280
|
+
async function startWorkgraphMcpServer(options) {
|
|
2281
|
+
const server = createWorkgraphMcpServer(options);
|
|
2282
|
+
const transport = new StdioServerTransport();
|
|
2283
|
+
await server.connect(transport);
|
|
2284
|
+
return server;
|
|
2285
|
+
}
|
|
2286
|
+
function registerResources(server, options) {
|
|
2287
|
+
server.registerResource(
|
|
2288
|
+
"workspace-status",
|
|
2289
|
+
"workgraph://status",
|
|
2625
2290
|
{
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2291
|
+
title: "Workgraph Status Snapshot",
|
|
2292
|
+
description: "Current thread/claim/primitive counts for the workspace.",
|
|
2293
|
+
mimeType: "application/json"
|
|
2294
|
+
},
|
|
2295
|
+
async () => {
|
|
2296
|
+
const snapshot = statusSnapshot(options.workspacePath);
|
|
2297
|
+
return {
|
|
2298
|
+
contents: [
|
|
2299
|
+
{
|
|
2300
|
+
uri: "workgraph://status",
|
|
2301
|
+
mimeType: "application/json",
|
|
2302
|
+
text: toPrettyJson(snapshot)
|
|
2303
|
+
}
|
|
2304
|
+
]
|
|
2305
|
+
};
|
|
2629
2306
|
}
|
|
2630
2307
|
);
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
"
|
|
2308
|
+
server.registerResource(
|
|
2309
|
+
"actor-brief",
|
|
2310
|
+
new ResourceTemplate("workgraph://brief/{actor}", { list: void 0 }),
|
|
2634
2311
|
{
|
|
2635
|
-
title:
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
spaces: spacesCreated,
|
|
2639
|
-
thread_refs: threadsCreated,
|
|
2640
|
-
board: boardResult.outputPath,
|
|
2641
|
-
command_center: commandCenterResult.outputPath,
|
|
2642
|
-
tags: ["onboarding"]
|
|
2312
|
+
title: "Actor Brief",
|
|
2313
|
+
description: "Actor-specific operational brief derived from workspace state.",
|
|
2314
|
+
mimeType: "application/json"
|
|
2643
2315
|
},
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
`Board: [[${boardResult.outputPath}]]`,
|
|
2658
|
-
`Command Center: [[${commandCenterResult.outputPath}]]`,
|
|
2659
|
-
""
|
|
2660
|
-
].join("\n"),
|
|
2661
|
-
options.actor
|
|
2316
|
+
async (uri, variables) => {
|
|
2317
|
+
const actor = String(variables.actor ?? options.defaultActor ?? "anonymous");
|
|
2318
|
+
const brief2 = brief(options.workspacePath, actor);
|
|
2319
|
+
return {
|
|
2320
|
+
contents: [
|
|
2321
|
+
{
|
|
2322
|
+
uri: uri.toString(),
|
|
2323
|
+
mimeType: "application/json",
|
|
2324
|
+
text: toPrettyJson(brief2)
|
|
2325
|
+
}
|
|
2326
|
+
]
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2662
2329
|
);
|
|
2663
|
-
return {
|
|
2664
|
-
actor: options.actor,
|
|
2665
|
-
spacesCreated,
|
|
2666
|
-
threadsCreated,
|
|
2667
|
-
boardPath: boardResult.outputPath,
|
|
2668
|
-
commandCenterPath: commandCenterResult.outputPath,
|
|
2669
|
-
checkpointPath: checkpointResult.path,
|
|
2670
|
-
onboardingPath: onboarding.path
|
|
2671
|
-
};
|
|
2672
2330
|
}
|
|
2673
|
-
function
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2331
|
+
function registerTools(server, options) {
|
|
2332
|
+
server.registerTool(
|
|
2333
|
+
"workgraph_status",
|
|
2334
|
+
{
|
|
2335
|
+
title: "Workgraph Status",
|
|
2336
|
+
description: "Return a compact status snapshot for the configured workspace.",
|
|
2337
|
+
annotations: {
|
|
2338
|
+
readOnlyHint: true,
|
|
2339
|
+
idempotentHint: true
|
|
2340
|
+
}
|
|
2341
|
+
},
|
|
2342
|
+
async () => {
|
|
2343
|
+
try {
|
|
2344
|
+
const snapshot = statusSnapshot(options.workspacePath);
|
|
2345
|
+
return okResult(snapshot, renderStatusSummary(snapshot));
|
|
2346
|
+
} catch (error) {
|
|
2347
|
+
return errorResult(error);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
);
|
|
2351
|
+
server.registerTool(
|
|
2352
|
+
"workgraph_brief",
|
|
2353
|
+
{
|
|
2354
|
+
title: "Workgraph Brief",
|
|
2355
|
+
description: "Return actor-centric operational brief (claims, blockers, and next work).",
|
|
2356
|
+
inputSchema: {
|
|
2357
|
+
actor: z.string().optional(),
|
|
2358
|
+
recentCount: z.number().int().min(1).max(100).optional(),
|
|
2359
|
+
nextCount: z.number().int().min(1).max(100).optional()
|
|
2360
|
+
},
|
|
2361
|
+
annotations: {
|
|
2362
|
+
readOnlyHint: true,
|
|
2363
|
+
idempotentHint: true
|
|
2364
|
+
}
|
|
2365
|
+
},
|
|
2366
|
+
async (args) => {
|
|
2367
|
+
try {
|
|
2368
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2369
|
+
const brief2 = brief(options.workspacePath, actor, {
|
|
2370
|
+
recentCount: args.recentCount,
|
|
2371
|
+
nextCount: args.nextCount
|
|
2372
|
+
});
|
|
2373
|
+
return okResult(brief2, `Brief for ${actor}: claims=${brief2.myClaims.length}, blocked=${brief2.blockedThreads.length}`);
|
|
2374
|
+
} catch (error) {
|
|
2375
|
+
return errorResult(error);
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
);
|
|
2379
|
+
server.registerTool(
|
|
2380
|
+
"workgraph_query",
|
|
2381
|
+
{
|
|
2382
|
+
title: "Workgraph Query",
|
|
2383
|
+
description: "Query primitives using multi-field filters.",
|
|
2384
|
+
inputSchema: {
|
|
2385
|
+
type: z.string().optional(),
|
|
2386
|
+
status: z.string().optional(),
|
|
2387
|
+
owner: z.string().optional(),
|
|
2388
|
+
tag: z.string().optional(),
|
|
2389
|
+
text: z.string().optional(),
|
|
2390
|
+
pathIncludes: z.string().optional(),
|
|
2391
|
+
updatedAfter: z.string().optional(),
|
|
2392
|
+
updatedBefore: z.string().optional(),
|
|
2393
|
+
createdAfter: z.string().optional(),
|
|
2394
|
+
createdBefore: z.string().optional(),
|
|
2395
|
+
limit: z.number().int().min(0).max(1e3).optional(),
|
|
2396
|
+
offset: z.number().int().min(0).max(1e4).optional()
|
|
2397
|
+
},
|
|
2398
|
+
annotations: {
|
|
2399
|
+
readOnlyHint: true,
|
|
2400
|
+
idempotentHint: true
|
|
2401
|
+
}
|
|
2402
|
+
},
|
|
2403
|
+
async (args) => {
|
|
2404
|
+
try {
|
|
2405
|
+
const results = queryPrimitives(options.workspacePath, {
|
|
2406
|
+
type: args.type,
|
|
2407
|
+
status: args.status,
|
|
2408
|
+
owner: args.owner,
|
|
2409
|
+
tag: args.tag,
|
|
2410
|
+
text: args.text,
|
|
2411
|
+
pathIncludes: args.pathIncludes,
|
|
2412
|
+
updatedAfter: args.updatedAfter,
|
|
2413
|
+
updatedBefore: args.updatedBefore,
|
|
2414
|
+
createdAfter: args.createdAfter,
|
|
2415
|
+
createdBefore: args.createdBefore,
|
|
2416
|
+
limit: args.limit,
|
|
2417
|
+
offset: args.offset
|
|
2418
|
+
});
|
|
2419
|
+
return okResult({ results, count: results.length }, `Query returned ${results.length} primitive(s).`);
|
|
2420
|
+
} catch (error) {
|
|
2421
|
+
return errorResult(error);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
);
|
|
2425
|
+
server.registerTool(
|
|
2426
|
+
"workgraph_thread_list",
|
|
2427
|
+
{
|
|
2428
|
+
title: "Thread List",
|
|
2429
|
+
description: "List workspace threads, optionally filtered by status/space/readiness.",
|
|
2430
|
+
inputSchema: {
|
|
2431
|
+
status: z.string().optional(),
|
|
2432
|
+
readyOnly: z.boolean().optional(),
|
|
2433
|
+
space: z.string().optional()
|
|
2434
|
+
},
|
|
2435
|
+
annotations: {
|
|
2436
|
+
readOnlyHint: true,
|
|
2437
|
+
idempotentHint: true
|
|
2438
|
+
}
|
|
2439
|
+
},
|
|
2440
|
+
async (args) => {
|
|
2441
|
+
try {
|
|
2442
|
+
let threads = args.space ? threadsInSpace(options.workspacePath, args.space) : list(options.workspacePath, "thread");
|
|
2443
|
+
const readySet = new Set(
|
|
2444
|
+
(args.space ? listReadyThreadsInSpace(options.workspacePath, args.space) : listReadyThreads(options.workspacePath)).map((entry) => entry.path)
|
|
2445
|
+
);
|
|
2446
|
+
if (args.status) {
|
|
2447
|
+
threads = threads.filter((entry) => String(entry.fields.status) === args.status);
|
|
2448
|
+
}
|
|
2449
|
+
if (args.readyOnly) {
|
|
2450
|
+
threads = threads.filter((entry) => readySet.has(entry.path));
|
|
2451
|
+
}
|
|
2452
|
+
const enriched = threads.map((entry) => ({
|
|
2453
|
+
...entry,
|
|
2454
|
+
ready: readySet.has(entry.path)
|
|
2455
|
+
}));
|
|
2456
|
+
return okResult({ threads: enriched, count: enriched.length }, `Thread list returned ${enriched.length} item(s).`);
|
|
2457
|
+
} catch (error) {
|
|
2458
|
+
return errorResult(error);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
);
|
|
2462
|
+
server.registerTool(
|
|
2463
|
+
"workgraph_thread_show",
|
|
2464
|
+
{
|
|
2465
|
+
title: "Thread Show",
|
|
2466
|
+
description: "Read one thread and its ledger history.",
|
|
2467
|
+
inputSchema: {
|
|
2468
|
+
threadPath: z.string().min(1)
|
|
2469
|
+
},
|
|
2470
|
+
annotations: {
|
|
2471
|
+
readOnlyHint: true,
|
|
2472
|
+
idempotentHint: true
|
|
2473
|
+
}
|
|
2474
|
+
},
|
|
2475
|
+
async (args) => {
|
|
2476
|
+
try {
|
|
2477
|
+
const threadEntry = read(options.workspacePath, args.threadPath);
|
|
2478
|
+
if (!threadEntry) {
|
|
2479
|
+
return errorResult(`Thread not found: ${args.threadPath}`);
|
|
2480
|
+
}
|
|
2481
|
+
const history = historyOf(options.workspacePath, args.threadPath);
|
|
2482
|
+
return okResult({ thread: threadEntry, history }, `Thread ${args.threadPath} has ${history.length} ledger event(s).`);
|
|
2483
|
+
} catch (error) {
|
|
2484
|
+
return errorResult(error);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
);
|
|
2488
|
+
server.registerTool(
|
|
2489
|
+
"workgraph_ledger_recent",
|
|
2490
|
+
{
|
|
2491
|
+
title: "Ledger Recent",
|
|
2492
|
+
description: "Read recent ledger events.",
|
|
2493
|
+
inputSchema: {
|
|
2494
|
+
count: z.number().int().min(1).max(500).optional(),
|
|
2495
|
+
actor: z.string().optional()
|
|
2496
|
+
},
|
|
2497
|
+
annotations: {
|
|
2498
|
+
readOnlyHint: true,
|
|
2499
|
+
idempotentHint: true
|
|
2500
|
+
}
|
|
2501
|
+
},
|
|
2502
|
+
async (args) => {
|
|
2503
|
+
try {
|
|
2504
|
+
let entries = recent(options.workspacePath, args.count ?? 20);
|
|
2505
|
+
if (args.actor) {
|
|
2506
|
+
entries = entries.filter((entry) => entry.actor === args.actor);
|
|
2507
|
+
}
|
|
2508
|
+
return okResult({ entries, count: entries.length }, `Ledger returned ${entries.length} event(s).`);
|
|
2509
|
+
} catch (error) {
|
|
2510
|
+
return errorResult(error);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
);
|
|
2514
|
+
server.registerTool(
|
|
2515
|
+
"workgraph_graph_hygiene",
|
|
2516
|
+
{
|
|
2517
|
+
title: "Graph Hygiene",
|
|
2518
|
+
description: "Generate wiki-link graph hygiene report.",
|
|
2519
|
+
annotations: {
|
|
2520
|
+
readOnlyHint: true,
|
|
2521
|
+
idempotentHint: true
|
|
2522
|
+
}
|
|
2523
|
+
},
|
|
2524
|
+
async () => {
|
|
2525
|
+
try {
|
|
2526
|
+
const report = graphHygieneReport(options.workspacePath);
|
|
2527
|
+
return okResult(
|
|
2528
|
+
report,
|
|
2529
|
+
`Graph hygiene: nodes=${report.nodeCount}, edges=${report.edgeCount}, orphans=${report.orphanCount}, broken=${report.brokenLinkCount}`
|
|
2530
|
+
);
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
return errorResult(error);
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
);
|
|
2536
|
+
server.registerTool(
|
|
2537
|
+
"workgraph_thread_claim",
|
|
2538
|
+
{
|
|
2539
|
+
title: "Thread Claim",
|
|
2540
|
+
description: "Claim a thread for an actor (policy-scoped write).",
|
|
2541
|
+
inputSchema: {
|
|
2542
|
+
threadPath: z.string().min(1),
|
|
2543
|
+
actor: z.string().optional()
|
|
2544
|
+
},
|
|
2545
|
+
annotations: {
|
|
2546
|
+
destructiveHint: true,
|
|
2547
|
+
idempotentHint: false
|
|
2548
|
+
}
|
|
2549
|
+
},
|
|
2550
|
+
async (args) => {
|
|
2551
|
+
try {
|
|
2552
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2553
|
+
const gate = checkWriteGate(options, actor, ["thread:claim", "mcp:write"]);
|
|
2554
|
+
if (!gate.allowed) return errorResult(gate.reason);
|
|
2555
|
+
const updated = claim(options.workspacePath, args.threadPath, actor);
|
|
2556
|
+
return okResult({ thread: updated }, `Claimed ${updated.path} as ${actor}.`);
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
return errorResult(error);
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
);
|
|
2562
|
+
server.registerTool(
|
|
2563
|
+
"workgraph_thread_done",
|
|
2564
|
+
{
|
|
2565
|
+
title: "Thread Done",
|
|
2566
|
+
description: "Mark a thread as done with output summary (policy-scoped write).",
|
|
2567
|
+
inputSchema: {
|
|
2568
|
+
threadPath: z.string().min(1),
|
|
2569
|
+
actor: z.string().optional(),
|
|
2570
|
+
output: z.string().optional()
|
|
2571
|
+
},
|
|
2572
|
+
annotations: {
|
|
2573
|
+
destructiveHint: true,
|
|
2574
|
+
idempotentHint: false
|
|
2575
|
+
}
|
|
2576
|
+
},
|
|
2577
|
+
async (args) => {
|
|
2578
|
+
try {
|
|
2579
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2580
|
+
const gate = checkWriteGate(options, actor, ["thread:done", "mcp:write"]);
|
|
2581
|
+
if (!gate.allowed) return errorResult(gate.reason);
|
|
2582
|
+
const updated = done(options.workspacePath, args.threadPath, actor, args.output);
|
|
2583
|
+
return okResult({ thread: updated }, `Marked ${updated.path} done as ${actor}.`);
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
return errorResult(error);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
);
|
|
2589
|
+
server.registerTool(
|
|
2590
|
+
"workgraph_checkpoint_create",
|
|
2591
|
+
{
|
|
2592
|
+
title: "Checkpoint Create",
|
|
2593
|
+
description: "Create a checkpoint primitive for hand-off continuity (policy-scoped write).",
|
|
2594
|
+
inputSchema: {
|
|
2595
|
+
actor: z.string().optional(),
|
|
2596
|
+
summary: z.string().min(1),
|
|
2597
|
+
next: z.array(z.string()).optional(),
|
|
2598
|
+
blocked: z.array(z.string()).optional(),
|
|
2599
|
+
tags: z.array(z.string()).optional()
|
|
2600
|
+
},
|
|
2601
|
+
annotations: {
|
|
2602
|
+
destructiveHint: true,
|
|
2603
|
+
idempotentHint: false
|
|
2604
|
+
}
|
|
2605
|
+
},
|
|
2606
|
+
async (args) => {
|
|
2607
|
+
try {
|
|
2608
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2609
|
+
const gate = checkWriteGate(options, actor, ["checkpoint:create", "mcp:write"]);
|
|
2610
|
+
if (!gate.allowed) return errorResult(gate.reason);
|
|
2611
|
+
const checkpoint2 = checkpoint(options.workspacePath, actor, args.summary, {
|
|
2612
|
+
next: args.next,
|
|
2613
|
+
blocked: args.blocked,
|
|
2614
|
+
tags: args.tags
|
|
2615
|
+
});
|
|
2616
|
+
return okResult({ checkpoint: checkpoint2 }, `Created checkpoint ${checkpoint2.path}.`);
|
|
2617
|
+
} catch (error) {
|
|
2618
|
+
return errorResult(error);
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
);
|
|
2622
|
+
server.registerTool(
|
|
2623
|
+
"workgraph_dispatch_create",
|
|
2624
|
+
{
|
|
2625
|
+
title: "Dispatch Create",
|
|
2626
|
+
description: "Create a dispatch run request (policy-scoped write).",
|
|
2627
|
+
inputSchema: {
|
|
2628
|
+
actor: z.string().optional(),
|
|
2629
|
+
objective: z.string().min(1),
|
|
2630
|
+
adapter: z.string().optional(),
|
|
2631
|
+
idempotencyKey: z.string().optional()
|
|
2632
|
+
},
|
|
2633
|
+
annotations: {
|
|
2634
|
+
destructiveHint: true,
|
|
2635
|
+
idempotentHint: false
|
|
2636
|
+
}
|
|
2637
|
+
},
|
|
2638
|
+
async (args) => {
|
|
2639
|
+
try {
|
|
2640
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2641
|
+
const gate = checkWriteGate(options, actor, ["dispatch:run", "mcp:write"]);
|
|
2642
|
+
if (!gate.allowed) return errorResult(gate.reason);
|
|
2643
|
+
const run = createRun(options.workspacePath, {
|
|
2644
|
+
actor,
|
|
2645
|
+
objective: args.objective,
|
|
2646
|
+
adapter: args.adapter,
|
|
2647
|
+
idempotencyKey: args.idempotencyKey
|
|
2648
|
+
});
|
|
2649
|
+
return okResult({ run }, `Created run ${run.id} (${run.status}).`);
|
|
2650
|
+
} catch (error) {
|
|
2651
|
+
return errorResult(error);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
);
|
|
2655
|
+
server.registerTool(
|
|
2656
|
+
"workgraph_dispatch_execute",
|
|
2657
|
+
{
|
|
2658
|
+
title: "Dispatch Execute",
|
|
2659
|
+
description: "Execute one queued/running run through its adapter (policy-scoped write).",
|
|
2660
|
+
inputSchema: {
|
|
2661
|
+
actor: z.string().optional(),
|
|
2662
|
+
runId: z.string().min(1),
|
|
2663
|
+
agents: z.array(z.string()).optional(),
|
|
2664
|
+
maxSteps: z.number().int().min(1).max(5e3).optional(),
|
|
2665
|
+
stepDelayMs: z.number().int().min(0).max(5e3).optional(),
|
|
2666
|
+
space: z.string().optional(),
|
|
2667
|
+
createCheckpoint: z.boolean().optional()
|
|
2668
|
+
},
|
|
2669
|
+
annotations: {
|
|
2670
|
+
destructiveHint: true,
|
|
2671
|
+
idempotentHint: false
|
|
2672
|
+
}
|
|
2673
|
+
},
|
|
2674
|
+
async (args) => {
|
|
2675
|
+
try {
|
|
2676
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2677
|
+
const gate = checkWriteGate(options, actor, ["dispatch:run", "mcp:write"]);
|
|
2678
|
+
if (!gate.allowed) return errorResult(gate.reason);
|
|
2679
|
+
const run = await executeRun(options.workspacePath, args.runId, {
|
|
2680
|
+
actor,
|
|
2681
|
+
agents: args.agents,
|
|
2682
|
+
maxSteps: args.maxSteps,
|
|
2683
|
+
stepDelayMs: args.stepDelayMs,
|
|
2684
|
+
space: args.space,
|
|
2685
|
+
createCheckpoint: args.createCheckpoint
|
|
2686
|
+
});
|
|
2687
|
+
return okResult({ run }, `Executed run ${run.id} -> ${run.status}.`);
|
|
2688
|
+
} catch (error) {
|
|
2689
|
+
return errorResult(error);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
);
|
|
2693
|
+
server.registerTool(
|
|
2694
|
+
"workgraph_dispatch_followup",
|
|
2695
|
+
{
|
|
2696
|
+
title: "Dispatch Follow-up",
|
|
2697
|
+
description: "Send follow-up input to a run (policy-scoped write).",
|
|
2698
|
+
inputSchema: {
|
|
2699
|
+
actor: z.string().optional(),
|
|
2700
|
+
runId: z.string().min(1),
|
|
2701
|
+
input: z.string().min(1)
|
|
2702
|
+
},
|
|
2703
|
+
annotations: {
|
|
2704
|
+
destructiveHint: true,
|
|
2705
|
+
idempotentHint: false
|
|
2706
|
+
}
|
|
2707
|
+
},
|
|
2708
|
+
async (args) => {
|
|
2709
|
+
try {
|
|
2710
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2711
|
+
const gate = checkWriteGate(options, actor, ["dispatch:run", "mcp:write"]);
|
|
2712
|
+
if (!gate.allowed) return errorResult(gate.reason);
|
|
2713
|
+
const run = followup(options.workspacePath, args.runId, actor, args.input);
|
|
2714
|
+
return okResult({ run }, `Follow-up recorded for ${run.id}.`);
|
|
2715
|
+
} catch (error) {
|
|
2716
|
+
return errorResult(error);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
);
|
|
2720
|
+
server.registerTool(
|
|
2721
|
+
"workgraph_dispatch_stop",
|
|
2722
|
+
{
|
|
2723
|
+
title: "Dispatch Stop",
|
|
2724
|
+
description: "Stop/cancel a run (policy-scoped write).",
|
|
2725
|
+
inputSchema: {
|
|
2726
|
+
actor: z.string().optional(),
|
|
2727
|
+
runId: z.string().min(1)
|
|
2728
|
+
},
|
|
2729
|
+
annotations: {
|
|
2730
|
+
destructiveHint: true,
|
|
2731
|
+
idempotentHint: false
|
|
2732
|
+
}
|
|
2733
|
+
},
|
|
2734
|
+
async (args) => {
|
|
2735
|
+
try {
|
|
2736
|
+
const actor = resolveActor(args.actor, options.defaultActor);
|
|
2737
|
+
const gate = checkWriteGate(options, actor, ["dispatch:run", "mcp:write"]);
|
|
2738
|
+
if (!gate.allowed) return errorResult(gate.reason);
|
|
2739
|
+
const run = stop(options.workspacePath, args.runId, actor);
|
|
2740
|
+
return okResult({ run }, `Stopped run ${run.id}.`);
|
|
2741
|
+
} catch (error) {
|
|
2742
|
+
return errorResult(error);
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2690
2745
|
);
|
|
2691
2746
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
completed: []
|
|
2696
|
-
};
|
|
2697
|
-
function titleCase2(value) {
|
|
2698
|
-
return value.split(/[-_\s]/g).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
|
|
2747
|
+
function resolveActor(actor, defaultActor) {
|
|
2748
|
+
const resolved = actor ?? defaultActor ?? "anonymous";
|
|
2749
|
+
return String(resolved);
|
|
2699
2750
|
}
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
var search_qmd_adapter_exports = {};
|
|
2703
|
-
__export(search_qmd_adapter_exports, {
|
|
2704
|
-
search: () => search
|
|
2705
|
-
});
|
|
2706
|
-
function search(workspacePath, text, options = {}) {
|
|
2707
|
-
const requestedMode = options.mode ?? "auto";
|
|
2708
|
-
const qmdEnabled = process.env.WORKGRAPH_QMD_ENDPOINT && process.env.WORKGRAPH_QMD_ENDPOINT.trim().length > 0;
|
|
2709
|
-
if (requestedMode === "qmd" && !qmdEnabled) {
|
|
2710
|
-
const results = keywordSearch(workspacePath, text, {
|
|
2711
|
-
type: options.type,
|
|
2712
|
-
limit: options.limit
|
|
2713
|
-
});
|
|
2751
|
+
function checkWriteGate(options, actor, requiredCapabilities) {
|
|
2752
|
+
if (options.readOnly) {
|
|
2714
2753
|
return {
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
results,
|
|
2718
|
-
fallbackReason: "QMD mode requested but WORKGRAPH_QMD_ENDPOINT is not configured."
|
|
2754
|
+
allowed: false,
|
|
2755
|
+
reason: "MCP server is configured read-only; write tool is disabled."
|
|
2719
2756
|
};
|
|
2720
2757
|
}
|
|
2721
|
-
if (
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2758
|
+
if (actor === "system") {
|
|
2759
|
+
return { allowed: true };
|
|
2760
|
+
}
|
|
2761
|
+
const party = getParty(options.workspacePath, actor);
|
|
2762
|
+
if (!party) {
|
|
2726
2763
|
return {
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
results,
|
|
2730
|
-
fallbackReason: "QMD endpoint configured; using core-compatible local ranking in MVP."
|
|
2764
|
+
allowed: false,
|
|
2765
|
+
reason: `Policy gate blocked MCP write: actor "${actor}" is not a registered party.`
|
|
2731
2766
|
};
|
|
2732
2767
|
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
type: options.type,
|
|
2736
|
-
limit: options.limit
|
|
2737
|
-
});
|
|
2768
|
+
const hasCapability = requiredCapabilities.some((capability) => party.capabilities.includes(capability));
|
|
2769
|
+
if (!hasCapability) {
|
|
2738
2770
|
return {
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
results,
|
|
2742
|
-
fallbackReason: "Auto mode selected; QMD endpoint detected; using core-compatible local ranking in MVP."
|
|
2771
|
+
allowed: false,
|
|
2772
|
+
reason: `Policy gate blocked MCP write: actor "${actor}" lacks capabilities [${requiredCapabilities.join(", ")}].`
|
|
2743
2773
|
};
|
|
2744
2774
|
}
|
|
2775
|
+
return { allowed: true };
|
|
2776
|
+
}
|
|
2777
|
+
function okResult(data, summary) {
|
|
2745
2778
|
return {
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2779
|
+
content: [
|
|
2780
|
+
{
|
|
2781
|
+
type: "text",
|
|
2782
|
+
text: `${summary}
|
|
2783
|
+
|
|
2784
|
+
${toPrettyJson(data)}`
|
|
2785
|
+
}
|
|
2786
|
+
],
|
|
2787
|
+
structuredContent: data
|
|
2752
2788
|
};
|
|
2753
2789
|
}
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
var trigger_exports = {};
|
|
2757
|
-
__export(trigger_exports, {
|
|
2758
|
-
fireTrigger: () => fireTrigger
|
|
2759
|
-
});
|
|
2760
|
-
import { createHash } from "crypto";
|
|
2761
|
-
function fireTrigger(workspacePath, triggerPath, options) {
|
|
2762
|
-
const trigger = read(workspacePath, triggerPath);
|
|
2763
|
-
if (!trigger) throw new Error(`Trigger not found: ${triggerPath}`);
|
|
2764
|
-
if (trigger.type !== "trigger") throw new Error(`Target is not a trigger primitive: ${triggerPath}`);
|
|
2765
|
-
const triggerStatus = String(trigger.fields.status ?? "draft");
|
|
2766
|
-
if (!["approved", "active"].includes(triggerStatus)) {
|
|
2767
|
-
throw new Error(`Trigger must be approved/active to fire. Current status: ${triggerStatus}`);
|
|
2768
|
-
}
|
|
2769
|
-
const objective = options.objective ?? `Trigger ${String(trigger.fields.title ?? triggerPath)} fired action ${String(trigger.fields.action ?? "run")}`;
|
|
2770
|
-
const eventSeed = options.eventKey ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2771
|
-
const idempotencyKey = buildIdempotencyKey(triggerPath, eventSeed, objective);
|
|
2772
|
-
const run = createRun(workspacePath, {
|
|
2773
|
-
actor: options.actor,
|
|
2774
|
-
objective,
|
|
2775
|
-
context: {
|
|
2776
|
-
trigger_path: triggerPath,
|
|
2777
|
-
trigger_event: String(trigger.fields.event ?? ""),
|
|
2778
|
-
...options.context
|
|
2779
|
-
},
|
|
2780
|
-
idempotencyKey
|
|
2781
|
-
});
|
|
2782
|
-
append(workspacePath, options.actor, "create", triggerPath, "trigger", {
|
|
2783
|
-
fired: true,
|
|
2784
|
-
event_key: eventSeed,
|
|
2785
|
-
run_id: run.id,
|
|
2786
|
-
idempotency_key: idempotencyKey
|
|
2787
|
-
});
|
|
2790
|
+
function errorResult(error) {
|
|
2791
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
2788
2792
|
return {
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2793
|
+
isError: true,
|
|
2794
|
+
content: [
|
|
2795
|
+
{
|
|
2796
|
+
type: "text",
|
|
2797
|
+
text
|
|
2798
|
+
}
|
|
2799
|
+
]
|
|
2792
2800
|
};
|
|
2793
2801
|
}
|
|
2794
|
-
function
|
|
2795
|
-
return
|
|
2802
|
+
function toPrettyJson(value) {
|
|
2803
|
+
return JSON.stringify(value, null, 2);
|
|
2804
|
+
}
|
|
2805
|
+
function renderStatusSummary(snapshot) {
|
|
2806
|
+
return [
|
|
2807
|
+
`threads(total=${snapshot.threads.total}, open=${snapshot.threads.open}, active=${snapshot.threads.active}, blocked=${snapshot.threads.blocked}, done=${snapshot.threads.done})`,
|
|
2808
|
+
`claims(active=${snapshot.claims.active})`,
|
|
2809
|
+
`primitives(total=${snapshot.primitives.total})`
|
|
2810
|
+
].join(" ");
|
|
2796
2811
|
}
|
|
2797
2812
|
|
|
2798
2813
|
export {
|
|
2814
|
+
__export,
|
|
2799
2815
|
THREAD_STATUS_TRANSITIONS,
|
|
2816
|
+
append,
|
|
2817
|
+
historyOf,
|
|
2818
|
+
allClaims,
|
|
2819
|
+
recent,
|
|
2800
2820
|
ledger_exports,
|
|
2821
|
+
loadRegistry,
|
|
2822
|
+
saveRegistry,
|
|
2823
|
+
listTypes,
|
|
2801
2824
|
registry_exports,
|
|
2825
|
+
refreshWikiLinkGraphIndex,
|
|
2802
2826
|
graph_exports,
|
|
2827
|
+
loadPolicyRegistry,
|
|
2803
2828
|
policy_exports,
|
|
2829
|
+
create,
|
|
2830
|
+
read,
|
|
2831
|
+
list,
|
|
2832
|
+
update,
|
|
2804
2833
|
store_exports,
|
|
2834
|
+
createThread,
|
|
2805
2835
|
thread_exports,
|
|
2806
|
-
|
|
2807
|
-
workspace_exports,
|
|
2808
|
-
command_center_exports,
|
|
2809
|
-
skill_exports,
|
|
2836
|
+
keywordSearch,
|
|
2810
2837
|
query_exports,
|
|
2838
|
+
checkpoint,
|
|
2811
2839
|
orientation_exports,
|
|
2812
|
-
|
|
2840
|
+
CursorCloudAdapter,
|
|
2841
|
+
createRun,
|
|
2813
2842
|
dispatch_exports,
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2843
|
+
createWorkgraphMcpServer,
|
|
2844
|
+
startWorkgraphMcpServer,
|
|
2845
|
+
mcp_server_exports
|
|
2817
2846
|
};
|