grounder 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +115 -28
- package/dist/agents/claude.d.ts +15 -0
- package/dist/agents/claude.d.ts.map +1 -1
- package/dist/agents/claude.js +256 -11
- package/dist/agents/claude.js.map +1 -1
- package/dist/agents/cursor-hook-input.d.ts +14 -0
- package/dist/agents/cursor-hook-input.d.ts.map +1 -0
- package/dist/agents/cursor-hook-input.js +80 -0
- package/dist/agents/cursor-hook-input.js.map +1 -0
- package/dist/agents/cursor.d.ts +11 -0
- package/dist/agents/cursor.d.ts.map +1 -1
- package/dist/agents/cursor.js +206 -11
- package/dist/agents/cursor.js.map +1 -1
- package/dist/agents/hook-runtime.d.ts +152 -0
- package/dist/agents/hook-runtime.d.ts.map +1 -0
- package/dist/agents/hook-runtime.js +401 -0
- package/dist/agents/hook-runtime.js.map +1 -0
- 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 +37 -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 +23 -1
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/cli.js +62 -53
- 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 +231 -14
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/handoff/list.d.ts +9 -0
- package/dist/commands/handoff/list.d.ts.map +1 -1
- package/dist/commands/handoff/list.js +20 -4
- package/dist/commands/handoff/list.js.map +1 -1
- package/dist/commands/handoff/peek.d.ts +41 -0
- package/dist/commands/handoff/peek.d.ts.map +1 -0
- package/dist/commands/handoff/peek.js +161 -0
- package/dist/commands/handoff/peek.js.map +1 -0
- 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.d.ts.map +1 -1
- package/dist/commands/note.js +5 -0
- package/dist/commands/note.js.map +1 -1
- 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 +23 -0
- package/dist/commands/plan/list.d.ts.map +1 -0
- package/dist/commands/plan/list.js +69 -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 +52 -9
- 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 +2 -0
- package/dist/commands/vault/init.d.ts.map +1 -1
- package/dist/commands/vault/init.js +28 -11
- 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 +95 -0
- package/dist/connector/state.d.ts.map +1 -0
- package/dist/connector/state.js +189 -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 +355 -0
- package/dist/help.js.map +1 -0
- package/dist/util/frontmatter.d.ts +11 -0
- package/dist/util/frontmatter.d.ts.map +1 -0
- package/dist/util/frontmatter.js +69 -0
- package/dist/util/frontmatter.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/merge-json.d.ts +15 -0
- package/dist/util/merge-json.d.ts.map +1 -0
- package/dist/util/merge-json.js +40 -0
- package/dist/util/merge-json.js.map +1 -0
- 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/find-usable-handoff.d.ts +19 -0
- package/dist/vault/find-usable-handoff.d.ts.map +1 -0
- package/dist/vault/find-usable-handoff.js +28 -0
- package/dist/vault/find-usable-handoff.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-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 +1 -1
- package/templates/agents/claude/commands/grounder-note.md +1 -1
- package/templates/agents/claude/commands/grounder-plan.md +65 -0
- package/templates/agents/claude/commands/grounder-task-handoff.md +3 -3
- package/templates/agents/claude/commands/grounder-task.md +4 -4
- package/templates/agents/cursor/commands/grounder-note.md +1 -1
- package/templates/agents/cursor/commands/grounder-plan.md +65 -0
- package/templates/agents/cursor/commands/grounder-task-handoff.md +3 -3
- package/templates/agents/cursor/commands/grounder-task.md +4 -4
- package/templates/vault/plan.md +16 -0
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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface FindUsableHandoffOptions {
|
|
2
|
+
/** Max newest-first candidates to scan (default: 5). */
|
|
3
|
+
limit?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface UsableHandoff {
|
|
6
|
+
/** Absolute path of the resolved handoff file. */
|
|
7
|
+
path: string;
|
|
8
|
+
/** Already-read file contents (avoids a second read by callers). */
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolves the newest-first handoff under `logsDir` that actually has content,
|
|
13
|
+
* skipping empty or unreadable files along the way (e.g. an interrupted write).
|
|
14
|
+
* Single source of truth for "which handoff is current" — shared by
|
|
15
|
+
* `grounder handoff peek` and `grounder handoff list --head` so both agree.
|
|
16
|
+
* Returns `undefined` when no candidate within the scan window is usable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function findUsableHandoff(logsDir: string, options?: FindUsableHandoffOptions): Promise<UsableHandoff | undefined>;
|
|
19
|
+
//# sourceMappingURL=find-usable-handoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-usable-handoff.d.ts","sourceRoot":"","sources":["../../src/vault/find-usable-handoff.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,wBAAwB;IACvC,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAcpC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { listHandoffs } from "./list-handoffs.js";
|
|
3
|
+
/** Newest-first candidates scanned for a usable handoff before giving up. */
|
|
4
|
+
const DEFAULT_SCAN_LIMIT = 5;
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the newest-first handoff under `logsDir` that actually has content,
|
|
7
|
+
* skipping empty or unreadable files along the way (e.g. an interrupted write).
|
|
8
|
+
* Single source of truth for "which handoff is current" — shared by
|
|
9
|
+
* `grounder handoff peek` and `grounder handoff list --head` so both agree.
|
|
10
|
+
* Returns `undefined` when no candidate within the scan window is usable.
|
|
11
|
+
*/
|
|
12
|
+
export async function findUsableHandoff(logsDir, options = {}) {
|
|
13
|
+
const paths = await listHandoffs(logsDir, { limit: options.limit ?? DEFAULT_SCAN_LIMIT });
|
|
14
|
+
for (const filePath of paths) {
|
|
15
|
+
let content;
|
|
16
|
+
try {
|
|
17
|
+
content = await readFile(filePath, "utf8");
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (content.trim().length > 0) {
|
|
23
|
+
return { path: filePath, content };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=find-usable-handoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-usable-handoff.js","sourceRoot":"","sources":["../../src/vault/find-usable-handoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,6EAA6E;AAC7E,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAc7B;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe,EACf,UAAoC,EAAE;IAEtC,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,kBAAkB,EAAE,CAAC,CAAC;IAC1F,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QACrC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,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,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
|
@@ -2,7 +2,7 @@ Save a note to the Obsidian vault for this project.
|
|
|
2
2
|
|
|
3
3
|
Run from the linked project folder or any subdirectory beneath it:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
{{GROUNDER_CLI}} note "<user text>"
|
|
6
6
|
|
|
7
7
|
The vault is outside the workspace — grant shell permissions if Claude Code prompts you.
|
|
8
8
|
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
Update vs create — never guess a `--title` for an update:
|
|
26
|
+
|
|
27
|
+
**1. Path known** (attached/open in chat, or printed by an earlier `grounder plan` this conversation): update that exact file.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
{{GROUNDER_CLI}} plan "$(cat <<'EOF'
|
|
31
|
+
# Plan: …
|
|
32
|
+
…
|
|
33
|
+
EOF
|
|
34
|
+
)" --path <path-to-existing-plan.md>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`--path` must resolve under this project's `plans/` dir; it always overwrites (no `--force`).
|
|
38
|
+
|
|
39
|
+
**2. Path unknown** (e.g. "update the plan" with nothing attached and no prior path this conversation): look it up, then update as in (1).
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
{{GROUNDER_CLI}} plan list --limit 5
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Pick the path matching the user's intent; ask if none or several match.
|
|
46
|
+
|
|
47
|
+
**3. Genuinely new plan** (not an update): derive a `--title`.
|
|
48
|
+
- If the instruction names one explicitly (e.g. `save as "implementation-phase-1"` or `…phase-1.md`), use it (strip a trailing `.md`).
|
|
49
|
+
- Otherwise derive a short kebab-case name from the plan's title/goal and confirm it with the user before writing.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
{{GROUNDER_CLI}} plan "$(cat <<'EOF'
|
|
53
|
+
# Plan: …
|
|
54
|
+
…
|
|
55
|
+
EOF
|
|
56
|
+
)" --title <name>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If the CLI refuses because that name already exists (non-zero exit; stderr names the conflict), tell the user and ask whether to overwrite (`--force`) or pick a different name. `--force` only resolves this title collision — it is never how you update a plan you already meant to target (use `--path` for that). **Never** silently pass `--force`.
|
|
60
|
+
|
|
61
|
+
Run from the linked project folder or any subdirectory beneath it.
|
|
62
|
+
The vault is outside the workspace — grant shell permissions if Claude Code prompts you.
|
|
63
|
+
|
|
64
|
+
Do not compute vault paths or write files yourself — the CLI handles it.
|
|
65
|
+
Report the CLI output path from stdout to the user.
|
|
@@ -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
|
|
@@ -4,13 +4,13 @@ Read-only — do not write to the vault. Do not invent vault paths.
|
|
|
4
4
|
|
|
5
5
|
From the linked project folder or any subdirectory beneath it:
|
|
6
6
|
|
|
7
|
-
1.
|
|
7
|
+
1. Get the current handoff (skips empty/unreadable files, same pick as the session-start teaser):
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
{{GROUNDER_CLI}} handoff list --head
|
|
10
10
|
|
|
11
|
-
2. If
|
|
11
|
+
2. If empty: tell the user there are no handoffs yet, then read repo `AGENTS.md` only and proceed.
|
|
12
12
|
|
|
13
|
-
3.
|
|
13
|
+
3. Otherwise, read that file. If the user names a specific session instead, run `{{GROUNDER_CLI}} handoff list --limit 5` and read the path they mean.
|
|
14
14
|
|
|
15
15
|
4. Read repo `AGENTS.md` (project conventions and constraints).
|
|
16
16
|
|
|
@@ -2,7 +2,7 @@ Save a note to the Obsidian vault for this project.
|
|
|
2
2
|
|
|
3
3
|
Run from the linked project folder or any subdirectory beneath it:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
{{GROUNDER_CLI}} note "<user text>"
|
|
6
6
|
|
|
7
7
|
The vault is outside the workspace — approve shell permissions if Cursor prompts you.
|
|
8
8
|
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
Update vs create — never guess a `--title` for an update:
|
|
26
|
+
|
|
27
|
+
**1. Path known** (attached/open in chat, or printed by an earlier `grounder plan` this conversation): update that exact file.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
{{GROUNDER_CLI}} plan "$(cat <<'EOF'
|
|
31
|
+
# Plan: …
|
|
32
|
+
…
|
|
33
|
+
EOF
|
|
34
|
+
)" --path <path-to-existing-plan.md>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`--path` must resolve under this project's `plans/` dir; it always overwrites (no `--force`).
|
|
38
|
+
|
|
39
|
+
**2. Path unknown** (e.g. "update the plan" with nothing attached and no prior path this conversation): look it up, then update as in (1).
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
{{GROUNDER_CLI}} plan list --limit 5
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Pick the path matching the user's intent; ask if none or several match.
|
|
46
|
+
|
|
47
|
+
**3. Genuinely new plan** (not an update): derive a `--title`.
|
|
48
|
+
- If the instruction names one explicitly (e.g. `save as "implementation-phase-1"` or `…phase-1.md`), use it (strip a trailing `.md`).
|
|
49
|
+
- Otherwise derive a short kebab-case name from the plan's title/goal and confirm it with the user before writing.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
{{GROUNDER_CLI}} plan "$(cat <<'EOF'
|
|
53
|
+
# Plan: …
|
|
54
|
+
…
|
|
55
|
+
EOF
|
|
56
|
+
)" --title <name>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If the CLI refuses because that name already exists (non-zero exit; stderr names the conflict), tell the user and ask whether to overwrite (`--force`) or pick a different name. `--force` only resolves this title collision — it is never how you update a plan you already meant to target (use `--path` for that). **Never** silently pass `--force`.
|
|
60
|
+
|
|
61
|
+
Run from the linked project folder or any subdirectory beneath it.
|
|
62
|
+
The vault is outside the workspace — approve shell permissions if Cursor prompts you.
|
|
63
|
+
|
|
64
|
+
Do not compute vault paths or write files yourself — the CLI handles it.
|
|
65
|
+
Report the CLI output path from stdout to the user.
|
|
@@ -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
|
|
@@ -4,13 +4,13 @@ Read-only — do not write to the vault. Do not invent vault paths.
|
|
|
4
4
|
|
|
5
5
|
From the linked project folder or any subdirectory beneath it:
|
|
6
6
|
|
|
7
|
-
1.
|
|
7
|
+
1. Get the current handoff (skips empty/unreadable files, same pick as the session-start teaser):
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
{{GROUNDER_CLI}} handoff list --head
|
|
10
10
|
|
|
11
|
-
2. If
|
|
11
|
+
2. If empty: tell the user there are no handoffs yet, then read repo `AGENTS.md` only and proceed.
|
|
12
12
|
|
|
13
|
-
3.
|
|
13
|
+
3. Otherwise, read that file. If the user names a specific session instead, run `{{GROUNDER_CLI}} handoff list --limit 5` and read the path they mean.
|
|
14
14
|
|
|
15
15
|
4. Read repo `AGENTS.md` (project conventions and constraints).
|
|
16
16
|
|