@blastin-dev/clocktopus-cli 0.1.4 → 0.2.1

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 (64) hide show
  1. package/README.md +163 -5
  2. package/dist/src/commands/agent/disable.d.ts +21 -0
  3. package/dist/src/commands/agent/disable.d.ts.map +1 -0
  4. package/dist/src/commands/agent/disable.js +106 -0
  5. package/dist/src/commands/agent/doctor.d.ts +2 -0
  6. package/dist/src/commands/agent/doctor.d.ts.map +1 -0
  7. package/dist/src/commands/agent/doctor.js +306 -0
  8. package/dist/src/commands/agent/hook.d.ts +5 -0
  9. package/dist/src/commands/agent/hook.d.ts.map +1 -0
  10. package/dist/src/commands/agent/hook.js +368 -0
  11. package/dist/src/commands/agent/setup.d.ts +28 -0
  12. package/dist/src/commands/agent/setup.d.ts.map +1 -0
  13. package/dist/src/commands/agent/setup.js +333 -0
  14. package/dist/src/commands/agent/status.d.ts +2 -0
  15. package/dist/src/commands/agent/status.d.ts.map +1 -0
  16. package/dist/src/commands/agent/status.js +182 -0
  17. package/dist/src/index.d.ts.map +1 -1
  18. package/dist/src/index.js +51 -2
  19. package/dist/src/lib/agent-config.d.ts +56 -0
  20. package/dist/src/lib/agent-config.d.ts.map +1 -0
  21. package/dist/src/lib/agent-config.js +168 -0
  22. package/dist/src/lib/agent-hook-state.d.ts +44 -0
  23. package/dist/src/lib/agent-hook-state.d.ts.map +1 -0
  24. package/dist/src/lib/agent-hook-state.js +155 -0
  25. package/dist/src/lib/agent-receiver.d.ts +26 -0
  26. package/dist/src/lib/agent-receiver.d.ts.map +1 -0
  27. package/dist/src/lib/agent-receiver.js +44 -0
  28. package/dist/src/lib/agents.d.ts +115 -0
  29. package/dist/src/lib/agents.d.ts.map +1 -0
  30. package/dist/src/lib/agents.js +245 -0
  31. package/dist/src/lib/claude-settings.d.ts +82 -0
  32. package/dist/src/lib/claude-settings.d.ts.map +1 -0
  33. package/dist/src/lib/claude-settings.js +271 -0
  34. package/dist/src/lib/claude-settings.test.d.ts +2 -0
  35. package/dist/src/lib/claude-settings.test.d.ts.map +1 -0
  36. package/dist/src/lib/claude-settings.test.js +193 -0
  37. package/dist/src/lib/codex-config.d.ts +166 -0
  38. package/dist/src/lib/codex-config.d.ts.map +1 -0
  39. package/dist/src/lib/codex-config.js +441 -0
  40. package/dist/src/lib/codex-config.test.d.ts +2 -0
  41. package/dist/src/lib/codex-config.test.d.ts.map +1 -0
  42. package/dist/src/lib/codex-config.test.js +359 -0
  43. package/dist/src/lib/config.d.ts +23 -0
  44. package/dist/src/lib/config.d.ts.map +1 -1
  45. package/dist/src/lib/config.js +14 -0
  46. package/dist/src/lib/format.d.ts +6 -0
  47. package/dist/src/lib/format.d.ts.map +1 -0
  48. package/dist/src/lib/format.js +19 -0
  49. package/dist/src/lib/git.d.ts +3 -0
  50. package/dist/src/lib/git.d.ts.map +1 -0
  51. package/dist/src/lib/git.js +30 -0
  52. package/dist/src/lib/opencode-config.d.ts +108 -0
  53. package/dist/src/lib/opencode-config.d.ts.map +1 -0
  54. package/dist/src/lib/opencode-config.js +330 -0
  55. package/dist/src/lib/opencode-config.test.d.ts +2 -0
  56. package/dist/src/lib/opencode-config.test.d.ts.map +1 -0
  57. package/dist/src/lib/opencode-config.test.js +140 -0
  58. package/dist/src/lib/repo-guidance.d.ts +40 -0
  59. package/dist/src/lib/repo-guidance.d.ts.map +1 -0
  60. package/dist/src/lib/repo-guidance.js +123 -0
  61. package/dist/src/lib/validators.d.ts +69 -0
  62. package/dist/src/lib/validators.d.ts.map +1 -1
  63. package/dist/src/lib/validators.js +69 -0
  64. package/package.json +7 -4
@@ -0,0 +1,193 @@
1
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { applyTelemetrySettings, buildTelemetryEnv, readInstalledTelemetry, readSettings, removeTelemetrySettings, SettingsParseError, writeSettings, } from "./claude-settings";
6
+ /**
7
+ * These tests exist because this module edits a file we do not own.
8
+ * `~/.claude/settings.json` holds the user's model choice, their own hooks,
9
+ * their permissions and their MCP servers, and a merge bug here destroys
10
+ * work that has nothing to do with us.
11
+ */
12
+ const INSTALL = {
13
+ token: "ctop_agt_testtoken",
14
+ endpoint: "https://otel.example.com",
15
+ hookCommand: "clocktopus agent hook",
16
+ };
17
+ /** A settings file with the user's own configuration already in it. */
18
+ function userSettings() {
19
+ return {
20
+ model: "opus",
21
+ env: { MY_OWN_VAR: "keep-me" },
22
+ hooks: {
23
+ SessionStart: [
24
+ { hooks: [{ type: "command", command: "echo user-session-start" }] },
25
+ ],
26
+ PreToolUse: [
27
+ {
28
+ matcher: "Bash",
29
+ hooks: [{ type: "command", command: "echo bash-guard" }],
30
+ },
31
+ ],
32
+ },
33
+ };
34
+ }
35
+ describe("applyTelemetrySettings", () => {
36
+ it("leaves everything it did not put there alone", () => {
37
+ const next = applyTelemetrySettings(userSettings(), INSTALL);
38
+ expect(next.model).toBe("opus");
39
+ expect(next.env.MY_OWN_VAR).toBe("keep-me");
40
+ expect(next.hooks.PreToolUse).toEqual(userSettings().hooks.PreToolUse);
41
+ });
42
+ it("keeps the user's own SessionStart hook alongside ours", () => {
43
+ const next = applyTelemetrySettings(userSettings(), INSTALL);
44
+ const commands = (next.hooks.SessionStart ?? []).flatMap((matcher) => matcher.hooks.map((entry) => entry.command));
45
+ expect(commands).toContain("echo user-session-start");
46
+ expect(commands).toContain("clocktopus agent hook");
47
+ });
48
+ it("installs both events — one without the other loses attribution", () => {
49
+ // SessionStart is the only source of `cwd` and the *before* SHA;
50
+ // SessionEnd is the only source of the exact commit list.
51
+ const next = applyTelemetrySettings({}, INSTALL);
52
+ const hooks = next.hooks;
53
+ expect(hooks.SessionStart).toBeDefined();
54
+ expect(hooks.SessionEnd).toBeDefined();
55
+ });
56
+ it("is idempotent — re-running setup does not fire the hook twice", () => {
57
+ const once = applyTelemetrySettings(userSettings(), INSTALL);
58
+ const twice = applyTelemetrySettings(once, INSTALL);
59
+ const ours = (twice.hooks.SessionStart ?? [])
60
+ .flatMap((matcher) => matcher.hooks)
61
+ .filter((entry) => entry.command === "clocktopus agent hook");
62
+ expect(ours).toHaveLength(1);
63
+ });
64
+ it("runs the hook out of band, so telemetry never delays session start", () => {
65
+ const next = applyTelemetrySettings({}, INSTALL);
66
+ const entry = next.hooks.SessionStart[0].hooks[0];
67
+ expect(entry.async).toBe(true);
68
+ // Above the hook's own 4s request timeout, so a slow network leaves the
69
+ // hook's recorded failure behind instead of being killed without trace.
70
+ expect(entry.timeout).toBe(10);
71
+ });
72
+ it("replaces the pre-CLI script hook instead of firing both", () => {
73
+ // Anyone who wired this up before the CLI existed points at a script
74
+ // inside a checkout of this repo. Leaving it installed alongside the new
75
+ // hook would POST every SessionStart twice.
76
+ const legacy = {
77
+ hooks: {
78
+ SessionStart: [
79
+ {
80
+ hooks: [
81
+ {
82
+ type: "command",
83
+ command: "node /home/me/Projects/clocktopus/scripts/agent-telemetry/claude-hook.mjs",
84
+ timeout: 10,
85
+ async: true,
86
+ },
87
+ ],
88
+ },
89
+ ],
90
+ },
91
+ };
92
+ const next = applyTelemetrySettings(legacy, INSTALL);
93
+ const commands = next.hooks.SessionStart.flatMap((matcher) => matcher.hooks.map((entry) => entry.command));
94
+ expect(commands).toEqual(["clocktopus agent hook"]);
95
+ });
96
+ it("replaces our hook when the command changes, rather than adding one", () => {
97
+ const first = applyTelemetrySettings({}, INSTALL);
98
+ const moved = applyTelemetrySettings(first, {
99
+ ...INSTALL,
100
+ hookCommand: "/usr/local/bin/node /opt/clocktopus/cli.js agent hook",
101
+ });
102
+ const commands = (moved.hooks.SessionEnd ?? []).flatMap((matcher) => matcher.hooks.map((entry) => entry.command));
103
+ expect(commands).toEqual([
104
+ "/usr/local/bin/node /opt/clocktopus/cli.js agent hook",
105
+ ]);
106
+ });
107
+ });
108
+ describe("buildTelemetryEnv", () => {
109
+ it("configures the exporter the only way the receiver accepts", () => {
110
+ const env = buildTelemetryEnv(INSTALL);
111
+ // Protobuf gets an explicit 415 from the receiver.
112
+ expect(env.OTEL_EXPORTER_OTLP_PROTOCOL).toBe("http/json");
113
+ expect(env.OTEL_EXPORTER_OTLP_HEADERS).toBe("Authorization=Bearer ctop_agt_testtoken");
114
+ // Without the session id the metric stream cannot be joined to the hook
115
+ // stream, and every session loses its repository and branch.
116
+ expect(env.OTEL_METRICS_INCLUDE_SESSION_ID).toBe("true");
117
+ });
118
+ it("normalises a trailing slash so paths do not double up", () => {
119
+ const env = buildTelemetryEnv({
120
+ ...INSTALL,
121
+ endpoint: "https://otel.example.com/",
122
+ });
123
+ expect(env.OTEL_EXPORTER_OTLP_ENDPOINT).toBe("https://otel.example.com");
124
+ expect(env.CLOCKTOPUS_OTEL_ENDPOINT).toBe("https://otel.example.com");
125
+ });
126
+ });
127
+ describe("removeTelemetrySettings", () => {
128
+ it("removes only what setup added", () => {
129
+ const installed = applyTelemetrySettings(userSettings(), INSTALL);
130
+ const { settings, removedEnvKeys, removedHooks } = removeTelemetrySettings(installed);
131
+ expect(removedHooks).toBe(true);
132
+ expect(removedEnvKeys).toContain("CLOCKTOPUS_INGEST_TOKEN");
133
+ expect(settings.model).toBe("opus");
134
+ expect(settings.env.MY_OWN_VAR).toBe("keep-me");
135
+ expect(settings.env.CLOCKTOPUS_INGEST_TOKEN).toBeUndefined();
136
+ const sessionStart = settings.hooks.SessionStart;
137
+ expect(sessionStart?.flatMap((m) => m.hooks.map((h) => h.command))).toEqual(["echo user-session-start"]);
138
+ expect(settings.hooks.PreToolUse).toBeDefined();
139
+ });
140
+ it("drops the env and hooks keys entirely when nothing else used them", () => {
141
+ const installed = applyTelemetrySettings({ model: "opus" }, INSTALL);
142
+ const { settings } = removeTelemetrySettings(installed);
143
+ expect(settings.env).toBeUndefined();
144
+ expect(settings.hooks).toBeUndefined();
145
+ expect(settings.model).toBe("opus");
146
+ });
147
+ it("reports nothing removed when telemetry was never installed", () => {
148
+ const { removedEnvKeys, removedHooks } = removeTelemetrySettings(userSettings());
149
+ expect(removedEnvKeys).toEqual([]);
150
+ expect(removedHooks).toBe(false);
151
+ });
152
+ });
153
+ describe("reading and writing the file", () => {
154
+ let dir;
155
+ let path;
156
+ beforeEach(() => {
157
+ dir = mkdtempSync(join(tmpdir(), "clocktopus-settings-"));
158
+ path = join(dir, "settings.json");
159
+ });
160
+ afterEach(() => {
161
+ rmSync(dir, { recursive: true, force: true });
162
+ });
163
+ it("refuses to read malformed JSON rather than overwrite it", () => {
164
+ // A broken settings file is far more likely to be a half-finished edit
165
+ // than something to clobber.
166
+ writeFileSync(path, '{ "model": "opus",,, }', "utf8");
167
+ expect(() => readSettings(path)).toThrow(SettingsParseError);
168
+ });
169
+ it("treats a missing file as empty settings", () => {
170
+ const result = readSettings(path);
171
+ expect(result.exists).toBe(false);
172
+ expect(result.settings).toEqual({});
173
+ });
174
+ it("backs up the previous file before replacing it", () => {
175
+ writeFileSync(path, JSON.stringify(userSettings()), "utf8");
176
+ const { backupPath } = writeSettings(applyTelemetrySettings(readSettings(path).settings, INSTALL), path);
177
+ expect(backupPath).toBe(`${path}.clocktopus-backup`);
178
+ expect(JSON.parse(readFileSync(backupPath, "utf8"))).toEqual(userSettings());
179
+ });
180
+ it("round-trips what setup installed", () => {
181
+ writeSettings(applyTelemetrySettings(userSettings(), INSTALL), path);
182
+ const installed = readInstalledTelemetry(path);
183
+ expect(installed.env.CLOCKTOPUS_INGEST_TOKEN).toBe("ctop_agt_testtoken");
184
+ expect(installed.hookCommands.SessionStart).toBe("clocktopus agent hook");
185
+ expect(installed.hookCommands.SessionEnd).toBe("clocktopus agent hook");
186
+ });
187
+ it("reports no telemetry for a settings file that only has the user's own hooks", () => {
188
+ writeFileSync(path, JSON.stringify(userSettings()), "utf8");
189
+ const installed = readInstalledTelemetry(path);
190
+ expect(installed.env).toEqual({});
191
+ expect(installed.hookCommands.SessionStart).toBeUndefined();
192
+ });
193
+ });
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Reads and edits Codex CLI's configuration on the user's behalf.
3
+ *
4
+ * Codex splits what Claude Code keeps in one file: telemetry goes in
5
+ * `~/.codex/config.toml` under `[otel]`, hooks go in a separate
6
+ * `~/.codex/hooks.json`. Both are edited here under the same two rules as
7
+ * `claude-settings.ts` — never write over something we could not parse, and
8
+ * only ever touch keys we put there — plus a third that TOML forces on us:
9
+ *
10
+ * **Splice, don't re-serialise.** A round trip through a TOML parser throws
11
+ * away every comment and blank line in the file. `config.toml` is a file
12
+ * people hand-write and annotate, so only the `[otel]` region is replaced
13
+ * textually; everything else survives byte for byte. `writeCodexConfig`
14
+ * re-parses the spliced result and refuses to write if anything outside
15
+ * `[otel]` moved, which is what makes the textual approach safe.
16
+ *
17
+ * ## What Codex does with these, and why it matters here
18
+ *
19
+ * - `otel.exporter` is the **log** exporter, and Codex POSTs to the URL
20
+ * verbatim — it does not append `/v1/logs` the way the OTel SDK does. The
21
+ * path is therefore part of the configured endpoint, and
22
+ * `readCodexTelemetry` strips it back off to recover the receiver base.
23
+ * - Codex will not run a `hooks.json` it has not been shown: the first
24
+ * session after this file changes prompts "Hooks need review" and
25
+ * persists the answer under `[hooks.state]`. Setup cannot do that for the
26
+ * user, so it tells them instead — see `setup.ts`.
27
+ */
28
+ export declare const CODEX_CONFIG_FILENAME = "config.toml";
29
+ export declare const CODEX_HOOKS_FILENAME = "hooks.json";
30
+ /**
31
+ * The receiver path Codex's log exporter is pointed at.
32
+ *
33
+ * Load-bearing in both directions: written onto the endpoint because Codex
34
+ * sends to the literal URL, and stripped when reading because the hook and
35
+ * `/v1/verify` need the base.
36
+ */
37
+ export declare const CODEX_LOGS_PATH = "/v1/logs";
38
+ /**
39
+ * Seconds Codex will wait for each hook.
40
+ *
41
+ * SessionEnd is 3 rather than 10 because Codex hard-clamps it to 3 and
42
+ * prints `warning: clamping SessionEnd hook timeout to 3s` on every single
43
+ * session start otherwise — a permanent warning about a value we chose is
44
+ * worse than the shorter budget, and the hook's own request timeout is 4s.
45
+ */
46
+ export declare const CODEX_HOOK_TIMEOUT_SECONDS: {
47
+ SessionStart: number;
48
+ SessionEnd: number;
49
+ };
50
+ declare const HOOK_EVENTS: readonly ["SessionStart", "SessionEnd"];
51
+ /** Exported so callers can tell "none approved" from "some approved". */
52
+ export declare const HOOK_EVENT_COUNT: 2;
53
+ type HookEvent = (typeof HOOK_EVENTS)[number];
54
+ type TomlTable = Record<string, unknown>;
55
+ export declare class CodexConfigParseError extends Error {
56
+ readonly path: string;
57
+ constructor(path: string, detail: string);
58
+ }
59
+ export declare function codexConfigDir(): string;
60
+ export declare function codexConfigPath(): string;
61
+ export declare function codexHooksPath(): string;
62
+ export declare function readCodexConfig(path?: string): {
63
+ path: string;
64
+ exists: boolean;
65
+ modifiedAt: Date | null;
66
+ text: string;
67
+ config: TomlTable;
68
+ };
69
+ export declare function buildCodexOtel(input: {
70
+ token: string;
71
+ endpoint: string;
72
+ }): TomlTable;
73
+ /**
74
+ * Replaces the `[otel]` region of a TOML document, preserving everything else.
75
+ *
76
+ * Returns the new text. Region detection is deliberately dumb — table
77
+ * headers only — and `writeCodexConfig` verifies the result by re-parsing,
78
+ * so a document this misreads is refused rather than mangled.
79
+ */
80
+ export declare function spliceOtelSection(text: string, otel: TomlTable | null): string;
81
+ /**
82
+ * Writes config.toml, keeping a one-deep backup and verifying the splice.
83
+ *
84
+ * The verification is the point: it re-parses what is about to be written
85
+ * and compares every top-level key *except* `otel` against what was there
86
+ * before. If the textual splice disturbed anything else — an exotic layout
87
+ * the region scanner misread — the write is refused with the user's file
88
+ * still intact.
89
+ */
90
+ export declare function writeCodexConfig(input: {
91
+ path?: string;
92
+ previousText: string;
93
+ previousConfig: TomlTable;
94
+ nextText: string;
95
+ }): {
96
+ backupPath: string | null;
97
+ };
98
+ /** What config.toml currently declares, for `status` and `doctor`. */
99
+ export declare function readCodexTelemetry(path?: string): {
100
+ path: string;
101
+ exists: boolean;
102
+ modifiedAt: Date | null;
103
+ token: string | null;
104
+ /** The receiver base, with `/v1/logs` stripped back off. */
105
+ endpoint: string | null;
106
+ protocol: string | null;
107
+ logUserPrompt: boolean | null;
108
+ };
109
+ export declare function applyCodexOtel(current: {
110
+ text: string;
111
+ config: TomlTable;
112
+ }, input: {
113
+ token: string;
114
+ endpoint: string;
115
+ }): string;
116
+ export declare function removeCodexOtel(current: {
117
+ text: string;
118
+ config: TomlTable;
119
+ }): {
120
+ nextText: string;
121
+ removedKeys: string[];
122
+ };
123
+ export declare function readCodexHooks(path?: string): {
124
+ path: string;
125
+ exists: boolean;
126
+ modifiedAt: Date | null;
127
+ file: Record<string, unknown>;
128
+ commands: Partial<Record<HookEvent, string>>;
129
+ };
130
+ export declare function applyCodexHooks(file: Record<string, unknown>, input: {
131
+ hookCommand: string;
132
+ }): Record<string, unknown>;
133
+ export declare function removeCodexHooks(file: Record<string, unknown>): {
134
+ file: Record<string, unknown>;
135
+ removed: boolean;
136
+ };
137
+ export declare function writeCodexHooks(file: Record<string, unknown>, path?: string): {
138
+ backupPath: string | null;
139
+ };
140
+ /**
141
+ * Which of our hooks Codex has been shown and told to run.
142
+ *
143
+ * Trust is recorded **per hook entry**, not per file:
144
+ *
145
+ * ```toml
146
+ * [hooks.state."/home/you/.codex/hooks.json:session_end:0:0"]
147
+ * trusted_hash = "sha256:…"
148
+ * ```
149
+ *
150
+ * The key is `<path>:<event>:<group index>:<hook index>`, with the event in
151
+ * snake_case. Per-entry granularity is why this reports each event rather
152
+ * than a single boolean: partial trust is a real state, and the one that
153
+ * matters. A machine whose `SessionEnd` is trusted and whose `SessionStart`
154
+ * is not records spend with no repository attached to it — which is exactly
155
+ * what an `async: true` SessionStart used to produce on Codex 0.147, since
156
+ * the hook was skipped before it could ever be offered for approval.
157
+ *
158
+ * The hash is Codex's own and is not recomputed here; changing `hooks.json`
159
+ * invalidates it and Codex re-prompts, which is the behaviour we want.
160
+ */
161
+ export declare function readCodexHookTrust(path?: string): {
162
+ trusted: HookEvent[];
163
+ untrusted: HookEvent[];
164
+ };
165
+ export {};
166
+ //# sourceMappingURL=codex-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-config.d.ts","sourceRoot":"","sources":["../../../src/lib/codex-config.ts"],"names":[],"mappings":"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"}