snow-ai 0.7.15 → 0.7.16

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/bundle/cli.mjs CHANGED
@@ -53450,7 +53450,24 @@ Notebook is your persistent memory for the codebase. Use it aggressively to reco
53450
53450
  - If you notice an existing note is outdated or incorrect, fix it immediately \u2014 do NOT leave stale notes
53451
53451
  - After refactoring removes the fragile code a note warned about, delete that note
53452
53452
 
53453
- **Key actions:** \`action:"add"\` to record, \`action:"query"\` to recall before starting a task, \`action:"update"\` / \`action:"delete"\` to keep notes accurate
53453
+ **PARALLEL CALLS RULE:**
53454
+ ALWAYS pair notebook-manage with action tools in same call:
53455
+ - CORRECT: notebook-manage({action:"query"}) + filesystem-read | notebook-manage({action:"add",...}) + filesystem-edit
53456
+ - WRONG: Call notebook-manage alone, wait for result, then act
53457
+
53458
+ **Single tool \u2014 \`notebook-manage\` (required \`action\`):**
53459
+ - **query**: Search by fuzzy file path pattern; optional \`filePathPattern\`, \`topN\`
53460
+ - **list**: All entries for one exact file; required \`filePath\`
53461
+ - **add**: \`filePath\` + \`note\` (string or string[] for batch); records note(s) for a file
53462
+ - **update**: \`notebookId\` + \`note\` (string); updates one entry's content
53463
+ - **delete**: \`notebookId\` (string or string[]); removes entry(s)
53464
+
53465
+ **Examples:**
53466
+ \`\`\`
53467
+ notebook-manage({action:"query", filePathPattern:"auth"}) + filesystem-read("src/auth.ts")
53468
+ notebook-manage({action:"add", filePath:"src/auth.ts", note:["validateInput() MUST be called first","Session token is nullable"]}) + filesystem-edit(...)
53469
+ notebook-manage({action:"delete", notebookId:["id1","id2"]}) + filesystem-edit(...)
53470
+ \`\`\`
53454
53471
 
53455
53472
  **Golden rule:** If you had to think hard to understand something, write it down so the next session doesn't have to.
53456
53473
 
@@ -363045,6 +363062,56 @@ function deleteNotebook(notebookId) {
363045
363062
  }
363046
363063
  return found;
363047
363064
  }
363065
+ function addNotebooks(filePath, notes) {
363066
+ const normalizedPath = normalizePath2(filePath);
363067
+ const data = readNotebookData();
363068
+ if (!data[normalizedPath]) {
363069
+ data[normalizedPath] = [];
363070
+ }
363071
+ const entries = [];
363072
+ for (const note of notes) {
363073
+ const now = /* @__PURE__ */ new Date();
363074
+ const localTimeStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}T${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}:${String(now.getSeconds()).padStart(2, "0")}.${String(now.getMilliseconds()).padStart(3, "0")}`;
363075
+ const entry = {
363076
+ id: `notebook-${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
363077
+ filePath: normalizedPath,
363078
+ note,
363079
+ createdAt: localTimeStr,
363080
+ updatedAt: localTimeStr
363081
+ };
363082
+ data[normalizedPath].unshift(entry);
363083
+ entries.push(entry);
363084
+ }
363085
+ if (data[normalizedPath].length > MAX_ENTRIES_PER_FILE) {
363086
+ data[normalizedPath] = data[normalizedPath].slice(0, MAX_ENTRIES_PER_FILE);
363087
+ }
363088
+ saveNotebookData(data);
363089
+ return entries;
363090
+ }
363091
+ function deleteNotebooks(notebookIds) {
363092
+ var _a20;
363093
+ const data = readNotebookData();
363094
+ const idSet = new Set(notebookIds);
363095
+ const deleted = [];
363096
+ for (const [, entries] of Object.entries(data)) {
363097
+ for (let i = entries.length - 1; i >= 0; i--) {
363098
+ if (idSet.has(entries[i].id)) {
363099
+ deleted.push(entries[i].id);
363100
+ entries.splice(i, 1);
363101
+ idSet.delete(((_a20 = entries[i]) == null ? void 0 : _a20.id) ?? "");
363102
+ }
363103
+ }
363104
+ if (idSet.size === 0)
363105
+ break;
363106
+ }
363107
+ if (deleted.length > 0) {
363108
+ saveNotebookData(data);
363109
+ }
363110
+ return {
363111
+ deleted,
363112
+ notFound: notebookIds.filter((id) => !deleted.includes(id))
363113
+ };
363114
+ }
363048
363115
  function getNotebookSnapshotFilePath() {
363049
363116
  const projectRoot = process.cwd();
363050
363117
  const projectName = path23.basename(projectRoot);
@@ -441038,6 +441105,7 @@ EXAMPLES:
441038
441105
 
441039
441106
  // dist/mcp/notebook.js
441040
441107
  async function executeNotebookTool(toolName, args2) {
441108
+ var _a20;
441041
441109
  try {
441042
441110
  const legacyActionMap = {
441043
441111
  "notebook-add": "add",
@@ -441061,18 +441129,57 @@ async function executeNotebookTool(toolName, args2) {
441061
441129
  switch (action) {
441062
441130
  case "add": {
441063
441131
  const { filePath, note } = args2;
441064
- if (!filePath || !note) {
441132
+ if (!filePath || note === void 0 || note === null) {
441065
441133
  return {
441066
441134
  content: [
441067
441135
  {
441068
441136
  type: "text",
441069
- text: "Error: Both filePath and note are required"
441137
+ text: 'Error: action=add requires both "filePath" and "note"'
441070
441138
  }
441071
441139
  ],
441072
441140
  isError: true
441073
441141
  };
441074
441142
  }
441075
- const entry = addNotebook(filePath, note);
441143
+ let parsedNote = note;
441144
+ if (typeof note === "string") {
441145
+ try {
441146
+ const parsed = JSON.parse(note);
441147
+ if (Array.isArray(parsed)) {
441148
+ parsedNote = parsed;
441149
+ }
441150
+ } catch {
441151
+ }
441152
+ }
441153
+ if (Array.isArray(parsedNote)) {
441154
+ const entries = addNotebooks(filePath, parsedNote);
441155
+ try {
441156
+ const context2 = getConversationContext();
441157
+ if (context2) {
441158
+ for (const entry2 of entries) {
441159
+ recordNotebookAddition(context2.sessionId, context2.messageIndex, entry2.id);
441160
+ }
441161
+ }
441162
+ } catch {
441163
+ }
441164
+ return {
441165
+ content: [
441166
+ {
441167
+ type: "text",
441168
+ text: JSON.stringify({
441169
+ success: true,
441170
+ message: `${entries.length} notebook entries added for: ${((_a20 = entries[0]) == null ? void 0 : _a20.filePath) ?? filePath}`,
441171
+ entries: entries.map((e) => ({
441172
+ id: e.id,
441173
+ filePath: e.filePath,
441174
+ note: e.note,
441175
+ createdAt: e.createdAt
441176
+ }))
441177
+ }, null, 2)
441178
+ }
441179
+ ]
441180
+ };
441181
+ }
441182
+ const entry = addNotebook(filePath, parsedNote);
441076
441183
  try {
441077
441184
  const context2 = getConversationContext();
441078
441185
  if (context2) {
@@ -441136,12 +441243,12 @@ async function executeNotebookTool(toolName, args2) {
441136
441243
  }
441137
441244
  case "update": {
441138
441245
  const { notebookId, note } = args2;
441139
- if (!notebookId || !note) {
441246
+ if (!notebookId || !note || typeof note !== "string") {
441140
441247
  return {
441141
441248
  content: [
441142
441249
  {
441143
441250
  type: "text",
441144
- text: "Error: Both notebookId and note are required"
441251
+ text: 'Error: action=update requires "notebookId" (string) and "note" (string)'
441145
441252
  }
441146
441253
  ],
441147
441254
  isError: true
@@ -441191,47 +441298,60 @@ async function executeNotebookTool(toolName, args2) {
441191
441298
  }
441192
441299
  case "delete": {
441193
441300
  const { notebookId } = args2;
441194
- if (!notebookId) {
441301
+ if (notebookId === void 0 || notebookId === null) {
441195
441302
  return {
441196
441303
  content: [
441197
441304
  {
441198
441305
  type: "text",
441199
- text: "Error: notebookId is required"
441306
+ text: 'Error: action=delete requires "notebookId"'
441200
441307
  }
441201
441308
  ],
441202
441309
  isError: true
441203
441310
  };
441204
441311
  }
441205
- const entryToDelete = findNotebookById(notebookId);
441206
- const deleted = deleteNotebook(notebookId);
441207
- if (!deleted) {
441312
+ const ids = Array.isArray(notebookId) ? notebookId : [notebookId];
441313
+ const entriesToDelete = ids.map((id) => findNotebookById(id)).filter((e) => e !== null);
441314
+ const result2 = ids.length === 1 ? (() => {
441315
+ const deleted = deleteNotebook(ids[0]);
441316
+ return {
441317
+ deleted: deleted ? [ids[0]] : [],
441318
+ notFound: deleted ? [] : [ids[0]]
441319
+ };
441320
+ })() : deleteNotebooks(ids);
441321
+ try {
441322
+ const context2 = getConversationContext();
441323
+ if (context2) {
441324
+ for (const entry of entriesToDelete) {
441325
+ if (result2.deleted.includes(entry.id)) {
441326
+ recordNotebookDeletion(context2.sessionId, context2.messageIndex, entry);
441327
+ }
441328
+ }
441329
+ }
441330
+ } catch {
441331
+ }
441332
+ if (result2.deleted.length === 0) {
441208
441333
  return {
441209
441334
  content: [
441210
441335
  {
441211
441336
  type: "text",
441212
441337
  text: JSON.stringify({
441213
441338
  success: false,
441214
- message: `Notebook entry not found: ${notebookId}`
441339
+ message: `Notebook entries not found: ${result2.notFound.join(", ")}`
441215
441340
  }, null, 2)
441216
441341
  }
441217
441342
  ],
441218
441343
  isError: true
441219
441344
  };
441220
441345
  }
441221
- try {
441222
- const context2 = getConversationContext();
441223
- if (context2 && entryToDelete) {
441224
- recordNotebookDeletion(context2.sessionId, context2.messageIndex, entryToDelete);
441225
- }
441226
- } catch {
441227
- }
441228
441346
  return {
441229
441347
  content: [
441230
441348
  {
441231
441349
  type: "text",
441232
441350
  text: JSON.stringify({
441233
441351
  success: true,
441234
- message: `Notebook entry deleted: ${notebookId}`
441352
+ message: `${result2.deleted.length} notebook entries deleted`,
441353
+ deleted: result2.deleted,
441354
+ ...result2.notFound.length > 0 ? { notFound: result2.notFound } : {}
441235
441355
  }, null, 2)
441236
441356
  }
441237
441357
  ]
@@ -441244,7 +441364,7 @@ async function executeNotebookTool(toolName, args2) {
441244
441364
  content: [
441245
441365
  {
441246
441366
  type: "text",
441247
- text: "Error: filePath is required"
441367
+ text: 'Error: action=list requires "filePath"'
441248
441368
  }
441249
441369
  ],
441250
441370
  isError: true
@@ -441302,35 +441422,35 @@ var init_notebook = __esm({
441302
441422
  mcpTools9 = [
441303
441423
  {
441304
441424
  name: "notebook-manage",
441305
- description: `\u{1F4DD} Unified notebook management tool. Use required "action" field to operate code memory.
441425
+ description: `Unified notebook management tool. Use required field "action" \u2014 one of query | list | add | update | delete.
441306
441426
 
441307
- **Core Purpose:** Prevent new features from breaking existing functionality.
441427
+ PARALLEL CALLS ONLY: MUST pair with other tools (notebook-manage + filesystem-read/terminal-execute/etc).
441428
+ NEVER call notebook-manage alone \u2014 always combine with an action tool in the same turn.
441308
441429
 
441309
- **Actions:**
441310
- - query: Search entries by fuzzy file path pattern (default action to discover notes)
441311
- - list: List all entries for one exact file path
441312
- - add: Record a new note for a file
441313
- - update: Update existing note by notebookId
441314
- - delete: Delete outdated note by notebookId
441430
+ ACTIONS:
441431
+ - query: Search entries by fuzzy file path pattern. Optional "filePathPattern" and "topN".
441432
+ - list: List all entries for one exact file path. Required "filePath".
441433
+ - add: Record note(s) for a file. Required "filePath" and "note" (string or string[]). Batch adds share the same filePath.
441434
+ - update: Update note by ID. Required "notebookId" and "note".
441435
+ - delete: Remove note(s) by ID. Required "notebookId" (string or string[]).
441315
441436
 
441316
- **When to add notes:**
441317
- - After fixing bugs that could easily reoccur
441318
- - Fragile code that new features might break
441319
- - Non-obvious dependencies between components
441320
- - Workarounds that shouldn't be "optimized away"
441437
+ BEST PRACTICES:
441438
+ - After fixing non-trivial bugs, record what caused it and why the fix works.
441439
+ - When discovering fragile dependencies or hidden coupling, record immediately.
441440
+ - When an existing note is outdated or incorrect, update/delete it immediately \u2014 do NOT leave stale notes.
441441
+ - Use query before modifying code to recall relevant notes.
441321
441442
 
441322
- **Examples:**
441323
- - "\u26A0\uFE0F validateInput() MUST be called first - new features broke this twice"
441324
- - "Component X depends on null return - DO NOT change to empty array"
441325
- - "setTimeout workaround for race condition - don't remove"
441326
- - "Parser expects exact format - adding fields breaks backward compat"`,
441443
+ EXAMPLES:
441444
+ - notebook-manage({action:"query", filePathPattern:"auth"}) + filesystem-read(...)
441445
+ - notebook-manage({action:"add", filePath:"src/auth.ts", note:["validateInput() MUST be called first","Session token is nullable"]}) + filesystem-edit(...)
441446
+ - notebook-manage({action:"delete", notebookId:["id1","id2"]}) + filesystem-edit(...)`,
441327
441447
  inputSchema: {
441328
441448
  type: "object",
441329
441449
  properties: {
441330
441450
  action: {
441331
441451
  type: "string",
441332
441452
  enum: ["query", "list", "add", "update", "delete"],
441333
- description: "Operation to run: query | list | add | update | delete."
441453
+ description: "Which operation to run on the notebook."
441334
441454
  },
441335
441455
  filePath: {
441336
441456
  type: "string",
@@ -441349,12 +441469,32 @@ var init_notebook = __esm({
441349
441469
  maximum: 50
441350
441470
  },
441351
441471
  notebookId: {
441352
- type: "string",
441353
- description: "For action=update/delete: notebook entry ID (from query/list)."
441472
+ oneOf: [
441473
+ {
441474
+ type: "string",
441475
+ description: "Single notebook entry ID"
441476
+ },
441477
+ {
441478
+ type: "array",
441479
+ items: { type: "string" },
441480
+ description: "Multiple IDs (same delete applies to all)"
441481
+ }
441482
+ ],
441483
+ description: "For action=update or delete: entry id(s) from action=query/list."
441354
441484
  },
441355
441485
  note: {
441356
- type: "string",
441357
- description: "For action=add/update: brief and specific risk/constraint note."
441486
+ oneOf: [
441487
+ {
441488
+ type: "string",
441489
+ description: "For action=add: one note. For action=update: new note content."
441490
+ },
441491
+ {
441492
+ type: "array",
441493
+ items: { type: "string" },
441494
+ description: "For action=add only: batch add multiple notes for the same file."
441495
+ }
441496
+ ],
441497
+ description: "For add: required (string or string[]). For update: required string."
441358
441498
  }
441359
441499
  },
441360
441500
  required: ["action"]
@@ -457684,8 +457824,17 @@ var init_LSPClient = __esm({
457684
457824
  return Boolean(this.connection && this.isInitialized && this.isProcessAlive && !this.isShuttingDown && stdin && !stdin.destroyed && !stdin.writableEnded);
457685
457825
  }
457686
457826
  markTransportClosed() {
457827
+ if (!this.isProcessAlive && !this.isInitialized) {
457828
+ return;
457829
+ }
457687
457830
  this.isInitialized = false;
457688
457831
  this.isProcessAlive = false;
457832
+ if (this.connection) {
457833
+ try {
457834
+ this.connection.dispose();
457835
+ } catch {
457836
+ }
457837
+ }
457689
457838
  }
457690
457839
  constructor(config3) {
457691
457840
  Object.defineProperty(this, "config", {
@@ -458419,20 +458568,30 @@ var init_LSPManager = __esm({
458419
458568
  if (!client) {
458420
458569
  return null;
458421
458570
  }
458571
+ let uri;
458422
458572
  try {
458423
- const uri = this.pathToUri(filePath);
458573
+ uri = this.pathToUri(filePath);
458424
458574
  const content = await this.getDocumentContent(filePath);
458425
458575
  if (!content) {
458426
458576
  return null;
458427
458577
  }
458428
458578
  await client.openDocument(uri, content);
458579
+ if (!client.isReady()) {
458580
+ return null;
458581
+ }
458429
458582
  const position = { line, character: column };
458430
458583
  const locations = await client.gotoDefinition(uri, position);
458431
- await client.closeDocument(uri);
458432
458584
  return locations.length > 0 ? locations[0] : null;
458433
458585
  } catch (error40) {
458434
458586
  console.debug("LSP findDefinition error:", error40);
458435
458587
  return null;
458588
+ } finally {
458589
+ if (uri) {
458590
+ try {
458591
+ await client.closeDocument(uri);
458592
+ } catch {
458593
+ }
458594
+ }
458436
458595
  }
458437
458596
  }
458438
458597
  async findReferences(filePath, line, column, maxResults = 100) {
@@ -458444,20 +458603,30 @@ var init_LSPManager = __esm({
458444
458603
  if (!client) {
458445
458604
  return [];
458446
458605
  }
458606
+ let uri;
458447
458607
  try {
458448
- const uri = this.pathToUri(filePath);
458608
+ uri = this.pathToUri(filePath);
458449
458609
  const content = await this.getDocumentContent(filePath);
458450
458610
  if (!content) {
458451
458611
  return [];
458452
458612
  }
458453
458613
  await client.openDocument(uri, content);
458614
+ if (!client.isReady()) {
458615
+ return [];
458616
+ }
458454
458617
  const position = { line, character: column };
458455
458618
  const locations = await client.findReferences(uri, position, false);
458456
- await client.closeDocument(uri);
458457
458619
  return locations.slice(0, maxResults);
458458
458620
  } catch (error40) {
458459
458621
  console.debug("LSP findReferences error:", error40);
458460
458622
  return [];
458623
+ } finally {
458624
+ if (uri) {
458625
+ try {
458626
+ await client.closeDocument(uri);
458627
+ } catch {
458628
+ }
458629
+ }
458461
458630
  }
458462
458631
  }
458463
458632
  async getDocumentSymbols(filePath) {
@@ -458469,19 +458638,29 @@ var init_LSPManager = __esm({
458469
458638
  if (!client) {
458470
458639
  return null;
458471
458640
  }
458641
+ let uri;
458472
458642
  try {
458473
- const uri = this.pathToUri(filePath);
458643
+ uri = this.pathToUri(filePath);
458474
458644
  const content = await this.getDocumentContent(filePath);
458475
458645
  if (!content) {
458476
458646
  return null;
458477
458647
  }
458478
458648
  await client.openDocument(uri, content);
458649
+ if (!client.isReady()) {
458650
+ return null;
458651
+ }
458479
458652
  const symbols2 = await client.documentSymbol(uri);
458480
- await client.closeDocument(uri);
458481
458653
  return symbols2;
458482
458654
  } catch (error40) {
458483
458655
  console.debug("LSP documentSymbol error:", error40);
458484
458656
  return null;
458657
+ } finally {
458658
+ if (uri) {
458659
+ try {
458660
+ await client.closeDocument(uri);
458661
+ } catch {
458662
+ }
458663
+ }
458485
458664
  }
458486
458665
  }
458487
458666
  async getHoverInfo(filePath, line, column) {
@@ -458493,20 +458672,30 @@ var init_LSPManager = __esm({
458493
458672
  if (!client) {
458494
458673
  return null;
458495
458674
  }
458675
+ let uri;
458496
458676
  try {
458497
- const uri = this.pathToUri(filePath);
458677
+ uri = this.pathToUri(filePath);
458498
458678
  const content = await this.getDocumentContent(filePath);
458499
458679
  if (!content) {
458500
458680
  return null;
458501
458681
  }
458502
458682
  await client.openDocument(uri, content);
458683
+ if (!client.isReady()) {
458684
+ return null;
458685
+ }
458503
458686
  const position = { line, character: column };
458504
458687
  const hover = await client.hover(uri, position);
458505
- await client.closeDocument(uri);
458506
458688
  return hover;
458507
458689
  } catch (error40) {
458508
458690
  console.debug("LSP hover error:", error40);
458509
458691
  return null;
458692
+ } finally {
458693
+ if (uri) {
458694
+ try {
458695
+ await client.closeDocument(uri);
458696
+ } catch {
458697
+ }
458698
+ }
458510
458699
  }
458511
458700
  }
458512
458701
  async getDocumentContent(filePath) {
@@ -458641,6 +458830,8 @@ var init_HybridCodeSearchService = __esm({
458641
458830
  const timeoutMs = contextFile.endsWith(".cs") ? this.csharpLspTimeout : this.lspTimeout;
458642
458831
  const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13(null), timeoutMs));
458643
458832
  const lspPromise = this.lspManager.findDefinition(contextFile, position.line, position.column);
458833
+ lspPromise.catch(() => {
458834
+ });
458644
458835
  const location = await Promise.race([lspPromise, timeoutPromise]);
458645
458836
  if (!location) {
458646
458837
  return null;
@@ -458662,6 +458853,8 @@ var init_HybridCodeSearchService = __esm({
458662
458853
  try {
458663
458854
  const timeoutPromise = new Promise((resolve13) => setTimeout(() => resolve13(null), this.lspTimeout));
458664
458855
  const lspPromise = this.lspManager.getDocumentSymbols(filePath);
458856
+ lspPromise.catch(() => {
458857
+ });
458665
458858
  const symbols2 = await Promise.race([lspPromise, timeoutPromise]);
458666
458859
  if (symbols2 && symbols2.length > 0) {
458667
458860
  return this.convertLSPSymbolsToCodeSymbols(symbols2, filePath);
@@ -467773,22 +467966,36 @@ function millisecondsToLabel(intervalMs) {
467773
467966
  }
467774
467967
  return `${intervalMs / 1e3}s`;
467775
467968
  }
467969
+ function parseDurationString(durationStr) {
467970
+ const pattern = /(\d+)\s*([a-zA-Z]+)/g;
467971
+ let match;
467972
+ let totalMs = 0;
467973
+ while ((match = pattern.exec(durationStr)) !== null) {
467974
+ const value = Number.parseInt(match[1], 10);
467975
+ const unit = match[2];
467976
+ totalMs += unitToMilliseconds(value, unit);
467977
+ }
467978
+ if (totalMs <= 0) {
467979
+ throw new Error("Invalid duration string.");
467980
+ }
467981
+ return totalMs;
467982
+ }
467776
467983
  function formatTimestamp(timestamp) {
467777
467984
  return new Date(timestamp).toLocaleString();
467778
467985
  }
467779
467986
  function parseLoopSchedule(rawArgs) {
467780
467987
  const args2 = (rawArgs == null ? void 0 : rawArgs.trim()) || "";
467781
467988
  if (!args2) {
467782
- throw new Error("Usage: /loop 5m <prompt> | /loop <prompt> every 2 hours | /loop list | /loop cancel <id> | /loop tasks");
467989
+ throw new Error("Usage: /loop 5m <prompt> | /loop 8h30m <prompt> | /loop <prompt> every 2 hours | /loop list | /loop cancel <id> | /loop tasks");
467783
467990
  }
467784
- if (/^\d+\s*[a-zA-Z]+$/.test(args2)) {
467991
+ if (/^(?:\d+\s*[a-zA-Z]+\s*)+\s*$/.test(args2)) {
467785
467992
  throw new Error("Loop prompt is required after the interval.");
467786
467993
  }
467787
- const prefixMatch = args2.match(/^(\d+)\s*([a-zA-Z]+)\s+([\s\S]+)$/);
467788
- if ((prefixMatch == null ? void 0 : prefixMatch[1]) && prefixMatch[2] && prefixMatch[3]) {
467789
- const intervalMs = unitToMilliseconds(Number.parseInt(prefixMatch[1], 10), prefixMatch[2]);
467994
+ const prefixMatch = args2.match(/^((?:\d+\s*[a-zA-Z]+\s*)+?)\s+([\s\S]+)$/);
467995
+ if ((prefixMatch == null ? void 0 : prefixMatch[1]) && prefixMatch[2]) {
467996
+ const intervalMs = parseDurationString(prefixMatch[1]);
467790
467997
  return {
467791
- prompt: prefixMatch[3].trim(),
467998
+ prompt: prefixMatch[2].trim(),
467792
467999
  intervalMs,
467793
468000
  intervalLabel: millisecondsToLabel(intervalMs)
467794
468001
  };
@@ -467965,7 +468172,7 @@ var init_loop = __esm({
467965
468172
  "use strict";
467966
468173
  init_commandExecutor();
467967
468174
  init_loopManager();
467968
- LOOP_USAGE = "Usage: /loop 5m <prompt> | /loop <prompt> every 2 hours | /loop list | /loop cancel <id> | /loop tasks";
468175
+ LOOP_USAGE = "Usage: /loop 5m <prompt> | /loop 8h30m <prompt> | /loop <prompt> every 2 hours | /loop list | /loop cancel <id> | /loop tasks";
467969
468176
  registerCommand("loop", {
467970
468177
  execute: async (args2) => {
467971
468178
  const trimmedArgs = args2 == null ? void 0 : args2.trim();
@@ -602100,7 +602307,7 @@ var require_package3 = __commonJS({
602100
602307
  "package.json"(exports2, module2) {
602101
602308
  module2.exports = {
602102
602309
  name: "snow-ai",
602103
- version: "0.7.15",
602310
+ version: "0.7.16",
602104
602311
  description: "Agentic coding in your terminal",
602105
602312
  license: "MIT",
602106
602313
  bin: {
@@ -603602,6 +603809,28 @@ process.emitWarning = function(warning, ...args2) {
603602
603809
  return;
603603
603810
  return originalEmitWarning.apply(process, [warning, ...args2]);
603604
603811
  };
603812
+ function isStreamDestroyedError(err) {
603813
+ if (!(err instanceof Error))
603814
+ return false;
603815
+ const code = err.code;
603816
+ if (code === "ERR_STREAM_DESTROYED" || code === "EPIPE")
603817
+ return true;
603818
+ const msg = err.message || "";
603819
+ return msg.includes("stream was destroyed") || msg.includes("ERR_STREAM_DESTROYED") || msg.includes("write after end") || msg.includes("Cannot call write after a stream was destroyed");
603820
+ }
603821
+ process.on("uncaughtException", (err) => {
603822
+ if (isStreamDestroyedError(err)) {
603823
+ return;
603824
+ }
603825
+ console.error("Uncaught Exception:", err);
603826
+ process.exit(1);
603827
+ });
603828
+ process.on("unhandledRejection", (reason) => {
603829
+ if (isStreamDestroyedError(reason)) {
603830
+ return;
603831
+ }
603832
+ console.error("Unhandled Rejection:", reason);
603833
+ });
603605
603834
  var args = process.argv.slice(2);
603606
603835
  var isQuickCommand = args.some((arg) => arg === "--version" || arg === "-v" || arg === "--help" || arg === "-h" || arg === "--acp" || arg === "--sse" || arg === "--sse-daemon");
603607
603836
  if (!isQuickCommand) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.7.15",
3
+ "version": "0.7.16",
4
4
  "description": "Agentic coding in your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.7.15",
3
+ "version": "0.7.16",
4
4
  "description": "Agentic coding in your terminal",
5
5
  "license": "MIT",
6
6
  "bin": {