@guckdev/cli 0.1.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/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+ import { randomUUID } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { spawn } from "node:child_process";
6
+ import { loadConfig, resolveStoreDir, getDefaultConfig, findRepoRoot, resolveCheckpointPath, redactEvent, appendEvent, } from "@guckdev/core";
7
+ import { emit } from "@guckdev/sdk";
8
+ import { startMcpServer } from "@guckdev/mcp";
9
+ const printHelp = () => {
10
+ console.log(`Guck - MCP-first telemetry\n\nCommands:\n init Create .guck.json and .guck.local.json\n checkpoint Write a .guck-checkpoint epoch timestamp\n wrap --service <s> --session <id> -- <cmd...>\n Capture stdout/stderr and write JSONL\n emit --service <s> --session <id>\n Read JSON events from stdin and append\n mcp Start MCP server\n`);
11
+ };
12
+ const parseArgs = (argv) => {
13
+ const args = [...argv];
14
+ const opts = {};
15
+ const rest = [];
16
+ while (args.length > 0) {
17
+ const next = args.shift();
18
+ if (!next) {
19
+ break;
20
+ }
21
+ if (next === "--") {
22
+ rest.push(...args);
23
+ break;
24
+ }
25
+ if (next.startsWith("--")) {
26
+ const key = next.slice(2);
27
+ const value = args.shift();
28
+ if (!value) {
29
+ throw new Error(`Missing value for --${key}`);
30
+ }
31
+ opts[key] = value;
32
+ continue;
33
+ }
34
+ rest.push(next);
35
+ }
36
+ return { opts, rest };
37
+ };
38
+ const writeInitFiles = (rootDir) => {
39
+ const configPath = path.join(rootDir, ".guck.json");
40
+ const defaultConfig = getDefaultConfig();
41
+ const repoName = path.basename(rootDir);
42
+ const seededConfig = {
43
+ ...defaultConfig,
44
+ default_service: repoName || defaultConfig.default_service,
45
+ };
46
+ if (!fs.existsSync(configPath)) {
47
+ fs.writeFileSync(configPath, JSON.stringify(seededConfig, null, 2));
48
+ console.log(`Created ${configPath}`);
49
+ }
50
+ else {
51
+ console.log(`Config already exists: ${configPath}`);
52
+ }
53
+ };
54
+ const toLevel = (type) => type === "stderr" ? "error" : "info";
55
+ const buildEvent = (type, message, base) => {
56
+ return {
57
+ id: randomUUID(),
58
+ ts: new Date().toISOString(),
59
+ level: toLevel(type),
60
+ type,
61
+ service: base.service,
62
+ run_id: base.run_id,
63
+ session_id: base.session_id,
64
+ message,
65
+ source: { kind: type },
66
+ };
67
+ };
68
+ const captureStream = (stream, type, base, storeDir, config, forward) => {
69
+ let buffer = "";
70
+ stream.on("data", (chunk) => {
71
+ const text = chunk.toString();
72
+ forward.write(chunk);
73
+ buffer += text;
74
+ const lines = buffer.split(/\r?\n/);
75
+ buffer = lines.pop() ?? "";
76
+ for (const line of lines) {
77
+ if (line.trim().length === 0) {
78
+ continue;
79
+ }
80
+ const event = buildEvent(type, line, base);
81
+ const redacted = redactEvent(config, event);
82
+ void appendEvent(storeDir, redacted);
83
+ }
84
+ });
85
+ stream.on("end", () => {
86
+ if (buffer.trim().length > 0) {
87
+ const event = buildEvent(type, buffer, base);
88
+ const redacted = redactEvent(config, event);
89
+ void appendEvent(storeDir, redacted);
90
+ }
91
+ });
92
+ };
93
+ const handleWrap = async (argv) => {
94
+ const { opts, rest } = parseArgs(argv);
95
+ if (rest.length === 0 || !rest[0]) {
96
+ console.error("wrap requires a command after --");
97
+ return 1;
98
+ }
99
+ const { config, rootDir } = loadConfig();
100
+ if (!config.enabled) {
101
+ console.log("Guck disabled via config/env; running command without capture.");
102
+ const child = spawn(rest[0], rest.slice(1), { stdio: "inherit" });
103
+ return new Promise((resolve) => {
104
+ child.on("exit", (code) => resolve(code ?? 0));
105
+ });
106
+ }
107
+ const runId = process.env.GUCK_RUN_ID ?? randomUUID();
108
+ const service = opts.service ?? config.default_service;
109
+ const session = opts.session ?? process.env.GUCK_SESSION_ID;
110
+ const storeDir = resolveStoreDir(config, rootDir);
111
+ const child = spawn(rest[0], rest.slice(1), {
112
+ stdio: ["inherit", "pipe", "pipe"],
113
+ env: {
114
+ ...process.env,
115
+ GUCK_RUN_ID: runId,
116
+ GUCK_SERVICE: service,
117
+ GUCK_WRAPPED: "1",
118
+ ...(session ? { GUCK_SESSION_ID: session } : {}),
119
+ },
120
+ });
121
+ const base = { service, run_id: runId, session_id: session };
122
+ if (child.stdout) {
123
+ captureStream(child.stdout, "stdout", base, storeDir, config, process.stdout);
124
+ }
125
+ if (child.stderr) {
126
+ captureStream(child.stderr, "stderr", base, storeDir, config, process.stderr);
127
+ }
128
+ return new Promise((resolve) => {
129
+ child.on("exit", (code) => resolve(code ?? 0));
130
+ });
131
+ };
132
+ const parseJsonInput = (input) => {
133
+ const trimmed = input.trim();
134
+ if (!trimmed) {
135
+ return [];
136
+ }
137
+ try {
138
+ const parsed = JSON.parse(trimmed);
139
+ if (Array.isArray(parsed)) {
140
+ return parsed;
141
+ }
142
+ return [parsed];
143
+ }
144
+ catch {
145
+ const lines = trimmed.split(/\r?\n/).filter(Boolean);
146
+ const items = [];
147
+ for (const line of lines) {
148
+ items.push(JSON.parse(line));
149
+ }
150
+ return items;
151
+ }
152
+ };
153
+ const handleEmit = async (argv) => {
154
+ const { opts } = parseArgs(argv);
155
+ const input = await new Promise((resolve) => {
156
+ let buffer = "";
157
+ process.stdin.setEncoding("utf8");
158
+ process.stdin.on("data", (chunk) => {
159
+ buffer += chunk;
160
+ });
161
+ process.stdin.on("end", () => resolve(buffer));
162
+ });
163
+ let items = [];
164
+ try {
165
+ items = parseJsonInput(input);
166
+ }
167
+ catch (error) {
168
+ console.error("Failed to parse JSON input", error);
169
+ return 1;
170
+ }
171
+ for (const item of items) {
172
+ if (item && typeof item === "object") {
173
+ const event = item;
174
+ if (opts.service && !event.service) {
175
+ event.service = opts.service;
176
+ }
177
+ if (opts.session && !event.session_id) {
178
+ event.session_id = opts.session;
179
+ }
180
+ await emit(event);
181
+ }
182
+ }
183
+ return 0;
184
+ };
185
+ const main = async () => {
186
+ const argv = process.argv.slice(2);
187
+ const command = argv[0];
188
+ if (!command || command === "help" || command === "--help" || command === "-h") {
189
+ printHelp();
190
+ return;
191
+ }
192
+ if (command === "init") {
193
+ const root = findRepoRoot(process.cwd());
194
+ writeInitFiles(root);
195
+ return;
196
+ }
197
+ if (command === "wrap") {
198
+ const exitCode = await handleWrap(argv.slice(1));
199
+ process.exitCode = exitCode;
200
+ return;
201
+ }
202
+ if (command === "emit") {
203
+ const exitCode = await handleEmit(argv.slice(1));
204
+ process.exitCode = exitCode;
205
+ return;
206
+ }
207
+ if (command === "checkpoint") {
208
+ const { rootDir, config } = loadConfig();
209
+ const storeDir = resolveStoreDir(config, rootDir);
210
+ const checkpointPath = resolveCheckpointPath(storeDir);
211
+ const value = Date.now();
212
+ fs.writeFileSync(checkpointPath, `${value}\n`, "utf8");
213
+ process.stdout.write(`${value}\n`);
214
+ return;
215
+ }
216
+ if (command === "mcp") {
217
+ await startMcpServer();
218
+ return;
219
+ }
220
+ console.error(`Unknown command: ${command}`);
221
+ printHelp();
222
+ process.exitCode = 1;
223
+ };
224
+ main().catch((error) => {
225
+ console.error(error);
226
+ process.exitCode = 1;
227
+ });
228
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@guckdev/cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for Guck telemetry capture and MCP",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "guck": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "scripts",
13
+ "src",
14
+ "tsconfig.json"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "scripts": {
20
+ "build": "node scripts/ensure-deps.mjs && tsc -p tsconfig.json",
21
+ "build:watch": "tsc -p tsconfig.json --watch",
22
+ "dev": "node --watch dist/cli.js",
23
+ "prepare": "pnpm run build",
24
+ "test": "pnpm run build"
25
+ },
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "dependencies": {
30
+ "@guckdev/sdk": "workspace:*",
31
+ "@guckdev/core": "workspace:*",
32
+ "@guckdev/mcp": "workspace:*"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "25.2.2"
36
+ }
37
+ }
@@ -0,0 +1,30 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+
5
+ const deps = [
6
+ { name: "@guckdev/core", entry: "dist/index.js" },
7
+ { name: "@guckdev/sdk", entry: "dist/index.js" },
8
+ { name: "@guckdev/mcp", entry: "dist/index.js" },
9
+ ];
10
+
11
+ const npmExecPath = process.env.npm_execpath;
12
+ const run = (cwd, args) => {
13
+ const cmd = npmExecPath ? process.execPath : "pnpm";
14
+ const cmdArgs = npmExecPath ? [npmExecPath, ...args] : args;
15
+ const result = spawnSync(cmd, cmdArgs, { cwd, stdio: "inherit" });
16
+ if (result.status !== 0) {
17
+ process.exit(result.status ?? 1);
18
+ }
19
+ };
20
+
21
+ for (const dep of deps) {
22
+ const depDir = path.join(process.cwd(), "node_modules", dep.name);
23
+ if (!existsSync(depDir)) {
24
+ continue;
25
+ }
26
+ const entryPath = path.join(depDir, dep.entry);
27
+ if (!existsSync(entryPath)) {
28
+ run(depDir, ["run", "build"]);
29
+ }
30
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+ import { randomUUID } from "node:crypto";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { spawn } from "node:child_process";
6
+ import {
7
+ loadConfig,
8
+ resolveStoreDir,
9
+ getDefaultConfig,
10
+ findRepoRoot,
11
+ resolveCheckpointPath,
12
+ redactEvent,
13
+ appendEvent,
14
+ } from "@guckdev/core";
15
+ import { GuckEvent, GuckLevel } from "@guckdev/core";
16
+ import { emit } from "@guckdev/sdk";
17
+ import { startMcpServer } from "@guckdev/mcp";
18
+
19
+ const printHelp = (): void => {
20
+ console.log(`Guck - MCP-first telemetry\n\nCommands:\n init Create .guck.json and .guck.local.json\n checkpoint Write a .guck-checkpoint epoch timestamp\n wrap --service <s> --session <id> -- <cmd...>\n Capture stdout/stderr and write JSONL\n emit --service <s> --session <id>\n Read JSON events from stdin and append\n mcp Start MCP server\n`);
21
+ };
22
+
23
+ const parseArgs = (argv: string[]) => {
24
+ const args = [...argv];
25
+ const opts: Record<string, string> = {};
26
+ const rest: string[] = [];
27
+ while (args.length > 0) {
28
+ const next = args.shift();
29
+ if (!next) {
30
+ break;
31
+ }
32
+ if (next === "--") {
33
+ rest.push(...args);
34
+ break;
35
+ }
36
+ if (next.startsWith("--")) {
37
+ const key = next.slice(2);
38
+ const value = args.shift();
39
+ if (!value) {
40
+ throw new Error(`Missing value for --${key}`);
41
+ }
42
+ opts[key] = value;
43
+ continue;
44
+ }
45
+ rest.push(next);
46
+ }
47
+ return { opts, rest };
48
+ };
49
+
50
+ const writeInitFiles = (rootDir: string): void => {
51
+ const configPath = path.join(rootDir, ".guck.json");
52
+ const defaultConfig = getDefaultConfig();
53
+ const repoName = path.basename(rootDir);
54
+ const seededConfig = {
55
+ ...defaultConfig,
56
+ default_service: repoName || defaultConfig.default_service,
57
+ };
58
+
59
+ if (!fs.existsSync(configPath)) {
60
+ fs.writeFileSync(configPath, JSON.stringify(seededConfig, null, 2));
61
+ console.log(`Created ${configPath}`);
62
+ } else {
63
+ console.log(`Config already exists: ${configPath}`);
64
+ }
65
+ };
66
+
67
+ const toLevel = (type: "stdout" | "stderr"): GuckLevel =>
68
+ type === "stderr" ? "error" : "info";
69
+
70
+ const buildEvent = (
71
+ type: "stdout" | "stderr",
72
+ message: string,
73
+ base: Pick<GuckEvent, "service" | "run_id" | "session_id">,
74
+ ): GuckEvent => {
75
+ return {
76
+ id: randomUUID(),
77
+ ts: new Date().toISOString(),
78
+ level: toLevel(type),
79
+ type,
80
+ service: base.service,
81
+ run_id: base.run_id,
82
+ session_id: base.session_id,
83
+ message,
84
+ source: { kind: type },
85
+ };
86
+ };
87
+
88
+ const captureStream = (
89
+ stream: NodeJS.ReadableStream,
90
+ type: "stdout" | "stderr",
91
+ base: Pick<GuckEvent, "service" | "run_id" | "session_id">,
92
+ storeDir: string,
93
+ config: ReturnType<typeof loadConfig>["config"],
94
+ forward: NodeJS.WriteStream,
95
+ ) => {
96
+ let buffer = "";
97
+ stream.on("data", (chunk: Buffer) => {
98
+ const text = chunk.toString();
99
+ forward.write(chunk);
100
+ buffer += text;
101
+ const lines = buffer.split(/\r?\n/);
102
+ buffer = lines.pop() ?? "";
103
+ for (const line of lines) {
104
+ if (line.trim().length === 0) {
105
+ continue;
106
+ }
107
+ const event = buildEvent(type, line, base);
108
+ const redacted = redactEvent(config, event);
109
+ void appendEvent(storeDir, redacted);
110
+ }
111
+ });
112
+ stream.on("end", () => {
113
+ if (buffer.trim().length > 0) {
114
+ const event = buildEvent(type, buffer, base);
115
+ const redacted = redactEvent(config, event);
116
+ void appendEvent(storeDir, redacted);
117
+ }
118
+ });
119
+ };
120
+
121
+ const handleWrap = async (argv: string[]): Promise<number> => {
122
+ const { opts, rest } = parseArgs(argv);
123
+ if (rest.length === 0 || !rest[0]) {
124
+ console.error("wrap requires a command after --");
125
+ return 1;
126
+ }
127
+
128
+ const { config, rootDir } = loadConfig();
129
+ if (!config.enabled) {
130
+ console.log("Guck disabled via config/env; running command without capture.");
131
+ const child = spawn(rest[0], rest.slice(1), { stdio: "inherit" });
132
+ return new Promise((resolve) => {
133
+ child.on("exit", (code: number | null) => resolve(code ?? 0));
134
+ });
135
+ }
136
+
137
+ const runId = process.env.GUCK_RUN_ID ?? randomUUID();
138
+ const service = opts.service ?? config.default_service;
139
+ const session = opts.session ?? process.env.GUCK_SESSION_ID;
140
+ const storeDir = resolveStoreDir(config, rootDir);
141
+
142
+ const child = spawn(rest[0], rest.slice(1), {
143
+ stdio: ["inherit", "pipe", "pipe"],
144
+ env: {
145
+ ...process.env,
146
+ GUCK_RUN_ID: runId,
147
+ GUCK_SERVICE: service,
148
+ GUCK_WRAPPED: "1",
149
+ ...(session ? { GUCK_SESSION_ID: session } : {}),
150
+ },
151
+ });
152
+
153
+ const base = { service, run_id: runId, session_id: session };
154
+ if (child.stdout) {
155
+ captureStream(child.stdout, "stdout", base, storeDir, config, process.stdout);
156
+ }
157
+ if (child.stderr) {
158
+ captureStream(child.stderr, "stderr", base, storeDir, config, process.stderr);
159
+ }
160
+
161
+ return new Promise((resolve) => {
162
+ child.on("exit", (code: number | null) => resolve(code ?? 0));
163
+ });
164
+ };
165
+
166
+ const parseJsonInput = (input: string): unknown[] => {
167
+ const trimmed = input.trim();
168
+ if (!trimmed) {
169
+ return [];
170
+ }
171
+ try {
172
+ const parsed = JSON.parse(trimmed);
173
+ if (Array.isArray(parsed)) {
174
+ return parsed;
175
+ }
176
+ return [parsed];
177
+ } catch {
178
+ const lines = trimmed.split(/\r?\n/).filter(Boolean);
179
+ const items: unknown[] = [];
180
+ for (const line of lines) {
181
+ items.push(JSON.parse(line));
182
+ }
183
+ return items;
184
+ }
185
+ };
186
+
187
+ const handleEmit = async (argv: string[]): Promise<number> => {
188
+ const { opts } = parseArgs(argv);
189
+ const input = await new Promise<string>((resolve) => {
190
+ let buffer = "";
191
+ process.stdin.setEncoding("utf8");
192
+ process.stdin.on("data", (chunk: string) => {
193
+ buffer += chunk;
194
+ });
195
+ process.stdin.on("end", () => resolve(buffer));
196
+ });
197
+
198
+ let items: unknown[] = [];
199
+ try {
200
+ items = parseJsonInput(input);
201
+ } catch (error) {
202
+ console.error("Failed to parse JSON input", error);
203
+ return 1;
204
+ }
205
+
206
+ for (const item of items) {
207
+ if (item && typeof item === "object") {
208
+ const event = item as Partial<GuckEvent>;
209
+ if (opts.service && !event.service) {
210
+ event.service = opts.service;
211
+ }
212
+ if (opts.session && !event.session_id) {
213
+ event.session_id = opts.session;
214
+ }
215
+ await emit(event);
216
+ }
217
+ }
218
+
219
+ return 0;
220
+ };
221
+
222
+ const main = async (): Promise<void> => {
223
+ const argv = process.argv.slice(2);
224
+ const command = argv[0];
225
+
226
+ if (!command || command === "help" || command === "--help" || command === "-h") {
227
+ printHelp();
228
+ return;
229
+ }
230
+
231
+ if (command === "init") {
232
+ const root = findRepoRoot(process.cwd());
233
+ writeInitFiles(root);
234
+ return;
235
+ }
236
+
237
+ if (command === "wrap") {
238
+ const exitCode = await handleWrap(argv.slice(1));
239
+ process.exitCode = exitCode;
240
+ return;
241
+ }
242
+
243
+ if (command === "emit") {
244
+ const exitCode = await handleEmit(argv.slice(1));
245
+ process.exitCode = exitCode;
246
+ return;
247
+ }
248
+
249
+ if (command === "checkpoint") {
250
+ const { rootDir, config } = loadConfig();
251
+ const storeDir = resolveStoreDir(config, rootDir);
252
+ const checkpointPath = resolveCheckpointPath(storeDir);
253
+ const value = Date.now();
254
+ fs.writeFileSync(checkpointPath, `${value}\n`, "utf8");
255
+ process.stdout.write(`${value}\n`);
256
+ return;
257
+ }
258
+
259
+ if (command === "mcp") {
260
+ await startMcpServer();
261
+ return;
262
+ }
263
+
264
+ console.error(`Unknown command: ${command}`);
265
+ printHelp();
266
+ process.exitCode = 1;
267
+ };
268
+
269
+ main().catch((error) => {
270
+ console.error(error);
271
+ process.exitCode = 1;
272
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "rootDir": "src",
7
+ "outDir": "dist",
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "sourceMap": true,
11
+ "strict": true,
12
+ "noUncheckedIndexedAccess": true,
13
+ "esModuleInterop": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "skipLibCheck": true,
16
+ "types": ["node"]
17
+ },
18
+ "include": ["src/**/*.ts"]
19
+ }