clawvault 3.0.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 +352 -20
- package/bin/clawvault.js +8 -2
- package/bin/command-registration.test.js +3 -1
- package/bin/command-runtime.js +9 -1
- package/bin/register-core-commands.js +23 -10
- package/bin/register-maintenance-commands.js +39 -3
- package/bin/register-query-commands.js +58 -29
- 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-WGRQ6HDV.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-YKTA5JOJ.js → chunk-GAOWA7GR.js} +212 -46
- package/dist/chunk-GGA32J2R.js +784 -0
- package/dist/chunk-GNJL4YGR.js +79 -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-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-3D6BCTP6.js → chunk-X3SPPUFG.js} +51 -39
- package/dist/{chunk-THRJVD4L.js → chunk-Y6VJKXGL.js} +1 -1
- package/dist/{chunk-ZVVFWOLW.js → chunk-ZN54U2OZ.js} +123 -10
- package/dist/cli/index.js +32 -25
- 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 -90
- 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.js +3 -3
- 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 +337 -191
- package/dist/index.js +387 -118
- package/dist/{inject-Bzi5E-By.d.cts → 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 +100 -16
- package/dist/lib/canvas-layout.js +21 -78
- 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.js +1 -1
- package/dist/lib/task-utils.js +3 -3
- package/dist/lib/template-engine.js +1 -1
- package/dist/lib/webdav.js +1 -1
- 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.cts → 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 +34 -4
- package/hooks/clawvault/handler.js +760 -78
- package/hooks/clawvault/handler.test.js +235 -79
- package/hooks/clawvault/openclaw.plugin.json +72 -0
- package/openclaw.plugin.json +65 -38
- package/package.json +15 -18
- package/dist/chunk-3RG5ZIWI.js +0 -10
- package/dist/chunk-6U6MK36V.js +0 -205
- package/dist/chunk-7R7O6STJ.js +0 -88
- package/dist/chunk-CMB7UL7C.js +0 -327
- package/dist/chunk-DEFFDRVP.js +0 -938
- package/dist/chunk-E7MFQB6D.js +0 -163
- package/dist/chunk-GAJV4IGR.js +0 -82
- package/dist/chunk-GQSLDZTS.js +0 -560
- package/dist/chunk-K234IDRJ.js +0 -1073
- package/dist/chunk-MFM6K7PU.js +0 -374
- package/dist/chunk-MXSSG3QU.js +0 -42
- package/dist/chunk-PAH27GSN.js +0 -108
- package/dist/cli/index.cjs +0 -10033
- 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 -369
- package/dist/commands/compat.d.cts +0 -28
- package/dist/commands/context.cjs +0 -2989
- package/dist/commands/context.d.cts +0 -2
- package/dist/commands/doctor.cjs +0 -3062
- 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 -1345
- package/dist/commands/setup.d.cts +0 -100
- package/dist/commands/shell-init.cjs +0 -75
- package/dist/commands/shell-init.d.cts +0 -7
- package/dist/commands/sleep.cjs +0 -6028
- package/dist/commands/sleep.d.cts +0 -36
- package/dist/commands/status.cjs +0 -2736
- package/dist/commands/status.d.cts +0 -52
- package/dist/commands/tailscale.cjs +0 -1532
- package/dist/commands/tailscale.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 -2626
- package/dist/commands/wake.d.cts +0 -22
- package/dist/context-BUGaWpyL.d.cts +0 -46
- package/dist/index.cjs +0 -14526
- package/dist/index.d.cts +0 -858
- package/dist/inject-Bzi5E-By.d.ts +0 -137
- package/dist/lib/auto-linker.cjs +0 -176
- package/dist/lib/auto-linker.d.cts +0 -26
- package/dist/lib/canvas-layout.cjs +0 -136
- package/dist/lib/canvas-layout.d.cts +0 -31
- 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/tailscale.cjs +0 -1183
- package/dist/lib/tailscale.d.cts +0 -225
- 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/lib/webdav.cjs +0 -568
- package/dist/lib/webdav.d.cts +0 -109
- 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.ts +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
|
@@ -1,1341 +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/project.ts
|
|
31
|
-
var project_exports = {};
|
|
32
|
-
__export(project_exports, {
|
|
33
|
-
buildProjectBoardLanes: () => buildProjectBoardLanes,
|
|
34
|
-
formatProjectDetails: () => formatProjectDetails,
|
|
35
|
-
formatProjectList: () => formatProjectList,
|
|
36
|
-
generateProjectBoardMarkdown: () => generateProjectBoardMarkdown,
|
|
37
|
-
projectAdd: () => projectAdd,
|
|
38
|
-
projectArchive: () => projectArchive,
|
|
39
|
-
projectCommand: () => projectCommand,
|
|
40
|
-
projectList: () => projectList,
|
|
41
|
-
projectUpdate: () => projectUpdate,
|
|
42
|
-
syncProjectBoard: () => syncProjectBoard
|
|
43
|
-
});
|
|
44
|
-
module.exports = __toCommonJS(project_exports);
|
|
45
|
-
var fs4 = __toESM(require("fs"), 1);
|
|
46
|
-
var path4 = __toESM(require("path"), 1);
|
|
47
|
-
|
|
48
|
-
// src/lib/project-utils.ts
|
|
49
|
-
var fs3 = __toESM(require("fs"), 1);
|
|
50
|
-
var path3 = __toESM(require("path"), 1);
|
|
51
|
-
var import_gray_matter3 = __toESM(require("gray-matter"), 1);
|
|
52
|
-
|
|
53
|
-
// src/lib/task-utils.ts
|
|
54
|
-
var fs2 = __toESM(require("fs"), 1);
|
|
55
|
-
var path2 = __toESM(require("path"), 1);
|
|
56
|
-
var import_gray_matter2 = __toESM(require("gray-matter"), 1);
|
|
57
|
-
|
|
58
|
-
// src/lib/primitive-templates.ts
|
|
59
|
-
var fs = __toESM(require("fs"), 1);
|
|
60
|
-
var path = __toESM(require("path"), 1);
|
|
61
|
-
var import_url = require("url");
|
|
62
|
-
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
63
|
-
|
|
64
|
-
// src/lib/template-engine.ts
|
|
65
|
-
function buildTemplateVariables(input = {}, now = /* @__PURE__ */ new Date()) {
|
|
66
|
-
const datetime = input.datetime ?? now.toISOString();
|
|
67
|
-
const date = input.date ?? datetime.split("T")[0];
|
|
68
|
-
return {
|
|
69
|
-
title: input.title ?? "",
|
|
70
|
-
type: input.type ?? "",
|
|
71
|
-
date,
|
|
72
|
-
datetime
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
function renderTemplate(template, variables) {
|
|
76
|
-
return template.replace(/\{\{\s*([a-zA-Z0-9_-]+)\s*\}\}/g, (match, key) => {
|
|
77
|
-
const value = variables[key];
|
|
78
|
-
return value !== void 0 ? String(value) : match;
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// src/lib/primitive-templates.ts
|
|
83
|
-
var import_meta = {};
|
|
84
|
-
var TEMPLATE_EXTENSION = ".md";
|
|
85
|
-
function isRecord(value) {
|
|
86
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
87
|
-
}
|
|
88
|
-
function normalizeTemplateName(name) {
|
|
89
|
-
const base = path.basename(name, path.extname(name));
|
|
90
|
-
return base.trim();
|
|
91
|
-
}
|
|
92
|
-
function resolveBuiltinTemplatesDir(override) {
|
|
93
|
-
if (override) {
|
|
94
|
-
const resolved = path.resolve(override);
|
|
95
|
-
return fs.existsSync(resolved) && fs.statSync(resolved).isDirectory() ? resolved : null;
|
|
96
|
-
}
|
|
97
|
-
const moduleDir = path.dirname((0, import_url.fileURLToPath)(import_meta.url));
|
|
98
|
-
const candidates = [
|
|
99
|
-
path.resolve(moduleDir, "../templates"),
|
|
100
|
-
path.resolve(moduleDir, "../../templates")
|
|
101
|
-
];
|
|
102
|
-
for (const candidate of candidates) {
|
|
103
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
104
|
-
return candidate;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
function listTemplateFiles(dir, ignore) {
|
|
110
|
-
const entries = /* @__PURE__ */ new Map();
|
|
111
|
-
if (!fs.existsSync(dir)) return entries;
|
|
112
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
113
|
-
if (!entry.isFile() || !entry.name.endsWith(TEMPLATE_EXTENSION)) continue;
|
|
114
|
-
const name = normalizeTemplateName(entry.name);
|
|
115
|
-
if (!name) continue;
|
|
116
|
-
if (ignore?.has(name)) continue;
|
|
117
|
-
entries.set(name, path.join(dir, entry.name));
|
|
118
|
-
}
|
|
119
|
-
return entries;
|
|
120
|
-
}
|
|
121
|
-
function buildTemplateIndex(options = {}) {
|
|
122
|
-
const index = /* @__PURE__ */ new Map();
|
|
123
|
-
const builtinDir = resolveBuiltinTemplatesDir(options.builtinDir);
|
|
124
|
-
if (builtinDir) {
|
|
125
|
-
for (const [name, filePath] of listTemplateFiles(builtinDir, options.ignoreBuiltinNames)) {
|
|
126
|
-
index.set(name, filePath);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if (options.vaultPath) {
|
|
130
|
-
const vaultTemplatesDir = path.join(path.resolve(options.vaultPath), "templates");
|
|
131
|
-
for (const [name, filePath] of listTemplateFiles(vaultTemplatesDir)) {
|
|
132
|
-
index.set(name, filePath);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return index;
|
|
136
|
-
}
|
|
137
|
-
function inferFieldType(defaultValue) {
|
|
138
|
-
if (Array.isArray(defaultValue)) {
|
|
139
|
-
const uniqueItemTypes = [...new Set(defaultValue.map((value) => typeof value))];
|
|
140
|
-
if (uniqueItemTypes.length === 1 && uniqueItemTypes[0] === "string") {
|
|
141
|
-
return "string[]";
|
|
142
|
-
}
|
|
143
|
-
return "array";
|
|
144
|
-
}
|
|
145
|
-
switch (typeof defaultValue) {
|
|
146
|
-
case "string":
|
|
147
|
-
return "string";
|
|
148
|
-
case "number":
|
|
149
|
-
return "number";
|
|
150
|
-
case "boolean":
|
|
151
|
-
return "boolean";
|
|
152
|
-
case "object":
|
|
153
|
-
if (defaultValue === null) return "string";
|
|
154
|
-
return "object";
|
|
155
|
-
default:
|
|
156
|
-
return "string";
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function normalizeFieldDefinition(rawField) {
|
|
160
|
-
if (!isRecord(rawField)) {
|
|
161
|
-
return {
|
|
162
|
-
type: inferFieldType(rawField),
|
|
163
|
-
default: rawField
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
const rawType = typeof rawField.type === "string" ? rawField.type.trim() : "";
|
|
167
|
-
const normalized = {
|
|
168
|
-
type: rawType || inferFieldType(rawField.default)
|
|
169
|
-
};
|
|
170
|
-
if (typeof rawField.description === "string" && rawField.description.trim()) {
|
|
171
|
-
normalized.description = rawField.description.trim();
|
|
172
|
-
}
|
|
173
|
-
if (typeof rawField.required === "boolean") {
|
|
174
|
-
normalized.required = rawField.required;
|
|
175
|
-
}
|
|
176
|
-
if (Object.prototype.hasOwnProperty.call(rawField, "default")) {
|
|
177
|
-
normalized.default = rawField.default;
|
|
178
|
-
}
|
|
179
|
-
if (Array.isArray(rawField.enum)) {
|
|
180
|
-
normalized.enum = rawField.enum;
|
|
181
|
-
}
|
|
182
|
-
return normalized;
|
|
183
|
-
}
|
|
184
|
-
function normalizeFieldDefinitions(rawFields) {
|
|
185
|
-
const normalized = {};
|
|
186
|
-
for (const [fieldName, rawField] of Object.entries(rawFields)) {
|
|
187
|
-
const normalizedName = String(fieldName).trim();
|
|
188
|
-
if (!normalizedName) continue;
|
|
189
|
-
normalized[normalizedName] = normalizeFieldDefinition(rawField);
|
|
190
|
-
}
|
|
191
|
-
return normalized;
|
|
192
|
-
}
|
|
193
|
-
function extractSchemaDefinition(frontmatter) {
|
|
194
|
-
const primitive = typeof frontmatter.primitive === "string" ? frontmatter.primitive.trim() : "";
|
|
195
|
-
const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : void 0;
|
|
196
|
-
if (primitive && isRecord(frontmatter.fields)) {
|
|
197
|
-
return {
|
|
198
|
-
primitive,
|
|
199
|
-
description,
|
|
200
|
-
fields: frontmatter.fields
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
const containerCandidates = [frontmatter.schema, frontmatter.template];
|
|
204
|
-
for (const candidate of containerCandidates) {
|
|
205
|
-
if (!isRecord(candidate)) continue;
|
|
206
|
-
const nestedPrimitive = typeof candidate.primitive === "string" ? candidate.primitive.trim() : primitive;
|
|
207
|
-
if (!nestedPrimitive || !isRecord(candidate.fields)) continue;
|
|
208
|
-
const nestedDescription = typeof candidate.description === "string" ? candidate.description.trim() : description;
|
|
209
|
-
return {
|
|
210
|
-
primitive: nestedPrimitive,
|
|
211
|
-
description: nestedDescription,
|
|
212
|
-
fields: candidate.fields
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
function inferLegacyFieldDefinitions(frontmatter) {
|
|
218
|
-
const normalized = {};
|
|
219
|
-
const ignoredKeys = /* @__PURE__ */ new Set(["primitive", "fields", "schema", "template"]);
|
|
220
|
-
for (const [key, value] of Object.entries(frontmatter)) {
|
|
221
|
-
if (ignoredKeys.has(key)) continue;
|
|
222
|
-
normalized[key] = {
|
|
223
|
-
type: inferFieldType(value),
|
|
224
|
-
default: value
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
return normalized;
|
|
228
|
-
}
|
|
229
|
-
function parseTemplateDefinition(rawTemplate, templateName, sourcePath) {
|
|
230
|
-
const normalizedName = normalizeTemplateName(templateName);
|
|
231
|
-
const { data, content } = (0, import_gray_matter.default)(rawTemplate);
|
|
232
|
-
const frontmatter = isRecord(data) ? data : {};
|
|
233
|
-
const extractedSchema = extractSchemaDefinition(frontmatter);
|
|
234
|
-
if (extractedSchema) {
|
|
235
|
-
return {
|
|
236
|
-
name: normalizedName,
|
|
237
|
-
primitive: extractedSchema.primitive,
|
|
238
|
-
description: extractedSchema.description,
|
|
239
|
-
fields: normalizeFieldDefinitions(extractedSchema.fields),
|
|
240
|
-
body: content,
|
|
241
|
-
format: "schema",
|
|
242
|
-
sourcePath
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
return {
|
|
246
|
-
name: normalizedName,
|
|
247
|
-
primitive: normalizedName,
|
|
248
|
-
description: typeof frontmatter.description === "string" ? frontmatter.description.trim() : void 0,
|
|
249
|
-
fields: inferLegacyFieldDefinitions(frontmatter),
|
|
250
|
-
body: content,
|
|
251
|
-
format: "legacy",
|
|
252
|
-
sourcePath
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
function readTemplateDefinitionFromPath(filePath, templateName) {
|
|
256
|
-
try {
|
|
257
|
-
const raw = fs.readFileSync(filePath, "utf-8");
|
|
258
|
-
return parseTemplateDefinition(raw, templateName, filePath);
|
|
259
|
-
} catch {
|
|
260
|
-
return null;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
function loadTemplateDefinition(templateName, options = {}) {
|
|
264
|
-
const normalizedName = normalizeTemplateName(templateName);
|
|
265
|
-
if (!normalizedName) return null;
|
|
266
|
-
const index = buildTemplateIndex(options);
|
|
267
|
-
const filePath = index.get(normalizedName);
|
|
268
|
-
if (!filePath) return null;
|
|
269
|
-
return readTemplateDefinitionFromPath(filePath, normalizedName);
|
|
270
|
-
}
|
|
271
|
-
function loadSchemaTemplateDefinition(templateName, options = {}) {
|
|
272
|
-
const definition = loadTemplateDefinition(templateName, options);
|
|
273
|
-
if (!definition || definition.format !== "schema") {
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
return definition;
|
|
277
|
-
}
|
|
278
|
-
function resolveInterpolatedValue(value, variables) {
|
|
279
|
-
if (typeof value === "string") {
|
|
280
|
-
return renderTemplate(value, variables);
|
|
281
|
-
}
|
|
282
|
-
if (Array.isArray(value)) {
|
|
283
|
-
return value.map((item) => resolveInterpolatedValue(item, variables));
|
|
284
|
-
}
|
|
285
|
-
if (isRecord(value)) {
|
|
286
|
-
const resolved = {};
|
|
287
|
-
for (const [key, nested] of Object.entries(value)) {
|
|
288
|
-
resolved[key] = resolveInterpolatedValue(nested, variables);
|
|
289
|
-
}
|
|
290
|
-
return resolved;
|
|
291
|
-
}
|
|
292
|
-
return value;
|
|
293
|
-
}
|
|
294
|
-
function pruneFrontmatter(frontmatter, options) {
|
|
295
|
-
const dropEmptyStrings = options.dropEmptyStrings ?? true;
|
|
296
|
-
const dropEmptyArrays = options.dropEmptyArrays ?? true;
|
|
297
|
-
const pruned = {};
|
|
298
|
-
for (const [key, value] of Object.entries(frontmatter)) {
|
|
299
|
-
if (value === void 0 || value === null) continue;
|
|
300
|
-
if (dropEmptyStrings && typeof value === "string" && value.trim() === "") continue;
|
|
301
|
-
if (dropEmptyArrays && Array.isArray(value) && value.length === 0) continue;
|
|
302
|
-
pruned[key] = value;
|
|
303
|
-
}
|
|
304
|
-
return pruned;
|
|
305
|
-
}
|
|
306
|
-
function buildFrontmatterFromTemplate(definition, variables, overrides = {}, options = {}) {
|
|
307
|
-
const frontmatter = {};
|
|
308
|
-
for (const [fieldName, schema] of Object.entries(definition.fields)) {
|
|
309
|
-
if (!Object.prototype.hasOwnProperty.call(schema, "default")) continue;
|
|
310
|
-
frontmatter[fieldName] = resolveInterpolatedValue(schema.default, variables);
|
|
311
|
-
}
|
|
312
|
-
for (const [fieldName, value] of Object.entries(overrides)) {
|
|
313
|
-
if (value === void 0) continue;
|
|
314
|
-
if (value === null) {
|
|
315
|
-
delete frontmatter[fieldName];
|
|
316
|
-
continue;
|
|
317
|
-
}
|
|
318
|
-
frontmatter[fieldName] = value;
|
|
319
|
-
}
|
|
320
|
-
if (!options.pruneEmpty) {
|
|
321
|
-
return frontmatter;
|
|
322
|
-
}
|
|
323
|
-
return pruneFrontmatter(frontmatter, options);
|
|
324
|
-
}
|
|
325
|
-
function renderDocumentFromTemplate(definition, options = {}) {
|
|
326
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
327
|
-
const variables = {
|
|
328
|
-
...buildTemplateVariables(
|
|
329
|
-
{
|
|
330
|
-
title: options.title ?? "",
|
|
331
|
-
type: options.type ?? definition.primitive
|
|
332
|
-
},
|
|
333
|
-
now
|
|
334
|
-
),
|
|
335
|
-
...options.variables ?? {}
|
|
336
|
-
};
|
|
337
|
-
const frontmatter = buildFrontmatterFromTemplate(
|
|
338
|
-
definition,
|
|
339
|
-
variables,
|
|
340
|
-
options.overrides,
|
|
341
|
-
options.frontmatter
|
|
342
|
-
);
|
|
343
|
-
const content = renderTemplate(definition.body, variables);
|
|
344
|
-
const markdown = import_gray_matter.default.stringify(content, frontmatter);
|
|
345
|
-
return {
|
|
346
|
-
frontmatter,
|
|
347
|
-
content,
|
|
348
|
-
markdown,
|
|
349
|
-
variables
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// src/lib/task-utils.ts
|
|
354
|
-
function slugify(text) {
|
|
355
|
-
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").trim();
|
|
356
|
-
}
|
|
357
|
-
function getTasksDir(vaultPath) {
|
|
358
|
-
return path2.join(path2.resolve(vaultPath), "tasks");
|
|
359
|
-
}
|
|
360
|
-
function getTaskPath(vaultPath, slug) {
|
|
361
|
-
return path2.join(getTasksDir(vaultPath), `${slug}.md`);
|
|
362
|
-
}
|
|
363
|
-
function extractTitle(content) {
|
|
364
|
-
const match = content.match(/^#\s+(.+)$/m);
|
|
365
|
-
return match ? match[1].trim() : "";
|
|
366
|
-
}
|
|
367
|
-
function parseDueDate(value) {
|
|
368
|
-
if (!value) return null;
|
|
369
|
-
const timestamp = Date.parse(value);
|
|
370
|
-
if (Number.isNaN(timestamp)) return null;
|
|
371
|
-
return timestamp;
|
|
372
|
-
}
|
|
373
|
-
function startOfToday() {
|
|
374
|
-
const now = /* @__PURE__ */ new Date();
|
|
375
|
-
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
376
|
-
}
|
|
377
|
-
function readTask(vaultPath, slug) {
|
|
378
|
-
const taskPath = getTaskPath(vaultPath, slug);
|
|
379
|
-
if (!fs2.existsSync(taskPath)) {
|
|
380
|
-
return null;
|
|
381
|
-
}
|
|
382
|
-
try {
|
|
383
|
-
const raw = fs2.readFileSync(taskPath, "utf-8");
|
|
384
|
-
const { data, content } = (0, import_gray_matter2.default)(raw);
|
|
385
|
-
const title = extractTitle(content) || slug;
|
|
386
|
-
return {
|
|
387
|
-
slug,
|
|
388
|
-
title,
|
|
389
|
-
content,
|
|
390
|
-
frontmatter: data,
|
|
391
|
-
path: taskPath
|
|
392
|
-
};
|
|
393
|
-
} catch {
|
|
394
|
-
return null;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
function listTasks(vaultPath, filters) {
|
|
398
|
-
const tasksDir = getTasksDir(vaultPath);
|
|
399
|
-
if (!fs2.existsSync(tasksDir)) {
|
|
400
|
-
return [];
|
|
401
|
-
}
|
|
402
|
-
const tasks = [];
|
|
403
|
-
const entries = fs2.readdirSync(tasksDir, { withFileTypes: true });
|
|
404
|
-
const today = startOfToday();
|
|
405
|
-
for (const entry of entries) {
|
|
406
|
-
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
const slug = entry.name.replace(/\.md$/, "");
|
|
410
|
-
const task = readTask(vaultPath, slug);
|
|
411
|
-
if (!task) continue;
|
|
412
|
-
if (filters) {
|
|
413
|
-
if (filters.status && task.frontmatter.status !== filters.status) continue;
|
|
414
|
-
if (filters.owner && task.frontmatter.owner !== filters.owner) continue;
|
|
415
|
-
if (filters.project && task.frontmatter.project !== filters.project) continue;
|
|
416
|
-
if (filters.priority && task.frontmatter.priority !== filters.priority) continue;
|
|
417
|
-
if (filters.due && !task.frontmatter.due) continue;
|
|
418
|
-
if (filters.tag) {
|
|
419
|
-
const tags = task.frontmatter.tags || [];
|
|
420
|
-
const hasTag = tags.some((tag) => tag.toLowerCase() === filters.tag?.toLowerCase());
|
|
421
|
-
if (!hasTag) continue;
|
|
422
|
-
}
|
|
423
|
-
if (filters.overdue) {
|
|
424
|
-
const dueTime = parseDueDate(task.frontmatter.due);
|
|
425
|
-
if (task.frontmatter.status === "done" || dueTime === null || dueTime >= today) continue;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
tasks.push(task);
|
|
429
|
-
}
|
|
430
|
-
const priorityOrder = {
|
|
431
|
-
critical: 0,
|
|
432
|
-
high: 1,
|
|
433
|
-
medium: 2,
|
|
434
|
-
low: 3
|
|
435
|
-
};
|
|
436
|
-
if (filters?.due || filters?.overdue) {
|
|
437
|
-
return tasks.sort((a, b) => {
|
|
438
|
-
const aDue = parseDueDate(a.frontmatter.due);
|
|
439
|
-
const bDue = parseDueDate(b.frontmatter.due);
|
|
440
|
-
if (aDue !== null && bDue !== null && aDue !== bDue) {
|
|
441
|
-
return aDue - bDue;
|
|
442
|
-
}
|
|
443
|
-
if (aDue !== null && bDue === null) return -1;
|
|
444
|
-
if (aDue === null && bDue !== null) return 1;
|
|
445
|
-
return new Date(b.frontmatter.created).getTime() - new Date(a.frontmatter.created).getTime();
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
return tasks.sort((a, b) => {
|
|
449
|
-
const aPriority = priorityOrder[a.frontmatter.priority || "low"];
|
|
450
|
-
const bPriority = priorityOrder[b.frontmatter.priority || "low"];
|
|
451
|
-
if (aPriority !== bPriority) {
|
|
452
|
-
return aPriority - bPriority;
|
|
453
|
-
}
|
|
454
|
-
return new Date(b.frontmatter.created).getTime() - new Date(a.frontmatter.created).getTime();
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
function getStatusIcon(status) {
|
|
458
|
-
switch (status) {
|
|
459
|
-
case "in-progress":
|
|
460
|
-
return "\u25CF";
|
|
461
|
-
case "blocked":
|
|
462
|
-
return "\u25A0";
|
|
463
|
-
case "open":
|
|
464
|
-
return "\u25CB";
|
|
465
|
-
case "done":
|
|
466
|
-
return "\u2713";
|
|
467
|
-
default:
|
|
468
|
-
return "\u25CB";
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
function getStatusDisplay(status) {
|
|
472
|
-
switch (status) {
|
|
473
|
-
case "in-progress":
|
|
474
|
-
return "active";
|
|
475
|
-
case "blocked":
|
|
476
|
-
return "blocked";
|
|
477
|
-
case "open":
|
|
478
|
-
return "open";
|
|
479
|
-
case "done":
|
|
480
|
-
return "done";
|
|
481
|
-
default:
|
|
482
|
-
return status;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// src/lib/project-utils.ts
|
|
487
|
-
function extractTitle2(content) {
|
|
488
|
-
const match = content.match(/^#\s+(.+)$/m);
|
|
489
|
-
return match ? match[1].trim() : "";
|
|
490
|
-
}
|
|
491
|
-
function isDateSlug(slug) {
|
|
492
|
-
return /^\d{4}-\d{2}-\d{2}$/.test(slug);
|
|
493
|
-
}
|
|
494
|
-
function normalizeStringArray(value) {
|
|
495
|
-
return value.map((item) => item.trim()).filter(Boolean);
|
|
496
|
-
}
|
|
497
|
-
function getProjectsDir(vaultPath) {
|
|
498
|
-
return path3.join(path3.resolve(vaultPath), "projects");
|
|
499
|
-
}
|
|
500
|
-
function ensureProjectsDir(vaultPath) {
|
|
501
|
-
const projectsDir = getProjectsDir(vaultPath);
|
|
502
|
-
if (!fs3.existsSync(projectsDir)) {
|
|
503
|
-
fs3.mkdirSync(projectsDir, { recursive: true });
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
function getProjectPath(vaultPath, slug) {
|
|
507
|
-
return path3.join(getProjectsDir(vaultPath), `${slug}.md`);
|
|
508
|
-
}
|
|
509
|
-
function parseProjectDateValue(filePath) {
|
|
510
|
-
const filename = path3.basename(filePath, ".md");
|
|
511
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(filename)) {
|
|
512
|
-
const dateTs = Date.parse(`${filename}T00:00:00.000Z`);
|
|
513
|
-
if (!Number.isNaN(dateTs)) {
|
|
514
|
-
return dateTs;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
return fs3.statSync(filePath).mtime.getTime();
|
|
518
|
-
}
|
|
519
|
-
function parseSortableTimestamp(value) {
|
|
520
|
-
if (!value) return 0;
|
|
521
|
-
const timestamp = Date.parse(value);
|
|
522
|
-
return Number.isNaN(timestamp) ? 0 : timestamp;
|
|
523
|
-
}
|
|
524
|
-
function normalizeProjectStatus(value) {
|
|
525
|
-
if (value === "active" || value === "paused" || value === "completed" || value === "archived") {
|
|
526
|
-
return value;
|
|
527
|
-
}
|
|
528
|
-
return "active";
|
|
529
|
-
}
|
|
530
|
-
function buildProjectFrontmatterFallback(now, options) {
|
|
531
|
-
const frontmatter = {
|
|
532
|
-
type: "project",
|
|
533
|
-
status: options.status ?? "active",
|
|
534
|
-
created: now,
|
|
535
|
-
updated: now
|
|
536
|
-
};
|
|
537
|
-
if (options.owner) frontmatter.owner = options.owner;
|
|
538
|
-
if (options.team && options.team.length > 0) {
|
|
539
|
-
const team = normalizeStringArray(options.team);
|
|
540
|
-
if (team.length > 0) frontmatter.team = team;
|
|
541
|
-
}
|
|
542
|
-
if (options.client) frontmatter.client = options.client;
|
|
543
|
-
if (options.tags && options.tags.length > 0) {
|
|
544
|
-
const tags = normalizeStringArray(options.tags);
|
|
545
|
-
if (tags.length > 0) frontmatter.tags = tags;
|
|
546
|
-
}
|
|
547
|
-
if (options.description) frontmatter.description = options.description;
|
|
548
|
-
if (options.started) frontmatter.started = options.started;
|
|
549
|
-
if (options.deadline) frontmatter.deadline = options.deadline;
|
|
550
|
-
if (options.repo) frontmatter.repo = options.repo;
|
|
551
|
-
if (options.url) frontmatter.url = options.url;
|
|
552
|
-
if (options.completed) frontmatter.completed = options.completed;
|
|
553
|
-
if (options.reason) frontmatter.reason = options.reason;
|
|
554
|
-
return frontmatter;
|
|
555
|
-
}
|
|
556
|
-
function buildProjectContentFallback(title, options) {
|
|
557
|
-
let content = `# ${title}
|
|
558
|
-
`;
|
|
559
|
-
const wikiLinks = /* @__PURE__ */ new Set();
|
|
560
|
-
if (options.owner) wikiLinks.add(options.owner);
|
|
561
|
-
if (options.client) wikiLinks.add(options.client);
|
|
562
|
-
for (const member of options.team || []) {
|
|
563
|
-
const trimmed = member.trim();
|
|
564
|
-
if (trimmed) wikiLinks.add(trimmed);
|
|
565
|
-
}
|
|
566
|
-
if (wikiLinks.size > 0) {
|
|
567
|
-
content += `
|
|
568
|
-
${Array.from(wikiLinks).map((link) => `[[${link}]]`).join(" | ")}
|
|
569
|
-
`;
|
|
570
|
-
}
|
|
571
|
-
if (options.content) {
|
|
572
|
-
content += `
|
|
573
|
-
${options.content}
|
|
574
|
-
`;
|
|
575
|
-
}
|
|
576
|
-
return content;
|
|
577
|
-
}
|
|
578
|
-
function buildProjectTemplateOverrides(options) {
|
|
579
|
-
const overrides = {};
|
|
580
|
-
if (options.status) overrides.status = options.status;
|
|
581
|
-
if (options.owner) overrides.owner = options.owner;
|
|
582
|
-
if (options.team && options.team.length > 0) {
|
|
583
|
-
const team = normalizeStringArray(options.team);
|
|
584
|
-
if (team.length > 0) overrides.team = team;
|
|
585
|
-
}
|
|
586
|
-
if (options.client) overrides.client = options.client;
|
|
587
|
-
if (options.tags && options.tags.length > 0) {
|
|
588
|
-
const tags = normalizeStringArray(options.tags);
|
|
589
|
-
if (tags.length > 0) overrides.tags = tags;
|
|
590
|
-
}
|
|
591
|
-
if (options.description) overrides.description = options.description;
|
|
592
|
-
if (options.started) overrides.started = options.started;
|
|
593
|
-
if (options.deadline) overrides.deadline = options.deadline;
|
|
594
|
-
if (options.repo) overrides.repo = options.repo;
|
|
595
|
-
if (options.url) overrides.url = options.url;
|
|
596
|
-
if (options.completed) overrides.completed = options.completed;
|
|
597
|
-
if (options.reason) overrides.reason = options.reason;
|
|
598
|
-
return overrides;
|
|
599
|
-
}
|
|
600
|
-
function buildProjectTemplateVariables(title, slug, options) {
|
|
601
|
-
const ownerLink = options.owner ? `[[${options.owner}]]` : "";
|
|
602
|
-
const clientLink = options.client ? `[[${options.client}]]` : "";
|
|
603
|
-
const teamLinks = (options.team || []).map((member) => member.trim()).filter(Boolean).map((member) => `[[${member}]]`);
|
|
604
|
-
const linksLine = [ownerLink, clientLink, ...teamLinks].filter(Boolean).join(" | ");
|
|
605
|
-
return {
|
|
606
|
-
title,
|
|
607
|
-
slug,
|
|
608
|
-
status: options.status ?? "",
|
|
609
|
-
owner: options.owner ?? "",
|
|
610
|
-
client: options.client ?? "",
|
|
611
|
-
team_csv: (options.team || []).join(", "),
|
|
612
|
-
tags_csv: (options.tags || []).join(", "),
|
|
613
|
-
description: options.description ?? "",
|
|
614
|
-
started: options.started ?? "",
|
|
615
|
-
deadline: options.deadline ?? "",
|
|
616
|
-
repo: options.repo ?? "",
|
|
617
|
-
url: options.url ?? "",
|
|
618
|
-
completed: options.completed ?? "",
|
|
619
|
-
reason: options.reason ?? "",
|
|
620
|
-
content: options.content ?? "",
|
|
621
|
-
owner_link: ownerLink,
|
|
622
|
-
client_link: clientLink,
|
|
623
|
-
team_links_line: teamLinks.join(" | "),
|
|
624
|
-
links_line: linksLine
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
function normalizeProjectFrontmatter(frontmatter) {
|
|
628
|
-
const normalizedCreated = typeof frontmatter.created === "string" && frontmatter.created ? frontmatter.created : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
629
|
-
const normalizedUpdated = typeof frontmatter.updated === "string" && frontmatter.updated ? frontmatter.updated : normalizedCreated;
|
|
630
|
-
const normalized = {
|
|
631
|
-
...frontmatter,
|
|
632
|
-
type: "project",
|
|
633
|
-
status: normalizeProjectStatus(frontmatter.status),
|
|
634
|
-
created: normalizedCreated,
|
|
635
|
-
updated: normalizedUpdated
|
|
636
|
-
};
|
|
637
|
-
if (normalized.team) {
|
|
638
|
-
const team = normalizeStringArray(normalized.team);
|
|
639
|
-
if (team.length === 0) {
|
|
640
|
-
delete normalized.team;
|
|
641
|
-
} else {
|
|
642
|
-
normalized.team = team;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
if (normalized.tags) {
|
|
646
|
-
const tags = normalizeStringArray(normalized.tags);
|
|
647
|
-
if (tags.length === 0) {
|
|
648
|
-
delete normalized.tags;
|
|
649
|
-
} else {
|
|
650
|
-
normalized.tags = tags;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
return normalized;
|
|
654
|
-
}
|
|
655
|
-
function listProjects(vaultPath, filters) {
|
|
656
|
-
const projectsDir = getProjectsDir(vaultPath);
|
|
657
|
-
if (!fs3.existsSync(projectsDir)) {
|
|
658
|
-
return [];
|
|
659
|
-
}
|
|
660
|
-
const projects = [];
|
|
661
|
-
const entries = fs3.readdirSync(projectsDir, { withFileTypes: true });
|
|
662
|
-
for (const entry of entries) {
|
|
663
|
-
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
664
|
-
continue;
|
|
665
|
-
}
|
|
666
|
-
const slug = entry.name.replace(/\.md$/, "");
|
|
667
|
-
if (isDateSlug(slug)) {
|
|
668
|
-
continue;
|
|
669
|
-
}
|
|
670
|
-
const project = readProject(vaultPath, slug);
|
|
671
|
-
if (!project) continue;
|
|
672
|
-
if (filters) {
|
|
673
|
-
if (filters.status && project.frontmatter.status !== filters.status) continue;
|
|
674
|
-
if (filters.owner && project.frontmatter.owner !== filters.owner) continue;
|
|
675
|
-
if (filters.client && project.frontmatter.client !== filters.client) continue;
|
|
676
|
-
if (filters.tag) {
|
|
677
|
-
const tags = project.frontmatter.tags || [];
|
|
678
|
-
const hasTag = tags.some((tag) => tag.toLowerCase() === filters.tag?.toLowerCase());
|
|
679
|
-
if (!hasTag) continue;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
projects.push(project);
|
|
683
|
-
}
|
|
684
|
-
return projects.sort((left, right) => {
|
|
685
|
-
const rightTime = parseSortableTimestamp(right.frontmatter.updated || right.frontmatter.created);
|
|
686
|
-
const leftTime = parseSortableTimestamp(left.frontmatter.updated || left.frontmatter.created);
|
|
687
|
-
return rightTime - leftTime;
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
function readProject(vaultPath, slug) {
|
|
691
|
-
if (!slug || isDateSlug(slug) || slug.includes(path3.sep)) {
|
|
692
|
-
return null;
|
|
693
|
-
}
|
|
694
|
-
const projectPath = getProjectPath(vaultPath, slug);
|
|
695
|
-
if (!fs3.existsSync(projectPath)) {
|
|
696
|
-
return null;
|
|
697
|
-
}
|
|
698
|
-
try {
|
|
699
|
-
const raw = fs3.readFileSync(projectPath, "utf-8");
|
|
700
|
-
const { data, content } = (0, import_gray_matter3.default)(raw);
|
|
701
|
-
if (data.type !== "project") {
|
|
702
|
-
return null;
|
|
703
|
-
}
|
|
704
|
-
const frontmatter = normalizeProjectFrontmatter(data);
|
|
705
|
-
const title = extractTitle2(content) || slug;
|
|
706
|
-
return {
|
|
707
|
-
slug,
|
|
708
|
-
title,
|
|
709
|
-
content,
|
|
710
|
-
frontmatter
|
|
711
|
-
};
|
|
712
|
-
} catch {
|
|
713
|
-
return null;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
function createProject(vaultPath, title, options = {}) {
|
|
717
|
-
ensureProjectsDir(vaultPath);
|
|
718
|
-
const slug = slugify(title);
|
|
719
|
-
const projectPath = getProjectPath(vaultPath, slug);
|
|
720
|
-
if (fs3.existsSync(projectPath)) {
|
|
721
|
-
throw new Error(`Project already exists: ${slug}`);
|
|
722
|
-
}
|
|
723
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
724
|
-
const template = loadSchemaTemplateDefinition("project", {
|
|
725
|
-
vaultPath: path3.resolve(vaultPath)
|
|
726
|
-
});
|
|
727
|
-
let frontmatter;
|
|
728
|
-
let content;
|
|
729
|
-
if (template) {
|
|
730
|
-
const rendered = renderDocumentFromTemplate(template, {
|
|
731
|
-
title,
|
|
732
|
-
type: "project",
|
|
733
|
-
now: new Date(now),
|
|
734
|
-
variables: buildProjectTemplateVariables(title, slug, options),
|
|
735
|
-
overrides: buildProjectTemplateOverrides(options),
|
|
736
|
-
frontmatter: { pruneEmpty: true }
|
|
737
|
-
});
|
|
738
|
-
const templateFrontmatter = rendered.frontmatter;
|
|
739
|
-
frontmatter = normalizeProjectFrontmatter({
|
|
740
|
-
...templateFrontmatter,
|
|
741
|
-
type: "project",
|
|
742
|
-
status: normalizeProjectStatus(templateFrontmatter.status),
|
|
743
|
-
created: typeof templateFrontmatter.created === "string" && templateFrontmatter.created ? templateFrontmatter.created : now,
|
|
744
|
-
updated: typeof templateFrontmatter.updated === "string" && templateFrontmatter.updated ? templateFrontmatter.updated : now
|
|
745
|
-
});
|
|
746
|
-
content = rendered.content;
|
|
747
|
-
} else {
|
|
748
|
-
frontmatter = buildProjectFrontmatterFallback(now, options);
|
|
749
|
-
content = buildProjectContentFallback(title, options);
|
|
750
|
-
}
|
|
751
|
-
const fileContent = import_gray_matter3.default.stringify(content, frontmatter);
|
|
752
|
-
fs3.writeFileSync(projectPath, fileContent);
|
|
753
|
-
return {
|
|
754
|
-
slug,
|
|
755
|
-
title,
|
|
756
|
-
content,
|
|
757
|
-
frontmatter
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
function updateProject(vaultPath, slug, updates) {
|
|
761
|
-
const project = readProject(vaultPath, slug);
|
|
762
|
-
if (!project) {
|
|
763
|
-
throw new Error(`Project not found: ${slug}`);
|
|
764
|
-
}
|
|
765
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
766
|
-
const nextFrontmatter = {
|
|
767
|
-
...project.frontmatter,
|
|
768
|
-
type: "project",
|
|
769
|
-
updated: now
|
|
770
|
-
};
|
|
771
|
-
if (updates.status !== void 0) {
|
|
772
|
-
nextFrontmatter.status = updates.status;
|
|
773
|
-
if (updates.status === "completed" && !updates.completed && !nextFrontmatter.completed) {
|
|
774
|
-
nextFrontmatter.completed = now;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
if (updates.owner !== void 0) {
|
|
778
|
-
if (updates.owner === null || updates.owner.trim() === "") {
|
|
779
|
-
delete nextFrontmatter.owner;
|
|
780
|
-
} else {
|
|
781
|
-
nextFrontmatter.owner = updates.owner;
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
if (updates.team !== void 0) {
|
|
785
|
-
if (updates.team === null) {
|
|
786
|
-
delete nextFrontmatter.team;
|
|
787
|
-
} else {
|
|
788
|
-
const team = normalizeStringArray(updates.team);
|
|
789
|
-
if (team.length === 0) {
|
|
790
|
-
delete nextFrontmatter.team;
|
|
791
|
-
} else {
|
|
792
|
-
nextFrontmatter.team = team;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
if (updates.client !== void 0) {
|
|
797
|
-
if (updates.client === null || updates.client.trim() === "") {
|
|
798
|
-
delete nextFrontmatter.client;
|
|
799
|
-
} else {
|
|
800
|
-
nextFrontmatter.client = updates.client;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
if (updates.tags !== void 0) {
|
|
804
|
-
if (updates.tags === null) {
|
|
805
|
-
delete nextFrontmatter.tags;
|
|
806
|
-
} else {
|
|
807
|
-
const tags = normalizeStringArray(updates.tags);
|
|
808
|
-
if (tags.length === 0) {
|
|
809
|
-
delete nextFrontmatter.tags;
|
|
810
|
-
} else {
|
|
811
|
-
nextFrontmatter.tags = tags;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
if (updates.description !== void 0) {
|
|
816
|
-
if (updates.description === null || updates.description.trim() === "") {
|
|
817
|
-
delete nextFrontmatter.description;
|
|
818
|
-
} else {
|
|
819
|
-
nextFrontmatter.description = updates.description;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
if (updates.started !== void 0) {
|
|
823
|
-
if (updates.started === null || updates.started.trim() === "") {
|
|
824
|
-
delete nextFrontmatter.started;
|
|
825
|
-
} else {
|
|
826
|
-
nextFrontmatter.started = updates.started;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
if (updates.deadline !== void 0) {
|
|
830
|
-
if (updates.deadline === null || updates.deadline.trim() === "") {
|
|
831
|
-
delete nextFrontmatter.deadline;
|
|
832
|
-
} else {
|
|
833
|
-
nextFrontmatter.deadline = updates.deadline;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
if (updates.repo !== void 0) {
|
|
837
|
-
if (updates.repo === null || updates.repo.trim() === "") {
|
|
838
|
-
delete nextFrontmatter.repo;
|
|
839
|
-
} else {
|
|
840
|
-
nextFrontmatter.repo = updates.repo;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
if (updates.url !== void 0) {
|
|
844
|
-
if (updates.url === null || updates.url.trim() === "") {
|
|
845
|
-
delete nextFrontmatter.url;
|
|
846
|
-
} else {
|
|
847
|
-
nextFrontmatter.url = updates.url;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
if (updates.completed !== void 0) {
|
|
851
|
-
if (updates.completed === null || updates.completed.trim() === "") {
|
|
852
|
-
delete nextFrontmatter.completed;
|
|
853
|
-
} else {
|
|
854
|
-
nextFrontmatter.completed = updates.completed;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
if (updates.reason !== void 0) {
|
|
858
|
-
if (updates.reason === null || updates.reason.trim() === "") {
|
|
859
|
-
delete nextFrontmatter.reason;
|
|
860
|
-
} else {
|
|
861
|
-
nextFrontmatter.reason = updates.reason;
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
const projectPath = getProjectPath(vaultPath, slug);
|
|
865
|
-
fs3.writeFileSync(projectPath, import_gray_matter3.default.stringify(project.content, nextFrontmatter));
|
|
866
|
-
return {
|
|
867
|
-
...project,
|
|
868
|
-
frontmatter: nextFrontmatter
|
|
869
|
-
};
|
|
870
|
-
}
|
|
871
|
-
function archiveProject(vaultPath, slug, reason) {
|
|
872
|
-
return updateProject(vaultPath, slug, {
|
|
873
|
-
status: "archived",
|
|
874
|
-
reason: reason ?? null,
|
|
875
|
-
completed: (/* @__PURE__ */ new Date()).toISOString()
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
function getProjectTasks(vaultPath, slug) {
|
|
879
|
-
return listTasks(vaultPath, { project: slug });
|
|
880
|
-
}
|
|
881
|
-
function getProjectActivity(vaultPath, slug) {
|
|
882
|
-
const projectActivityDir = path3.join(getProjectsDir(vaultPath), slug);
|
|
883
|
-
if (!fs3.existsSync(projectActivityDir) || !fs3.statSync(projectActivityDir).isDirectory()) {
|
|
884
|
-
return [];
|
|
885
|
-
}
|
|
886
|
-
const entries = fs3.readdirSync(projectActivityDir, { withFileTypes: true });
|
|
887
|
-
const files = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => path3.join(projectActivityDir, entry.name));
|
|
888
|
-
return files.sort((left, right) => parseProjectDateValue(right) - parseProjectDateValue(left));
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
// src/commands/task.ts
|
|
892
|
-
function taskList(vaultPath, options = {}) {
|
|
893
|
-
const filters = {};
|
|
894
|
-
if (options.status) filters.status = options.status;
|
|
895
|
-
if (options.owner) filters.owner = options.owner;
|
|
896
|
-
if (options.project) filters.project = options.project;
|
|
897
|
-
if (options.priority) filters.priority = options.priority;
|
|
898
|
-
if (options.due) filters.due = true;
|
|
899
|
-
if (options.tag) filters.tag = options.tag;
|
|
900
|
-
if (options.overdue) filters.overdue = true;
|
|
901
|
-
const listed = listTasks(vaultPath, filters);
|
|
902
|
-
if (!options.status && !options.overdue) {
|
|
903
|
-
return listed.filter((t) => t.frontmatter.status !== "done");
|
|
904
|
-
}
|
|
905
|
-
return listed;
|
|
906
|
-
}
|
|
907
|
-
function formatTaskList(tasks) {
|
|
908
|
-
if (tasks.length === 0) {
|
|
909
|
-
return "No tasks found.\n";
|
|
910
|
-
}
|
|
911
|
-
const headers = ["STATUS", "OWNER", "PRIORITY", "PROJECT", "META", "TITLE"];
|
|
912
|
-
const widths = [10, 12, 8, 14, 64, 32];
|
|
913
|
-
const truncate = (value, width) => {
|
|
914
|
-
if (value.length <= width) return value;
|
|
915
|
-
return value.slice(0, width - 3) + "...";
|
|
916
|
-
};
|
|
917
|
-
let output = headers.map((h, i) => h.padEnd(widths[i])).join(" ") + "\n";
|
|
918
|
-
for (const task of tasks) {
|
|
919
|
-
const icon = getStatusIcon(task.frontmatter.status);
|
|
920
|
-
const statusDisplay = getStatusDisplay(task.frontmatter.status);
|
|
921
|
-
const status = `${icon} ${statusDisplay}`;
|
|
922
|
-
const owner = task.frontmatter.owner || "-";
|
|
923
|
-
const priority = task.frontmatter.priority || "low";
|
|
924
|
-
const project = task.frontmatter.project || "-";
|
|
925
|
-
const metaParts = [];
|
|
926
|
-
if (task.frontmatter.due) metaParts.push(`due:${task.frontmatter.due.split("T")[0]}`);
|
|
927
|
-
if (task.frontmatter.tags?.length) metaParts.push(task.frontmatter.tags.map((tag) => `#${tag}`).join(","));
|
|
928
|
-
if (task.frontmatter.parent) metaParts.push(`parent:${task.frontmatter.parent}`);
|
|
929
|
-
if (task.frontmatter.depends_on?.length) {
|
|
930
|
-
metaParts.push(`deps:${task.frontmatter.depends_on.join("|")}`);
|
|
931
|
-
}
|
|
932
|
-
const meta = metaParts.length > 0 ? metaParts.join(" ") : "-";
|
|
933
|
-
const title = truncate(task.title, widths[5]);
|
|
934
|
-
const row = [
|
|
935
|
-
status.padEnd(widths[0]),
|
|
936
|
-
owner.padEnd(widths[1]),
|
|
937
|
-
priority.padEnd(widths[2]),
|
|
938
|
-
project.padEnd(widths[3]),
|
|
939
|
-
truncate(meta, widths[4]).padEnd(widths[4]),
|
|
940
|
-
title
|
|
941
|
-
];
|
|
942
|
-
output += row.join(" ") + "\n";
|
|
943
|
-
}
|
|
944
|
-
return output;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// src/commands/project.ts
|
|
948
|
-
function toDateStr(value) {
|
|
949
|
-
if (!value) return "-";
|
|
950
|
-
return value.includes("T") ? value.split("T")[0] : value;
|
|
951
|
-
}
|
|
952
|
-
function toHashTag(value) {
|
|
953
|
-
return value.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9/_-]/g, "");
|
|
954
|
-
}
|
|
955
|
-
function toMention(value) {
|
|
956
|
-
return value.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "");
|
|
957
|
-
}
|
|
958
|
-
function normalizeBoardGroupBy(value) {
|
|
959
|
-
const normalized = String(value || "status").trim().toLowerCase();
|
|
960
|
-
if (normalized === "status" || normalized === "owner" || normalized === "client") {
|
|
961
|
-
return normalized;
|
|
962
|
-
}
|
|
963
|
-
throw new Error(`Unsupported project board group field: ${normalized}`);
|
|
964
|
-
}
|
|
965
|
-
function resolveBoardPath(vaultPath, output) {
|
|
966
|
-
const resolvedVaultPath = path4.resolve(vaultPath);
|
|
967
|
-
if (!output) {
|
|
968
|
-
return path4.join(resolvedVaultPath, "Projects-Board.md");
|
|
969
|
-
}
|
|
970
|
-
if (path4.isAbsolute(output)) {
|
|
971
|
-
return output;
|
|
972
|
-
}
|
|
973
|
-
return path4.join(resolvedVaultPath, output);
|
|
974
|
-
}
|
|
975
|
-
function parseDeadlineTimestamp(project) {
|
|
976
|
-
if (!project.frontmatter.deadline) return Number.POSITIVE_INFINITY;
|
|
977
|
-
const timestamp = Date.parse(project.frontmatter.deadline);
|
|
978
|
-
return Number.isNaN(timestamp) ? Number.POSITIVE_INFINITY : timestamp;
|
|
979
|
-
}
|
|
980
|
-
function sortProjectsForCards(projects) {
|
|
981
|
-
return [...projects].sort((left, right) => {
|
|
982
|
-
const deadlineDiff = parseDeadlineTimestamp(left) - parseDeadlineTimestamp(right);
|
|
983
|
-
if (deadlineDiff !== 0) return deadlineDiff;
|
|
984
|
-
return new Date(right.frontmatter.updated).getTime() - new Date(left.frontmatter.updated).getTime();
|
|
985
|
-
});
|
|
986
|
-
}
|
|
987
|
-
function laneNameForStatus(status) {
|
|
988
|
-
switch (status) {
|
|
989
|
-
case "active":
|
|
990
|
-
return "Active";
|
|
991
|
-
case "paused":
|
|
992
|
-
return "Paused";
|
|
993
|
-
case "completed":
|
|
994
|
-
return "Completed";
|
|
995
|
-
case "archived":
|
|
996
|
-
return "Archived";
|
|
997
|
-
default:
|
|
998
|
-
return "Active";
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
function laneNameForProject(project, groupBy) {
|
|
1002
|
-
switch (groupBy) {
|
|
1003
|
-
case "status":
|
|
1004
|
-
return laneNameForStatus(project.frontmatter.status);
|
|
1005
|
-
case "owner":
|
|
1006
|
-
return project.frontmatter.owner?.trim() || "Unassigned";
|
|
1007
|
-
case "client":
|
|
1008
|
-
return project.frontmatter.client?.trim() || "No Client";
|
|
1009
|
-
default:
|
|
1010
|
-
return laneNameForStatus(project.frontmatter.status);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
function defaultLaneOrder(groupBy, projects) {
|
|
1014
|
-
if (groupBy === "status") {
|
|
1015
|
-
return ["Active", "Paused", "Completed", "Archived"];
|
|
1016
|
-
}
|
|
1017
|
-
const fallback = groupBy === "owner" ? "Unassigned" : "No Client";
|
|
1018
|
-
const names = /* @__PURE__ */ new Set();
|
|
1019
|
-
for (const project of projects) {
|
|
1020
|
-
names.add(laneNameForProject(project, groupBy));
|
|
1021
|
-
}
|
|
1022
|
-
if (names.size === 0) {
|
|
1023
|
-
return [fallback];
|
|
1024
|
-
}
|
|
1025
|
-
const sorted = Array.from(names).sort((left, right) => left.localeCompare(right));
|
|
1026
|
-
if (sorted.includes(fallback)) {
|
|
1027
|
-
return [...sorted.filter((name) => name !== fallback), fallback];
|
|
1028
|
-
}
|
|
1029
|
-
return sorted;
|
|
1030
|
-
}
|
|
1031
|
-
function formatProjectCard(project) {
|
|
1032
|
-
const checkbox = project.frontmatter.status === "completed" || project.frontmatter.status === "archived" ? "x" : " ";
|
|
1033
|
-
const parts = [`[[projects/${project.slug}|${project.title}]]`];
|
|
1034
|
-
if (project.frontmatter.owner) {
|
|
1035
|
-
const mention = toMention(project.frontmatter.owner);
|
|
1036
|
-
if (mention) parts.push(`@${mention}`);
|
|
1037
|
-
}
|
|
1038
|
-
if (project.frontmatter.client) {
|
|
1039
|
-
const clientTag = toHashTag(project.frontmatter.client);
|
|
1040
|
-
if (clientTag) parts.push(`#client/${clientTag}`);
|
|
1041
|
-
}
|
|
1042
|
-
if (project.frontmatter.tags && project.frontmatter.tags.length > 0) {
|
|
1043
|
-
for (const tag of project.frontmatter.tags) {
|
|
1044
|
-
const normalizedTag = toHashTag(tag);
|
|
1045
|
-
if (normalizedTag) parts.push(`#${normalizedTag}`);
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
if (project.frontmatter.deadline) {
|
|
1049
|
-
parts.push(`\u{1F4C5} ${toDateStr(project.frontmatter.deadline)}`);
|
|
1050
|
-
}
|
|
1051
|
-
if (project.frontmatter.description) {
|
|
1052
|
-
parts.push(`\u2014 ${project.frontmatter.description}`);
|
|
1053
|
-
}
|
|
1054
|
-
return `- [${checkbox}] ${parts.join(" ")}`;
|
|
1055
|
-
}
|
|
1056
|
-
function buildProjectBoardLanes(projects, groupBy) {
|
|
1057
|
-
const laneOrder = defaultLaneOrder(groupBy, projects);
|
|
1058
|
-
const lanes = /* @__PURE__ */ new Map();
|
|
1059
|
-
for (const laneName of laneOrder) {
|
|
1060
|
-
lanes.set(laneName, []);
|
|
1061
|
-
}
|
|
1062
|
-
for (const project of sortProjectsForCards(projects)) {
|
|
1063
|
-
const laneName = laneNameForProject(project, groupBy);
|
|
1064
|
-
if (!lanes.has(laneName)) {
|
|
1065
|
-
lanes.set(laneName, []);
|
|
1066
|
-
}
|
|
1067
|
-
lanes.get(laneName)?.push(formatProjectCard(project));
|
|
1068
|
-
}
|
|
1069
|
-
return Array.from(lanes.entries()).map(([name, cards]) => ({ name, cards }));
|
|
1070
|
-
}
|
|
1071
|
-
function generateProjectBoardMarkdown(projects, options = {}) {
|
|
1072
|
-
const groupBy = normalizeBoardGroupBy(options.groupBy);
|
|
1073
|
-
const syncedAt = (options.now || /* @__PURE__ */ new Date()).toISOString();
|
|
1074
|
-
const lanes = buildProjectBoardLanes(projects, groupBy);
|
|
1075
|
-
const sections = lanes.map((lane) => {
|
|
1076
|
-
const cardsBlock = lane.cards.length > 0 ? lane.cards.join("\n") : "";
|
|
1077
|
-
return `## ${lane.name}
|
|
1078
|
-
|
|
1079
|
-
${cardsBlock}`.trimEnd();
|
|
1080
|
-
}).join("\n\n");
|
|
1081
|
-
return [
|
|
1082
|
-
"---",
|
|
1083
|
-
"kanban-plugin: basic",
|
|
1084
|
-
`clawvault-group-by: ${groupBy}`,
|
|
1085
|
-
`clawvault-last-sync: '${syncedAt}'`,
|
|
1086
|
-
"---",
|
|
1087
|
-
"",
|
|
1088
|
-
sections,
|
|
1089
|
-
"",
|
|
1090
|
-
"%% kanban:settings",
|
|
1091
|
-
'{"kanban-plugin":"basic","list-collapse":["Completed","Archived"],"show-checkboxes":true}',
|
|
1092
|
-
"%%",
|
|
1093
|
-
""
|
|
1094
|
-
].join("\n");
|
|
1095
|
-
}
|
|
1096
|
-
function syncProjectBoard(vaultPath, options = {}) {
|
|
1097
|
-
const groupBy = normalizeBoardGroupBy(options.groupBy);
|
|
1098
|
-
const outputPath = resolveBoardPath(vaultPath, options.output);
|
|
1099
|
-
const projects = listProjects(vaultPath);
|
|
1100
|
-
const markdown = generateProjectBoardMarkdown(projects, { groupBy, now: options.now });
|
|
1101
|
-
fs4.writeFileSync(outputPath, markdown);
|
|
1102
|
-
return {
|
|
1103
|
-
outputPath,
|
|
1104
|
-
groupBy,
|
|
1105
|
-
markdown,
|
|
1106
|
-
lanes: buildProjectBoardLanes(projects, groupBy),
|
|
1107
|
-
projectCount: projects.length
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
function projectAdd(vaultPath, title, options = {}) {
|
|
1111
|
-
return createProject(vaultPath, title, {
|
|
1112
|
-
status: options.status,
|
|
1113
|
-
owner: options.owner,
|
|
1114
|
-
team: options.team,
|
|
1115
|
-
client: options.client,
|
|
1116
|
-
tags: options.tags,
|
|
1117
|
-
description: options.description,
|
|
1118
|
-
deadline: options.deadline,
|
|
1119
|
-
repo: options.repo,
|
|
1120
|
-
url: options.url,
|
|
1121
|
-
content: options.content
|
|
1122
|
-
});
|
|
1123
|
-
}
|
|
1124
|
-
function projectUpdate(vaultPath, slug, options) {
|
|
1125
|
-
return updateProject(vaultPath, slug, {
|
|
1126
|
-
status: options.status,
|
|
1127
|
-
owner: options.owner,
|
|
1128
|
-
team: options.team,
|
|
1129
|
-
client: options.client,
|
|
1130
|
-
tags: options.tags,
|
|
1131
|
-
description: options.description,
|
|
1132
|
-
deadline: options.deadline,
|
|
1133
|
-
repo: options.repo,
|
|
1134
|
-
url: options.url
|
|
1135
|
-
});
|
|
1136
|
-
}
|
|
1137
|
-
function projectArchive(vaultPath, slug, options = {}) {
|
|
1138
|
-
return archiveProject(vaultPath, slug, options.reason);
|
|
1139
|
-
}
|
|
1140
|
-
function projectList(vaultPath, options = {}) {
|
|
1141
|
-
const projects = listProjects(vaultPath, {
|
|
1142
|
-
status: options.status,
|
|
1143
|
-
owner: options.owner,
|
|
1144
|
-
client: options.client,
|
|
1145
|
-
tag: options.tag
|
|
1146
|
-
});
|
|
1147
|
-
if (!options.status) {
|
|
1148
|
-
return projects.filter((project) => project.frontmatter.status !== "archived");
|
|
1149
|
-
}
|
|
1150
|
-
return projects;
|
|
1151
|
-
}
|
|
1152
|
-
function formatProjectList(projects) {
|
|
1153
|
-
if (projects.length === 0) {
|
|
1154
|
-
return "No projects found.\n";
|
|
1155
|
-
}
|
|
1156
|
-
const headers = ["STATUS", "OWNER", "DEADLINE", "TITLE"];
|
|
1157
|
-
const widths = [10, 12, 12, 40];
|
|
1158
|
-
const truncate = (value, width) => {
|
|
1159
|
-
if (value.length <= width) return value;
|
|
1160
|
-
return value.slice(0, width - 3) + "...";
|
|
1161
|
-
};
|
|
1162
|
-
let output = headers.map((header, index) => header.padEnd(widths[index])).join(" ") + "\n";
|
|
1163
|
-
for (const project of projects) {
|
|
1164
|
-
const row = [
|
|
1165
|
-
project.frontmatter.status.padEnd(widths[0]),
|
|
1166
|
-
(project.frontmatter.owner || "-").padEnd(widths[1]),
|
|
1167
|
-
toDateStr(project.frontmatter.deadline).padEnd(widths[2]),
|
|
1168
|
-
truncate(project.title, widths[3])
|
|
1169
|
-
];
|
|
1170
|
-
output += row.join(" ") + "\n";
|
|
1171
|
-
}
|
|
1172
|
-
return output;
|
|
1173
|
-
}
|
|
1174
|
-
function formatFieldValue(value) {
|
|
1175
|
-
if (Array.isArray(value)) {
|
|
1176
|
-
return value.join(", ");
|
|
1177
|
-
}
|
|
1178
|
-
if (typeof value === "string") {
|
|
1179
|
-
return value;
|
|
1180
|
-
}
|
|
1181
|
-
if (value === null || value === void 0) {
|
|
1182
|
-
return "-";
|
|
1183
|
-
}
|
|
1184
|
-
return String(value);
|
|
1185
|
-
}
|
|
1186
|
-
function countTasksByStatus(projectSlug, vaultPath) {
|
|
1187
|
-
const tasks = getProjectTasks(vaultPath, projectSlug);
|
|
1188
|
-
return {
|
|
1189
|
-
open: tasks.filter((task) => task.frontmatter.status === "open").length,
|
|
1190
|
-
inProgress: tasks.filter((task) => task.frontmatter.status === "in-progress").length,
|
|
1191
|
-
done: tasks.filter((task) => task.frontmatter.status === "done").length
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
function formatProjectDetails(vaultPath, project, options = {}) {
|
|
1195
|
-
const lines = [];
|
|
1196
|
-
const taskSummary = countTasksByStatus(project.slug, vaultPath);
|
|
1197
|
-
const activity = getProjectActivity(vaultPath, project.slug).slice(0, options.activityLimit ?? 5);
|
|
1198
|
-
lines.push(`# ${project.title}`);
|
|
1199
|
-
lines.push("-".repeat(40));
|
|
1200
|
-
const orderedFields = [
|
|
1201
|
-
"type",
|
|
1202
|
-
"status",
|
|
1203
|
-
"owner",
|
|
1204
|
-
"team",
|
|
1205
|
-
"client",
|
|
1206
|
-
"tags",
|
|
1207
|
-
"description",
|
|
1208
|
-
"started",
|
|
1209
|
-
"deadline",
|
|
1210
|
-
"repo",
|
|
1211
|
-
"url",
|
|
1212
|
-
"created",
|
|
1213
|
-
"updated",
|
|
1214
|
-
"completed",
|
|
1215
|
-
"reason"
|
|
1216
|
-
];
|
|
1217
|
-
for (const field of orderedFields) {
|
|
1218
|
-
const value = project.frontmatter[field];
|
|
1219
|
-
if (value === void 0) continue;
|
|
1220
|
-
lines.push(`${field}: ${formatFieldValue(value)}`);
|
|
1221
|
-
}
|
|
1222
|
-
lines.push("");
|
|
1223
|
-
lines.push(`Linked tasks: ${taskSummary.open} open, ${taskSummary.inProgress} in-progress, ${taskSummary.done} done`);
|
|
1224
|
-
if (project.frontmatter.team && project.frontmatter.team.length > 0) {
|
|
1225
|
-
lines.push("Team members:");
|
|
1226
|
-
for (const member of project.frontmatter.team) {
|
|
1227
|
-
lines.push(`- ${member}`);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
lines.push("");
|
|
1231
|
-
lines.push("Recent activity:");
|
|
1232
|
-
if (activity.length === 0) {
|
|
1233
|
-
lines.push("- none");
|
|
1234
|
-
} else {
|
|
1235
|
-
for (const filePath of activity) {
|
|
1236
|
-
lines.push(`- ${path4.basename(filePath)}`);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
return lines.join("\n");
|
|
1240
|
-
}
|
|
1241
|
-
async function projectCommand(vaultPath, action, args) {
|
|
1242
|
-
const options = args.options || {};
|
|
1243
|
-
switch (action) {
|
|
1244
|
-
case "add": {
|
|
1245
|
-
if (!args.title) {
|
|
1246
|
-
throw new Error("Title is required for project add");
|
|
1247
|
-
}
|
|
1248
|
-
const project = projectAdd(vaultPath, args.title, options);
|
|
1249
|
-
console.log(`\u2713 Created project: ${project.slug}`);
|
|
1250
|
-
console.log(` Path: ${path4.join(path4.resolve(vaultPath), "projects", `${project.slug}.md`)}`);
|
|
1251
|
-
break;
|
|
1252
|
-
}
|
|
1253
|
-
case "update": {
|
|
1254
|
-
if (!args.slug) {
|
|
1255
|
-
throw new Error("Project slug is required for update");
|
|
1256
|
-
}
|
|
1257
|
-
const project = projectUpdate(vaultPath, args.slug, options);
|
|
1258
|
-
console.log(`\u2713 Updated project: ${project.slug}`);
|
|
1259
|
-
break;
|
|
1260
|
-
}
|
|
1261
|
-
case "archive": {
|
|
1262
|
-
if (!args.slug) {
|
|
1263
|
-
throw new Error("Project slug is required for archive");
|
|
1264
|
-
}
|
|
1265
|
-
const project = projectArchive(vaultPath, args.slug, options);
|
|
1266
|
-
console.log(`\u2713 Archived project: ${project.slug}`);
|
|
1267
|
-
break;
|
|
1268
|
-
}
|
|
1269
|
-
case "list": {
|
|
1270
|
-
const projects = projectList(vaultPath, options);
|
|
1271
|
-
if (options.json) {
|
|
1272
|
-
console.log(JSON.stringify(projects, null, 2));
|
|
1273
|
-
} else {
|
|
1274
|
-
console.log(formatProjectList(projects));
|
|
1275
|
-
}
|
|
1276
|
-
break;
|
|
1277
|
-
}
|
|
1278
|
-
case "show": {
|
|
1279
|
-
if (!args.slug) {
|
|
1280
|
-
throw new Error("Project slug is required for show");
|
|
1281
|
-
}
|
|
1282
|
-
const project = readProject(vaultPath, args.slug);
|
|
1283
|
-
if (!project) {
|
|
1284
|
-
throw new Error(`Project not found: ${args.slug}`);
|
|
1285
|
-
}
|
|
1286
|
-
const taskSummary = countTasksByStatus(project.slug, vaultPath);
|
|
1287
|
-
const recentActivity = getProjectActivity(vaultPath, project.slug).slice(0, 5);
|
|
1288
|
-
if (options.json) {
|
|
1289
|
-
console.log(
|
|
1290
|
-
JSON.stringify(
|
|
1291
|
-
{
|
|
1292
|
-
project,
|
|
1293
|
-
taskSummary,
|
|
1294
|
-
team: project.frontmatter.team || [],
|
|
1295
|
-
recentActivity
|
|
1296
|
-
},
|
|
1297
|
-
null,
|
|
1298
|
-
2
|
|
1299
|
-
)
|
|
1300
|
-
);
|
|
1301
|
-
} else {
|
|
1302
|
-
console.log(formatProjectDetails(vaultPath, project, { activityLimit: 5 }));
|
|
1303
|
-
}
|
|
1304
|
-
break;
|
|
1305
|
-
}
|
|
1306
|
-
case "tasks": {
|
|
1307
|
-
if (!args.slug) {
|
|
1308
|
-
throw new Error("Project slug is required for tasks");
|
|
1309
|
-
}
|
|
1310
|
-
const tasks = taskList(vaultPath, { project: args.slug });
|
|
1311
|
-
if (options.json) {
|
|
1312
|
-
console.log(JSON.stringify(tasks, null, 2));
|
|
1313
|
-
} else {
|
|
1314
|
-
console.log(formatTaskList(tasks));
|
|
1315
|
-
}
|
|
1316
|
-
break;
|
|
1317
|
-
}
|
|
1318
|
-
case "board": {
|
|
1319
|
-
const result = syncProjectBoard(vaultPath, options);
|
|
1320
|
-
console.log(`\u2713 Synced project board: ${result.outputPath}`);
|
|
1321
|
-
console.log(` Grouped by: ${result.groupBy}`);
|
|
1322
|
-
console.log(` Projects included: ${result.projectCount}`);
|
|
1323
|
-
break;
|
|
1324
|
-
}
|
|
1325
|
-
default:
|
|
1326
|
-
throw new Error(`Unknown project action: ${action}`);
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1330
|
-
0 && (module.exports = {
|
|
1331
|
-
buildProjectBoardLanes,
|
|
1332
|
-
formatProjectDetails,
|
|
1333
|
-
formatProjectList,
|
|
1334
|
-
generateProjectBoardMarkdown,
|
|
1335
|
-
projectAdd,
|
|
1336
|
-
projectArchive,
|
|
1337
|
-
projectCommand,
|
|
1338
|
-
projectList,
|
|
1339
|
-
projectUpdate,
|
|
1340
|
-
syncProjectBoard
|
|
1341
|
-
});
|