toga-ai 1.0.991 → 1.0.993
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/knowledge/1.0/apps/tools/INDEX.md +1 -1
- package/knowledge/1.0/apps/tools/features/design-demo-admin.md +68 -60
- package/knowledge/1.0/apps/tools/features/github-audit.md +13 -11
- package/knowledge/1.0/apps/tools/features/multi-client-sql.md +1 -1
- package/knowledge/standalone/apps/claude/workflows/clickup-ticket-workflow.md +8 -0
- package/knowledge/standalone/apps/forward/INDEX.md +1 -1
- package/knowledge/standalone/apps/forward/architecture.md +6 -2
- package/knowledge/standalone/apps/forward/features/static-demo-hosting.md +33 -35
- package/package.json +1 -1
- package/scripts/hooks/session-start.js +43 -18
- package/skills/start/SKILL.md +13 -0
- package/skills/start/scripts/start-helper.js +20 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
| [Developer Dashboard (ClickUp Sprint, tools /developer)](features/clickup-sprint-dashboard.md) | Native ClickUp sprint dashboard in the 1.0 tools app at `/developer` (TV wall display), reading the Team DB via `/v2/sprints/*`; open for the data engine, the C |
|
|
9
9
|
| [CloudFront Client Setup](features/cloudfront-client-setup.md) | SSO-gated Tools admin at `/devops/cloudfront-clients` that onboards a client onto CloudFront + Route 53 across multiple AWS accounts in one run; open for the to |
|
|
10
10
|
| [Compass User & Persona Admin (tools /compass/users, /compass/personas)](features/compass-user-persona-admin.md) | Two pages in the Tools `developers` nav folder that let staff do, in a browser, the Compass requests that used to mean "email a developer, who writes a `dbchang |
|
|
11
|
-
| [Design Demo Admin](features/design-demo-admin.md) | SSO-gated Tools admin at `/design` that publishes self-contained "Claude Design" HTML exports as versioned demos into
|
|
11
|
+
| [Design Demo Admin](features/design-demo-admin.md) | SSO-gated Tools admin at `/design` that publishes self-contained "Claude Design" HTML exports as versioned demos into S3 bucket `togatech-demos` (served by Clou |
|
|
12
12
|
| [Tools — Developers Folder (UUID & Password Generators)](features/developer-tools.md) | Tools "Developers" folder — the Generate UUID and Generate Password tools (reference example of the one-tool-per-`mvc/<route>/get.php` pattern); open for the ge |
|
|
13
13
|
| [/emails Email Queue Review (Tools → 2.0 Logs_<Client>.Email)](features/email-queue-review.md) | Staff page to browse any 2.0 client's `Logs_<Client>.Email` queue and release/reject `HELD` review-hold emails. |
|
|
14
14
|
| [/errors Curation Console (Tools → shared Core Logs DB)](features/errors-curation-console.md) | Internal-only 1.0 Tools triage/curation screen (`/errors`) for the 2.0 Issue/Event error pipeline, reading the shared Core Logs DB via `db_toga2logs`; curates, |
|
|
@@ -6,10 +6,10 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-09
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
|
-
- tools/_/app/design/
|
|
12
|
+
- tools/_/app/design/s3.php
|
|
13
13
|
- tools/mvc/design/get.php
|
|
14
14
|
- tools/mvc/design/post.php
|
|
15
15
|
- tools/assets/css/design.css
|
|
@@ -27,88 +27,96 @@ related:
|
|
|
27
27
|
- ../../../standalone/apps/forward/features/static-demo-hosting.md
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
-
SSO-gated Tools admin at `/design` that publishes self-contained "Claude Design" HTML exports as versioned demos into
|
|
30
|
+
SSO-gated Tools admin at `/design` that publishes self-contained "Claude Design" HTML exports as versioned demos into S3 bucket `togatech-demos` (served by CloudFront at `demo.togatech.com`); open for the S3 conditional-write model, publish order, credentials, the plain-text/escape invariant, or the AWS setup.
|
|
31
31
|
|
|
32
32
|
## Summary
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Self-serve admin at **`/design`** in the SSO-protected **Tools** app. The design team publishes Claude Design HTML exports as **versioned demos** — no developer, no git. Storage = S3 bucket **`togatech-demos`**; public hosting = CloudFront at **`demo.togatech.com`** (URLs unchanged). Hosting setup, cache rules, and rollback: [Static Demo Hosting](../../../standalone/apps/forward/features/static-demo-hosting.md).
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Since 2026-10-09 (TRUE-82616) storage is S3, not the `forward` GitHub repo. `App_Design_Github` was deleted and replaced by **`App_Design_S3`** with the same `post.php` actions.
|
|
37
37
|
|
|
38
38
|
## How it works
|
|
39
39
|
|
|
40
40
|
### Key files / entry points
|
|
41
|
-
- `_/app/design/
|
|
41
|
+
- `_/app/design/s3.php` — class **`App_Design_S3`**: AWS S3/CloudFront clients, conditional writes, project/version/tab CRUD, upload checks, stub builder, Claude prompt. Constants: `BUCKET`, `REGION` (us-west-2), `DISTRIBUTION_ID`, `MAX_UPLOAD_BYTES`, `MAX_CONFLICT_RETRIES`, cache-control values.
|
|
42
42
|
- `mvc/design/get.php` — route `/design` (page shell). Self-guards with `App_Auth::requireAuth([...])` + `App_Page::validateCrossSiteRequestForgery()`.
|
|
43
43
|
- `mvc/design/post.php` — AJAX action dispatcher.
|
|
44
|
-
- `assets/css/design.css`, `assets/js/design.js` — namespaced under `.design-tool`, registered
|
|
45
|
-
- `_/app/nav.php` —
|
|
46
|
-
|
|
47
|
-
###
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
44
|
+
- `assets/css/design.css`, `assets/js/design.js` — namespaced under `.design-tool`, registered in `_/app/frameworkindex.php`.
|
|
45
|
+
- `_/app/nav.php` — `design` group, action **`Design Demos`** → `/design`, personas `['TOGa Technology','Development Team']`.
|
|
46
|
+
|
|
47
|
+
### Data model (S3 keys = URL paths)
|
|
48
|
+
- `<project>/project.json` — `{ title, latest, hidden, tabs[], versions[] }`. A folder is a managed demo **iff it has `project.json`**.
|
|
49
|
+
- `<project>/vN/index.html` — sequential `/v1`, `/v2`… (not semver/date).
|
|
50
|
+
- Root `tabs.json` — `{ "tabs": [...] }`, ordered tab list.
|
|
51
|
+
- `<project>/index.html` — generated **stub** that redirects (relative `./vN/`) to the newest **visible** version; rewritten on publish/unlist.
|
|
52
|
+
- Root `404.html` — CloudFront error page.
|
|
53
|
+
- `design/` and `sos/` are excluded from listing (`EXCLUDED_FOLDERS`).
|
|
54
|
+
|
|
55
|
+
### Conditional writes (no lost updates)
|
|
56
|
+
S3 has no multi-file commit, so writes use S3 conditional PUT (vendored aws-sdk-php 3.388.13 supports both):
|
|
57
|
+
- Create-only files: `PutObject IfNoneMatch '*'`.
|
|
58
|
+
- Updates of `project.json` / `tabs.json`: `PutObject IfMatch <ETag read earlier>`.
|
|
59
|
+
- On 412/409: re-read and retry, up to `MAX_CONFLICT_RETRIES` (3). Then the user sees *"Another change was just published — please try again."*
|
|
60
|
+
- **Publish order:** version file (create-only) → `project.json` → stub. Next version id = max of `project.json` versions and existing `vN/` prefixes, so a half-finished publish never reuses an id.
|
|
61
|
+
- **Tab rename/delete:** rewrite each affected `project.json` first, `tabs.json` last. Not one atomic step any more, but safe to re-run.
|
|
62
|
+
- **Cache headers:** `vN/index.html` `max-age=86400`; stub, `project.json`, `tabs.json` `max-age=60`. A normal publish needs no CloudFront invalidation (live within ~1 minute).
|
|
58
63
|
|
|
59
64
|
### Multi-tab membership
|
|
60
|
-
|
|
61
|
-
- **Rename / delete** — rewrite `tabs.json` **and** every affected `project.json` in one **atomic** commit.
|
|
62
|
-
- **Reorder** — a single-file `tabs.json` commit, validated as a **permutation** of the existing list.
|
|
65
|
+
`project.json` `tab` scalar → `tabs` array. Old scalar is read (backward-compatible) and dropped on the next write. Per-pill "⋯" menu: rename / delete (see above), reorder (one `tabs.json` write, validated as a permutation of the existing list).
|
|
63
66
|
|
|
64
67
|
### Text-storage invariant: JSON holds plain text, escape once at display
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- **
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
79
|
-
-
|
|
80
|
-
- **Set latest / toggle version / edit version / toggle project / set project tabs** — commit the relevant `project.json` (metadata-only edits do not rebuild the stub).
|
|
81
|
-
- **Copy URL** and **Copy Claude Design prompt** — clipboard helpers.
|
|
82
|
-
- **Single scroll region** — `#projects` is the only scroll area; header/tabbar are fixed, so switching tabs never shifts layout.
|
|
83
|
-
- The Claude Design export **prompt modal** forces a dark `<pre>` background + light text with `!important` (the global light `pre` theme otherwise wins).
|
|
68
|
+
Tab names and project `title` are stored **plain**. The one escape is at display: `design.js` `esc()` and `buildStub()` (`htmlspecialchars()`). `App_Design_S3::decodeEntities()` (one `html_entity_decode`, `ENT_QUOTES | UTF-8`) runs at both chokepoints:
|
|
69
|
+
- on read from storage — `loadTabs()`, `normalizeTabs()` (incl. legacy `tab`), project `title`;
|
|
70
|
+
- on request input — `createTab()`, `renameTab()` (old + new), `deleteTab()`, `reorderTabs()`, `filterKnownTabs()`, `createProject()`, `upload()` titles.
|
|
71
|
+
|
|
72
|
+
Both are needed: decode-on-read only → `renameTab()` compares `H&H` to stored `H&H` → *"No such tab."*; decode-on-write only → old bad data stays double-escaped. Every tab write rewrites values in full, so bad stored data heals on the next write.
|
|
73
|
+
|
|
74
|
+
### Actions
|
|
75
|
+
- **Publish a version** — next `vN/index.html`, version entry, stub.
|
|
76
|
+
- **Adopt** — turns a legacy folder with only `index.html` into a managed project (`v1` = that page).
|
|
77
|
+
- **Remove version** / **Show N unlisted** — hidden versions collapse.
|
|
78
|
+
- **Unlist project ("Remove")** — reversible `hidden` flag; files stay (frees no storage).
|
|
79
|
+
- **Hard-delete project** — list `<project>/` keys → `DeleteObjects` (batches of 1000) → CloudFront invalidation of `/<project>/*` and `/<project>`. Refuses folders without `project.json`/`index.html`.
|
|
80
|
+
- **Set latest / toggle version / edit version / toggle project / set project tabs** — `project.json` write (metadata-only edits skip the stub).
|
|
81
|
+
- **Copy URL**, **Copy Claude Design prompt** — clipboard helpers. Prompt modal forces a dark `<pre>` with `!important`.
|
|
82
|
+
- `#projects` is the only scroll region.
|
|
84
83
|
|
|
85
84
|
### Hardening
|
|
86
|
-
- `isValidProjectName()`
|
|
87
|
-
- `upload()`
|
|
88
|
-
-
|
|
89
|
-
-
|
|
85
|
+
- `isValidProjectName()` on every project action; `RESERVED_NAMES` blocked.
|
|
86
|
+
- `createProject()` / `upload()` refuse a name where a legacy `index.html` exists — adopt it instead.
|
|
87
|
+
- `upload()` errors on corrupt `project.json` instead of resetting history.
|
|
88
|
+
- `window.DESIGN_CONFIG` uses `JSON_HEX_*`; input length caps; version-id regex; HTML sniff on upload; AWS error detail is logged, never shown to the user.
|
|
90
89
|
|
|
91
|
-
### Upload size
|
|
92
|
-
-
|
|
93
|
-
- php.ini (`.ebextensions/009_setup_phpini.config`): `upload_max_filesize=50M` / `post_max_size=55M`. **Keep these above `MAX_UPLOAD_BYTES
|
|
94
|
-
- `ghApi` curl timeout = `GITHUB_TIMEOUT_SECONDS` (180s); a 50 MB file is ~67 MB as a base64 blob. If `json_encode` fails, `ghApi` logs and throws `RuntimeException` (never POSTs an empty body).
|
|
95
|
-
- Apache has no `LimitRequestBody`; `memory_limit` 2G is enough.
|
|
90
|
+
### Upload size
|
|
91
|
+
- `App_Design_S3::MAX_UPLOAD_BYTES` = 50 MB.
|
|
92
|
+
- php.ini (`.ebextensions/009_setup_phpini.config`): `upload_max_filesize=50M` / `post_max_size=55M`. **Keep these above `MAX_UPLOAD_BYTES`**; raise both together.
|
|
96
93
|
|
|
97
94
|
### Access control
|
|
98
|
-
|
|
95
|
+
Tools SSO + `requireAuth` + CSRF check; nav gated to `TOGa Technology` and `Development Team` personas.
|
|
99
96
|
|
|
100
|
-
###
|
|
101
|
-
`
|
|
97
|
+
### AWS credentials (values never recorded)
|
|
98
|
+
`credentials()` order:
|
|
99
|
+
1. Env `DEMOS_AWS_ACCESS_KEY_ID` / `DEMOS_AWS_SECRET_ACCESS_KEY` — read from `getenv()` AND `$_SERVER` AND `$_ENV` (php-fpm `clear_env=yes`; see deploy workflow).
|
|
100
|
+
2. Config `[design] aws_access_key_id` / `aws_secret_access_key` — local dev.
|
|
101
|
+
3. SDK default chain — **prod uses this**: the EB instance role `ElasticBeanstalk-EC2-Instance-Profile` (shared prod EB role) has IAM policy **`togatech-demos-publisher`**: S3 Get/Put/Delete on bucket objects, ListBucket on the bucket, `cloudfront:CreateInvalidation` on the distribution.
|
|
102
|
+
|
|
103
|
+
Tools runs in the **PROD** account as EB env `tools-production`, app `Tools`, **us-east-1**, host `tools.togahub.com`. The bucket is in us-west-2 — the client region comes from `REGION`, not the host.
|
|
102
104
|
|
|
103
105
|
## Gotchas
|
|
104
|
-
- **`curl_close()` is
|
|
105
|
-
- **
|
|
106
|
-
- **
|
|
107
|
-
- **
|
|
108
|
-
- **
|
|
109
|
-
- Never hardcode
|
|
106
|
+
- **`curl_close()` is fatal on PHP 8.5** in the 1.0 framework (deprecation → `ErrorException`). Removed in this tool; audit ported code for other deprecated calls.
|
|
107
|
+
- **Double-escaped names (`H&H`, rename says "No such tab.")** — values arrived already HTML-encoded (paste from rendered HTML / pre-fix build). Fixed by `decodeEntities()` on both sides (above). Stubs were affected the same way.
|
|
108
|
+
- **Upload limit** — raise `MAX_UPLOAD_BYTES` and php.ini together.
|
|
109
|
+
- **Locally, the AWS SDK prints `syntax error, unexpected '='` warnings** while parsing a local `~/.aws` config file. Harmless; not seen in prod.
|
|
110
|
+
- **Do not go back to storing demos in a git repo deployed to EB.** That is why `forward` broke (EB 500 MB bundle cap) — see [Static Demo Hosting](../../../standalone/apps/forward/features/static-demo-hosting.md).
|
|
111
|
+
- Never hardcode AWS keys in tracked source.
|
|
110
112
|
|
|
111
113
|
## Related
|
|
112
114
|
- [Tools Architecture](../architecture.md)
|
|
113
115
|
- [Static Demo Hosting](../../../standalone/apps/forward/features/static-demo-hosting.md)
|
|
114
116
|
- [Deploying Tools to Elastic Beanstalk (AL2023)](../workflows/deploy-to-elastic-beanstalk-al2023.md)
|
|
117
|
+
- [GitHub Audit](./github-audit.md) — used to share this tool's GitHub token; now has its own
|
|
118
|
+
|
|
119
|
+
## Change history
|
|
120
|
+
- 2026-10-09 — Storage moved from the `forward` GitHub repo to S3 `togatech-demos` + CloudFront; `App_Design_Github` → `App_Design_S3`; conditional writes; prod access via instance role (TRUE-82616) (jcardinal)
|
|
121
|
+
- 2026-10-08 — Upload cap 25 MB → 50 MB (php.ini + `MAX_UPLOAD_BYTES`) (jcardinal)
|
|
122
|
+
- 2026-07-24 — Admin UI moved from `forward` (no auth) into SSO-protected Tools (jcardinal)
|
|
@@ -6,7 +6,7 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09
|
|
9
|
+
updated: 2026-10-09
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/_/app/github/audit.php
|
|
@@ -21,24 +21,24 @@ related:
|
|
|
21
21
|
- ../../test/features/github-audit-script.md
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
Internal monthly code-churn report at `/developers/github-audit` (lines added/removed across every `agilantsolutions` repo for one month); open for the two-phase run/progress model, the
|
|
24
|
+
Internal monthly code-churn report at `/developers/github-audit` (lines added/removed across every `agilantsolutions` repo for one month); open for the two-phase run/progress model, the rate-limit budget gate, or the SSRF guard.
|
|
25
25
|
|
|
26
26
|
## Summary
|
|
27
27
|
|
|
28
28
|
An internal report at **`/developers/github-audit`** (Developers nav group, personas `['Development Team','TOGa Technology']`) that shows, for one calendar month, the **lines added/removed across every repo in the `agilantsolutions` GitHub org**, plus commit count, unique authors, and active repo count. Built so the team gets a monthly code-churn report without leaving GitHub.
|
|
29
29
|
|
|
30
|
-
It is the app port of the standalone [`test/team/github_audit.php`](../../test/features/github-audit-script.md) script (built first, then moved into Tools).
|
|
30
|
+
It is the app port of the standalone [`test/team/github_audit.php`](../../test/features/github-audit-script.md) script (built first, then moved into Tools). Uses the same log-detail/return-generic error pattern as the Design tool.
|
|
31
31
|
|
|
32
32
|
## How it works
|
|
33
33
|
|
|
34
34
|
### Key files / entry points
|
|
35
|
-
- `_/app/github/audit.php` — class **`App_Github_Audit`**: a static, read-only GitHub REST engine (not a Controller/Model).
|
|
35
|
+
- `_/app/github/audit.php` — class **`App_Github_Audit`**: a static, read-only GitHub REST engine (not a Controller/Model). Same static-engine pattern as `App_Design_S3`.
|
|
36
36
|
- `mvc/developers/github-audit/get.php` — the page: native `<input type="month">` picker (defaults to last month), a Run button, a progress bar, and results (summary cards + per-repo table). Themed with `--c-*` tokens (light/dark).
|
|
37
37
|
- `mvc/developers/github-audit/post.php` — AJAX JSON endpoint. `requireAuth` + `App_Page::validateCrossSiteRequestForgery()`; allowlisted actions only.
|
|
38
38
|
- `_/app/nav.php` — one nav action under the `developers` group.
|
|
39
39
|
|
|
40
|
-
###
|
|
41
|
-
|
|
40
|
+
### GitHub token
|
|
41
|
+
`App_Github_Audit::token()` resolves its own token: EB env `GITHUB_TOKEN` from `getenv()` AND `$_SERVER` AND `$_ENV` (php-fpm `clear_env=yes`), then config `[design] github_token` for local dev. Value never recorded, logged, or echoed. The Design tool no longer uses GitHub (moved to S3 2026-10-09), so this tool is the token's only Tools user.
|
|
42
42
|
|
|
43
43
|
### Accuracy — per-commit exact counting
|
|
44
44
|
To get exact line counts, the engine lists **each commit** in the month window (`since`/`until`), then fetches **each commit's stats**. Merge commits are **skipped** (they would double-count), and forks are **skipped**.
|
|
@@ -59,7 +59,7 @@ The client accumulates totals and authors across the per-repo responses and rend
|
|
|
59
59
|
- **Budget gate** — `const MIN_REMAINING_TO_RUN = 1000;` sets the floor. Enforced at two points, both throwing a user-safe (`ERR_USER_SAFE`) message:
|
|
60
60
|
- **Pre-flight:** `App_Github_Audit::assertBudget()` calls `GET /rate_limit` (which does **not** count against the rate limit) at the start of a run. If the shared token's core `remaining` is **at or below 1000**, it refuses to start and the scan never begins.
|
|
61
61
|
- **Mid-run:** `assertRateLimitOk()` **stops** the run once `x-ratelimit-remaining` drops to 1000 — read from headers already returned, so no extra calls — and shows partial results.
|
|
62
|
-
- This replaced the earlier tiny `RATE_LIMIT_FLOOR=25` safety. Rationale: the
|
|
62
|
+
- This replaced the earlier tiny `RATE_LIMIT_FLOOR=25` safety. Rationale: the org rate limit is shared with other GitHub users of the token, so the audit leaves a healthy budget.
|
|
63
63
|
|
|
64
64
|
### Live API-call + budget counter
|
|
65
65
|
- `App_Github_Audit::callCount()` — calls made in this PHP request.
|
|
@@ -68,10 +68,10 @@ The client accumulates totals and authors across the per-repo responses and rend
|
|
|
68
68
|
The page shows a live line: `API calls this run: N · GitHub budget: X of 5,000 left`, and the total in the results caption.
|
|
69
69
|
|
|
70
70
|
### Per-session concurrency lock (shared-token protection)
|
|
71
|
-
`post.php` takes an **APCu lock** (`apcu_add`, key `gha_audit_lock_<session_id>`, 300s TTL) so only one audit request runs at a time per session. Reason:
|
|
71
|
+
`post.php` takes an **APCu lock** (`apcu_add`, key `gha_audit_lock_<session_id>`, 300s TTL) so only one audit request runs at a time per session. Reason: parallel scans would burn the org's rate limit for everyone. If APCu is unavailable the lock is skipped cleanly. **Known limit:** APCu is per-EB-instance, so the lock is **per-instance**, not global.
|
|
72
72
|
|
|
73
73
|
### Error sanitizing
|
|
74
|
-
Only a **user-safe** `RuntimeException` (the rate-limit notice, thrown with code `App_Github_Audit::ERR_USER_SAFE`) reaches the browser. Every other `RuntimeException` / `Throwable` is logged and returned as a **generic** message — GitHub's raw text and repo names never surface. Same log-detail / return-generic pattern as
|
|
74
|
+
Only a **user-safe** `RuntimeException` (the rate-limit notice, thrown with code `App_Github_Audit::ERR_USER_SAFE`) reaches the browser. Every other `RuntimeException` / `Throwable` is logged and returned as a **generic** message — GitHub's raw text and repo names never surface. Same log-detail / return-generic pattern as the Design tool.
|
|
75
75
|
|
|
76
76
|
### Input validation & SSRF guard
|
|
77
77
|
- **Month:** must match `^\d{4}-\d{2}$` **and** month number 01–12. The regex alone accepts `2026-99`, which `DateTime::createFromFormat` silently overflows.
|
|
@@ -87,10 +87,12 @@ Behind Tools SSO. `get.php` and `post.php` self-guard with `requireAuth` + `App_
|
|
|
87
87
|
- **The `hidden` attribute is beaten by a class `display:` rule.** The results/spinner box had the HTML `hidden` attribute but still showed on load (looked like the tool auto-ran), because an author `display:flex` class rule beats the UA `[hidden]{display:none}`. Fix: a scoped guard `.<scope> [hidden] { display:none !important; }` (or `:not([hidden])` on the display rule). General server-rendered-UI trap.
|
|
88
88
|
- **`curl_close()` is fatal on PHP 8.5** in the 1.0 framework (deprecation → `ErrorException` → aborted request). The audit engine avoids `curl_close()` — same rule as the Design tool. See [Tools architecture](../architecture.md) and framework-rules.
|
|
89
89
|
- **APCu lock is per-EB-instance**, so the single-run guard is per-instance, not org-wide.
|
|
90
|
-
- **Shared rate-limit budget** — this tool and the Design tool draw on the same GitHub token, so a big audit eats into the Design tool's budget and vice versa.
|
|
91
90
|
|
|
92
91
|
## Related
|
|
93
92
|
- [Tools Architecture](../architecture.md)
|
|
94
|
-
- [Design Demo Admin](./design-demo-admin.md) —
|
|
93
|
+
- [Design Demo Admin](./design-demo-admin.md) — same error pattern
|
|
95
94
|
- [Front-End Standards](../../../standards/frontend.md)
|
|
96
95
|
- [github_audit.php standalone script](../../test/features/github-audit-script.md)
|
|
96
|
+
|
|
97
|
+
## Change history
|
|
98
|
+
- 2026-10-09 — `token()` now resolves its own `GITHUB_TOKEN`; no longer borrows `App_Design_Github::token()` (deleted when Design moved to S3, TRUE-82616) (jcardinal)
|
|
@@ -34,7 +34,7 @@ Replaces `test/team/2.0 deployment/generate_client_sql.php`. Development Team pe
|
|
|
34
34
|
3. Lists are dynamic — no tools commit when clients change:
|
|
35
35
|
- **All Clients / Client Logs / Client Archives** — live read of `Core.Clients` joined to `Core.Databases` on `clientDatabaseId` / `logDatabaseId` / `archiveDatabaseId`, via config `[database_toga2core]`. Keeps a DB only if `EXISTS` a `Core.DatabaseHosts` row (INNER JOIN `Environments` on `slug`) for the `ENVIRONMENT` env var (tools is production-only: `production`). Missing `ENVIRONMENT` → user-safe error, never an unfiltered list. Why: `Core.Clients` has never-built clients (see [per-client DB gotcha](../../../../2.0/apps/_underscore/features/per-client-database-connections.md)). `isActive` flag rejected: inactive ≠ no DB, flags drift, and it is a Core schema change.
|
|
36
36
|
- **Module lists** — one per folder under `dbchanges2/_modules` (branch `_main`). A client is in the list when `Client_<Name>/_modules.txt` names the module (case-insensitive); DB name = client folder name. GitHub GraphQL, 2 calls (folder list, then all `_modules.txt` as aliased blobs). APCu cache 5 min + 7-day "last good" fallback shown with a warning. Warns when a module client is not in `Core.Clients`; drops clients whose client DB is not built. One retry on a dropped connection; log names the failed step (`folder list` / `_modules.txt read`).
|
|
37
|
-
4. Env vars `GITHUB_TOKEN` and `ENVIRONMENT` read by shared `envValue()` (`getenv` + `$_SERVER` + `$_ENV`, because php-fpm `clear_env`). GitHub token: **only** `GITHUB_TOKEN`. No config fallback (developer decision). Own curl
|
|
37
|
+
4. Env vars `GITHUB_TOKEN` and `ENVIRONMENT` read by shared `envValue()` (`getenv` + `$_SERVER` + `$_ENV`, because php-fpm `clear_env`). GitHub token: **only** `GITHUB_TOKEN`. No config fallback (developer decision). Own curl. Needs read on `agilantsolutions/dbchanges2`; set as an EB environment property in prod.
|
|
38
38
|
5. Every list returns a `skipped` array, shown on the page ("Skipped N (database not built — no DatabaseHosts row): …").
|
|
39
39
|
6. Every DB/folder name is checked against `^[A-Za-z0-9_]{1,64}$` before it goes into GraphQL or a `USE` line. Page writes via `textContent`/`.value` only.
|
|
40
40
|
|
|
@@ -148,6 +148,11 @@ All tested 2026-10-08:
|
|
|
148
148
|
ticket-branch commits' `/commit` notes + the diff (risk from `skills/finish/review/pr-risk.js`).
|
|
149
149
|
An open PR gets its description rebuilt (`gh pr edit --body-file`). The GitHub Action files
|
|
150
150
|
(`.github/workflows/pr-summary.yml`, `ci/pr-review/`) stay in the repo unused.
|
|
151
|
+
- **Session title.** After verify, `/start` saves `<TICKET>: <2-4 word short title>` with
|
|
152
|
+
`start-helper.js set-title` (`sessionTitle` in the session file) and shows `/rename <title>`
|
|
153
|
+
to paste. The SessionStart hook also returns it as `sessionTitle` when the developer has not
|
|
154
|
+
named the session. Hook titles only work at SessionStart (per docs); VS Code support is
|
|
155
|
+
unconfirmed; `/rename` works everywhere.
|
|
151
156
|
|
|
152
157
|
## Change history
|
|
153
158
|
|
|
@@ -169,6 +174,9 @@ All tested 2026-10-08:
|
|
|
169
174
|
- 2026-10-09 — PM feedback: PM comment is plain bullets for a non-technical PM; Apps, Clients,
|
|
170
175
|
PRs removed; empty sections left out; field changes as plain sentences with the reason. (jcardinal)
|
|
171
176
|
|
|
177
|
+
- 2026-10-09 — TRUE-82605: `/start` names the session `<TICKET>: <short title>` (set-title +
|
|
178
|
+
SessionStart `sessionTitle`; `/rename` to paste). (jcardinal)
|
|
179
|
+
|
|
172
180
|
## Related
|
|
173
181
|
|
|
174
182
|
- [Ticket-branch creation safety](./ticket-branch-creation-safety.md)
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
|-----|---------|
|
|
5
5
|
| [Forwarder Architecture](architecture.md) | Tiny standalone PHP redirect app for TOGA's branded short domains (Apache RewriteMap + light `index.php`); open for the two redirect layers, deploy, decrypt sch |
|
|
6
6
|
| [Encrypted-Link Handler](features/encrypted-link-handler.md) | Forwarder `index.php` handler for redirect domains whose URL path carries an encrypted token (PCMatic/Newegg/`forward.*` agent downloads); open for config + dec |
|
|
7
|
-
| [Static Demo Hosting](features/static-demo-hosting.md) |
|
|
7
|
+
| [Static Demo Hosting](features/static-demo-hosting.md) | How `demo.togatech.com/<project>/vN/` static demos are hosted: S3 `togatech-demos` + CloudFront (prod account) since 2026-10-09, no longer `forward`; open for t |
|
|
@@ -6,7 +6,7 @@ project: Forwarder
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09
|
|
9
|
+
updated: 2026-10-09
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- forward/forward.ini
|
|
@@ -55,7 +55,7 @@ The `.htaccess` at the repo root drives a plain-text lookup table:
|
|
|
55
55
|
- **Local directory handler** — if the first host label matches a directory in the repo (e.g. `sos.*` → `/sos/`), it redirects to `/<subdomain>/` and serves that dir's `index.html`. Used for the bundled `sos/` Splashtop SOS download page.
|
|
56
56
|
- **404** — otherwise returns a styled `404` echoing the unmatched `host + uri`.
|
|
57
57
|
|
|
58
|
-
> **
|
|
58
|
+
> **Design demos are no longer in `forward`.** Since 2026-10-09 (TRUE-82616) demos are stored in S3 `togatech-demos` and served by CloudFront at `demo.togatech.com` (prod account); the 24 demo folders + `tabs.json` were removed from this repo (~31 MB now). The admin UI is in Tools at `/design`. See `standalone/apps/forward/features/static-demo-hosting.md` and `1.0/apps/tools/features/design-demo-admin.md`. `sos/` (local directory handler) stays. The `.htaccess` static-demo rule + `DirectoryIndex` line remain but match nothing today.
|
|
59
59
|
|
|
60
60
|
## Deployment (Elastic Beanstalk / Apache)
|
|
61
61
|
|
|
@@ -65,6 +65,7 @@ The `.htaccess` at the repo root drives a plain-text lookup table:
|
|
|
65
65
|
- `.ebextensions/rewritemap.config` — legacy **Amazon Linux 1** `files:` fallback. Both can coexist; AL2+ ignores the `.ebextensions` file path managed by `.platform`, and AL1 ignores `.platform` entirely.
|
|
66
66
|
- `forward.ini` deploys to `/var/www/html/forward.ini` (the path the map points at).
|
|
67
67
|
- `composer.json` declares a `Togatech\Forward\` PSR-4 autoload over `src/` but has **no dependencies** and no `src/` is shipped today — the app is effectively a single `index.php` plus the Apache config.
|
|
68
|
+
- **Three EB environments** run this repo: `agilant-forward-prod` and `true-forward-prod` (legacy account, CodePipeline `Forward-Production`), and `forward-production` (prod account, us-east-1, serves `togatechnology.com`). A change to `forward.ini`/`index.php` must reach all three.
|
|
68
69
|
- **`DirectoryIndex index.html index.php`** in `.htaccess` lets nested static-demo version paths `/<project>/<vN>/` serve via Apache's normal directory index. The single-segment static-demo rewrite rule does not match two-segment paths, and the `index.php` map-miss fallback does not fire for real directories — so versioned demo URLs resolve as directory-index lookups.
|
|
69
70
|
|
|
70
71
|
## The `decrypt()` scheme
|
|
@@ -84,3 +85,6 @@ Treat this as obfuscation only. Anything genuinely sensitive must not rely on it
|
|
|
84
85
|
- Specific-path map lines must precede host-only lines (see `forward.ini` header).
|
|
85
86
|
- New **encrypted** domains require a code change (`$encryptedDomains` in `index.php`), not just a `forward.ini` edit.
|
|
86
87
|
- `index.php` only runs on a map miss, so a bad/over-broad `forward.ini` host-only line can shadow paths you expected PHP to handle.
|
|
88
|
+
- **EB rejects bundles over 500 MB** ("The artifact is empty or exceeds the maximum size supported by AWS Elastic Beanstalk"), and the CodePipeline EB deploy ignores `.ebignore`. Never commit large static assets here — that broke every deploy from 2026-10-08 until demos moved to S3.
|
|
89
|
+
- **Git history still holds ~500 MB of old demo blobs.** Removing them needs a history rewrite + force-push — deferred; developer's call.
|
|
90
|
+
- **Removing a top-level folder:** first check no Route 53 name pointing at forward EB has a first label matching it (local directory handler). Checked for the 2026-10-09 removal; `togadesk.beta.agilantsolutions.com` is redirected by `forward.ini` first.
|
|
@@ -6,62 +6,60 @@ project: Forwarder
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09
|
|
9
|
+
updated: 2026-10-09
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- forward/.htaccess
|
|
13
|
-
-
|
|
13
|
+
- tools/_/app/design/s3.php
|
|
14
14
|
related:
|
|
15
15
|
- standalone/apps/forward/architecture.md
|
|
16
|
-
-
|
|
16
|
+
- 1.0/apps/tools/features/design-demo-admin.md
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
How `demo.togatech.com/<project>/vN/` static demos are hosted: S3 `togatech-demos` + CloudFront (prod account) since 2026-10-09, no longer `forward`; open for the AWS setup, cache rules, rollback to EB, or the Claude Design export prompt.
|
|
20
20
|
|
|
21
21
|
## Summary
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Self-contained static HTML demos (mostly exported **Claude Design** pages) served at `https://demo.togatech.com/<project>` with versioned `/<project>/vN/`. Since **2026-10-09** (TRUE-82616) they live in S3 bucket **`togatech-demos`** behind CloudFront — **not** in the `forward` repo. URLs did not change. Publishing is done by [Design Demo Admin](../../../../1.0/apps/tools/features/design-demo-admin.md) in Tools.
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
**Key files / entry points:**
|
|
28
|
-
|
|
29
|
-
- `forward/.htaccess` — a guarded static-serving rule placed **before** the `forward.ini` RewriteMap rules.
|
|
30
|
-
- `forward/<name>/index.html` — one directory per demo at the repo root (e.g. `forward/togadesk/index.html`). Apache serves it directly.
|
|
31
|
-
- A `demo.togatech.com` CNAME pointed at the Forwarder Elastic Beanstalk environment.
|
|
32
|
-
|
|
33
|
-
The rule in `.htaccess`:
|
|
34
|
-
|
|
35
|
-
```apache
|
|
36
|
-
# Static demo hosting (e.g. demo.togatech.com/togadesk)
|
|
37
|
-
RewriteCond %{DOCUMENT_ROOT}/$1/index.html -f
|
|
38
|
-
RewriteRule ^([^/]+)/?$ /$1/index.html [L]
|
|
39
|
-
```
|
|
25
|
+
Why moved: the `forward` repo grew to 757 MB (toga-website alone 345 MB over 24 versions). CodePipeline `Forward-Production` (legacy account) failed every deploy from 2026-10-08 with *"The artifact is empty or exceeds the maximum size supported by AWS Elastic Beanstalk"* (EB 500 MB bundle cap), so new demos 404'd.
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
- The `RewriteCond … -f` guard means the rule only fires when that `index.html` actually exists, so it never interferes with the existing `forward.ini` redirect map; unmatched paths fall through to the map and then `index.php` exactly as before.
|
|
43
|
-
- It sits before the RewriteMap block on purpose, so a real demo file always wins over a map lookup.
|
|
27
|
+
## How it works
|
|
44
28
|
|
|
45
|
-
|
|
29
|
+
All in the **PROD** account (654654170868).
|
|
46
30
|
|
|
47
|
-
|
|
31
|
+
- **S3 `togatech-demos`** (us-west-2): private, Block Public Access, BucketOwnerEnforced, SSE-S3, versioning ON. Lifecycle: noncurrent versions expire after 90 days; incomplete multipart uploads abort after 7 days. Root `404.html`. Bucket policy allows only this distribution (`AWS:SourceArn`).
|
|
32
|
+
- **CloudFront `E2XN9C2HZYWW3D`** (`dxd7aolawne76.cloudfront.net`): alias `demo.togatech.com`, existing ACM cert `*.togatech.com` (us-east-1), OAC `togatech-demos-oac`, PriceClass_100, redirect-to-https, cache policy **CachingOptimized**. Custom errors 403/404 → `/404.html` (status 404).
|
|
33
|
+
- **CloudFront Function `demo-togatech-dir-index`** (viewer-request): `/x/` → `/x/index.html`; extension-less `/x` → 301 to `/x/` (so the stub's relative `./vN/` links resolve).
|
|
34
|
+
- **Route 53** zone `togatech.com` (prod): `demo.togatech.com` = A + AAAA alias → the distribution (was a CNAME to `agilant-forward-prod`).
|
|
35
|
+
- **Cache-Control per object** (set by the admin tool): `vN/index.html` `public, max-age=86400`; stub `<project>/index.html`, `project.json`, `tabs.json` `public, max-age=60`. A normal publish needs no invalidation; hard delete invalidates `/<project>/*` and `/<project>`.
|
|
36
|
+
- **Migration (done):** 24 projects (folders with `project.json`) + `tabs.json` = 153 files / 728 MB copied with the same keys, checked byte-for-byte (sha256) through CloudFront, then DNS cutover, then removed from `forward`.
|
|
48
37
|
|
|
49
|
-
|
|
38
|
+
### Rollback demo.togatech.com to forward EB
|
|
39
|
+
Only works if the demo files are put back in `forward` and it deploys under 500 MB.
|
|
40
|
+
1. Restore the demo folders + `tabs.json` into `forward` (copy from S3 or from git history) and confirm the EB bundle is under 500 MB.
|
|
41
|
+
2. Deploy `forward` via `Forward-Production` and confirm it succeeds.
|
|
42
|
+
3. In Route 53 zone `togatech.com` (prod), reverse the change batch: delete the A + AAAA alias records for `demo.togatech.com`, create the CNAME → `agilant-forward-prod` EB host.
|
|
43
|
+
4. Confirm `https://demo.togatech.com/<project>` loads.
|
|
50
44
|
|
|
51
|
-
|
|
45
|
+
### Exporting a Claude Design page
|
|
46
|
+
Prompt to give inside the Claude Design conversation (also in the admin's "Copy Claude Design prompt"):
|
|
52
47
|
|
|
53
48
|
> Package this design as a single, fully self-contained HTML file I can host anywhere, and give it to me as a downloadable file (not a copy/paste code block). Requirements: one file with no build step or local assets; inline all CSS and JS; embed images/fonts as `data:` URIs or absolute `https://` URLs (no root-relative `/...` or relative `./...` paths, and no `<base>` tag); remove anything tied to the editing environment (no sidebar/chat refs, no live data calls back to the session — replace dynamic data with static placeholder content); it must render when opened directly in a browser; set a sensible `<title>` and viewport meta. Then save it as a downloadable `.html` file and give me the download link.
|
|
54
49
|
|
|
55
|
-
Before hosting,
|
|
50
|
+
Before hosting, check: no `<base>` tag, no root-relative/relative asset paths, no `fetch`/XHR to a live backend, renders when double-clicked. Public CDN dependencies (Font Awesome, Tailwind CDN, Google Fonts) are fine but need internet.
|
|
56
51
|
|
|
57
52
|
## Gotchas
|
|
58
|
-
|
|
59
|
-
- **
|
|
60
|
-
- **
|
|
61
|
-
- **
|
|
62
|
-
-
|
|
53
|
+
- **Never use the managed cache policy `UseOriginCacheControlHeaders` with an S3 origin.** It forwards the `Host` header, so S3 returns NotFound for every key. Use `CachingOptimized`.
|
|
54
|
+
- **EB has a 500 MB bundle cap**, and the CodePipeline EB deploy action **ignores `.ebignore`**. Large static assets do not belong in an EB-deployed repo.
|
|
55
|
+
- **"Unlist" frees no storage** — files stay. Only hard delete removes them (S3 keeps noncurrent versions 90 days).
|
|
56
|
+
- **Extension-less `/x` must 301 to `/x/`**, or the stub's relative `./vN/` links resolve against the root.
|
|
57
|
+
- `forward/.htaccess` still has the old static-demo rewrite + `DirectoryIndex` lines; harmless now that no demo folders are in the repo.
|
|
63
58
|
|
|
64
59
|
## Related
|
|
65
|
-
|
|
60
|
+
- [Design Demo Admin](../../../../1.0/apps/tools/features/design-demo-admin.md)
|
|
66
61
|
- [Forwarder Architecture](../architecture.md)
|
|
67
|
-
|
|
62
|
+
|
|
63
|
+
## Change history
|
|
64
|
+
- 2026-10-09 — Hosting moved from `forward` EB to S3 `togatech-demos` + CloudFront (prod); DNS cut over; demo folders removed from `forward` (TRUE-82616) (jcardinal)
|
|
65
|
+
- Earlier — demos served from `forward/<name>/index.html` via the `.htaccess` rule, versioned `/vN/` added by the admin tool (jcardinal)
|
package/package.json
CHANGED
|
@@ -50,7 +50,27 @@ function teamSessions(teamRepo) {
|
|
|
50
50
|
} catch { return []; }
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
const MAX_TITLE_LENGTH = 60;
|
|
54
|
+
|
|
55
|
+
function readStdin() {
|
|
56
|
+
try { return JSON.parse(fs.readFileSync(0, 'utf8') || '{}') || {}; } catch { return {}; }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Saved "<TICKET>: <short>" title from /start (start-helper.js set-title), or ''.
|
|
60
|
+
function savedTitle(sessionId) {
|
|
61
|
+
const sid = String(sessionId || '').replace(/[^A-Za-z0-9_-]/g, '');
|
|
62
|
+
if (!sid) return '';
|
|
63
|
+
const dir = process.env.TOGA_SESSIONS_DIR || path.join(require('os').homedir(), '.claude', 'toga-sessions');
|
|
64
|
+
try {
|
|
65
|
+
const t = JSON.parse(fs.readFileSync(path.join(dir, sid + '.json'), 'utf8')).sessionTitle;
|
|
66
|
+
return typeof t === 'string' ? t.trim().slice(0, MAX_TITLE_LENGTH) : '';
|
|
67
|
+
} catch { return ''; }
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
function main() {
|
|
71
|
+
const input = readStdin();
|
|
72
|
+
const lines = [];
|
|
73
|
+
const log = (line) => lines.push(line == null ? '' : String(line));
|
|
54
74
|
const cwd = process.cwd();
|
|
55
75
|
const now = new Date().toLocaleDateString('en-US', {
|
|
56
76
|
weekday: 'long',
|
|
@@ -59,19 +79,19 @@ function main() {
|
|
|
59
79
|
day: 'numeric',
|
|
60
80
|
});
|
|
61
81
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
log('');
|
|
83
|
+
log('================================================');
|
|
84
|
+
log(' TOGA Team Knowledge — ' + now);
|
|
85
|
+
log('================================================');
|
|
86
|
+
log('');
|
|
87
|
+
log('Tip of the day:');
|
|
88
|
+
log(' ' + getTip());
|
|
89
|
+
log('');
|
|
70
90
|
|
|
71
91
|
if (checkUnfinishedSession(cwd)) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
92
|
+
log('Unfinished session detected.');
|
|
93
|
+
log(' Run /session-resume latest to reload your previous session state.');
|
|
94
|
+
log('');
|
|
75
95
|
}
|
|
76
96
|
|
|
77
97
|
// Surface team-shared sessions (best-effort, time-boxed — never blocks startup).
|
|
@@ -80,16 +100,21 @@ function main() {
|
|
|
80
100
|
if (sessions.length) {
|
|
81
101
|
const newest = sessions[0];
|
|
82
102
|
const fresh = sessions.filter(s => !s.stale).length;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
103
|
+
log(`${sessions.length} team session(s) available${fresh < sessions.length ? ` (${sessions.length - fresh} stale)` : ''}.`);
|
|
104
|
+
log(` Newest: ${newest.id} by ${newest.author || 'unknown'} — "${newest.title || ''}"`);
|
|
105
|
+
log(' Run /session-resume <id> to continue one, or /session-resume list to see all.');
|
|
106
|
+
log('');
|
|
87
107
|
}
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
log('Ready. Run /start TRUE-1234 (your ClickUp ticket id) to prime context for your work session.');
|
|
110
|
+
log('');
|
|
91
111
|
|
|
112
|
+
const out = { hookEventName: 'SessionStart', additionalContext: lines.join('\n') };
|
|
113
|
+
// Never override a name the developer set.
|
|
114
|
+
const title = String(input.session_title || '').trim() ? '' : savedTitle(input.session_id);
|
|
115
|
+
if (title) out.sessionTitle = title;
|
|
116
|
+
process.stdout.write(JSON.stringify({ hookSpecificOutput: out }) + '\n');
|
|
92
117
|
process.exit(0);
|
|
93
118
|
}
|
|
94
119
|
|
|
95
|
-
main();
|
|
120
|
+
try { main(); } catch { process.exit(0); }
|
package/skills/start/SKILL.md
CHANGED
|
@@ -94,6 +94,18 @@ customId is not the session ticket, the session file is missing, the internal id
|
|
|
94
94
|
or the url is not `https://app.clickup.com/...`. On a refusal, show the error and stop. On
|
|
95
95
|
`{ok:true,...}` the gate moves to the primer stage.
|
|
96
96
|
|
|
97
|
+
Then name the session. Make a short title: `<T>: <2-4 plain words from the ticket title>`
|
|
98
|
+
(e.g. `TRUE-82605: ClickUp workflow`, `TRUE-81379: Supply filter speed`). Drop "Client - "
|
|
99
|
+
prefix words only if needed to stay short; never "TOGa". Max 60 characters. Save it:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
node ".../start-helper.js" set-title --session <sid> --title "<T>: <short>"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Then tell the developer in one line: "Paste this to name the session: `/rename <title>`".
|
|
106
|
+
The SessionStart hook also offers it as the session title on resume/compact, only when the
|
|
107
|
+
developer has not named the session.
|
|
108
|
+
|
|
97
109
|
## Step 4 — Ticket repos and branches
|
|
98
110
|
|
|
99
111
|
1. **Repo list from the ticket's `application` values:**
|
|
@@ -231,6 +243,7 @@ kickoff Step 6: planner -> implement -> reviewers; auto-review rules apply):
|
|
|
231
243
|
|
|
232
244
|
```
|
|
233
245
|
Ticket: <T> — <title> (<status>, <list>)
|
|
246
|
+
Session: <session title> (paste: /rename <session title>)
|
|
234
247
|
Apps: <repos> (core: <core>)
|
|
235
248
|
Clients: <clients>
|
|
236
249
|
Task type: <type>
|
|
@@ -63,6 +63,8 @@ const GIT_NETWORK_TIMEOUT_MS = 120000;
|
|
|
63
63
|
const MAX_ERROR_CHARS = 500;
|
|
64
64
|
const TICKET_RE = /^[A-Za-z][A-Za-z0-9]*-\d+$/;
|
|
65
65
|
const KB_REMOTE_RE = /(agilantsolutions|togatech)\/claude(\.git)?\/?$/i;
|
|
66
|
+
const MAX_TITLE_LENGTH = 60;
|
|
67
|
+
const CONTROL_CHARS_RE = /[\u0000-\u001f\u007f]+/g;
|
|
66
68
|
const DEFAULT_BRANCH_GUESSES = ['_production', 'main', 'master', '_main'];
|
|
67
69
|
|
|
68
70
|
/* ---------------- args + output ---------------- */
|
|
@@ -342,6 +344,22 @@ function cmdVerify(args) {
|
|
|
342
344
|
print(Object.assign({ ok: true }, s));
|
|
343
345
|
}
|
|
344
346
|
|
|
347
|
+
/* Session display title "<TICKET>: <short>". The SessionStart hook offers it as sessionTitle. */
|
|
348
|
+
function cmdSetTitle(args) {
|
|
349
|
+
const r = requireSession(args);
|
|
350
|
+
if (!r) return;
|
|
351
|
+
if (typeof args.title !== 'string') return fail('missing --title "<TICKET>: <short title>"');
|
|
352
|
+
const title = args.title.replace(CONTROL_CHARS_RE, ' ').replace(/\s+/g, ' ').trim();
|
|
353
|
+
const prefix = String(r.s.ticket || '').toUpperCase() + ': ';
|
|
354
|
+
if (!r.s.ticket || !title.toUpperCase().startsWith(prefix) || title.length <= prefix.length) {
|
|
355
|
+
return fail('title must start with "' + prefix + '" and have a short name after it');
|
|
356
|
+
}
|
|
357
|
+
if (title.length > MAX_TITLE_LENGTH) return fail('title is longer than ' + MAX_TITLE_LENGTH + ' characters');
|
|
358
|
+
r.s.sessionTitle = prefix + title.slice(prefix.length);
|
|
359
|
+
writeSession(r.sid, r.s);
|
|
360
|
+
print({ ok: true, sessionId: r.sid, ticket: r.s.ticket, sessionTitle: r.s.sessionTitle });
|
|
361
|
+
}
|
|
362
|
+
|
|
345
363
|
/* Secret scan for text Claude is about to post (PR body, PM comment). Prints hit TYPES only. */
|
|
346
364
|
function cmdScan(args) {
|
|
347
365
|
if (typeof args.file !== 'string') { fail('missing --file <path>'); process.exitCode = SCAN_ERROR_EXIT; return; }
|
|
@@ -525,8 +543,9 @@ function main() {
|
|
|
525
543
|
case 'repos-with-work': return cmdReposWithWork(args);
|
|
526
544
|
case 'create-branch': return cmdCreateBranch(args);
|
|
527
545
|
case 'scan': return cmdScan(args);
|
|
546
|
+
case 'set-title': return cmdSetTitle(args);
|
|
528
547
|
default:
|
|
529
|
-
return fail('usage: start-helper.js <get|verify|branch-check|create-branch|override-branch|latest-for|repos-with-work|scan> [--flags]');
|
|
548
|
+
return fail('usage: start-helper.js <get|verify|branch-check|create-branch|override-branch|set-title|latest-for|repos-with-work|scan> [--flags]');
|
|
530
549
|
}
|
|
531
550
|
}
|
|
532
551
|
|