grounder 0.2.0 → 0.4.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 +179 -66
- package/dist/agents/claude.d.ts +1 -0
- package/dist/agents/claude.d.ts.map +1 -1
- package/dist/agents/claude.js +37 -13
- package/dist/agents/claude.js.map +1 -1
- package/dist/agents/cursor.d.ts +1 -0
- package/dist/agents/cursor.d.ts.map +1 -1
- package/dist/agents/cursor.js +37 -13
- package/dist/agents/cursor.js.map +1 -1
- package/dist/agents/hook-runtime.d.ts +65 -21
- package/dist/agents/hook-runtime.d.ts.map +1 -1
- package/dist/agents/hook-runtime.js +172 -31
- package/dist/agents/hook-runtime.js.map +1 -1
- package/dist/agents/index.d.ts +18 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +27 -4
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/install-command.d.ts +35 -0
- package/dist/agents/install-command.d.ts.map +1 -0
- package/dist/agents/install-command.js +65 -0
- package/dist/agents/install-command.js.map +1 -0
- package/dist/agents/types.d.ts +19 -1
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/cli.js +62 -60
- package/dist/cli.js.map +1 -1
- package/dist/commands/apply-agent-installs.d.ts +34 -0
- package/dist/commands/apply-agent-installs.d.ts.map +1 -0
- package/dist/commands/apply-agent-installs.js +137 -0
- package/dist/commands/apply-agent-installs.js.map +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +278 -60
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/handoff/list.d.ts +15 -7
- package/dist/commands/handoff/list.d.ts.map +1 -1
- package/dist/commands/handoff/list.js +21 -13
- package/dist/commands/handoff/list.js.map +1 -1
- package/dist/commands/handoff/peek.d.ts +5 -2
- package/dist/commands/handoff/peek.d.ts.map +1 -1
- package/dist/commands/handoff/peek.js +47 -6
- package/dist/commands/handoff/peek.js.map +1 -1
- package/dist/commands/handoff.d.ts.map +1 -1
- package/dist/commands/handoff.js +5 -0
- package/dist/commands/handoff.js.map +1 -1
- package/dist/commands/migrate.d.ts +23 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +104 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/note/list.d.ts +28 -0
- package/dist/commands/note/list.d.ts.map +1 -0
- package/dist/commands/note/list.js +72 -0
- package/dist/commands/note/list.js.map +1 -0
- package/dist/commands/note.d.ts.map +1 -1
- package/dist/commands/note.js +6 -1
- package/dist/commands/note.js.map +1 -1
- package/dist/commands/output.d.ts +27 -0
- package/dist/commands/output.d.ts.map +1 -0
- package/dist/commands/output.js +51 -0
- package/dist/commands/output.js.map +1 -0
- package/dist/commands/package-version-notice.d.ts +18 -0
- package/dist/commands/package-version-notice.d.ts.map +1 -0
- package/dist/commands/package-version-notice.js +39 -0
- package/dist/commands/package-version-notice.js.map +1 -0
- package/dist/commands/path/logs.d.ts +1 -1
- package/dist/commands/path/logs.d.ts.map +1 -1
- package/dist/commands/path/logs.js +6 -1
- package/dist/commands/path/logs.js.map +1 -1
- package/dist/commands/path/notes.d.ts +1 -1
- package/dist/commands/path/notes.d.ts.map +1 -1
- package/dist/commands/path/notes.js +6 -1
- package/dist/commands/path/notes.js.map +1 -1
- package/dist/commands/path/plans.d.ts +7 -0
- package/dist/commands/path/plans.d.ts.map +1 -0
- package/dist/commands/path/plans.js +22 -0
- package/dist/commands/path/plans.js.map +1 -0
- package/dist/commands/plan/list.d.ts +31 -0
- package/dist/commands/plan/list.d.ts.map +1 -0
- package/dist/commands/plan/list.js +75 -0
- package/dist/commands/plan/list.js.map +1 -0
- package/dist/commands/plan.d.ts +34 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +121 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/repo/init.d.ts.map +1 -1
- package/dist/commands/repo/init.js +12 -1
- package/dist/commands/repo/init.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +62 -18
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade-banner.d.ts +9 -0
- package/dist/commands/upgrade-banner.d.ts.map +1 -0
- package/dist/commands/upgrade-banner.js +27 -0
- package/dist/commands/upgrade-banner.js.map +1 -0
- package/dist/commands/vault/init.d.ts.map +1 -1
- package/dist/commands/vault/init.js +18 -29
- package/dist/commands/vault/init.js.map +1 -1
- package/dist/connector/repo.d.ts +2 -0
- package/dist/connector/repo.d.ts.map +1 -1
- package/dist/connector/repo.js +12 -1
- package/dist/connector/repo.js.map +1 -1
- package/dist/connector/state.d.ts +86 -0
- package/dist/connector/state.d.ts.map +1 -0
- package/dist/connector/state.js +171 -0
- package/dist/connector/state.js.map +1 -0
- package/dist/connector/unsupported-schema.d.ts +9 -0
- package/dist/connector/unsupported-schema.d.ts.map +1 -0
- package/dist/connector/unsupported-schema.js +14 -0
- package/dist/connector/unsupported-schema.js.map +1 -0
- package/dist/connector/vault.d.ts +2 -0
- package/dist/connector/vault.d.ts.map +1 -1
- package/dist/connector/vault.js +6 -1
- package/dist/connector/vault.js.map +1 -1
- package/dist/help.d.ts +45 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +368 -0
- package/dist/help.js.map +1 -0
- package/dist/util/fs.d.ts +6 -0
- package/dist/util/fs.d.ts.map +1 -1
- package/dist/util/fs.js +15 -1
- package/dist/util/fs.js.map +1 -1
- package/dist/util/hash.d.ts +3 -0
- package/dist/util/hash.d.ts.map +1 -0
- package/dist/util/hash.js +6 -0
- package/dist/util/hash.js.map +1 -0
- package/dist/util/parse-args.d.ts.map +1 -1
- package/dist/util/parse-args.js +11 -2
- package/dist/util/parse-args.js.map +1 -1
- package/dist/util/path.d.ts +10 -0
- package/dist/util/path.d.ts.map +1 -1
- package/dist/util/path.js +31 -0
- package/dist/util/path.js.map +1 -1
- package/dist/util/plan-name.d.ts +10 -0
- package/dist/util/plan-name.d.ts.map +1 -0
- package/dist/util/plan-name.js +18 -0
- package/dist/util/plan-name.js.map +1 -0
- package/dist/util/semver.d.ts +10 -0
- package/dist/util/semver.d.ts.map +1 -0
- package/dist/util/semver.js +40 -0
- package/dist/util/semver.js.map +1 -0
- package/dist/vault/layout.d.ts +2 -0
- package/dist/vault/layout.d.ts.map +1 -1
- package/dist/vault/layout.js +4 -0
- package/dist/vault/layout.js.map +1 -1
- package/dist/vault/list-notes.d.ts +15 -0
- package/dist/vault/list-notes.d.ts.map +1 -0
- package/dist/vault/list-notes.js +44 -0
- package/dist/vault/list-notes.js.map +1 -0
- package/dist/vault/list-plans.d.ts +11 -0
- package/dist/vault/list-plans.d.ts.map +1 -0
- package/dist/vault/list-plans.js +40 -0
- package/dist/vault/list-plans.js.map +1 -0
- package/dist/vault/write-plan.d.ts +28 -0
- package/dist/vault/write-plan.d.ts.map +1 -0
- package/dist/vault/write-plan.js +61 -0
- package/dist/vault/write-plan.js.map +1 -0
- package/package.json +5 -3
- package/templates/agents/claude/commands/grounder-note.md +25 -3
- package/templates/agents/claude/commands/grounder-plan.md +69 -0
- package/templates/agents/claude/commands/grounder-task-handoff.md +3 -3
- package/templates/agents/claude/commands/grounder-task.md +5 -3
- package/templates/agents/cursor/commands/grounder-note.md +26 -4
- package/templates/agents/cursor/commands/grounder-plan.md +69 -0
- package/templates/agents/cursor/commands/grounder-task-handoff.md +4 -4
- package/templates/agents/cursor/commands/grounder-task.md +6 -4
- package/templates/vault/plan.md +16 -0
package/dist/util/path.d.ts
CHANGED
|
@@ -7,4 +7,14 @@ export declare function expandHome(input: string): string;
|
|
|
7
7
|
* Expand a leading `~`, then resolve to an absolute path (relative to `cwd` if given).
|
|
8
8
|
*/
|
|
9
9
|
export declare function resolveUserPath(input: string, cwd?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* True when `child` is inside `parent` (or equal), using lexical `path.resolve` only
|
|
12
|
+
* (does not follow symlinks). Rejects `..` escape and cross-root relatives.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isPathInside(parent: string, child: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* True when `child`'s real path is strictly inside `parent` (symlinks followed).
|
|
17
|
+
* Returns `null` if either path cannot be resolved (e.g. missing).
|
|
18
|
+
*/
|
|
19
|
+
export declare function isRealPathInside(parent: string, child: string): Promise<boolean | null>;
|
|
10
20
|
//# sourceMappingURL=path.d.ts.map
|
package/dist/util/path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/util/path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/util/path.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAsB,GAAG,MAAM,CAElF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAanE;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAQ7F"}
|
package/dist/util/path.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
1
2
|
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
/**
|
|
@@ -19,4 +20,34 @@ export function expandHome(input) {
|
|
|
19
20
|
export function resolveUserPath(input, cwd = process.cwd()) {
|
|
20
21
|
return path.resolve(cwd, expandHome(input));
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* True when `child` is inside `parent` (or equal), using lexical `path.resolve` only
|
|
25
|
+
* (does not follow symlinks). Rejects `..` escape and cross-root relatives.
|
|
26
|
+
*/
|
|
27
|
+
export function isPathInside(parent, child) {
|
|
28
|
+
const root = path.resolve(parent);
|
|
29
|
+
const target = path.resolve(child);
|
|
30
|
+
if (root === target) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const relative = path.relative(root, target);
|
|
34
|
+
return (relative !== "" &&
|
|
35
|
+
relative !== ".." &&
|
|
36
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
37
|
+
!path.isAbsolute(relative));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* True when `child`'s real path is strictly inside `parent` (symlinks followed).
|
|
41
|
+
* Returns `null` if either path cannot be resolved (e.g. missing).
|
|
42
|
+
*/
|
|
43
|
+
export async function isRealPathInside(parent, child) {
|
|
44
|
+
try {
|
|
45
|
+
const root = await realpath(parent);
|
|
46
|
+
const target = await realpath(child);
|
|
47
|
+
return root !== target && isPathInside(root, target);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
22
53
|
//# sourceMappingURL=path.js.map
|
package/dist/util/path.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/util/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QAClB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IACxE,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9C,CAAC"}
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/util/path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QAClB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;QAC/D,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE;IACxE,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,KAAa;IACxD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC7C,OAAO,CACL,QAAQ,KAAK,EAAE;QACf,QAAQ,KAAK,IAAI;QACjB,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;QACrC,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAC3B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,MAAc,EAAE,KAAa;IAClE,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,IAAI,KAAK,MAAM,IAAI,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Generous cap for plan filenames — real plan names often exceed note/handoff's 20-char slug. */
|
|
2
|
+
export declare const MAX_PLAN_NAME_LENGTH = 80;
|
|
3
|
+
/**
|
|
4
|
+
* Sanitize a `--title` value into a filename-safe plan name.
|
|
5
|
+
* Trims, strips a trailing `.md` (case-insensitive), applies {@link sanitizeProjectId}
|
|
6
|
+
* character rules, and caps at {@link MAX_PLAN_NAME_LENGTH}.
|
|
7
|
+
* Returns `""` when the result is empty / unusable.
|
|
8
|
+
*/
|
|
9
|
+
export declare function sanitizePlanName(rawTitle: string): string;
|
|
10
|
+
//# sourceMappingURL=plan-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-name.d.ts","sourceRoot":"","sources":["../../src/util/plan-name.ts"],"names":[],"mappings":"AAEA,kGAAkG;AAClG,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOzD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { sanitizeProjectId } from "./project-id.js";
|
|
2
|
+
/** Generous cap for plan filenames — real plan names often exceed note/handoff's 20-char slug. */
|
|
3
|
+
export const MAX_PLAN_NAME_LENGTH = 80;
|
|
4
|
+
/**
|
|
5
|
+
* Sanitize a `--title` value into a filename-safe plan name.
|
|
6
|
+
* Trims, strips a trailing `.md` (case-insensitive), applies {@link sanitizeProjectId}
|
|
7
|
+
* character rules, and caps at {@link MAX_PLAN_NAME_LENGTH}.
|
|
8
|
+
* Returns `""` when the result is empty / unusable.
|
|
9
|
+
*/
|
|
10
|
+
export function sanitizePlanName(rawTitle) {
|
|
11
|
+
const trimmed = rawTitle.trim().replace(/\.md$/i, "");
|
|
12
|
+
const sanitized = sanitizeProjectId(trimmed);
|
|
13
|
+
if (!sanitized) {
|
|
14
|
+
return "";
|
|
15
|
+
}
|
|
16
|
+
return sanitized.slice(0, MAX_PLAN_NAME_LENGTH).replace(/-+$/g, "");
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=plan-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-name.js","sourceRoot":"","sources":["../../src/util/plan-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEpD,kGAAkG;AAClG,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare npm-style `major.minor.patch` prefixes.
|
|
3
|
+
* Returns negative / zero / positive like `strcmp`, or `null` if either side
|
|
4
|
+
* lacks a leading `x.y.z` (prerelease/build suffixes after the triple are ignored).
|
|
5
|
+
*/
|
|
6
|
+
export declare function compareSemver(a: string, b: string): number | null;
|
|
7
|
+
/** How the running package version relates to the last-recorded ledger version. */
|
|
8
|
+
export type PackageVersionRelation = "match" | "ahead" | "behind" | "differs";
|
|
9
|
+
export declare function packageVersionRelation(running: string, recorded: string): PackageVersionRelation;
|
|
10
|
+
//# sourceMappingURL=semver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semver.d.ts","sourceRoot":"","sources":["../../src/util/semver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBjE;AAED,mFAAmF;AACnF,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,sBAAsB,CAUhG"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare npm-style `major.minor.patch` prefixes.
|
|
3
|
+
* Returns negative / zero / positive like `strcmp`, or `null` if either side
|
|
4
|
+
* lacks a leading `x.y.z` (prerelease/build suffixes after the triple are ignored).
|
|
5
|
+
*/
|
|
6
|
+
export function compareSemver(a, b) {
|
|
7
|
+
const parse = (v) => {
|
|
8
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
|
|
9
|
+
if (!match) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
13
|
+
};
|
|
14
|
+
const left = parse(a);
|
|
15
|
+
const right = parse(b);
|
|
16
|
+
if (!left || !right) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
for (let i = 0; i < 3; i++) {
|
|
20
|
+
if (left[i] < right[i]) {
|
|
21
|
+
return -1;
|
|
22
|
+
}
|
|
23
|
+
if (left[i] > right[i]) {
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
export function packageVersionRelation(running, recorded) {
|
|
30
|
+
if (running === recorded) {
|
|
31
|
+
return "match";
|
|
32
|
+
}
|
|
33
|
+
const cmp = compareSemver(running, recorded);
|
|
34
|
+
if (cmp === null || cmp === 0) {
|
|
35
|
+
// Unparseable, or same x.y.z with different suffix/string form.
|
|
36
|
+
return "differs";
|
|
37
|
+
}
|
|
38
|
+
return cmp > 0 ? "ahead" : "behind";
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=semver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semver.js","sourceRoot":"","sources":["../../src/util/semver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,CAAS;IAChD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAmC,EAAE;QAC3D,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,CAAC,CAAC;QACZ,CAAC;QACD,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAKD,MAAM,UAAU,sBAAsB,CAAC,OAAe,EAAE,QAAgB;IACtE,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;QAC9B,gEAAgE;QAChE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACtC,CAAC"}
|
package/dist/vault/layout.d.ts
CHANGED
|
@@ -10,4 +10,6 @@ export declare function projectDir(vaultRoot: string, projectId: string): string
|
|
|
10
10
|
export declare function notesDir(vaultRoot: string, projectId: string): string;
|
|
11
11
|
/** `<vault>/10-Projects/{projectId}/logs` — session handoffs. */
|
|
12
12
|
export declare function logsDir(vaultRoot: string, projectId: string): string;
|
|
13
|
+
/** `<vault>/10-Projects/{projectId}/plans` — named, updatable plan docs. */
|
|
14
|
+
export declare function plansDir(vaultRoot: string, projectId: string): string;
|
|
13
15
|
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/vault/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAE1C,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,+CAA+C;AAC/C,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,iEAAiE;AACjE,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpE"}
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/vault/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAE1C,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED,+CAA+C;AAC/C,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,iEAAiE;AACjE,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAErE"}
|
package/dist/vault/layout.js
CHANGED
|
@@ -19,4 +19,8 @@ export function notesDir(vaultRoot, projectId) {
|
|
|
19
19
|
export function logsDir(vaultRoot, projectId) {
|
|
20
20
|
return path.join(vaultRoot, PROJECTS_DIR, projectId, "logs");
|
|
21
21
|
}
|
|
22
|
+
/** `<vault>/10-Projects/{projectId}/plans` — named, updatable plan docs. */
|
|
23
|
+
export function plansDir(vaultRoot, projectId) {
|
|
24
|
+
return path.join(vaultRoot, PROJECTS_DIR, projectId, "plans");
|
|
25
|
+
}
|
|
22
26
|
//# sourceMappingURL=layout.js.map
|
package/dist/vault/layout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/vault/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAE1C,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAC5C,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,SAAiB;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AACvD,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,QAAQ,CAAC,SAAiB,EAAE,SAAiB;IAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,OAAO,CAAC,SAAiB,EAAE,SAAiB;IAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAC/D,CAAC"}
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/vault/layout.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAE1C,6DAA6D;AAC7D,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAC5C,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,SAAiB;IAC7D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AACvD,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,QAAQ,CAAC,SAAiB,EAAE,SAAiB;IAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,OAAO,CAAC,SAAiB,EAAE,SAAiB;IAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAC/D,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,QAAQ,CAAC,SAAiB,EAAE,SAAiB;IAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ListNotesOptions {
|
|
2
|
+
/** Max paths to return (newest first). Omit to return all. */
|
|
3
|
+
limit?: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Lists note markdown files under `notesDir`, newest mtime first.
|
|
7
|
+
* Returns absolute paths. Missing or empty dirs yield `[]`.
|
|
8
|
+
* Ties break by filename descending for stable output.
|
|
9
|
+
*
|
|
10
|
+
* Filename-descending alone is a viable alternative for notes (timestamp
|
|
11
|
+
* prefixes, like `listHandoffs`), but this intentionally matches `listPlans`
|
|
12
|
+
* for consistency across list commands.
|
|
13
|
+
*/
|
|
14
|
+
export declare function listNotes(notesDir: string, options?: ListNotesOptions): Promise<string[]>;
|
|
15
|
+
//# sourceMappingURL=list-notes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-notes.d.ts","sourceRoot":"","sources":["../../src/vault/list-notes.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CAoCnB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Lists note markdown files under `notesDir`, newest mtime first.
|
|
5
|
+
* Returns absolute paths. Missing or empty dirs yield `[]`.
|
|
6
|
+
* Ties break by filename descending for stable output.
|
|
7
|
+
*
|
|
8
|
+
* Filename-descending alone is a viable alternative for notes (timestamp
|
|
9
|
+
* prefixes, like `listHandoffs`), but this intentionally matches `listPlans`
|
|
10
|
+
* for consistency across list commands.
|
|
11
|
+
*/
|
|
12
|
+
export async function listNotes(notesDir, options = {}) {
|
|
13
|
+
let entries;
|
|
14
|
+
try {
|
|
15
|
+
entries = await readdir(notesDir);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error.code === "ENOENT") {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
const mdNames = entries.filter((name) => name.endsWith(".md"));
|
|
24
|
+
const withMtime = await Promise.all(mdNames.map(async (name) => {
|
|
25
|
+
const filePath = path.join(notesDir, name);
|
|
26
|
+
const { mtimeMs } = await stat(filePath);
|
|
27
|
+
return { filePath, name, mtimeMs };
|
|
28
|
+
}));
|
|
29
|
+
withMtime.sort((a, b) => {
|
|
30
|
+
if (a.mtimeMs !== b.mtimeMs) {
|
|
31
|
+
return b.mtimeMs - a.mtimeMs;
|
|
32
|
+
}
|
|
33
|
+
return a.name < b.name ? 1 : a.name > b.name ? -1 : 0;
|
|
34
|
+
});
|
|
35
|
+
const paths = withMtime.map((entry) => entry.filePath);
|
|
36
|
+
if (options.limit === undefined) {
|
|
37
|
+
return paths;
|
|
38
|
+
}
|
|
39
|
+
if (options.limit <= 0) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return paths.slice(0, options.limit);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=list-notes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-notes.js","sourceRoot":"","sources":["../../src/vault/list-notes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,IAAI,MAAM,WAAW,CAAC;AAO7B;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,UAA4B,EAAE;IAE9B,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ListPlansOptions {
|
|
2
|
+
/** Max paths to return (newest first). Omit to return all. */
|
|
3
|
+
limit?: number;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Lists plan markdown files under `plansDir`, newest mtime first.
|
|
7
|
+
* Returns absolute paths. Missing or empty dirs yield `[]`.
|
|
8
|
+
* Ties break by filename descending for stable output.
|
|
9
|
+
*/
|
|
10
|
+
export declare function listPlans(plansDir: string, options?: ListPlansOptions): Promise<string[]>;
|
|
11
|
+
//# sourceMappingURL=list-plans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-plans.d.ts","sourceRoot":"","sources":["../../src/vault/list-plans.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CAoCnB"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Lists plan markdown files under `plansDir`, newest mtime first.
|
|
5
|
+
* Returns absolute paths. Missing or empty dirs yield `[]`.
|
|
6
|
+
* Ties break by filename descending for stable output.
|
|
7
|
+
*/
|
|
8
|
+
export async function listPlans(plansDir, options = {}) {
|
|
9
|
+
let entries;
|
|
10
|
+
try {
|
|
11
|
+
entries = await readdir(plansDir);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (error.code === "ENOENT") {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
const mdNames = entries.filter((name) => name.endsWith(".md"));
|
|
20
|
+
const withMtime = await Promise.all(mdNames.map(async (name) => {
|
|
21
|
+
const filePath = path.join(plansDir, name);
|
|
22
|
+
const { mtimeMs } = await stat(filePath);
|
|
23
|
+
return { filePath, name, mtimeMs };
|
|
24
|
+
}));
|
|
25
|
+
withMtime.sort((a, b) => {
|
|
26
|
+
if (a.mtimeMs !== b.mtimeMs) {
|
|
27
|
+
return b.mtimeMs - a.mtimeMs;
|
|
28
|
+
}
|
|
29
|
+
return a.name < b.name ? 1 : a.name > b.name ? -1 : 0;
|
|
30
|
+
});
|
|
31
|
+
const paths = withMtime.map((entry) => entry.filePath);
|
|
32
|
+
if (options.limit === undefined) {
|
|
33
|
+
return paths;
|
|
34
|
+
}
|
|
35
|
+
if (options.limit <= 0) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
return paths.slice(0, options.limit);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=list-plans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-plans.js","sourceRoot":"","sources":["../../src/vault/list-plans.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,IAAI,MAAM,WAAW,CAAC;AAO7B;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,QAAgB,EAChB,UAA4B,EAAE;IAE9B,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CACjC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACrC,CAAC,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACtB,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;QAC/B,CAAC;QACD,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEvD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Options for {@link writePlan}. */
|
|
2
|
+
export interface WritePlanOptions {
|
|
3
|
+
/** Project id written into YAML frontmatter. */
|
|
4
|
+
projectId: string;
|
|
5
|
+
/** When true, overwrite an existing plan (preserving original `created`). */
|
|
6
|
+
force?: boolean;
|
|
7
|
+
/** Timestamp for `created` / `updated` (default: now). */
|
|
8
|
+
now?: Date;
|
|
9
|
+
}
|
|
10
|
+
export type WritePlanStatus = "created" | "overwritten" | "exists";
|
|
11
|
+
export interface WritePlanResult {
|
|
12
|
+
path: string;
|
|
13
|
+
status: WritePlanStatus;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Overwrites an existing plan at an absolute path.
|
|
17
|
+
* Preserves original `created` (falls back to `now` if missing) and sets `updated`.
|
|
18
|
+
* Caller must validate the path (e.g. inside this project's `plans/` dir).
|
|
19
|
+
*/
|
|
20
|
+
export declare function updatePlanAtPath(filePath: string, body: string, options: Omit<WritePlanOptions, "force">): Promise<WritePlanResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Writes or updates a named plan markdown file under `plansDir` (created if missing).
|
|
23
|
+
* Target is always `plansDir/<name>.md` — no collision suffixes.
|
|
24
|
+
* Without `force`, an existing file is left untouched and status `"exists"` is returned.
|
|
25
|
+
* With `force`, overwrites the body, preserves original `created`, and sets `updated`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function writePlan(plansDir: string, name: string, body: string, options: WritePlanOptions): Promise<WritePlanResult>;
|
|
28
|
+
//# sourceMappingURL=write-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-plan.d.ts","sourceRoot":"","sources":["../../src/vault/write-plan.ts"],"names":[],"mappings":"AAMA,qCAAqC;AACrC,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;CACzB;AAmBD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GACvC,OAAO,CAAC,eAAe,CAAC,CAgB1B;AAED;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAuB1B"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseHandoffFrontmatter } from "../util/frontmatter.js";
|
|
4
|
+
import { fileExists } from "../util/fs.js";
|
|
5
|
+
import { yamlDoubleQuoted } from "../util/yaml.js";
|
|
6
|
+
function buildFrontmatter(options) {
|
|
7
|
+
const lines = [
|
|
8
|
+
"---",
|
|
9
|
+
`project: ${yamlDoubleQuoted(options.projectId)}`,
|
|
10
|
+
`created: ${yamlDoubleQuoted(options.created)}`,
|
|
11
|
+
];
|
|
12
|
+
if (options.updated) {
|
|
13
|
+
lines.push(`updated: ${yamlDoubleQuoted(options.updated)}`);
|
|
14
|
+
}
|
|
15
|
+
lines.push("---");
|
|
16
|
+
return `${lines.join("\n")}\n\n`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Overwrites an existing plan at an absolute path.
|
|
20
|
+
* Preserves original `created` (falls back to `now` if missing) and sets `updated`.
|
|
21
|
+
* Caller must validate the path (e.g. inside this project's `plans/` dir).
|
|
22
|
+
*/
|
|
23
|
+
export async function updatePlanAtPath(filePath, body, options) {
|
|
24
|
+
const now = options.now ?? new Date();
|
|
25
|
+
const existing = await readFile(filePath, "utf8");
|
|
26
|
+
const fm = parseHandoffFrontmatter(existing);
|
|
27
|
+
const created = fm.created ?? now.toISOString();
|
|
28
|
+
const updated = now.toISOString();
|
|
29
|
+
const content = buildFrontmatter({
|
|
30
|
+
projectId: options.projectId,
|
|
31
|
+
created,
|
|
32
|
+
updated,
|
|
33
|
+
}) + body;
|
|
34
|
+
await writeFile(filePath, content, "utf8");
|
|
35
|
+
return { path: filePath, status: "overwritten" };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Writes or updates a named plan markdown file under `plansDir` (created if missing).
|
|
39
|
+
* Target is always `plansDir/<name>.md` — no collision suffixes.
|
|
40
|
+
* Without `force`, an existing file is left untouched and status `"exists"` is returned.
|
|
41
|
+
* With `force`, overwrites the body, preserves original `created`, and sets `updated`.
|
|
42
|
+
*/
|
|
43
|
+
export async function writePlan(plansDir, name, body, options) {
|
|
44
|
+
const now = options.now ?? new Date();
|
|
45
|
+
await mkdir(plansDir, { recursive: true });
|
|
46
|
+
const filePath = path.join(plansDir, `${name}.md`);
|
|
47
|
+
const exists = await fileExists(filePath);
|
|
48
|
+
if (exists && !options.force) {
|
|
49
|
+
return { path: filePath, status: "exists" };
|
|
50
|
+
}
|
|
51
|
+
if (exists && options.force) {
|
|
52
|
+
return updatePlanAtPath(filePath, body, { projectId: options.projectId, now });
|
|
53
|
+
}
|
|
54
|
+
const content = buildFrontmatter({
|
|
55
|
+
projectId: options.projectId,
|
|
56
|
+
created: now.toISOString(),
|
|
57
|
+
}) + body;
|
|
58
|
+
await writeFile(filePath, content, "utf8");
|
|
59
|
+
return { path: filePath, status: "created" };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=write-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-plan.js","sourceRoot":"","sources":["../../src/vault/write-plan.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grounder",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Markdown-native memory for Cursor and Claude Code — session handoffs and plans in a vault you own",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"grounder": "./dist/cli.js"
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"keywords": [
|
|
17
17
|
"obsidian",
|
|
18
18
|
"cursor",
|
|
19
|
-
"
|
|
19
|
+
"claude-code",
|
|
20
|
+
"handoff",
|
|
21
|
+
"developer-tools",
|
|
20
22
|
"ai",
|
|
21
23
|
"cli"
|
|
22
24
|
],
|
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
Save a note to the Obsidian vault for this project.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Distill the instruction after `/grounder-note` into a clean note body — not a chat transcript.
|
|
4
|
+
Do not dump tool traces, full conversation, or false starts.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**Special case: the instruction asks to view existing notes, not write one** (`list`, `list 3 oldest`, `show notes`, etc.) → run `{{GROUNDER_CLI}} note list --limit <N>` (N = count named, else 5; ignore order words like "oldest" — output is always newest-first, never resort or relabel it) and stop — no note write. Relay the CLI stdout as-is (it already includes the count header).
|
|
7
|
+
|
|
8
|
+
Rules:
|
|
9
|
+
- Default → distill the args into a clean note body
|
|
10
|
+
- Exact wording marked to keep (quoted, "save exactly:"/"verbatim:", or a fenced block) → use that verbatim instead, unmodified
|
|
11
|
+
- Empty args → distill the central point of the current thread instead
|
|
12
|
+
|
|
13
|
+
Then run from the linked project folder or any subdirectory beneath it:
|
|
14
|
+
|
|
15
|
+
{{GROUNDER_CLI}} note "<body>"
|
|
16
|
+
|
|
17
|
+
Optional short title slug (filename):
|
|
18
|
+
|
|
19
|
+
{{GROUNDER_CLI}} note --title <slug> "<body>"
|
|
20
|
+
|
|
21
|
+
For multi-line bodies, prefer a shell heredoc so quoting does not break:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
{{GROUNDER_CLI}} note "$(cat <<'EOF'
|
|
25
|
+
…
|
|
26
|
+
EOF
|
|
27
|
+
)"
|
|
28
|
+
```
|
|
6
29
|
|
|
7
30
|
The vault is outside the workspace — grant shell permissions if Claude Code prompts you.
|
|
8
31
|
|
|
9
|
-
Use the text after `/grounder-note` as the note body.
|
|
10
32
|
Do not compute vault paths or write files yourself — the CLI handles it.
|
|
11
33
|
Report the CLI output path from stdout to the user.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Write a named, updatable plan document to the Obsidian vault for this project.
|
|
2
|
+
|
|
3
|
+
Distill the instruction after `/grounder-plan` into a structured plan — not a chat transcript.
|
|
4
|
+
Do not dump tool traces, full conversation, or false starts.
|
|
5
|
+
|
|
6
|
+
Build a markdown body with these sections:
|
|
7
|
+
|
|
8
|
+
```markdown
|
|
9
|
+
# Plan: <title>
|
|
10
|
+
|
|
11
|
+
## Goal
|
|
12
|
+
…
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
1. …
|
|
16
|
+
2. …
|
|
17
|
+
|
|
18
|
+
## Decisions / open questions
|
|
19
|
+
- …
|
|
20
|
+
|
|
21
|
+
## Status
|
|
22
|
+
…
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Special case: the instruction asks to view existing plans, not name a new topic** (`list`, `list 3 oldest`, `show plans`, etc.) → run `{{GROUNDER_CLI}} plan list --limit <N>` (N = count named, else 5; ignore order words like "oldest" — output is always newest-first, never resort or relabel it) and stop — no plan write, no title. Relay the CLI stdout as-is (it already includes the count header).
|
|
26
|
+
|
|
27
|
+
Otherwise, resolve the target, then **state it plainly before writing** — `Updating plan at <path>.` or `Creating new plan titled <title>.` This is a visible record, not a blocking confirmation — updates overwrite with no `--force`, so get the match right.
|
|
28
|
+
|
|
29
|
+
**1. Known path** (attached/open in chat, or printed by an earlier `grounder plan` this conversation) → update it directly.
|
|
30
|
+
|
|
31
|
+
**2. No path, but update intent** (e.g. "update/continue/revise the plan", or a name that sounds like an existing one) → look it up first:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
{{GROUNDER_CLI}} plan list --limit 5
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
CLI output starts with a count header, then each result as a numbered two-line block — `N. ` + title (filename stem) on the first line, the absolute path indented beneath it.
|
|
38
|
+
|
|
39
|
+
A match counts only if its title actually corresponds to what the user named — not just "it's the only plan in the project." No name given and exactly one plan exists → that counts too. If the user refers to a plan by the number shown in *this* listing (e.g. "update plan 2"), that counts as a match too — resolve it to the path from this same output, don't reuse a number from an earlier listing in the conversation (it's positional, not a stable id, and can shift if plans changed since). Otherwise (no match, several matches, or a name/number that doesn't correspond to any existing plan) → ask; never guess.
|
|
40
|
+
|
|
41
|
+
Cases 1 and 2 (update) — run:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
{{GROUNDER_CLI}} plan "$(cat <<'EOF'
|
|
45
|
+
# Plan: …
|
|
46
|
+
…
|
|
47
|
+
EOF
|
|
48
|
+
)" --path <path>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`--path` must resolve under this project's `plans/` dir; it always overwrites (no `--force`).
|
|
52
|
+
|
|
53
|
+
**3. No path, no update intent → genuinely new plan.** Derive a `--title` (the user's explicit name, else a short kebab-case slug from the plan's title/goal) and write immediately — don't ask about the name itself.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
{{GROUNDER_CLI}} plan "$(cat <<'EOF'
|
|
57
|
+
# Plan: …
|
|
58
|
+
…
|
|
59
|
+
EOF
|
|
60
|
+
)" --title <name>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If `--title` collides with an existing plan (non-zero exit; stderr names the conflict), ask: overwrite (`--force`) or a different name. `--force` only resolves that collision — **never** use it to update a plan you meant to target with `--path`.
|
|
64
|
+
|
|
65
|
+
Run from the linked project folder or any subdirectory beneath it.
|
|
66
|
+
The vault is outside the workspace — grant shell permissions if Claude Code prompts you.
|
|
67
|
+
|
|
68
|
+
Do not compute vault paths or write files yourself — the CLI handles it.
|
|
69
|
+
Report the exact path the CLI prints on stdout — it confirms the real outcome, not just the intent.
|
|
@@ -32,16 +32,16 @@ Rules:
|
|
|
32
32
|
|
|
33
33
|
Then run from the linked project folder or any subdirectory beneath it:
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
{{GROUNDER_CLI}} handoff "<body>"
|
|
36
36
|
|
|
37
37
|
Optional short title slug (filename + frontmatter):
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
{{GROUNDER_CLI}} handoff --title <slug> "<body>"
|
|
40
40
|
|
|
41
41
|
For multi-line bodies, prefer a shell heredoc so quoting does not break:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
|
|
44
|
+
{{GROUNDER_CLI}} handoff "$(cat <<'EOF'
|
|
45
45
|
# Handoff: …
|
|
46
46
|
…
|
|
47
47
|
EOF
|
|
@@ -2,15 +2,17 @@ Hydrate this session from the latest vault handoff and repo truth.
|
|
|
2
2
|
|
|
3
3
|
Read-only — do not write to the vault. Do not invent vault paths.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Special case: the instruction asks to view existing handoffs, not hydrate** (`list`, `list 3 oldest`, `show handoffs`, etc.) → run `{{GROUNDER_CLI}} handoff list --limit <N>` (N = count named, else 5; ignore order words like "oldest" — output is always newest-first, never resort or relabel it) and stop — no hydrate, no `AGENTS.md`, no “start work.” Relay the CLI stdout as-is (it already includes the count header).
|
|
6
|
+
|
|
7
|
+
Otherwise, from the linked project folder or any subdirectory beneath it:
|
|
6
8
|
|
|
7
9
|
1. Get the current handoff (skips empty/unreadable files, same pick as the session-start teaser):
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
{{GROUNDER_CLI}} handoff list --head
|
|
10
12
|
|
|
11
13
|
2. If empty: tell the user there are no handoffs yet, then read repo `AGENTS.md` only and proceed.
|
|
12
14
|
|
|
13
|
-
3. Otherwise, read that file. If the user names a specific session instead
|
|
15
|
+
3. Otherwise, read that file. If the user names a specific session instead: `{{GROUNDER_CLI}} handoff list --limit 5` → match name/index to the indented path in *this* listing (positional, not a stable id). Miss → once with `--limit 50` (*that* listing only). Still miss → tell the user and stop — no guessed hydrate.
|
|
14
16
|
|
|
15
17
|
4. Read repo `AGENTS.md` (project conventions and constraints).
|
|
16
18
|
|