tide-commander 1.37.0 → 1.37.2

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 (48) hide show
  1. package/dist/assets/{BossLogsModal-BStUTJH0.js → BossLogsModal-C9c6b6JM.js} +1 -1
  2. package/dist/assets/{BossSpawnModal-BD9dP_bi.js → BossSpawnModal-dSXYldID.js} +1 -1
  3. package/dist/assets/{ControlsModal-D0z4kDXm.js → ControlsModal-C_W0XTlE.js} +1 -1
  4. package/dist/assets/{DockerLogsModal-Cic5gzhr.js → DockerLogsModal-CUweGxhz.js} +1 -1
  5. package/dist/assets/{EmbeddedEditor-DyhwTp2-.js → EmbeddedEditor-DU_lcIBD.js} +1 -1
  6. package/dist/assets/{GmailOAuthSetup-BkoZ3mMf.js → GmailOAuthSetup-DTAkTnzL.js} +1 -1
  7. package/dist/assets/{GoogleOAuthSetup-BS95rc2s.js → GoogleOAuthSetup-B83vPcAW.js} +1 -1
  8. package/dist/assets/{IframeModal-CZhllcDM.js → IframeModal-3rIUuKKu.js} +1 -1
  9. package/dist/assets/{IntegrationsPanel-D-7XWz0N.js → IntegrationsPanel-CJviFK3q.js} +2 -2
  10. package/dist/assets/{LogViewerModal-CUnmonYV.js → LogViewerModal-C9qFaC8V.js} +1 -1
  11. package/dist/assets/{MonitoringModal-CF7SKx1_.js → MonitoringModal-DRZrxL7t.js} +1 -1
  12. package/dist/assets/{PM2LogsModal-Chjt5CBh.js → PM2LogsModal-BLzKH4ql.js} +1 -1
  13. package/dist/assets/{RestoreArchivedAreaModal-D7uKvdQX.js → RestoreArchivedAreaModal-DHH8QKkx.js} +1 -1
  14. package/dist/assets/{SaveSnapshotModal-Dl2kQHjX.js → SaveSnapshotModal-BTvvgNqk.js} +1 -1
  15. package/dist/assets/{Scene2DCanvas-Dxc-W5Ez.js → Scene2DCanvas-Gu0Pplgl.js} +1 -1
  16. package/dist/assets/{SceneManager-C-0M1QjA.js → SceneManager-BD0UwiqS.js} +1 -1
  17. package/dist/assets/{SkillsPanel-Bn7OWkDt.js → SkillsPanel-Btfm8DLY.js} +1 -1
  18. package/dist/assets/{SnapshotManager-B3yvepjG.js → SnapshotManager-BgCuvtUx.js} +1 -1
  19. package/dist/assets/{SpawnModal-6ZRTBZl_.js → SpawnModal-ByhvPfN7.js} +1 -1
  20. package/dist/assets/{SubordinateAssignmentModal-BCpmlsht.js → SubordinateAssignmentModal-5Re0nGK9.js} +1 -1
  21. package/dist/assets/{SupervisorPanel-DEMnY_NN.js → SupervisorPanel-DtNzjH1r.js} +1 -1
  22. package/dist/assets/{TriggerManagerPanel-BLkhXpZ7.js → TriggerManagerPanel-DUXzcjBS.js} +1 -1
  23. package/dist/assets/{WorkflowEditorPanel-Bhgbd7ue.js → WorkflowEditorPanel-CCauefUO.js} +1 -1
  24. package/dist/assets/{index-4TQCW-iQ.js → index-BG-fI4o6.js} +1 -1
  25. package/dist/assets/{index-Ju1wytuU.js → index-BakRazz4.js} +1 -1
  26. package/dist/assets/{index-Cj1pOXXE.js → index-Bl5TthnX.js} +3 -3
  27. package/dist/assets/{index-DBiqKSeA.js → index-Bma1pZ6y.js} +2 -2
  28. package/dist/assets/{index-C6YLfMEv.js → index-CPBP46l5.js} +1 -1
  29. package/dist/assets/{index-D7oUKfnS.js → index-DTO0LlmC.js} +1 -1
  30. package/dist/assets/{index-BeCd-RaV.js → index-DfBVuA_K.js} +1 -1
  31. package/dist/assets/{index-CkeDulVE.js → index-DtDea-zV.js} +1 -1
  32. package/dist/assets/{main-CYbmXUen.js → main-lr_lRuBZ.js} +13 -13
  33. package/dist/assets/{web-5JUKV_Qr.js → web-CV2dsnZj.js} +1 -1
  34. package/dist/assets/{web-B9TtN1N1.js → web-DOowQZI5.js} +1 -1
  35. package/dist/index.html +1 -1
  36. package/dist/locales/de/config.json +2 -0
  37. package/dist/locales/en/config.json +2 -0
  38. package/dist/locales/es/config.json +2 -0
  39. package/dist/locales/fr/config.json +2 -0
  40. package/dist/locales/hi/config.json +2 -0
  41. package/dist/locales/it/config.json +2 -0
  42. package/dist/locales/ja/config.json +2 -0
  43. package/dist/locales/pt/config.json +2 -0
  44. package/dist/locales/ru/config.json +2 -0
  45. package/dist/locales/zh-CN/config.json +2 -0
  46. package/dist/src/packages/server/data/builtin-skills/create-building.js +91 -1
  47. package/dist/src/packages/server/data/index.js +24 -1
  48. package/package.json +1 -1
@@ -1 +1 @@
1
- import{bw as a}from"./main-CYbmXUen.js";import{ImpactStyle as i,NotificationType as r}from"./index-DBiqKSeA.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.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{bw as a}from"./main-lr_lRuBZ.js";import{ImpactStyle as i,NotificationType as r}from"./index-Bma1pZ6y.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.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{bw as s}from"./main-CYbmXUen.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.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{bw as s}from"./main-lr_lRuBZ.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.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,7 +22,7 @@
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-CYbmXUen.js"></script>
25
+ <script type="module" crossorigin src="/assets/main-lr_lRuBZ.js"></script>
26
26
  <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
27
27
  <link rel="modulepreload" crossorigin href="/assets/vendor-react--Eh9ivFN.js">
28
28
  <link rel="modulepreload" crossorigin href="/assets/vendor-three-Chj50gSY.js">
@@ -29,6 +29,8 @@
29
29
  "stt": "Sprache-zu-Text",
30
30
  "externalEditor": "Externer Editor",
31
31
  "externalEditorPlaceholder": "z.B. subl, code, nvim (leer lassen für Systemstandard)",
32
+ "tabTitle": "Tab-Titel",
33
+ "tabTitlePlaceholder": "Benutzerdefinierter Browser-Tab-Titel (Standard: Tide Commander)",
32
34
  "language": "Sprache",
33
35
  "compactView": "Kompakte Ansicht",
34
36
  "autoScroll": "Automatisches Scrollen",
@@ -29,6 +29,8 @@
29
29
  "stt": "Speech-to-Text",
30
30
  "externalEditor": "External Editor",
31
31
  "externalEditorPlaceholder": "e.g., subl, code, nvim (leave empty for system default)",
32
+ "tabTitle": "Tab Title",
33
+ "tabTitlePlaceholder": "Custom browser tab title (default: Tide Commander)",
32
34
  "language": "Language",
33
35
  "compactView": "Compact View",
34
36
  "autoScroll": "Auto Scroll",
@@ -29,6 +29,8 @@
29
29
  "stt": "Voz a texto",
30
30
  "externalEditor": "Editor externo",
31
31
  "externalEditorPlaceholder": "ej., subl, code, nvim (dejar vacio para el predeterminado)",
32
+ "tabTitle": "Título de pestaña",
33
+ "tabTitlePlaceholder": "Título personalizado de la pestaña (predeterminado: Tide Commander)",
32
34
  "language": "Idioma",
33
35
  "compactView": "Vista compacta",
34
36
  "autoScroll": "Desplazamiento automatico",
@@ -29,6 +29,8 @@
29
29
  "stt": "Reconnaissance vocale",
30
30
  "externalEditor": "Éditeur externe",
31
31
  "externalEditorPlaceholder": "ex. : subl, code, nvim (laisser vide pour le défaut système)",
32
+ "tabTitle": "Titre de l'onglet",
33
+ "tabTitlePlaceholder": "Titre personnalisé de l'onglet (par défaut : Tide Commander)",
32
34
  "language": "Langue",
33
35
  "compactView": "Vue compacte",
34
36
  "autoScroll": "Défilement automatique",
@@ -29,6 +29,8 @@
29
29
  "stt": "स्पीच-टू-टेक्स्ट",
30
30
  "externalEditor": "बाहरी संपादक",
31
31
  "externalEditorPlaceholder": "जैसे, subl, code, nvim (सिस्टम डिफ़ॉल्ट के लिए खाली छोड़ें)",
32
+ "tabTitle": "टैब शीर्षक",
33
+ "tabTitlePlaceholder": "कस्टम ब्राउज़र टैब शीर्षक (डिफ़ॉल्ट: Tide Commander)",
32
34
  "language": "भाषा",
33
35
  "compactView": "कॉम्पैक्ट दृश्य",
34
36
  "autoScroll": "ऑटो स्क्रॉल",
@@ -29,6 +29,8 @@
29
29
  "stt": "Riconoscimento vocale",
30
30
  "externalEditor": "Editor esterno",
31
31
  "externalEditorPlaceholder": "es. subl, code, nvim (lascia vuoto per il predefinito di sistema)",
32
+ "tabTitle": "Titolo scheda",
33
+ "tabTitlePlaceholder": "Titolo personalizzato della scheda del browser (predefinito: Tide Commander)",
32
34
  "language": "Lingua",
33
35
  "compactView": "Vista compatta",
34
36
  "autoScroll": "Scorrimento automatico",
@@ -29,6 +29,8 @@
29
29
  "stt": "音声認識",
30
30
  "externalEditor": "外部エディタ",
31
31
  "externalEditorPlaceholder": "例: subl, code, nvim (空欄でシステムデフォルト)",
32
+ "tabTitle": "タブタイトル",
33
+ "tabTitlePlaceholder": "カスタムブラウザタブタイトル(デフォルト: Tide Commander)",
32
34
  "language": "言語",
33
35
  "compactView": "コンパクト表示",
34
36
  "autoScroll": "自動スクロール",
@@ -29,6 +29,8 @@
29
29
  "stt": "Fala para Texto",
30
30
  "externalEditor": "Editor Externo",
31
31
  "externalEditorPlaceholder": "ex., subl, code, nvim (deixe vazio para o padrao do sistema)",
32
+ "tabTitle": "Título da aba",
33
+ "tabTitlePlaceholder": "Título personalizado da aba do navegador (padrão: Tide Commander)",
32
34
  "language": "Idioma",
33
35
  "compactView": "Visualizacao Compacta",
34
36
  "autoScroll": "Rolagem Automatica",
@@ -29,6 +29,8 @@
29
29
  "stt": "Распознавание речи",
30
30
  "externalEditor": "Внешний редактор",
31
31
  "externalEditorPlaceholder": "напр., subl, code, nvim (оставьте пустым для системного по умолчанию)",
32
+ "tabTitle": "Заголовок вкладки",
33
+ "tabTitlePlaceholder": "Пользовательский заголовок вкладки браузера (по умолчанию: Tide Commander)",
32
34
  "language": "Язык",
33
35
  "compactView": "Компактный вид",
34
36
  "autoScroll": "Автопрокрутка",
@@ -29,6 +29,8 @@
29
29
  "stt": "语音转文字",
30
30
  "externalEditor": "外部编辑器",
31
31
  "externalEditorPlaceholder": "例如:subl, code, nvim(留空使用系统默认)",
32
+ "tabTitle": "标签页标题",
33
+ "tabTitlePlaceholder": "自定义浏览器标签页标题(默认:Tide Commander)",
32
34
  "language": "语言",
33
35
  "compactView": "紧凑视图",
34
36
  "autoScroll": "自动滚动",
@@ -3,7 +3,7 @@ const BT3 = '```';
3
3
  export const createBuilding = {
4
4
  slug: 'create-building',
5
5
  name: 'Create Building',
6
- description: 'Create and manage buildings in Tide Commander with full control over configuration and placement',
6
+ description: 'Create and manage buildings and areas in Tide Commander with full control over configuration and placement',
7
7
  allowedTools: ['Bash(jq:*)', 'Bash(curl:*)', 'Bash(cat:*)'],
8
8
  content: `# Create Building Skill
9
9
 
@@ -575,5 +575,95 @@ ${BT3}
575
575
  - Refresh Tide Commander UI to see new buildings
576
576
  - Check "pm2 list" to verify building started correctly
577
577
  - Position buildings inside their designated area (check areas.json for coordinates)
578
+
579
+ ---
580
+
581
+ ## Area Management
582
+
583
+ Areas are project zones on the battlefield that group agents and buildings. File: ~/.local/share/tide-commander/areas.json
584
+
585
+ ### Area Schema (DrawingArea)
586
+
587
+ **CRITICAL: Follow this schema exactly. Incorrect fields will crash the application.**
588
+
589
+ ${BT3}typescript
590
+ {
591
+ "id": string, // Unique ID (e.g., "my-project-area")
592
+ "name": string, // Display name
593
+ "type": "rectangle" | "circle", // REQUIRED - shape type
594
+ "center": { "x": number, "z": number }, // REQUIRED - center position (NOT flat x/z)
595
+ "width": number, // Rectangle only - width in world units
596
+ "height": number, // Rectangle only - height in world units (NOT "depth")
597
+ "radius": number, // Circle only - radius in world units
598
+ "color": string, // Hex color (e.g., "#4a90d9")
599
+ "zIndex": number, // Stacking order (0 = bottom, higher = on top)
600
+ "assignedAgentIds": string[], // Agent IDs assigned to this area
601
+ "directories": string[], // Associated directory paths
602
+ "prompt": string // Optional - area-level system prompt for assigned agents
603
+ }
604
+ ${BT3}
605
+
606
+ **Common mistakes to avoid:**
607
+ - Do NOT use flat ${BT}"x"${BT} and ${BT}"z"${BT} at root level — use ${BT}"center": {"x": ..., "z": ...}${BT}
608
+ - Do NOT use ${BT}"depth"${BT} — use ${BT}"height"${BT}
609
+ - Do NOT omit ${BT}"type"${BT} — always specify ${BT}"rectangle"${BT} or ${BT}"circle"${BT}
610
+
611
+ ### Create a Rectangle Area
612
+
613
+ ${BT3}bash
614
+ jq '.areas += [{
615
+ "id": "my-project-area",
616
+ "name": "My Project",
617
+ "type": "rectangle",
618
+ "center": {"x": 0, "z": 0},
619
+ "width": 12,
620
+ "height": 10,
621
+ "color": "#4a90d9",
622
+ "zIndex": 0,
623
+ "assignedAgentIds": [],
624
+ "directories": []
625
+ }]' ~/.local/share/tide-commander/areas.json > /tmp/a.json && mv /tmp/a.json ~/.local/share/tide-commander/areas.json
626
+ ${BT3}
627
+
628
+ ### Create a Circle Area
629
+
630
+ ${BT3}bash
631
+ jq '.areas += [{
632
+ "id": "ops-area",
633
+ "name": "Operations",
634
+ "type": "circle",
635
+ "center": {"x": 10, "z": -5},
636
+ "radius": 6,
637
+ "color": "#d94a4a",
638
+ "zIndex": 0,
639
+ "assignedAgentIds": [],
640
+ "directories": []
641
+ }]' ~/.local/share/tide-commander/areas.json > /tmp/a.json && mv /tmp/a.json ~/.local/share/tide-commander/areas.json
642
+ ${BT3}
643
+
644
+ ### List Existing Areas
645
+
646
+ ${BT3}bash
647
+ jq '.areas | map({id, name, type, center, width, height, radius})' ~/.local/share/tide-commander/areas.json
648
+ ${BT3}
649
+
650
+ ### Assign Agents to an Area
651
+
652
+ ${BT3}bash
653
+ jq '(.areas[] | select(.id == "my-project-area")).assignedAgentIds += ["agent-id-here"]' ~/.local/share/tide-commander/areas.json > /tmp/a.json && mv /tmp/a.json ~/.local/share/tide-commander/areas.json
654
+ ${BT3}
655
+
656
+ ### Add Area-Level Prompt
657
+
658
+ ${BT3}bash
659
+ jq '(.areas[] | select(.id == "my-project-area")).prompt = "Always run tests before committing"' ~/.local/share/tide-commander/areas.json > /tmp/a.json && mv /tmp/a.json ~/.local/share/tide-commander/areas.json
660
+ ${BT3}
661
+
662
+ ### Verify
663
+
664
+ ${BT3}bash
665
+ jq empty ~/.local/share/tide-commander/areas.json && echo "Valid JSON"
666
+ jq '.areas | length' ~/.local/share/tide-commander/areas.json
667
+ ${BT3}
578
668
  `,
579
669
  };
@@ -184,13 +184,36 @@ export async function saveAgentsAsync(agents) {
184
184
  log.error(' Failed to save agents (async):', err);
185
185
  }
186
186
  }
187
+ /**
188
+ * Validate that an area has the required fields to avoid crashes from malformed data.
189
+ */
190
+ function isValidArea(area) {
191
+ return (area &&
192
+ typeof area.id === 'string' &&
193
+ typeof area.center === 'object' &&
194
+ area.center !== null &&
195
+ typeof area.center.x === 'number' &&
196
+ typeof area.center.z === 'number' &&
197
+ (area.type === 'rectangle' || area.type === 'circle'));
198
+ }
187
199
  /**
188
200
  * Load drawing areas from disk
189
201
  */
190
202
  export function loadAreas() {
191
203
  ensureDataDir();
192
204
  const data = safeReadJsonSync(AREAS_FILE, 'Areas');
193
- return data?.areas || [];
205
+ const raw = data?.areas || [];
206
+ const valid = [];
207
+ for (const area of raw) {
208
+ if (isValidArea(area)) {
209
+ valid.push(area);
210
+ }
211
+ else {
212
+ const a = area;
213
+ log.error(` Skipping malformed area "${a?.id ?? a?.name ?? 'unknown'}": missing required fields (center, type)`);
214
+ }
215
+ }
216
+ return valid;
194
217
  }
195
218
  /**
196
219
  * Save drawing areas to disk
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tide-commander",
3
- "version": "1.37.0",
3
+ "version": "1.37.2",
4
4
  "description": "Visual multi-agent orchestrator and manager for Claude Code with 3D/2D interface",
5
5
  "repository": {
6
6
  "type": "git",