codmir 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ var CodmirClient = class {
9
9
  };
10
10
  this.tickets = new TicketsAPI(this.config);
11
11
  this.testCases = new TestCasesAPI(this.config);
12
+ this.testing = new TestingAPI(this.config);
12
13
  }
13
14
  /**
14
15
  * Make an HTTP request to the codmir API
@@ -267,6 +268,75 @@ var TestCasesAPI = class {
267
268
  );
268
269
  }
269
270
  };
271
+ var TestingAPI = class {
272
+ constructor(config) {
273
+ this.config = config;
274
+ }
275
+ async request(method, path, body) {
276
+ const url = `${this.config.baseUrl}${path}`;
277
+ const headers = {
278
+ "Content-Type": "application/json",
279
+ ...this.config.headers
280
+ };
281
+ if (this.config.apiKey) {
282
+ headers["X-API-Key"] = this.config.apiKey;
283
+ }
284
+ const controller = new AbortController();
285
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
286
+ try {
287
+ const response = await fetch(url, {
288
+ method,
289
+ headers,
290
+ body: body ? JSON.stringify(body) : void 0,
291
+ signal: controller.signal
292
+ });
293
+ clearTimeout(timeoutId);
294
+ if (!response.ok) {
295
+ const errorData = await response.json().catch(() => ({}));
296
+ const error = new Error(errorData.error || `HTTP ${response.status}`);
297
+ error.statusCode = response.status;
298
+ error.response = errorData;
299
+ throw error;
300
+ }
301
+ if (response.status === 204) {
302
+ return {};
303
+ }
304
+ return await response.json();
305
+ } catch (error) {
306
+ clearTimeout(timeoutId);
307
+ if (error.name === "AbortError") {
308
+ const timeoutError = new Error("Request timeout");
309
+ timeoutError.statusCode = 408;
310
+ throw timeoutError;
311
+ }
312
+ throw error;
313
+ }
314
+ }
315
+ async submitTestRun(payload) {
316
+ return this.request("POST", "/api/testing/test-runs", payload);
317
+ }
318
+ async listTestRuns(projectId, options) {
319
+ const params = new URLSearchParams({ projectId });
320
+ if (options?.limit) params.set("limit", String(options.limit));
321
+ if (options?.suite) params.set("suite", options.suite);
322
+ const response = await this.request(
323
+ "GET",
324
+ `/api/testing/test-runs?${params.toString()}`
325
+ );
326
+ return response.runs || [];
327
+ }
328
+ async requestCoverageInsight(payload) {
329
+ return this.request("POST", "/api/testing/coverage-insight", payload);
330
+ }
331
+ async listCoverageInsights(projectId, limit = 5) {
332
+ const params = new URLSearchParams({ projectId, limit: String(limit) });
333
+ const response = await this.request(
334
+ "GET",
335
+ `/api/testing/coverage-insight?${params.toString()}`
336
+ );
337
+ return response.reports || [];
338
+ }
339
+ };
270
340
 
271
341
  export {
272
342
  CodmirClient
package/dist/cli/index.js CHANGED
@@ -137,8 +137,8 @@ var require_package = __commonJS({
137
137
  "package.json"(exports2, module2) {
138
138
  module2.exports = {
139
139
  name: "codmir",
140
- version: "0.3.3",
141
- description: "Official codmir SDK - CLI and SDK for AI-powered development: codebase analysis, task replication, usage tracking, and intelligent automation",
140
+ version: "0.4.1",
141
+ description: "Official codmir SDK - AI that prevents wasted engineering time. CLI, SDK, voice assistant, codebase analysis, and intelligent automation.",
142
142
  main: "dist/index.js",
143
143
  module: "dist/index.mjs",
144
144
  types: "dist/index.d.ts",
@@ -150,6 +150,16 @@ var require_package = __commonJS({
150
150
  types: "./dist/index.d.ts",
151
151
  require: "./dist/index.js",
152
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"
153
163
  }
154
164
  },
155
165
  files: [
@@ -159,7 +169,7 @@ var require_package = __commonJS({
159
169
  "runkit-example.js"
160
170
  ],
161
171
  scripts: {
162
- build: "tsup src/index.ts src/cli/index.ts --format cjs,esm --dts --clean",
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",
163
173
  dev: "tsup src/index.ts src/cli/index.ts --format cjs,esm --dts --watch",
164
174
  prepublishOnly: "pnpm build",
165
175
  test: "jest",
@@ -185,7 +195,11 @@ var require_package = __commonJS({
185
195
  "tasks",
186
196
  "automation",
187
197
  "multi-agent",
188
- "ai-assistant"
198
+ "ai-assistant",
199
+ "voice-assistant",
200
+ "speech-recognition",
201
+ "voice-daemon",
202
+ "wake-word"
189
203
  ],
190
204
  author: "codmir",
191
205
  license: "MIT",
@@ -210,12 +224,16 @@ var require_package = __commonJS({
210
224
  "@semantic-release/release-notes-generator": "^14.0.0",
211
225
  "@types/node": "^20.10.0",
212
226
  "@types/prompts": "^2.4.9",
227
+ "@types/ws": "^8.18.1",
228
+ "@types/blessed": "^0.1.25",
213
229
  "conventional-changelog-conventionalcommits": "^8.0.0",
214
230
  "semantic-release": "^24.0.0",
215
231
  tsup: "^8.0.1",
216
232
  typescript: "^5.8.3"
217
233
  },
218
234
  dependencies: {
235
+ blessed: "^0.1.81",
236
+ "blessed-contrib": "^4.11.0",
219
237
  chalk: "^5.3.0",
220
238
  clipboardy: "^5.0.1",
221
239
  commander: "^12.0.0",
@@ -1080,6 +1098,7 @@ var CodmirClient = class {
1080
1098
  };
1081
1099
  this.tickets = new TicketsAPI(this.config);
1082
1100
  this.testCases = new TestCasesAPI(this.config);
1101
+ this.testing = new TestingAPI(this.config);
1083
1102
  }
1084
1103
  /**
1085
1104
  * Make an HTTP request to the codmir API
@@ -1338,6 +1357,75 @@ var TestCasesAPI = class {
1338
1357
  );
1339
1358
  }
1340
1359
  };
1360
+ var TestingAPI = class {
1361
+ constructor(config) {
1362
+ this.config = config;
1363
+ }
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;
1372
+ }
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;
1402
+ }
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
+ };
1341
1429
 
1342
1430
  // src/cli/commands/link.ts
1343
1431
  var import_prompts2 = __toESM(require("prompts"));
@@ -1637,25 +1725,62 @@ var import_clipboardy = __toESM(require("clipboardy"));
1637
1725
  var import_fs6 = __toESM(require("fs"));
1638
1726
  var import_path6 = __toESM(require("path"));
1639
1727
  var import_form_data = __toESM(require("form-data"));
1728
+ var import_readline = __toESM(require("readline"));
1640
1729
  init_config();
1730
+ function renderLogo() {
1731
+ const c1 = import_chalk8.default.hex("#00D4FF");
1732
+ const c2 = import_chalk8.default.hex("#0099FF");
1733
+ const c3 = import_chalk8.default.hex("#7C3AED");
1734
+ const c4 = import_chalk8.default.hex("#EC4899");
1735
+ const c5 = import_chalk8.default.hex("#F472B6");
1736
+ const logo = `
1737
+ ${c1(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557")}${c2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ")}${c2("\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ")}${c3("\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557")}${c4("\u2588\u2588\u2557")}${c5("\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ")}
1738
+ ${c1(" \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D")}${c2("\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557")}${c2("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557")}${c3("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551")}${c4("\u2588\u2588\u2551")}${c5("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557")}
1739
+ ${c1(" \u2588\u2588\u2551 ")}${c2("\u2588\u2588\u2551 \u2588\u2588\u2551")}${c2("\u2588\u2588\u2551 \u2588\u2588\u2551")}${c3("\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551")}${c4("\u2588\u2588\u2551")}${c5("\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D")}
1740
+ ${c1(" \u2588\u2588\u2551 ")}${c2("\u2588\u2588\u2551 \u2588\u2588\u2551")}${c2("\u2588\u2588\u2551 \u2588\u2588\u2551")}${c3("\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551")}${c4("\u2588\u2588\u2551")}${c5("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557")}
1741
+ ${c1(" \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557")}${c2("\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D")}${c2("\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D")}${c3("\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551")}${c4("\u2588\u2588\u2551")}${c5("\u2588\u2588\u2551 \u2588\u2588\u2551")}
1742
+ ${c1(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D")}${c2(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D ")}${c2("\u255A\u2550\u2550\u2550\u2550\u2550\u255D ")}${c3("\u255A\u2550\u255D \u255A\u2550\u255D")}${c4("\u255A\u2550\u255D")}${c5("\u255A\u2550\u255D \u255A\u2550\u255D")}
1743
+ `;
1744
+ console.log(logo);
1745
+ }
1746
+ function renderTips() {
1747
+ console.log(import_chalk8.default.white("Tips for getting started:"));
1748
+ console.log(import_chalk8.default.dim("1. Ask questions, edit files, or run commands."));
1749
+ console.log(import_chalk8.default.dim("2. Be specific for the best results."));
1750
+ console.log(import_chalk8.default.dim("3. Create ") + import_chalk8.default.cyan("CODMIR.md") + import_chalk8.default.dim(" files to customize your interactions with codmir."));
1751
+ console.log(import_chalk8.default.dim("4. ") + import_chalk8.default.cyan("/help") + import_chalk8.default.dim(" for more information."));
1752
+ console.log();
1753
+ }
1754
+ function renderStatusBar(context, model) {
1755
+ const termWidth = process.stdout.columns || 80;
1756
+ const projectInfo = context.isLinkedProject ? import_chalk8.default.cyan(`~/${context.projectConfig?.projectName || "project"}`) : import_chalk8.default.yellow("~/global");
1757
+ const sandboxStatus = import_chalk8.default.dim("no sandbox") + import_chalk8.default.dim(" (see /docs)");
1758
+ const modelInfo = import_chalk8.default.green(`${model}`) + import_chalk8.default.dim(" (100% context left)");
1759
+ console.log();
1760
+ console.log(import_chalk8.default.dim("\u2500".repeat(termWidth)));
1761
+ console.log(`${projectInfo} ${sandboxStatus} ${modelInfo}`);
1762
+ }
1763
+ function renderEditHint() {
1764
+ const termWidth = process.stdout.columns || 80;
1765
+ const hint = import_chalk8.default.dim("accepting edits") + import_chalk8.default.dim(" (shift + tab to toggle)");
1766
+ const padding = " ".repeat(Math.max(0, termWidth - 35));
1767
+ console.log(padding + hint);
1768
+ }
1641
1769
  async function assistantCommand(query, options = {}) {
1642
1770
  const token = getToken();
1771
+ const context = getExecutionContext();
1772
+ const model = options.model || "gpt-4-turbo";
1773
+ console.clear();
1774
+ renderLogo();
1643
1775
  if (!token) {
1644
- console.error(import_chalk8.default.red("\u274C Not authenticated"));
1645
- console.log(import_chalk8.default.dim(" Run"), import_chalk8.default.cyan("codmir login"), import_chalk8.default.dim("first"));
1776
+ console.log(import_chalk8.default.red("\n\u274C Not authenticated"));
1777
+ console.log(import_chalk8.default.dim(" Run ") + import_chalk8.default.cyan("codmir login") + import_chalk8.default.dim(" to get started.\n"));
1646
1778
  process.exit(1);
1647
1779
  }
1780
+ renderTips();
1781
+ renderEditHint();
1648
1782
  const baseUrl = getBaseUrl();
1649
1783
  const conversationHistory = [];
1650
- const context = getExecutionContext();
1651
- if (context.isLinkedProject) {
1652
- console.log(import_chalk8.default.dim(` \u{1F4C1} Project: ${context.projectConfig?.projectName || context.projectConfig?.projectId}`));
1653
- console.log(import_chalk8.default.dim(` \u{1F517} Mode: ${import_chalk8.default.cyan("local")} (linked project)
1654
- `));
1655
- } else {
1656
- console.log(import_chalk8.default.dim(` \u{1F310} Mode: ${import_chalk8.default.yellow("global")}
1657
- `));
1658
- }
1659
1784
  conversationHistory.push({
1660
1785
  role: "system",
1661
1786
  content: `You are codmir, an AI assistant for developers. You help with:
@@ -1666,28 +1791,47 @@ async function assistantCommand(query, options = {}) {
1666
1791
 
1667
1792
  Be concise, practical, and code-focused. Use lowercase "codmir" for the brand.`
1668
1793
  });
1669
- console.log(import_chalk8.default.bold("\n\u{1F916} codmir"));
1670
- console.log(import_chalk8.default.dim(" AI assistant for developers\n"));
1671
1794
  if (query) {
1672
1795
  await handleQuery(query, conversationHistory, baseUrl, token, options);
1796
+ renderStatusBar(context, model);
1673
1797
  return;
1674
1798
  }
1675
- console.log(import_chalk8.default.dim(" Type your question or"), import_chalk8.default.cyan("exit"), import_chalk8.default.dim("to quit"));
1676
- console.log(import_chalk8.default.dim(" \u{1F4CB} Tip: Copy images to clipboard before asking for automatic upload"));
1677
- console.log(import_chalk8.default.dim(' Example: "How do I setup authentication?"\n'));
1678
- while (true) {
1679
- const { userInput } = await (0, import_prompts3.default)({
1680
- type: "text",
1681
- name: "userInput",
1682
- message: import_chalk8.default.cyan("codmir"),
1683
- validate: (value) => value.length > 0 || "Please enter a question"
1799
+ const rl = import_readline.default.createInterface({
1800
+ input: process.stdin,
1801
+ output: process.stdout
1802
+ });
1803
+ const promptUser = () => {
1804
+ rl.question(import_chalk8.default.cyan("> ") + import_chalk8.default.dim("Type your message or @path/to/file "), async (userInput) => {
1805
+ if (!userInput || userInput.toLowerCase() === "exit" || userInput.toLowerCase() === "quit" || userInput === "/exit") {
1806
+ console.log(import_chalk8.default.dim("\nGoodbye! \u{1F44B}\n"));
1807
+ rl.close();
1808
+ return;
1809
+ }
1810
+ if (userInput === "/help") {
1811
+ console.log("\n" + import_chalk8.default.bold("Available commands:"));
1812
+ console.log(import_chalk8.default.cyan(" /help") + import_chalk8.default.dim(" - Show this help message"));
1813
+ console.log(import_chalk8.default.cyan(" /clear") + import_chalk8.default.dim(" - Clear conversation history"));
1814
+ console.log(import_chalk8.default.cyan(" /exit") + import_chalk8.default.dim(" - Exit codmir"));
1815
+ console.log(import_chalk8.default.cyan(" @file") + import_chalk8.default.dim(" - Reference a file in your message"));
1816
+ console.log();
1817
+ promptUser();
1818
+ return;
1819
+ }
1820
+ if (userInput === "/clear") {
1821
+ conversationHistory.length = 1;
1822
+ console.log(import_chalk8.default.dim("\n\u2713 Conversation cleared\n"));
1823
+ promptUser();
1824
+ return;
1825
+ }
1826
+ await handleQuery(userInput, conversationHistory, baseUrl, token, options);
1827
+ console.log();
1828
+ promptUser();
1684
1829
  });
1685
- if (!userInput || userInput.toLowerCase() === "exit" || userInput.toLowerCase() === "quit") {
1686
- console.log(import_chalk8.default.dim("\n Goodbye! \u{1F44B}\n"));
1687
- break;
1688
- }
1689
- await handleQuery(userInput, conversationHistory, baseUrl, token, options);
1690
- }
1830
+ };
1831
+ promptUser();
1832
+ rl.on("close", () => {
1833
+ renderStatusBar(context, model);
1834
+ });
1691
1835
  }
1692
1836
  async function handleQuery(query, conversationHistory, baseUrl, token, options) {
1693
1837
  let imagePaths;
@@ -2251,30 +2395,34 @@ function handleCouncilMessage(message, context) {
2251
2395
  console.log("");
2252
2396
  break;
2253
2397
  case "round-start":
2254
- printRoundHeader(message.round);
2398
+ printRoundHeader(message.round ?? 0);
2255
2399
  roundResponses.length = 0;
2256
2400
  break;
2257
- case "member-thinking":
2401
+ case "member-thinking": {
2402
+ const role = message.role ?? "architect";
2258
2403
  const thinkSpinner = (0, import_ora3.default)({
2259
- text: `${ROLE_ICONS[message.role]} ${capitalize(message.role)} is analyzing...`,
2404
+ text: `${ROLE_ICONS[role]} ${capitalize(role)} is analyzing...`,
2260
2405
  color: "yellow"
2261
2406
  }).start();
2262
2407
  onSpinnerUpdate(thinkSpinner);
2263
2408
  break;
2264
- case "member-response":
2409
+ }
2410
+ case "member-response": {
2265
2411
  if (currentSpinner) currentSpinner.stop();
2266
- const roleColor = ROLE_COLORS[message.role] || import_chalk10.default.white;
2267
- const icon = ROLE_ICONS[message.role] || "\u{1F916}";
2412
+ const role = message.role ?? "architect";
2413
+ const roleColor = ROLE_COLORS[role] || import_chalk10.default.white;
2414
+ const icon = ROLE_ICONS[role] || "\u{1F916}";
2268
2415
  console.log("");
2269
- console.log(roleColor.bold(`${icon} ${capitalize(message.role)}:`));
2270
- const formatted = formatResponse(message.content);
2416
+ console.log(roleColor.bold(`${icon} ${capitalize(role)}:`));
2417
+ const formatted = formatResponse(typeof message.content === "string" ? message.content : "");
2271
2418
  console.log(import_chalk10.default.gray(` ${formatted}`));
2272
2419
  console.log("");
2273
2420
  roundResponses.push(message);
2274
2421
  break;
2422
+ }
2275
2423
  case "round-complete":
2276
- const agreementCount = message.agreements || 0;
2277
- const totalMembers = message.totalMembers || roundResponses.length;
2424
+ const agreementCount = typeof message.agreements === "number" ? message.agreements : 0;
2425
+ const totalMembers = typeof message.totalMembers === "number" ? message.totalMembers : roundResponses.length;
2278
2426
  const agreementPercent = Math.round(agreementCount / totalMembers * 100);
2279
2427
  console.log(import_chalk10.default.gray(` Agreement: ${agreementCount}/${totalMembers} (${agreementPercent}%)`));
2280
2428
  console.log("");
@@ -2509,7 +2657,12 @@ program.command("council [task...]").description("\u{1F3AD} Assemble a council o
2509
2657
  const taskString = task ? task.join(" ") : void 0;
2510
2658
  councilCommand(taskString, options);
2511
2659
  });
2512
- program.parse(process.argv);
2513
- if (!process.argv.slice(2).length) {
2514
- program.outputHelp();
2660
+ if (!process.argv.slice(2).length || process.argv[2] === "--help" || process.argv[2] === "-h") {
2661
+ if (process.argv[2] === "--help" || process.argv[2] === "-h") {
2662
+ program.parse(process.argv);
2663
+ } else {
2664
+ assistantCommand(void 0, {});
2665
+ }
2666
+ } else {
2667
+ program.parse(process.argv);
2515
2668
  }