claudemesh-cli 0.6.7 → 0.6.9

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.
Files changed (3) hide show
  1. package/LICENSE.md +37 -0
  2. package/dist/index.js +357 -130
  3. package/package.json +20 -21
package/LICENSE.md ADDED
@@ -0,0 +1,37 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alezmad (claudemesh)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+ ---
24
+
25
+ ## Attribution
26
+
27
+ This project was originally scaffolded using TurboStarter (https://turbostarter.dev),
28
+ a proprietary SaaS starter kit. The TurboStarter scaffold code is covered by
29
+ your separate purchase agreement with TurboStarter and is NOT re-licensed by
30
+ this MIT license. The MIT license above covers claudemesh-specific additions,
31
+ modifications, and original code written on top of that scaffold — including
32
+ but not limited to: apps/broker, apps/cli, apps/web/src/modules/marketing/home,
33
+ packages/db/src/schema/mesh.ts, the protocol, and the documentation.
34
+
35
+ If you are redistributing this repository, you are responsible for compliance
36
+ with BOTH the TurboStarter EULA (for scaffold components) and this MIT license
37
+ (for claudemesh code).
package/dist/index.js CHANGED
@@ -47376,31 +47376,22 @@ var TOOLS = [
47376
47376
  },
47377
47377
  {
47378
47378
  name: "schedule_reminder",
47379
- description: "Schedule a reminder message delivered back to yourself at a future time. The broker fires it as a push when the time arrives. Use to prompt yourself to check on something later.",
47379
+ description: "Schedule a one-shot or recurring message. Without `to`, it fires back to yourself (a self-reminder). With `to`, it delivers to a peer, @group, or * broadcast. For one-shot, provide `deliver_at` or `in_seconds`. For recurring, provide `cron` (standard 5-field expression). The broker persists schedules to the database they survive restarts. Receivers see `subtype: reminder` in the push envelope.",
47380
47380
  inputSchema: {
47381
47381
  type: "object",
47382
47382
  properties: {
47383
- message: { type: "string", description: "Reminder text" },
47384
- deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver" },
47385
- in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds" }
47383
+ message: { type: "string", description: "Message or reminder text" },
47384
+ deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver (one-shot)" },
47385
+ in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds (one-shot)" },
47386
+ cron: { type: "string", description: "Cron expression for recurring reminders (e.g. '0 */2 * * *' for every 2 hours, '30 9 * * 1-5' for 9:30 weekdays)" },
47387
+ to: {
47388
+ type: "string",
47389
+ description: "Recipient: display name, pubkey hex, @group, or * (omit for self-reminder)"
47390
+ }
47386
47391
  },
47387
47392
  required: ["message"]
47388
47393
  }
47389
47394
  },
47390
- {
47391
- name: "send_later",
47392
- description: "Send a message to a peer, @group, or broadcast (*) at a future time. The broker holds it and delivers when the time arrives.",
47393
- inputSchema: {
47394
- type: "object",
47395
- properties: {
47396
- to: { type: "string", description: "Recipient: display name, pubkey hex, @group, or *" },
47397
- message: { type: "string", description: "Message text" },
47398
- deliver_at: { type: "number", description: "Unix timestamp (ms) when to deliver" },
47399
- in_seconds: { type: "number", description: "Alternative to deliver_at: fire after N seconds" }
47400
- },
47401
- required: ["to", "message"]
47402
- }
47403
- },
47404
47395
  {
47405
47396
  name: "list_scheduled",
47406
47397
  description: "List all your pending scheduled messages: id, recipient, preview, and delivery time.",
@@ -47575,6 +47566,9 @@ class BrokerClient {
47575
47566
  sessionId: `${process.pid}-${Date.now()}`,
47576
47567
  pid: process.pid,
47577
47568
  cwd: process.cwd(),
47569
+ peerType: "ai",
47570
+ channel: "claude-code",
47571
+ model: process.env.CLAUDE_MODEL || undefined,
47578
47572
  timestamp,
47579
47573
  signature
47580
47574
  }));
@@ -47783,7 +47777,7 @@ class BrokerClient {
47783
47777
  return;
47784
47778
  this.ws.send(JSON.stringify({ type: "forget", memoryId }));
47785
47779
  }
47786
- async scheduleMessage(to, message, deliverAt) {
47780
+ async scheduleMessage(to, message, deliverAt, isReminder = false, cron) {
47787
47781
  if (!this.ws || this.ws.readyState !== this.ws.OPEN)
47788
47782
  return null;
47789
47783
  return new Promise((resolve) => {
@@ -47792,7 +47786,15 @@ class BrokerClient {
47792
47786
  if (this.scheduledAckResolvers.delete(reqId))
47793
47787
  resolve(null);
47794
47788
  }, 8000) });
47795
- this.ws.send(JSON.stringify({ type: "schedule", to, message, deliverAt, _reqId: reqId }));
47789
+ this.ws.send(JSON.stringify({
47790
+ type: "schedule",
47791
+ to,
47792
+ message,
47793
+ deliverAt,
47794
+ ...isReminder ? { subtype: "reminder" } : {},
47795
+ ...cron ? { cron, recurring: true } : {},
47796
+ _reqId: reqId
47797
+ }));
47796
47798
  });
47797
47799
  }
47798
47800
  async listScheduled() {
@@ -48236,7 +48238,10 @@ class BrokerClient {
48236
48238
  createdAt: String(msg.createdAt ?? ""),
48237
48239
  receivedAt: new Date().toISOString(),
48238
48240
  plaintext,
48239
- kind
48241
+ kind,
48242
+ ...msg.subtype ? { subtype: msg.subtype } : {},
48243
+ ...msg.event ? { event: String(msg.event) } : {},
48244
+ ...msg.eventData ? { eventData: msg.eventData } : {}
48240
48245
  };
48241
48246
  this.pushBuffer.push(push);
48242
48247
  if (this.pushBuffer.length > 500)
@@ -48404,7 +48409,8 @@ class BrokerClient {
48404
48409
  if (msg.type === "scheduled_ack") {
48405
48410
  this.resolveFromMap(this.scheduledAckResolvers, msgReqId, {
48406
48411
  scheduledId: String(msg.scheduledId ?? ""),
48407
- deliverAt: Number(msg.deliverAt ?? 0)
48412
+ deliverAt: Number(msg.deliverAt ?? 0),
48413
+ ...msg.cron ? { cron: String(msg.cron) } : {}
48408
48414
  });
48409
48415
  return;
48410
48416
  }
@@ -48631,7 +48637,8 @@ function decryptFailedWarning(senderPubkey) {
48631
48637
  }
48632
48638
  function formatPush(p, meshSlug) {
48633
48639
  const body = p.plaintext ?? decryptFailedWarning(p.senderPubkey);
48634
- return `[${meshSlug}] from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):
48640
+ const tag = p.subtype === "reminder" ? " [REMINDER]" : "";
48641
+ return `[${meshSlug}]${tag} from ${p.senderPubkey.slice(0, 12)}… (${p.priority}, ${p.createdAt}):
48635
48642
  ${body}`;
48636
48643
  }
48637
48644
  async function startMcpServer() {
@@ -48651,6 +48658,8 @@ You are "${myName}"${myRole ? ` (${myRole})` : ""} — a peer in the claudemesh
48651
48658
  ## Responding to messages
48652
48659
  When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATELY. Pause your current task, reply via send_message, then resume. Read from_name, mesh_slug, and priority from the channel attributes. Reply by setting \`to\` to the sender's from_name (display name). Stay in character per your system prompt. Do not ignore low-priority messages — acknowledge them briefly even if you defer action.
48653
48660
 
48661
+ If the channel meta contains \`subtype: reminder\`, this is a scheduled reminder you set for yourself — act on it immediately (no reply needed).
48662
+
48654
48663
  ## Tools
48655
48664
  | Tool | Description |
48656
48665
  |------|-------------|
@@ -48692,6 +48701,9 @@ When you receive a <channel source="claudemesh" ...> message, RESPOND IMMEDIATEL
48692
48701
  | claim_task(id) | Claim an unclaimed task. |
48693
48702
  | complete_task(id, result?) | Mark task done with optional result. |
48694
48703
  | list_tasks(status?, assignee?) | List tasks filtered by status/assignee. |
48704
+ | schedule_reminder(message, in_seconds?, deliver_at?, to?) | Schedule a reminder to yourself (no \`to\`) or a delayed message to a peer/group. Delivered as a push with \`subtype: reminder\` in the channel meta. |
48705
+ | list_scheduled() | List pending scheduled reminders and messages. |
48706
+ | cancel_scheduled(id) | Cancel a pending scheduled item. |
48695
48707
 
48696
48708
  If multiple meshes are joined, prefix \`to\` with \`<mesh-slug>:\` to disambiguate (e.g. \`dev-team:Alice\`).
48697
48709
 
@@ -48801,7 +48813,16 @@ No peers connected.`);
48801
48813
  const peerLines = peers.map((p) => {
48802
48814
  const summary = p.summary ? ` — "${p.summary}"` : "";
48803
48815
  const groupsStr = p.groups?.length ? ` [${p.groups.map((g) => `@${g.name}${g.role ? ":" + g.role : ""}`).join(", ")}]` : "";
48804
- return `- **${p.displayName}** [${p.status}]${groupsStr} (${p.pubkey.slice(0, 12)}…)${summary}`;
48816
+ const meta2 = [];
48817
+ if (p.peerType)
48818
+ meta2.push(`type:${p.peerType}`);
48819
+ if (p.channel)
48820
+ meta2.push(`channel:${p.channel}`);
48821
+ if (p.model)
48822
+ meta2.push(`model:${p.model}`);
48823
+ const metaStr = meta2.length ? ` {${meta2.join(", ")}}` : "";
48824
+ const cwdStr = p.cwd ? ` cwd:${p.cwd}` : "";
48825
+ return `- **${p.displayName}** [${p.status}]${groupsStr}${metaStr} (${p.pubkey.slice(0, 12)}…)${cwdStr}${summary}`;
48805
48826
  });
48806
48827
  sections.push(`${header}
48807
48828
  ${peerLines.join(`
@@ -48950,40 +48971,48 @@ ${lines.join(`
48950
48971
  await client2.forget(id);
48951
48972
  return text(`Forgotten: ${id}`);
48952
48973
  }
48953
- case "schedule_reminder":
48954
- case "send_later": {
48974
+ case "schedule_reminder": {
48955
48975
  const sArgs = args ?? {};
48956
48976
  if (!sArgs.message)
48957
- return text(`${name}: \`message\` required`, true);
48958
- const to = name === "schedule_reminder" ? "self" : sArgs.to ?? "";
48959
- if (name === "send_later" && !to)
48960
- return text("send_later: `to` required", true);
48977
+ return text("schedule_reminder: `message` required", true);
48978
+ const isCron = !!sArgs.cron;
48961
48979
  let deliverAt;
48962
- if (sArgs.deliver_at) {
48980
+ if (isCron) {
48981
+ deliverAt = 0;
48982
+ } else if (sArgs.deliver_at) {
48963
48983
  deliverAt = Number(sArgs.deliver_at);
48964
48984
  } else if (sArgs.in_seconds) {
48965
48985
  deliverAt = Date.now() + Number(sArgs.in_seconds) * 1000;
48966
48986
  } else {
48967
- return text(`${name}: provide \`deliver_at\` (ms timestamp) or \`in_seconds\``, true);
48968
- }
48969
- let targetSpec = to;
48970
- if (name === "send_later" && !to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to) && to !== "self") {
48971
- const peers = await client.listPeers();
48972
- const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
48973
- if (!match) {
48974
- const names = peers.map((p) => p.displayName).join(", ");
48975
- return text(`send_later: peer "${to}" not found. Online: ${names || "(none)"}`, true);
48976
- }
48977
- targetSpec = match.pubkey;
48987
+ return text("schedule_reminder: provide `deliver_at` (ms timestamp), `in_seconds`, or `cron` expression", true);
48978
48988
  }
48979
- if (name === "schedule_reminder") {
48989
+ const isSelf = !sArgs.to;
48990
+ let targetSpec;
48991
+ if (isSelf) {
48980
48992
  targetSpec = client.getSessionPubkey() ?? "*";
48993
+ } else {
48994
+ const to = sArgs.to;
48995
+ if (!to.startsWith("@") && to !== "*" && !/^[0-9a-f]{64}$/i.test(to)) {
48996
+ const peers = await client.listPeers();
48997
+ const match = peers.find((p) => p.displayName.toLowerCase() === to.toLowerCase());
48998
+ if (!match) {
48999
+ const names = peers.map((p) => p.displayName).join(", ");
49000
+ return text(`schedule_reminder: peer "${to}" not found. Online: ${names || "(none)"}`, true);
49001
+ }
49002
+ targetSpec = match.pubkey;
49003
+ } else {
49004
+ targetSpec = to;
49005
+ }
48981
49006
  }
48982
- const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt);
49007
+ const result = await client.scheduleMessage(targetSpec, sArgs.message, deliverAt, true, sArgs.cron);
48983
49008
  if (!result)
48984
- return text(`${name}: broker did not acknowledge — check connection`, true);
49009
+ return text("schedule_reminder: broker did not acknowledge — check connection", true);
49010
+ if (isCron) {
49011
+ const nextFire = new Date(result.deliverAt).toISOString();
49012
+ return text(isSelf ? `Recurring self-reminder scheduled (${result.scheduledId.slice(0, 8)}): "${sArgs.message.slice(0, 60)}" — cron: ${sArgs.cron}, next fire: ${nextFire}` : `Recurring reminder to "${sArgs.to}" scheduled (${result.scheduledId.slice(0, 8)}) — cron: ${sArgs.cron}, next fire: ${nextFire}`);
49013
+ }
48985
49014
  const when = new Date(result.deliverAt).toISOString();
48986
- return text(name === "schedule_reminder" ? `Reminder scheduled (${result.scheduledId.slice(0, 8)}): "${sArgs.message.slice(0, 60)}" at ${when}` : `Message to "${to}" scheduled (${result.scheduledId.slice(0, 8)}) for ${when}`);
49015
+ return text(isSelf ? `Self-reminder scheduled (${result.scheduledId.slice(0, 8)}): "${sArgs.message.slice(0, 60)}" at ${when}` : `Reminder to "${sArgs.to}" scheduled (${result.scheduledId.slice(0, 8)}) for ${when}`);
48987
49016
  }
48988
49017
  case "list_scheduled": {
48989
49018
  const scheduled = await client.listScheduled();
@@ -49497,6 +49526,39 @@ ${rows.join(`
49497
49526
  client2.onPush(async (msg) => {
49498
49527
  if (messageMode === "off")
49499
49528
  return;
49529
+ if (msg.subtype === "system" && msg.event) {
49530
+ const eventName = msg.event;
49531
+ const data = msg.eventData ?? {};
49532
+ let content2;
49533
+ if (eventName === "peer_joined") {
49534
+ content2 = `[system] Peer "${data.name ?? "unknown"}" joined the mesh`;
49535
+ } else if (eventName === "peer_left") {
49536
+ content2 = `[system] Peer "${data.name ?? "unknown"}" left the mesh`;
49537
+ } else {
49538
+ content2 = `[system] ${eventName}: ${JSON.stringify(data)}`;
49539
+ }
49540
+ try {
49541
+ await server.notification({
49542
+ method: "notifications/claude/channel",
49543
+ params: {
49544
+ content: content2,
49545
+ meta: {
49546
+ kind: "system",
49547
+ event: eventName,
49548
+ mesh_slug: client2.meshSlug,
49549
+ mesh_id: client2.meshId,
49550
+ ...Object.keys(data).length > 0 ? { eventData: data } : {}
49551
+ }
49552
+ }
49553
+ });
49554
+ process.stderr.write(`[claudemesh] system: ${content2}
49555
+ `);
49556
+ } catch (pushErr) {
49557
+ process.stderr.write(`[claudemesh] system push FAILED: ${pushErr}
49558
+ `);
49559
+ }
49560
+ return;
49561
+ }
49500
49562
  const fromPubkey = msg.senderPubkey || "";
49501
49563
  const fromName = fromPubkey ? await resolvePeerName(client2, fromPubkey) : "unknown";
49502
49564
  if (messageMode === "inbox") {
@@ -49525,7 +49587,8 @@ ${rows.join(`
49525
49587
  priority: msg.priority,
49526
49588
  sent_at: msg.createdAt,
49527
49589
  delivered_at: msg.receivedAt,
49528
- kind: msg.kind
49590
+ kind: msg.kind,
49591
+ ...msg.subtype ? { subtype: msg.subtype } : {}
49529
49592
  }
49530
49593
  }
49531
49594
  });
@@ -49719,47 +49782,51 @@ function writeClaudeSettings(obj) {
49719
49782
  `, "utf-8");
49720
49783
  }
49721
49784
  var CLAUDEMESH_TOOLS = [
49722
- "mcp__claudemesh__send_message",
49723
- "mcp__claudemesh__list_peers",
49785
+ "mcp__claudemesh__cancel_scheduled",
49724
49786
  "mcp__claudemesh__check_messages",
49725
- "mcp__claudemesh__set_summary",
49726
- "mcp__claudemesh__set_status",
49727
- "mcp__claudemesh__join_group",
49728
- "mcp__claudemesh__leave_group",
49729
- "mcp__claudemesh__get_state",
49730
- "mcp__claudemesh__set_state",
49731
- "mcp__claudemesh__list_state",
49732
- "mcp__claudemesh__remember",
49733
- "mcp__claudemesh__recall",
49787
+ "mcp__claudemesh__claim_task",
49788
+ "mcp__claudemesh__complete_task",
49789
+ "mcp__claudemesh__create_stream",
49790
+ "mcp__claudemesh__create_task",
49791
+ "mcp__claudemesh__delete_file",
49792
+ "mcp__claudemesh__file_status",
49734
49793
  "mcp__claudemesh__forget",
49735
- "mcp__claudemesh__share_file",
49794
+ "mcp__claudemesh__get_context",
49736
49795
  "mcp__claudemesh__get_file",
49737
- "mcp__claudemesh__list_files",
49738
- "mcp__claudemesh__file_status",
49739
- "mcp__claudemesh__delete_file",
49740
- "mcp__claudemesh__vector_store",
49741
- "mcp__claudemesh__vector_search",
49742
- "mcp__claudemesh__vector_delete",
49743
- "mcp__claudemesh__list_collections",
49744
- "mcp__claudemesh__graph_query",
49796
+ "mcp__claudemesh__get_state",
49797
+ "mcp__claudemesh__grant_file_access",
49745
49798
  "mcp__claudemesh__graph_execute",
49746
- "mcp__claudemesh__mesh_info",
49747
- "mcp__claudemesh__ping_mesh",
49748
- "mcp__claudemesh__message_status",
49749
- "mcp__claudemesh__share_context",
49750
- "mcp__claudemesh__get_context",
49799
+ "mcp__claudemesh__graph_query",
49800
+ "mcp__claudemesh__join_group",
49801
+ "mcp__claudemesh__leave_group",
49802
+ "mcp__claudemesh__list_collections",
49751
49803
  "mcp__claudemesh__list_contexts",
49752
- "mcp__claudemesh__create_task",
49753
- "mcp__claudemesh__claim_task",
49754
- "mcp__claudemesh__complete_task",
49755
- "mcp__claudemesh__list_tasks",
49756
- "mcp__claudemesh__create_stream",
49757
- "mcp__claudemesh__publish",
49758
- "mcp__claudemesh__subscribe",
49804
+ "mcp__claudemesh__list_files",
49805
+ "mcp__claudemesh__list_peers",
49806
+ "mcp__claudemesh__list_scheduled",
49807
+ "mcp__claudemesh__list_state",
49759
49808
  "mcp__claudemesh__list_streams",
49809
+ "mcp__claudemesh__list_tasks",
49760
49810
  "mcp__claudemesh__mesh_execute",
49811
+ "mcp__claudemesh__mesh_info",
49761
49812
  "mcp__claudemesh__mesh_query",
49762
- "mcp__claudemesh__mesh_schema"
49813
+ "mcp__claudemesh__mesh_schema",
49814
+ "mcp__claudemesh__message_status",
49815
+ "mcp__claudemesh__ping_mesh",
49816
+ "mcp__claudemesh__publish",
49817
+ "mcp__claudemesh__recall",
49818
+ "mcp__claudemesh__remember",
49819
+ "mcp__claudemesh__schedule_reminder",
49820
+ "mcp__claudemesh__send_message",
49821
+ "mcp__claudemesh__set_state",
49822
+ "mcp__claudemesh__set_status",
49823
+ "mcp__claudemesh__set_summary",
49824
+ "mcp__claudemesh__share_context",
49825
+ "mcp__claudemesh__share_file",
49826
+ "mcp__claudemesh__subscribe",
49827
+ "mcp__claudemesh__vector_delete",
49828
+ "mcp__claudemesh__vector_search",
49829
+ "mcp__claudemesh__vector_store"
49763
49830
  ];
49764
49831
  function installAllowedTools() {
49765
49832
  const settings = readClaudeSettings();
@@ -50553,7 +50620,7 @@ init_config();
50553
50620
  // package.json
50554
50621
  var package_default = {
50555
50622
  name: "claudemesh-cli",
50556
- version: "0.6.7",
50623
+ version: "0.6.9",
50557
50624
  description: "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
50558
50625
  keywords: [
50559
50626
  "claude-code",
@@ -51019,8 +51086,19 @@ async function runPeers(flags) {
51019
51086
  const groups = p.groups.length ? " [" + p.groups.map((g) => `@${g.name}${g.role ? `:${g.role}` : ""}`).join(", ") + "]" : "";
51020
51087
  const statusIcon = p.status === "working" ? yellow("●") : green("●");
51021
51088
  const name = bold2(p.displayName);
51089
+ const meta2 = [];
51090
+ if (p.peerType)
51091
+ meta2.push(p.peerType);
51092
+ if (p.channel)
51093
+ meta2.push(p.channel);
51094
+ if (p.model)
51095
+ meta2.push(p.model);
51096
+ const metaStr = meta2.length ? dim(` (${meta2.join(", ")})`) : "";
51097
+ const cwdStr = p.cwd ? dim(` cwd: ${p.cwd}`) : "";
51022
51098
  const summary = p.summary ? dim(` ${p.summary}`) : "";
51023
- console.log(` ${statusIcon} ${name}${groups}${summary}`);
51099
+ console.log(` ${statusIcon} ${name}${groups}${metaStr}${summary}`);
51100
+ if (cwdStr)
51101
+ console.log(` ${cwdStr}`);
51024
51102
  }
51025
51103
  console.log("");
51026
51104
  });
@@ -51307,13 +51385,15 @@ async function runRemind(flags, positional) {
51307
51385
  if (!message) {
51308
51386
  console.error("Usage: claudemesh remind <message> --in <duration>");
51309
51387
  console.error(" claudemesh remind <message> --at <time>");
51388
+ console.error(' claudemesh remind <message> --cron "0 */2 * * *"');
51310
51389
  console.error(" claudemesh remind list");
51311
51390
  console.error(" claudemesh remind cancel <id>");
51312
51391
  process.exit(1);
51313
51392
  }
51314
- const deliverAt = parseDeliverAt(flags);
51315
- if (deliverAt === null) {
51316
- console.error('Specify when: --in <duration> (e.g. "2h", "30m") or --at <time> (e.g. "15:00")');
51393
+ const isCron = !!flags.cron;
51394
+ const deliverAt = isCron ? 0 : parseDeliverAt(flags);
51395
+ if (!isCron && deliverAt === null) {
51396
+ console.error('Specify when: --in <duration> (e.g. "2h", "30m"), --at <time> (e.g. "15:00"), or --cron <expression>');
51317
51397
  process.exit(1);
51318
51398
  }
51319
51399
  await withMesh({ meshSlug: flags.mesh ?? null }, async (client2) => {
@@ -51333,7 +51413,7 @@ async function runRemind(flags, positional) {
51333
51413
  } else {
51334
51414
  targetSpec = client2.getSessionPubkey() ?? "*";
51335
51415
  }
51336
- const result = await client2.scheduleMessage(targetSpec, message, deliverAt);
51416
+ const result = await client2.scheduleMessage(targetSpec, message, deliverAt ?? 0, false, flags.cron);
51337
51417
  if (!result) {
51338
51418
  console.error("✗ Broker did not acknowledge — check connection");
51339
51419
  process.exit(1);
@@ -51342,34 +51422,170 @@ async function runRemind(flags, positional) {
51342
51422
  console.log(JSON.stringify(result));
51343
51423
  return;
51344
51424
  }
51345
- const when = new Date(result.deliverAt).toLocaleString();
51346
51425
  const toLabel = !flags.to || flags.to === "self" ? "yourself" : flags.to;
51347
- console.log(`✓ Reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} at ${when}`);
51426
+ if (isCron) {
51427
+ const nextFire = new Date(result.deliverAt).toLocaleString();
51428
+ console.log(`✓ Recurring reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} — cron: ${flags.cron}, next fire: ${nextFire}`);
51429
+ } else {
51430
+ const when = new Date(result.deliverAt).toLocaleString();
51431
+ console.log(`✓ Reminder set (${result.scheduledId.slice(0, 8)}): "${message}" → ${toLabel} at ${when}`);
51432
+ }
51348
51433
  });
51349
51434
  }
51435
+ // src/templates/dev-team.json
51436
+ var dev_team_default = {
51437
+ name: "dev-team",
51438
+ description: "Software development team with frontend, backend, and devops groups",
51439
+ groups: [
51440
+ { name: "frontend", roles: ["lead", "member"] },
51441
+ { name: "backend", roles: ["lead", "member"] },
51442
+ { name: "devops", roles: ["lead", "member"] },
51443
+ { name: "qa", roles: ["lead", "member"] }
51444
+ ],
51445
+ stateKeys: {
51446
+ sprint: "current",
51447
+ "deploy-frozen": "false",
51448
+ "pr-queue": "[]"
51449
+ },
51450
+ suggestedRoles: ["lead", "member", "reviewer"],
51451
+ systemPromptHint: "You are part of a dev team. Coordinate with @frontend, @backend, @devops groups. Check state keys for sprint status and deploy freezes before making changes."
51452
+ };
51453
+ // src/templates/research.json
51454
+ var research_default = {
51455
+ name: "research",
51456
+ description: "Research and analysis team focused on deep investigation and knowledge sharing",
51457
+ groups: [
51458
+ { name: "analysis", roles: ["lead", "analyst"] },
51459
+ { name: "writing", roles: ["lead", "writer", "reviewer"] },
51460
+ { name: "data", roles: ["engineer", "analyst"] }
51461
+ ],
51462
+ stateKeys: {
51463
+ "research-topic": "",
51464
+ phase: "exploration",
51465
+ "findings-count": "0"
51466
+ },
51467
+ suggestedRoles: ["lead", "analyst", "writer", "reviewer"],
51468
+ systemPromptHint: "You are part of a research team. Share findings via remember(), use recall() before starting new analysis. Coordinate phases through state keys."
51469
+ };
51470
+ // src/templates/ops-incident.json
51471
+ var ops_incident_default = {
51472
+ name: "ops-incident",
51473
+ description: "Incident response team with oncall, comms, and engineering groups",
51474
+ groups: [
51475
+ { name: "oncall", roles: ["primary", "secondary"] },
51476
+ { name: "comms", roles: ["lead", "scribe"] },
51477
+ { name: "engineering", roles: ["lead", "responder"] }
51478
+ ],
51479
+ stateKeys: {
51480
+ "incident-status": "investigating",
51481
+ severity: "unknown",
51482
+ commander: "",
51483
+ timeline: "[]"
51484
+ },
51485
+ suggestedRoles: ["commander", "primary-oncall", "scribe", "responder"],
51486
+ systemPromptHint: "INCIDENT MODE. Priority: now for all messages. Update incident-status state. Commander coordinates. Scribe maintains timeline. Engineering fixes."
51487
+ };
51488
+ // src/templates/simulation.json
51489
+ var simulation_default = {
51490
+ name: "simulation",
51491
+ description: "Load testing simulation with configurable time multiplier and user personas",
51492
+ groups: [
51493
+ { name: "personas", roles: ["admin", "user", "customer"] },
51494
+ { name: "observers", roles: ["monitor", "analyst"] },
51495
+ { name: "control", roles: ["orchestrator"] }
51496
+ ],
51497
+ stateKeys: {
51498
+ "clock-speed": "x1",
51499
+ "sim-status": "paused",
51500
+ "tick-count": "0",
51501
+ scenario: ""
51502
+ },
51503
+ suggestedRoles: ["orchestrator", "persona", "monitor"],
51504
+ systemPromptHint: "SIMULATION MODE. Follow the clock-speed state for time multiplier. Act according to your persona role and the simulated time. Report actions to @observers."
51505
+ };
51506
+ // src/templates/personal.json
51507
+ var personal_default = {
51508
+ name: "personal",
51509
+ description: "Private mesh for a single user — all sessions auto-join",
51510
+ groups: [],
51511
+ stateKeys: {
51512
+ focus: "",
51513
+ todos: "[]"
51514
+ },
51515
+ suggestedRoles: [],
51516
+ systemPromptHint: "Personal workspace. All your Claude Code sessions share this mesh. Use state keys to track focus and todos across sessions."
51517
+ };
51518
+
51519
+ // src/templates/index.ts
51520
+ var TEMPLATES = {
51521
+ "dev-team": dev_team_default,
51522
+ research: research_default,
51523
+ "ops-incident": ops_incident_default,
51524
+ simulation: simulation_default,
51525
+ personal: personal_default
51526
+ };
51527
+ function listTemplates() {
51528
+ return Object.values(TEMPLATES);
51529
+ }
51530
+ function getTemplate(name) {
51531
+ return TEMPLATES[name];
51532
+ }
51533
+
51534
+ // src/commands/create.ts
51535
+ function runCreate(args) {
51536
+ if (args["list-templates"]) {
51537
+ console.log(`Available mesh templates:
51538
+ `);
51539
+ for (const t of listTemplates()) {
51540
+ console.log(` ${t.name}`);
51541
+ console.log(` ${t.description}`);
51542
+ console.log(` Groups: ${t.groups.map((g) => g.name).join(", ") || "(none)"}`);
51543
+ console.log(` State keys: ${Object.keys(t.stateKeys).join(", ") || "(none)"}`);
51544
+ console.log();
51545
+ }
51546
+ return;
51547
+ }
51548
+ const templateName = args.template;
51549
+ if (templateName) {
51550
+ const template = getTemplate(templateName);
51551
+ if (!template) {
51552
+ console.error(`Unknown template "${templateName}". Use --list-templates to see available options.`);
51553
+ process.exit(1);
51554
+ }
51555
+ console.log(`Template "${template.name}" loaded:`);
51556
+ console.log(` Groups: ${template.groups.map((g) => `@${g.name}`).join(", ")}`);
51557
+ console.log(` State keys: ${Object.keys(template.stateKeys).join(", ")}`);
51558
+ console.log(` Hint: ${template.systemPromptHint.slice(0, 80)}...`);
51559
+ console.log();
51560
+ console.log("Template applied. Use `claudemesh launch` with --groups to join the predefined groups.");
51561
+ return;
51562
+ }
51563
+ console.log("Usage: claudemesh create --template <name>");
51564
+ console.log(" claudemesh create --list-templates");
51565
+ }
51350
51566
 
51351
51567
  // src/index.ts
51352
51568
  var launch = defineCommand({
51353
51569
  meta: {
51354
51570
  name: "launch",
51355
- description: "Launch Claude Code connected to a mesh with real-time peer messaging"
51571
+ description: "Spawn a Claude Code session with mesh connectivity and MCP tools"
51356
51572
  },
51357
51573
  args: {
51358
51574
  name: {
51359
51575
  type: "string",
51360
- description: "Display name for this session"
51576
+ description: "Display name visible to other peers"
51361
51577
  },
51362
51578
  role: {
51363
51579
  type: "string",
51364
- description: "Role tag (dev, lead, analyst — free-form)"
51580
+ description: "Free-form role tag: `dev`, `lead`, `analyst`, etc"
51365
51581
  },
51366
51582
  groups: {
51367
51583
  type: "string",
51368
- description: 'Groups to join: "group:role,group2"colon sets role. Hierarchy via slash: "eng/frontend:lead"'
51584
+ description: 'Groups to join as `group:role,...`e.g. `"eng/frontend:lead,qa:member"`'
51369
51585
  },
51370
51586
  mesh: {
51371
51587
  type: "string",
51372
- description: "Select mesh by slug (interactive picker if omitted and >1 joined)"
51588
+ description: "Mesh slug (interactive picker if omitted and >1 joined)"
51373
51589
  },
51374
51590
  join: {
51375
51591
  type: "string",
@@ -51377,21 +51593,21 @@ var launch = defineCommand({
51377
51593
  },
51378
51594
  "message-mode": {
51379
51595
  type: "string",
51380
- description: "push (default) | inbox | off — controls how peer messages are delivered"
51596
+ description: '`"push"` (default) | `"inbox"` | `"off"` — how peer messages arrive'
51381
51597
  },
51382
51598
  "system-prompt": {
51383
51599
  type: "string",
51384
- description: "Set Claude's system prompt for this session"
51600
+ description: "Custom system prompt for this Claude session"
51385
51601
  },
51386
51602
  yes: {
51387
51603
  type: "boolean",
51388
51604
  alias: "y",
51389
- description: "Skip permission confirmation",
51605
+ description: "Skip the --dangerously-skip-permissions confirmation",
51390
51606
  default: false
51391
51607
  },
51392
51608
  quiet: {
51393
51609
  type: "boolean",
51394
- description: "Skip banner and all interactive prompts",
51610
+ description: "Suppress banner and interactive prompts",
51395
51611
  default: false
51396
51612
  }
51397
51613
  },
@@ -51402,7 +51618,7 @@ var launch = defineCommand({
51402
51618
  var install = defineCommand({
51403
51619
  meta: {
51404
51620
  name: "install",
51405
- description: "Register MCP server + status hooks with Claude Code"
51621
+ description: "Register MCP server and status hooks with Claude Code"
51406
51622
  },
51407
51623
  args: {
51408
51624
  "no-hooks": {
@@ -51418,12 +51634,12 @@ var install = defineCommand({
51418
51634
  var join7 = defineCommand({
51419
51635
  meta: {
51420
51636
  name: "join",
51421
- description: "Join a mesh via invite URL"
51637
+ description: "Join a mesh via invite URL or token"
51422
51638
  },
51423
51639
  args: {
51424
51640
  url: {
51425
51641
  type: "positional",
51426
- description: "Invite URL (https://claudemesh.com/join/...)",
51642
+ description: "Invite URL (`https://claudemesh.com/join/...`) or token",
51427
51643
  required: true
51428
51644
  }
51429
51645
  },
@@ -51434,12 +51650,12 @@ var join7 = defineCommand({
51434
51650
  var leave = defineCommand({
51435
51651
  meta: {
51436
51652
  name: "leave",
51437
- description: "Leave a joined mesh"
51653
+ description: "Leave a joined mesh and remove its local keypair"
51438
51654
  },
51439
51655
  args: {
51440
51656
  slug: {
51441
51657
  type: "positional",
51442
- description: "Mesh slug to leave",
51658
+ description: "Mesh slug to leave (see `claudemesh list`)",
51443
51659
  required: true
51444
51660
  }
51445
51661
  },
@@ -51455,23 +51671,33 @@ var main = defineCommand({
51455
51671
  },
51456
51672
  subCommands: {
51457
51673
  launch,
51674
+ create: defineCommand({
51675
+ meta: { name: "create", description: "Create a new mesh from a template" },
51676
+ args: {
51677
+ template: { type: "string", description: "Template name: `dev-team`, `research`, `ops-incident`, `simulation`, `personal`" },
51678
+ "list-templates": { type: "boolean", description: "List available templates and exit", default: false }
51679
+ },
51680
+ run({ args }) {
51681
+ runCreate(args);
51682
+ }
51683
+ }),
51458
51684
  install,
51459
51685
  uninstall: defineCommand({
51460
- meta: { name: "uninstall", description: "Remove MCP server and hooks" },
51686
+ meta: { name: "uninstall", description: "Remove MCP server and hooks from Claude Code config" },
51461
51687
  run() {
51462
51688
  runUninstall();
51463
51689
  }
51464
51690
  }),
51465
51691
  join: join7,
51466
51692
  list: defineCommand({
51467
- meta: { name: "list", description: "Show joined meshes and identities" },
51693
+ meta: { name: "list", description: "Show joined meshes, slugs, and local identities" },
51468
51694
  run() {
51469
51695
  runList();
51470
51696
  }
51471
51697
  }),
51472
51698
  leave,
51473
51699
  peers: defineCommand({
51474
- meta: { name: "peers", description: "List connected peers in the mesh" },
51700
+ meta: { name: "peers", description: "List online peers with status, summary, and groups" },
51475
51701
  args: {
51476
51702
  mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51477
51703
  json: { type: "boolean", description: "Output as JSON", default: false }
@@ -51481,34 +51707,34 @@ var main = defineCommand({
51481
51707
  }
51482
51708
  }),
51483
51709
  send: defineCommand({
51484
- meta: { name: "send", description: "Send a message to a peer, group, or broadcast" },
51710
+ meta: { name: "send", description: "Send a message to a peer, group, or all peers" },
51485
51711
  args: {
51486
- to: { type: "positional", description: "Recipient: display name, @group, pubkey, or *", required: true },
51712
+ to: { type: "positional", description: "Recipient: display name, `@group`, `*` (broadcast), or pubkey hex", required: true },
51487
51713
  message: { type: "positional", description: "Message text", required: true },
51488
51714
  mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51489
- priority: { type: "string", description: "now | next (default) | low" }
51715
+ priority: { type: "string", description: '`"now"` | `"next"` (default) | `"low"`' }
51490
51716
  },
51491
51717
  async run({ args }) {
51492
51718
  await runSend(args, args.to, args.message);
51493
51719
  }
51494
51720
  }),
51495
51721
  inbox: defineCommand({
51496
- meta: { name: "inbox", description: "Read pending peer messages" },
51722
+ meta: { name: "inbox", description: "Drain pending inbound messages" },
51497
51723
  args: {
51498
51724
  mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51499
51725
  json: { type: "boolean", description: "Output as JSON", default: false },
51500
- wait: { type: "string", description: "Seconds to wait for broker delivery (default: 1)" }
51726
+ wait: { type: "string", description: "Seconds to wait for broker delivery (default: `1`)" }
51501
51727
  },
51502
51728
  async run({ args }) {
51503
51729
  await runInbox({ ...args, wait: args.wait ? parseInt(args.wait, 10) : undefined });
51504
51730
  }
51505
51731
  }),
51506
51732
  state: defineCommand({
51507
- meta: { name: "state", description: "Read or write shared mesh state" },
51733
+ meta: { name: "state", description: "Get, set, or list shared key-value state in the mesh" },
51508
51734
  args: {
51509
- action: { type: "positional", description: "get | set | list", required: true },
51510
- key: { type: "positional", description: "State key (required for get/set)" },
51511
- value: { type: "positional", description: "Value to set (required for set)" },
51735
+ action: { type: "positional", description: "`get <key>` | `set <key> <value>` | `list`", required: true },
51736
+ key: { type: "positional", description: "State key (required for `get` and `set`)" },
51737
+ value: { type: "positional", description: "Value to store (required for `set`)" },
51512
51738
  mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51513
51739
  json: { type: "boolean", description: "Output as JSON", default: false }
51514
51740
  },
@@ -51544,11 +51770,11 @@ var main = defineCommand({
51544
51770
  }
51545
51771
  }),
51546
51772
  remember: defineCommand({
51547
- meta: { name: "remember", description: "Store a memory in the mesh (accessible to all peers)" },
51773
+ meta: { name: "remember", description: "Store a persistent memory visible to all peers" },
51548
51774
  args: {
51549
- content: { type: "positional", description: "Text to remember", required: true },
51775
+ content: { type: "positional", description: "Text to store", required: true },
51550
51776
  mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51551
- tags: { type: "string", description: "Comma-separated tags (e.g. task,context)" },
51777
+ tags: { type: "string", description: "Comma-separated tags, e.g. `task,context`" },
51552
51778
  json: { type: "boolean", description: "Output as JSON", default: false }
51553
51779
  },
51554
51780
  async run({ args }) {
@@ -51556,9 +51782,9 @@ var main = defineCommand({
51556
51782
  }
51557
51783
  }),
51558
51784
  recall: defineCommand({
51559
- meta: { name: "recall", description: "Search mesh memory by keyword or phrase" },
51785
+ meta: { name: "recall", description: "Search mesh memories by keyword or phrase" },
51560
51786
  args: {
51561
- query: { type: "positional", description: "Search query", required: true },
51787
+ query: { type: "positional", description: "Full-text search query", required: true },
51562
51788
  mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51563
51789
  json: { type: "boolean", description: "Output as JSON", default: false }
51564
51790
  },
@@ -51567,13 +51793,14 @@ var main = defineCommand({
51567
51793
  }
51568
51794
  }),
51569
51795
  remind: defineCommand({
51570
- meta: { name: "remind", description: "Schedule a reminder or delayed message via the broker" },
51796
+ meta: { name: "remind", description: "Schedule a delayed message. Also: `remind list`, `remind cancel <id>`" },
51571
51797
  args: {
51572
- message: { type: "positional", description: "Message text, or: list | cancel <id>", required: false },
51573
- extra: { type: "positional", description: "Additional positional args", required: false },
51574
- in: { type: "string", description: 'Deliver after duration: "2h", "30m", "90s"' },
51575
- at: { type: "string", description: 'Deliver at time: "15:00" or ISO timestamp' },
51576
- to: { type: "string", description: "Recipient (default: self). Name, @group, pubkey, or *" },
51798
+ message: { type: "positional", description: "Message text or `list` / `cancel <id>` to manage reminders", required: false },
51799
+ extra: { type: "positional", description: "Reminder ID for `cancel`", required: false },
51800
+ in: { type: "string", description: 'Deliver after duration: `"2h"`, `"30m"`, `"90s"`' },
51801
+ at: { type: "string", description: 'Deliver at time: `"15:00"` or ISO timestamp' },
51802
+ cron: { type: "string", description: 'Recurring cron expression: `"0 */2 * * *"` (every 2h), `"30 9 * * 1-5"` (9:30 weekdays)' },
51803
+ to: { type: "string", description: "Recipient (default: self). Name, `@group`, `*`, or pubkey" },
51577
51804
  mesh: { type: "string", description: "Mesh slug (auto-selected if only one joined)" },
51578
51805
  json: { type: "boolean", description: "Output as JSON", default: false }
51579
51806
  },
@@ -51583,31 +51810,31 @@ var main = defineCommand({
51583
51810
  }
51584
51811
  }),
51585
51812
  status: defineCommand({
51586
- meta: { name: "status", description: "Check broker reachability for each joined mesh" },
51813
+ meta: { name: "status", description: "Check broker connectivity for each joined mesh" },
51587
51814
  async run() {
51588
51815
  await runStatus();
51589
51816
  }
51590
51817
  }),
51591
51818
  doctor: defineCommand({
51592
- meta: { name: "doctor", description: "Diagnose install, config, keypairs, and PATH" },
51819
+ meta: { name: "doctor", description: "Diagnose install, config, keypairs, and PATH issues" },
51593
51820
  async run() {
51594
51821
  await runDoctor();
51595
51822
  }
51596
51823
  }),
51597
51824
  mcp: defineCommand({
51598
- meta: { name: "mcp", description: "Start MCP server (stdio invoked by Claude Code, not users)" },
51825
+ meta: { name: "mcp", description: "Start MCP server on stdio (called by Claude Code, not users)" },
51599
51826
  async run() {
51600
51827
  await startMcpServer();
51601
51828
  }
51602
51829
  }),
51603
51830
  "seed-test-mesh": defineCommand({
51604
- meta: { name: "seed-test-mesh", description: "Dev only: inject a mesh into config (skips invite flow)" },
51831
+ meta: { name: "seed-test-mesh", description: "Dev: inject a mesh into local config, skip invite flow" },
51605
51832
  run({ rawArgs }) {
51606
51833
  runSeedTestMesh(rawArgs);
51607
51834
  }
51608
51835
  }),
51609
51836
  hook: defineCommand({
51610
- meta: { name: "hook", description: "Internal hook handler (invoked by Claude Code hooks)" },
51837
+ meta: { name: "hook", description: "Internal: handle Claude Code hook events" },
51611
51838
  async run({ rawArgs }) {
51612
51839
  await runHook(rawArgs);
51613
51840
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudemesh-cli",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "Claude Code MCP client for claudemesh — peer mesh messaging between Claude sessions.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -30,17 +30,6 @@
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  },
33
- "scripts": {
34
- "build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",
35
- "clean": "git clean -xdf .cache .turbo dist node_modules",
36
- "dev": "bun --hot src/index.ts",
37
- "start": "bun src/index.ts",
38
- "format": "prettier --check . --ignore-path ../../.gitignore",
39
- "lint": "eslint",
40
- "prepublishOnly": "bun run build",
41
- "test": "vitest run",
42
- "typecheck": "tsc --noEmit"
43
- },
44
33
  "prettier": "@turbostarter/prettier-config",
45
34
  "engines": {
46
35
  "node": ">=20"
@@ -53,15 +42,25 @@
53
42
  "zod": "4.1.13"
54
43
  },
55
44
  "devDependencies": {
56
- "@turbostarter/eslint-config": "workspace:*",
57
- "@turbostarter/prettier-config": "workspace:*",
58
- "@turbostarter/tsconfig": "workspace:*",
59
- "@turbostarter/vitest-config": "workspace:*",
60
45
  "@types/libsodium-wrappers": "0.7.14",
61
46
  "@types/ws": "8.5.13",
62
- "eslint": "catalog:",
63
- "prettier": "catalog:",
64
- "typescript": "catalog:",
65
- "vitest": "catalog:"
47
+ "eslint": "9.39.0",
48
+ "prettier": "3.6.2",
49
+ "typescript": "5.9.3",
50
+ "vitest": "4.0.14",
51
+ "@turbostarter/eslint-config": "0.1.0",
52
+ "@turbostarter/prettier-config": "0.1.0",
53
+ "@turbostarter/tsconfig": "0.1.0",
54
+ "@turbostarter/vitest-config": "0.1.0"
55
+ },
56
+ "scripts": {
57
+ "build": "bun build src/index.ts --target=node --outfile dist/index.js --banner \"#!/usr/bin/env node\" && chmod +x dist/index.js",
58
+ "clean": "git clean -xdf .cache .turbo dist node_modules",
59
+ "dev": "bun --hot src/index.ts",
60
+ "start": "bun src/index.ts",
61
+ "format": "prettier --check . --ignore-path ../../.gitignore",
62
+ "lint": "eslint",
63
+ "test": "vitest run",
64
+ "typecheck": "tsc --noEmit"
66
65
  }
67
- }
66
+ }