@groundtrack/hooks 0.1.4 → 0.1.6
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/dist/agent-instructions.js +38 -1
- package/dist/doctor.js +2 -4
- package/dist/enrich-store-event.js +13 -1
- package/package.json +1 -1
- package/templates/AGENTS.md +1 -2
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const VERSION_PATTERN = /<!-- groundtrack:start version=(\d+) -->/;
|
|
3
4
|
/** Canonical managed Groundtrack policy, packaged with the CLI. */
|
|
4
5
|
export const AGENT_INSTRUCTION_BODY = readFileSync(new URL("../templates/AGENTS.md", import.meta.url), "utf8").trim();
|
|
5
6
|
const versionMatch = VERSION_PATTERN.exec(AGENT_INSTRUCTION_BODY);
|
|
6
7
|
if (!versionMatch) {
|
|
7
8
|
throw new Error("Canonical Groundtrack AGENTS.md template is missing its version marker.");
|
|
8
9
|
}
|
|
10
|
+
const AGENT_INSTRUCTION_START_MARKER = versionMatch[0];
|
|
9
11
|
/** Version is derived from the canonical template marker; never define it separately. */
|
|
10
12
|
export const AGENT_INSTRUCTION_TEMPLATE_VERSION = Number(versionMatch[1]);
|
|
13
|
+
function readInstructions(path) {
|
|
14
|
+
try {
|
|
15
|
+
return readFileSync(path, "utf8");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function instructionVersion(content) {
|
|
22
|
+
const match = content ? VERSION_PATTERN.exec(content) : null;
|
|
23
|
+
return match ? Number(match[1]) : null;
|
|
24
|
+
}
|
|
25
|
+
/** Detect the managed policy version available to a client in one repository. */
|
|
26
|
+
export function detectAgentInstructionsVersion(repoDir, client) {
|
|
27
|
+
const agents = readInstructions(join(repoDir, "AGENTS.md"));
|
|
28
|
+
const agentsVersion = instructionVersion(agents);
|
|
29
|
+
if (client !== "claude")
|
|
30
|
+
return agentsVersion;
|
|
31
|
+
const claude = readInstructions(join(repoDir, "CLAUDE.md"));
|
|
32
|
+
if (!claude)
|
|
33
|
+
return null;
|
|
34
|
+
if (/^@AGENTS\.md\s*$/m.test(claude)) {
|
|
35
|
+
const bridgeVersion = instructionVersion(claude);
|
|
36
|
+
if (agentsVersion == null)
|
|
37
|
+
return null;
|
|
38
|
+
return bridgeVersion == null ? agentsVersion : Math.min(bridgeVersion, agentsVersion);
|
|
39
|
+
}
|
|
40
|
+
const inlineVersion = instructionVersion(claude);
|
|
41
|
+
if (inlineVersion != null)
|
|
42
|
+
return inlineVersion;
|
|
43
|
+
const unmarkedCurrentInstructions = AGENT_INSTRUCTION_BODY.replace(AGENT_INSTRUCTION_START_MARKER, "")
|
|
44
|
+
.replace("<!-- groundtrack:end -->", "")
|
|
45
|
+
.trim();
|
|
46
|
+
return claude.includes(unmarkedCurrentInstructions) ? AGENT_INSTRUCTION_TEMPLATE_VERSION : null;
|
|
47
|
+
}
|
package/dist/doctor.js
CHANGED
|
@@ -267,14 +267,12 @@ function removeLegacyAgentInstructions(content) {
|
|
|
267
267
|
return lines.join("\n");
|
|
268
268
|
}
|
|
269
269
|
function upsertManagedContent(content, desired, migrateLegacyAgentInstructions = false) {
|
|
270
|
-
const existing =
|
|
271
|
-
? removeLegacyAgentInstructions(content ?? "")
|
|
272
|
-
: (content ?? "");
|
|
270
|
+
const existing = content ?? "";
|
|
273
271
|
const managed = /<!-- groundtrack:start version=\d+ -->[\s\S]*?<!-- groundtrack:end -->/;
|
|
274
272
|
if (managed.test(existing)) {
|
|
275
273
|
return `${existing.replace(managed, desired).trimEnd()}\n`;
|
|
276
274
|
}
|
|
277
|
-
const base = existing.trimEnd();
|
|
275
|
+
const base = (migrateLegacyAgentInstructions ? removeLegacyAgentInstructions(existing) : existing).trimEnd();
|
|
278
276
|
return `${base}${base ? "\n\n" : ""}${desired}\n`;
|
|
279
277
|
}
|
|
280
278
|
function versionParts(version) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { closeSync, fstatSync, openSync, readSync } from "node:fs";
|
|
3
|
+
import { detectAgentInstructionsVersion, } from "./agent-instructions.js";
|
|
3
4
|
const PROVIDERS = [
|
|
4
5
|
[/^claude(?:[-_.]|$)/, "anthropic"],
|
|
5
6
|
[/^(?:gpt|o\d|codex)(?:[-_.]|$)/, "openai"],
|
|
@@ -120,6 +121,11 @@ function gitContext(cwd) {
|
|
|
120
121
|
isDirty: status == null ? null : status.length > 0,
|
|
121
122
|
};
|
|
122
123
|
}
|
|
124
|
+
function repositoryRoot(cwd) {
|
|
125
|
+
if (!cwd)
|
|
126
|
+
return null;
|
|
127
|
+
return git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd;
|
|
128
|
+
}
|
|
123
129
|
export function enrichInput(hookInput, clientOverride, tool = "store_event") {
|
|
124
130
|
const toolInput = hookInput.tool_input &&
|
|
125
131
|
typeof hookInput.tool_input === "object" &&
|
|
@@ -133,7 +139,8 @@ export function enrichInput(hookInput, clientOverride, tool = "store_event") {
|
|
|
133
139
|
? (hookInput.model_id ?? hookInput.model)
|
|
134
140
|
: (hookInput.model ?? hookInput.model_id ?? transcriptModel(hookInput.transcript_path)));
|
|
135
141
|
const invocationId = nonEmptyString(isCursor ? hookInput.generation_id : hookInput.tool_use_id);
|
|
136
|
-
const
|
|
142
|
+
const cwd = workspacePath(hookInput);
|
|
143
|
+
const context = gitContext(cwd);
|
|
137
144
|
if (hostSessionId)
|
|
138
145
|
toolInput.sessionId = `${host}:${hostSessionId}`;
|
|
139
146
|
if (hostModel)
|
|
@@ -141,6 +148,11 @@ export function enrichInput(hookInput, clientOverride, tool = "store_event") {
|
|
|
141
148
|
if (tool === "store_event" && invocationId) {
|
|
142
149
|
toolInput.clientEventId = [host, hostSessionId, invocationId].filter(Boolean).join(":");
|
|
143
150
|
}
|
|
151
|
+
if (tool === "search_experiences") {
|
|
152
|
+
const client = host === "claude" || host === "cursor" ? host : "codex";
|
|
153
|
+
const root = repositoryRoot(cwd);
|
|
154
|
+
toolInput.agentInstructionsVersion = root ? detectAgentInstructionsVersion(root, client) : null;
|
|
155
|
+
}
|
|
144
156
|
if (context)
|
|
145
157
|
Object.assign(toolInput, context);
|
|
146
158
|
return toolInput;
|
package/package.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- groundtrack:start version=
|
|
1
|
+
<!-- groundtrack:start version=3 -->
|
|
2
2
|
|
|
3
3
|
## Groundtrack
|
|
4
4
|
|
|
@@ -12,6 +12,5 @@ Use Groundtrack as the shared engineering memory for this repository.
|
|
|
12
12
|
- After a non-obvious discovery, decision, workaround, correction, or failed approach that would help a future agent, call `store_event` promptly with a concise title and an explanation of what happened and why it matters.
|
|
13
13
|
- Do not store routine implementation details, obvious facts, unrestricted transcripts, or secrets.
|
|
14
14
|
- Keep the raw-event ids returned by `store_event`. When the related work lands in a commit or pull request, call `link_outcomes` with those ids and the explicit commit SHA and/or pull-request number. Never infer an outcome from worktree HEAD.
|
|
15
|
-
- When recurring engineering friction may support or oppose an existing initiative, call `search_initiatives` before storing the event and link it to the matching initiative with the appropriate direction. Propose a new initiative only when explicitly recommending organizational change and no existing initiative matches.
|
|
16
15
|
|
|
17
16
|
<!-- groundtrack:end -->
|