codmir 0.3.2 → 0.4.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/index.js CHANGED
@@ -6,6 +6,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __commonJS = (cb, mod) => function __require() {
13
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
14
+ };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
9
19
  var __copyProps = (to, from, except, desc) => {
10
20
  if (from && typeof from === "object" || typeof from === "function") {
11
21
  for (let key of __getOwnPropNames(from))
@@ -23,20 +33,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
33
  mod
24
34
  ));
25
35
 
26
- // src/cli/index.ts
27
- var import_commander = require("commander");
28
-
29
- // src/cli/utils/auth.ts
30
- var import_open = __toESM(require("open"));
31
- var readline = __toESM(require("readline"));
32
-
33
36
  // src/cli/utils/config.ts
34
- var import_fs = __toESM(require("fs"));
35
- var import_path = __toESM(require("path"));
36
- var import_os = __toESM(require("os"));
37
- var CONFIG_DIR = import_path.default.join(import_os.default.homedir(), ".codmir");
38
- var CONFIG_FILE = import_path.default.join(CONFIG_DIR, "config.json");
39
- var PROJECT_CONFIG_FILE = ".codmir.json";
40
37
  function ensureConfigDir() {
41
38
  if (!import_fs.default.existsSync(CONFIG_DIR)) {
42
39
  import_fs.default.mkdirSync(CONFIG_DIR, { recursive: true });
@@ -99,8 +96,836 @@ function isAuthenticated() {
99
96
  const token = getToken();
100
97
  return !!token;
101
98
  }
99
+ function getBaseUrl() {
100
+ if (process.env.CODMIR_API_URL) {
101
+ return process.env.CODMIR_API_URL.replace(/\/$/, "");
102
+ }
103
+ const config = readConfig();
104
+ if (config.baseUrl) {
105
+ return config.baseUrl.replace(/\/$/, "");
106
+ }
107
+ return "https://codmir.com";
108
+ }
109
+ function getProjectConfig() {
110
+ return readProjectConfig();
111
+ }
112
+ function getExecutionContext() {
113
+ const projectConfig = readProjectConfig();
114
+ const isLinkedProject = projectConfig !== null;
115
+ return {
116
+ mode: isLinkedProject ? "local" : "global",
117
+ isLinkedProject,
118
+ projectConfig: projectConfig || void 0,
119
+ workingDirectory: process.cwd()
120
+ };
121
+ }
122
+ var import_fs, import_path, import_os, CONFIG_DIR, CONFIG_FILE, PROJECT_CONFIG_FILE;
123
+ var init_config = __esm({
124
+ "src/cli/utils/config.ts"() {
125
+ "use strict";
126
+ import_fs = __toESM(require("fs"));
127
+ import_path = __toESM(require("path"));
128
+ import_os = __toESM(require("os"));
129
+ CONFIG_DIR = import_path.default.join(import_os.default.homedir(), ".codmir");
130
+ CONFIG_FILE = import_path.default.join(CONFIG_DIR, "config.json");
131
+ PROJECT_CONFIG_FILE = ".codmir.json";
132
+ }
133
+ });
134
+
135
+ // package.json
136
+ var require_package = __commonJS({
137
+ "package.json"(exports2, module2) {
138
+ module2.exports = {
139
+ name: "codmir",
140
+ version: "0.4.0",
141
+ description: "Official codmir SDK - AI that prevents wasted engineering time. CLI, SDK, voice assistant, codebase analysis, and intelligent automation.",
142
+ main: "dist/index.js",
143
+ module: "dist/index.mjs",
144
+ types: "dist/index.d.ts",
145
+ bin: {
146
+ codmir: "dist/cli/index.js"
147
+ },
148
+ exports: {
149
+ ".": {
150
+ types: "./dist/index.d.ts",
151
+ require: "./dist/index.js",
152
+ import: "./dist/index.mjs"
153
+ },
154
+ "./voice-agent": {
155
+ types: "./dist/voice-agent/index.d.ts",
156
+ require: "./dist/voice-agent/index.js",
157
+ import: "./dist/voice-agent/index.mjs"
158
+ },
159
+ "./voice-daemon": {
160
+ types: "./dist/voice-daemon/index.d.ts",
161
+ require: "./dist/voice-daemon/index.js",
162
+ import: "./dist/voice-daemon/index.mjs"
163
+ }
164
+ },
165
+ files: [
166
+ "dist",
167
+ "README.md",
168
+ "LICENSE",
169
+ "runkit-example.js"
170
+ ],
171
+ scripts: {
172
+ build: "tsup src/index.ts src/cli/index.ts src/voice-agent/index.ts src/voice-daemon/index.ts --format cjs,esm --dts --clean",
173
+ dev: "tsup src/index.ts src/cli/index.ts --format cjs,esm --dts --watch",
174
+ prepublishOnly: "pnpm build",
175
+ test: "jest",
176
+ "test:login": "node scripts/test/login.mjs",
177
+ "test:link": "node scripts/test/link.mjs",
178
+ "test:projects": "node scripts/test/projects.mjs",
179
+ "test:whoami": "node scripts/test/whoami.mjs",
180
+ "test:logout": "node scripts/test/logout.mjs",
181
+ "test:all": "npm run test:logout && npm run test:login && npm run test:whoami && npm run test:projects"
182
+ },
183
+ keywords: [
184
+ "codmir",
185
+ "sdk",
186
+ "cli",
187
+ "ai",
188
+ "codebase-analysis",
189
+ "knowledge-base",
190
+ "task-replication",
191
+ "usage-tracking",
192
+ "observability",
193
+ "project-management",
194
+ "tickets",
195
+ "tasks",
196
+ "automation",
197
+ "multi-agent",
198
+ "ai-assistant",
199
+ "voice-assistant",
200
+ "speech-recognition",
201
+ "voice-daemon",
202
+ "wake-word"
203
+ ],
204
+ author: "codmir",
205
+ license: "MIT",
206
+ repository: {
207
+ type: "git",
208
+ url: "https://github.com/codmir/codmir.git",
209
+ directory: "apps/web/packages/codmir-client"
210
+ },
211
+ bugs: {
212
+ url: "https://github.com/codmir/codmir/issues"
213
+ },
214
+ homepage: "https://codmir.com",
215
+ runkit: {
216
+ example: "runkit-example.js"
217
+ },
218
+ devDependencies: {
219
+ "@semantic-release/changelog": "^6.0.3",
220
+ "@semantic-release/commit-analyzer": "^13.0.0",
221
+ "@semantic-release/git": "^10.0.1",
222
+ "@semantic-release/github": "^11.0.0",
223
+ "@semantic-release/npm": "^12.0.1",
224
+ "@semantic-release/release-notes-generator": "^14.0.0",
225
+ "@types/node": "^20.10.0",
226
+ "@types/prompts": "^2.4.9",
227
+ "@types/ws": "^8.18.1",
228
+ "@types/blessed": "^0.1.25",
229
+ "conventional-changelog-conventionalcommits": "^8.0.0",
230
+ "semantic-release": "^24.0.0",
231
+ tsup: "^8.0.1",
232
+ typescript: "^5.8.3"
233
+ },
234
+ dependencies: {
235
+ blessed: "^0.1.81",
236
+ "blessed-contrib": "^4.11.0",
237
+ chalk: "^5.3.0",
238
+ clipboardy: "^5.0.1",
239
+ commander: "^12.0.0",
240
+ "form-data": "^4.0.5",
241
+ glob: "^10.3.10",
242
+ open: "^10.0.0",
243
+ ora: "^9.0.0",
244
+ prompts: "^2.4.2",
245
+ ws: "^8.18.3"
246
+ },
247
+ engines: {
248
+ node: ">=18.0.0"
249
+ }
250
+ };
251
+ }
252
+ });
253
+
254
+ // src/cli/utils/codebase-analyzer/local.ts
255
+ async function analyzeLocal(projectPath) {
256
+ const startTime = Date.now();
257
+ const [structure, techStack, entryPoints, documentation, metadata] = await Promise.all([
258
+ buildFileTree(projectPath),
259
+ detectTechStack(projectPath),
260
+ findEntryPoints(projectPath),
261
+ extractDocumentation(projectPath),
262
+ collectMetadata(projectPath)
263
+ ]);
264
+ const analysis = {
265
+ structure,
266
+ techStack,
267
+ entryPoints,
268
+ documentation,
269
+ metadata,
270
+ analyzedAt: (/* @__PURE__ */ new Date()).toISOString()
271
+ };
272
+ const duration = Date.now() - startTime;
273
+ console.log(`Analysis complete in ${duration}ms`);
274
+ return analysis;
275
+ }
276
+ async function buildFileTree(projectPath) {
277
+ const stats = import_fs3.default.statSync(projectPath);
278
+ const name = import_path3.default.basename(projectPath);
279
+ if (!stats.isDirectory()) {
280
+ return {
281
+ name,
282
+ path: projectPath,
283
+ type: "file",
284
+ size: stats.size,
285
+ extension: import_path3.default.extname(projectPath)
286
+ };
287
+ }
288
+ const children = [];
289
+ const entries = import_fs3.default.readdirSync(projectPath);
290
+ for (const entry of entries) {
291
+ if (shouldIgnore(entry)) continue;
292
+ const fullPath = import_path3.default.join(projectPath, entry);
293
+ try {
294
+ const childNode = await buildFileTree(fullPath);
295
+ children.push(childNode);
296
+ } catch (error) {
297
+ continue;
298
+ }
299
+ }
300
+ return {
301
+ name,
302
+ path: projectPath,
303
+ type: "directory",
304
+ children: children.sort((a, b) => {
305
+ if (a.type !== b.type) {
306
+ return a.type === "directory" ? -1 : 1;
307
+ }
308
+ return a.name.localeCompare(b.name);
309
+ })
310
+ };
311
+ }
312
+ async function detectTechStack(projectPath) {
313
+ const techStack = {
314
+ languages: {},
315
+ frameworks: [],
316
+ libraries: [],
317
+ tools: [],
318
+ packageManager: null
319
+ };
320
+ if (import_fs3.default.existsSync(import_path3.default.join(projectPath, "pnpm-lock.yaml"))) {
321
+ techStack.packageManager = "pnpm";
322
+ } else if (import_fs3.default.existsSync(import_path3.default.join(projectPath, "yarn.lock"))) {
323
+ techStack.packageManager = "yarn";
324
+ } else if (import_fs3.default.existsSync(import_path3.default.join(projectPath, "package-lock.json"))) {
325
+ techStack.packageManager = "npm";
326
+ } else if (import_fs3.default.existsSync(import_path3.default.join(projectPath, "bun.lockb"))) {
327
+ techStack.packageManager = "bun";
328
+ }
329
+ const files = await (0, import_glob.glob)("**/*", {
330
+ cwd: projectPath,
331
+ ignore: IGNORE_PATTERNS,
332
+ nodir: true
333
+ });
334
+ const languageCounts = {};
335
+ let totalCodeFiles = 0;
336
+ for (const file of files) {
337
+ const ext = import_path3.default.extname(file);
338
+ const language = LANGUAGE_EXTENSIONS[ext];
339
+ if (language) {
340
+ languageCounts[language] = (languageCounts[language] || 0) + 1;
341
+ totalCodeFiles++;
342
+ }
343
+ }
344
+ for (const [language, count] of Object.entries(languageCounts)) {
345
+ techStack.languages[language] = parseFloat(
346
+ (count / totalCodeFiles * 100).toFixed(1)
347
+ );
348
+ }
349
+ const packageJsonPath = import_path3.default.join(projectPath, "package.json");
350
+ if (import_fs3.default.existsSync(packageJsonPath)) {
351
+ const packageJson = JSON.parse(import_fs3.default.readFileSync(packageJsonPath, "utf-8"));
352
+ const dependencies = {
353
+ ...packageJson.dependencies,
354
+ ...packageJson.devDependencies
355
+ };
356
+ if (dependencies["next"]) techStack.frameworks.push("Next.js");
357
+ if (dependencies["react"]) techStack.frameworks.push("React");
358
+ if (dependencies["vue"]) techStack.frameworks.push("Vue");
359
+ if (dependencies["svelte"]) techStack.frameworks.push("Svelte");
360
+ if (dependencies["express"]) techStack.frameworks.push("Express");
361
+ if (dependencies["fastify"]) techStack.frameworks.push("Fastify");
362
+ if (dependencies["nestjs"]) techStack.frameworks.push("NestJS");
363
+ if (dependencies["@remix-run/react"]) techStack.frameworks.push("Remix");
364
+ if (dependencies["gatsby"]) techStack.frameworks.push("Gatsby");
365
+ if (dependencies["nuxt"]) techStack.frameworks.push("Nuxt");
366
+ if (dependencies["prisma"]) techStack.libraries.push("Prisma");
367
+ if (dependencies["@tanstack/react-query"]) techStack.libraries.push("TanStack Query");
368
+ if (dependencies["tailwindcss"]) techStack.libraries.push("Tailwind CSS");
369
+ if (dependencies["shadcn/ui"]) techStack.libraries.push("shadcn/ui");
370
+ if (dependencies["zod"]) techStack.libraries.push("Zod");
371
+ if (dependencies["react-hook-form"]) techStack.libraries.push("React Hook Form");
372
+ if (dependencies["typescript"]) techStack.tools.push("TypeScript");
373
+ if (dependencies["eslint"]) techStack.tools.push("ESLint");
374
+ if (dependencies["prettier"]) techStack.tools.push("Prettier");
375
+ if (dependencies["jest"]) techStack.tools.push("Jest");
376
+ if (dependencies["vitest"]) techStack.tools.push("Vitest");
377
+ if (dependencies["playwright"]) techStack.tools.push("Playwright");
378
+ }
379
+ return techStack;
380
+ }
381
+ async function findEntryPoints(projectPath) {
382
+ const entryPoints = [];
383
+ const packageJsonPath = import_path3.default.join(projectPath, "package.json");
384
+ if (import_fs3.default.existsSync(packageJsonPath)) {
385
+ const packageJson = JSON.parse(import_fs3.default.readFileSync(packageJsonPath, "utf-8"));
386
+ if (packageJson.scripts) {
387
+ for (const [name, script] of Object.entries(packageJson.scripts)) {
388
+ entryPoints.push({
389
+ type: "script",
390
+ name,
391
+ path: "package.json",
392
+ description: script
393
+ });
394
+ }
395
+ }
396
+ if (packageJson.main) {
397
+ entryPoints.push({
398
+ type: "main",
399
+ name: "main",
400
+ path: packageJson.main,
401
+ description: "Main entry point"
402
+ });
403
+ }
404
+ }
405
+ const commonEntries = [
406
+ "src/index.ts",
407
+ "src/index.js",
408
+ "src/main.ts",
409
+ "src/main.js",
410
+ "src/app/page.tsx",
411
+ "src/app/layout.tsx",
412
+ "app/page.tsx",
413
+ "app/layout.tsx",
414
+ "pages/index.tsx",
415
+ "pages/index.js",
416
+ "pages/_app.tsx",
417
+ "pages/_app.js"
418
+ ];
419
+ for (const entry of commonEntries) {
420
+ const fullPath = import_path3.default.join(projectPath, entry);
421
+ if (import_fs3.default.existsSync(fullPath)) {
422
+ entryPoints.push({
423
+ type: entry.includes("page") ? "page" : "main",
424
+ name: import_path3.default.basename(entry),
425
+ path: entry,
426
+ description: getEntryDescription(entry)
427
+ });
428
+ }
429
+ }
430
+ return entryPoints;
431
+ }
432
+ async function extractDocumentation(projectPath) {
433
+ const documentation = [];
434
+ const docFiles = [
435
+ { pattern: "README.md", type: "readme" },
436
+ { pattern: "CHANGELOG.md", type: "changelog" },
437
+ { pattern: "CONTRIBUTING.md", type: "contributing" },
438
+ { pattern: "LICENSE", type: "license" },
439
+ { pattern: "LICENSE.md", type: "license" },
440
+ { pattern: "docs/**/*.md", type: "other" }
441
+ ];
442
+ for (const { pattern, type } of docFiles) {
443
+ const files = await (0, import_glob.glob)(pattern, {
444
+ cwd: projectPath,
445
+ nodir: true
446
+ });
447
+ for (const file of files) {
448
+ const fullPath = import_path3.default.join(projectPath, file);
449
+ const content = import_fs3.default.readFileSync(fullPath, "utf-8");
450
+ documentation.push({
451
+ type,
452
+ path: file,
453
+ title: extractTitle(content),
454
+ content: content.substring(0, 1e3)
455
+ // First 1000 chars
456
+ });
457
+ }
458
+ }
459
+ return documentation;
460
+ }
461
+ async function collectMetadata(projectPath) {
462
+ const metadata = {
463
+ name: import_path3.default.basename(projectPath),
464
+ totalFiles: 0,
465
+ totalLines: 0
466
+ };
467
+ const packageJsonPath = import_path3.default.join(projectPath, "package.json");
468
+ if (import_fs3.default.existsSync(packageJsonPath)) {
469
+ const packageJson = JSON.parse(import_fs3.default.readFileSync(packageJsonPath, "utf-8"));
470
+ metadata.name = packageJson.name || metadata.name;
471
+ metadata.version = packageJson.version;
472
+ metadata.description = packageJson.description;
473
+ metadata.author = packageJson.author;
474
+ metadata.license = packageJson.license;
475
+ metadata.scripts = packageJson.scripts;
476
+ if (packageJson.repository) {
477
+ metadata.repository = typeof packageJson.repository === "string" ? packageJson.repository : packageJson.repository.url;
478
+ }
479
+ }
480
+ const files = await (0, import_glob.glob)("**/*", {
481
+ cwd: projectPath,
482
+ ignore: IGNORE_PATTERNS,
483
+ nodir: true
484
+ });
485
+ metadata.totalFiles = files.length;
486
+ for (const file of files) {
487
+ const ext = import_path3.default.extname(file);
488
+ if (LANGUAGE_EXTENSIONS[ext]) {
489
+ const fullPath = import_path3.default.join(projectPath, file);
490
+ const content = import_fs3.default.readFileSync(fullPath, "utf-8");
491
+ metadata.totalLines += content.split("\n").length;
492
+ }
493
+ }
494
+ const gitPath = import_path3.default.join(projectPath, ".git");
495
+ if (import_fs3.default.existsSync(gitPath)) {
496
+ try {
497
+ const headPath = import_path3.default.join(gitPath, "HEAD");
498
+ const head = import_fs3.default.readFileSync(headPath, "utf-8").trim();
499
+ metadata.gitBranch = head.replace("ref: refs/heads/", "");
500
+ } catch {
501
+ }
502
+ }
503
+ return metadata;
504
+ }
505
+ function shouldIgnore(name) {
506
+ const ignoreList = [
507
+ "node_modules",
508
+ ".git",
509
+ "dist",
510
+ "build",
511
+ ".next",
512
+ "coverage",
513
+ ".cache",
514
+ "tmp",
515
+ ".turbo",
516
+ ".vercel",
517
+ ".DS_Store"
518
+ ];
519
+ return ignoreList.includes(name) || name.startsWith(".");
520
+ }
521
+ function getEntryDescription(entryPath) {
522
+ if (entryPath.includes("layout")) return "Root layout component";
523
+ if (entryPath.includes("page")) return "Page component";
524
+ if (entryPath.includes("_app")) return "App component";
525
+ if (entryPath.includes("index")) return "Main entry point";
526
+ return "Entry file";
527
+ }
528
+ function extractTitle(content) {
529
+ const lines = content.split("\n");
530
+ for (const line of lines) {
531
+ if (line.startsWith("# ")) {
532
+ return line.substring(2).trim();
533
+ }
534
+ }
535
+ return void 0;
536
+ }
537
+ var import_fs3, import_path3, import_glob, IGNORE_PATTERNS, LANGUAGE_EXTENSIONS;
538
+ var init_local = __esm({
539
+ "src/cli/utils/codebase-analyzer/local.ts"() {
540
+ "use strict";
541
+ import_fs3 = __toESM(require("fs"));
542
+ import_path3 = __toESM(require("path"));
543
+ import_glob = require("glob");
544
+ IGNORE_PATTERNS = [
545
+ "**/node_modules/**",
546
+ "**/.git/**",
547
+ "**/dist/**",
548
+ "**/build/**",
549
+ "**/.next/**",
550
+ "**/coverage/**",
551
+ "**/.cache/**",
552
+ "**/tmp/**",
553
+ "**/.turbo/**",
554
+ "**/.vercel/**"
555
+ ];
556
+ LANGUAGE_EXTENSIONS = {
557
+ ".ts": "TypeScript",
558
+ ".tsx": "TypeScript",
559
+ ".js": "JavaScript",
560
+ ".jsx": "JavaScript",
561
+ ".py": "Python",
562
+ ".rb": "Ruby",
563
+ ".go": "Go",
564
+ ".rs": "Rust",
565
+ ".java": "Java",
566
+ ".kt": "Kotlin",
567
+ ".swift": "Swift",
568
+ ".php": "PHP",
569
+ ".cs": "C#",
570
+ ".cpp": "C++",
571
+ ".c": "C",
572
+ ".css": "CSS",
573
+ ".scss": "SCSS",
574
+ ".sass": "Sass",
575
+ ".html": "HTML",
576
+ ".vue": "Vue",
577
+ ".svelte": "Svelte",
578
+ ".sql": "SQL",
579
+ ".sh": "Shell",
580
+ ".yaml": "YAML",
581
+ ".yml": "YAML",
582
+ ".json": "JSON",
583
+ ".md": "Markdown"
584
+ };
585
+ }
586
+ });
587
+
588
+ // src/cli/utils/knowledge-base/storage.ts
589
+ function getKnowledgeBaseDir(projectPath) {
590
+ return import_path4.default.join(projectPath, KNOWLEDGE_BASE_DIR);
591
+ }
592
+ function ensureKnowledgeBaseDir(projectPath) {
593
+ const kbDir = getKnowledgeBaseDir(projectPath);
594
+ if (!import_fs4.default.existsSync(kbDir)) {
595
+ import_fs4.default.mkdirSync(kbDir, { recursive: true });
596
+ }
597
+ }
598
+ function saveKnowledgeBase(projectPath, knowledgeBase) {
599
+ ensureKnowledgeBaseDir(projectPath);
600
+ const kbDir = getKnowledgeBaseDir(projectPath);
601
+ const indexPath = import_path4.default.join(kbDir, INDEX_FILE);
602
+ import_fs4.default.writeFileSync(indexPath, JSON.stringify(knowledgeBase, null, 2));
603
+ if (knowledgeBase.localAnalysis) {
604
+ saveComponent(kbDir, "local-analysis.json", knowledgeBase.localAnalysis);
605
+ }
606
+ }
607
+ function loadKnowledgeBase(projectPath) {
608
+ const indexPath = import_path4.default.join(projectPath, KNOWLEDGE_BASE_DIR, INDEX_FILE);
609
+ if (!import_fs4.default.existsSync(indexPath)) {
610
+ return null;
611
+ }
612
+ try {
613
+ const content = import_fs4.default.readFileSync(indexPath, "utf-8");
614
+ const knowledgeBase = JSON.parse(content);
615
+ const kbDir = getKnowledgeBaseDir(projectPath);
616
+ if (knowledgeBase.analysisType === "local") {
617
+ const localAnalysisPath = import_path4.default.join(kbDir, "local-analysis.json");
618
+ if (import_fs4.default.existsSync(localAnalysisPath)) {
619
+ knowledgeBase.localAnalysis = JSON.parse(
620
+ import_fs4.default.readFileSync(localAnalysisPath, "utf-8")
621
+ );
622
+ }
623
+ }
624
+ return knowledgeBase;
625
+ } catch (error) {
626
+ console.error("Error loading knowledge base:", error);
627
+ return null;
628
+ }
629
+ }
630
+ function hasKnowledgeBase(projectPath) {
631
+ const indexPath = import_path4.default.join(projectPath, KNOWLEDGE_BASE_DIR, INDEX_FILE);
632
+ return import_fs4.default.existsSync(indexPath);
633
+ }
634
+ function getKnowledgeBaseStats(projectPath) {
635
+ const kb = loadKnowledgeBase(projectPath);
636
+ if (!kb) {
637
+ return null;
638
+ }
639
+ return {
640
+ version: kb.version,
641
+ analyzedAt: kb.analyzedAt,
642
+ analysisType: kb.analysisType,
643
+ totalFiles: kb.localAnalysis?.metadata.totalFiles || 0,
644
+ totalLines: kb.localAnalysis?.metadata.totalLines || 0,
645
+ languages: kb.localAnalysis?.techStack.languages || {},
646
+ frameworks: kb.localAnalysis?.techStack.frameworks || []
647
+ };
648
+ }
649
+ function saveComponent(kbDir, filename, data) {
650
+ const filePath = import_path4.default.join(kbDir, filename);
651
+ import_fs4.default.writeFileSync(filePath, JSON.stringify(data, null, 2));
652
+ }
653
+ function initializeKnowledgeBase(projectPath, projectId, projectName, localAnalysis) {
654
+ const knowledgeBase = {
655
+ version: "1.0.0",
656
+ projectId,
657
+ projectName,
658
+ analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
659
+ analysisType: "local",
660
+ localAnalysis,
661
+ queries: {}
662
+ };
663
+ saveKnowledgeBase(projectPath, knowledgeBase);
664
+ return knowledgeBase;
665
+ }
666
+ var import_fs4, import_path4, KNOWLEDGE_BASE_DIR, INDEX_FILE;
667
+ var init_storage = __esm({
668
+ "src/cli/utils/knowledge-base/storage.ts"() {
669
+ "use strict";
670
+ import_fs4 = __toESM(require("fs"));
671
+ import_path4 = __toESM(require("path"));
672
+ KNOWLEDGE_BASE_DIR = ".codmir/knowledge";
673
+ INDEX_FILE = "index.json";
674
+ }
675
+ });
676
+
677
+ // src/cli/commands/analyze.ts
678
+ var analyze_exports = {};
679
+ __export(analyze_exports, {
680
+ analyzeCommand: () => analyzeCommand
681
+ });
682
+ async function analyzeCommand(options = {}) {
683
+ console.log(import_chalk4.default.bold("\n\u{1F50D} Codebase Analysis\n"));
684
+ const projectConfig = getProjectConfig();
685
+ if (!projectConfig) {
686
+ console.error(import_chalk4.default.red("\u274C Project not linked"));
687
+ console.log(import_chalk4.default.dim(" Run"), import_chalk4.default.cyan("codmir link"), import_chalk4.default.dim("first"));
688
+ process.exit(1);
689
+ }
690
+ const projectPath = process.cwd();
691
+ const existingKB = hasKnowledgeBase(projectPath);
692
+ if (existingKB && !options.force) {
693
+ const stats = getKnowledgeBaseStats(projectPath);
694
+ console.log(import_chalk4.default.yellow("\u26A0\uFE0F Knowledge base already exists"));
695
+ console.log(import_chalk4.default.dim(` Last analyzed: ${new Date(stats.analyzedAt).toLocaleString()}`));
696
+ console.log(import_chalk4.default.dim(` Files: ${stats.totalFiles}, Lines: ${stats.totalLines}`));
697
+ const { reanalyze } = await (0, import_prompts.default)({
698
+ type: "confirm",
699
+ name: "reanalyze",
700
+ message: "Re-analyze codebase?",
701
+ initial: false
702
+ });
703
+ if (!reanalyze) {
704
+ console.log(import_chalk4.default.dim("\n Use"), import_chalk4.default.cyan("--force"), import_chalk4.default.dim("to force re-analysis"));
705
+ return;
706
+ }
707
+ }
708
+ let analysisMode = options.mode || "local";
709
+ if (!options.mode) {
710
+ const { mode } = await (0, import_prompts.default)({
711
+ type: "select",
712
+ name: "mode",
713
+ message: "Choose analysis mode",
714
+ choices: [
715
+ {
716
+ title: "\u{1F4BB} Local (Quick)",
717
+ value: "local",
718
+ description: "Fast structural analysis (1-5 seconds)"
719
+ },
720
+ {
721
+ title: "\u{1F682} Railway (Deep)",
722
+ value: "railway",
723
+ description: "Comprehensive analysis with embeddings (~2 minutes)"
724
+ }
725
+ ],
726
+ initial: 0
727
+ });
728
+ analysisMode = mode;
729
+ }
730
+ if (analysisMode === "local") {
731
+ await runLocalAnalysis(projectPath, projectConfig.projectId, projectConfig.projectName);
732
+ } else {
733
+ await runRailwayAnalysis(projectPath, projectConfig.projectId);
734
+ }
735
+ }
736
+ async function runLocalAnalysis(projectPath, projectId, projectName) {
737
+ const spinner = (0, import_ora.default)({
738
+ text: import_chalk4.default.dim("Analyzing project structure..."),
739
+ color: "cyan"
740
+ }).start();
741
+ try {
742
+ const analysis = await analyzeLocal(projectPath);
743
+ spinner.text = import_chalk4.default.dim("Saving knowledge base...");
744
+ initializeKnowledgeBase(projectPath, projectId, projectName, analysis);
745
+ spinner.succeed(import_chalk4.default.green("Analysis complete!"));
746
+ console.log();
747
+ console.log(import_chalk4.default.bold("\u{1F4CA} Results:"));
748
+ console.log();
749
+ console.log(import_chalk4.default.dim(" Project:"), import_chalk4.default.cyan(projectName));
750
+ console.log(import_chalk4.default.dim(" Files:"), import_chalk4.default.white(analysis.metadata.totalFiles));
751
+ console.log(import_chalk4.default.dim(" Lines:"), import_chalk4.default.white(analysis.metadata.totalLines));
752
+ console.log();
753
+ if (Object.keys(analysis.techStack.languages).length > 0) {
754
+ console.log(import_chalk4.default.bold("\u{1F5E3}\uFE0F Languages:"));
755
+ for (const [lang, percent] of Object.entries(analysis.techStack.languages)) {
756
+ const bar = createProgressBar(percent, 20);
757
+ console.log(` ${import_chalk4.default.cyan(lang.padEnd(15))} ${bar} ${percent}%`);
758
+ }
759
+ console.log();
760
+ }
761
+ if (analysis.techStack.frameworks.length > 0) {
762
+ console.log(import_chalk4.default.bold("\u26A1 Frameworks:"));
763
+ analysis.techStack.frameworks.forEach((fw) => {
764
+ console.log(` \u2022 ${import_chalk4.default.green(fw)}`);
765
+ });
766
+ console.log();
767
+ }
768
+ if (analysis.entryPoints.length > 0) {
769
+ console.log(import_chalk4.default.bold("\u{1F680} Entry Points:"));
770
+ analysis.entryPoints.slice(0, 5).forEach((ep) => {
771
+ console.log(` \u2022 ${import_chalk4.default.yellow(ep.name)} ${import_chalk4.default.dim(`(${ep.path})`)}`);
772
+ });
773
+ if (analysis.entryPoints.length > 5) {
774
+ console.log(import_chalk4.default.dim(` ... and ${analysis.entryPoints.length - 5} more`));
775
+ }
776
+ console.log();
777
+ }
778
+ if (analysis.documentation.length > 0) {
779
+ console.log(import_chalk4.default.bold("\u{1F4DA} Documentation:"));
780
+ analysis.documentation.forEach((doc) => {
781
+ const icon = getDocIcon(doc.type);
782
+ console.log(` ${icon} ${import_chalk4.default.cyan(doc.path)}`);
783
+ });
784
+ console.log();
785
+ }
786
+ console.log(import_chalk4.default.dim(" Knowledge base saved to:"), import_chalk4.default.cyan(".codmir/knowledge/"));
787
+ console.log();
788
+ console.log(import_chalk4.default.dim(" Try:"), import_chalk4.default.cyan('codmir ai --context "Explain the project structure"'));
789
+ console.log();
790
+ } catch (error) {
791
+ spinner.fail(import_chalk4.default.red("Analysis failed"));
792
+ console.error(error);
793
+ process.exit(1);
794
+ }
795
+ }
796
+ async function runRailwayAnalysis(projectPath, projectId) {
797
+ const token = getToken();
798
+ if (!token) {
799
+ console.error(import_chalk4.default.red("\u274C Not authenticated"));
800
+ console.log(import_chalk4.default.dim(" Run"), import_chalk4.default.cyan("codmir login"), import_chalk4.default.dim("first"));
801
+ process.exit(1);
802
+ }
803
+ const baseUrl = getBaseUrl();
804
+ const gitRemote = await getGitRemote(projectPath);
805
+ if (!gitRemote) {
806
+ console.error(import_chalk4.default.red("\u274C No git remote found"));
807
+ console.log(import_chalk4.default.dim(" Project must be a git repository with remote"));
808
+ process.exit(1);
809
+ }
810
+ console.log(import_chalk4.default.dim(" Repository:"), import_chalk4.default.cyan(gitRemote));
811
+ console.log();
812
+ const spinner = (0, import_ora.default)({
813
+ text: import_chalk4.default.dim("Starting Railway analysis..."),
814
+ color: "cyan"
815
+ }).start();
816
+ try {
817
+ const response = await fetch(`${baseUrl}/api/analysis/deep`, {
818
+ method: "POST",
819
+ headers: {
820
+ "Content-Type": "application/json",
821
+ "X-API-Key": token
822
+ },
823
+ body: JSON.stringify({
824
+ projectId,
825
+ repoUrl: gitRemote,
826
+ branch: await getGitBranch(projectPath)
827
+ })
828
+ });
829
+ if (!response.ok) {
830
+ throw new Error(`API error: ${response.status}`);
831
+ }
832
+ const { jobId, estimatedTime } = await response.json();
833
+ spinner.text = import_chalk4.default.dim(`Analysis in progress (${estimatedTime})...`);
834
+ await pollAnalysisStatus(baseUrl, token, jobId, spinner);
835
+ spinner.succeed(import_chalk4.default.green("Deep analysis complete!"));
836
+ console.log();
837
+ console.log(import_chalk4.default.dim(" Knowledge base synced from Railway"));
838
+ console.log(import_chalk4.default.dim(" Includes:"));
839
+ console.log(import_chalk4.default.dim(" \u2022 Full AST parsing"));
840
+ console.log(import_chalk4.default.dim(" \u2022 Code embeddings"));
841
+ console.log(import_chalk4.default.dim(" \u2022 Best practices report"));
842
+ console.log(import_chalk4.default.dim(" \u2022 Generated documentation"));
843
+ console.log();
844
+ } catch (error) {
845
+ spinner.fail(import_chalk4.default.red("Railway analysis failed"));
846
+ console.error(error);
847
+ process.exit(1);
848
+ }
849
+ }
850
+ async function pollAnalysisStatus(baseUrl, token, jobId, spinner) {
851
+ const maxAttempts = 120;
852
+ let attempts = 0;
853
+ while (attempts < maxAttempts) {
854
+ await new Promise((resolve) => setTimeout(resolve, 5e3));
855
+ const response = await fetch(`${baseUrl}/api/analysis/deep/${jobId}`, {
856
+ headers: { "X-API-Key": token }
857
+ });
858
+ const { status, progress } = await response.json();
859
+ spinner.text = import_chalk4.default.dim(`${status}... ${progress || ""}%`);
860
+ if (status === "complete") {
861
+ return;
862
+ }
863
+ if (status === "failed") {
864
+ throw new Error("Analysis failed on Railway");
865
+ }
866
+ attempts++;
867
+ }
868
+ throw new Error("Analysis timeout");
869
+ }
870
+ async function getGitRemote(projectPath) {
871
+ try {
872
+ const { execSync } = require("child_process");
873
+ const remote = execSync("git config --get remote.origin.url", {
874
+ cwd: projectPath,
875
+ encoding: "utf-8"
876
+ }).trim();
877
+ return remote;
878
+ } catch {
879
+ return null;
880
+ }
881
+ }
882
+ async function getGitBranch(projectPath) {
883
+ try {
884
+ const { execSync } = require("child_process");
885
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
886
+ cwd: projectPath,
887
+ encoding: "utf-8"
888
+ }).trim();
889
+ return branch;
890
+ } catch {
891
+ return "main";
892
+ }
893
+ }
894
+ function createProgressBar(percent, width) {
895
+ const filled = Math.round(percent / 100 * width);
896
+ const empty = width - filled;
897
+ return import_chalk4.default.cyan("\u2588".repeat(filled)) + import_chalk4.default.dim("\u2591".repeat(empty));
898
+ }
899
+ function getDocIcon(type) {
900
+ const icons = {
901
+ readme: "\u{1F4D6}",
902
+ changelog: "\u{1F4DD}",
903
+ contributing: "\u{1F91D}",
904
+ license: "\u2696\uFE0F",
905
+ other: "\u{1F4C4}"
906
+ };
907
+ return icons[type] || "\u{1F4C4}";
908
+ }
909
+ var import_chalk4, import_ora, import_prompts;
910
+ var init_analyze = __esm({
911
+ "src/cli/commands/analyze.ts"() {
912
+ "use strict";
913
+ import_chalk4 = __toESM(require("chalk"));
914
+ import_ora = __toESM(require("ora"));
915
+ import_prompts = __toESM(require("prompts"));
916
+ init_config();
917
+ init_local();
918
+ init_storage();
919
+ }
920
+ });
921
+
922
+ // src/cli/index.ts
923
+ var import_commander = require("commander");
102
924
 
103
925
  // src/cli/utils/auth.ts
926
+ var import_open = __toESM(require("open"));
927
+ var readline = __toESM(require("readline"));
928
+ init_config();
104
929
  function promptForToken() {
105
930
  return new Promise((resolve) => {
106
931
  const rl = readline.createInterface({
@@ -142,7 +967,8 @@ async function startOAuthFlow(baseUrl = "https://codmir.com") {
142
967
  if (!userResponse.ok) {
143
968
  throw new Error("Invalid token or authentication failed");
144
969
  }
145
- const user = await userResponse.json();
970
+ const response = await userResponse.json();
971
+ const user = response.data || response;
146
972
  return {
147
973
  token,
148
974
  user: {
@@ -166,7 +992,8 @@ async function authenticateWithToken(token, baseUrl = "https://codmir.com") {
166
992
  if (!response.ok) {
167
993
  throw new Error("Invalid token");
168
994
  }
169
- const user = await response.json();
995
+ const responseData = await response.json();
996
+ const user = responseData.data || responseData;
170
997
  return {
171
998
  token,
172
999
  user: {
@@ -189,6 +1016,7 @@ function saveAuth(authResult) {
189
1016
  }
190
1017
 
191
1018
  // src/cli/commands/login.ts
1019
+ init_config();
192
1020
  var import_chalk = __toESM(require("chalk"));
193
1021
  async function loginCommand(options) {
194
1022
  if (isAuthenticated()) {
@@ -201,11 +1029,16 @@ async function loginCommand(options) {
201
1029
  console.log(import_chalk.default.dim(" Authenticate to start using codmir CLI\n"));
202
1030
  try {
203
1031
  let authResult;
1032
+ const baseUrl = getBaseUrl();
1033
+ if (baseUrl !== "https://codmir.com") {
1034
+ console.log(import_chalk.default.dim(` Connecting to: ${baseUrl}
1035
+ `));
1036
+ }
204
1037
  if (options.token) {
205
1038
  console.log(import_chalk.default.dim(" Authenticating with provided token..."));
206
- authResult = await authenticateWithToken(options.token);
1039
+ authResult = await authenticateWithToken(options.token, baseUrl);
207
1040
  } else {
208
- authResult = await startOAuthFlow();
1041
+ authResult = await startOAuthFlow(baseUrl);
209
1042
  }
210
1043
  saveAuth(authResult);
211
1044
  console.log(import_chalk.default.green("\n\u2705 Successfully logged in!"));
@@ -218,6 +1051,42 @@ async function loginCommand(options) {
218
1051
  }
219
1052
  }
220
1053
 
1054
+ // src/cli/commands/logout.ts
1055
+ init_config();
1056
+ var import_chalk2 = __toESM(require("chalk"));
1057
+ async function logoutCommand() {
1058
+ if (!isAuthenticated()) {
1059
+ console.log(import_chalk2.default.yellow("\u26A0\uFE0F Not logged in"));
1060
+ return;
1061
+ }
1062
+ const config = readConfig();
1063
+ clearConfig();
1064
+ console.log(import_chalk2.default.green("\u2705 Successfully logged out"));
1065
+ if (config.email) {
1066
+ console.log(import_chalk2.default.dim(" Goodbye,"), import_chalk2.default.bold(config.email));
1067
+ }
1068
+ }
1069
+
1070
+ // src/cli/commands/whoami.ts
1071
+ init_config();
1072
+ var import_chalk3 = __toESM(require("chalk"));
1073
+ async function whoamiCommand() {
1074
+ if (!isAuthenticated()) {
1075
+ console.error(import_chalk3.default.red("\u274C Not authenticated"));
1076
+ console.log(import_chalk3.default.dim(" Run"), import_chalk3.default.cyan("codmir login"), import_chalk3.default.dim("first"));
1077
+ process.exit(1);
1078
+ }
1079
+ const config = readConfig();
1080
+ console.log(import_chalk3.default.bold("\n\u{1F464} Current User\n"));
1081
+ console.log(import_chalk3.default.dim(" Name:"), import_chalk3.default.bold(config.name || "N/A"));
1082
+ console.log(import_chalk3.default.dim(" Email:"), import_chalk3.default.bold(config.email || "N/A"));
1083
+ console.log(import_chalk3.default.dim(" User ID:"), import_chalk3.default.dim(config.userId || "N/A"));
1084
+ if (config.lastLogin) {
1085
+ const lastLogin = new Date(config.lastLogin);
1086
+ console.log(import_chalk3.default.dim(" Last Login:"), import_chalk3.default.dim(lastLogin.toLocaleString()));
1087
+ }
1088
+ }
1089
+
221
1090
  // src/client.ts
222
1091
  var CodmirClient = class {
223
1092
  constructor(config) {
@@ -229,12 +1098,13 @@ var CodmirClient = class {
229
1098
  };
230
1099
  this.tickets = new TicketsAPI(this.config);
231
1100
  this.testCases = new TestCasesAPI(this.config);
1101
+ this.testing = new TestingAPI(this.config);
232
1102
  }
233
1103
  /**
234
1104
  * Make an HTTP request to the codmir API
235
1105
  */
236
- async request(method, path3, body) {
237
- const url = `${this.config.baseUrl}${path3}`;
1106
+ async request(method, path8, body) {
1107
+ const url = `${this.config.baseUrl}${path8}`;
238
1108
  const headers = {
239
1109
  "Content-Type": "application/json",
240
1110
  ...this.config.headers
@@ -291,8 +1161,8 @@ var TicketsAPI = class {
291
1161
  constructor(config) {
292
1162
  this.config = config;
293
1163
  }
294
- async request(method, path3, body) {
295
- const url = `${this.config.baseUrl}${path3}`;
1164
+ async request(method, path8, body) {
1165
+ const url = `${this.config.baseUrl}${path8}`;
296
1166
  const headers = {
297
1167
  "Content-Type": "application/json",
298
1168
  ...this.config.headers
@@ -371,8 +1241,8 @@ var TestCasesAPI = class {
371
1241
  constructor(config) {
372
1242
  this.config = config;
373
1243
  }
374
- async request(method, path3, body) {
375
- const url = `${this.config.baseUrl}${path3}`;
1244
+ async request(method, path8, body) {
1245
+ const url = `${this.config.baseUrl}${path8}`;
376
1246
  const headers = {
377
1247
  "Content-Type": "application/json",
378
1248
  ...this.config.headers
@@ -487,61 +1357,200 @@ var TestCasesAPI = class {
487
1357
  );
488
1358
  }
489
1359
  };
490
-
491
- // src/cli/commands/link.ts
492
- var import_chalk2 = __toESM(require("chalk"));
493
- var import_prompts = __toESM(require("prompts"));
494
- async function linkCommand(options) {
495
- const token = getToken();
496
- if (!token) {
497
- console.error(import_chalk2.default.red("\u274C Not authenticated"));
498
- console.log(import_chalk2.default.dim(" Run"), import_chalk2.default.cyan("codmir login"), import_chalk2.default.dim("first"));
499
- process.exit(1);
1360
+ var TestingAPI = class {
1361
+ constructor(config) {
1362
+ this.config = config;
500
1363
  }
501
- console.log(import_chalk2.default.bold("\n\u{1F517} codmir link"));
502
- console.log(import_chalk2.default.dim(" Link this directory to a codmir project\n"));
503
- const existing = readProjectConfig();
504
- if (existing) {
505
- console.log(import_chalk2.default.yellow("\u26A0\uFE0F This directory is already linked to:"));
506
- console.log(import_chalk2.default.dim(" Project ID:"), import_chalk2.default.bold(existing.projectId));
507
- if (existing.projectName) {
508
- console.log(import_chalk2.default.dim(" Project:"), import_chalk2.default.bold(existing.projectName));
509
- }
510
- const { overwrite } = await (0, import_prompts.default)({
511
- type: "confirm",
512
- name: "overwrite",
513
- message: "Do you want to overwrite this configuration?",
514
- initial: false
515
- });
516
- if (!overwrite) {
517
- console.log(import_chalk2.default.dim(" Cancelled"));
518
- return;
1364
+ async request(method, path8, body) {
1365
+ const url = `${this.config.baseUrl}${path8}`;
1366
+ const headers = {
1367
+ "Content-Type": "application/json",
1368
+ ...this.config.headers
1369
+ };
1370
+ if (this.config.apiKey) {
1371
+ headers["X-API-Key"] = this.config.apiKey;
519
1372
  }
520
- }
521
- try {
522
- const client = new CodmirClient({
523
- apiKey: token,
524
- baseUrl: process.env.CODMIR_API_URL || "https://codmir.com"
525
- });
526
- let projectId = options.project;
527
- let orgId = options.org;
528
- console.log(import_chalk2.default.dim(" Fetching your projects...\n"));
529
- const response = await fetch(`${process.env.CODMIR_API_URL || "https://codmir.com"}/api/projects`, {
530
- headers: {
531
- "Authorization": `Bearer ${token}`
532
- }
533
- });
534
- if (!response.ok) {
535
- throw new Error("Failed to fetch projects");
1373
+ const controller = new AbortController();
1374
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
1375
+ try {
1376
+ const response = await fetch(url, {
1377
+ method,
1378
+ headers,
1379
+ body: body ? JSON.stringify(body) : void 0,
1380
+ signal: controller.signal
1381
+ });
1382
+ clearTimeout(timeoutId);
1383
+ if (!response.ok) {
1384
+ const errorData = await response.json().catch(() => ({}));
1385
+ const error = new Error(errorData.error || `HTTP ${response.status}`);
1386
+ error.statusCode = response.status;
1387
+ error.response = errorData;
1388
+ throw error;
1389
+ }
1390
+ if (response.status === 204) {
1391
+ return {};
1392
+ }
1393
+ return await response.json();
1394
+ } catch (error) {
1395
+ clearTimeout(timeoutId);
1396
+ if (error.name === "AbortError") {
1397
+ const timeoutError = new Error("Request timeout");
1398
+ timeoutError.statusCode = 408;
1399
+ throw timeoutError;
1400
+ }
1401
+ throw error;
536
1402
  }
537
- const projects = await response.json();
1403
+ }
1404
+ async submitTestRun(payload) {
1405
+ return this.request("POST", "/api/testing/test-runs", payload);
1406
+ }
1407
+ async listTestRuns(projectId, options) {
1408
+ const params = new URLSearchParams({ projectId });
1409
+ if (options?.limit) params.set("limit", String(options.limit));
1410
+ if (options?.suite) params.set("suite", options.suite);
1411
+ const response = await this.request(
1412
+ "GET",
1413
+ `/api/testing/test-runs?${params.toString()}`
1414
+ );
1415
+ return response.runs || [];
1416
+ }
1417
+ async requestCoverageInsight(payload) {
1418
+ return this.request("POST", "/api/testing/coverage-insight", payload);
1419
+ }
1420
+ async listCoverageInsights(projectId, limit = 5) {
1421
+ const params = new URLSearchParams({ projectId, limit: String(limit) });
1422
+ const response = await this.request(
1423
+ "GET",
1424
+ `/api/testing/coverage-insight?${params.toString()}`
1425
+ );
1426
+ return response.reports || [];
1427
+ }
1428
+ };
1429
+
1430
+ // src/cli/commands/link.ts
1431
+ var import_prompts2 = __toESM(require("prompts"));
1432
+ var import_chalk5 = __toESM(require("chalk"));
1433
+ init_config();
1434
+
1435
+ // src/cli/utils/machine-id.ts
1436
+ var import_fs2 = __toESM(require("fs"));
1437
+ var import_path2 = __toESM(require("path"));
1438
+ var import_os2 = __toESM(require("os"));
1439
+ var import_crypto = __toESM(require("crypto"));
1440
+ init_config();
1441
+ var MACHINE_ID_FILE = import_path2.default.join(import_os2.default.homedir(), ".codmir", "machine-id");
1442
+ function getMachineId() {
1443
+ ensureConfigDir();
1444
+ if (import_fs2.default.existsSync(MACHINE_ID_FILE)) {
1445
+ try {
1446
+ return import_fs2.default.readFileSync(MACHINE_ID_FILE, "utf-8").trim();
1447
+ } catch (error) {
1448
+ console.error("Error reading machine ID:", error);
1449
+ }
1450
+ }
1451
+ const machineId = import_crypto.default.randomBytes(16).toString("hex");
1452
+ try {
1453
+ import_fs2.default.writeFileSync(MACHINE_ID_FILE, machineId);
1454
+ } catch (error) {
1455
+ console.error("Error saving machine ID:", error);
1456
+ }
1457
+ return machineId;
1458
+ }
1459
+ function getMachineInfo() {
1460
+ return {
1461
+ machineId: getMachineId(),
1462
+ hostname: import_os2.default.hostname(),
1463
+ platform: import_os2.default.platform(),
1464
+ arch: import_os2.default.arch(),
1465
+ nodeVersion: process.version,
1466
+ cliVersion: getPackageVersion()
1467
+ };
1468
+ }
1469
+ function getPackageVersion() {
1470
+ try {
1471
+ const pkg = require_package();
1472
+ return pkg.version;
1473
+ } catch {
1474
+ return "unknown";
1475
+ }
1476
+ }
1477
+ async function registerMachine(projectId, token, baseUrl) {
1478
+ const machineInfo = getMachineInfo();
1479
+ try {
1480
+ const response = await fetch(`${baseUrl}/api/cli/register`, {
1481
+ method: "POST",
1482
+ headers: {
1483
+ "Content-Type": "application/json",
1484
+ "X-API-Key": token
1485
+ },
1486
+ body: JSON.stringify({
1487
+ projectId,
1488
+ ...machineInfo,
1489
+ workingDirectory: process.cwd()
1490
+ })
1491
+ });
1492
+ if (!response.ok) {
1493
+ throw new Error(`Failed to register machine: ${response.statusText}`);
1494
+ }
1495
+ } catch (error) {
1496
+ console.error("[codmir] Failed to register machine:", error);
1497
+ }
1498
+ }
1499
+
1500
+ // src/cli/commands/link.ts
1501
+ async function linkCommand(options) {
1502
+ const token = getToken();
1503
+ if (!token) {
1504
+ console.error(import_chalk5.default.red("\u274C Not authenticated"));
1505
+ console.log(import_chalk5.default.dim(" Run"), import_chalk5.default.cyan("codmir login"), import_chalk5.default.dim("first"));
1506
+ process.exit(1);
1507
+ }
1508
+ console.log(import_chalk5.default.bold("\n\u{1F517} codmir link"));
1509
+ console.log(import_chalk5.default.dim(" Link this directory to a codmir project\n"));
1510
+ const existing = readProjectConfig();
1511
+ if (existing) {
1512
+ console.log(import_chalk5.default.yellow("\u26A0\uFE0F This directory is already linked to:"));
1513
+ console.log(import_chalk5.default.dim(" Project ID:"), import_chalk5.default.bold(existing.projectId));
1514
+ if (existing.projectName) {
1515
+ console.log(import_chalk5.default.dim(" Project:"), import_chalk5.default.bold(existing.projectName));
1516
+ }
1517
+ const { overwrite } = await (0, import_prompts2.default)({
1518
+ type: "confirm",
1519
+ name: "overwrite",
1520
+ message: "Do you want to overwrite this configuration?",
1521
+ initial: false
1522
+ });
1523
+ if (!overwrite) {
1524
+ console.log(import_chalk5.default.dim(" Cancelled"));
1525
+ return;
1526
+ }
1527
+ }
1528
+ try {
1529
+ const baseUrl = getBaseUrl();
1530
+ const client = new CodmirClient({
1531
+ apiKey: token,
1532
+ baseUrl
1533
+ });
1534
+ let projectId = options.project;
1535
+ let orgId = options.org;
1536
+ console.log(import_chalk5.default.dim(" Fetching your projects...\n"));
1537
+ const response = await fetch(`${baseUrl}/api/projects`, {
1538
+ headers: {
1539
+ "X-API-Key": token
1540
+ }
1541
+ });
1542
+ if (!response.ok) {
1543
+ throw new Error("Failed to fetch projects");
1544
+ }
1545
+ const responseData = await response.json();
1546
+ const projects = responseData.data || responseData;
538
1547
  if (!projectId) {
539
1548
  if (projects.length === 0) {
540
- console.log(import_chalk2.default.yellow("\u26A0\uFE0F You don't have any projects yet"));
541
- console.log(import_chalk2.default.dim(" Create a project at"), import_chalk2.default.cyan("https://codmir.com"));
1549
+ console.log(import_chalk5.default.yellow("\u26A0\uFE0F You don't have any projects yet"));
1550
+ console.log(import_chalk5.default.dim(" Create a project at"), import_chalk5.default.cyan("https://codmir.com"));
542
1551
  process.exit(1);
543
1552
  }
544
- const { selectedProject } = await (0, import_prompts.default)({
1553
+ const { selectedProject } = await (0, import_prompts2.default)({
545
1554
  type: "select",
546
1555
  name: "selectedProject",
547
1556
  message: "Select a project to link:",
@@ -552,14 +1561,14 @@ async function linkCommand(options) {
552
1561
  }))
553
1562
  });
554
1563
  if (!selectedProject) {
555
- console.log(import_chalk2.default.dim(" Cancelled"));
1564
+ console.log(import_chalk5.default.dim(" Cancelled"));
556
1565
  return;
557
1566
  }
558
1567
  projectId = selectedProject;
559
1568
  }
560
1569
  const project = projects.find((p) => p.id === projectId);
561
1570
  if (!project) {
562
- console.error(import_chalk2.default.red("\u274C Project not found"));
1571
+ console.error(import_chalk5.default.red("\u274C Project not found"));
563
1572
  process.exit(1);
564
1573
  }
565
1574
  writeProjectConfig({
@@ -567,67 +1576,52 @@ async function linkCommand(options) {
567
1576
  organizationId: project.organizationId || orgId,
568
1577
  projectName: project.name
569
1578
  });
570
- console.log(import_chalk2.default.green("\n\u2705 Successfully linked!"));
571
- console.log(import_chalk2.default.dim(" Project:"), import_chalk2.default.bold(project.name));
572
- console.log(import_chalk2.default.dim(" ID:"), import_chalk2.default.bold(project.id));
573
- console.log(import_chalk2.default.dim("\n Configuration saved to"), import_chalk2.default.cyan(".codmir.json"));
574
- console.log(import_chalk2.default.dim(" You can now use the codmir package in your project"));
1579
+ console.log(import_chalk5.default.green("\n\u2705 Successfully linked!"));
1580
+ console.log(import_chalk5.default.dim(" Project:"), import_chalk5.default.bold(project.name));
1581
+ console.log(import_chalk5.default.dim(" ID:"), import_chalk5.default.bold(project.id));
1582
+ console.log(import_chalk5.default.dim("\n Configuration saved to"), import_chalk5.default.cyan(".codmir.json"));
1583
+ console.log(import_chalk5.default.dim(" You can now use the codmir package in your project"));
1584
+ await registerMachine(project.id, token, baseUrl);
1585
+ console.log();
1586
+ const { analyze } = await (0, import_prompts2.default)({
1587
+ type: "confirm",
1588
+ name: "analyze",
1589
+ message: "Analyze codebase now? (Recommended)",
1590
+ initial: true
1591
+ });
1592
+ if (analyze) {
1593
+ console.log();
1594
+ const { analyzeCommand: analyzeCommand2 } = await Promise.resolve().then(() => (init_analyze(), analyze_exports));
1595
+ await analyzeCommand2({ mode: "local" });
1596
+ } else {
1597
+ console.log();
1598
+ console.log(import_chalk5.default.dim(" You can analyze later with:"), import_chalk5.default.cyan("codmir analyze"));
1599
+ console.log();
1600
+ }
575
1601
  } catch (error) {
576
- console.error(import_chalk2.default.red("\n\u274C Link failed:"), error instanceof Error ? error.message : "Unknown error");
577
- process.exit(1);
578
- }
579
- }
580
-
581
- // src/cli/commands/whoami.ts
582
- var import_chalk3 = __toESM(require("chalk"));
583
- async function whoamiCommand() {
584
- if (!isAuthenticated()) {
585
- console.error(import_chalk3.default.red("\u274C Not authenticated"));
586
- console.log(import_chalk3.default.dim(" Run"), import_chalk3.default.cyan("codmir login"), import_chalk3.default.dim("first"));
1602
+ console.log(import_chalk5.default.red("\n\u274C Failed to link project"));
1603
+ console.log(import_chalk5.default.dim(error.message));
587
1604
  process.exit(1);
588
1605
  }
589
- const config = readConfig();
590
- console.log(import_chalk3.default.bold("\n\u{1F464} Current User\n"));
591
- console.log(import_chalk3.default.dim(" Name:"), import_chalk3.default.bold(config.name || "N/A"));
592
- console.log(import_chalk3.default.dim(" Email:"), import_chalk3.default.bold(config.email || "N/A"));
593
- console.log(import_chalk3.default.dim(" User ID:"), import_chalk3.default.dim(config.userId || "N/A"));
594
- if (config.lastLogin) {
595
- const lastLogin = new Date(config.lastLogin);
596
- console.log(import_chalk3.default.dim(" Last Login:"), import_chalk3.default.dim(lastLogin.toLocaleString()));
597
- }
598
- }
599
-
600
- // src/cli/commands/logout.ts
601
- var import_chalk4 = __toESM(require("chalk"));
602
- async function logoutCommand() {
603
- if (!isAuthenticated()) {
604
- console.log(import_chalk4.default.yellow("\u26A0\uFE0F Not logged in"));
605
- return;
606
- }
607
- const config = readConfig();
608
- clearConfig();
609
- console.log(import_chalk4.default.green("\u2705 Successfully logged out"));
610
- if (config.email) {
611
- console.log(import_chalk4.default.dim(" Goodbye,"), import_chalk4.default.bold(config.email));
612
- }
613
1606
  }
614
1607
 
615
1608
  // src/cli/commands/init.ts
616
- var import_chalk5 = __toESM(require("chalk"));
617
- var import_fs2 = __toESM(require("fs"));
618
- var import_path2 = __toESM(require("path"));
1609
+ init_config();
1610
+ var import_chalk6 = __toESM(require("chalk"));
1611
+ var import_fs5 = __toESM(require("fs"));
1612
+ var import_path5 = __toESM(require("path"));
619
1613
  async function initCommand() {
620
- console.log(import_chalk5.default.bold("\n\u{1F389} Initialize codmir\n"));
1614
+ console.log(import_chalk6.default.bold("\n\u{1F389} Initialize codmir\n"));
621
1615
  const config = readProjectConfig();
622
1616
  if (!config) {
623
- console.log(import_chalk5.default.yellow("\u26A0\uFE0F Project not linked"));
624
- console.log(import_chalk5.default.dim(" Run"), import_chalk5.default.cyan("codmir link"), import_chalk5.default.dim("to link this project first"));
1617
+ console.log(import_chalk6.default.yellow("\u26A0\uFE0F Project not linked"));
1618
+ console.log(import_chalk6.default.dim(" Run"), import_chalk6.default.cyan("codmir link"), import_chalk6.default.dim("to link this project first"));
625
1619
  return;
626
1620
  }
627
- console.log(import_chalk5.default.dim(" Project:"), import_chalk5.default.bold(config.projectName || config.projectId));
628
- const exampleFile = import_path2.default.join(process.cwd(), "codmir.example.ts");
629
- if (import_fs2.default.existsSync(exampleFile)) {
630
- console.log(import_chalk5.default.yellow("\n\u26A0\uFE0F codmir.example.ts already exists"));
1621
+ console.log(import_chalk6.default.dim(" Project:"), import_chalk6.default.bold(config.projectName || config.projectId));
1622
+ const exampleFile = import_path5.default.join(process.cwd(), "codmir.example.ts");
1623
+ if (import_fs5.default.existsSync(exampleFile)) {
1624
+ console.log(import_chalk6.default.yellow("\n\u26A0\uFE0F codmir.example.ts already exists"));
631
1625
  } else {
632
1626
  const example = `import { CodmirClient } from 'codmir';
633
1627
 
@@ -669,56 +1663,920 @@ async function createTestCase() {
669
1663
  createTicket().catch(console.error);
670
1664
  createTestCase().catch(console.error);
671
1665
  `;
672
- import_fs2.default.writeFileSync(exampleFile, example);
673
- console.log(import_chalk5.default.green("\n\u2705 Created"), import_chalk5.default.cyan("codmir.example.ts"));
1666
+ import_fs5.default.writeFileSync(exampleFile, example);
1667
+ console.log(import_chalk6.default.green("\n\u2705 Created"), import_chalk6.default.cyan("codmir.example.ts"));
674
1668
  }
675
- console.log(import_chalk5.default.bold("\n\u{1F4DA} Next Steps:\n"));
676
- console.log(import_chalk5.default.dim(" 1."), "Install the package:", import_chalk5.default.cyan("npm install codmir"));
677
- console.log(import_chalk5.default.dim(" 2."), "Get your API token from:", import_chalk5.default.cyan("https://codmir.com/settings/tokens"));
678
- console.log(import_chalk5.default.dim(" 3."), "Set environment variable:", import_chalk5.default.cyan("CODMIR_API_KEY=your-token"));
679
- console.log(import_chalk5.default.dim(" 4."), "Check", import_chalk5.default.cyan("codmir.example.ts"), "for usage examples");
680
- console.log(import_chalk5.default.dim("\n \u{1F4D6} Documentation:"), import_chalk5.default.cyan("https://codmir.com/docs"));
1669
+ console.log(import_chalk6.default.bold("\n\u{1F4DA} Next Steps:\n"));
1670
+ console.log(import_chalk6.default.dim(" 1."), "Install the package:", import_chalk6.default.cyan("npm install codmir"));
1671
+ console.log(import_chalk6.default.dim(" 2."), "Get your API token from:", import_chalk6.default.cyan("https://codmir.com/settings/tokens"));
1672
+ console.log(import_chalk6.default.dim(" 3."), "Set environment variable:", import_chalk6.default.cyan("CODMIR_API_KEY=your-token"));
1673
+ console.log(import_chalk6.default.dim(" 4."), "Check", import_chalk6.default.cyan("codmir.example.ts"), "for usage examples");
1674
+ console.log(import_chalk6.default.dim("\n \u{1F4D6} Documentation:"), import_chalk6.default.cyan("https://codmir.com/docs"));
681
1675
  }
682
1676
 
683
1677
  // src/cli/commands/projects.ts
684
- var import_chalk6 = __toESM(require("chalk"));
1678
+ init_config();
1679
+ var import_chalk7 = __toESM(require("chalk"));
685
1680
  async function projectsCommand() {
686
1681
  const token = getToken();
687
1682
  if (!token) {
688
- console.error(import_chalk6.default.red("\u274C Not authenticated"));
689
- console.log(import_chalk6.default.dim(" Run"), import_chalk6.default.cyan("codmir login"), import_chalk6.default.dim("first"));
1683
+ console.error(import_chalk7.default.red("\u274C Not authenticated"));
1684
+ console.log(import_chalk7.default.dim(" Run"), import_chalk7.default.cyan("codmir login"), import_chalk7.default.dim("first"));
690
1685
  process.exit(1);
691
1686
  }
692
- console.log(import_chalk6.default.bold("\n\u{1F4C1} Your Projects\n"));
1687
+ console.log(import_chalk7.default.bold("\n\u{1F4C1} Your Projects\n"));
693
1688
  try {
694
- const response = await fetch(`${process.env.CODMIR_API_URL || "https://codmir.com"}/api/projects`, {
1689
+ const baseUrl = getBaseUrl();
1690
+ const response = await fetch(`${baseUrl}/api/projects`, {
695
1691
  headers: {
696
- "Authorization": `Bearer ${token}`
1692
+ "X-API-Key": token
697
1693
  }
698
1694
  });
699
1695
  if (!response.ok) {
700
1696
  throw new Error("Failed to fetch projects");
701
1697
  }
702
- const projects = await response.json();
1698
+ const responseData = await response.json();
1699
+ const projects = responseData.data || responseData;
703
1700
  if (projects.length === 0) {
704
- console.log(import_chalk6.default.yellow(" No projects found"));
705
- console.log(import_chalk6.default.dim(" Create one at"), import_chalk6.default.cyan("https://codmir.com"));
1701
+ console.log(import_chalk7.default.yellow(" No projects found"));
1702
+ console.log(import_chalk7.default.dim(" Create one at"), import_chalk7.default.cyan("https://codmir.com"));
706
1703
  return;
707
1704
  }
708
1705
  projects.forEach((project, index) => {
709
- console.log(import_chalk6.default.bold(` ${index + 1}. ${project.name}`), import_chalk6.default.dim(`(${project.key})`));
710
- console.log(import_chalk6.default.dim(" ID:"), project.id);
1706
+ console.log(import_chalk7.default.bold(` ${index + 1}. ${project.name}`), import_chalk7.default.dim(`(${project.key})`));
1707
+ console.log(import_chalk7.default.dim(" ID:"), project.id);
711
1708
  if (project.description) {
712
- console.log(import_chalk6.default.dim(" Description:"), project.description);
1709
+ console.log(import_chalk7.default.dim(" Description:"), project.description);
1710
+ }
1711
+ console.log("");
1712
+ });
1713
+ console.log(import_chalk7.default.dim(" Link a project:"), import_chalk7.default.cyan("codmir link"));
1714
+ } catch (error) {
1715
+ console.error(import_chalk7.default.red("\n\u274C Failed to fetch projects:"), error instanceof Error ? error.message : "Unknown error");
1716
+ process.exit(1);
1717
+ }
1718
+ }
1719
+
1720
+ // src/cli/commands/assistant.ts
1721
+ var import_chalk8 = __toESM(require("chalk"));
1722
+ var import_ora2 = __toESM(require("ora"));
1723
+ var import_prompts3 = __toESM(require("prompts"));
1724
+ var import_clipboardy = __toESM(require("clipboardy"));
1725
+ var import_fs6 = __toESM(require("fs"));
1726
+ var import_path6 = __toESM(require("path"));
1727
+ var import_form_data = __toESM(require("form-data"));
1728
+ init_config();
1729
+ async function assistantCommand(query, options = {}) {
1730
+ const token = getToken();
1731
+ if (!token) {
1732
+ console.error(import_chalk8.default.red("\u274C Not authenticated"));
1733
+ console.log(import_chalk8.default.dim(" Run"), import_chalk8.default.cyan("codmir login"), import_chalk8.default.dim("first"));
1734
+ process.exit(1);
1735
+ }
1736
+ const baseUrl = getBaseUrl();
1737
+ const conversationHistory = [];
1738
+ const context = getExecutionContext();
1739
+ if (context.isLinkedProject) {
1740
+ console.log(import_chalk8.default.dim(` \u{1F4C1} Project: ${context.projectConfig?.projectName || context.projectConfig?.projectId}`));
1741
+ console.log(import_chalk8.default.dim(` \u{1F517} Mode: ${import_chalk8.default.cyan("local")} (linked project)
1742
+ `));
1743
+ } else {
1744
+ console.log(import_chalk8.default.dim(` \u{1F310} Mode: ${import_chalk8.default.yellow("global")}
1745
+ `));
1746
+ }
1747
+ conversationHistory.push({
1748
+ role: "system",
1749
+ content: `You are codmir, an AI assistant for developers. You help with:
1750
+ - Code questions and debugging
1751
+ - Project setup and configuration
1752
+ - Best practices and patterns
1753
+ - Task automation
1754
+
1755
+ Be concise, practical, and code-focused. Use lowercase "codmir" for the brand.`
1756
+ });
1757
+ console.log(import_chalk8.default.bold("\n\u{1F916} codmir"));
1758
+ console.log(import_chalk8.default.dim(" AI assistant for developers\n"));
1759
+ if (query) {
1760
+ await handleQuery(query, conversationHistory, baseUrl, token, options);
1761
+ return;
1762
+ }
1763
+ console.log(import_chalk8.default.dim(" Type your question or"), import_chalk8.default.cyan("exit"), import_chalk8.default.dim("to quit"));
1764
+ console.log(import_chalk8.default.dim(" \u{1F4CB} Tip: Copy images to clipboard before asking for automatic upload"));
1765
+ console.log(import_chalk8.default.dim(' Example: "How do I setup authentication?"\n'));
1766
+ while (true) {
1767
+ const { userInput } = await (0, import_prompts3.default)({
1768
+ type: "text",
1769
+ name: "userInput",
1770
+ message: import_chalk8.default.cyan("codmir"),
1771
+ validate: (value) => value.length > 0 || "Please enter a question"
1772
+ });
1773
+ if (!userInput || userInput.toLowerCase() === "exit" || userInput.toLowerCase() === "quit") {
1774
+ console.log(import_chalk8.default.dim("\n Goodbye! \u{1F44B}\n"));
1775
+ break;
1776
+ }
1777
+ await handleQuery(userInput, conversationHistory, baseUrl, token, options);
1778
+ }
1779
+ }
1780
+ async function handleQuery(query, conversationHistory, baseUrl, token, options) {
1781
+ let imagePaths;
1782
+ const hasClipboardImage = await checkClipboardForImage();
1783
+ if (hasClipboardImage) {
1784
+ const { attachImage } = await (0, import_prompts3.default)({
1785
+ type: "confirm",
1786
+ name: "attachImage",
1787
+ message: import_chalk8.default.cyan("\u{1F4CB} Image detected in clipboard. Attach to message?"),
1788
+ initial: true
1789
+ });
1790
+ if (attachImage) {
1791
+ const imagePath = await saveClipboardImage();
1792
+ if (imagePath) {
1793
+ imagePaths = [imagePath];
1794
+ console.log(import_chalk8.default.green(" \u2713 Image attached from clipboard\n"));
1795
+ }
1796
+ }
1797
+ }
1798
+ conversationHistory.push({
1799
+ role: "user",
1800
+ content: query,
1801
+ images: imagePaths
1802
+ });
1803
+ const spinner = (0, import_ora2.default)({
1804
+ text: import_chalk8.default.dim("Thinking..."),
1805
+ color: "cyan",
1806
+ spinner: {
1807
+ interval: 80,
1808
+ frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"]
1809
+ }
1810
+ }).start();
1811
+ try {
1812
+ let response;
1813
+ if (imagePaths && imagePaths.length > 0) {
1814
+ const formData = new import_form_data.default();
1815
+ formData.append("query", query);
1816
+ formData.append("model", options.model || "gpt-4-turbo-preview");
1817
+ if (options.project) formData.append("project", options.project);
1818
+ if (options.context) formData.append("includeContext", "true");
1819
+ formData.append("messages", JSON.stringify(conversationHistory));
1820
+ for (const imagePath of imagePaths) {
1821
+ formData.append("images", import_fs6.default.createReadStream(imagePath));
1822
+ }
1823
+ response = await fetch(`${baseUrl}/api/assistant/chat`, {
1824
+ method: "POST",
1825
+ headers: {
1826
+ "X-API-Key": token,
1827
+ ...formData.getHeaders()
1828
+ },
1829
+ body: formData
1830
+ });
1831
+ } else {
1832
+ response = await fetch(`${baseUrl}/api/assistant/chat`, {
1833
+ method: "POST",
1834
+ headers: {
1835
+ "Content-Type": "application/json",
1836
+ "X-API-Key": token
1837
+ },
1838
+ body: JSON.stringify({
1839
+ messages: conversationHistory,
1840
+ model: options.model || "gpt-4-turbo-preview",
1841
+ project: options.project,
1842
+ includeContext: options.context
1843
+ })
1844
+ });
1845
+ }
1846
+ spinner.stop();
1847
+ if (!response.ok) {
1848
+ throw new Error(`API error: ${response.status}`);
1849
+ }
1850
+ const data = await response.json();
1851
+ const assistantMessage = data.message || data.content || "Sorry, I couldn't process that.";
1852
+ conversationHistory.push({
1853
+ role: "assistant",
1854
+ content: assistantMessage
1855
+ });
1856
+ console.log("\n" + renderResponse(assistantMessage) + "\n");
1857
+ } catch (error) {
1858
+ spinner.stop();
1859
+ console.error(import_chalk8.default.red("\n\u274C Error:"), error instanceof Error ? error.message : "Unknown error");
1860
+ console.log(import_chalk8.default.dim(" Try again or check your connection\n"));
1861
+ }
1862
+ }
1863
+ function renderResponse(message) {
1864
+ const indicator = getIndicator();
1865
+ const lines = message.split("\n");
1866
+ let output = "";
1867
+ lines.forEach((line, index) => {
1868
+ if (index === 0) {
1869
+ output += `${indicator} ${import_chalk8.default.white(line)}
1870
+ `;
1871
+ } else {
1872
+ output += ` ${import_chalk8.default.white(line)}
1873
+ `;
1874
+ }
1875
+ });
1876
+ return output;
1877
+ }
1878
+ function getIndicator() {
1879
+ const isDark = isTerminalDark();
1880
+ if (isDark) {
1881
+ return import_chalk8.default.whiteBright("\u25CF");
1882
+ } else {
1883
+ return import_chalk8.default.black("\u25CF");
1884
+ }
1885
+ }
1886
+ function isTerminalDark() {
1887
+ const colorScheme = process.env.COLORFGBG;
1888
+ if (colorScheme) {
1889
+ const parts = colorScheme.split(";");
1890
+ const bg = parseInt(parts[1] || "0");
1891
+ return bg < 8;
1892
+ }
1893
+ return true;
1894
+ }
1895
+ async function quickQueryCommand(query, options = {}) {
1896
+ const conversationHistory = [{
1897
+ role: "system",
1898
+ content: "You are codmir, a concise AI assistant for developers. Give short, practical answers."
1899
+ }];
1900
+ const token = getToken();
1901
+ if (!token) {
1902
+ console.error(import_chalk8.default.red("\u274C Not authenticated"));
1903
+ process.exit(1);
1904
+ }
1905
+ await handleQuery(query, conversationHistory, getBaseUrl(), token, options);
1906
+ }
1907
+ async function checkClipboardForImage() {
1908
+ try {
1909
+ const clipboardContent = await import_clipboardy.default.read();
1910
+ return clipboardContent.startsWith("data:image/") || clipboardContent.match(/\.(png|jpg|jpeg|gif|webp)$/i) !== null;
1911
+ } catch {
1912
+ return false;
1913
+ }
1914
+ }
1915
+ async function saveClipboardImage() {
1916
+ try {
1917
+ const clipboardContent = await import_clipboardy.default.read();
1918
+ if (clipboardContent.startsWith("data:image/")) {
1919
+ const matches = clipboardContent.match(/^data:image\/(\w+);base64,(.+)$/);
1920
+ if (!matches) return null;
1921
+ const ext = matches[1];
1922
+ const base64Data = matches[2];
1923
+ const buffer = Buffer.from(base64Data, "base64");
1924
+ const tmpPath = import_path6.default.join("/tmp", `codmir-assistant-${Date.now()}.${ext}`);
1925
+ import_fs6.default.writeFileSync(tmpPath, buffer);
1926
+ return tmpPath;
1927
+ }
1928
+ if (import_fs6.default.existsSync(clipboardContent)) {
1929
+ return clipboardContent;
1930
+ }
1931
+ return null;
1932
+ } catch (error) {
1933
+ console.error(import_chalk8.default.dim(" Failed to save clipboard image"));
1934
+ return null;
1935
+ }
1936
+ }
1937
+
1938
+ // src/cli/commands/ticket.ts
1939
+ var import_chalk9 = __toESM(require("chalk"));
1940
+ var import_prompts4 = __toESM(require("prompts"));
1941
+ var import_clipboardy2 = __toESM(require("clipboardy"));
1942
+ var import_fs7 = __toESM(require("fs"));
1943
+ var import_path7 = __toESM(require("path"));
1944
+ var import_form_data2 = __toESM(require("form-data"));
1945
+ init_config();
1946
+ async function createTicketCommand() {
1947
+ const token = getToken();
1948
+ if (!token) {
1949
+ console.error(import_chalk9.default.red("\u274C Not authenticated"));
1950
+ console.log(import_chalk9.default.dim(" Run"), import_chalk9.default.cyan("codmir login"), import_chalk9.default.dim("first"));
1951
+ process.exit(1);
1952
+ }
1953
+ console.log(import_chalk9.default.bold("\n\u{1F3AB} Create Ticket\n"));
1954
+ try {
1955
+ const linkedProject = getProjectConfig();
1956
+ let selectedProject = null;
1957
+ let selectedOrg = null;
1958
+ if (linkedProject) {
1959
+ const { useLinked } = await (0, import_prompts4.default)({
1960
+ type: "confirm",
1961
+ name: "useLinked",
1962
+ message: `Use linked project: ${import_chalk9.default.cyan(linkedProject.name)}?`,
1963
+ initial: true
1964
+ });
1965
+ if (useLinked) {
1966
+ selectedProject = linkedProject;
713
1967
  }
1968
+ }
1969
+ if (!selectedProject) {
1970
+ const selection = await navigateAndSelectProject(token);
1971
+ if (!selection) {
1972
+ console.log(import_chalk9.default.yellow("\n\u26A0\uFE0F Cancelled"));
1973
+ return;
1974
+ }
1975
+ selectedProject = selection.project;
1976
+ selectedOrg = selection.organization;
1977
+ }
1978
+ console.log(import_chalk9.default.green("\n\u2713 Project:"), import_chalk9.default.cyan(selectedProject.name));
1979
+ const ticketData = await collectTicketDetails();
1980
+ if (!ticketData) {
1981
+ console.log(import_chalk9.default.yellow("\n\u26A0\uFE0F Cancelled"));
1982
+ return;
1983
+ }
1984
+ const hasClipboardImage = await checkClipboardForImage2();
1985
+ if (hasClipboardImage) {
1986
+ const { attachImage } = await (0, import_prompts4.default)({
1987
+ type: "confirm",
1988
+ name: "attachImage",
1989
+ message: import_chalk9.default.cyan("\u{1F4CB} Image detected in clipboard. Attach to ticket?"),
1990
+ initial: true
1991
+ });
1992
+ if (attachImage) {
1993
+ const imagePath = await saveClipboardImage2();
1994
+ if (imagePath) {
1995
+ ticketData.images = [imagePath];
1996
+ console.log(import_chalk9.default.green("\u2713 Image attached"));
1997
+ }
1998
+ }
1999
+ }
2000
+ await createTicket(token, selectedProject.id, ticketData);
2001
+ } catch (error) {
2002
+ console.error(import_chalk9.default.red("\n\u274C Error:"), error instanceof Error ? error.message : "Unknown error");
2003
+ process.exit(1);
2004
+ }
2005
+ }
2006
+ async function navigateAndSelectProject(token) {
2007
+ const baseUrl = getBaseUrl();
2008
+ console.log(import_chalk9.default.dim("\n Loading organizations..."));
2009
+ const orgsResponse = await fetch(`${baseUrl}/api/user/organizations`, {
2010
+ headers: { "X-API-Key": token }
2011
+ });
2012
+ if (!orgsResponse.ok) {
2013
+ throw new Error("Failed to fetch organizations");
2014
+ }
2015
+ const { organizations } = await orgsResponse.json();
2016
+ if (!organizations || organizations.length === 0) {
2017
+ console.log(import_chalk9.default.yellow("\n\u26A0\uFE0F No organizations found"));
2018
+ return null;
2019
+ }
2020
+ console.log(import_chalk9.default.bold("\n\u{1F579}\uFE0F Select Organization"));
2021
+ console.log(import_chalk9.default.dim(" Use \u2191\u2193 arrows to navigate, Enter to select\n"));
2022
+ const { orgIndex } = await (0, import_prompts4.default)({
2023
+ type: "select",
2024
+ name: "orgIndex",
2025
+ message: import_chalk9.default.cyan("Organization"),
2026
+ choices: organizations.map((org, index) => ({
2027
+ title: `${getJoystickIndicator(index)} ${org.name}`,
2028
+ value: index,
2029
+ description: org.slug
2030
+ })),
2031
+ initial: 0
2032
+ });
2033
+ if (orgIndex === void 0) return null;
2034
+ const selectedOrg = organizations[orgIndex];
2035
+ console.log(import_chalk9.default.dim("\n Loading projects..."));
2036
+ const projectsResponse = await fetch(
2037
+ `${baseUrl}/api/organizations/${selectedOrg.id}/projects`,
2038
+ { headers: { "X-API-Key": token } }
2039
+ );
2040
+ if (!projectsResponse.ok) {
2041
+ throw new Error("Failed to fetch projects");
2042
+ }
2043
+ const projectsData = await projectsResponse.json();
2044
+ const projects = projectsData.data || projectsData;
2045
+ if (!projects || projects.length === 0) {
2046
+ console.log(import_chalk9.default.yellow("\n\u26A0\uFE0F No projects found in this organization"));
2047
+ return null;
2048
+ }
2049
+ console.log(import_chalk9.default.bold("\n\u{1F579}\uFE0F Select Project"));
2050
+ console.log(import_chalk9.default.dim(" Use \u2191\u2193 arrows to navigate, Enter to select\n"));
2051
+ const { projectIndex } = await (0, import_prompts4.default)({
2052
+ type: "select",
2053
+ name: "projectIndex",
2054
+ message: import_chalk9.default.cyan("Project"),
2055
+ choices: projects.map((project, index) => ({
2056
+ title: `${getJoystickIndicator(index)} ${project.name}`,
2057
+ value: index,
2058
+ description: project.key
2059
+ })),
2060
+ initial: 0
2061
+ });
2062
+ if (projectIndex === void 0) return null;
2063
+ return {
2064
+ organization: selectedOrg,
2065
+ project: projects[projectIndex]
2066
+ };
2067
+ }
2068
+ function getJoystickIndicator(index) {
2069
+ const indicators = ["\u25B8", "\u25B9", "\u25B8", "\u25B9"];
2070
+ return import_chalk9.default.cyan(indicators[index % indicators.length]);
2071
+ }
2072
+ async function collectTicketDetails() {
2073
+ console.log(import_chalk9.default.bold("\n\u{1F4DD} Ticket Details\n"));
2074
+ const responses = await (0, import_prompts4.default)([
2075
+ {
2076
+ type: "text",
2077
+ name: "title",
2078
+ message: import_chalk9.default.cyan("Title"),
2079
+ validate: (value) => value.length > 0 || "Title is required"
2080
+ },
2081
+ {
2082
+ type: "text",
2083
+ name: "description",
2084
+ message: import_chalk9.default.cyan("Description"),
2085
+ initial: ""
2086
+ },
2087
+ {
2088
+ type: "select",
2089
+ name: "type",
2090
+ message: import_chalk9.default.cyan("Type"),
2091
+ choices: [
2092
+ { title: "\u{1F41B} Bug", value: "BUG" },
2093
+ { title: "\u2728 Feature", value: "FEATURE" },
2094
+ { title: "\u{1F4CB} Task", value: "TASK" },
2095
+ { title: "\u{1F527} Improvement", value: "IMPROVEMENT" }
2096
+ ],
2097
+ initial: 0
2098
+ },
2099
+ {
2100
+ type: "select",
2101
+ name: "priority",
2102
+ message: import_chalk9.default.cyan("Priority"),
2103
+ choices: [
2104
+ { title: "\u{1F7E2} Low", value: "LOW" },
2105
+ { title: "\u{1F7E1} Medium", value: "MEDIUM" },
2106
+ { title: "\u{1F7E0} High", value: "HIGH" },
2107
+ { title: "\u{1F534} Critical", value: "CRITICAL" }
2108
+ ],
2109
+ initial: 1
2110
+ }
2111
+ ]);
2112
+ if (!responses.title) return null;
2113
+ return responses;
2114
+ }
2115
+ async function checkClipboardForImage2() {
2116
+ try {
2117
+ const clipboardContent = await import_clipboardy2.default.read();
2118
+ return clipboardContent.startsWith("data:image/") || clipboardContent.match(/\.(png|jpg|jpeg|gif|webp)$/i) !== null;
2119
+ } catch {
2120
+ return false;
2121
+ }
2122
+ }
2123
+ async function saveClipboardImage2() {
2124
+ try {
2125
+ const clipboardContent = await import_clipboardy2.default.read();
2126
+ if (clipboardContent.startsWith("data:image/")) {
2127
+ const matches = clipboardContent.match(/^data:image\/(\w+);base64,(.+)$/);
2128
+ if (!matches) return null;
2129
+ const ext = matches[1];
2130
+ const base64Data = matches[2];
2131
+ const buffer = Buffer.from(base64Data, "base64");
2132
+ const tmpPath = import_path7.default.join("/tmp", `codmir-ticket-${Date.now()}.${ext}`);
2133
+ import_fs7.default.writeFileSync(tmpPath, buffer);
2134
+ return tmpPath;
2135
+ }
2136
+ if (import_fs7.default.existsSync(clipboardContent)) {
2137
+ return clipboardContent;
2138
+ }
2139
+ return null;
2140
+ } catch (error) {
2141
+ console.error(import_chalk9.default.dim(" Failed to save clipboard image"));
2142
+ return null;
2143
+ }
2144
+ }
2145
+ async function createTicket(token, projectId, ticketData) {
2146
+ const baseUrl = getBaseUrl();
2147
+ console.log(import_chalk9.default.dim("\n Creating ticket..."));
2148
+ try {
2149
+ let response;
2150
+ if (ticketData.images && ticketData.images.length > 0) {
2151
+ const formData = new import_form_data2.default();
2152
+ formData.append("title", ticketData.title);
2153
+ if (ticketData.description) formData.append("description", ticketData.description);
2154
+ if (ticketData.type) formData.append("type", ticketData.type);
2155
+ if (ticketData.priority) formData.append("priority", ticketData.priority);
2156
+ for (const imagePath of ticketData.images) {
2157
+ formData.append("images", import_fs7.default.createReadStream(imagePath));
2158
+ }
2159
+ response = await fetch(`${baseUrl}/api/project/${projectId}/tickets`, {
2160
+ method: "POST",
2161
+ headers: {
2162
+ "X-API-Key": token,
2163
+ ...formData.getHeaders()
2164
+ },
2165
+ body: formData
2166
+ });
2167
+ } else {
2168
+ response = await fetch(`${baseUrl}/api/project/${projectId}/tickets`, {
2169
+ method: "POST",
2170
+ headers: {
2171
+ "Content-Type": "application/json",
2172
+ "X-API-Key": token
2173
+ },
2174
+ body: JSON.stringify(ticketData)
2175
+ });
2176
+ }
2177
+ if (!response.ok) {
2178
+ const error = await response.text();
2179
+ throw new Error(`Failed to create ticket: ${error}`);
2180
+ }
2181
+ const ticket = await response.json();
2182
+ const ticketData_response = ticket.data || ticket;
2183
+ console.log(import_chalk9.default.green("\n\u2705 Ticket created successfully!\n"));
2184
+ console.log(import_chalk9.default.bold(" Ticket:"), import_chalk9.default.cyan(`#${ticketData_response.key || ticketData_response.id}`));
2185
+ console.log(import_chalk9.default.bold(" Title:"), ticketData_response.title);
2186
+ console.log(import_chalk9.default.bold(" Type:"), getTypeEmoji(ticketData_response.type), ticketData_response.type);
2187
+ console.log(import_chalk9.default.bold(" Priority:"), getPriorityEmoji(ticketData_response.priority), ticketData_response.priority);
2188
+ if (ticketData.images && ticketData.images.length > 0) {
2189
+ console.log(import_chalk9.default.bold(" Attachments:"), import_chalk9.default.green(`${ticketData.images.length} image(s)`));
2190
+ }
2191
+ console.log();
2192
+ } catch (error) {
2193
+ throw error;
2194
+ }
2195
+ }
2196
+ function getTypeEmoji(type) {
2197
+ const emojis = {
2198
+ BUG: "\u{1F41B}",
2199
+ FEATURE: "\u2728",
2200
+ TASK: "\u{1F4CB}",
2201
+ IMPROVEMENT: "\u{1F527}"
2202
+ };
2203
+ return emojis[type] || "\u{1F4CB}";
2204
+ }
2205
+ function getPriorityEmoji(priority) {
2206
+ const emojis = {
2207
+ LOW: "\u{1F7E2}",
2208
+ MEDIUM: "\u{1F7E1}",
2209
+ HIGH: "\u{1F7E0}",
2210
+ CRITICAL: "\u{1F534}"
2211
+ };
2212
+ return emojis[priority] || "\u{1F7E1}";
2213
+ }
2214
+
2215
+ // src/cli/commands/council.ts
2216
+ var import_chalk10 = __toESM(require("chalk"));
2217
+ var import_ora3 = __toESM(require("ora"));
2218
+ var import_prompts5 = __toESM(require("prompts"));
2219
+ var import_ws = __toESM(require("ws"));
2220
+ init_config();
2221
+ var ROLE_ICONS = {
2222
+ architect: "\u{1F3D7}\uFE0F ",
2223
+ security: "\u{1F512}",
2224
+ implementer: "\u{1F4BB}",
2225
+ reviewer: "\u{1F50D}"
2226
+ };
2227
+ var ROLE_COLORS = {
2228
+ architect: import_chalk10.default.blue,
2229
+ security: import_chalk10.default.red,
2230
+ implementer: import_chalk10.default.green,
2231
+ reviewer: import_chalk10.default.yellow
2232
+ };
2233
+ async function councilCommand(task, options = {}) {
2234
+ try {
2235
+ printHeader();
2236
+ if (!task) {
2237
+ const response = await (0, import_prompts5.default)({
2238
+ type: "text",
2239
+ name: "task",
2240
+ message: import_chalk10.default.cyan("\u{1F916} What would you like the council to build?"),
2241
+ validate: (value) => value.length > 0 || "Task cannot be empty"
2242
+ });
2243
+ if (!response.task) {
2244
+ console.log(import_chalk10.default.yellow("\nCouncil session cancelled."));
2245
+ process.exit(0);
2246
+ }
2247
+ task = response.task;
2248
+ }
2249
+ console.log("");
2250
+ const token = getToken();
2251
+ const baseUrl = getBaseUrl();
2252
+ if (!token) {
2253
+ console.log(import_chalk10.default.red("\u274C Not authenticated. Please run: codmir login"));
2254
+ process.exit(1);
2255
+ }
2256
+ const mode = options.local ? "local" : "lambda";
2257
+ const apiUrl = baseUrl.replace("http", "ws") + "/council";
2258
+ console.log(import_chalk10.default.gray(`Mode: ${mode === "lambda" ? "AWS Lambda (Serverless)" : "Local LAN Cluster"}`));
2259
+ console.log("");
2260
+ const ws = new import_ws.default(apiUrl, {
2261
+ headers: {
2262
+ Authorization: `Bearer ${token}`
2263
+ }
2264
+ });
2265
+ let sessionId = null;
2266
+ let currentSpinner = null;
2267
+ let roundResponses = [];
2268
+ ws.on("open", () => {
2269
+ ws.send(JSON.stringify({
2270
+ type: "create-session",
2271
+ problem: task,
2272
+ mode,
2273
+ options: {
2274
+ initialMembers: parseInt(options.members || "2"),
2275
+ timeout: parseInt(options.timeout || "300") * 1e3
2276
+ }
2277
+ }));
2278
+ });
2279
+ ws.on("message", (data) => {
2280
+ const message = JSON.parse(data.toString());
2281
+ handleCouncilMessage(message, {
2282
+ currentSpinner,
2283
+ roundResponses,
2284
+ onSpinnerUpdate: (spinner) => {
2285
+ currentSpinner = spinner;
2286
+ },
2287
+ onExit: () => process.exit(0)
2288
+ });
2289
+ });
2290
+ ws.on("error", (error) => {
2291
+ if (currentSpinner) currentSpinner.fail();
2292
+ console.log(import_chalk10.default.red(`
2293
+ \u274C Connection error: ${error.message}`));
2294
+ process.exit(1);
2295
+ });
2296
+ ws.on("close", () => {
2297
+ if (currentSpinner) currentSpinner.stop();
2298
+ });
2299
+ } catch (error) {
2300
+ console.log(import_chalk10.default.red(`
2301
+ \u274C Error: ${error instanceof Error ? error.message : "Unknown error"}`));
2302
+ process.exit(1);
2303
+ }
2304
+ }
2305
+ function printHeader() {
2306
+ console.log("");
2307
+ console.log(import_chalk10.default.blue.bold("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2308
+ console.log(import_chalk10.default.blue.bold("\u2551 Claude Council - AI Collaboration \u2551"));
2309
+ console.log(import_chalk10.default.blue.bold('\u2551 "Preventing wasted engineering time" \u2551'));
2310
+ console.log(import_chalk10.default.blue.bold("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2311
+ console.log("");
2312
+ }
2313
+ function handleCouncilMessage(message, context) {
2314
+ const { currentSpinner, roundResponses, onSpinnerUpdate, onExit } = context;
2315
+ switch (message.type) {
2316
+ case "session-created":
2317
+ console.log(import_chalk10.default.yellow("\u{1F3AD} Assembling Claude Council..."));
2318
+ console.log("");
2319
+ break;
2320
+ case "spawning-member":
2321
+ const spinner = (0, import_ora3.default)({
2322
+ text: `Spawning ${message.role} in Docker container on ${message.location || "Lambda"}...`,
2323
+ color: "cyan"
2324
+ }).start();
2325
+ onSpinnerUpdate(spinner);
2326
+ break;
2327
+ case "member-ready":
2328
+ if (currentSpinner) {
2329
+ currentSpinner.succeed(
2330
+ import_chalk10.default.green(`\u2713 ${capitalize(message.role)} ready (${message.memberId})`)
2331
+ );
2332
+ }
2333
+ break;
2334
+ case "all-members-ready":
714
2335
  console.log("");
2336
+ console.log(import_chalk10.default.cyan("\u{1F4AC} Council Session Started"));
2337
+ console.log(import_chalk10.default.gray(` Session ID: ${message.sessionId}`));
2338
+ console.log(import_chalk10.default.gray(` Members: ${message.memberCount}`));
2339
+ console.log("");
2340
+ break;
2341
+ case "round-start":
2342
+ printRoundHeader(message.round ?? 0);
2343
+ roundResponses.length = 0;
2344
+ break;
2345
+ case "member-thinking": {
2346
+ const role = message.role ?? "architect";
2347
+ const thinkSpinner = (0, import_ora3.default)({
2348
+ text: `${ROLE_ICONS[role]} ${capitalize(role)} is analyzing...`,
2349
+ color: "yellow"
2350
+ }).start();
2351
+ onSpinnerUpdate(thinkSpinner);
2352
+ break;
2353
+ }
2354
+ case "member-response": {
2355
+ if (currentSpinner) currentSpinner.stop();
2356
+ const role = message.role ?? "architect";
2357
+ const roleColor = ROLE_COLORS[role] || import_chalk10.default.white;
2358
+ const icon = ROLE_ICONS[role] || "\u{1F916}";
2359
+ console.log("");
2360
+ console.log(roleColor.bold(`${icon} ${capitalize(role)}:`));
2361
+ const formatted = formatResponse(typeof message.content === "string" ? message.content : "");
2362
+ console.log(import_chalk10.default.gray(` ${formatted}`));
2363
+ console.log("");
2364
+ roundResponses.push(message);
2365
+ break;
2366
+ }
2367
+ case "round-complete":
2368
+ const agreementCount = typeof message.agreements === "number" ? message.agreements : 0;
2369
+ const totalMembers = typeof message.totalMembers === "number" ? message.totalMembers : roundResponses.length;
2370
+ const agreementPercent = Math.round(agreementCount / totalMembers * 100);
2371
+ console.log(import_chalk10.default.gray(` Agreement: ${agreementCount}/${totalMembers} (${agreementPercent}%)`));
2372
+ console.log("");
2373
+ break;
2374
+ case "consensus-reached":
2375
+ console.log(import_chalk10.default.green.bold("\n\u2705 CONSENSUS REACHED!"));
2376
+ console.log(import_chalk10.default.gray(` Rounds: ${message.rounds}`));
2377
+ console.log(import_chalk10.default.gray(` Time: ${message.time}s`));
2378
+ console.log("");
2379
+ break;
2380
+ case "spawning-additional-member":
2381
+ console.log(import_chalk10.default.yellow(`
2382
+ \u26A0\uFE0F Discussion needs more input. Spawning ${message.role}...`));
2383
+ break;
2384
+ case "generating-solution":
2385
+ const genSpinner = (0, import_ora3.default)({
2386
+ text: "Synthesizing final solution from council discussion...",
2387
+ color: "green"
2388
+ }).start();
2389
+ onSpinnerUpdate(genSpinner);
2390
+ break;
2391
+ case "solution-ready":
2392
+ if (currentSpinner) currentSpinner.succeed("Solution generated");
2393
+ console.log("");
2394
+ console.log(import_chalk10.default.blue.bold("\u2500".repeat(65)));
2395
+ console.log(import_chalk10.default.blue.bold("\u{1F4CB} Solution"));
2396
+ console.log(import_chalk10.default.blue.bold("\u2500".repeat(65)));
2397
+ console.log("");
2398
+ console.log(message.solution);
2399
+ console.log("");
2400
+ if (message.files && message.files.length > 0) {
2401
+ console.log(import_chalk10.default.cyan("\u{1F4C1} Files created:"));
2402
+ message.files.forEach((file) => {
2403
+ console.log(import_chalk10.default.gray(` \u251C\u2500 ${file}`));
2404
+ });
2405
+ console.log("");
2406
+ }
2407
+ break;
2408
+ case "stats":
2409
+ printStats(message.stats);
2410
+ break;
2411
+ case "cleanup-start":
2412
+ console.log(import_chalk10.default.yellow("\u{1F9F9} Cleaning up council members..."));
2413
+ break;
2414
+ case "member-destroyed":
2415
+ console.log(import_chalk10.default.gray(` \u251C\u2500 Destroying ${message.role}... \u2713`));
2416
+ break;
2417
+ case "session-complete":
2418
+ console.log("");
2419
+ console.log(import_chalk10.default.green.bold("\u2728 Council session complete!"));
2420
+ console.log("");
2421
+ onExit();
2422
+ break;
2423
+ case "timeout":
2424
+ console.log(import_chalk10.default.yellow("\n\u23F1\uFE0F Discussion timeout reached"));
2425
+ console.log(import_chalk10.default.gray(" The council needs your input to continue."));
2426
+ break;
2427
+ case "error":
2428
+ if (currentSpinner) currentSpinner.fail();
2429
+ console.log(import_chalk10.default.red(`
2430
+ \u274C Error: ${message.error}`));
2431
+ onExit();
2432
+ break;
2433
+ }
2434
+ }
2435
+ function printRoundHeader(round) {
2436
+ console.log(import_chalk10.default.cyan("\u2500".repeat(65)));
2437
+ console.log(import_chalk10.default.cyan(`Round ${round} - Council Discussion`));
2438
+ console.log(import_chalk10.default.cyan("\u2500".repeat(65)));
2439
+ }
2440
+ function formatResponse(content) {
2441
+ const maxLength = 300;
2442
+ if (content.length > maxLength) {
2443
+ return content.substring(0, maxLength) + "...";
2444
+ }
2445
+ return content;
2446
+ }
2447
+ function printStats(stats) {
2448
+ console.log(import_chalk10.default.blue.bold("\u2500".repeat(65)));
2449
+ console.log(import_chalk10.default.blue.bold("\u{1F4CA} Session Statistics"));
2450
+ console.log(import_chalk10.default.blue.bold("\u2500".repeat(65)));
2451
+ console.log("");
2452
+ console.log(import_chalk10.default.gray(` Council members: ${stats.members || 0}`));
2453
+ console.log(import_chalk10.default.gray(` Discussion rounds: ${stats.rounds || 0}`));
2454
+ console.log(import_chalk10.default.gray(` Total time: ${stats.time || 0}s`));
2455
+ console.log(import_chalk10.default.gray(` API cost: $${(stats.cost || 0).toFixed(2)}`));
2456
+ console.log(import_chalk10.default.gray(` Lines of code: ${stats.linesOfCode || 0}`));
2457
+ console.log("");
2458
+ }
2459
+ function capitalize(str) {
2460
+ return str.charAt(0).toUpperCase() + str.slice(1);
2461
+ }
2462
+
2463
+ // src/cli/index.ts
2464
+ init_analyze();
2465
+
2466
+ // src/cli/commands/usage.ts
2467
+ var import_chalk11 = __toESM(require("chalk"));
2468
+ var import_prompts6 = __toESM(require("prompts"));
2469
+ init_config();
2470
+ async function usageCommand(options = {}) {
2471
+ const token = getToken();
2472
+ if (!token) {
2473
+ console.error(import_chalk11.default.red("\u274C Not authenticated"));
2474
+ console.log(import_chalk11.default.dim(" Run"), import_chalk11.default.cyan("codmir login"), import_chalk11.default.dim("first"));
2475
+ process.exit(1);
2476
+ }
2477
+ console.log(import_chalk11.default.bold("\n\u{1F4CA} API Usage & Billing\n"));
2478
+ const baseUrl = getBaseUrl();
2479
+ const month = options.month || (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
2480
+ try {
2481
+ const response = await fetch(`${baseUrl}/api/keys`, {
2482
+ headers: { "X-API-Key": token }
715
2483
  });
716
- console.log(import_chalk6.default.dim(" Link a project:"), import_chalk6.default.cyan("codmir link"));
2484
+ if (!response.ok) {
2485
+ throw new Error(`Failed to fetch API keys: ${response.status}`);
2486
+ }
2487
+ const { keys } = await response.json();
2488
+ if (keys.length === 0) {
2489
+ console.log(import_chalk11.default.yellow("\u26A0\uFE0F No API keys found"));
2490
+ console.log(import_chalk11.default.dim(" Create one at:"), import_chalk11.default.cyan("https://codmir.com/settings/api-keys"));
2491
+ return;
2492
+ }
2493
+ console.log(import_chalk11.default.bold(`\u{1F4C5} Month: ${month}
2494
+ `));
2495
+ let totalRequests = 0;
2496
+ let totalAICalls = 0;
2497
+ let totalTokens = 0;
2498
+ let totalCostCents = 0;
2499
+ for (const key of keys) {
2500
+ const usage = key.monthlyUsage[0];
2501
+ const costDollars = usage ? (usage.totalCostCents / 100).toFixed(2) : "0.00";
2502
+ const tokenCount = Number(usage?.totalTokens || 0);
2503
+ totalRequests += usage?.totalRequests || 0;
2504
+ totalAICalls += usage?.totalAICalls || 0;
2505
+ totalTokens += tokenCount;
2506
+ totalCostCents += usage?.totalCostCents || 0;
2507
+ console.log(import_chalk11.default.cyan.bold(`\u{1F511} ${key.name}`));
2508
+ console.log(import_chalk11.default.dim(` Key: ${key.keyPrefix}...`));
2509
+ console.log(import_chalk11.default.dim(` Scope: ${key.scope}`));
2510
+ console.log();
2511
+ console.log(import_chalk11.default.bold(" Usage:"));
2512
+ console.log(import_chalk11.default.dim(` Total Requests: ${(usage?.totalRequests || 0).toLocaleString()}`));
2513
+ console.log(import_chalk11.default.dim(` AI Calls: ${(usage?.totalAICalls || 0).toLocaleString()}`));
2514
+ console.log(import_chalk11.default.dim(` Tokens Used: ${tokenCount.toLocaleString()}`));
2515
+ const costColor = getCostColor(usage?.totalCostCents || 0);
2516
+ console.log(import_chalk11.default.bold(" Cost:"));
2517
+ console.log(costColor(` This Month: $${costDollars}`));
2518
+ if (key.lastUsedAt) {
2519
+ const lastUsed = new Date(key.lastUsedAt);
2520
+ const daysAgo = Math.floor((Date.now() - lastUsed.getTime()) / (1e3 * 60 * 60 * 24));
2521
+ console.log(import_chalk11.default.dim(` Last Used: ${lastUsed.toLocaleDateString()} (${daysAgo} days ago)`));
2522
+ } else {
2523
+ console.log(import_chalk11.default.dim(" Last Used: Never"));
2524
+ }
2525
+ if (options.detailed && usage) {
2526
+ console.log();
2527
+ console.log(import_chalk11.default.bold(" Provider Breakdown:"));
2528
+ if (usage.openaiTokens > 0) {
2529
+ console.log(import_chalk11.default.dim(` OpenAI: ${Number(usage.openaiTokens).toLocaleString()} tokens ($${(usage.openaiCostCents / 100).toFixed(2)})`));
2530
+ }
2531
+ if (usage.anthropicTokens > 0) {
2532
+ console.log(import_chalk11.default.dim(` Anthropic: ${Number(usage.anthropicTokens).toLocaleString()} tokens ($${(usage.anthropicCostCents / 100).toFixed(2)})`));
2533
+ }
2534
+ if (usage.localTokens > 0) {
2535
+ console.log(import_chalk11.default.dim(` Local: ${Number(usage.localTokens).toLocaleString()} tokens (free)`));
2536
+ }
2537
+ if (usage.totalAnalyses > 0) {
2538
+ console.log();
2539
+ console.log(import_chalk11.default.bold(" Codebase Analysis:"));
2540
+ console.log(import_chalk11.default.dim(` Total Analyses: ${usage.totalAnalyses}`));
2541
+ console.log(import_chalk11.default.dim(` Local: ${usage.localAnalyses}`));
2542
+ console.log(import_chalk11.default.dim(` Deep (Railway): ${usage.deepAnalyses} ($${(usage.railwayCostCents / 100).toFixed(2)})`));
2543
+ console.log(import_chalk11.default.dim(` Files Processed: ${Number(usage.filesProcessed).toLocaleString()}`));
2544
+ console.log(import_chalk11.default.dim(` Lines Processed: ${Number(usage.linesProcessed).toLocaleString()}`));
2545
+ }
2546
+ }
2547
+ console.log();
2548
+ console.log(import_chalk11.default.dim(" \u2500".repeat(50)));
2549
+ console.log();
2550
+ }
2551
+ console.log(import_chalk11.default.bold.cyan("\u{1F4C8} Summary\n"));
2552
+ console.log(import_chalk11.default.dim(` Total Requests: ${totalRequests.toLocaleString()}`));
2553
+ console.log(import_chalk11.default.dim(` AI Calls: ${totalAICalls.toLocaleString()}`));
2554
+ console.log(import_chalk11.default.dim(` Tokens Used: ${totalTokens.toLocaleString()}`));
2555
+ console.log();
2556
+ console.log(import_chalk11.default.bold.green(` Total Cost: $${(totalCostCents / 100).toFixed(2)}`));
2557
+ console.log();
2558
+ if (totalCostCents > 5e3) {
2559
+ console.log(import_chalk11.default.yellow(" \u26A0\uFE0F High usage detected this month"));
2560
+ console.log(import_chalk11.default.dim(" Consider optimizing AI calls or using local mode"));
2561
+ console.log();
2562
+ }
2563
+ if (!options.detailed && keys.length > 0) {
2564
+ console.log(import_chalk11.default.dim(" \u{1F4A1} Tip: Use"), import_chalk11.default.cyan("--detailed"), import_chalk11.default.dim("for provider breakdown"));
2565
+ }
2566
+ console.log(import_chalk11.default.dim(" \u{1F4C5} View other months:"), import_chalk11.default.cyan("codmir usage --month 2025-01"));
2567
+ console.log();
717
2568
  } catch (error) {
718
- console.error(import_chalk6.default.red("\n\u274C Failed to fetch projects:"), error instanceof Error ? error.message : "Unknown error");
2569
+ console.error(import_chalk11.default.red("\n\u274C Failed to get usage stats"));
2570
+ console.error(import_chalk11.default.dim(" Error:"), error instanceof Error ? error.message : "Unknown error");
719
2571
  process.exit(1);
720
2572
  }
721
2573
  }
2574
+ function getCostColor(costCents) {
2575
+ if (costCents === 0) return import_chalk11.default.dim;
2576
+ if (costCents < 100) return import_chalk11.default.green;
2577
+ if (costCents < 1e3) return import_chalk11.default.yellow;
2578
+ return import_chalk11.default.red;
2579
+ }
722
2580
 
723
2581
  // src/cli/index.ts
724
2582
  var program = new import_commander.Command();
@@ -728,7 +2586,21 @@ program.command("link").description("Link current directory to a codmir project"
728
2586
  program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
729
2587
  program.command("logout").description("Log out of codmir").action(logoutCommand);
730
2588
  program.command("init").description("Initialize codmir in your project").action(initCommand);
731
- program.command("projects").alias("ls").description("List your projects").action(projectsCommand);
2589
+ program.command("projects").alias("ls").description("List all your projects").action(projectsCommand);
2590
+ program.command("ticket").alias("create").description("\u{1F3AB} Create a new ticket with interactive navigation").action(createTicketCommand);
2591
+ program.command("codmir [query...]").alias("ai").description("\u{1F916} AI assistant for developers").option("-p, --project <id>", "Include project context").option("-c, --context", "Include current directory context").option("-m, --model <name>", "AI model to use (default: gpt-4-turbo-preview)").action((query, options) => {
2592
+ const queryString = query ? query.join(" ") : void 0;
2593
+ assistantCommand(queryString, options);
2594
+ });
2595
+ program.command("ask <query...>").description("Quick AI query without interactive mode").option("-p, --project <id>", "Include project context").action((query, options) => {
2596
+ quickQueryCommand(query.join(" "), options);
2597
+ });
2598
+ program.command("analyze").description("\u{1F50D} Analyze codebase and generate knowledge base").option("--full", "Run full deep analysis").option("--force", "Force re-analysis even if knowledge base exists").option("--mode <mode>", "Analysis mode: local or railway").action(analyzeCommand);
2599
+ program.command("usage").description("\u{1F4CA} View API usage and billing stats").option("--month <YYYY-MM>", "Month to view (default: current month)").option("--detailed", "Show detailed provider breakdown").action(usageCommand);
2600
+ program.command("council [task...]").description("\u{1F3AD} Assemble a council of Claude AI agents to collaborate on complex tasks").option("-m, --members <count>", "Number of initial council members (2-4)", "2").option("-t, --timeout <seconds>", "Max discussion time in seconds", "300").option("--local", "Run on local LAN cluster instead of Lambda").action((task, options) => {
2601
+ const taskString = task ? task.join(" ") : void 0;
2602
+ councilCommand(taskString, options);
2603
+ });
732
2604
  program.parse(process.argv);
733
2605
  if (!process.argv.slice(2).length) {
734
2606
  program.outputHelp();