@thinkingpatterns/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/index.js ADDED
@@ -0,0 +1,432 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/lib/client.ts
7
+ import { ThinkingPatternsClient } from "@thinkingpatterns/core";
8
+
9
+ // src/lib/credentials.ts
10
+ import { readFileSync, writeFileSync, mkdirSync, rmSync, existsSync } from "fs";
11
+ import { join } from "path";
12
+ import { homedir } from "os";
13
+ var CONFIG_DIR = join(homedir(), ".thinkingpatterns");
14
+ var CREDENTIALS_FILE = join(CONFIG_DIR, "credentials.json");
15
+ function loadCredentials() {
16
+ if (!existsSync(CREDENTIALS_FILE)) return null;
17
+ try {
18
+ const content = readFileSync(CREDENTIALS_FILE, "utf-8");
19
+ return JSON.parse(content);
20
+ } catch {
21
+ return null;
22
+ }
23
+ }
24
+ function saveCredentials(apiKey, apiUrl) {
25
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
26
+ const data = { api_key: apiKey };
27
+ if (apiUrl) data.api_url = apiUrl;
28
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(data, null, 2) + "\n", {
29
+ mode: 384
30
+ });
31
+ }
32
+ function clearCredentials() {
33
+ if (!existsSync(CREDENTIALS_FILE)) return false;
34
+ rmSync(CREDENTIALS_FILE);
35
+ return true;
36
+ }
37
+
38
+ // src/lib/client.ts
39
+ var DEFAULT_API_URL = "https://thinkingpatterns.ai";
40
+ var _client = null;
41
+ var _opts = {};
42
+ function setGlobalOpts(opts) {
43
+ _opts = opts;
44
+ _client = null;
45
+ }
46
+ function resolveApiKey() {
47
+ if (_opts.apiKey) return _opts.apiKey;
48
+ if (process.env.TP_API_KEY) return process.env.TP_API_KEY;
49
+ return loadCredentials()?.api_key;
50
+ }
51
+ function resolveApiUrl() {
52
+ return _opts.apiUrl || process.env.TP_API_URL || loadCredentials()?.api_url || DEFAULT_API_URL;
53
+ }
54
+ function getClient() {
55
+ if (!_client) {
56
+ const apiKey = resolveApiKey() || "";
57
+ const apiUrl = resolveApiUrl();
58
+ _client = new ThinkingPatternsClient({ apiKey, apiUrl });
59
+ }
60
+ return _client;
61
+ }
62
+
63
+ // src/commands/login.ts
64
+ import { createInterface } from "readline";
65
+ import { ThinkingPatternsClient as ThinkingPatternsClient2 } from "@thinkingpatterns/core";
66
+ var DEFAULT_API_URL2 = "https://thinkingpatterns.ai";
67
+ function prompt(question) {
68
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
69
+ return new Promise((resolve) => {
70
+ rl.question(question, (answer) => {
71
+ rl.close();
72
+ resolve(answer.trim());
73
+ });
74
+ });
75
+ }
76
+ function registerLoginCommands(program2) {
77
+ program2.command("login").description("Authenticate and save credentials").option("--api-url <url>", "API URL (default: https://thinkingpatterns.ai)").action(async (opts) => {
78
+ const apiUrl = opts.apiUrl || DEFAULT_API_URL2;
79
+ process.stderr.write(
80
+ `
81
+ Paste your API key from ${apiUrl}/settings/api-keys
82
+
83
+ `
84
+ );
85
+ const apiKey = await prompt("API key: ");
86
+ if (!apiKey) {
87
+ process.stderr.write("Aborted \u2014 no key provided.\n");
88
+ process.exit(1);
89
+ }
90
+ process.stderr.write("Verifying...");
91
+ try {
92
+ const client = new ThinkingPatternsClient2({ apiKey, apiUrl });
93
+ const me = await client.whoami();
94
+ saveCredentials(apiKey, apiUrl !== DEFAULT_API_URL2 ? apiUrl : void 0);
95
+ const identity = me.profile?.email ?? me.profile?.display_name ?? me.org?.name ?? "authenticated user";
96
+ process.stderr.write(
97
+ ` logged in as ${identity}.
98
+
99
+ Credentials saved to ~/.thinkingpatterns/credentials.json
100
+ `
101
+ );
102
+ } catch {
103
+ process.stderr.write(" failed.\n\nError: Invalid API key.\n");
104
+ process.exit(2);
105
+ }
106
+ });
107
+ program2.command("logout").description("Remove saved credentials").action(() => {
108
+ const had = loadCredentials();
109
+ clearCredentials();
110
+ if (had) {
111
+ process.stderr.write("Credentials removed.\n");
112
+ } else {
113
+ process.stderr.write("No saved credentials found.\n");
114
+ }
115
+ });
116
+ }
117
+
118
+ // src/lib/output.ts
119
+ import { TPError, TPAuthError, TPNotFoundError } from "@thinkingpatterns/core";
120
+ var EXIT_CLIENT_ERROR = 1;
121
+ var EXIT_AUTH_ERROR = 2;
122
+ var EXIT_NOT_FOUND = 3;
123
+ var EXIT_UNEXPECTED = 10;
124
+ function printJson(data) {
125
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
126
+ }
127
+ function printError(err) {
128
+ if (err instanceof TPAuthError) {
129
+ process.stderr.write(`Error: ${err.message}
130
+ Hint: Run \`tp login\` or check your --api-key value.
131
+ `);
132
+ process.exit(EXIT_AUTH_ERROR);
133
+ }
134
+ if (err instanceof TPNotFoundError) {
135
+ process.stderr.write(`Error: ${err.message}
136
+ `);
137
+ process.exit(EXIT_NOT_FOUND);
138
+ }
139
+ if (err instanceof TPError) {
140
+ process.stderr.write(`Error: ${err.message}
141
+ `);
142
+ if (err.details) {
143
+ process.stderr.write(`Details: ${JSON.stringify(err.details, null, 2)}
144
+ `);
145
+ }
146
+ process.exit(EXIT_CLIENT_ERROR);
147
+ }
148
+ if (err instanceof Error) {
149
+ process.stderr.write(`Error: ${err.message}
150
+ `);
151
+ } else {
152
+ process.stderr.write(`Error: ${String(err)}
153
+ `);
154
+ }
155
+ process.exit(EXIT_UNEXPECTED);
156
+ }
157
+
158
+ // src/commands/auth.ts
159
+ function registerAuthCommands(program2) {
160
+ program2.command("whoami").description("Show current authentication context").action(async () => {
161
+ try {
162
+ const result = await getClient().whoami();
163
+ printJson(result);
164
+ } catch (err) {
165
+ printError(err);
166
+ }
167
+ });
168
+ }
169
+
170
+ // src/commands/projects.ts
171
+ function registerProjectCommands(program2) {
172
+ const projects = program2.command("projects").description("Manage projects");
173
+ projects.command("list").description("List all projects").action(async () => {
174
+ try {
175
+ const result = await getClient().listProjects();
176
+ printJson(result);
177
+ } catch (err) {
178
+ printError(err);
179
+ }
180
+ });
181
+ }
182
+
183
+ // src/lib/config.ts
184
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
185
+ import { join as join2, dirname } from "path";
186
+ var CONFIG_FILE = ".thinkingpatterns.json";
187
+ function loadProjectConfig() {
188
+ let dir = process.cwd();
189
+ const root = dirname(dir) === dir ? dir : "/";
190
+ while (true) {
191
+ const filePath = join2(dir, CONFIG_FILE);
192
+ if (existsSync2(filePath)) {
193
+ try {
194
+ const content = readFileSync2(filePath, "utf-8");
195
+ return JSON.parse(content);
196
+ } catch {
197
+ return null;
198
+ }
199
+ }
200
+ const parent = dirname(dir);
201
+ if (parent === dir || dir === root) break;
202
+ dir = parent;
203
+ }
204
+ return null;
205
+ }
206
+ function resolveProjectId(explicit) {
207
+ if (explicit) return explicit;
208
+ const config = loadProjectConfig();
209
+ if (config?.project_id) return config.project_id;
210
+ process.stderr.write(
211
+ "Error: --project is required (or set project_id in .thinkingpatterns.json)\n"
212
+ );
213
+ process.exit(1);
214
+ }
215
+
216
+ // src/commands/findings.ts
217
+ function registerFindingCommands(program2) {
218
+ const findings = program2.command("findings").description("Manage security findings");
219
+ findings.command("list").description("List findings for a project").option("-p, --project <id>", "Project ID").option("--severity <severity>", "Filter by severity (critical|high|medium|low|info)").option("--status <status>", "Filter by status (open|triaged|completed|dismissed)").option("--category <category>", "Filter by category").option("--source-tool <tool>", "Filter by source tool").option("--module-path <path>", "Filter by module path prefix").option("--branch <branch>", "Filter by branch").option("--limit <n>", "Max results", parseInt).option("--offset <n>", "Result offset", parseInt).action(async (opts) => {
220
+ try {
221
+ const projectId = resolveProjectId(opts.project);
222
+ const result = await getClient().listFindings({
223
+ project_id: projectId,
224
+ severity: opts.severity,
225
+ status: opts.status,
226
+ category: opts.category,
227
+ source_tool: opts.sourceTool,
228
+ module_path: opts.modulePath,
229
+ branch: opts.branch,
230
+ limit: opts.limit,
231
+ offset: opts.offset
232
+ });
233
+ printJson(result);
234
+ } catch (err) {
235
+ printError(err);
236
+ }
237
+ });
238
+ findings.command("get <id>").description("Get finding details").action(async (id) => {
239
+ try {
240
+ const result = await getClient().getFinding(id);
241
+ printJson(result);
242
+ } catch (err) {
243
+ printError(err);
244
+ }
245
+ });
246
+ findings.command("triage <id>").description("Triage a finding").requiredOption("--action <action>", "Triage action (accept|dismiss|defer|complete|reopen)").option("--reason <reason>", "Reason for the triage decision").action(async (id, opts) => {
247
+ try {
248
+ const result = await getClient().triageFinding(
249
+ id,
250
+ opts.action,
251
+ opts.reason
252
+ );
253
+ printJson(result);
254
+ } catch (err) {
255
+ printError(err);
256
+ }
257
+ });
258
+ findings.command("bulk-triage").description("Triage multiple findings at once").requiredOption("--ids <ids>", "Comma-separated finding IDs").requiredOption("--action <action>", "Triage action (accept|dismiss|defer|complete|reopen)").option("--reason <reason>", "Reason for the triage decision").action(async (opts) => {
259
+ try {
260
+ const findingIds = opts.ids.split(",").map((s) => s.trim());
261
+ const result = await getClient().bulkTriage({
262
+ finding_ids: findingIds,
263
+ action: opts.action,
264
+ reason: opts.reason
265
+ });
266
+ printJson(result);
267
+ } catch (err) {
268
+ printError(err);
269
+ }
270
+ });
271
+ findings.command("comment <id>").description("Add a comment to a finding").requiredOption("--body <text>", "Comment text").action(async (id, opts) => {
272
+ try {
273
+ const result = await getClient().addComment(id, opts.body);
274
+ printJson(result);
275
+ } catch (err) {
276
+ printError(err);
277
+ }
278
+ });
279
+ }
280
+
281
+ // src/commands/metrics.ts
282
+ function registerMetricsCommands(program2) {
283
+ program2.command("metrics").description("Get project metrics (MTTR, fix rate, open count)").option("-p, --project <id>", "Project ID").action(async (opts) => {
284
+ try {
285
+ const projectId = resolveProjectId(opts.project);
286
+ const result = await getClient().getProjectMetrics(projectId);
287
+ printJson(result);
288
+ } catch (err) {
289
+ printError(err);
290
+ }
291
+ });
292
+ }
293
+
294
+ // src/commands/risk-modules.ts
295
+ function registerRiskModulesCommands(program2) {
296
+ program2.command("risk-modules").description("Get top risk modules for a project").option("-p, --project <id>", "Project ID").option("--limit <n>", "Max results", parseInt).action(async (opts) => {
297
+ try {
298
+ const projectId = resolveProjectId(opts.project);
299
+ const result = await getClient().getTopRiskModules(projectId, opts.limit);
300
+ printJson(result);
301
+ } catch (err) {
302
+ printError(err);
303
+ }
304
+ });
305
+ }
306
+
307
+ // src/commands/runs.ts
308
+ function registerRunCommands(program2) {
309
+ const runs = program2.command("runs").description("Manage scan runs");
310
+ runs.command("list").description("List runs for a project").option("-p, --project <id>", "Project ID").option("--status <status>", "Filter by status").option("--limit <n>", "Max results", parseInt).option("--offset <n>", "Result offset", parseInt).action(async (opts) => {
311
+ try {
312
+ const projectId = resolveProjectId(opts.project);
313
+ const result = await getClient().listRuns(projectId, {
314
+ status: opts.status,
315
+ limit: opts.limit,
316
+ offset: opts.offset
317
+ });
318
+ printJson(result);
319
+ } catch (err) {
320
+ printError(err);
321
+ }
322
+ });
323
+ }
324
+
325
+ // src/commands/ingest.ts
326
+ import { readFileSync as readFileSync3 } from "fs";
327
+ function registerIngestCommands(program2) {
328
+ const ingest = program2.command("ingest").description("Ingest scan results");
329
+ ingest.command("sarif").description("Ingest a SARIF file").option("-p, --project <id>", "Project ID").requiredOption("--file <path>", "Path to SARIF file").action(async (opts) => {
330
+ try {
331
+ const projectId = resolveProjectId(opts.project);
332
+ const content = readFileSync3(opts.file, "utf-8");
333
+ const result = await getClient().ingestSarif(projectId, content);
334
+ printJson(result);
335
+ } catch (err) {
336
+ printError(err);
337
+ }
338
+ });
339
+ }
340
+
341
+ // src/commands/agents.ts
342
+ import { readFileSync as readFileSync4 } from "fs";
343
+ function registerAgentCommands(program2) {
344
+ const agents = program2.command("agents").description("Manage analysis agents");
345
+ agents.command("list").description("List available agents").option("--category <category>", "Filter by category").option("--status <status>", "Filter by status (draft|active|archived)").option("--limit <n>", "Max results", parseInt).option("--offset <n>", "Result offset", parseInt).action(async (opts) => {
346
+ try {
347
+ const result = await getClient().listAgents({
348
+ category: opts.category,
349
+ status: opts.status,
350
+ limit: opts.limit,
351
+ offset: opts.offset
352
+ });
353
+ printJson(result);
354
+ } catch (err) {
355
+ printError(err);
356
+ }
357
+ });
358
+ agents.command("get <id>").description("Get agent details").action(async (id) => {
359
+ try {
360
+ const result = await getClient().getAgent(id);
361
+ printJson(result);
362
+ } catch (err) {
363
+ printError(err);
364
+ }
365
+ });
366
+ agents.command("create").description("Create a new agent").requiredOption("--name <name>", "Agent name").requiredOption("--slug <slug>", "Agent slug (URL-safe identifier)").requiredOption("--skill-md <path>", "Path to SKILL.md file").option("--description <desc>", "Agent description").option("--category <category>", "Agent category").action(async (opts) => {
367
+ try {
368
+ const skillMd = readFileSync4(opts.skillMd, "utf-8");
369
+ const result = await getClient().createAgent({
370
+ name: opts.name,
371
+ slug: opts.slug,
372
+ skill_md: skillMd,
373
+ description: opts.description,
374
+ category: opts.category
375
+ });
376
+ printJson(result);
377
+ } catch (err) {
378
+ printError(err);
379
+ }
380
+ });
381
+ agents.command("run <id>").description("Run an agent against a project").option("-p, --project <id>", "Project ID").action(async (id, opts) => {
382
+ try {
383
+ const projectId = resolveProjectId(opts.project);
384
+ const client = getClient();
385
+ const [agent, projects] = await Promise.all([
386
+ client.getAgent(id),
387
+ client.listProjects()
388
+ ]);
389
+ const project = projects.find((p) => p.id === projectId);
390
+ if (!project) {
391
+ process.stderr.write(`Error: Project ${projectId} not found
392
+ `);
393
+ process.exit(3);
394
+ }
395
+ printJson({
396
+ agent: { id: agent.id, name: agent.name, slug: agent.slug },
397
+ project: { id: project.id, name: project.name },
398
+ skill_md: agent.skill_md,
399
+ instruction: "Follow the SKILL.md workflow above. When done, ingest results with: tp ingest sarif --project " + projectId + " --file results.sarif"
400
+ });
401
+ } catch (err) {
402
+ printError(err);
403
+ }
404
+ });
405
+ }
406
+
407
+ // src/index.ts
408
+ var NO_AUTH_COMMANDS = /* @__PURE__ */ new Set(["login", "logout", "help"]);
409
+ var program = new Command();
410
+ program.name("tp").description("ThinkingPatterns CLI \u2014 security findings management").version("0.1.0").option("--api-key <key>", "API key (overrides TP_API_KEY env)").option("--api-url <url>", "API URL (overrides TP_API_URL env)").option("--quiet", "Suppress non-data output").hook("preAction", (thisCommand) => {
411
+ const commandName = thisCommand.args?.[0] ?? thisCommand.name();
412
+ if (NO_AUTH_COMMANDS.has(commandName)) return;
413
+ const opts = program.opts();
414
+ setGlobalOpts({ apiKey: opts.apiKey, apiUrl: opts.apiUrl });
415
+ if (!resolveApiKey()) {
416
+ process.stderr.write(
417
+ "Error: Not authenticated. Run `tp login` or set TP_API_KEY.\n"
418
+ );
419
+ process.exit(2);
420
+ }
421
+ });
422
+ registerLoginCommands(program);
423
+ registerAuthCommands(program);
424
+ registerProjectCommands(program);
425
+ registerFindingCommands(program);
426
+ registerMetricsCommands(program);
427
+ registerRiskModulesCommands(program);
428
+ registerRunCommands(program);
429
+ registerIngestCommands(program);
430
+ registerAgentCommands(program);
431
+ program.parse();
432
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/lib/client.ts","../src/lib/credentials.ts","../src/commands/login.ts","../src/lib/output.ts","../src/commands/auth.ts","../src/commands/projects.ts","../src/lib/config.ts","../src/commands/findings.ts","../src/commands/metrics.ts","../src/commands/risk-modules.ts","../src/commands/runs.ts","../src/commands/ingest.ts","../src/commands/agents.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { setGlobalOpts, resolveApiKey } from \"./lib/client.js\";\nimport { registerLoginCommands } from \"./commands/login.js\";\nimport { registerAuthCommands } from \"./commands/auth.js\";\nimport { registerProjectCommands } from \"./commands/projects.js\";\nimport { registerFindingCommands } from \"./commands/findings.js\";\nimport { registerMetricsCommands } from \"./commands/metrics.js\";\nimport { registerRiskModulesCommands } from \"./commands/risk-modules.js\";\nimport { registerRunCommands } from \"./commands/runs.js\";\nimport { registerIngestCommands } from \"./commands/ingest.js\";\nimport { registerAgentCommands } from \"./commands/agents.js\";\n\n/** Commands that work without authentication. */\nconst NO_AUTH_COMMANDS = new Set([\"login\", \"logout\", \"help\"]);\n\nconst program = new Command();\n\nprogram\n .name(\"tp\")\n .description(\"ThinkingPatterns CLI — security findings management\")\n .version(\"0.1.0\")\n .option(\"--api-key <key>\", \"API key (overrides TP_API_KEY env)\")\n .option(\"--api-url <url>\", \"API URL (overrides TP_API_URL env)\")\n .option(\"--quiet\", \"Suppress non-data output\")\n .hook(\"preAction\", (thisCommand) => {\n const commandName = thisCommand.args?.[0] ?? thisCommand.name();\n if (NO_AUTH_COMMANDS.has(commandName)) return;\n\n const opts = program.opts();\n setGlobalOpts({ apiKey: opts.apiKey, apiUrl: opts.apiUrl });\n\n if (!resolveApiKey()) {\n process.stderr.write(\n \"Error: Not authenticated. Run `tp login` or set TP_API_KEY.\\n\"\n );\n process.exit(2);\n }\n });\n\nregisterLoginCommands(program);\nregisterAuthCommands(program);\nregisterProjectCommands(program);\nregisterFindingCommands(program);\nregisterMetricsCommands(program);\nregisterRiskModulesCommands(program);\nregisterRunCommands(program);\nregisterIngestCommands(program);\nregisterAgentCommands(program);\n\nprogram.parse();\n","import { ThinkingPatternsClient } from \"@thinkingpatterns/core\";\nimport { loadCredentials } from \"./credentials.js\";\n\nconst DEFAULT_API_URL = \"https://thinkingpatterns.ai\";\n\nlet _client: ThinkingPatternsClient | null = null;\nlet _opts: { apiKey?: string; apiUrl?: string } = {};\n\n/** Call once from index.ts to pass through global CLI options. */\nexport function setGlobalOpts(opts: { apiKey?: string; apiUrl?: string }): void {\n _opts = opts;\n _client = null; // reset if options change\n}\n\n/**\n * Resolve the API key from (in priority order):\n * 1. --api-key flag\n * 2. TP_API_KEY env var\n * 3. ~/.thinkingpatterns/credentials.json (saved by `tp login`)\n */\nexport function resolveApiKey(): string | undefined {\n if (_opts.apiKey) return _opts.apiKey;\n if (process.env.TP_API_KEY) return process.env.TP_API_KEY;\n return loadCredentials()?.api_key;\n}\n\n/** Resolve the API URL from flag, env, saved credentials, or default. */\nfunction resolveApiUrl(): string {\n return _opts.apiUrl || process.env.TP_API_URL || loadCredentials()?.api_url || DEFAULT_API_URL;\n}\n\n/** Get the shared client instance (created lazily). */\nexport function getClient(): ThinkingPatternsClient {\n if (!_client) {\n const apiKey = resolveApiKey() || \"\";\n const apiUrl = resolveApiUrl();\n _client = new ThinkingPatternsClient({ apiKey, apiUrl });\n }\n return _client;\n}\n","import { readFileSync, writeFileSync, mkdirSync, rmSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst CONFIG_DIR = join(homedir(), \".thinkingpatterns\");\nconst CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n\ninterface StoredCredentials {\n api_key: string;\n api_url?: string;\n}\n\n/** Read persisted credentials, or null if none saved. */\nexport function loadCredentials(): StoredCredentials | null {\n if (!existsSync(CREDENTIALS_FILE)) return null;\n try {\n const content = readFileSync(CREDENTIALS_FILE, \"utf-8\");\n return JSON.parse(content) as StoredCredentials;\n } catch {\n return null;\n }\n}\n\n/** Persist credentials to ~/.thinkingpatterns/credentials.json. */\nexport function saveCredentials(apiKey: string, apiUrl?: string): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\n const data: StoredCredentials = { api_key: apiKey };\n if (apiUrl) data.api_url = apiUrl;\n writeFileSync(CREDENTIALS_FILE, JSON.stringify(data, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Remove persisted credentials. Returns true if a file was deleted. */\nexport function clearCredentials(): boolean {\n if (!existsSync(CREDENTIALS_FILE)) return false;\n rmSync(CREDENTIALS_FILE);\n return true;\n}\n","import type { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { ThinkingPatternsClient } from \"@thinkingpatterns/core\";\nimport { saveCredentials, clearCredentials, loadCredentials } from \"../lib/credentials.js\";\n\nconst DEFAULT_API_URL = \"https://thinkingpatterns.ai\";\n\nfunction prompt(question: string): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nexport function registerLoginCommands(program: Command): void {\n program\n .command(\"login\")\n .description(\"Authenticate and save credentials\")\n .option(\"--api-url <url>\", \"API URL (default: https://thinkingpatterns.ai)\")\n .action(async (opts) => {\n const apiUrl = opts.apiUrl || DEFAULT_API_URL;\n\n process.stderr.write(\n `\\nPaste your API key from ${apiUrl}/settings/api-keys\\n\\n`\n );\n\n const apiKey = await prompt(\"API key: \");\n if (!apiKey) {\n process.stderr.write(\"Aborted — no key provided.\\n\");\n process.exit(1);\n }\n\n // Validate the key\n process.stderr.write(\"Verifying...\");\n try {\n const client = new ThinkingPatternsClient({ apiKey, apiUrl });\n const me = await client.whoami();\n saveCredentials(apiKey, apiUrl !== DEFAULT_API_URL ? apiUrl : undefined);\n const identity = me.profile?.email ?? me.profile?.display_name ?? me.org?.name ?? \"authenticated user\";\n process.stderr.write(\n ` logged in as ${identity}.\\n\\n` +\n `Credentials saved to ~/.thinkingpatterns/credentials.json\\n`\n );\n } catch {\n process.stderr.write(\" failed.\\n\\nError: Invalid API key.\\n\");\n process.exit(2);\n }\n });\n\n program\n .command(\"logout\")\n .description(\"Remove saved credentials\")\n .action(() => {\n const had = loadCredentials();\n clearCredentials();\n if (had) {\n process.stderr.write(\"Credentials removed.\\n\");\n } else {\n process.stderr.write(\"No saved credentials found.\\n\");\n }\n });\n}\n","import { TPError, TPAuthError, TPNotFoundError } from \"@thinkingpatterns/core\";\n\n/* ─── Exit codes ─── */\n\nexport const EXIT_OK = 0;\nexport const EXIT_CLIENT_ERROR = 1;\nexport const EXIT_AUTH_ERROR = 2;\nexport const EXIT_NOT_FOUND = 3;\nexport const EXIT_UNEXPECTED = 10;\n\n/* ─── Output helpers ─── */\n\nexport function printJson(data: unknown): void {\n process.stdout.write(JSON.stringify(data, null, 2) + \"\\n\");\n}\n\nexport function printError(err: unknown): never {\n if (err instanceof TPAuthError) {\n process.stderr.write(`Error: ${err.message}\\nHint: Run \\`tp login\\` or check your --api-key value.\\n`);\n process.exit(EXIT_AUTH_ERROR);\n }\n if (err instanceof TPNotFoundError) {\n process.stderr.write(`Error: ${err.message}\\n`);\n process.exit(EXIT_NOT_FOUND);\n }\n if (err instanceof TPError) {\n process.stderr.write(`Error: ${err.message}\\n`);\n if (err.details) {\n process.stderr.write(`Details: ${JSON.stringify(err.details, null, 2)}\\n`);\n }\n process.exit(EXIT_CLIENT_ERROR);\n }\n if (err instanceof Error) {\n process.stderr.write(`Error: ${err.message}\\n`);\n } else {\n process.stderr.write(`Error: ${String(err)}\\n`);\n }\n process.exit(EXIT_UNEXPECTED);\n}\n","import type { Command } from \"commander\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\n\nexport function registerAuthCommands(program: Command): void {\n program\n .command(\"whoami\")\n .description(\"Show current authentication context\")\n .action(async () => {\n try {\n const result = await getClient().whoami();\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\n\nexport function registerProjectCommands(program: Command): void {\n const projects = program\n .command(\"projects\")\n .description(\"Manage projects\");\n\n projects\n .command(\"list\")\n .description(\"List all projects\")\n .action(async () => {\n try {\n const result = await getClient().listProjects();\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\n\nconst CONFIG_FILE = \".thinkingpatterns.json\";\n\ninterface ProjectConfig {\n project_id?: string;\n}\n\n/**\n * Walk up from cwd looking for .thinkingpatterns.json.\n * Returns the parsed config or null if not found.\n */\nexport function loadProjectConfig(): ProjectConfig | null {\n let dir = process.cwd();\n const root = dirname(dir) === dir ? dir : \"/\";\n\n while (true) {\n const filePath = join(dir, CONFIG_FILE);\n if (existsSync(filePath)) {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n return JSON.parse(content) as ProjectConfig;\n } catch {\n return null;\n }\n }\n const parent = dirname(dir);\n if (parent === dir || dir === root) break;\n dir = parent;\n }\n return null;\n}\n\n/**\n * Resolve project ID from explicit option, falling back to config file.\n * Exits with error if neither is available.\n */\nexport function resolveProjectId(explicit?: string): string {\n if (explicit) return explicit;\n const config = loadProjectConfig();\n if (config?.project_id) return config.project_id;\n process.stderr.write(\n \"Error: --project is required (or set project_id in .thinkingpatterns.json)\\n\"\n );\n process.exit(1);\n}\n","import type { Command } from \"commander\";\nimport type { TriageAction } from \"@thinkingpatterns/core\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\nimport { resolveProjectId } from \"../lib/config.js\";\n\nexport function registerFindingCommands(program: Command): void {\n const findings = program\n .command(\"findings\")\n .description(\"Manage security findings\");\n\n findings\n .command(\"list\")\n .description(\"List findings for a project\")\n .option(\"-p, --project <id>\", \"Project ID\")\n .option(\"--severity <severity>\", \"Filter by severity (critical|high|medium|low|info)\")\n .option(\"--status <status>\", \"Filter by status (open|triaged|completed|dismissed)\")\n .option(\"--category <category>\", \"Filter by category\")\n .option(\"--source-tool <tool>\", \"Filter by source tool\")\n .option(\"--module-path <path>\", \"Filter by module path prefix\")\n .option(\"--branch <branch>\", \"Filter by branch\")\n .option(\"--limit <n>\", \"Max results\", parseInt)\n .option(\"--offset <n>\", \"Result offset\", parseInt)\n .action(async (opts) => {\n try {\n const projectId = resolveProjectId(opts.project);\n const result = await getClient().listFindings({\n project_id: projectId,\n severity: opts.severity,\n status: opts.status,\n category: opts.category,\n source_tool: opts.sourceTool,\n module_path: opts.modulePath,\n branch: opts.branch,\n limit: opts.limit,\n offset: opts.offset,\n });\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n\n findings\n .command(\"get <id>\")\n .description(\"Get finding details\")\n .action(async (id: string) => {\n try {\n const result = await getClient().getFinding(id);\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n\n findings\n .command(\"triage <id>\")\n .description(\"Triage a finding\")\n .requiredOption(\"--action <action>\", \"Triage action (accept|dismiss|defer|complete|reopen)\")\n .option(\"--reason <reason>\", \"Reason for the triage decision\")\n .action(async (id: string, opts) => {\n try {\n const result = await getClient().triageFinding(\n id,\n opts.action as TriageAction,\n opts.reason\n );\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n\n findings\n .command(\"bulk-triage\")\n .description(\"Triage multiple findings at once\")\n .requiredOption(\"--ids <ids>\", \"Comma-separated finding IDs\")\n .requiredOption(\"--action <action>\", \"Triage action (accept|dismiss|defer|complete|reopen)\")\n .option(\"--reason <reason>\", \"Reason for the triage decision\")\n .action(async (opts) => {\n try {\n const findingIds = opts.ids.split(\",\").map((s: string) => s.trim());\n const result = await getClient().bulkTriage({\n finding_ids: findingIds,\n action: opts.action as TriageAction,\n reason: opts.reason,\n });\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n\n findings\n .command(\"comment <id>\")\n .description(\"Add a comment to a finding\")\n .requiredOption(\"--body <text>\", \"Comment text\")\n .action(async (id: string, opts) => {\n try {\n const result = await getClient().addComment(id, opts.body);\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\nimport { resolveProjectId } from \"../lib/config.js\";\n\nexport function registerMetricsCommands(program: Command): void {\n program\n .command(\"metrics\")\n .description(\"Get project metrics (MTTR, fix rate, open count)\")\n .option(\"-p, --project <id>\", \"Project ID\")\n .action(async (opts) => {\n try {\n const projectId = resolveProjectId(opts.project);\n const result = await getClient().getProjectMetrics(projectId);\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\nimport { resolveProjectId } from \"../lib/config.js\";\n\nexport function registerRiskModulesCommands(program: Command): void {\n program\n .command(\"risk-modules\")\n .description(\"Get top risk modules for a project\")\n .option(\"-p, --project <id>\", \"Project ID\")\n .option(\"--limit <n>\", \"Max results\", parseInt)\n .action(async (opts) => {\n try {\n const projectId = resolveProjectId(opts.project);\n const result = await getClient().getTopRiskModules(projectId, opts.limit);\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\nimport { resolveProjectId } from \"../lib/config.js\";\n\nexport function registerRunCommands(program: Command): void {\n const runs = program\n .command(\"runs\")\n .description(\"Manage scan runs\");\n\n runs\n .command(\"list\")\n .description(\"List runs for a project\")\n .option(\"-p, --project <id>\", \"Project ID\")\n .option(\"--status <status>\", \"Filter by status\")\n .option(\"--limit <n>\", \"Max results\", parseInt)\n .option(\"--offset <n>\", \"Result offset\", parseInt)\n .action(async (opts) => {\n try {\n const projectId = resolveProjectId(opts.project);\n const result = await getClient().listRuns(projectId, {\n status: opts.status,\n limit: opts.limit,\n offset: opts.offset,\n });\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport type { Command } from \"commander\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\nimport { resolveProjectId } from \"../lib/config.js\";\n\nexport function registerIngestCommands(program: Command): void {\n const ingest = program\n .command(\"ingest\")\n .description(\"Ingest scan results\");\n\n ingest\n .command(\"sarif\")\n .description(\"Ingest a SARIF file\")\n .option(\"-p, --project <id>\", \"Project ID\")\n .requiredOption(\"--file <path>\", \"Path to SARIF file\")\n .action(async (opts) => {\n try {\n const projectId = resolveProjectId(opts.project);\n const content = readFileSync(opts.file, \"utf-8\");\n const result = await getClient().ingestSarif(projectId, content);\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport type { Command } from \"commander\";\nimport { getClient } from \"../lib/client.js\";\nimport { printJson, printError } from \"../lib/output.js\";\nimport { resolveProjectId } from \"../lib/config.js\";\n\nexport function registerAgentCommands(program: Command): void {\n const agents = program\n .command(\"agents\")\n .description(\"Manage analysis agents\");\n\n agents\n .command(\"list\")\n .description(\"List available agents\")\n .option(\"--category <category>\", \"Filter by category\")\n .option(\"--status <status>\", \"Filter by status (draft|active|archived)\")\n .option(\"--limit <n>\", \"Max results\", parseInt)\n .option(\"--offset <n>\", \"Result offset\", parseInt)\n .action(async (opts) => {\n try {\n const result = await getClient().listAgents({\n category: opts.category,\n status: opts.status,\n limit: opts.limit,\n offset: opts.offset,\n });\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n\n agents\n .command(\"get <id>\")\n .description(\"Get agent details\")\n .action(async (id: string) => {\n try {\n const result = await getClient().getAgent(id);\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n\n agents\n .command(\"create\")\n .description(\"Create a new agent\")\n .requiredOption(\"--name <name>\", \"Agent name\")\n .requiredOption(\"--slug <slug>\", \"Agent slug (URL-safe identifier)\")\n .requiredOption(\"--skill-md <path>\", \"Path to SKILL.md file\")\n .option(\"--description <desc>\", \"Agent description\")\n .option(\"--category <category>\", \"Agent category\")\n .action(async (opts) => {\n try {\n const skillMd = readFileSync(opts.skillMd, \"utf-8\");\n const result = await getClient().createAgent({\n name: opts.name,\n slug: opts.slug,\n skill_md: skillMd,\n description: opts.description,\n category: opts.category,\n });\n printJson(result);\n } catch (err) {\n printError(err);\n }\n });\n\n agents\n .command(\"run <id>\")\n .description(\"Run an agent against a project\")\n .option(\"-p, --project <id>\", \"Project ID\")\n .action(async (id: string, opts) => {\n try {\n const projectId = resolveProjectId(opts.project);\n const client = getClient();\n const [agent, projects] = await Promise.all([\n client.getAgent(id),\n client.listProjects(),\n ]);\n const project = projects.find((p) => p.id === projectId);\n if (!project) {\n process.stderr.write(`Error: Project ${projectId} not found\\n`);\n process.exit(3);\n }\n printJson({\n agent: { id: agent.id, name: agent.name, slug: agent.slug },\n project: { id: project.id, name: project.name },\n skill_md: agent.skill_md,\n instruction:\n \"Follow the SKILL.md workflow above. When done, ingest results with: tp ingest sarif --project \" +\n projectId +\n \" --file results.sarif\",\n });\n } catch (err) {\n printError(err);\n }\n });\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;;;ACAxB,SAAS,8BAA8B;;;ACAvC,SAAS,cAAc,eAAe,WAAW,QAAQ,kBAAkB;AAC3E,SAAS,YAAY;AACrB,SAAS,eAAe;AAExB,IAAM,aAAa,KAAK,QAAQ,GAAG,mBAAmB;AACtD,IAAM,mBAAmB,KAAK,YAAY,kBAAkB;AAQrD,SAAS,kBAA4C;AAC1D,MAAI,CAAC,WAAW,gBAAgB,EAAG,QAAO;AAC1C,MAAI;AACF,UAAM,UAAU,aAAa,kBAAkB,OAAO;AACtD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,gBAAgB,QAAgB,QAAuB;AACrE,YAAU,YAAY,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AACtD,QAAM,OAA0B,EAAE,SAAS,OAAO;AAClD,MAAI,OAAQ,MAAK,UAAU;AAC3B,gBAAc,kBAAkB,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM;AAAA,IACpE,MAAM;AAAA,EACR,CAAC;AACH;AAGO,SAAS,mBAA4B;AAC1C,MAAI,CAAC,WAAW,gBAAgB,EAAG,QAAO;AAC1C,SAAO,gBAAgB;AACvB,SAAO;AACT;;;ADnCA,IAAM,kBAAkB;AAExB,IAAI,UAAyC;AAC7C,IAAI,QAA8C,CAAC;AAG5C,SAAS,cAAc,MAAkD;AAC9E,UAAQ;AACR,YAAU;AACZ;AAQO,SAAS,gBAAoC;AAClD,MAAI,MAAM,OAAQ,QAAO,MAAM;AAC/B,MAAI,QAAQ,IAAI,WAAY,QAAO,QAAQ,IAAI;AAC/C,SAAO,gBAAgB,GAAG;AAC5B;AAGA,SAAS,gBAAwB;AAC/B,SAAO,MAAM,UAAU,QAAQ,IAAI,cAAc,gBAAgB,GAAG,WAAW;AACjF;AAGO,SAAS,YAAoC;AAClD,MAAI,CAAC,SAAS;AACZ,UAAM,SAAS,cAAc,KAAK;AAClC,UAAM,SAAS,cAAc;AAC7B,cAAU,IAAI,uBAAuB,EAAE,QAAQ,OAAO,CAAC;AAAA,EACzD;AACA,SAAO;AACT;;;AEtCA,SAAS,uBAAuB;AAChC,SAAS,0BAAAA,+BAA8B;AAGvC,IAAMC,mBAAkB;AAExB,SAAS,OAAO,UAAmC;AACjD,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,SAAG,MAAM;AACT,cAAQ,OAAO,KAAK,CAAC;AAAA,IACvB,CAAC;AAAA,EACH,CAAC;AACH;AAEO,SAAS,sBAAsBC,UAAwB;AAC5D,EAAAA,SACG,QAAQ,OAAO,EACf,YAAY,mCAAmC,EAC/C,OAAO,mBAAmB,gDAAgD,EAC1E,OAAO,OAAO,SAAS;AACtB,UAAM,SAAS,KAAK,UAAUD;AAE9B,YAAQ,OAAO;AAAA,MACb;AAAA,0BAA6B,MAAM;AAAA;AAAA;AAAA,IACrC;AAEA,UAAM,SAAS,MAAM,OAAO,WAAW;AACvC,QAAI,CAAC,QAAQ;AACX,cAAQ,OAAO,MAAM,mCAA8B;AACnD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,YAAQ,OAAO,MAAM,cAAc;AACnC,QAAI;AACF,YAAM,SAAS,IAAIE,wBAAuB,EAAE,QAAQ,OAAO,CAAC;AAC5D,YAAM,KAAK,MAAM,OAAO,OAAO;AAC/B,sBAAgB,QAAQ,WAAWF,mBAAkB,SAAS,MAAS;AACvE,YAAM,WAAW,GAAG,SAAS,SAAS,GAAG,SAAS,gBAAgB,GAAG,KAAK,QAAQ;AAClF,cAAQ,OAAO;AAAA,QACb,iBAAiB,QAAQ;AAAA;AAAA;AAAA;AAAA,MAE3B;AAAA,IACF,QAAQ;AACN,cAAQ,OAAO,MAAM,uCAAuC;AAC5D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,EAAAC,SACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,MAAM;AACZ,UAAM,MAAM,gBAAgB;AAC5B,qBAAiB;AACjB,QAAI,KAAK;AACP,cAAQ,OAAO,MAAM,wBAAwB;AAAA,IAC/C,OAAO;AACL,cAAQ,OAAO,MAAM,+BAA+B;AAAA,IACtD;AAAA,EACF,CAAC;AACL;;;AChEA,SAAS,SAAS,aAAa,uBAAuB;AAK/C,IAAM,oBAAoB;AAC1B,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAIxB,SAAS,UAAU,MAAqB;AAC7C,UAAQ,OAAO,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,IAAI;AAC3D;AAEO,SAAS,WAAW,KAAqB;AAC9C,MAAI,eAAe,aAAa;AAC9B,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA;AAAA,CAA2D;AACrG,YAAQ,KAAK,eAAe;AAAA,EAC9B;AACA,MAAI,eAAe,iBAAiB;AAClC,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA,CAAI;AAC9C,YAAQ,KAAK,cAAc;AAAA,EAC7B;AACA,MAAI,eAAe,SAAS;AAC1B,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA,CAAI;AAC9C,QAAI,IAAI,SAAS;AACf,cAAQ,OAAO,MAAM,YAAY,KAAK,UAAU,IAAI,SAAS,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,IAC3E;AACA,YAAQ,KAAK,iBAAiB;AAAA,EAChC;AACA,MAAI,eAAe,OAAO;AACxB,YAAQ,OAAO,MAAM,UAAU,IAAI,OAAO;AAAA,CAAI;AAAA,EAChD,OAAO;AACL,YAAQ,OAAO,MAAM,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AAAA,EAChD;AACA,UAAQ,KAAK,eAAe;AAC9B;;;AClCO,SAAS,qBAAqBE,UAAwB;AAC3D,EAAAA,SACG,QAAQ,QAAQ,EAChB,YAAY,qCAAqC,EACjD,OAAO,YAAY;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,EAAE,OAAO;AACxC,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;ACZO,SAAS,wBAAwBC,UAAwB;AAC9D,QAAM,WAAWA,SACd,QAAQ,UAAU,EAClB,YAAY,iBAAiB;AAEhC,WACG,QAAQ,MAAM,EACd,YAAY,mBAAmB,EAC/B,OAAO,YAAY;AAClB,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,EAAE,aAAa;AAC9C,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;ACpBA,SAAS,gBAAAC,eAAc,cAAAC,mBAAkB;AACzC,SAAS,QAAAC,OAAM,eAAe;AAE9B,IAAM,cAAc;AAUb,SAAS,oBAA0C;AACxD,MAAI,MAAM,QAAQ,IAAI;AACtB,QAAM,OAAO,QAAQ,GAAG,MAAM,MAAM,MAAM;AAE1C,SAAO,MAAM;AACX,UAAM,WAAWA,MAAK,KAAK,WAAW;AACtC,QAAID,YAAW,QAAQ,GAAG;AACxB,UAAI;AACF,cAAM,UAAUD,cAAa,UAAU,OAAO;AAC9C,eAAO,KAAK,MAAM,OAAO;AAAA,MAC3B,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,SAAS,QAAQ,GAAG;AAC1B,QAAI,WAAW,OAAO,QAAQ,KAAM;AACpC,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAMO,SAAS,iBAAiB,UAA2B;AAC1D,MAAI,SAAU,QAAO;AACrB,QAAM,SAAS,kBAAkB;AACjC,MAAI,QAAQ,WAAY,QAAO,OAAO;AACtC,UAAQ,OAAO;AAAA,IACb;AAAA,EACF;AACA,UAAQ,KAAK,CAAC;AAChB;;;ACxCO,SAAS,wBAAwBG,UAAwB;AAC9D,QAAM,WAAWA,SACd,QAAQ,UAAU,EAClB,YAAY,0BAA0B;AAEzC,WACG,QAAQ,MAAM,EACd,YAAY,6BAA6B,EACzC,OAAO,sBAAsB,YAAY,EACzC,OAAO,yBAAyB,oDAAoD,EACpF,OAAO,qBAAqB,qDAAqD,EACjF,OAAO,yBAAyB,oBAAoB,EACpD,OAAO,wBAAwB,uBAAuB,EACtD,OAAO,wBAAwB,8BAA8B,EAC7D,OAAO,qBAAqB,kBAAkB,EAC9C,OAAO,eAAe,eAAe,QAAQ,EAC7C,OAAO,gBAAgB,iBAAiB,QAAQ,EAChD,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,YAAY,iBAAiB,KAAK,OAAO;AAC/C,YAAM,SAAS,MAAM,UAAU,EAAE,aAAa;AAAA,QAC5C,YAAY;AAAA,QACZ,UAAU,KAAK;AAAA,QACf,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,aAAa,KAAK;AAAA,QAClB,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,WACG,QAAQ,UAAU,EAClB,YAAY,qBAAqB,EACjC,OAAO,OAAO,OAAe;AAC5B,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,EAAE,WAAW,EAAE;AAC9C,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,WACG,QAAQ,aAAa,EACrB,YAAY,kBAAkB,EAC9B,eAAe,qBAAqB,sDAAsD,EAC1F,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,IAAY,SAAS;AAClC,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,EAAE;AAAA,QAC/B;AAAA,QACA,KAAK;AAAA,QACL,KAAK;AAAA,MACP;AACA,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,WACG,QAAQ,aAAa,EACrB,YAAY,kCAAkC,EAC9C,eAAe,eAAe,6BAA6B,EAC3D,eAAe,qBAAqB,sDAAsD,EAC1F,OAAO,qBAAqB,gCAAgC,EAC5D,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,aAAa,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC;AAClE,YAAM,SAAS,MAAM,UAAU,EAAE,WAAW;AAAA,QAC1C,aAAa;AAAA,QACb,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,WACG,QAAQ,cAAc,EACtB,YAAY,4BAA4B,EACxC,eAAe,iBAAiB,cAAc,EAC9C,OAAO,OAAO,IAAY,SAAS;AAClC,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,EAAE,WAAW,IAAI,KAAK,IAAI;AACzD,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;ACpGO,SAAS,wBAAwBC,UAAwB;AAC9D,EAAAA,SACG,QAAQ,SAAS,EACjB,YAAY,kDAAkD,EAC9D,OAAO,sBAAsB,YAAY,EACzC,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,YAAY,iBAAiB,KAAK,OAAO;AAC/C,YAAM,SAAS,MAAM,UAAU,EAAE,kBAAkB,SAAS;AAC5D,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;ACdO,SAAS,4BAA4BC,UAAwB;AAClE,EAAAA,SACG,QAAQ,cAAc,EACtB,YAAY,oCAAoC,EAChD,OAAO,sBAAsB,YAAY,EACzC,OAAO,eAAe,eAAe,QAAQ,EAC7C,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,YAAY,iBAAiB,KAAK,OAAO;AAC/C,YAAM,SAAS,MAAM,UAAU,EAAE,kBAAkB,WAAW,KAAK,KAAK;AACxE,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;ACfO,SAAS,oBAAoBC,UAAwB;AAC1D,QAAM,OAAOA,SACV,QAAQ,MAAM,EACd,YAAY,kBAAkB;AAEjC,OACG,QAAQ,MAAM,EACd,YAAY,yBAAyB,EACrC,OAAO,sBAAsB,YAAY,EACzC,OAAO,qBAAqB,kBAAkB,EAC9C,OAAO,eAAe,eAAe,QAAQ,EAC7C,OAAO,gBAAgB,iBAAiB,QAAQ,EAChD,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,YAAY,iBAAiB,KAAK,OAAO;AAC/C,YAAM,SAAS,MAAM,UAAU,EAAE,SAAS,WAAW;AAAA,QACnD,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;AC9BA,SAAS,gBAAAC,qBAAoB;AAMtB,SAAS,uBAAuBC,UAAwB;AAC7D,QAAM,SAASA,SACZ,QAAQ,QAAQ,EAChB,YAAY,qBAAqB;AAEpC,SACG,QAAQ,OAAO,EACf,YAAY,qBAAqB,EACjC,OAAO,sBAAsB,YAAY,EACzC,eAAe,iBAAiB,oBAAoB,EACpD,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,YAAY,iBAAiB,KAAK,OAAO;AAC/C,YAAM,UAAUC,cAAa,KAAK,MAAM,OAAO;AAC/C,YAAM,SAAS,MAAM,UAAU,EAAE,YAAY,WAAW,OAAO;AAC/D,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;AC1BA,SAAS,gBAAAC,qBAAoB;AAMtB,SAAS,sBAAsBC,UAAwB;AAC5D,QAAM,SAASA,SACZ,QAAQ,QAAQ,EAChB,YAAY,wBAAwB;AAEvC,SACG,QAAQ,MAAM,EACd,YAAY,uBAAuB,EACnC,OAAO,yBAAyB,oBAAoB,EACpD,OAAO,qBAAqB,0CAA0C,EACtE,OAAO,eAAe,eAAe,QAAQ,EAC7C,OAAO,gBAAgB,iBAAiB,QAAQ,EAChD,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,EAAE,WAAW;AAAA,QAC1C,UAAU,KAAK;AAAA,QACf,QAAQ,KAAK;AAAA,QACb,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,SACG,QAAQ,UAAU,EAClB,YAAY,mBAAmB,EAC/B,OAAO,OAAO,OAAe;AAC5B,QAAI;AACF,YAAM,SAAS,MAAM,UAAU,EAAE,SAAS,EAAE;AAC5C,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,SACG,QAAQ,QAAQ,EAChB,YAAY,oBAAoB,EAChC,eAAe,iBAAiB,YAAY,EAC5C,eAAe,iBAAiB,kCAAkC,EAClE,eAAe,qBAAqB,uBAAuB,EAC3D,OAAO,wBAAwB,mBAAmB,EAClD,OAAO,yBAAyB,gBAAgB,EAChD,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,UAAUC,cAAa,KAAK,SAAS,OAAO;AAClD,YAAM,SAAS,MAAM,UAAU,EAAE,YAAY;AAAA,QAC3C,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,UAAU;AAAA,QACV,aAAa,KAAK;AAAA,QAClB,UAAU,KAAK;AAAA,MACjB,CAAC;AACD,gBAAU,MAAM;AAAA,IAClB,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,SACG,QAAQ,UAAU,EAClB,YAAY,gCAAgC,EAC5C,OAAO,sBAAsB,YAAY,EACzC,OAAO,OAAO,IAAY,SAAS;AAClC,QAAI;AACF,YAAM,YAAY,iBAAiB,KAAK,OAAO;AAC/C,YAAM,SAAS,UAAU;AACzB,YAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC1C,OAAO,SAAS,EAAE;AAAA,QAClB,OAAO,aAAa;AAAA,MACtB,CAAC;AACD,YAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS;AACvD,UAAI,CAAC,SAAS;AACZ,gBAAQ,OAAO,MAAM,kBAAkB,SAAS;AAAA,CAAc;AAC9D,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,gBAAU;AAAA,QACR,OAAO,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC1D,SAAS,EAAE,IAAI,QAAQ,IAAI,MAAM,QAAQ,KAAK;AAAA,QAC9C,UAAU,MAAM;AAAA,QAChB,aACE,mGACA,YACA;AAAA,MACJ,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,iBAAW,GAAG;AAAA,IAChB;AAAA,EACF,CAAC;AACL;;;AbrFA,IAAM,mBAAmB,oBAAI,IAAI,CAAC,SAAS,UAAU,MAAM,CAAC;AAE5D,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,IAAI,EACT,YAAY,0DAAqD,EACjE,QAAQ,OAAO,EACf,OAAO,mBAAmB,oCAAoC,EAC9D,OAAO,mBAAmB,oCAAoC,EAC9D,OAAO,WAAW,0BAA0B,EAC5C,KAAK,aAAa,CAAC,gBAAgB;AAClC,QAAM,cAAc,YAAY,OAAO,CAAC,KAAK,YAAY,KAAK;AAC9D,MAAI,iBAAiB,IAAI,WAAW,EAAG;AAEvC,QAAM,OAAO,QAAQ,KAAK;AAC1B,gBAAc,EAAE,QAAQ,KAAK,QAAQ,QAAQ,KAAK,OAAO,CAAC;AAE1D,MAAI,CAAC,cAAc,GAAG;AACpB,YAAQ,OAAO;AAAA,MACb;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,sBAAsB,OAAO;AAC7B,qBAAqB,OAAO;AAC5B,wBAAwB,OAAO;AAC/B,wBAAwB,OAAO;AAC/B,wBAAwB,OAAO;AAC/B,4BAA4B,OAAO;AACnC,oBAAoB,OAAO;AAC3B,uBAAuB,OAAO;AAC9B,sBAAsB,OAAO;AAE7B,QAAQ,MAAM;","names":["ThinkingPatternsClient","DEFAULT_API_URL","program","ThinkingPatternsClient","program","program","readFileSync","existsSync","join","program","program","program","program","readFileSync","program","readFileSync","readFileSync","program","readFileSync"]}
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@thinkingpatterns/cli",
3
+ "version": "0.1.0",
4
+ "description": "ThinkingPatterns CLI for security findings management",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "tp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup",
15
+ "dev": "tsup --watch",
16
+ "start": "node dist/index.js"
17
+ },
18
+ "dependencies": {
19
+ "@thinkingpatterns/core": "^0.1.0",
20
+ "commander": "^13.0.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^25.5.0",
24
+ "tsup": "^8.0.0",
25
+ "typescript": "^5.9.0"
26
+ }
27
+ }