tide-commander 1.6.2 → 1.7.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.
@@ -1 +1 @@
1
- import{W as a}from"./main-D0GF8iqn.js";import{ImpactStyle as i,NotificationType as r}from"./index-8_UuIg_g.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react-uS-d4TUT.js";import"./vendor-three-DJ4p3FLF.js";class u extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{u as HapticsWeb};
1
+ import{W as a}from"./main-CWSD437V.js";import{ImpactStyle as i,NotificationType as r}from"./index-CPL6EJyt.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react-uS-d4TUT.js";import"./vendor-three-DJ4p3FLF.js";class u extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{u as HapticsWeb};
@@ -1 +1 @@
1
- import{W as s}from"./main-D0GF8iqn.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react-uS-d4TUT.js";import"./vendor-three-DJ4p3FLF.js";class f extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{f as LocalNotificationsWeb};
1
+ import{W as s}from"./main-CWSD437V.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react-uS-d4TUT.js";import"./vendor-three-DJ4p3FLF.js";class f extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{f as LocalNotificationsWeb};
package/dist/index.html CHANGED
@@ -22,11 +22,11 @@
22
22
  <link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
23
23
  <link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
24
24
  <title>Tide Commander</title>
25
- <script type="module" crossorigin src="/assets/main-D0GF8iqn.js"></script>
25
+ <script type="module" crossorigin src="/assets/main-CWSD437V.js"></script>
26
26
  <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
27
27
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-uS-d4TUT.js">
28
28
  <link rel="modulepreload" crossorigin href="/assets/vendor-three-DJ4p3FLF.js">
29
- <link rel="stylesheet" crossorigin href="/assets/main-BKW9YctF.css">
29
+ <link rel="stylesheet" crossorigin href="/assets/main-Bh6jO32I.css">
30
30
  </head>
31
31
  <body>
32
32
  <div id="app"></div>
@@ -125,24 +125,55 @@ If the user asks you to tell an agent something, **just delegate it**. Don't ove
125
125
  - If something is unclear, the assigned agent will ask or figure it out
126
126
  - Be confident - you're the boss
127
127
 
128
- ## 🔧 YOU HAVE TOOLS - USE THEM
128
+ ## 🔧 YOU HAVE TOOLS - BUT DELEGATE FIRST
129
129
 
130
- **Before asking the user, consider investigating yourself.** You have access to tools:
131
- - **Glob/Grep** - Search for files and patterns in the codebase
132
- - **Read** - Look at file contents to understand context
133
- - **Bash** - Run commands to explore the project
130
+ You have access to tools (Glob, Grep, Read, Bash), but **your default should be to DELEGATE, not to investigate yourself.**
134
131
 
135
- **If you're unsure about something:**
136
- 1. First, try to find the answer yourself using tools
137
- 2. Or delegate to a scout agent to investigate
138
- 3. Only ask the user if you truly can't figure it out
132
+ **Your tools are ONLY for:**
133
+ - Quick, immediate lookups that take <10 seconds (e.g., checking an agent's status endpoint)
134
+ - Reading a response or result that was returned to YOU directly
135
+ - Running API calls to manage your team (e.g., fetching agent details)
139
136
 
140
- **Example - ASKING USER (BAD):**
141
- > "What project is this for? Where does the auth module live?"
137
+ **Your tools are NOT for:**
138
+ - Exploring or investigating codebases **DELEGATE to a scout**
139
+ - Reading source files to understand implementation — **DELEGATE to a scout**
140
+ - Searching for patterns, files, or code — **DELEGATE to a scout**
141
+ - Running build/test commands — **DELEGATE to an agent**
142
+ - Any research that could take an agent to complete — **DELEGATE IT**
142
143
 
143
- **Example - INVESTIGATING (GOOD):**
144
- > [Uses Glob to find auth-related files, then delegates with context]
145
- > "I found the auth module at src/auth/. Delegating to Builder to add the new feature there."
144
+ **Rule of thumb:** If you're about to use Read, Grep, or Glob on source code, STOP and delegate instead.
145
+
146
+ **Example - USING TOOLS YOURSELF (BAD):**
147
+ > [Uses Grep to search for auth patterns, then Read to inspect 3 files, then Glob to find related modules...]
148
+ > "After investigating, I found the auth module at src/auth/..."
149
+
150
+ **Example - DELEGATING (GOOD):**
151
+ > "Delegating to Scout to explore the auth module and report back."
152
+
153
+ ---
154
+
155
+ ## ⚠️ DELEGATION OVER TOOL USE
156
+
157
+ **CRITICAL: Using Read/Grep/Glob yourself is doing work that should be delegated.** You are the coordinator, NOT the investigator.
158
+
159
+ ### What MUST be delegated (NEVER do yourself):
160
+ - "Check how the CEP endpoint is implemented" → **Delegate to scout** (don't Read/Grep yourself)
161
+ - "Find where the payment flow is defined" → **Delegate to scout** (don't Glob yourself)
162
+ - "Investigate why the build fails" → **Delegate to debugger** (don't Bash yourself)
163
+ - "Understand the database schema" → **Delegate to scout** (don't Read yourself)
164
+ - "Look at what changed in the last PR" → **Delegate to scout** (don't use git log yourself)
165
+ - Any task where you'd use 2+ tool calls to explore → **DELEGATE IT**
166
+
167
+ ### What's acceptable to do yourself:
168
+ - \`curl\` to check agent status via the Tide Commander API
169
+ - Reading a SHORT response/result that was sent directly to you
170
+ - Quick \`ls\` to verify a directory exists before delegating with correct paths
171
+ - Fetching agent history/details via \`/api/agents/\` endpoints
172
+
173
+ ### The principle:
174
+ > **You are the boss. Bosses don't dig through filing cabinets — they send someone to get the information.**
175
+ > If a task involves exploring, researching, reading code, or investigating, that's your team's job.
176
+ > Your job is to DECIDE and DELEGATE, not to DO.
146
177
 
147
178
  ---
148
179
 
@@ -435,6 +466,31 @@ User: "Analyze the frontend, create a parallelizable plan, and assign tasks"
435
466
 
436
467
  ---
437
468
 
469
+ ## ⚠️ PARALLELIZATION CAUTION
470
+
471
+ **Only parallelize when you are 100% certain:**
472
+ 1. Tasks are truly independent with NO shared context or dependencies
473
+ 2. Each task has ALL required information/context to complete independently
474
+ 3. One task's output won't be needed as input for another task
475
+ 4. Both agents can work simultaneously without blocking each other
476
+ 5. You've verified both agents have the capabilities needed
477
+
478
+ **If unsure, use SEQUENTIAL delegation instead.** It's better to have one agent complete work, then pass context to the next agent, than to have two agents working in parallel and later discovering they needed each other's results.
479
+
480
+ **Default to SINGLE AGENT** for most work. Parallelization should be rare and intentional, not the default.
481
+
482
+ **Example - DON'T PARALLELIZE (BAD):**
483
+ - Task A: "Refactor the auth module"
484
+ - Task B: "Update the login form to match new auth API"
485
+ → Task B depends on Task A being done first. Sequential only.
486
+
487
+ **Example - DO PARALLELIZE (GOOD):**
488
+ - Task A: "Add button styling to _buttons.scss"
489
+ - Task B: "Add input styling to _inputs.scss"
490
+ → Independent files, no dependencies. Can parallelize.
491
+
492
+ ---
493
+
438
494
  ## SPAWNING NEW AGENTS:
439
495
  You can ONLY spawn new agents when the user EXPLICITLY requests it.
440
496
 
@@ -85,6 +85,54 @@ function buildEstimatedContextStats(totalTokens, contextWindow, model) {
85
85
  lastUpdated: Date.now(),
86
86
  };
87
87
  }
88
+ /**
89
+ * Update an existing contextStats with new totalTokens, keeping authoritative
90
+ * category breakdowns (from /context) if they exist, but always refreshing
91
+ * the top-level totalTokens/usedPercent/freeSpace so the value never goes stale.
92
+ */
93
+ function updateContextStatsTokens(existing, totalTokens, contextWindow) {
94
+ const safeWindow = contextWindow && contextWindow > 0 ? contextWindow : existing.contextWindow;
95
+ const usedPercent = Math.min(100, Math.max(0, Math.round((totalTokens / safeWindow) * 100)));
96
+ const freeTokens = Math.max(0, safeWindow - totalTokens);
97
+ const freePercent = Number(((freeTokens / safeWindow) * 100).toFixed(1));
98
+ const cats = existing.categories;
99
+ const hasAuthoritativeBreakdown = (cats.systemPrompt?.tokens || 0) > 0 || (cats.systemTools?.tokens || 0) > 0;
100
+ let updatedCategories;
101
+ if (hasAuthoritativeBreakdown) {
102
+ // Authoritative breakdown from /context — keep systemPrompt/systemTools/autocompactBuffer,
103
+ // adjust messages to account for the difference
104
+ const fixedTokens = (cats.systemPrompt?.tokens || 0) + (cats.systemTools?.tokens || 0)
105
+ + (cats.autocompactBuffer?.tokens || 0);
106
+ const messagesTokens = Math.max(0, totalTokens - fixedTokens);
107
+ const messagesPercent = Number(((messagesTokens / safeWindow) * 100).toFixed(1));
108
+ updatedCategories = {
109
+ systemPrompt: cats.systemPrompt,
110
+ systemTools: cats.systemTools,
111
+ messages: { tokens: messagesTokens, percent: messagesPercent },
112
+ freeSpace: { tokens: freeTokens, percent: freePercent },
113
+ autocompactBuffer: cats.autocompactBuffer,
114
+ };
115
+ }
116
+ else {
117
+ // Estimated mode — all tokens attributed to messages
118
+ const messagesPercent = Number(((totalTokens / safeWindow) * 100).toFixed(1));
119
+ updatedCategories = {
120
+ systemPrompt: { tokens: 0, percent: 0 },
121
+ systemTools: { tokens: 0, percent: 0 },
122
+ messages: { tokens: totalTokens, percent: messagesPercent },
123
+ freeSpace: { tokens: freeTokens, percent: freePercent },
124
+ autocompactBuffer: { tokens: 0, percent: 0 },
125
+ };
126
+ }
127
+ return {
128
+ ...existing,
129
+ contextWindow: safeWindow,
130
+ totalTokens,
131
+ usedPercent,
132
+ categories: updatedCategories,
133
+ lastUpdated: Date.now(),
134
+ };
135
+ }
88
136
  export function createRuntimeEventHandlers(deps) {
89
137
  const { log, emitEvent, emitOutput, emitComplete, emitError, parseUsageOutput, executeCommand, } = deps;
90
138
  function handleEvent(agentId, event) {
@@ -148,9 +196,13 @@ export function createRuntimeEventHandlers(deps) {
148
196
  const updates = {
149
197
  contextUsed: safeContextUsed,
150
198
  };
151
- // Build estimated contextStats so the frontend shows a real bar
152
- // instead of "Not retrieved yet" skip if authoritative /context data exists.
153
- if (!agent.contextStats || !agent.contextStats.lastUpdated) {
199
+ // Always keep contextStats.totalTokens in sync with contextUsed.
200
+ // If authoritative stats exist (from /context), merge the new total
201
+ // while preserving category breakdowns. Otherwise build estimated stats.
202
+ if (agent.contextStats && agent.contextStats.lastUpdated) {
203
+ updates.contextStats = updateContextStatsTokens(agent.contextStats, safeContextUsed, effectiveLimit);
204
+ }
205
+ else {
154
206
  updates.contextStats = buildEstimatedContextStats(safeContextUsed, effectiveLimit, agent.model || 'claude');
155
207
  }
156
208
  agentService.updateAgent(agentId, updates, false);
@@ -259,11 +311,13 @@ export function createRuntimeEventHandlers(deps) {
259
311
  contextUsed,
260
312
  contextLimit,
261
313
  };
262
- // Build estimated contextStats for all providers so the frontend always
263
- // has hasData=true. For Claude agents, skip if authoritative stats from
264
- // a /context command already exist (they have category breakdowns).
265
- const hasAuthoritativeStats = isClaudeProvider && agent.contextStats && agent.contextStats.lastUpdated > 0;
266
- if (!hasAuthoritativeStats) {
314
+ // Always keep contextStats in sync. If authoritative stats exist (from
315
+ // /context), merge the updated totalTokens while preserving category
316
+ // breakdowns. Otherwise build fresh estimated stats.
317
+ if (agent.contextStats && agent.contextStats.lastUpdated) {
318
+ updates.contextStats = updateContextStatsTokens(agent.contextStats, Math.max(0, Math.round(contextUsed)), Math.max(1, Math.round(contextLimit)));
319
+ }
320
+ else {
267
321
  updates.contextStats = buildEstimatedContextStats(Math.max(0, Math.round(contextUsed)), Math.max(1, Math.round(contextLimit)), isClaudeProvider ? (agent.model || 'claude') : (agent.codexModel || agent.model));
268
322
  }
269
323
  agentService.updateAgent(agentId, updates);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tide-commander",
3
- "version": "1.6.2",
3
+ "version": "1.7.1",
4
4
  "description": "Visual multi-agent orchestrator and manager for Claude Code with 3D/2D interface",
5
5
  "repository": {
6
6
  "type": "git",