toga-ai 1.0.35 → 1.0.37

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.
@@ -3,3 +3,4 @@
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
5
  | [Library (1.0 Framework) Architecture](architecture.md) | `library` is the shared library repository for **all 1.0 (legacy) applications** — the `App_` framework. | library/_.php, library/app/, library/browser/ |
6
+ | [Elite Freshservice Sync (library)](features/elite-freshservice-sync.md) | `App_Api_Toga2` in `library/app/api/toga2.php` orchestrates bidirectional sync between TOGA 2 and TOGaDesk. | library/app/api/toga2.php |
@@ -0,0 +1,166 @@
1
+ ---
2
+ title: Elite Freshservice Sync (library)
3
+ framework: "1.0"
4
+ repo: library
5
+ project: Library
6
+ client: elite
7
+ type: client-feature
8
+ status: active
9
+ updated: 2026-06-10
10
+ owners: []
11
+ files:
12
+ - library/app/api/toga2.php
13
+ related:
14
+ - clients/elite/profile.md
15
+ - 2.0/apps/worker2/features/elite-freshservice-sync.md
16
+ ---
17
+
18
+ ## Summary
19
+
20
+ `App_Api_Toga2` in `library/app/api/toga2.php` orchestrates bidirectional sync between
21
+ TOGA 2 and TOGaDesk. The top-level entry point is `syncWithTogadesk()`. Three private
22
+ helper methods handle file-attachment sync within that orchestration.
23
+
24
+ ## Top-level entry point — `syncWithTogadesk()`
25
+
26
+ 14-parameter static method. Called from a cron script for each client.
27
+
28
+ ```php
29
+ App_Api_Toga2::syncWithTogadesk(
30
+ int $togadeskClientId,
31
+ int $togadeskTicketDepartmentId,
32
+ string $togaClientUuid,
33
+ string $togaApiKey,
34
+ string $togaApiSecret,
35
+ string $integrationType, // 'REPAIR_ORDERS' or 'TICKETS'
36
+ bool $isEnabledToga2ToTogadeskIntegration,
37
+ bool $isEnabledTogadeskToToga2Integration,
38
+ bool $isEnabledToga2ContactsToTogadeskPeopleIntegration,
39
+ bool $isEnabledToga2ItemsUnitsToTogadeskAssetsIntegration,
40
+ bool $isEnabledToga2PredefinedRepliesToTogadeskTicketsPrIntegration,
41
+ bool $isEnabledToga2TicketTeamsToTogadeskGroupsIntegration,
42
+ bool $isEnabledToga2TicketCategoriesToTogadeskCategorySubcategoryItemsIntegration,
43
+ array $monitorTogadeskDepartmentIds = []
44
+ );
45
+ ```
46
+
47
+ Constants for `$integrationType`:
48
+ ```php
49
+ App_Api_Toga2::SYNC_WITH_TOGADESK_INTEGRATION_TYPE__REPAIR_ORDERS // = 'REPAIR_ORDERS'
50
+ App_Api_Toga2::SYNC_WITH_TOGADESK_INTEGRATION_TYPE__TICKETS // = 'TICKETS'
51
+ ```
52
+
53
+ ## Watermark timestamps
54
+
55
+ The sync uses two watermarks stored as TOGA 2 `/parameters` records. These are fetched
56
+ at the start of each run and updated at the end:
57
+
58
+ | TOGA 2 parameter key | Meaning | Used for |
59
+ |---|---|---|
60
+ | `TOGADESK_LAST_TICKET_INTEGRATION_DATETIME` | Newest `_updated` timestamp of tickets synced from TOGA 2 | Drives TOGA 2 → TOGaDesk query |
61
+ | `TOGA_LAST_TICKET_INTEGRATION_DATETIME` | Newest `timestamp` of tickets/replies synced from TOGaDesk | Drives TOGaDesk → TOGA 2 query |
62
+
63
+ Both default to `2000-01-01 00:00:00` if not yet set (full initial sync).
64
+
65
+ ## TOGA 2 → TOGaDesk sync (`isEnabledToga2ToTogadeskIntegration`)
66
+
67
+ Runs when this flag is `true`. Processes 200 records per page.
68
+
69
+ **Step 1 — Changed tickets:**
70
+ - `GET /tickets?where[_updated > TOGADESK_LAST_TICKET_INTEGRATION_DATETIME]` (paged)
71
+ - Each ticket re-fetched at `depth=5` for full details
72
+ - Calls `syncToga2TicketIntoTogadesk1Ticket($togaTicket)`
73
+
74
+ **Step 2 — Changed ticket-notes (catches notes on already-processed tickets):**
75
+ - `GET /ticket-notes?where[_updated > TOGADESK_LAST_TICKET_INTEGRATION_DATETIME]` (paged)
76
+ - Skips tickets already processed in Step 1 (tracked in `$alreadyProcessedToga2TicketUuids`)
77
+ - Re-fetches the parent ticket at `depth=5`, calls `syncToga2TicketIntoTogadesk1Ticket`
78
+
79
+ **Inside `syncToga2TicketIntoTogadesk1Ticket`:**
80
+ - `isExternal = true` note → creates `tickets_replies` record with `referenceid = ticketNote->uuid`
81
+ - `isExternal = false` note → creates `comments` record with `referenceid = ticketNote->uuid`
82
+ - After creating a new reply: calls `syncToga2NoteFilesIntoTogadesk1($ticketNote, $togadeskReplyId)`
83
+ - Also writes a `tickets_history` row for each new reply
84
+
85
+ ## TOGaDesk → TOGA 2 sync (`isEnabledTogadeskToToga2Integration`)
86
+
87
+ Runs when this flag is `true`.
88
+
89
+ **Ticket selection:**
90
+ ```sql
91
+ SELECT tickets.id,
92
+ GREATEST(
93
+ COALESCE(latestReply.timestamp, tickets.timestamp),
94
+ COALESCE(latestComment.timestamp, tickets.timestamp)
95
+ ) AS ticketIntegrationTimestamp
96
+ FROM tickets
97
+ LEFT JOIN (SELECT ticketid, MAX(timestamp) FROM tickets_replies GROUP BY ticketid) AS latestReply ...
98
+ LEFT JOIN (SELECT ticketid, MAX(timestamp) FROM comments GROUP BY ticketid) AS latestComment ...
99
+ WHERE tickets.clientid = ? AND tickets.departmentId IN (?)
100
+ AND GREATEST(...) > '$dtTogaLastTicketIntegration'
101
+ ```
102
+
103
+ **Per ticket:** calls `syncTogaDesk1TicketIntoToga2Ticket($togadeskTicketId)`
104
+
105
+ **Inside that method (replies loop):**
106
+ - For each `tickets_replies` row without a `referenceId`: POST to `/ticket-notes` in TOGA 2
107
+ with `c_eliteConversationId` (for Elite) or `c_wjeConversationId` (for WJE)
108
+ - Stores returned `ticketNotes.uuid` back into `tickets_replies.referenceId`
109
+ - Calls `syncTogadesk1NoteFilesIntoToga2($togadeskReplyId, $ticketNoteUuid)`
110
+
111
+ **`referenceid` field** in TOGaDesk (`tickets_replies.referenceId`, `comments.referenceId`)
112
+ is the foreign key to TOGA 2 — it holds the TOGA 2 `ticketNotes.uuid`. When `referenceId`
113
+ is already populated the row was previously synced and is skipped.
114
+
115
+ ## Other sync operations within `syncWithTogadesk`
116
+
117
+ | Flag | What it syncs | TOGA 2 source → TOGaDesk target |
118
+ |---|---|---|
119
+ | `isEnabledToga2ContactsToTogadeskPeopleIntegration` | Contacts → People | `/contacts` → `people` table |
120
+ | `isEnabledToga2ItemsUnitsToTogadeskAssetsIntegration` | Units → Assets | `/units` → `assets` table |
121
+ | `isEnabledToga2PredefinedRepliesToTogadeskTicketsPrIntegration` | Predefined replies → TicketsPR | `/predefined-replies` → `tickets_pr` table |
122
+ | `isEnabledToga2TicketTeamsToTogadeskGroupsIntegration` | Ticket teams → Groups | `/ticket-teams` → `groups` table |
123
+ | `isEnabledToga2TicketCategoriesToTogadeskCategorySubcategoryItemsIntegration` | Categories → custom field | Three-level tree flattened to comma-separated string, stored in TOGaDesk `tickets_customfields` |
124
+
125
+ ## File sync helpers
126
+
127
+ ### `syncToga2NoteFilesIntoTogadesk1(object $ticketNote, int $togadeskReplyId)`
128
+ Direction: **TOGA 2 → TOGaDesk**
129
+
130
+ 1. `GET /ticket-notes/{uuid}?depth=5` → reads `ticketNoteFiles` array
131
+ 2. `SELECT name FROM files WHERE ticketreplyid = $togadeskReplyId` — existing filename dedup
132
+ 3. For each new file:
133
+ - `GET /files/{uuid}?depth=1` → `base64_decode($fileData->data->files->data)`
134
+ - INSERT `App_Model_TogaDesk_File` (ticketreplyid, name) → get `$fileId`
135
+ - Write to disk: `/var/www/html/ontrack/desk/uploads/{fileId}-{name}`
136
+ - `UPDATE files SET file='{fileId}-{name}', filetype=?, filesize=? WHERE id=?`
137
+
138
+ ### `syncTogadesk1NoteFilesIntoToga2(int $togadeskReplyId, string $ticketNoteUuid)`
139
+ Direction: **TOGaDesk → TOGA 2**
140
+
141
+ 1. `SELECT id, name, file FROM files WHERE ticketreplyid = $togadeskReplyId`
142
+ 2. `GET /ticket-notes/{uuid}?depth=5` → reads `ticketNoteFiles` — existing filename dedup
143
+ 3. For each new file:
144
+ - Read from disk: `/var/www/html/ontrack/desk/uploads/{file}`
145
+ - `POST /files` with base64-encoded data → get `$fileUuid`
146
+ - `POST /ticket-notes/{uuid}/ticket-note-files` with `{file: {uuid: $fileUuid}}`
147
+
148
+ ### `syncFreshserviceConversationAttachmentsIntoToga2(...)`
149
+ Direction: **Freshservice → TOGA 2** — see worker2 doc.
150
+
151
+ ## TOGA 2 API — critical gotchas
152
+
153
+ **Response key is `ticketNoteFiles`, not `ticketNotesFiles`.**
154
+ The extra `s` causes a silent empty array — dedup never fires, files re-upload every run.
155
+
156
+ **`GET /ticket-notes/{uuid}/ticket-note-files` is NOT a list endpoint.**
157
+ `childPolicy = MATCH`: 0 records → 404 EV-6, 2+ records → 404 EV-14.
158
+ Always use `depth=5` on the parent note for listing.
159
+
160
+ **`throwExceptionOnApiError` (8th param of `App_Api_Toga2::send()`).**
161
+ Pass `true` for the dedup GET — errors must not silently empty the dedup array.
162
+
163
+ ## Error handling
164
+
165
+ Each ticket sync is wrapped in `try/catch` — exceptions are sent to Sentry and the run
166
+ continues with the next ticket. A single bad ticket does not abort the whole sync.
@@ -5,3 +5,4 @@
5
5
  | [Worker (worker2) Architecture](architecture.md) | Worker (repo `worker2`) is an AWS Elastic Beanstalk **Worker Tier** application that processes background jobs. | worker2/Controller/Index.php, worker2/Worker/, worker2/LambdaFunctions/, _underscore/Worker.php |
6
6
  | [ClickUp Project & Opportunity Multi-List Routing](features/clickup-project-routing.md) | Routes ClickUp tasks into the correct **secondary multi-list memberships** based on their custom-field values, via the `clickup` webhook. | worker2/Worker/Clickup/Project.php, worker2/Worker/Clickup.php |
7
7
  | [Creating Worker Actions](features/creating-worker-actions.md) | How to add a new callable Worker action — a PHP class whose `public static` methods are invoked as background jobs (via webhook, cron, or `_Worker::runTask()`). | worker2/Worker/, worker2/Controller/Index.php, _underscore/Worker.php |
8
+ | [Elite Freshservice Sync (worker2)](features/elite-freshservice-sync.md) | `_Worker_Elite` processes Freshservice webhook events and syncs them into TOGA 2. | worker2/Worker/Elite.php, worker2/Config/dev-kmaramreddy-laptop.ini |
@@ -0,0 +1,142 @@
1
+ ---
2
+ title: Elite Freshservice Sync (worker2)
3
+ framework: "2.0"
4
+ repo: worker2
5
+ project: Worker
6
+ client: elite
7
+ type: client-feature
8
+ status: active
9
+ updated: 2026-06-10
10
+ owners: []
11
+ files:
12
+ - worker2/Worker/Elite.php
13
+ - worker2/Config/dev-kmaramreddy-laptop.ini
14
+ related:
15
+ - clients/elite/profile.md
16
+ - 1.0/apps/library/features/elite-freshservice-sync.md
17
+ ---
18
+
19
+ ## Summary
20
+
21
+ `_Worker_Elite` processes Freshservice webhook events and syncs them into TOGA 2.
22
+ Dispatched via action `Elite/Webhook` on the worker queue.
23
+
24
+ ## Key files / entry points
25
+
26
+ - `Worker/Elite.php` — `_Worker_Elite` class, all sync logic lives here
27
+ - `_Component_Api_Elite` (`_underscore`) — authenticated Freshservice HTTP client
28
+ - `_Component_Api_Toga` (`_underscore`) — authenticated TOGA 2 HTTP client
29
+
30
+ ## How the webhook flow works
31
+
32
+ 1. Freshservice fires a webhook to `webhook.togahub.com/elite`
33
+ 2. Lambda inserts a `WorkerJobs` row with action `Elite/Webhook`
34
+ 3. Worker picks up the job → calls `_Worker_Elite::Webhook($payload, $headers)`
35
+ 4. Worker syncs the ticket and all conversations into TOGA 2
36
+
37
+ ## Constants
38
+
39
+ ```php
40
+ const CLIENT_UUID_ELITE = '97627e9d-605f-3ded-9e83-97a4ec7a0c15';
41
+ const API_UUID_ELITE = 'ee08cf3d-caee-462d-adb5-acc5c36b1f01';
42
+ const API_SECRET_ELITE = 'c2701d21-1908-4895-aaed-2e6456710e8d';
43
+ ```
44
+
45
+ ## Freshservice API — gotchas
46
+
47
+ **No single-conversation GET endpoint.**
48
+ `GET /api/v2/conversations/{id}` only supports PATCH/PUT/DELETE — it returns 405 on GET.
49
+ To fetch a specific conversation, retrieve the full list for the ticket:
50
+
51
+ ```php
52
+ $conversations = _Component_Api_Elite::send('GET', '/tickets/' . $ticketId . '/conversations', null);
53
+ foreach ($conversations->conversations as $conv) {
54
+ if ((string) $conv->id === $targetId) { ... }
55
+ }
56
+ ```
57
+
58
+ **Attachment URLs are presigned S3 URLs — they expire.**
59
+ Call the attachment sync method immediately at webhook time. Do not queue the download.
60
+
61
+ **Attachment shape on a conversation object:**
62
+ ```
63
+ $attachment->name
64
+ $attachment->content_type
65
+ $attachment->size
66
+ $attachment->attachment_url // presigned S3 URL
67
+ ```
68
+
69
+ ## TOGA 2 API — gotchas
70
+
71
+ **`childPolicy = MATCH` nested routes are get-ONE, not a list.**
72
+ `GET /ticket-notes/{uuid}/ticket-note-files` returns:
73
+ - 404 EV-6 when 0 bridge records exist
74
+ - 200 object when exactly 1 exists
75
+ - 404 EV-14 ("multiple found, expected one") when 2+ exist
76
+
77
+ **Never use this route for listing or dedup.** Use `depth=5` on the parent note instead:
78
+
79
+ ```php
80
+ $togaNote = _Component_Api_Toga::send(..., 'GET', '/ticket-notes/' . $uuid, null, ['depth' => 5], true, $endpoint);
81
+ $existingFileNames = [];
82
+ foreach (($togaNote->data->ticketNotes->ticketNoteFiles ?? []) as $link) {
83
+ if (!empty($link->file->name)) {
84
+ $existingFileNames[] = $link->file->name;
85
+ }
86
+ }
87
+ ```
88
+
89
+ Response key is `ticketNoteFiles` (not `ticketNotesFiles` — no `s` before `Files`).
90
+
91
+ ## Local dev setup
92
+
93
+ To test worker2 Elite code locally against production TOGA 2 and Freshservice:
94
+
95
+ 1. **Dev config** — `[api]` section needs both keys (`endpoint` for some callers,
96
+ `_` for `_Config::api('_')`):
97
+ ```ini
98
+ [api]
99
+ _ = "https://api.togahub.com/v2"
100
+ endpoint = "https://api.togahub.com/v2"
101
+ ```
102
+
103
+ 2. **`ClientLogs` database** must exist locally — the framework logs every API call there:
104
+ ```sql
105
+ CREATE DATABASE IF NOT EXISTS ClientLogs CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
106
+ USE ClientLogs;
107
+ CREATE TABLE `Api` (
108
+ `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
109
+ `uuid` CHAR(36) NOT NULL,
110
+ `dtStamp` DATETIME NOT NULL,
111
+ `transactionId` VARCHAR(64) NULL,
112
+ `apiId` INT UNSIGNED NULL,
113
+ `userId` INT UNSIGNED NULL,
114
+ `isAuthRequest` TINYINT UNSIGNED NOT NULL DEFAULT 0,
115
+ `direction` ENUM('IN','OUT') NOT NULL,
116
+ `source` VARCHAR(255) NULL,
117
+ `sourceIp` VARCHAR(45) NULL,
118
+ `instanceId` VARCHAR(128) NULL,
119
+ `executionTimeSeconds` DECIMAL(10,4) NULL,
120
+ `method` VARCHAR(10) NOT NULL,
121
+ `hostname` VARCHAR(255) NOT NULL,
122
+ `route` VARCHAR(1024) NOT NULL,
123
+ `responseCode` SMALLINT UNSIGNED NULL,
124
+ `queryString` TEXT NULL,
125
+ `requestHeaders` TEXT NULL,
126
+ `requestPayload` MEDIUMTEXT NULL,
127
+ `responseHeaders` TEXT NULL,
128
+ `responsePayload` MEDIUMTEXT NULL
129
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
130
+ ```
131
+
132
+ 3. **Bootstrap** for a standalone test script:
133
+ ```php
134
+ chdir(__DIR__);
135
+ putenv('ENVIRONMENT=dev-kmaramreddy-laptop');
136
+ $_ENV['ENVIRONMENT'] = 'dev-kmaramreddy-laptop';
137
+ require_once 'vendor/autoload.php';
138
+ require '_underscore.php';
139
+ ```
140
+
141
+ 4. **PHP version** — worker2 vendor targets PHP >= 8.5. Local PHP 8.1 requires a
142
+ temporary bypass in `vendor/composer/platform_check.php` — revert before committing.
@@ -4,13 +4,13 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
4
4
 
5
5
  ## 1.0 framework
6
6
 
7
- - **library** (Library) _(framework core)_ — 1 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
7
+ - **library** (Library) _(framework core)_ — 2 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
8
  - **worker** (Worker) — 1 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
9
9
 
10
10
  ## 2.0 framework
11
11
 
12
12
  - **_underscore** (_Underscore) _(framework core)_ — 3 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
13
- - **worker2** (Worker) — 3 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
13
+ - **worker2** (Worker) — 4 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
14
14
  - **api2** (API) — 1 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
15
15
  - **dbchanges2** (Database Changes) _(framework core)_ — 1 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
16
16
  - **toga2-supply** (TOGa Supply) — 2 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
@@ -19,4 +19,5 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
19
19
 
20
20
  - **compass-canada** → [clients/compass-canada/INDEX.md](clients/compass-canada/INDEX.md)
21
21
  - **compass-usa** → [clients/compass-usa/INDEX.md](clients/compass-usa/INDEX.md)
22
+ - **elite** → [clients/elite/INDEX.md](clients/elite/INDEX.md)
22
23
 
@@ -0,0 +1,5 @@
1
+ # Client: elite
2
+
3
+ | Doc | Framework | Summary | Files |
4
+ |-----|-----------|---------|-------|
5
+ | [Elite Client Profile](profile.md) | 2.0 | Elite is a managed-services client that uses **Freshservice** as their helpdesk platform. | worker2/Worker/Elite.php, library/app/api/toga2.php |
@@ -0,0 +1,61 @@
1
+ ---
2
+ title: Elite Client Profile
3
+ framework: "2.0"
4
+ project: Worker
5
+ client: elite
6
+ type: client-feature
7
+ status: active
8
+ updated: 2026-06-10
9
+ owners: []
10
+ files:
11
+ - worker2/Worker/Elite.php
12
+ - library/app/api/toga2.php
13
+ related:
14
+ - 2.0/apps/worker2/features/elite-freshservice-sync.md
15
+ - 1.0/apps/library/features/elite-freshservice-sync.md
16
+ ---
17
+
18
+ ## Summary
19
+
20
+ Elite is a managed-services client that uses **Freshservice** as their helpdesk platform.
21
+ TOGA 2 is the source of truth for tickets, notes, and file attachments. The integration
22
+ keeps Freshservice and TOGA 2 in sync via webhook-driven worker jobs.
23
+
24
+ ## TOGA 2 API credentials
25
+
26
+ Constants live in `worker2/Worker/Elite.php`:
27
+
28
+ ```php
29
+ const CLIENT_UUID_ELITE = '97627e9d-605f-3ded-9e83-97a4ec7a0c15';
30
+ const API_UUID_ELITE = 'ee08cf3d-caee-462d-adb5-acc5c36b1f01';
31
+ const API_SECRET_ELITE = 'c2701d21-1908-4895-aaed-2e6456710e8d';
32
+ ```
33
+
34
+ The library version of the sync methods (`library/app/api/toga2.php`) passes these in as
35
+ parameters — they are not constants there.
36
+
37
+ ## Freshservice instance
38
+
39
+ - Base URL: `https://eliteservicedesk.freshservice.com`
40
+ - Configured in `_underscore` framework via `_Component_Api_Elite`
41
+ - API logs written to the `ClientLogs` database (`ClientLogs.Api` table) — this DB must
42
+ exist locally when running worker2 scripts against production Freshservice
43
+
44
+ ## Key custom fields
45
+
46
+ | TOGA 2 field | Purpose |
47
+ |---|---|
48
+ | `c_eliteTicketId` | Freshservice ticket numeric ID (on `Tickets`) |
49
+ | `c_eliteConversationId` | Freshservice conversation ID (on `TicketNotes`) |
50
+ | `c_eliteUserId` | Freshservice user ID (on `Contacts`) |
51
+ | `c_eliteCategoryId` | Freshservice category ID (on `TicketCategories`) |
52
+ | `c_eliteGroupId` | Freshservice group ID (on `TicketTeams`) |
53
+
54
+ ## Systems involved
55
+
56
+ | System | Role |
57
+ |---|---|
58
+ | Freshservice | Client helpdesk — source of ticket/conversation events |
59
+ | TOGA 2 (`api2`) | System of record — tickets, notes, contacts, files |
60
+ | worker2 `_Worker_Elite` | Webhook processor — syncs Freshservice events into TOGA 2 |
61
+ | library `App_Api_Toga2` | Shared sync helpers (note-file bidirectional sync) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.35",
3
+ "version": "1.0.37",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",