clawvault 3.1.0 → 3.2.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 +422 -141
- package/bin/clawvault.js +10 -2
- package/bin/command-registration.test.js +3 -1
- package/bin/command-runtime.js +9 -1
- package/bin/register-core-commands.js +23 -28
- package/bin/register-maintenance-commands.js +39 -3
- package/bin/register-query-commands.js +58 -29
- package/bin/register-tailscale-commands.js +106 -0
- package/bin/register-task-commands.js +18 -1
- package/bin/register-task-commands.test.js +16 -0
- package/bin/register-vault-operations-commands.js +29 -1
- package/bin/register-workgraph-commands.js +1368 -0
- package/dashboard/lib/graph-diff.js +104 -0
- package/dashboard/lib/graph-diff.test.js +75 -0
- package/dashboard/lib/vault-parser.js +556 -0
- package/dashboard/lib/vault-parser.test.js +254 -0
- package/dashboard/public/app.js +796 -0
- package/dashboard/public/index.html +52 -0
- package/dashboard/public/styles.css +221 -0
- package/dashboard/server.js +374 -0
- package/dist/{chunk-F2JEUD4J.js → chunk-23YDQ3QU.js} +6 -8
- package/dist/{chunk-C7OK5WKP.js → chunk-2JQ3O2YL.js} +4 -4
- package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
- package/dist/chunk-2ZDO52B4.js +52 -0
- package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
- package/dist/chunk-33VSQP4J.js +37 -0
- package/dist/chunk-4BQTQMJP.js +93 -0
- package/dist/{chunk-GUKMRGM7.js → chunk-4OXMU5S2.js} +1 -1
- package/dist/{chunk-62YTUT6J.js → chunk-4PY655YM.js} +15 -3
- package/dist/chunk-6FH3IULF.js +352 -0
- package/dist/{chunk-3NSBOUT3.js → chunk-77Q5CSPJ.js} +404 -80
- package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
- package/dist/chunk-BSJ6RIT7.js +447 -0
- package/dist/chunk-BUEW6IIK.js +364 -0
- package/dist/{chunk-LI4O6NVK.js → chunk-CLJTREDS.js} +74 -14
- package/dist/chunk-EK6S23ZB.js +469 -0
- package/dist/{chunk-LNJA2UGL.js → chunk-ESFLMDRB.js} +9 -86
- package/dist/{chunk-H34S76MB.js → chunk-ESVS6K2B.js} +6 -6
- package/dist/{chunk-WAZ3NLWL.js → chunk-F55HGNU4.js} +0 -47
- package/dist/{chunk-QK3UCXWL.js → chunk-FHFUXL6G.js} +2 -2
- package/dist/{chunk-H62BP7RI.js → chunk-GAOWA7GR.js} +212 -46
- package/dist/chunk-GGA32J2R.js +784 -0
- package/dist/chunk-GNJL4YGR.js +79 -0
- package/dist/chunk-IVRIKYFE.js +520 -0
- package/dist/chunk-MDIH26GC.js +183 -0
- package/dist/{chunk-LYHGEHXG.js → chunk-MFAWT5O5.js} +0 -1
- package/dist/chunk-MM6QGW3P.js +207 -0
- package/dist/{chunk-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
- package/dist/chunk-NCKFNBHJ.js +257 -0
- package/dist/{chunk-QBLMXKF2.js → chunk-OIWVQYQF.js} +1 -1
- package/dist/{chunk-42MXU7A6.js → chunk-P62WHA27.js} +58 -47
- package/dist/chunk-PBACDKKP.js +66 -0
- package/dist/{chunk-VGLOTGAS.js → chunk-QSHD36LH.js} +2 -2
- package/dist/{chunk-OZ7RIXTO.js → chunk-QSRRMEYM.js} +2 -2
- package/dist/chunk-QVEERJSP.js +152 -0
- package/dist/{chunk-N2AXRYLC.js → chunk-QWQ3TIKS.js} +1 -1
- package/dist/{chunk-3DHXQHYG.js → chunk-R2MIW5G7.js} +1 -1
- package/dist/{chunk-SJSFRIYS.js → chunk-SLXOR3CC.js} +2 -2
- package/dist/chunk-SS4B7P7V.js +99 -0
- package/dist/{chunk-JY6FYXIT.js → chunk-STCQGCEQ.js} +6 -11
- package/dist/chunk-TIGW564L.js +628 -0
- package/dist/chunk-U4O6C46S.js +154 -0
- package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
- package/dist/chunk-VSL7KY3M.js +189 -0
- package/dist/{chunk-U55BGUAU.js → chunk-W4SPAEE7.js} +6 -6
- package/dist/chunk-WMGIIABP.js +15 -0
- package/dist/{chunk-33UGEQRT.js → chunk-X3SPPUFG.js} +151 -64
- package/dist/chunk-Y6VJKXGL.js +373 -0
- package/dist/{chunk-3WRJEKN4.js → chunk-ZN54U2OZ.js} +123 -10
- package/dist/cli/index.js +34 -24
- package/dist/commands/archive.js +3 -3
- package/dist/commands/backlog.js +3 -3
- package/dist/commands/blocked.js +3 -3
- package/dist/commands/canvas.d.ts +15 -0
- package/dist/commands/canvas.js +200 -0
- package/dist/commands/checkpoint.js +2 -2
- package/dist/commands/compat.js +2 -2
- package/dist/commands/context.js +8 -6
- package/dist/commands/doctor.d.ts +11 -7
- package/dist/commands/doctor.js +18 -16
- package/dist/commands/embed.js +5 -6
- package/dist/commands/entities.js +2 -2
- package/dist/commands/graph.js +4 -4
- package/dist/commands/inject.d.ts +1 -1
- package/dist/commands/inject.js +5 -6
- package/dist/commands/kanban.js +4 -4
- package/dist/commands/link.js +5 -5
- package/dist/commands/migrate-observations.js +4 -4
- package/dist/commands/observe.d.ts +0 -1
- package/dist/commands/observe.js +14 -13
- package/dist/commands/project.js +5 -5
- package/dist/commands/rebuild-embeddings.d.ts +21 -0
- package/dist/commands/rebuild-embeddings.js +91 -0
- package/dist/commands/rebuild.js +12 -11
- package/dist/commands/recover.js +3 -3
- package/dist/commands/reflect.js +6 -7
- package/dist/commands/repair-session.js +1 -1
- package/dist/commands/replay.js +14 -14
- package/dist/commands/session-recap.js +1 -1
- package/dist/commands/setup.d.ts +2 -89
- package/dist/commands/setup.js +3 -21
- package/dist/commands/shell-init.js +1 -1
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +20 -19
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +57 -35
- package/dist/commands/sync-bd.d.ts +10 -0
- package/dist/commands/sync-bd.js +10 -0
- package/dist/commands/tailscale.d.ts +52 -0
- package/dist/commands/tailscale.js +26 -0
- package/dist/commands/task.js +4 -4
- package/dist/commands/template.js +2 -2
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +11 -10
- package/dist/commands/workgraph.d.ts +124 -0
- package/dist/commands/workgraph.js +38 -0
- package/dist/index.d.ts +341 -191
- package/dist/index.js +446 -116
- package/dist/{inject-Bzi5E-By.d.ts → inject-DYUrDqQO.d.ts} +3 -3
- package/dist/ledger-B7g7jhqG.d.ts +44 -0
- package/dist/lib/auto-linker.js +2 -2
- package/dist/lib/canvas-layout.d.ts +115 -0
- package/dist/lib/canvas-layout.js +35 -0
- package/dist/lib/config.d.ts +27 -3
- package/dist/lib/config.js +4 -2
- package/dist/lib/entity-index.js +1 -1
- package/dist/lib/project-utils.js +4 -4
- package/dist/lib/session-repair.js +1 -1
- package/dist/lib/session-utils.js +1 -1
- package/dist/lib/tailscale.d.ts +225 -0
- package/dist/lib/tailscale.js +50 -0
- package/dist/lib/task-utils.js +3 -3
- package/dist/lib/template-engine.js +1 -1
- package/dist/lib/webdav.d.ts +109 -0
- package/dist/lib/webdav.js +35 -0
- package/dist/onnxruntime_binding-5QEF3SUC.node +0 -0
- package/dist/onnxruntime_binding-BKPKNEGC.node +0 -0
- package/dist/onnxruntime_binding-FMOXGIUT.node +0 -0
- package/dist/onnxruntime_binding-OI2KMXC5.node +0 -0
- package/dist/onnxruntime_binding-UX44MLAZ.node +0 -0
- package/dist/onnxruntime_binding-Y2W7N7WY.node +0 -0
- package/dist/openclaw-plugin.d.ts +8 -0
- package/dist/openclaw-plugin.js +14 -0
- package/dist/registry-BR4326o0.d.ts +30 -0
- package/dist/store-CA-6sKCJ.d.ts +34 -0
- package/dist/thread-B9LhXNU0.d.ts +41 -0
- package/dist/transformers.node-A2ZRORSQ.js +46775 -0
- package/dist/{types-Y2_Um2Ls.d.ts → types-BbWJoC1c.d.ts} +1 -44
- package/dist/workgraph/index.d.ts +5 -0
- package/dist/workgraph/index.js +23 -0
- package/dist/workgraph/ledger.d.ts +2 -0
- package/dist/workgraph/ledger.js +25 -0
- package/dist/workgraph/registry.d.ts +2 -0
- package/dist/workgraph/registry.js +19 -0
- package/dist/workgraph/store.d.ts +2 -0
- package/dist/workgraph/store.js +25 -0
- package/dist/workgraph/thread.d.ts +2 -0
- package/dist/workgraph/thread.js +25 -0
- package/dist/workgraph/types.d.ts +54 -0
- package/dist/workgraph/types.js +7 -0
- package/hooks/clawvault/HOOK.md +113 -0
- package/hooks/clawvault/handler.js +1561 -0
- package/hooks/clawvault/handler.test.js +510 -0
- package/hooks/clawvault/openclaw.plugin.json +72 -0
- package/openclaw.plugin.json +65 -38
- package/package.json +25 -22
- package/dist/chunk-3RG5ZIWI.js +0 -10
- package/dist/chunk-3ZIH425O.js +0 -871
- package/dist/chunk-6U6MK36V.js +0 -205
- package/dist/chunk-CMB7UL7C.js +0 -327
- package/dist/chunk-D2H45LON.js +0 -1074
- package/dist/chunk-E7MFQB6D.js +0 -163
- package/dist/chunk-GQSLDZTS.js +0 -560
- package/dist/chunk-MFM6K7PU.js +0 -374
- package/dist/chunk-MXSSG3QU.js +0 -42
- package/dist/chunk-OCGVIN3L.js +0 -88
- package/dist/chunk-PAH27GSN.js +0 -108
- package/dist/chunk-YCUNCH2I.js +0 -78
- package/dist/cli/index.cjs +0 -8584
- package/dist/cli/index.d.cts +0 -5
- package/dist/commands/archive.cjs +0 -287
- package/dist/commands/archive.d.cts +0 -11
- package/dist/commands/backlog.cjs +0 -721
- package/dist/commands/backlog.d.cts +0 -53
- package/dist/commands/blocked.cjs +0 -204
- package/dist/commands/blocked.d.cts +0 -26
- package/dist/commands/checkpoint.cjs +0 -244
- package/dist/commands/checkpoint.d.cts +0 -41
- package/dist/commands/compat.cjs +0 -294
- package/dist/commands/compat.d.cts +0 -28
- package/dist/commands/context.cjs +0 -2990
- package/dist/commands/context.d.cts +0 -2
- package/dist/commands/doctor.cjs +0 -2986
- package/dist/commands/doctor.d.cts +0 -21
- package/dist/commands/embed.cjs +0 -232
- package/dist/commands/embed.d.cts +0 -17
- package/dist/commands/entities.cjs +0 -141
- package/dist/commands/entities.d.cts +0 -7
- package/dist/commands/graph.cjs +0 -501
- package/dist/commands/graph.d.cts +0 -21
- package/dist/commands/inject.cjs +0 -1636
- package/dist/commands/inject.d.cts +0 -2
- package/dist/commands/kanban.cjs +0 -884
- package/dist/commands/kanban.d.cts +0 -63
- package/dist/commands/link.cjs +0 -965
- package/dist/commands/link.d.cts +0 -11
- package/dist/commands/migrate-observations.cjs +0 -362
- package/dist/commands/migrate-observations.d.cts +0 -19
- package/dist/commands/observe.cjs +0 -4099
- package/dist/commands/observe.d.cts +0 -23
- package/dist/commands/project.cjs +0 -1341
- package/dist/commands/project.d.cts +0 -85
- package/dist/commands/rebuild.cjs +0 -3136
- package/dist/commands/rebuild.d.cts +0 -11
- package/dist/commands/recover.cjs +0 -361
- package/dist/commands/recover.d.cts +0 -38
- package/dist/commands/reflect.cjs +0 -1008
- package/dist/commands/reflect.d.cts +0 -11
- package/dist/commands/repair-session.cjs +0 -457
- package/dist/commands/repair-session.d.cts +0 -38
- package/dist/commands/replay.cjs +0 -4103
- package/dist/commands/replay.d.cts +0 -16
- package/dist/commands/session-recap.cjs +0 -353
- package/dist/commands/session-recap.d.cts +0 -27
- package/dist/commands/setup.cjs +0 -1278
- package/dist/commands/setup.d.cts +0 -99
- package/dist/commands/shell-init.cjs +0 -75
- package/dist/commands/shell-init.d.cts +0 -7
- package/dist/commands/sleep.cjs +0 -6029
- package/dist/commands/sleep.d.cts +0 -36
- package/dist/commands/status.cjs +0 -2737
- package/dist/commands/status.d.cts +0 -52
- package/dist/commands/task.cjs +0 -1236
- package/dist/commands/task.d.cts +0 -97
- package/dist/commands/template.cjs +0 -457
- package/dist/commands/template.d.cts +0 -36
- package/dist/commands/wake.cjs +0 -2627
- package/dist/commands/wake.d.cts +0 -22
- package/dist/context-BUGaWpyL.d.cts +0 -46
- package/dist/index.cjs +0 -12373
- package/dist/index.d.cts +0 -854
- package/dist/inject-Bzi5E-By.d.cts +0 -137
- package/dist/lib/auto-linker.cjs +0 -176
- package/dist/lib/auto-linker.d.cts +0 -26
- package/dist/lib/config.cjs +0 -78
- package/dist/lib/config.d.cts +0 -11
- package/dist/lib/entity-index.cjs +0 -84
- package/dist/lib/entity-index.d.cts +0 -26
- package/dist/lib/project-utils.cjs +0 -864
- package/dist/lib/project-utils.d.cts +0 -97
- package/dist/lib/session-repair.cjs +0 -239
- package/dist/lib/session-repair.d.cts +0 -110
- package/dist/lib/session-utils.cjs +0 -209
- package/dist/lib/session-utils.d.cts +0 -63
- package/dist/lib/task-utils.cjs +0 -1137
- package/dist/lib/task-utils.d.cts +0 -208
- package/dist/lib/template-engine.cjs +0 -47
- package/dist/lib/template-engine.d.cts +0 -11
- package/dist/plugin/index.cjs +0 -1907
- package/dist/plugin/index.d.cts +0 -36
- package/dist/plugin/index.d.ts +0 -36
- package/dist/plugin/index.js +0 -572
- package/dist/plugin/inject.cjs +0 -356
- package/dist/plugin/inject.d.cts +0 -54
- package/dist/plugin/inject.d.ts +0 -54
- package/dist/plugin/inject.js +0 -17
- package/dist/plugin/observe.cjs +0 -631
- package/dist/plugin/observe.d.cts +0 -39
- package/dist/plugin/observe.d.ts +0 -39
- package/dist/plugin/observe.js +0 -18
- package/dist/plugin/templates.cjs +0 -593
- package/dist/plugin/templates.d.cts +0 -52
- package/dist/plugin/templates.d.ts +0 -52
- package/dist/plugin/templates.js +0 -25
- package/dist/plugin/types.cjs +0 -18
- package/dist/plugin/types.d.cts +0 -209
- package/dist/plugin/types.d.ts +0 -209
- package/dist/plugin/types.js +0 -0
- package/dist/plugin/vault.cjs +0 -927
- package/dist/plugin/vault.d.cts +0 -68
- package/dist/plugin/vault.d.ts +0 -68
- package/dist/plugin/vault.js +0 -22
- package/dist/types-Y2_Um2Ls.d.cts +0 -205
- package/templates/memory-event.md +0 -67
- package/templates/party.md +0 -63
- package/templates/primitive-registry.yaml +0 -551
- package/templates/run.md +0 -68
- package/templates/trigger.md +0 -68
- package/templates/workspace.md +0 -50
package/dist/commands/kanban.cjs
DELETED
|
@@ -1,884 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/commands/kanban.ts
|
|
31
|
-
var kanban_exports = {};
|
|
32
|
-
__export(kanban_exports, {
|
|
33
|
-
buildKanbanLanes: () => buildKanbanLanes,
|
|
34
|
-
extractCardSlug: () => extractCardSlug,
|
|
35
|
-
formatKanbanCard: () => formatKanbanCard,
|
|
36
|
-
generateKanbanMarkdown: () => generateKanbanMarkdown,
|
|
37
|
-
importKanbanBoard: () => importKanbanBoard,
|
|
38
|
-
kanbanCommand: () => kanbanCommand,
|
|
39
|
-
parseKanbanMarkdown: () => parseKanbanMarkdown,
|
|
40
|
-
syncKanbanBoard: () => syncKanbanBoard
|
|
41
|
-
});
|
|
42
|
-
module.exports = __toCommonJS(kanban_exports);
|
|
43
|
-
var fs3 = __toESM(require("fs"), 1);
|
|
44
|
-
var path3 = __toESM(require("path"), 1);
|
|
45
|
-
var import_gray_matter3 = __toESM(require("gray-matter"), 1);
|
|
46
|
-
|
|
47
|
-
// src/lib/task-utils.ts
|
|
48
|
-
var fs2 = __toESM(require("fs"), 1);
|
|
49
|
-
var path2 = __toESM(require("path"), 1);
|
|
50
|
-
var import_gray_matter2 = __toESM(require("gray-matter"), 1);
|
|
51
|
-
|
|
52
|
-
// src/lib/transition-ledger.ts
|
|
53
|
-
var fs = __toESM(require("fs"), 1);
|
|
54
|
-
var path = __toESM(require("path"), 1);
|
|
55
|
-
var REGRESSION_PAIRS = [
|
|
56
|
-
["done", "open"],
|
|
57
|
-
["done", "blocked"],
|
|
58
|
-
["in-progress", "blocked"]
|
|
59
|
-
];
|
|
60
|
-
function isRegression(from, to) {
|
|
61
|
-
return REGRESSION_PAIRS.some(([f, t]) => f === from && t === to);
|
|
62
|
-
}
|
|
63
|
-
function getLedgerDir(vaultPath) {
|
|
64
|
-
return path.join(path.resolve(vaultPath), "ledger", "transitions");
|
|
65
|
-
}
|
|
66
|
-
function getTodayLedgerPath(vaultPath) {
|
|
67
|
-
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
68
|
-
return path.join(getLedgerDir(vaultPath), `${date}.jsonl`);
|
|
69
|
-
}
|
|
70
|
-
var RETRYABLE_APPEND_CODES = /* @__PURE__ */ new Set(["ENOENT", "EAGAIN", "EBUSY"]);
|
|
71
|
-
var MAX_APPEND_RETRIES = 2;
|
|
72
|
-
function asErrno(error) {
|
|
73
|
-
if (!error || typeof error !== "object") {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
return error;
|
|
77
|
-
}
|
|
78
|
-
function formatLedgerWriteError(filePath, error) {
|
|
79
|
-
const errno = asErrno(error);
|
|
80
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
-
if (errno?.code === "ENOSPC") {
|
|
82
|
-
return new Error(`Failed to write transition ledger at ${filePath}: no space left on device.`);
|
|
83
|
-
}
|
|
84
|
-
if (errno?.code === "EACCES" || errno?.code === "EPERM") {
|
|
85
|
-
return new Error(`Failed to write transition ledger at ${filePath}: permission denied.`);
|
|
86
|
-
}
|
|
87
|
-
return new Error(`Failed to write transition ledger at ${filePath}: ${message}`);
|
|
88
|
-
}
|
|
89
|
-
function appendTransition(vaultPath, event) {
|
|
90
|
-
const ledgerDir = getLedgerDir(vaultPath);
|
|
91
|
-
try {
|
|
92
|
-
fs.mkdirSync(ledgerDir, { recursive: true });
|
|
93
|
-
} catch (error) {
|
|
94
|
-
throw formatLedgerWriteError(ledgerDir, error);
|
|
95
|
-
}
|
|
96
|
-
const filePath = getTodayLedgerPath(vaultPath);
|
|
97
|
-
const payload = JSON.stringify(event) + "\n";
|
|
98
|
-
for (let attempt = 0; attempt <= MAX_APPEND_RETRIES; attempt += 1) {
|
|
99
|
-
try {
|
|
100
|
-
fs.appendFileSync(filePath, payload);
|
|
101
|
-
return;
|
|
102
|
-
} catch (error) {
|
|
103
|
-
const errno = asErrno(error);
|
|
104
|
-
const code = errno?.code;
|
|
105
|
-
if (code === "ENOENT") {
|
|
106
|
-
try {
|
|
107
|
-
fs.mkdirSync(ledgerDir, { recursive: true });
|
|
108
|
-
} catch (mkdirError) {
|
|
109
|
-
throw formatLedgerWriteError(filePath, mkdirError);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (code && RETRYABLE_APPEND_CODES.has(code) && attempt < MAX_APPEND_RETRIES) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
throw formatLedgerWriteError(filePath, error);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
function buildTransitionEvent(taskId, fromStatus, toStatus, options = {}) {
|
|
120
|
-
const agentId = process.env.OPENCLAW_AGENT_ID || "manual";
|
|
121
|
-
const costTokensRaw = process.env.OPENCLAW_TOKEN_ESTIMATE;
|
|
122
|
-
const costTokens = costTokensRaw ? parseInt(costTokensRaw, 10) : null;
|
|
123
|
-
return {
|
|
124
|
-
task_id: taskId,
|
|
125
|
-
agent_id: agentId,
|
|
126
|
-
from_status: fromStatus,
|
|
127
|
-
to_status: toStatus,
|
|
128
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
129
|
-
confidence: options.confidence ?? (agentId === "manual" ? 1 : 1),
|
|
130
|
-
cost_tokens: costTokens !== null && !isNaN(costTokens) ? costTokens : null,
|
|
131
|
-
reason: options.reason || null
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
function readAllTransitions(vaultPath) {
|
|
135
|
-
const ledgerDir = getLedgerDir(vaultPath);
|
|
136
|
-
if (!fs.existsSync(ledgerDir)) return [];
|
|
137
|
-
let files = [];
|
|
138
|
-
try {
|
|
139
|
-
files = fs.readdirSync(ledgerDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
140
|
-
} catch {
|
|
141
|
-
return [];
|
|
142
|
-
}
|
|
143
|
-
const events = [];
|
|
144
|
-
for (const file of files) {
|
|
145
|
-
let lines = [];
|
|
146
|
-
try {
|
|
147
|
-
lines = fs.readFileSync(path.join(ledgerDir, file), "utf-8").split("\n").filter((l) => l.trim());
|
|
148
|
-
} catch {
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
for (const line of lines) {
|
|
152
|
-
try {
|
|
153
|
-
events.push(JSON.parse(line));
|
|
154
|
-
} catch {
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return events;
|
|
159
|
-
}
|
|
160
|
-
function countBlockedTransitions(vaultPath, taskId) {
|
|
161
|
-
const events = readAllTransitions(vaultPath);
|
|
162
|
-
return events.filter((e) => e.task_id === taskId && e.to_status === "blocked").length;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// src/lib/primitive-templates.ts
|
|
166
|
-
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
167
|
-
|
|
168
|
-
// src/lib/task-utils.ts
|
|
169
|
-
function getTasksDir(vaultPath) {
|
|
170
|
-
return path2.join(path2.resolve(vaultPath), "tasks");
|
|
171
|
-
}
|
|
172
|
-
function getTaskPath(vaultPath, slug) {
|
|
173
|
-
return path2.join(getTasksDir(vaultPath), `${slug}.md`);
|
|
174
|
-
}
|
|
175
|
-
function extractTitle(content) {
|
|
176
|
-
const match = content.match(/^#\s+(.+)$/m);
|
|
177
|
-
return match ? match[1].trim() : "";
|
|
178
|
-
}
|
|
179
|
-
function parseDueDate(value) {
|
|
180
|
-
if (!value) return null;
|
|
181
|
-
const timestamp = Date.parse(value);
|
|
182
|
-
if (Number.isNaN(timestamp)) return null;
|
|
183
|
-
return timestamp;
|
|
184
|
-
}
|
|
185
|
-
function startOfToday() {
|
|
186
|
-
const now = /* @__PURE__ */ new Date();
|
|
187
|
-
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
188
|
-
}
|
|
189
|
-
var VALID_TASK_STATUSES = /* @__PURE__ */ new Set([
|
|
190
|
-
"open",
|
|
191
|
-
"in-progress",
|
|
192
|
-
"blocked",
|
|
193
|
-
"done"
|
|
194
|
-
]);
|
|
195
|
-
function isTaskStatus(value) {
|
|
196
|
-
return typeof value === "string" && VALID_TASK_STATUSES.has(value);
|
|
197
|
-
}
|
|
198
|
-
function persistTaskFrontmatter(task, frontmatter) {
|
|
199
|
-
fs2.writeFileSync(task.path, import_gray_matter2.default.stringify(task.content, frontmatter));
|
|
200
|
-
}
|
|
201
|
-
function resolveStatusTransition(previousStatus, nextStatus) {
|
|
202
|
-
if (!isTaskStatus(previousStatus) || !isTaskStatus(nextStatus)) {
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
if (previousStatus === nextStatus) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
return { fromStatus: previousStatus, toStatus: nextStatus };
|
|
209
|
-
}
|
|
210
|
-
function logStatusTransition({
|
|
211
|
-
vaultPath,
|
|
212
|
-
task,
|
|
213
|
-
fromStatus,
|
|
214
|
-
toStatus,
|
|
215
|
-
frontmatter,
|
|
216
|
-
options
|
|
217
|
-
}) {
|
|
218
|
-
const normalizedReason = typeof options.reason === "string" ? options.reason.trim() : "";
|
|
219
|
-
const reason = normalizedReason || (isRegression(fromStatus, toStatus) ? `regression: ${fromStatus} -> ${toStatus}` : void 0);
|
|
220
|
-
const event = buildTransitionEvent(task.slug, fromStatus, toStatus, {
|
|
221
|
-
confidence: options.confidence,
|
|
222
|
-
reason
|
|
223
|
-
});
|
|
224
|
-
try {
|
|
225
|
-
appendTransition(vaultPath, event);
|
|
226
|
-
} catch {
|
|
227
|
-
return frontmatter;
|
|
228
|
-
}
|
|
229
|
-
if (toStatus !== "blocked" || frontmatter.escalation) {
|
|
230
|
-
return frontmatter;
|
|
231
|
-
}
|
|
232
|
-
let blockedCount = 0;
|
|
233
|
-
try {
|
|
234
|
-
blockedCount = countBlockedTransitions(vaultPath, task.slug);
|
|
235
|
-
} catch {
|
|
236
|
-
return frontmatter;
|
|
237
|
-
}
|
|
238
|
-
if (blockedCount < 3) {
|
|
239
|
-
return frontmatter;
|
|
240
|
-
}
|
|
241
|
-
const escalatedFrontmatter = {
|
|
242
|
-
...frontmatter,
|
|
243
|
-
escalation: true
|
|
244
|
-
};
|
|
245
|
-
try {
|
|
246
|
-
persistTaskFrontmatter(task, escalatedFrontmatter);
|
|
247
|
-
return escalatedFrontmatter;
|
|
248
|
-
} catch {
|
|
249
|
-
return frontmatter;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
function readTask(vaultPath, slug) {
|
|
253
|
-
const taskPath = getTaskPath(vaultPath, slug);
|
|
254
|
-
if (!fs2.existsSync(taskPath)) {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
try {
|
|
258
|
-
const raw = fs2.readFileSync(taskPath, "utf-8");
|
|
259
|
-
const { data, content } = (0, import_gray_matter2.default)(raw);
|
|
260
|
-
const title = extractTitle(content) || slug;
|
|
261
|
-
return {
|
|
262
|
-
slug,
|
|
263
|
-
title,
|
|
264
|
-
content,
|
|
265
|
-
frontmatter: data,
|
|
266
|
-
path: taskPath
|
|
267
|
-
};
|
|
268
|
-
} catch {
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
function listTasks(vaultPath, filters) {
|
|
273
|
-
const tasksDir = getTasksDir(vaultPath);
|
|
274
|
-
if (!fs2.existsSync(tasksDir)) {
|
|
275
|
-
return [];
|
|
276
|
-
}
|
|
277
|
-
const tasks = [];
|
|
278
|
-
const entries = fs2.readdirSync(tasksDir, { withFileTypes: true });
|
|
279
|
-
const today = startOfToday();
|
|
280
|
-
for (const entry of entries) {
|
|
281
|
-
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
const slug = entry.name.replace(/\.md$/, "");
|
|
285
|
-
const task = readTask(vaultPath, slug);
|
|
286
|
-
if (!task) continue;
|
|
287
|
-
if (filters) {
|
|
288
|
-
if (filters.status && task.frontmatter.status !== filters.status) continue;
|
|
289
|
-
if (filters.owner && task.frontmatter.owner !== filters.owner) continue;
|
|
290
|
-
if (filters.project && task.frontmatter.project !== filters.project) continue;
|
|
291
|
-
if (filters.priority && task.frontmatter.priority !== filters.priority) continue;
|
|
292
|
-
if (filters.due && !task.frontmatter.due) continue;
|
|
293
|
-
if (filters.tag) {
|
|
294
|
-
const tags = task.frontmatter.tags || [];
|
|
295
|
-
const hasTag = tags.some((tag) => tag.toLowerCase() === filters.tag?.toLowerCase());
|
|
296
|
-
if (!hasTag) continue;
|
|
297
|
-
}
|
|
298
|
-
if (filters.overdue) {
|
|
299
|
-
const dueTime = parseDueDate(task.frontmatter.due);
|
|
300
|
-
if (task.frontmatter.status === "done" || dueTime === null || dueTime >= today) continue;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
tasks.push(task);
|
|
304
|
-
}
|
|
305
|
-
const priorityOrder = {
|
|
306
|
-
critical: 0,
|
|
307
|
-
high: 1,
|
|
308
|
-
medium: 2,
|
|
309
|
-
low: 3
|
|
310
|
-
};
|
|
311
|
-
if (filters?.due || filters?.overdue) {
|
|
312
|
-
return tasks.sort((a, b) => {
|
|
313
|
-
const aDue = parseDueDate(a.frontmatter.due);
|
|
314
|
-
const bDue = parseDueDate(b.frontmatter.due);
|
|
315
|
-
if (aDue !== null && bDue !== null && aDue !== bDue) {
|
|
316
|
-
return aDue - bDue;
|
|
317
|
-
}
|
|
318
|
-
if (aDue !== null && bDue === null) return -1;
|
|
319
|
-
if (aDue === null && bDue !== null) return 1;
|
|
320
|
-
return new Date(b.frontmatter.created).getTime() - new Date(a.frontmatter.created).getTime();
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
return tasks.sort((a, b) => {
|
|
324
|
-
const aPriority = priorityOrder[a.frontmatter.priority || "low"];
|
|
325
|
-
const bPriority = priorityOrder[b.frontmatter.priority || "low"];
|
|
326
|
-
if (aPriority !== bPriority) {
|
|
327
|
-
return aPriority - bPriority;
|
|
328
|
-
}
|
|
329
|
-
return new Date(b.frontmatter.created).getTime() - new Date(a.frontmatter.created).getTime();
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
function updateTask(vaultPath, slug, updates, options = {}) {
|
|
333
|
-
const task = readTask(vaultPath, slug);
|
|
334
|
-
if (!task) {
|
|
335
|
-
throw new Error(`Task not found: ${slug}`);
|
|
336
|
-
}
|
|
337
|
-
if (updates.status !== void 0 && !isTaskStatus(updates.status)) {
|
|
338
|
-
throw new Error(`Invalid task status: ${String(updates.status)}`);
|
|
339
|
-
}
|
|
340
|
-
const previousStatus = task.frontmatter.status;
|
|
341
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
342
|
-
let newFrontmatter = {
|
|
343
|
-
...task.frontmatter,
|
|
344
|
-
updated: now
|
|
345
|
-
};
|
|
346
|
-
if (updates.status !== void 0) {
|
|
347
|
-
newFrontmatter.status = updates.status;
|
|
348
|
-
if (updates.status === "done" && !newFrontmatter.completed) {
|
|
349
|
-
newFrontmatter.completed = now;
|
|
350
|
-
}
|
|
351
|
-
if (updates.status !== "done") {
|
|
352
|
-
delete newFrontmatter.completed;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
if (updates.source !== void 0) {
|
|
356
|
-
if (updates.source === null || updates.source.trim() === "") {
|
|
357
|
-
delete newFrontmatter.source;
|
|
358
|
-
} else {
|
|
359
|
-
newFrontmatter.source = updates.source;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
if (updates.owner !== void 0) {
|
|
363
|
-
if (updates.owner === null || updates.owner.trim() === "") {
|
|
364
|
-
delete newFrontmatter.owner;
|
|
365
|
-
} else {
|
|
366
|
-
newFrontmatter.owner = updates.owner;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
if (updates.project !== void 0) {
|
|
370
|
-
if (updates.project === null || updates.project.trim() === "") {
|
|
371
|
-
delete newFrontmatter.project;
|
|
372
|
-
} else {
|
|
373
|
-
newFrontmatter.project = updates.project;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
if (updates.priority !== void 0) {
|
|
377
|
-
if (updates.priority === null) {
|
|
378
|
-
delete newFrontmatter.priority;
|
|
379
|
-
} else {
|
|
380
|
-
newFrontmatter.priority = updates.priority;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
if (updates.due !== void 0) {
|
|
384
|
-
if (updates.due === null || updates.due.trim() === "") {
|
|
385
|
-
delete newFrontmatter.due;
|
|
386
|
-
} else {
|
|
387
|
-
newFrontmatter.due = updates.due;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (updates.tags !== void 0) {
|
|
391
|
-
if (updates.tags === null) {
|
|
392
|
-
delete newFrontmatter.tags;
|
|
393
|
-
} else {
|
|
394
|
-
const normalizedTags = updates.tags.map((tag) => tag.trim()).filter(Boolean);
|
|
395
|
-
if (normalizedTags.length === 0) {
|
|
396
|
-
delete newFrontmatter.tags;
|
|
397
|
-
} else {
|
|
398
|
-
newFrontmatter.tags = normalizedTags;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
if (updates.completed !== void 0) {
|
|
403
|
-
if (updates.completed === null || updates.completed.trim() === "") {
|
|
404
|
-
delete newFrontmatter.completed;
|
|
405
|
-
} else {
|
|
406
|
-
newFrontmatter.completed = updates.completed;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
if (updates.escalation !== void 0) {
|
|
410
|
-
if (updates.escalation === null) {
|
|
411
|
-
delete newFrontmatter.escalation;
|
|
412
|
-
} else {
|
|
413
|
-
newFrontmatter.escalation = updates.escalation;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (updates.confidence !== void 0) {
|
|
417
|
-
if (updates.confidence === null) {
|
|
418
|
-
delete newFrontmatter.confidence;
|
|
419
|
-
} else {
|
|
420
|
-
newFrontmatter.confidence = updates.confidence;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
if (updates.reason !== void 0) {
|
|
424
|
-
if (updates.reason === null || updates.reason.trim() === "") {
|
|
425
|
-
delete newFrontmatter.reason;
|
|
426
|
-
} else {
|
|
427
|
-
newFrontmatter.reason = updates.reason;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
if (updates.description !== void 0) {
|
|
431
|
-
if (updates.description === null || updates.description.trim() === "") {
|
|
432
|
-
delete newFrontmatter.description;
|
|
433
|
-
} else {
|
|
434
|
-
newFrontmatter.description = updates.description;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
if (updates.estimate !== void 0) {
|
|
438
|
-
if (updates.estimate === null || updates.estimate.trim() === "") {
|
|
439
|
-
delete newFrontmatter.estimate;
|
|
440
|
-
} else {
|
|
441
|
-
newFrontmatter.estimate = updates.estimate;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
if (updates.parent !== void 0) {
|
|
445
|
-
if (updates.parent === null || updates.parent.trim() === "") {
|
|
446
|
-
delete newFrontmatter.parent;
|
|
447
|
-
} else {
|
|
448
|
-
newFrontmatter.parent = updates.parent;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
if (updates.depends_on !== void 0) {
|
|
452
|
-
if (updates.depends_on === null) {
|
|
453
|
-
delete newFrontmatter.depends_on;
|
|
454
|
-
} else {
|
|
455
|
-
const normalizedDeps = updates.depends_on.map((dep) => dep.trim()).filter(Boolean);
|
|
456
|
-
if (normalizedDeps.length === 0) {
|
|
457
|
-
delete newFrontmatter.depends_on;
|
|
458
|
-
} else {
|
|
459
|
-
newFrontmatter.depends_on = normalizedDeps;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
if (updates.blocked_by !== void 0) {
|
|
464
|
-
if (updates.blocked_by === null || updates.blocked_by.trim() === "") {
|
|
465
|
-
delete newFrontmatter.blocked_by;
|
|
466
|
-
} else {
|
|
467
|
-
newFrontmatter.blocked_by = updates.blocked_by;
|
|
468
|
-
}
|
|
469
|
-
} else if (updates.status !== void 0 && updates.status !== "blocked") {
|
|
470
|
-
delete newFrontmatter.blocked_by;
|
|
471
|
-
}
|
|
472
|
-
persistTaskFrontmatter(task, newFrontmatter);
|
|
473
|
-
const transition = options.skipTransition ? null : resolveStatusTransition(previousStatus, newFrontmatter.status);
|
|
474
|
-
if (transition) {
|
|
475
|
-
const confidence = options.confidence ?? (typeof updates.confidence === "number" ? updates.confidence : void 0);
|
|
476
|
-
const reason = options.reason ?? updates.reason ?? null;
|
|
477
|
-
newFrontmatter = logStatusTransition({
|
|
478
|
-
vaultPath,
|
|
479
|
-
task,
|
|
480
|
-
fromStatus: transition.fromStatus,
|
|
481
|
-
toStatus: transition.toStatus,
|
|
482
|
-
frontmatter: newFrontmatter,
|
|
483
|
-
options: {
|
|
484
|
-
confidence,
|
|
485
|
-
reason
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
return {
|
|
490
|
-
...task,
|
|
491
|
-
frontmatter: newFrontmatter
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// src/commands/kanban.ts
|
|
496
|
-
var STATUS_LANES = [
|
|
497
|
-
{ status: "open", name: "Open" },
|
|
498
|
-
{ status: "in-progress", name: "In Progress" },
|
|
499
|
-
{ status: "blocked", name: "Blocked" },
|
|
500
|
-
{ status: "done", name: "Done" }
|
|
501
|
-
];
|
|
502
|
-
var PRIORITY_LANES = [
|
|
503
|
-
{ priority: "critical", name: "\u{1F525} Critical" },
|
|
504
|
-
{ priority: "high", name: "\u{1F534} High" },
|
|
505
|
-
{ priority: "medium", name: "\u{1F7E1} Medium" },
|
|
506
|
-
{ priority: "low", name: "\u{1F7E2} Low" },
|
|
507
|
-
{ priority: null, name: "\u26AA Unset" }
|
|
508
|
-
];
|
|
509
|
-
var PRIORITY_EMOJI = {
|
|
510
|
-
critical: "\u{1F525}",
|
|
511
|
-
high: "\u{1F534}",
|
|
512
|
-
medium: "\u{1F7E1}",
|
|
513
|
-
low: "\u{1F7E2}"
|
|
514
|
-
};
|
|
515
|
-
function normalizeGroupBy(value) {
|
|
516
|
-
const normalized = String(value || "status").trim().toLowerCase();
|
|
517
|
-
if (normalized === "status" || normalized === "priority" || normalized === "project" || normalized === "owner") {
|
|
518
|
-
return normalized;
|
|
519
|
-
}
|
|
520
|
-
throw new Error(`Unsupported kanban group field: ${normalized}`);
|
|
521
|
-
}
|
|
522
|
-
function resolveBoardPath(vaultPath, output) {
|
|
523
|
-
const resolvedVaultPath = path3.resolve(vaultPath);
|
|
524
|
-
if (!output) {
|
|
525
|
-
return path3.join(resolvedVaultPath, "Board.md");
|
|
526
|
-
}
|
|
527
|
-
if (path3.isAbsolute(output)) {
|
|
528
|
-
return output;
|
|
529
|
-
}
|
|
530
|
-
return path3.join(resolvedVaultPath, output);
|
|
531
|
-
}
|
|
532
|
-
function toHashTag(value) {
|
|
533
|
-
return value.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9/_-]/g, "");
|
|
534
|
-
}
|
|
535
|
-
function toMention(value) {
|
|
536
|
-
return value.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "");
|
|
537
|
-
}
|
|
538
|
-
function dateOnly(value) {
|
|
539
|
-
return value.includes("T") ? value.split("T")[0] : value;
|
|
540
|
-
}
|
|
541
|
-
function dueTimestamp(task) {
|
|
542
|
-
if (!task.frontmatter.due) return Number.POSITIVE_INFINITY;
|
|
543
|
-
const timestamp = Date.parse(task.frontmatter.due);
|
|
544
|
-
return Number.isNaN(timestamp) ? Number.POSITIVE_INFINITY : timestamp;
|
|
545
|
-
}
|
|
546
|
-
function sortTasksForCards(tasks) {
|
|
547
|
-
return [...tasks].sort((left, right) => {
|
|
548
|
-
const dueDiff = dueTimestamp(left) - dueTimestamp(right);
|
|
549
|
-
if (dueDiff !== 0) return dueDiff;
|
|
550
|
-
return new Date(right.frontmatter.created).getTime() - new Date(left.frontmatter.created).getTime();
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
function statusLaneName(status) {
|
|
554
|
-
const lane = STATUS_LANES.find((entry) => entry.status === status);
|
|
555
|
-
return lane ? lane.name : "Open";
|
|
556
|
-
}
|
|
557
|
-
function priorityLaneName(priority) {
|
|
558
|
-
const lane = PRIORITY_LANES.find((entry) => entry.priority === (priority ?? null));
|
|
559
|
-
return lane ? lane.name : "\u26AA Unset";
|
|
560
|
-
}
|
|
561
|
-
function laneNameForTask(task, groupBy) {
|
|
562
|
-
switch (groupBy) {
|
|
563
|
-
case "status":
|
|
564
|
-
return statusLaneName(task.frontmatter.status);
|
|
565
|
-
case "priority":
|
|
566
|
-
return priorityLaneName(task.frontmatter.priority);
|
|
567
|
-
case "project":
|
|
568
|
-
return task.frontmatter.project?.trim() || "No Project";
|
|
569
|
-
case "owner":
|
|
570
|
-
return task.frontmatter.owner?.trim() || "Unassigned";
|
|
571
|
-
default:
|
|
572
|
-
return statusLaneName(task.frontmatter.status);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
function defaultLaneOrder(groupBy, tasks) {
|
|
576
|
-
if (groupBy === "status") {
|
|
577
|
-
return STATUS_LANES.map((entry) => entry.name);
|
|
578
|
-
}
|
|
579
|
-
if (groupBy === "priority") {
|
|
580
|
-
return PRIORITY_LANES.map((entry) => entry.name);
|
|
581
|
-
}
|
|
582
|
-
const fallback = groupBy === "project" ? "No Project" : "Unassigned";
|
|
583
|
-
const values = /* @__PURE__ */ new Set();
|
|
584
|
-
for (const task of tasks) {
|
|
585
|
-
values.add(laneNameForTask(task, groupBy));
|
|
586
|
-
}
|
|
587
|
-
if (values.size === 0) {
|
|
588
|
-
return [fallback];
|
|
589
|
-
}
|
|
590
|
-
const sorted = Array.from(values).sort((left, right) => left.localeCompare(right));
|
|
591
|
-
if (sorted.includes(fallback)) {
|
|
592
|
-
return [...sorted.filter((value) => value !== fallback), fallback];
|
|
593
|
-
}
|
|
594
|
-
return sorted;
|
|
595
|
-
}
|
|
596
|
-
function formatKanbanCard(task) {
|
|
597
|
-
const checkbox = task.frontmatter.status === "done" ? "x" : " ";
|
|
598
|
-
const parts = [];
|
|
599
|
-
if (task.frontmatter.priority) {
|
|
600
|
-
parts.push(PRIORITY_EMOJI[task.frontmatter.priority]);
|
|
601
|
-
}
|
|
602
|
-
parts.push(`[[${task.slug}|${task.title}]]`);
|
|
603
|
-
if (task.frontmatter.project) {
|
|
604
|
-
const projectTag = toHashTag(task.frontmatter.project);
|
|
605
|
-
if (projectTag) parts.push(`#${projectTag}`);
|
|
606
|
-
}
|
|
607
|
-
if (task.frontmatter.owner) {
|
|
608
|
-
const mention = toMention(task.frontmatter.owner);
|
|
609
|
-
if (mention) parts.push(`@${mention}`);
|
|
610
|
-
}
|
|
611
|
-
if (task.frontmatter.tags && task.frontmatter.tags.length > 0) {
|
|
612
|
-
for (const tag of task.frontmatter.tags) {
|
|
613
|
-
const normalizedTag = toHashTag(tag);
|
|
614
|
-
if (normalizedTag) parts.push(`#${normalizedTag}`);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
if (task.frontmatter.due) {
|
|
618
|
-
parts.push(`\u{1F4C5} ${dateOnly(task.frontmatter.due)}`);
|
|
619
|
-
}
|
|
620
|
-
if (task.frontmatter.status === "blocked" || task.frontmatter.blocked_by) {
|
|
621
|
-
parts.push("\u26D4");
|
|
622
|
-
}
|
|
623
|
-
return `- [${checkbox}] ${parts.join(" ")}`;
|
|
624
|
-
}
|
|
625
|
-
function buildKanbanLanes(tasks, groupBy) {
|
|
626
|
-
const laneOrder = defaultLaneOrder(groupBy, tasks);
|
|
627
|
-
const lanes = /* @__PURE__ */ new Map();
|
|
628
|
-
for (const laneName of laneOrder) {
|
|
629
|
-
lanes.set(laneName, []);
|
|
630
|
-
}
|
|
631
|
-
for (const task of sortTasksForCards(tasks)) {
|
|
632
|
-
const laneName = laneNameForTask(task, groupBy);
|
|
633
|
-
if (!lanes.has(laneName)) {
|
|
634
|
-
lanes.set(laneName, []);
|
|
635
|
-
}
|
|
636
|
-
lanes.get(laneName)?.push(formatKanbanCard(task));
|
|
637
|
-
}
|
|
638
|
-
return Array.from(lanes.entries()).map(([name, cards]) => ({ name, cards }));
|
|
639
|
-
}
|
|
640
|
-
function generateKanbanMarkdown(tasks, options = {}) {
|
|
641
|
-
const groupBy = normalizeGroupBy(options.groupBy);
|
|
642
|
-
const syncedAt = (options.now || /* @__PURE__ */ new Date()).toISOString();
|
|
643
|
-
const lanes = buildKanbanLanes(tasks, groupBy);
|
|
644
|
-
const sections = lanes.map((lane) => {
|
|
645
|
-
const cardsBlock = lane.cards.length > 0 ? lane.cards.join("\n") : "";
|
|
646
|
-
return `## ${lane.name}
|
|
647
|
-
|
|
648
|
-
${cardsBlock}`.trimEnd();
|
|
649
|
-
}).join("\n\n");
|
|
650
|
-
return [
|
|
651
|
-
"---",
|
|
652
|
-
"kanban-plugin: basic",
|
|
653
|
-
`clawvault-group-by: ${groupBy}`,
|
|
654
|
-
`clawvault-last-sync: '${syncedAt}'`,
|
|
655
|
-
"---",
|
|
656
|
-
"",
|
|
657
|
-
sections,
|
|
658
|
-
"",
|
|
659
|
-
"%% kanban:settings",
|
|
660
|
-
'{"kanban-plugin":"basic","list-collapse":["Done"],"show-checkboxes":true}',
|
|
661
|
-
"%%",
|
|
662
|
-
""
|
|
663
|
-
].join("\n");
|
|
664
|
-
}
|
|
665
|
-
function syncKanbanBoard(vaultPath, options = {}) {
|
|
666
|
-
const groupBy = normalizeGroupBy(options.groupBy);
|
|
667
|
-
const outputPath = resolveBoardPath(vaultPath, options.output);
|
|
668
|
-
let tasks = listTasks(vaultPath);
|
|
669
|
-
if (options.filterProject) {
|
|
670
|
-
tasks = tasks.filter((task) => task.frontmatter.project === options.filterProject);
|
|
671
|
-
}
|
|
672
|
-
if (options.filterOwner) {
|
|
673
|
-
tasks = tasks.filter((task) => task.frontmatter.owner === options.filterOwner);
|
|
674
|
-
}
|
|
675
|
-
if (!options.includeDone) {
|
|
676
|
-
tasks = tasks.filter((task) => task.frontmatter.status !== "done");
|
|
677
|
-
}
|
|
678
|
-
const markdown = generateKanbanMarkdown(tasks, {
|
|
679
|
-
groupBy,
|
|
680
|
-
now: options.now
|
|
681
|
-
});
|
|
682
|
-
fs3.writeFileSync(outputPath, markdown);
|
|
683
|
-
return {
|
|
684
|
-
outputPath,
|
|
685
|
-
groupBy,
|
|
686
|
-
markdown,
|
|
687
|
-
lanes: buildKanbanLanes(tasks, groupBy),
|
|
688
|
-
taskCount: tasks.length
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
function normalizeLaneKey(laneName) {
|
|
692
|
-
return laneName.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").replace(/\s+/g, " ").trim();
|
|
693
|
-
}
|
|
694
|
-
function statusFromLaneName(laneName) {
|
|
695
|
-
const key = normalizeLaneKey(laneName);
|
|
696
|
-
if (key.includes("in progress") || key.includes("in-progress") || key === "active") return "in-progress";
|
|
697
|
-
if (key.includes("blocked")) return "blocked";
|
|
698
|
-
if (key.includes("done") || key.includes("complete")) return "done";
|
|
699
|
-
if (key.includes("open")) return "open";
|
|
700
|
-
return null;
|
|
701
|
-
}
|
|
702
|
-
function priorityFromLaneName(laneName) {
|
|
703
|
-
const key = normalizeLaneKey(laneName);
|
|
704
|
-
if (key.includes("critical")) return "critical";
|
|
705
|
-
if (key.includes("high")) return "high";
|
|
706
|
-
if (key.includes("medium")) return "medium";
|
|
707
|
-
if (key.includes("low")) return "low";
|
|
708
|
-
if (key.includes("unset") || key.includes("none") || key.includes("no priority")) return null;
|
|
709
|
-
return void 0;
|
|
710
|
-
}
|
|
711
|
-
function isProjectFallbackLane(laneName) {
|
|
712
|
-
const key = normalizeLaneKey(laneName);
|
|
713
|
-
return key === "no project" || key === "none";
|
|
714
|
-
}
|
|
715
|
-
function isOwnerFallbackLane(laneName) {
|
|
716
|
-
const key = normalizeLaneKey(laneName);
|
|
717
|
-
return key === "unassigned" || key === "none";
|
|
718
|
-
}
|
|
719
|
-
function extractCardSlug(line) {
|
|
720
|
-
const wikiMatch = line.match(/\[\[([^\]]+)\]\]/);
|
|
721
|
-
if (!wikiMatch) return null;
|
|
722
|
-
let target = wikiMatch[1].split("|")[0].trim();
|
|
723
|
-
if (!target) return null;
|
|
724
|
-
target = target.split("#")[0].trim();
|
|
725
|
-
const filePart = target.split("/").pop() || target;
|
|
726
|
-
const slug = filePart.replace(/\.md$/i, "").trim();
|
|
727
|
-
return slug || null;
|
|
728
|
-
}
|
|
729
|
-
function parseKanbanMarkdown(markdown) {
|
|
730
|
-
const parsed = (0, import_gray_matter3.default)(markdown);
|
|
731
|
-
const groupBy = normalizeGroupBy(
|
|
732
|
-
typeof parsed.data["clawvault-group-by"] === "string" ? parsed.data["clawvault-group-by"] : void 0
|
|
733
|
-
);
|
|
734
|
-
const lanes = [];
|
|
735
|
-
const laneByName = /* @__PURE__ */ new Map();
|
|
736
|
-
let currentLane = null;
|
|
737
|
-
const lines = parsed.content.split(/\r?\n/);
|
|
738
|
-
for (const line of lines) {
|
|
739
|
-
const headerMatch = line.match(/^##\s+(.+?)\s*$/);
|
|
740
|
-
if (headerMatch) {
|
|
741
|
-
const laneName = headerMatch[1].trim();
|
|
742
|
-
if (!laneByName.has(laneName)) {
|
|
743
|
-
const lane = { name: laneName, slugs: [] };
|
|
744
|
-
laneByName.set(laneName, lane);
|
|
745
|
-
lanes.push(lane);
|
|
746
|
-
}
|
|
747
|
-
currentLane = laneByName.get(laneName) || null;
|
|
748
|
-
continue;
|
|
749
|
-
}
|
|
750
|
-
if (!currentLane || !/^\s*-\s*\[[ xX]\]\s+/.test(line)) {
|
|
751
|
-
continue;
|
|
752
|
-
}
|
|
753
|
-
const slug = extractCardSlug(line);
|
|
754
|
-
if (slug) {
|
|
755
|
-
currentLane.slugs.push(slug);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return { groupBy, lanes };
|
|
759
|
-
}
|
|
760
|
-
function hasUpdates(updates) {
|
|
761
|
-
return Object.keys(updates).length > 0;
|
|
762
|
-
}
|
|
763
|
-
function importKanbanBoard(vaultPath, options = {}) {
|
|
764
|
-
const outputPath = resolveBoardPath(vaultPath, options.output);
|
|
765
|
-
if (!fs3.existsSync(outputPath)) {
|
|
766
|
-
throw new Error(`Kanban board not found: ${outputPath}`);
|
|
767
|
-
}
|
|
768
|
-
const markdown = fs3.readFileSync(outputPath, "utf-8");
|
|
769
|
-
const parsed = parseKanbanMarkdown(markdown);
|
|
770
|
-
const changes = [];
|
|
771
|
-
const missingSlugs = [];
|
|
772
|
-
const seenSlugs = /* @__PURE__ */ new Set();
|
|
773
|
-
for (const lane of parsed.lanes) {
|
|
774
|
-
for (const slug of lane.slugs) {
|
|
775
|
-
if (seenSlugs.has(slug)) continue;
|
|
776
|
-
seenSlugs.add(slug);
|
|
777
|
-
const task = readTask(vaultPath, slug);
|
|
778
|
-
if (!task) {
|
|
779
|
-
missingSlugs.push(slug);
|
|
780
|
-
continue;
|
|
781
|
-
}
|
|
782
|
-
const updates = {};
|
|
783
|
-
if (parsed.groupBy === "status") {
|
|
784
|
-
const desiredStatus = statusFromLaneName(lane.name);
|
|
785
|
-
if (desiredStatus && task.frontmatter.status !== desiredStatus) {
|
|
786
|
-
updates.status = desiredStatus;
|
|
787
|
-
changes.push({
|
|
788
|
-
slug,
|
|
789
|
-
field: "status",
|
|
790
|
-
from: task.frontmatter.status,
|
|
791
|
-
to: desiredStatus
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
} else if (parsed.groupBy === "priority") {
|
|
795
|
-
const desiredPriority = priorityFromLaneName(lane.name);
|
|
796
|
-
if (desiredPriority !== void 0) {
|
|
797
|
-
const currentPriority = task.frontmatter.priority ?? null;
|
|
798
|
-
if (currentPriority !== desiredPriority) {
|
|
799
|
-
updates.priority = desiredPriority;
|
|
800
|
-
changes.push({
|
|
801
|
-
slug,
|
|
802
|
-
field: "priority",
|
|
803
|
-
from: currentPriority,
|
|
804
|
-
to: desiredPriority
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
} else if (parsed.groupBy === "project") {
|
|
809
|
-
const desiredProject = isProjectFallbackLane(lane.name) ? null : lane.name.trim();
|
|
810
|
-
const currentProject = task.frontmatter.project ?? null;
|
|
811
|
-
if (currentProject !== desiredProject) {
|
|
812
|
-
updates.project = desiredProject;
|
|
813
|
-
changes.push({
|
|
814
|
-
slug,
|
|
815
|
-
field: "project",
|
|
816
|
-
from: currentProject,
|
|
817
|
-
to: desiredProject
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
} else if (parsed.groupBy === "owner") {
|
|
821
|
-
const desiredOwner = isOwnerFallbackLane(lane.name) ? null : lane.name.trim();
|
|
822
|
-
const currentOwner = task.frontmatter.owner ?? null;
|
|
823
|
-
if (currentOwner !== desiredOwner) {
|
|
824
|
-
updates.owner = desiredOwner;
|
|
825
|
-
changes.push({
|
|
826
|
-
slug,
|
|
827
|
-
field: "owner",
|
|
828
|
-
from: currentOwner,
|
|
829
|
-
to: desiredOwner
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
if (hasUpdates(updates)) {
|
|
834
|
-
updateTask(vaultPath, slug, updates);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
return {
|
|
839
|
-
outputPath,
|
|
840
|
-
groupBy: parsed.groupBy,
|
|
841
|
-
changes,
|
|
842
|
-
missingSlugs
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
async function kanbanCommand(vaultPath, action, options = {}) {
|
|
846
|
-
if (action === "sync") {
|
|
847
|
-
const result = syncKanbanBoard(vaultPath, options);
|
|
848
|
-
console.log(`\u2713 Synced kanban board: ${result.outputPath}`);
|
|
849
|
-
console.log(` Grouped by: ${result.groupBy}`);
|
|
850
|
-
console.log(` Tasks included: ${result.taskCount}`);
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
|
-
if (action === "import") {
|
|
854
|
-
const result = importKanbanBoard(vaultPath, options);
|
|
855
|
-
console.log(`\u2713 Imported kanban board: ${result.outputPath}`);
|
|
856
|
-
console.log(` Grouped by: ${result.groupBy}`);
|
|
857
|
-
if (result.changes.length === 0) {
|
|
858
|
-
console.log(" No task updates required.");
|
|
859
|
-
} else {
|
|
860
|
-
console.log(` Updated ${result.changes.length} task field(s):`);
|
|
861
|
-
for (const change of result.changes) {
|
|
862
|
-
const from = change.from ?? "(unset)";
|
|
863
|
-
const to = change.to ?? "(unset)";
|
|
864
|
-
console.log(` - ${change.slug}: ${change.field} ${from} -> ${to}`);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
if (result.missingSlugs.length > 0) {
|
|
868
|
-
console.log(` Missing tasks (${result.missingSlugs.length}): ${result.missingSlugs.join(", ")}`);
|
|
869
|
-
}
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
throw new Error(`Unknown kanban action: ${action}`);
|
|
873
|
-
}
|
|
874
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
875
|
-
0 && (module.exports = {
|
|
876
|
-
buildKanbanLanes,
|
|
877
|
-
extractCardSlug,
|
|
878
|
-
formatKanbanCard,
|
|
879
|
-
generateKanbanMarkdown,
|
|
880
|
-
importKanbanBoard,
|
|
881
|
-
kanbanCommand,
|
|
882
|
-
parseKanbanMarkdown,
|
|
883
|
-
syncKanbanBoard
|
|
884
|
-
});
|