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.
Files changed (60) hide show
  1. package/README.md +113 -40
  2. package/dist/agents/claude.d.ts +15 -0
  3. package/dist/agents/claude.d.ts.map +1 -1
  4. package/dist/agents/claude.js +225 -1
  5. package/dist/agents/claude.js.map +1 -1
  6. package/dist/agents/cursor-hook-input.d.ts +14 -0
  7. package/dist/agents/cursor-hook-input.d.ts.map +1 -0
  8. package/dist/agents/cursor-hook-input.js +80 -0
  9. package/dist/agents/cursor-hook-input.js.map +1 -0
  10. package/dist/agents/cursor.d.ts +11 -0
  11. package/dist/agents/cursor.d.ts.map +1 -1
  12. package/dist/agents/cursor.js +175 -1
  13. package/dist/agents/cursor.js.map +1 -1
  14. package/dist/agents/hook-runtime.d.ts +108 -0
  15. package/dist/agents/hook-runtime.d.ts.map +1 -0
  16. package/dist/agents/hook-runtime.js +260 -0
  17. package/dist/agents/hook-runtime.js.map +1 -0
  18. package/dist/agents/types.d.ts +6 -0
  19. package/dist/agents/types.d.ts.map +1 -1
  20. package/dist/cli.js +25 -1
  21. package/dist/cli.js.map +1 -1
  22. package/dist/commands/check.d.ts +13 -0
  23. package/dist/commands/check.d.ts.map +1 -0
  24. package/dist/commands/check.js +21 -0
  25. package/dist/commands/check.js.map +1 -0
  26. package/dist/commands/doctor.d.ts +9 -0
  27. package/dist/commands/doctor.d.ts.map +1 -0
  28. package/dist/commands/doctor.js +270 -0
  29. package/dist/commands/doctor.js.map +1 -0
  30. package/dist/commands/handoff/list.d.ts +9 -0
  31. package/dist/commands/handoff/list.d.ts.map +1 -1
  32. package/dist/commands/handoff/list.js +15 -4
  33. package/dist/commands/handoff/list.js.map +1 -1
  34. package/dist/commands/handoff/peek.d.ts +38 -0
  35. package/dist/commands/handoff/peek.d.ts.map +1 -0
  36. package/dist/commands/handoff/peek.js +120 -0
  37. package/dist/commands/handoff/peek.js.map +1 -0
  38. package/dist/commands/status.d.ts +7 -0
  39. package/dist/commands/status.d.ts.map +1 -0
  40. package/dist/commands/status.js +101 -0
  41. package/dist/commands/status.js.map +1 -0
  42. package/dist/commands/vault/init.d.ts +2 -0
  43. package/dist/commands/vault/init.d.ts.map +1 -1
  44. package/dist/commands/vault/init.js +28 -0
  45. package/dist/commands/vault/init.js.map +1 -1
  46. package/dist/util/frontmatter.d.ts +11 -0
  47. package/dist/util/frontmatter.d.ts.map +1 -0
  48. package/dist/util/frontmatter.js +69 -0
  49. package/dist/util/frontmatter.js.map +1 -0
  50. package/dist/util/merge-json.d.ts +15 -0
  51. package/dist/util/merge-json.d.ts.map +1 -0
  52. package/dist/util/merge-json.js +40 -0
  53. package/dist/util/merge-json.js.map +1 -0
  54. package/dist/vault/find-usable-handoff.d.ts +19 -0
  55. package/dist/vault/find-usable-handoff.d.ts.map +1 -0
  56. package/dist/vault/find-usable-handoff.js +28 -0
  57. package/dist/vault/find-usable-handoff.js.map +1 -0
  58. package/package.json +1 -1
  59. package/templates/agents/claude/commands/grounder-task.md +4 -4
  60. 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":"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"}
@@ -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":"AAaA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,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,CAkBlE;AAED,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DxF"}
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":"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"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grounder",
3
- "version": "0.0.2",
3
+ "version": "0.2.0",
4
4
  "description": "Connect git projects to Obsidian dev vaults for AI agent memory",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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. List recent handoffs (newest first):
7
+ 1. Get the current handoff (skips empty/unreadable files, same pick as the session-start teaser):
8
8
 
9
- npx grounder handoff list --limit 5
9
+ npx grounder handoff list --head
10
10
 
11
- 2. If the list is empty: tell the user there are no handoffs yet, then read repo `AGENTS.md` only and proceed.
11
+ 2. If empty: tell the user there are no handoffs yet, then read repo `AGENTS.md` only and proceed.
12
12
 
13
- 3. If handoffs exist: read the newest file (first path). Optionally skim other listed paths if the user names a session or the newest is clearly wrong.
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. List recent handoffs (newest first):
7
+ 1. Get the current handoff (skips empty/unreadable files, same pick as the session-start teaser):
8
8
 
9
- npx grounder handoff list --limit 5
9
+ npx grounder handoff list --head
10
10
 
11
- 2. If the list is empty: tell the user there are no handoffs yet, then read repo `AGENTS.md` only and proceed.
11
+ 2. If empty: tell the user there are no handoffs yet, then read repo `AGENTS.md` only and proceed.
12
12
 
13
- 3. If handoffs exist: read the newest file (first path). Optionally skim other listed paths if the user names a session or the newest is clearly wrong.
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