@groundtrack/hooks 0.1.3 → 0.1.4
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 +6 -1
- package/dist/doctor.js +16 -9
- package/dist/groundtrack.js +25 -3
- package/package.json +1 -1
- package/templates/AGENTS.md +2 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ repository root:
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
groundtrack doctor
|
|
19
|
-
groundtrack doctor --fix
|
|
19
|
+
groundtrack doctor --fix --client codex
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
`doctor` checks the installed package/runtime version, discovers the actual
|
|
@@ -24,6 +24,11 @@ Groundtrack MCP server alias, validates Claude/Codex/Cursor hook configuration,
|
|
|
24
24
|
checks the managed `AGENTS.md` and `CLAUDE.md` instructions, and simulates a
|
|
25
25
|
hook invocation. It exits non-zero when repair is needed.
|
|
26
26
|
|
|
27
|
+
Pass `--client claude`, `--client codex`, or `--client cursor` when repairing a
|
|
28
|
+
repository. This limits MCP alias discovery and configuration changes to that
|
|
29
|
+
client. If more than one client is detected, `doctor --fix` refuses to guess
|
|
30
|
+
which one to modify.
|
|
31
|
+
|
|
27
32
|
The Doctor verifies local configuration and enrichment behavior, but it cannot
|
|
28
33
|
observe whether a coding-agent host loaded or trusted a project hook. A
|
|
29
34
|
`search_experiences` MCP response is the authoritative live check: Groundtrack
|
package/dist/doctor.js
CHANGED
|
@@ -53,9 +53,13 @@ function matcherForAliases(aliases) {
|
|
|
53
53
|
const server = escaped.length === 1 ? escaped[0] : `(?:${escaped.join("|")})`;
|
|
54
54
|
return `^mcp__${server}__(store_event|search_experiences)$`;
|
|
55
55
|
}
|
|
56
|
-
function groundtrackAliases(repoDir) {
|
|
56
|
+
function groundtrackAliases(repoDir, clients) {
|
|
57
57
|
const aliases = new Set();
|
|
58
|
-
|
|
58
|
+
const jsonConfigs = [
|
|
59
|
+
...(clients.includes("claude") ? [".mcp.json"] : []),
|
|
60
|
+
...(clients.includes("cursor") ? [".cursor/mcp.json"] : []),
|
|
61
|
+
];
|
|
62
|
+
for (const relative of jsonConfigs) {
|
|
59
63
|
const path = join(repoDir, relative);
|
|
60
64
|
try {
|
|
61
65
|
const config = readJson(path);
|
|
@@ -73,11 +77,13 @@ function groundtrackAliases(repoDir) {
|
|
|
73
77
|
// The owning adapter reports malformed config; alias discovery stays fail-open.
|
|
74
78
|
}
|
|
75
79
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
if (clients.includes("codex")) {
|
|
81
|
+
const codex = readText(join(repoDir, ".codex/config.toml")) ?? "";
|
|
82
|
+
const table = /^\[mcp_servers\.([^\]]+)\]([\s\S]*?)(?=^\[|(?![\s\S]))/gm;
|
|
83
|
+
for (const match of codex.matchAll(table)) {
|
|
84
|
+
if (match[1]?.toLowerCase().includes("groundtrack") || match[2]?.includes("groundtrack")) {
|
|
85
|
+
aliases.add(match[1]);
|
|
86
|
+
}
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
89
|
if (aliases.size === 0)
|
|
@@ -360,7 +366,7 @@ function addFileCheck(checks, changedFiles, options, input) {
|
|
|
360
366
|
export function runDoctor(options) {
|
|
361
367
|
const version = packageVersion();
|
|
362
368
|
const clients = options.clients ?? detectClients(options.repoDir);
|
|
363
|
-
const aliases = groundtrackAliases(options.repoDir);
|
|
369
|
+
const aliases = groundtrackAliases(options.repoDir, clients);
|
|
364
370
|
const matcher = matcherForAliases(aliases);
|
|
365
371
|
const checks = [];
|
|
366
372
|
const changedFiles = [];
|
|
@@ -474,7 +480,8 @@ export function runDoctor(options) {
|
|
|
474
480
|
}
|
|
475
481
|
}
|
|
476
482
|
const instructionFiles = new Map([["AGENTS.md", AGENT_INSTRUCTION_BODY]]);
|
|
477
|
-
if (clients.includes("claude") ||
|
|
483
|
+
if (clients.includes("claude") ||
|
|
484
|
+
(options.clients === undefined && existsSync(join(options.repoDir, "CLAUDE.md")))) {
|
|
478
485
|
instructionFiles.set("CLAUDE.md", CLAUDE_INSTRUCTION_BODY);
|
|
479
486
|
}
|
|
480
487
|
for (const [name, desired] of instructionFiles) {
|
package/dist/groundtrack.js
CHANGED
|
@@ -10,13 +10,16 @@ function usage() {
|
|
|
10
10
|
return `Groundtrack CLI
|
|
11
11
|
|
|
12
12
|
Usage:
|
|
13
|
-
groundtrack doctor [--fix] [--yes] [--json] [--repo PATH]
|
|
13
|
+
groundtrack doctor [--fix] [--yes] [--json] [--client CLIENT] [--repo PATH]
|
|
14
14
|
groundtrack version [--json]
|
|
15
15
|
|
|
16
16
|
Commands:
|
|
17
17
|
doctor Diagnose Groundtrack hooks and managed agent instructions
|
|
18
18
|
doctor --fix Repair deterministic configuration drift
|
|
19
19
|
version Print the installed CLI/runtime version
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--client Limit diagnosis and repair to claude, codex, or cursor
|
|
20
23
|
`;
|
|
21
24
|
}
|
|
22
25
|
function parseArguments(argv) {
|
|
@@ -28,22 +31,37 @@ function parseArguments(argv) {
|
|
|
28
31
|
const command = argv[0] === "version" || argv[0] === "--version" ? "version" : "doctor";
|
|
29
32
|
let repoDir = process.cwd();
|
|
30
33
|
let homeDir = homedir();
|
|
34
|
+
let client;
|
|
31
35
|
for (let index = 0; index < argv.length; index += 1) {
|
|
32
36
|
if (argv[index] === "--repo" && argv[index + 1])
|
|
33
37
|
repoDir = resolve(argv[++index]);
|
|
34
38
|
if (argv[index] === "--home" && argv[index + 1])
|
|
35
39
|
homeDir = resolve(argv[++index]);
|
|
40
|
+
if (argv[index] === "--client") {
|
|
41
|
+
const value = argv[++index];
|
|
42
|
+
if (value !== "claude" && value !== "codex" && value !== "cursor") {
|
|
43
|
+
throw new Error("--client must be one of: claude, codex, cursor");
|
|
44
|
+
}
|
|
45
|
+
client = value;
|
|
46
|
+
}
|
|
36
47
|
}
|
|
37
48
|
return {
|
|
38
49
|
command,
|
|
39
50
|
fix: argv.includes("--fix"),
|
|
40
51
|
yes: argv.includes("--yes") || argv.includes("-y"),
|
|
41
52
|
json: argv.includes("--json"),
|
|
53
|
+
client,
|
|
42
54
|
repoDir,
|
|
43
55
|
homeDir,
|
|
44
56
|
};
|
|
45
57
|
}
|
|
46
58
|
function printResult(result, options = {}) {
|
|
59
|
+
const selectedClient = options.client ?? (result.clients.length === 1 ? result.clients[0] : null);
|
|
60
|
+
const fixHint = !options.showFixHint
|
|
61
|
+
? ""
|
|
62
|
+
: selectedClient
|
|
63
|
+
? ` Run: groundtrack doctor --fix --client ${selectedClient}`
|
|
64
|
+
: " Re-run with --client claude, --client codex, or --client cursor.";
|
|
47
65
|
stdout.write(`Groundtrack doctor ${result.version}\n`);
|
|
48
66
|
stdout.write(`Clients: ${result.clients.join(", ")}\n`);
|
|
49
67
|
stdout.write(`MCP aliases: ${result.aliases.join(", ")}\n\n`);
|
|
@@ -64,7 +82,7 @@ function printResult(result, options = {}) {
|
|
|
64
82
|
}
|
|
65
83
|
stdout.write(result.healthy
|
|
66
84
|
? "\nGroundtrack local configuration is healthy. Live hook activation is verified by Groundtrack MCP calls.\n"
|
|
67
|
-
: `\nGroundtrack local configuration needs attention.${
|
|
85
|
+
: `\nGroundtrack local configuration needs attention.${fixHint}\n`);
|
|
68
86
|
}
|
|
69
87
|
async function confirmFix() {
|
|
70
88
|
const prompt = createInterface({ input: stdin, output: stdout });
|
|
@@ -132,6 +150,7 @@ async function main() {
|
|
|
132
150
|
const base = {
|
|
133
151
|
repoDir: args.repoDir,
|
|
134
152
|
homeDir: args.homeDir,
|
|
153
|
+
clients: args.client ? [args.client] : undefined,
|
|
135
154
|
skipPackageInstall: process.env.GROUNDTRACK_DOCTOR_SKIP_INSTALL === "1",
|
|
136
155
|
latestVersion: await latestPackageVersion(),
|
|
137
156
|
};
|
|
@@ -140,10 +159,13 @@ async function main() {
|
|
|
140
159
|
if (args.json)
|
|
141
160
|
stdout.write(`${JSON.stringify(diagnosis, null, 2)}\n`);
|
|
142
161
|
else
|
|
143
|
-
printResult(diagnosis, { showFixHint: true });
|
|
162
|
+
printResult(diagnosis, { showFixHint: true, client: args.client });
|
|
144
163
|
process.exitCode = diagnosis.healthy ? 0 : 1;
|
|
145
164
|
return;
|
|
146
165
|
}
|
|
166
|
+
if (!args.client && diagnosis.clients.length > 1) {
|
|
167
|
+
throw new Error(`Multiple coding-agent clients detected: ${diagnosis.clients.join(", ")}. Re-run with --client claude, --client codex, or --client cursor so Doctor does not modify unrelated client configuration.`);
|
|
168
|
+
}
|
|
147
169
|
if (diagnosis.healthy) {
|
|
148
170
|
if (args.json)
|
|
149
171
|
stdout.write(`${JSON.stringify(diagnosis, null, 2)}\n`);
|
package/package.json
CHANGED
package/templates/AGENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- groundtrack:start version=
|
|
1
|
+
<!-- groundtrack:start version=2 -->
|
|
2
2
|
|
|
3
3
|
## Groundtrack
|
|
4
4
|
|
|
@@ -8,6 +8,7 @@ Use Groundtrack as the shared engineering memory for this repository.
|
|
|
8
8
|
- When a relevant experience needs more context or verification, call `get_experience_events` with its id to inspect the underlying raw evidence and provenance.
|
|
9
9
|
- Apply relevant current experiences and heed any caveats that an experience was contradicted, superseded, or expired.
|
|
10
10
|
- Once the effect of a search is known, call `report_experience_use` with its retrieval id. Report when a result changed the approach or avoided a failure, and report `not_helpful` when none applied.
|
|
11
|
+
- After a task reaches a concrete result, call `record_outcome` with a short title, a concise summary, and the retrieval ids for searches that contributed. Include measurements only for sourced, quantified impact on time, cost, reliability, quality, performance, or throughput; tests, typechecks, lint, and builds are validation rather than impact. Include avoided work only when it was explicitly estimated, and never infer unknown values.
|
|
11
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.
|
|
12
13
|
- Do not store routine implementation details, obvious facts, unrestricted transcripts, or secrets.
|
|
13
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.
|