grounder 0.0.2 → 0.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 +113 -40
- package/dist/agents/claude.d.ts +15 -0
- package/dist/agents/claude.d.ts.map +1 -1
- package/dist/agents/claude.js +225 -1
- 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 +175 -1
- package/dist/agents/cursor.js.map +1 -1
- package/dist/agents/hook-runtime.d.ts +108 -0
- package/dist/agents/hook-runtime.d.ts.map +1 -0
- package/dist/agents/hook-runtime.js +260 -0
- package/dist/agents/hook-runtime.js.map +1 -0
- package/dist/agents/types.d.ts +6 -0
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/cli.js +25 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/check.d.ts +13 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +21 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +270 -0
- package/dist/commands/doctor.js.map +1 -0
- 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 +15 -4
- package/dist/commands/handoff/list.js.map +1 -1
- package/dist/commands/handoff/peek.d.ts +38 -0
- package/dist/commands/handoff/peek.d.ts.map +1 -0
- package/dist/commands/handoff/peek.js +120 -0
- package/dist/commands/handoff/peek.js.map +1 -0
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +101 -0
- package/dist/commands/status.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 -0
- package/dist/commands/vault/init.js.map +1 -1
- 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/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/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/package.json +1 -1
- package/templates/agents/claude/commands/grounder-task.md +4 -4
- package/templates/agents/cursor/commands/grounder-task.md +4 -4
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { readCursorHookWorkspaceRoot } from "../../agents/cursor-hook-input.js";
|
|
3
|
+
import { withHomeDir } from "../../connector/home.js";
|
|
4
|
+
import { resolveLinkedProject } from "../../connector/linked.js";
|
|
5
|
+
import { resolveLogsDir } from "../../connector/vault.js";
|
|
6
|
+
import { parseHandoffFrontmatter } from "../../util/frontmatter.js";
|
|
7
|
+
import { flagBool, parseArgs } from "../../util/parse-args.js";
|
|
8
|
+
import { findUsableHandoff } from "../../vault/find-usable-handoff.js";
|
|
9
|
+
/** `YYYY-MM-DD-HHmm` or `YYYY-MM-DD-HHmmss`, optionally followed by `-slug`. */
|
|
10
|
+
const TIMESTAMP_STEM = /^(\d{4}-\d{2}-\d{2})-\d{4}(?:\d{2})?(?:-(.*))?$/;
|
|
11
|
+
/**
|
|
12
|
+
* Derive a display label from a handoff filename when frontmatter has no title:
|
|
13
|
+
* strip the timestamp prefix and `.md`, replace `-` with spaces.
|
|
14
|
+
*/
|
|
15
|
+
export function labelFromHandoffFilename(filePath) {
|
|
16
|
+
const stem = path.basename(filePath, ".md");
|
|
17
|
+
const match = TIMESTAMP_STEM.exec(stem);
|
|
18
|
+
if (!match) {
|
|
19
|
+
return stem.replace(/-/g, " ");
|
|
20
|
+
}
|
|
21
|
+
const slug = match[2];
|
|
22
|
+
if (!slug) {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
return slug.replace(/-/g, " ");
|
|
26
|
+
}
|
|
27
|
+
function createdDateFromFilename(filePath) {
|
|
28
|
+
const stem = path.basename(filePath, ".md");
|
|
29
|
+
const match = TIMESTAMP_STEM.exec(stem);
|
|
30
|
+
return match?.[1];
|
|
31
|
+
}
|
|
32
|
+
/** Prefer frontmatter `created` ISO prefix; else filename date. */
|
|
33
|
+
function formatCreatedDate(created, filePath) {
|
|
34
|
+
if (created) {
|
|
35
|
+
const match = /^(\d{4}-\d{2}-\d{2})/.exec(created);
|
|
36
|
+
if (match) {
|
|
37
|
+
return match[1];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return createdDateFromFilename(filePath);
|
|
41
|
+
}
|
|
42
|
+
function writePeekOutput(teaser, json) {
|
|
43
|
+
if (json) {
|
|
44
|
+
process.stdout.write(teaser === undefined ? "{}\n" : `${JSON.stringify({ additional_context: teaser })}\n`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (teaser !== undefined) {
|
|
48
|
+
process.stdout.write(`${teaser}\n`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the workspace root for a Cursor sessionStart hook.
|
|
53
|
+
* Prefer stdin `workspace_roots[0]`, then `CURSOR_PROJECT_DIR` (always set for
|
|
54
|
+
* Cursor hook scripts), else leave undefined so callers fall back to `cwd`.
|
|
55
|
+
*/
|
|
56
|
+
function resolveCursorHookCwd(stdinWorkspaceRoot) {
|
|
57
|
+
if (stdinWorkspaceRoot !== undefined) {
|
|
58
|
+
return stdinWorkspaceRoot;
|
|
59
|
+
}
|
|
60
|
+
const fromEnv = process.env.CURSOR_PROJECT_DIR?.trim();
|
|
61
|
+
return fromEnv || undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* CLI entry for `grounder handoff peek`.
|
|
65
|
+
* Silent by default: prints nothing and exits 0 when unlinked, empty, or on any error.
|
|
66
|
+
* Used by session-start hooks — must never crash or print noise.
|
|
67
|
+
* Reads Cursor hook stdin for `workspace_roots[0]` when present; falls back to
|
|
68
|
+
* `CURSOR_PROJECT_DIR` (user-level hooks often run with cwd under `~/.cursor`).
|
|
69
|
+
* Pass `--json` for Cursor's `additional_context` stdout contract.
|
|
70
|
+
* @returns Always `0`.
|
|
71
|
+
*/
|
|
72
|
+
export async function runHandoffPeek(argv) {
|
|
73
|
+
const { flags } = parseArgs(argv);
|
|
74
|
+
const stdinWorkspaceRoot = await readCursorHookWorkspaceRoot(process.stdin);
|
|
75
|
+
return runHandoffPeekWithOptions({
|
|
76
|
+
cwd: resolveCursorHookCwd(stdinWorkspaceRoot),
|
|
77
|
+
json: flagBool(flags, "json"),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolves the linked project and prints a one-line teaser for the newest *usable*
|
|
82
|
+
* handoff, or nothing. "Usable" (via {@link findUsableHandoff}) skips empty/unreadable
|
|
83
|
+
* files and falls back to the next-newest — the same selection `grounder handoff list
|
|
84
|
+
* --head` uses, so the teaser and `/grounder-task` never disagree about which handoff
|
|
85
|
+
* is current.
|
|
86
|
+
* Uses {@link resolveLinkedProject} directly (not `requireLinkedProject`) so failures stay silent.
|
|
87
|
+
* @returns Always `0`.
|
|
88
|
+
*/
|
|
89
|
+
export async function runHandoffPeekWithOptions(options = {}) {
|
|
90
|
+
try {
|
|
91
|
+
return await withHomeDir(options.homeDir, async () => {
|
|
92
|
+
let teaser;
|
|
93
|
+
try {
|
|
94
|
+
const resolved = await resolveLinkedProject(options.cwd ?? process.cwd());
|
|
95
|
+
if (resolved.ok) {
|
|
96
|
+
const logsDir = resolveLogsDir(resolved.value.home, resolved.value.repo);
|
|
97
|
+
const usable = await findUsableHandoff(logsDir);
|
|
98
|
+
if (usable) {
|
|
99
|
+
const fm = parseHandoffFrontmatter(usable.content);
|
|
100
|
+
const label = fm.title?.trim() || labelFromHandoffFilename(usable.path);
|
|
101
|
+
const createdDate = formatCreatedDate(fm.created, usable.path);
|
|
102
|
+
if (createdDate) {
|
|
103
|
+
teaser = `[grounder] Latest handoff: "${label}" (${createdDate}). Run /grounder-task to load it, or ignore if unrelated.`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
teaser = undefined;
|
|
110
|
+
}
|
|
111
|
+
writePeekOutput(teaser, options.json);
|
|
112
|
+
return 0;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
writePeekOutput(undefined, options.json);
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=peek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peek.js","sourceRoot":"","sources":["../../../src/commands/handoff/peek.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,2BAA2B,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAevE,gFAAgF;AAChF,MAAM,cAAc,GAAG,iDAAiD,CAAC;AAEzE;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,uBAAuB,CAAC,QAAgB;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,mEAAmE;AACnE,SAAS,iBAAiB,CAAC,OAA2B,EAAE,QAAgB;IACtE,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,uBAAuB,CAAC,QAAQ,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,eAAe,CAAC,MAA0B,EAAE,IAAyB;IAC5E,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC,IAAI,CACtF,CAAC;QACF,OAAO;IACT,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,kBAAsC;IAClE,IAAI,kBAAkB,KAAK,SAAS,EAAE,CAAC;QACrC,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACvD,OAAO,OAAO,IAAI,SAAS,CAAC;AAC9B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAc;IACjD,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,kBAAkB,GAAG,MAAM,2BAA2B,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5E,OAAO,yBAAyB,CAAC;QAC/B,GAAG,EAAE,oBAAoB,CAAC,kBAAkB,CAAC;QAC7C,IAAI,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;KAC9B,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,UAA8B,EAAE;IAC9E,IAAI,CAAC;QACH,OAAO,MAAM,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;YACnD,IAAI,MAA0B,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC1E,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;oBAChB,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACzE,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;oBAChD,IAAI,MAAM,EAAE,CAAC;wBACX,MAAM,EAAE,GAAG,uBAAuB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;wBACnD,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,wBAAwB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;wBACxE,MAAM,WAAW,GAAG,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;wBAC/D,IAAI,WAAW,EAAE,CAAC;4BAChB,MAAM,GAAG,+BAA+B,KAAK,MAAM,WAAW,2DAA2D,CAAC;wBAC5H,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,SAAS,CAAC;YACrB,CAAC;YACD,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YACtC,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface StatusOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
homeDir?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function runStatus(_argv: string[]): Promise<number>;
|
|
6
|
+
export declare function runStatusWithOptions(options?: StatusOptions): Promise<number>;
|
|
7
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA8CD,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhE;AAED,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgEvF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { currentBranch, findGitRoot } from "../connector/git.js";
|
|
3
|
+
import { homeConfigPath, readHomeConfig, withHomeDir } from "../connector/home.js";
|
|
4
|
+
import { findLinkedRepoRoot, readRepoConfig, repoConfigPath, } from "../connector/repo.js";
|
|
5
|
+
import { resolveLogsDir, resolveNotesDir, resolveVaultRoot } from "../connector/vault.js";
|
|
6
|
+
const LABEL_WIDTH = 12;
|
|
7
|
+
const VAULT_INIT = "grounder vault init <path>";
|
|
8
|
+
const REPO_INIT = "grounder init";
|
|
9
|
+
const REPO_INIT_FORCE = "grounder init --force";
|
|
10
|
+
function section(title) {
|
|
11
|
+
return `${title}\n`;
|
|
12
|
+
}
|
|
13
|
+
function statusLine(label, value) {
|
|
14
|
+
return ` ${label.padEnd(LABEL_WIDTH)}${value}\n`;
|
|
15
|
+
}
|
|
16
|
+
function errorDetail(error) {
|
|
17
|
+
return error instanceof Error ? error.message : String(error);
|
|
18
|
+
}
|
|
19
|
+
async function writeGitLine(gitRoot) {
|
|
20
|
+
if (!gitRoot) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const branch = await currentBranch(gitRoot);
|
|
24
|
+
const gitValue = branch !== undefined ? `${gitRoot} (branch: ${branch})` : gitRoot;
|
|
25
|
+
process.stdout.write(statusLine("Git:", gitValue));
|
|
26
|
+
}
|
|
27
|
+
async function tryReadHome() {
|
|
28
|
+
try {
|
|
29
|
+
return { home: await readHomeConfig(), invalid: null };
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
return { home: null, invalid: errorDetail(error) };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function tryReadRepo(linkedRoot) {
|
|
36
|
+
try {
|
|
37
|
+
return { repo: await readRepoConfig(linkedRoot), invalid: null };
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return { repo: null, invalid: errorDetail(error) };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function runStatus(_argv) {
|
|
44
|
+
return runStatusWithOptions({});
|
|
45
|
+
}
|
|
46
|
+
export async function runStatusWithOptions(options = {}) {
|
|
47
|
+
return withHomeDir(options.homeDir, async () => {
|
|
48
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
49
|
+
const { home, invalid: homeInvalid } = await tryReadHome();
|
|
50
|
+
const gitRoot = await findGitRoot(cwd);
|
|
51
|
+
const linkedRoot = await findLinkedRepoRoot(cwd, gitRoot);
|
|
52
|
+
const { repo, invalid: repoInvalid } = linkedRoot
|
|
53
|
+
? await tryReadRepo(linkedRoot)
|
|
54
|
+
: { repo: null, invalid: null };
|
|
55
|
+
process.stdout.write(section("Machine"));
|
|
56
|
+
if (homeInvalid) {
|
|
57
|
+
process.stdout.write(statusLine("Config:", `invalid → run: ${VAULT_INIT}`));
|
|
58
|
+
}
|
|
59
|
+
else if (!home) {
|
|
60
|
+
process.stdout.write(statusLine("Config:", `missing → run: ${VAULT_INIT}`));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
process.stdout.write(statusLine("Config:", homeConfigPath()));
|
|
64
|
+
process.stdout.write(statusLine("Vault:", resolveVaultRoot(home)));
|
|
65
|
+
}
|
|
66
|
+
process.stdout.write("\n");
|
|
67
|
+
process.stdout.write(section("Project"));
|
|
68
|
+
if (!linkedRoot) {
|
|
69
|
+
process.stdout.write(statusLine("Linked:", "no"));
|
|
70
|
+
if (home) {
|
|
71
|
+
process.stdout.write(statusLine("Config:", `missing → run: ${REPO_INIT}`));
|
|
72
|
+
}
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
if (repoInvalid || !repo) {
|
|
76
|
+
const fix = home ? REPO_INIT_FORCE : VAULT_INIT;
|
|
77
|
+
process.stdout.write(statusLine("Linked:", `incomplete → run: ${fix}`));
|
|
78
|
+
process.stdout.write(statusLine("Folder:", linkedRoot));
|
|
79
|
+
process.stdout.write(statusLine("Config:", repoInvalid ? `invalid → run: ${REPO_INIT_FORCE}` : `missing → run: ${REPO_INIT}`));
|
|
80
|
+
await writeGitLine(gitRoot);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
if (!home) {
|
|
84
|
+
process.stdout.write(statusLine("Linked:", `incomplete → run: ${VAULT_INIT}`));
|
|
85
|
+
process.stdout.write(statusLine("Folder:", linkedRoot));
|
|
86
|
+
process.stdout.write(statusLine("Config:", repoConfigPath(linkedRoot)));
|
|
87
|
+
process.stdout.write(statusLine("Id:", repo.projectId));
|
|
88
|
+
await writeGitLine(gitRoot);
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
process.stdout.write(statusLine("Linked:", "yes"));
|
|
92
|
+
process.stdout.write(statusLine("Folder:", linkedRoot));
|
|
93
|
+
process.stdout.write(statusLine("Config:", repoConfigPath(linkedRoot)));
|
|
94
|
+
process.stdout.write(statusLine("Id:", repo.projectId));
|
|
95
|
+
process.stdout.write(statusLine("Notes:", resolveNotesDir(home, repo)));
|
|
96
|
+
process.stdout.write(statusLine("Logs:", resolveLogsDir(home, repo)));
|
|
97
|
+
await writeGitLine(gitRoot);
|
|
98
|
+
return 0;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ export interface VaultInitOptions {
|
|
|
2
2
|
vaultPath: string;
|
|
3
3
|
yes?: boolean;
|
|
4
4
|
force?: boolean;
|
|
5
|
+
/** Also install session-start teaser hooks for adapters that support them. */
|
|
6
|
+
hooks?: boolean;
|
|
5
7
|
homeDir?: string;
|
|
6
8
|
/** Agent ids to install for. Defaults to auto-detecting installed agents. */
|
|
7
9
|
agents?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/vault/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/vault/init.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlE;AAED,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuFxF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { installHookRuntime, runtimeCliPath } from "../../agents/hook-runtime.js";
|
|
2
3
|
import { resolveAgents } from "../../agents/index.js";
|
|
3
4
|
import { homeConfigPath, readHomeConfig, withHomeDir, writeHomeConfig, } from "../../connector/home.js";
|
|
4
5
|
import { flagBool, flagStrings, parseArgs } from "../../util/parse-args.js";
|
|
@@ -10,6 +11,7 @@ export async function runVaultInit(argv) {
|
|
|
10
11
|
const vaultPathArg = positional[0];
|
|
11
12
|
const yes = flagBool(flags, "yes", "y");
|
|
12
13
|
const force = flagBool(flags, "force", "f");
|
|
14
|
+
const hooks = flagBool(flags, "hooks");
|
|
13
15
|
const agents = flagStrings(repeated, "agent");
|
|
14
16
|
if (!vaultPathArg) {
|
|
15
17
|
process.stderr.write("Usage: grounder vault init <path>\n");
|
|
@@ -19,6 +21,7 @@ export async function runVaultInit(argv) {
|
|
|
19
21
|
vaultPath: vaultPathArg,
|
|
20
22
|
yes,
|
|
21
23
|
force,
|
|
24
|
+
hooks,
|
|
22
25
|
agents: agents.length > 0 ? agents : undefined,
|
|
23
26
|
});
|
|
24
27
|
}
|
|
@@ -27,6 +30,7 @@ export async function runVaultInitWithOptions(options) {
|
|
|
27
30
|
const vaultRoot = resolveUserPath(options.vaultPath);
|
|
28
31
|
const yes = options.yes ?? false;
|
|
29
32
|
const force = options.force ?? false;
|
|
33
|
+
const hooks = options.hooks ?? false;
|
|
30
34
|
const homeDir = options.homeDir;
|
|
31
35
|
const existingHome = await readHomeConfig();
|
|
32
36
|
const projectsDir = projectsParent(vaultRoot);
|
|
@@ -39,8 +43,18 @@ export async function runVaultInitWithOptions(options) {
|
|
|
39
43
|
process.stdout.write("Will write:\n");
|
|
40
44
|
process.stdout.write(` home ${homeConfigPath(homeDir)}\n`);
|
|
41
45
|
process.stdout.write(" vault 10-Projects/ (if missing)\n");
|
|
46
|
+
const hookAgents = hooks ? agents.filter((agent) => agent.installHooks) : [];
|
|
47
|
+
if (hookAgents.length > 0) {
|
|
48
|
+
// Shared across agents — list once (not under each adapter).
|
|
49
|
+
process.stdout.write(` grounder runtime ${runtimeCliPath(homeDir)}\n`);
|
|
50
|
+
}
|
|
42
51
|
for (const agent of agents) {
|
|
43
52
|
process.stdout.write(` ${agent.id.padEnd(8)} (${agent.name} artifacts)\n`);
|
|
53
|
+
if (hooks && agent.expectedHookArtifacts) {
|
|
54
|
+
for (const hookPath of agent.expectedHookArtifacts(homeDir)) {
|
|
55
|
+
process.stdout.write(` ${agent.id.padEnd(8)} hook ${hookPath}\n`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
44
58
|
}
|
|
45
59
|
if (agents.length === 0) {
|
|
46
60
|
process.stdout.write(" (no supported agents detected — skipping agent artifacts)\n");
|
|
@@ -57,6 +71,13 @@ export async function runVaultInitWithOptions(options) {
|
|
|
57
71
|
await mkdir(projectsDir, { recursive: true });
|
|
58
72
|
process.stdout.write("✓ Wrote home config\n");
|
|
59
73
|
process.stdout.write(`✓ Vault scaffold: ${projectsDir}\n`);
|
|
74
|
+
if (hookAgents.length > 0) {
|
|
75
|
+
const runtime = await installHookRuntime({ homeDir });
|
|
76
|
+
const runtimeLabel = runtime.status === "skipped"
|
|
77
|
+
? "already exists (skipped)"
|
|
78
|
+
: `installed (${runtime.mode}): ${runtime.cliPath}`;
|
|
79
|
+
process.stdout.write(`✓ Grounder hook runtime ${runtimeLabel}\n`);
|
|
80
|
+
}
|
|
60
81
|
for (const agent of agents) {
|
|
61
82
|
const result = await agent.install({ force, homeDir });
|
|
62
83
|
for (const [artifactPath, status] of Object.entries(result.artifacts)) {
|
|
@@ -66,6 +87,13 @@ export async function runVaultInitWithOptions(options) {
|
|
|
66
87
|
if (Object.keys(result.artifacts).length === 0) {
|
|
67
88
|
process.stdout.write(`✓ ${agent.name}: no artifacts to install yet\n`);
|
|
68
89
|
}
|
|
90
|
+
if (hooks && agent.installHooks) {
|
|
91
|
+
const hookResult = await agent.installHooks({ force, homeDir });
|
|
92
|
+
for (const [artifactPath, status] of Object.entries(hookResult.artifacts)) {
|
|
93
|
+
const label = status === "skipped" ? "already exists (skipped)" : `installed: ${artifactPath}`;
|
|
94
|
+
process.stdout.write(`✓ ${agent.name} hook ${label}\n`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
69
97
|
}
|
|
70
98
|
return 0;
|
|
71
99
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/vault/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EACL,cAAc,EACd,cAAc,EACd,WAAW,EACX,eAAe,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/commands/vault/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse `title` / `created` from Grounder handoff frontmatter.
|
|
3
|
+
* Accepts the quoted `key: "value"` shape {@link writeHandoff} writes today, and
|
|
4
|
+
* unquoted `key: value` from earlier handoffs. Not a general YAML parser.
|
|
5
|
+
* Returns `{}` on anything unexpected; never throws.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseHandoffFrontmatter(content: string): {
|
|
8
|
+
title?: string;
|
|
9
|
+
created?: string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=frontmatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../src/util/frontmatter.ts"],"names":[],"mappings":"AA6BA;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAqC7F"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unescape sequences produced by {@link yamlDoubleQuoted}: `\\`, `\"`, `\r`, `\n`.
|
|
3
|
+
*/
|
|
4
|
+
function unescapeYamlDoubleQuoted(value) {
|
|
5
|
+
let result = "";
|
|
6
|
+
for (let i = 0; i < value.length; i++) {
|
|
7
|
+
if (value[i] === "\\" && i + 1 < value.length) {
|
|
8
|
+
const next = value[i + 1];
|
|
9
|
+
if (next === "\\" || next === '"') {
|
|
10
|
+
result += next;
|
|
11
|
+
i++;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (next === "n") {
|
|
15
|
+
result += "\n";
|
|
16
|
+
i++;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (next === "r") {
|
|
20
|
+
result += "\r";
|
|
21
|
+
i++;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
result += value[i];
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse `title` / `created` from Grounder handoff frontmatter.
|
|
31
|
+
* Accepts the quoted `key: "value"` shape {@link writeHandoff} writes today, and
|
|
32
|
+
* unquoted `key: value` from earlier handoffs. Not a general YAML parser.
|
|
33
|
+
* Returns `{}` on anything unexpected; never throws.
|
|
34
|
+
*/
|
|
35
|
+
export function parseHandoffFrontmatter(content) {
|
|
36
|
+
try {
|
|
37
|
+
const lines = content.split(/\r?\n/);
|
|
38
|
+
if (lines[0] !== "---") {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
const result = {};
|
|
42
|
+
for (let i = 1; i < lines.length; i++) {
|
|
43
|
+
const line = lines[i] ?? "";
|
|
44
|
+
if (line === "---") {
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
const quoted = /^(title|created):\s*"(.*)"\s*$/.exec(line);
|
|
48
|
+
if (quoted) {
|
|
49
|
+
const key = quoted[1];
|
|
50
|
+
result[key] = unescapeYamlDoubleQuoted(quoted[2] ?? "");
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Opening quote without a closing pair — ignore rather than treat as unquoted.
|
|
54
|
+
if (/^(title|created):\s*"/.test(line)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const unquoted = /^(title|created):\s*(.+?)\s*$/.exec(line);
|
|
58
|
+
if (unquoted) {
|
|
59
|
+
const key = unquoted[1];
|
|
60
|
+
result[key] = unquoted[2] ?? "";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=frontmatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/util/frontmatter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,SAAS,wBAAwB,CAAC,KAAa;IAC7C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBAClC,MAAM,IAAI,IAAI,CAAC;gBACf,CAAC,EAAE,CAAC;gBACJ,SAAS;YACX,CAAC;YACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACjB,MAAM,IAAI,IAAI,CAAC;gBACf,CAAC,EAAE,CAAC;gBACJ,SAAS;YACX,CAAC;YACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;gBACjB,MAAM,IAAI,IAAI,CAAC;gBACf,CAAC,EAAE,CAAC;gBACJ,SAAS;YACX,CAAC;QACH,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAe;IACrD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;YACvB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,MAAM,GAAyC,EAAE,CAAC;QACxD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;gBACnB,MAAM;YACR,CAAC;YAED,MAAM,MAAM,GAAG,gCAAgC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC3D,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAwB,CAAC;gBAC7C,MAAM,CAAC,GAAG,CAAC,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBACxD,SAAS;YACX,CAAC;YAED,+EAA+E;YAC/E,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,SAAS;YACX,CAAC;YAED,MAAM,QAAQ,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5D,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAwB,CAAC;gBAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAClC,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type MergeJsonResult = {
|
|
2
|
+
ok: true;
|
|
3
|
+
created: boolean;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
error: "unparseable";
|
|
7
|
+
message: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Read a JSON object file (default `{}` if missing), apply `merge`, write pretty-printed.
|
|
11
|
+
* On parse failure or non-object root: leaves the file untouched and returns an error
|
|
12
|
+
* so callers can warn without clobbering shared config.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mergeJsonFile(filePath: string, merge: (current: Record<string, unknown>) => Record<string, unknown>): Promise<MergeJsonResult>;
|
|
15
|
+
//# sourceMappingURL=merge-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-json.d.ts","sourceRoot":"","sources":["../../src/util/merge-json.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC9B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnE,OAAO,CAAC,eAAe,CAAC,CAiC1B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileExists } from "./fs.js";
|
|
4
|
+
/**
|
|
5
|
+
* Read a JSON object file (default `{}` if missing), apply `merge`, write pretty-printed.
|
|
6
|
+
* On parse failure or non-object root: leaves the file untouched and returns an error
|
|
7
|
+
* so callers can warn without clobbering shared config.
|
|
8
|
+
*/
|
|
9
|
+
export async function mergeJsonFile(filePath, merge) {
|
|
10
|
+
const existed = await fileExists(filePath);
|
|
11
|
+
let current = {};
|
|
12
|
+
if (existed) {
|
|
13
|
+
const raw = await readFile(filePath, "utf8");
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = JSON.parse(raw);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
error: "unparseable",
|
|
23
|
+
message: `Refusing to modify ${filePath}: invalid JSON (${detail})`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
error: "unparseable",
|
|
30
|
+
message: `Refusing to modify ${filePath}: root value must be a JSON object`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
current = parsed;
|
|
34
|
+
}
|
|
35
|
+
const next = merge(current);
|
|
36
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
37
|
+
await writeFile(filePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
38
|
+
return { ok: true, created: !existed };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=merge-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-json.js","sourceRoot":"","sources":["../../src/util/merge-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMrC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAgB,EAChB,KAAoE;IAEpE,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,OAAO,GAA4B,EAAE,CAAC;IAE1C,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7C,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChE,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,aAAa;gBACpB,OAAO,EAAE,sBAAsB,QAAQ,mBAAmB,MAAM,GAAG;aACpE,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3E,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,aAAa;gBACpB,OAAO,EAAE,sBAAsB,QAAQ,oCAAoC;aAC5E,CAAC;QACJ,CAAC;QAED,OAAO,GAAG,MAAiC,CAAC;IAC9C,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5B,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;AACzC,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/package.json
CHANGED
|
@@ -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
|
-
npx grounder handoff list --
|
|
9
|
+
npx grounder 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 `npx grounder 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
|
|
|
@@ -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
|
-
npx grounder handoff list --
|
|
9
|
+
npx grounder 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 `npx grounder 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
|
|