toga-ai 1.0.0
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/.claude/settings.json +119 -0
- package/.claude-plugin/marketplace.json +87 -0
- package/.claude-plugin/plugin.json +22 -0
- package/CLAUDE.md +161 -0
- package/README.md +72 -0
- package/agents/framework-pattern-checker.md +67 -0
- package/agents/harness-optimizer.md +102 -0
- package/agents/knowledge-writer.md +62 -0
- package/agents/php-build-resolver.md +51 -0
- package/agents/php-reviewer.md +51 -0
- package/agents/planner.md +88 -0
- package/agents/session-capture.md +101 -0
- package/agents/sql-reviewer.md +67 -0
- package/contexts/dev.md +43 -0
- package/contexts/research.md +49 -0
- package/contexts/review.md +37 -0
- package/knowledge/1.0/apps/library/INDEX.md +5 -0
- package/knowledge/1.0/apps/library/architecture.md +105 -0
- package/knowledge/1.0/apps/worker/INDEX.md +5 -0
- package/knowledge/1.0/apps/worker/architecture.md +223 -0
- package/knowledge/1.0/standards/backend-php.md +450 -0
- package/knowledge/2.0/apps/_underscore/INDEX.md +6 -0
- package/knowledge/2.0/apps/_underscore/architecture.md +183 -0
- package/knowledge/2.0/apps/_underscore/features/recursive-item-fulfillments.md +111 -0
- package/knowledge/2.0/apps/api2/INDEX.md +5 -0
- package/knowledge/2.0/apps/api2/architecture.md +162 -0
- package/knowledge/2.0/apps/worker2/INDEX.md +6 -0
- package/knowledge/2.0/apps/worker2/architecture.md +127 -0
- package/knowledge/2.0/apps/worker2/features/creating-worker-actions.md +135 -0
- package/knowledge/2.0/standards/backend-php.md +710 -0
- package/knowledge/CONVENTIONS.md +117 -0
- package/knowledge/INDEX.md +19 -0
- package/knowledge/clients/.gitkeep +0 -0
- package/knowledge/registry.json +7 -0
- package/knowledge.js +384 -0
- package/mcp-configs/README.md +72 -0
- package/mcp-configs/mcp-servers.json +23 -0
- package/package.json +50 -0
- package/rules/README.md +53 -0
- package/rules/common/coding-style.md +123 -0
- package/rules/common/git-workflow.md +72 -0
- package/rules/common/security.md +118 -0
- package/rules/common/testing.md +74 -0
- package/rules/php/app-framework.md +104 -0
- package/rules/php/underscore-framework.md +111 -0
- package/scripts/harness.js +605 -0
- package/scripts/hooks/evaluate-session.js +55 -0
- package/scripts/hooks/post-edit-validate.js +102 -0
- package/scripts/hooks/session-end.js +13 -0
- package/scripts/hooks/session-start.js +57 -0
- package/scripts/install.js +611 -0
- package/scripts/pre-commit +46 -0
- package/skills/capture/SKILL.md +294 -0
- package/skills/code-review/SKILL.md +140 -0
- package/skills/create-elastic-beanstalk/SKILL.md +217 -0
- package/skills/harness-audit/SKILL.md +152 -0
- package/skills/kickoff/SKILL.md +151 -0
- package/skills/php-patterns/SKILL.md +296 -0
- package/skills/session-resume/SKILL.md +156 -0
- package/skills/session-save/SKILL.md +158 -0
- package/skills/sync-team-skills/SKILL.md +87 -0
- package/sync-skills.js +71 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: capture
|
|
3
|
+
description: End-of-session knowledge writer for TOGA Technology projects. Run this at the END of a coding session to record what you worked on into the team `claude` knowledge base. It figures out what changed this session, matches it against existing knowledge, and proposes create/update/retire changes for one-tap approval — then writes them, keeping registry.json, frontmatter, and INDEX files consistent. Trigger whenever the user says "capture", "wrap up", "save my work to the knowledge base", "I'm done — record this", or finishes a feature and wants it documented.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Capture — record this session's work into the team knowledge base
|
|
7
|
+
|
|
8
|
+
You are the **only** editor of the knowledge base; developers never hand-edit it. So you
|
|
9
|
+
must keep everything consistent and **ask whenever a required fact is missing — never
|
|
10
|
+
assume**. Nothing is written until the developer approves your proposal.
|
|
11
|
+
|
|
12
|
+
### Contribution model
|
|
13
|
+
|
|
14
|
+
Every `/capture` automatically pushes new knowledge back to the team git repo after
|
|
15
|
+
validate + index succeed:
|
|
16
|
+
|
|
17
|
+
- Teammates get it on their **next `npx toga-harness`** run, which pulls the git repo
|
|
18
|
+
before installing — so the two-way loop is:
|
|
19
|
+
1. `npx toga-harness` seeds knowledge from the npm bundle
|
|
20
|
+
2. `/capture` grows the knowledge base and pushes it back to git
|
|
21
|
+
3. Teammates run `npx toga-harness` → git pull delivers the new docs → they get it
|
|
22
|
+
- The push is **non-blocking**: if offline or credentials are missing, capture is still
|
|
23
|
+
successful and you report a reminder to push manually.
|
|
24
|
+
- Only `knowledge/` files are ever staged — source code is never touched.
|
|
25
|
+
|
|
26
|
+
## Core data model (same as `knowledge/CONVENTIONS.md` and the `kickoff` skill)
|
|
27
|
+
|
|
28
|
+
- Knowledge lives in the **`claude` repo** under `knowledge/`, partitioned by framework
|
|
29
|
+
(`1.0/` = `App_`/`library`, `2.0/` = `_underscore`).
|
|
30
|
+
- `apps/<repo>/architecture.md` + `features/*.md`; `standards/*.md`; top-level
|
|
31
|
+
`clients/<client>/{profile.md, features/, workflows/}`. App folders keyed by **repo name**.
|
|
32
|
+
- `registry.json` maps **repo ↔ project ↔ framework ↔ role(core|app) ↔ dependsOn**.
|
|
33
|
+
- Doc types: `feature`, `client-feature`, `workflow`, `architecture` (elevated),
|
|
34
|
+
`standard` (elevated).
|
|
35
|
+
|
|
36
|
+
## Step 1 — Resolve the team-repo (`claude`) path
|
|
37
|
+
|
|
38
|
+
Same as kickoff: Claude memory `team-repo-path` → env `CLAUDE_TEAM_REPO` → auto-discover
|
|
39
|
+
(`./claude`, `../claude`, `../../claude`, walk up for `knowledge/INDEX.md`) → ask. Persist a
|
|
40
|
+
`team-repo-path` memory if missing. Call the helper as `node "<TEAM_REPO>/knowledge.js" …`.
|
|
41
|
+
|
|
42
|
+
## Step 2 — Determine framework / repo / project (ask if unknown)
|
|
43
|
+
|
|
44
|
+
1. From the **live session context**, identify what was built, changed, debugged, or decided,
|
|
45
|
+
and which source files were touched.
|
|
46
|
+
2. Identify the **repo** from the working path: match against your remembered `repo-path-<repo>`
|
|
47
|
+
memories; otherwise use the repo folder under a `C:\WWW\1.0` (→ framework 1.0) or
|
|
48
|
+
`C:\WWW\2.0` (→ framework 2.0) tree **as a suggestion to confirm, not a fact to assume**.
|
|
49
|
+
3. Look the repo up in `registry.json` to get its `framework` and `project`.
|
|
50
|
+
- If the repo is **not registered**, run **New-repo onboarding** (below) — ask everything.
|
|
51
|
+
- If you cannot confidently determine the repo/framework/project/client, **ask**.
|
|
52
|
+
|
|
53
|
+
## Step 3 — Match against existing knowledge
|
|
54
|
+
|
|
55
|
+
For each distinct thing worked on, search for a doc that already covers it:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
node "<TEAM_REPO>/knowledge.js" search --framework=<fw> --repo=<repo> --file=<touched-path>
|
|
59
|
+
node "<TEAM_REPO>/knowledge.js" search --framework=<fw> --repo=<repo> --q=<keywords>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Classify each item as **CREATE** (no doc yet), **UPDATE** (a doc exists and needs new
|
|
63
|
+
content / a new `files:` entry / a gotcha), or **DEPRECATE/DELETE** (the doc describes
|
|
64
|
+
something now removed or obsolete).
|
|
65
|
+
|
|
66
|
+
## Step 4 — Build the proposal (one-tap approval)
|
|
67
|
+
|
|
68
|
+
Show a short, scannable, framework+repo-qualified plan, then the proposed content/diffs.
|
|
69
|
+
Mark any `architecture` or `standard` change as **⚠ ELEVATED** (senior-owned). Example:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
CREATE 2.0/apps/worker2/features/clickup-deploy.md (feature)
|
|
73
|
+
UPDATE 2.0/apps/worker2/features/creating-worker-actions.md (add files: + gotcha)
|
|
74
|
+
⚠ ELEVATED 2.0/standards/backend-php.md (new cron-locking rule)
|
|
75
|
+
DELETE 2.0/apps/worker2/features/old-thing.md (removed this session)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Ask the developer to approve. They may accept all, accept some, or edit. **Write nothing
|
|
79
|
+
until they confirm.** If a needed placement detail is ambiguous (which client? new vs.
|
|
80
|
+
update?), ask before proposing.
|
|
81
|
+
|
|
82
|
+
## Step 5 — Apply the approved changes
|
|
83
|
+
|
|
84
|
+
Write/update/remove docs using the templates below. For every doc set/refresh frontmatter:
|
|
85
|
+
`framework`, `repo` (omit for pure client docs), `project`, `client` (`shared` for app docs;
|
|
86
|
+
a real slug for client docs), `type`, `status`, `updated` (today's date), `owners`, `files`,
|
|
87
|
+
`related`. Add `related:` cross-links (a `client-feature` must link back to the shared
|
|
88
|
+
`apps/<repo>/features/<base>.md`). If a new repo was onboarded, append it to `registry.json`.
|
|
89
|
+
|
|
90
|
+
## Step 6 — Re-index and guard integrity (mandatory)
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
node "<TEAM_REPO>/knowledge.js" index
|
|
94
|
+
node "<TEAM_REPO>/knowledge.js" validate
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`index` rebuilds all `INDEX.md` files from frontmatter (never hand-edit them). If `validate`
|
|
98
|
+
reports any error, **stop and fix it** before telling the developer you're done — do not
|
|
99
|
+
report success on an inconsistent knowledge base.
|
|
100
|
+
|
|
101
|
+
## Step 7 — Auto-push to share with teammates
|
|
102
|
+
|
|
103
|
+
After validate + index succeed, stage and push only `knowledge/` changes to a
|
|
104
|
+
**personal branch** so a maintainer can review before merging to main.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Derive branch name from git user — e.g. knowledge/john-smith
|
|
108
|
+
BRANCH="knowledge/$(git -C "<TEAM_REPO>" config user.name | tr ' ' '-' | tr '[:upper:]' '[:lower:]')"
|
|
109
|
+
|
|
110
|
+
# Stage only knowledge/ — never stages source code
|
|
111
|
+
git -C "<TEAM_REPO>" add knowledge/
|
|
112
|
+
git -C "<TEAM_REPO>" diff --cached --quiet || git -C "<TEAM_REPO>" commit -m "knowledge: <brief description of what was captured>"
|
|
113
|
+
|
|
114
|
+
# Push to personal branch — does not touch main
|
|
115
|
+
git -C "<TEAM_REPO>" push origin HEAD:"$BRANCH"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Replace `<brief description of what was captured>` with a one-line summary of the docs
|
|
119
|
+
written this capture (e.g. `"knowledge: add clickup-deploy feature doc for worker2"`).
|
|
120
|
+
|
|
121
|
+
**If push succeeds:** report
|
|
122
|
+
> "✓ Knowledge pushed to branch `knowledge/<your-name>` — a maintainer will review and merge to main"
|
|
123
|
+
|
|
124
|
+
**If push fails** (offline, no credentials, diverged branch, or any other error): do
|
|
125
|
+
**NOT** error out. Report:
|
|
126
|
+
> "⚠ Could not push — run `git -C <TEAM_REPO> push origin HEAD:knowledge/<your-name>` when ready"
|
|
127
|
+
and still consider the capture session successful.
|
|
128
|
+
|
|
129
|
+
Never stage anything outside `knowledge/`. If `git diff --cached` would include files
|
|
130
|
+
outside `knowledge/`, abort the commit and warn: "Unexpected staged files outside
|
|
131
|
+
`knowledge/` — push skipped. Review and push manually."
|
|
132
|
+
|
|
133
|
+
**Devs do not need write access to main.** They only need permission to push to
|
|
134
|
+
`knowledge/*` branches. A maintainer reviews the branch and merges via PR.
|
|
135
|
+
|
|
136
|
+
## Step 8 — Report
|
|
137
|
+
|
|
138
|
+
List exactly what changed (clickable relative paths), and note any ⚠ ELEVATED docs the
|
|
139
|
+
developer approved so they remember those touched senior-owned standards/architecture.
|
|
140
|
+
If Step 7 pushed successfully, include the push confirmation in the report.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Doc templates (write inline; no separate template files)
|
|
145
|
+
|
|
146
|
+
### feature / client-feature
|
|
147
|
+
```markdown
|
|
148
|
+
---
|
|
149
|
+
title: <Title>
|
|
150
|
+
framework: "<1.0|2.0>"
|
|
151
|
+
repo: <repo> # omit for a pure client doc not tied to one repo
|
|
152
|
+
project: <Project>
|
|
153
|
+
client: <shared|client-slug>
|
|
154
|
+
type: <feature|client-feature>
|
|
155
|
+
status: active
|
|
156
|
+
updated: <YYYY-MM-DD>
|
|
157
|
+
owners: [<who>]
|
|
158
|
+
files:
|
|
159
|
+
- <on-disk/path/one>
|
|
160
|
+
related: [] # client-feature: link the shared apps/<repo>/features/<base>.md
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Summary
|
|
164
|
+
What it does and why.
|
|
165
|
+
|
|
166
|
+
## Key files / entry points
|
|
167
|
+
The main files and how control flows.
|
|
168
|
+
|
|
169
|
+
## How it works
|
|
170
|
+
Step-by-step.
|
|
171
|
+
|
|
172
|
+
## Data model
|
|
173
|
+
Tables/columns touched (if any).
|
|
174
|
+
|
|
175
|
+
## Client variations
|
|
176
|
+
How behavior differs per client (or "none — uniform").
|
|
177
|
+
|
|
178
|
+
## Gotchas / known issues
|
|
179
|
+
The non-obvious things future-you will trip on.
|
|
180
|
+
|
|
181
|
+
## Related docs
|
|
182
|
+
Links.
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### workflow (client business process)
|
|
186
|
+
```markdown
|
|
187
|
+
---
|
|
188
|
+
title: <Workflow Name>
|
|
189
|
+
framework: "<1.0|2.0>"
|
|
190
|
+
project: <Project>
|
|
191
|
+
client: <client-slug>
|
|
192
|
+
type: workflow
|
|
193
|
+
status: active
|
|
194
|
+
updated: <YYYY-MM-DD>
|
|
195
|
+
owners: [<who>]
|
|
196
|
+
files: []
|
|
197
|
+
related: []
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Summary
|
|
201
|
+
The business process and who it serves.
|
|
202
|
+
|
|
203
|
+
## Steps
|
|
204
|
+
1. …
|
|
205
|
+
|
|
206
|
+
## Systems involved
|
|
207
|
+
Apps/repos, integrations, data.
|
|
208
|
+
|
|
209
|
+
## Edge cases & escalation
|
|
210
|
+
What can go wrong and who handles it.
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### architecture (one per repo) — ⚠ ELEVATED
|
|
214
|
+
```markdown
|
|
215
|
+
---
|
|
216
|
+
title: <Repo> Architecture
|
|
217
|
+
framework: "<1.0|2.0>"
|
|
218
|
+
repo: <repo>
|
|
219
|
+
project: <Project>
|
|
220
|
+
client: shared
|
|
221
|
+
type: architecture
|
|
222
|
+
status: active
|
|
223
|
+
updated: <YYYY-MM-DD>
|
|
224
|
+
owners: [<who>]
|
|
225
|
+
files: [<key paths>]
|
|
226
|
+
related: []
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Summary
|
|
230
|
+
## <system sections: components, data, flows, key decisions>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### standard (coding standard) — ⚠ ELEVATED
|
|
234
|
+
```markdown
|
|
235
|
+
---
|
|
236
|
+
title: <Backend|Frontend|…> Standards
|
|
237
|
+
framework: "<1.0|2.0>"
|
|
238
|
+
project: <core-project, e.g. _Underscore or Library>
|
|
239
|
+
client: shared
|
|
240
|
+
type: standard
|
|
241
|
+
status: active
|
|
242
|
+
updated: <YYYY-MM-DD>
|
|
243
|
+
owners: [<who>]
|
|
244
|
+
files: []
|
|
245
|
+
related: []
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## <topic sections: naming, structure, DB access, errors, testing, security…>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## New-repo onboarding (repo not in `registry.json`)
|
|
252
|
+
|
|
253
|
+
Ask **all** — assume nothing (a heuristic may seed a default to confirm):
|
|
254
|
+
1. Repo name (exact on-disk name). 2. Framework (1.0/2.0). 3. Project name.
|
|
255
|
+
4. Role (core or app). 5. `dependsOn` repos beyond the framework core (default none).
|
|
256
|
+
6. Local path on this machine (if not remembered → write a `repo-path-<repo>` memory).
|
|
257
|
+
|
|
258
|
+
Append the entry to `registry.json`, then continue. `validate` will confirm consistency.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### After Capture
|
|
263
|
+
|
|
264
|
+
Once you have completed a capture session and confirmed the changes are committed to
|
|
265
|
+
the knowledge base, suggest the following to the developer:
|
|
266
|
+
|
|
267
|
+
> "Session captured successfully. If you're done for today or may not finish this
|
|
268
|
+
> thread in one sitting, run `/session-save` to persist your session state. This
|
|
269
|
+
> records exactly what worked, what failed, what's pending, and your exact next step —
|
|
270
|
+
> so you can resume without losing context, even days later."
|
|
271
|
+
|
|
272
|
+
Provide the suggested command with a name hint:
|
|
273
|
+
```
|
|
274
|
+
/session-save <suggested-slug-based-on-what-was-worked-on>
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
For example, if the session was about "ClickUp deploy worker actions", suggest:
|
|
278
|
+
```
|
|
279
|
+
/session-save clickup-deploy-worker
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### PostToolUse hook auto-validation
|
|
283
|
+
|
|
284
|
+
The `.claude/settings.json` in this repo includes a `PostToolUse` hook that
|
|
285
|
+
automatically runs `node knowledge.js validate` after any file write to `knowledge/`.
|
|
286
|
+
This means:
|
|
287
|
+
|
|
288
|
+
- As each doc is written during the capture step, validation runs immediately.
|
|
289
|
+
- You will see inline output with any `ERROR:` or `OK` messages.
|
|
290
|
+
- If validation fails after a write, address the error before writing the next doc —
|
|
291
|
+
do not proceed to Step 6's explicit `validate` run while errors are outstanding.
|
|
292
|
+
- Step 6's explicit `node knowledge.js validate` call is still required as the final
|
|
293
|
+
gate — the hook fires per-write, but the explicit call confirms global consistency
|
|
294
|
+
after all writes are complete.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: PHP-focused code review for TOGA Technology projects. Reviews for SQL injection, XSS, framework pattern violations (App_ vs _underscore), N+1 queries, and coding standards. Trigger when the user says "code-review", "review this file", "check my code", or "review [filename]".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Review Skill
|
|
7
|
+
|
|
8
|
+
PHP-focused code review skill for TOGA Technology projects. Reviews for security vulnerabilities, framework pattern violations, SQL quality, and style issues.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/code-review [file-path]
|
|
14
|
+
/code-review src/Controllers/Orders.php
|
|
15
|
+
/code-review # reviews most recently modified PHP files
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Behavior
|
|
21
|
+
|
|
22
|
+
### Step 1 — Identify files to review
|
|
23
|
+
|
|
24
|
+
If a file path is provided: read that file.
|
|
25
|
+
|
|
26
|
+
If no file path is provided: find the most recently modified PHP files in the session by checking tool-use history for Write/Edit operations on `.php` files. Review up to 3 files.
|
|
27
|
+
|
|
28
|
+
If no PHP files were modified this session: ask the user which file to review.
|
|
29
|
+
|
|
30
|
+
### Step 2 — Detect framework
|
|
31
|
+
|
|
32
|
+
Read the file's class declarations:
|
|
33
|
+
- Class names starting with `App_` → Framework 1.0
|
|
34
|
+
- Class names with leading `_` or file under a `2.0` path → Framework 2.0
|
|
35
|
+
- Check `knowledge/registry.json` if still ambiguous
|
|
36
|
+
|
|
37
|
+
Load `knowledge/<fw>/standards/backend-php.md` for team-specific rules.
|
|
38
|
+
|
|
39
|
+
### Step 3 — Security scan (always run first)
|
|
40
|
+
|
|
41
|
+
Check every finding against a concrete failure mode. Only report issues where a specific exploit or data loss scenario can be described.
|
|
42
|
+
|
|
43
|
+
**Critical security issues to find:**
|
|
44
|
+
- SQL injection: user input (`$_GET`, `$_POST`, `$_COOKIE`, function params traceable to user input) concatenated/interpolated into SQL strings
|
|
45
|
+
- XSS: user-supplied data echoed or printed without `htmlspecialchars()`
|
|
46
|
+
- `eval()` or `exec()` on user-controlled strings
|
|
47
|
+
- Raw `$_GET`/`$_POST`/`$_COOKIE` used without validation
|
|
48
|
+
- Passwords stored in plaintext or hashed with MD5/SHA1
|
|
49
|
+
- Sensitive data (passwords, tokens, PII) in log statements
|
|
50
|
+
- File path construction from user input without allowlist
|
|
51
|
+
- Missing `WHERE` clause on `UPDATE` or `DELETE`
|
|
52
|
+
|
|
53
|
+
### Step 4 — Framework pattern scan
|
|
54
|
+
|
|
55
|
+
**For Framework 1.0 (App_):**
|
|
56
|
+
- Classes missing `App_` prefix
|
|
57
|
+
- Controllers not extending `App_Controller`
|
|
58
|
+
- Models not extending `App_Model`
|
|
59
|
+
- Direct model instantiation with `new` instead of `App_Registry::get()`
|
|
60
|
+
- `snake_case` public method names
|
|
61
|
+
|
|
62
|
+
**For Framework 2.0 (_underscore):**
|
|
63
|
+
- Workers not extending `_Worker` or missing `run()` method
|
|
64
|
+
- Workers called directly instead of via `_Queue::dispatch()`
|
|
65
|
+
- `api2` action methods not returning the standard `{success, data, errors}` envelope
|
|
66
|
+
- Missing error type distinction (retriable vs non-retriable) in workers
|
|
67
|
+
|
|
68
|
+
### Step 5 — SQL quality scan
|
|
69
|
+
|
|
70
|
+
- N+1: query inside a loop where the query uses a loop variable
|
|
71
|
+
- Unbounded `SELECT` without `LIMIT` on non-lookup tables
|
|
72
|
+
- `SELECT *` instead of named columns (INFO level)
|
|
73
|
+
- Deprecated: `mysql_query()`, `mysql_escape_string()`, `addslashes()` for SQL
|
|
74
|
+
|
|
75
|
+
### Step 6 — Style scan
|
|
76
|
+
|
|
77
|
+
Only flag style issues with a direct rule citation from `rules/common/coding-style.md`:
|
|
78
|
+
- Bare `catch` or catch-and-ignore
|
|
79
|
+
- Mutable state in catch block without log or rethrow
|
|
80
|
+
- Magic numbers (unexplained literals)
|
|
81
|
+
- Commented-out code
|
|
82
|
+
- Functions with 4+ positional parameters
|
|
83
|
+
- Missing return type declarations on public methods
|
|
84
|
+
|
|
85
|
+
### Step 7 — Confidence filter
|
|
86
|
+
|
|
87
|
+
Before including any finding in the output, confirm:
|
|
88
|
+
1. The exact file:line can be cited
|
|
89
|
+
2. The concrete failure mode (exploit, data loss, wrong behavior) can be described
|
|
90
|
+
3. It is not a style preference without a rule backing it
|
|
91
|
+
|
|
92
|
+
Remove any finding that fails this filter. It is better to report 2 real issues than 8 vague ones.
|
|
93
|
+
|
|
94
|
+
### Step 8 — Output
|
|
95
|
+
|
|
96
|
+
Build the findings table. Count by severity. Output the summary.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Output Format
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
## Code Review — src/Controllers/Orders.php
|
|
104
|
+
Framework: 2.0 (_underscore)
|
|
105
|
+
|
|
106
|
+
| File:Line | Severity | Issue | Fix |
|
|
107
|
+
|-----------|----------|-------|-----|
|
|
108
|
+
| Orders.php:34 | CRITICAL | SQL injection: `$_POST['status']` interpolated into query string | Use `_Db::select('orders', ['status' => $_POST['status']])` or PDO prepared statement |
|
|
109
|
+
| Orders.php:71 | CRITICAL | Missing WHERE on UPDATE: `_Db::update('orders', $data)` with no condition | Add condition array as third param: `_Db::update('orders', $data, ['id' => $orderId])` |
|
|
110
|
+
| Orders.php:102 | WARNING | Worker called directly: `$w = new _Worker_Notify(); $w->run()` | Dispatch via queue: `_Queue::dispatch('_Worker_Notify', $payload)` |
|
|
111
|
+
| Orders.php:118 | WARNING | N+1 query: `_Db::find('order_items', ['order_id' => $o->id])` inside foreach | Collect IDs, batch with IN clause outside loop |
|
|
112
|
+
| Orders.php:145 | INFO | `SELECT *` — retrieves all columns | Replace with explicit column list to reduce data transfer |
|
|
113
|
+
|
|
114
|
+
**Summary:** 2 critical, 2 warnings, 1 info
|
|
115
|
+
|
|
116
|
+
Critical issues must be fixed before merge.
|
|
117
|
+
Warnings should be fixed before merge.
|
|
118
|
+
Info items are optional improvements.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
If zero findings across all categories:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
## Code Review — src/Controllers/Orders.php
|
|
125
|
+
Framework: 2.0 (_underscore)
|
|
126
|
+
|
|
127
|
+
No issues found. All checks passed.
|
|
128
|
+
|
|
129
|
+
Checks run: SQL injection, XSS, eval/exec, input validation, framework patterns,
|
|
130
|
+
worker dispatch, API envelope, SQL quality, error handling, style rules.
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Notes
|
|
136
|
+
|
|
137
|
+
- Never invent findings to appear thorough. "No issues found" is a valid and useful result.
|
|
138
|
+
- If a file is too large to review completely in one pass, state which sections were reviewed and which were not.
|
|
139
|
+
- If the framework cannot be determined, state this and ask before proceeding.
|
|
140
|
+
- For files in the `library` or `_underscore` core repos: flag any change to a base class method signature as a WARNING with a note that dependent repos must be checked.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-elastic-beanstalk
|
|
3
|
+
description: Generates a ready-to-paste AWS CloudShell command that creates a new Elastic Beanstalk environment following True/Agilant conventions. Use this skill whenever the user wants to create, spin up, or provision a new Elastic Beanstalk environment, add an EB environment to an existing application, or asks for the CloudShell command to launch a beanstalk. Trigger immediately even if they just say "make me a new beanstalk for X" or "new EB env in the API app".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Elastic Beanstalk Environment
|
|
7
|
+
|
|
8
|
+
## What this skill does
|
|
9
|
+
|
|
10
|
+
You interview the user, then output a **single copy-paste CloudShell command** that creates a new
|
|
11
|
+
Elastic Beanstalk *environment* inside an *existing* EB *application*. The command must run as-is
|
|
12
|
+
when pasted into AWS CloudShell — no placeholders left behind, no editing required.
|
|
13
|
+
|
|
14
|
+
You do **not** run any AWS commands yourself (you are not in CloudShell). You gather the answers,
|
|
15
|
+
fill in the proven values below, and print the final command for the developer to paste.
|
|
16
|
+
|
|
17
|
+
## The core rule: PRODUCTION vs everything else
|
|
18
|
+
|
|
19
|
+
The value of the `ENVIRONMENT` variable decides almost everything. If the user's `ENVIRONMENT`
|
|
20
|
+
value is exactly `production`, use the **PRODUCTION profile**. Any other value
|
|
21
|
+
(`client-alpha`, `qa-beta`, `sandbox-dev`, etc.) uses the **NON-PROD profile**.
|
|
22
|
+
|
|
23
|
+
These two profiles live in **different AWS accounts** with different VPCs, subnets, roles, and
|
|
24
|
+
storage. The developer's CloudShell session must be logged into the matching account — always tell
|
|
25
|
+
them which account to confirm before they paste.
|
|
26
|
+
|
|
27
|
+
### PRODUCTION profile — AWS account `654654170868`
|
|
28
|
+
|
|
29
|
+
| Setting | Value |
|
|
30
|
+
|---|---|
|
|
31
|
+
| VPC | `vpc-08030daf1c2dad280` |
|
|
32
|
+
| Instance (private) subnets | `subnet-0e135d4093ab60b2f,subnet-0c6579c05a0f51ac6,subnet-096d9f8f0617949ef` |
|
|
33
|
+
| ELB (public) subnets | `subnet-016ceecffa8c484dd,subnet-0a03a9af3536ab3e2,subnet-0f4272cd10edeee8d` |
|
|
34
|
+
| Instance profile | `ElasticBeanstalk-EC2-Instance-Profile` |
|
|
35
|
+
| Service role | `arn:aws:iam::654654170868:role/service-role/aws-elasticbeanstalk-service-role` |
|
|
36
|
+
| Storage | `gp2` (SSD), 10 GB |
|
|
37
|
+
| Load balancer | **Dedicated** application LB (`LoadBalancerIsShared=false`), public-facing |
|
|
38
|
+
| Default instance type | `t4g.large` |
|
|
39
|
+
| Default instance count | Min = Max = `2` |
|
|
40
|
+
|
|
41
|
+
### NON-PROD profile — AWS account `975050298201`
|
|
42
|
+
|
|
43
|
+
| Setting | Value |
|
|
44
|
+
|---|---|
|
|
45
|
+
| VPC | `vpc-0c5319e7df6f736c5` |
|
|
46
|
+
| Instance (private) subnets | `subnet-0d6acbaf19ee03fa9,subnet-0f5085f369274c305,subnet-0133a6098281928c7` (`true-test-private-web-a/b/c`) |
|
|
47
|
+
| Instance profile | `aws-elasticbeanstalk-ec2-role` |
|
|
48
|
+
| Service role | `arn:aws:iam::975050298201:role/aws-elasticbeanstalk-service-role` |
|
|
49
|
+
| Storage | `standard` (magnetic), 10 GB |
|
|
50
|
+
| Load balancer | **Shared** application LB (`LoadBalancerIsShared=true`) — must pick one (see list) |
|
|
51
|
+
| Default instance type | `t4g.micro` |
|
|
52
|
+
| Default instance count | Min = Max = `1` |
|
|
53
|
+
|
|
54
|
+
> Non-prod uses a **shared** ALB, so do NOT set `ELBScheme`, `ELBSubnets`, or
|
|
55
|
+
> `AssociatePublicIpAddress`. Elastic Beanstalk rejects subnet config on a shared load balancer; it
|
|
56
|
+
> simply attaches a listener rule to the existing ALB.
|
|
57
|
+
|
|
58
|
+
## Always-true conventions (both profiles)
|
|
59
|
+
|
|
60
|
+
- **Platform:** PHP on Amazon Linux 2023, Apache proxy, **arm64** (`SupportedArchitectures=arm64`,
|
|
61
|
+
`t4g` family). Default solution stack: `64bit Amazon Linux 2023 v4.13.1 running PHP 8.5`.
|
|
62
|
+
- **Key pair:** `true-aws-infrastructure-production`.
|
|
63
|
+
- **Deployments:** `AllAtOnce` — **always**, no exceptions.
|
|
64
|
+
- **No real autoscaling:** AWS won't let you disable it, so neuter the trigger — watch
|
|
65
|
+
`CPUUtilization` with `LowerThreshold=2000000` and `UpperThreshold=6000000` so it never breaches.
|
|
66
|
+
Always use `Unit=Percent`. Set Min = Max instance count so the group size is pinned.
|
|
67
|
+
- **Instances** always go in the **private** subnets; load balancers in **public** subnets.
|
|
68
|
+
- **On-Demand only** — always default to On-Demand instances (`EnableSpot=false`), never Spot.
|
|
69
|
+
|
|
70
|
+
## Interview the user (ask before generating anything)
|
|
71
|
+
|
|
72
|
+
Ask these together in one message. Don't generate the command until you have the answers.
|
|
73
|
+
|
|
74
|
+
1. **`ENVIRONMENT` value** — what string goes in the `ENVIRONMENT` variable? (e.g. `production`,
|
|
75
|
+
`client-delta`, `qa-hotfix`). This selects the profile. Treat exactly `production` as PRODUCTION.
|
|
76
|
+
|
|
77
|
+
2. **EB application** — which existing application? It must already exist (this skill never creates
|
|
78
|
+
the application). Known applications: `SAML`, `talos-backend`, `TOGA Technology`, `API`,
|
|
79
|
+
`TOGaCommerce`, `TOGaIQ`, `Worker`. If unsure, have them run:
|
|
80
|
+
`aws elasticbeanstalk describe-applications --query 'Applications[].ApplicationName' --output table`
|
|
81
|
+
|
|
82
|
+
3. **Environment name** — the name for the new environment (e.g. `api-client-delta`). Lowercase,
|
|
83
|
+
hyphenated, ≤ 40 chars.
|
|
84
|
+
|
|
85
|
+
4. **Shared ALB (NON-PROD only)** — which shared ALB should it attach to? Ask for the ALB **name**;
|
|
86
|
+
the generated command resolves the ARN at runtime so it can't go stale. Known shared ALBs in
|
|
87
|
+
account `975050298201` (`describe-load-balancers` to refresh):
|
|
88
|
+
`true-alb-sandbox-dev`, `true-alb-sandbox-client`, `true-alb-client-alpha`,
|
|
89
|
+
`true-alb-client-beta`, `true-alb-client-gamma`, `true-alb-qa-alpha`, `true-alb-qa-beta`,
|
|
90
|
+
`true-alb-qa-gamma`, `true-alb-qa-hotfix`, `true-alb-qa-task`, `true-alb-qc-alpha`,
|
|
91
|
+
`true-alb-qc-beta`, `true-alb-qc-gamma`, `true-alb-qc-hotfix`, `true-alb-qc-performance`,
|
|
92
|
+
`true-alb-qc-security`, `true-alb-qc-task`.
|
|
93
|
+
|
|
94
|
+
5. **Instance type** — confirm or change. Default `t4g.micro` (non-prod) / `t4g.large` (prod). Must
|
|
95
|
+
be arm64 `t4g` family; `t4g.micro` is the floor.
|
|
96
|
+
|
|
97
|
+
6. **Instance count** — confirm or change. Default Min = Max = `1` (non-prod) / `2` (prod). It's
|
|
98
|
+
pinned (no scaling), so Min and Max are set equal.
|
|
99
|
+
|
|
100
|
+
7. **PHP version** *(optional)* — default `8.5`. Other current AL2023 options: `8.1`, `8.2`, `8.3`,
|
|
101
|
+
`8.4`. Maps to solution stack `64bit Amazon Linux 2023 v4.13.1 running PHP <ver>`.
|
|
102
|
+
|
|
103
|
+
8. **CNAME prefix** *(optional)* — defaults to the environment name.
|
|
104
|
+
|
|
105
|
+
## Generating the command
|
|
106
|
+
|
|
107
|
+
Substitute every value at generation time so the printed command has no placeholders. Build the
|
|
108
|
+
option settings as a JSON heredoc, then call `create-environment`.
|
|
109
|
+
|
|
110
|
+
Before the command, print a one-line **account check** banner, e.g.:
|
|
111
|
+
> ⚠️ Confirm your CloudShell is logged into account **975050298201** (non-prod) before pasting.
|
|
112
|
+
|
|
113
|
+
### NON-PROD template (shared ALB)
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Target account: 975050298201 | App: <APP> | Env: <ENV_NAME> | ENVIRONMENT=<ENV_VALUE>
|
|
117
|
+
SHARED_ALB_ARN=$(aws elbv2 describe-load-balancers \
|
|
118
|
+
--names "<SHARED_ALB_NAME>" \
|
|
119
|
+
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
|
|
120
|
+
|
|
121
|
+
cat > /tmp/eb-options.json <<EOF
|
|
122
|
+
[
|
|
123
|
+
{"Namespace":"aws:autoscaling:asg","OptionName":"MinSize","Value":"<COUNT>"},
|
|
124
|
+
{"Namespace":"aws:autoscaling:asg","OptionName":"MaxSize","Value":"<COUNT>"},
|
|
125
|
+
{"Namespace":"aws:ec2:instances","OptionName":"InstanceTypes","Value":"<INSTANCE_TYPE>"},
|
|
126
|
+
{"Namespace":"aws:ec2:instances","OptionName":"SupportedArchitectures","Value":"arm64"},
|
|
127
|
+
{"Namespace":"aws:ec2:instances","OptionName":"EnableSpot","Value":"false"},
|
|
128
|
+
{"Namespace":"aws:autoscaling:launchconfiguration","OptionName":"IamInstanceProfile","Value":"aws-elasticbeanstalk-ec2-role"},
|
|
129
|
+
{"Namespace":"aws:autoscaling:launchconfiguration","OptionName":"EC2KeyName","Value":"true-aws-infrastructure-production"},
|
|
130
|
+
{"Namespace":"aws:autoscaling:launchconfiguration","OptionName":"RootVolumeType","Value":"standard"},
|
|
131
|
+
{"Namespace":"aws:autoscaling:launchconfiguration","OptionName":"RootVolumeSize","Value":"10"},
|
|
132
|
+
{"Namespace":"aws:autoscaling:launchconfiguration","OptionName":"DisableIMDSv1","Value":"true"},
|
|
133
|
+
{"Namespace":"aws:ec2:vpc","OptionName":"VPCId","Value":"vpc-0c5319e7df6f736c5"},
|
|
134
|
+
{"Namespace":"aws:ec2:vpc","OptionName":"Subnets","Value":"subnet-0d6acbaf19ee03fa9,subnet-0f5085f369274c305,subnet-0133a6098281928c7"},
|
|
135
|
+
{"Namespace":"aws:elasticbeanstalk:environment","OptionName":"LoadBalancerType","Value":"application"},
|
|
136
|
+
{"Namespace":"aws:elasticbeanstalk:environment","OptionName":"LoadBalancerIsShared","Value":"true"},
|
|
137
|
+
{"Namespace":"aws:elasticbeanstalk:environment","OptionName":"ServiceRole","Value":"arn:aws:iam::975050298201:role/aws-elasticbeanstalk-service-role"},
|
|
138
|
+
{"Namespace":"aws:elbv2:loadbalancer","OptionName":"SharedLoadBalancer","Value":"${SHARED_ALB_ARN}"},
|
|
139
|
+
{"Namespace":"aws:elasticbeanstalk:command","OptionName":"DeploymentPolicy","Value":"AllAtOnce"},
|
|
140
|
+
{"Namespace":"aws:elasticbeanstalk:environment:proxy","OptionName":"ProxyServer","Value":"apache"},
|
|
141
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"MeasureName","Value":"CPUUtilization"},
|
|
142
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"Statistic","Value":"Average"},
|
|
143
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"Unit","Value":"Percent"},
|
|
144
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"LowerThreshold","Value":"2000000"},
|
|
145
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"UpperThreshold","Value":"6000000"},
|
|
146
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"LowerBreachScaleIncrement","Value":"-1"},
|
|
147
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"UpperBreachScaleIncrement","Value":"1"},
|
|
148
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"BreachDuration","Value":"5"},
|
|
149
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"Period","Value":"5"},
|
|
150
|
+
{"Namespace":"aws:autoscaling:trigger","OptionName":"EvaluationPeriods","Value":"1"},
|
|
151
|
+
{"Namespace":"aws:elasticbeanstalk:application:environment","OptionName":"ENVIRONMENT","Value":"<ENV_VALUE>"}
|
|
152
|
+
]
|
|
153
|
+
EOF
|
|
154
|
+
|
|
155
|
+
aws elasticbeanstalk create-environment \
|
|
156
|
+
--application-name "<APP>" \
|
|
157
|
+
--environment-name "<ENV_NAME>" \
|
|
158
|
+
--cname-prefix "<CNAME_PREFIX>" \
|
|
159
|
+
--solution-stack-name "64bit Amazon Linux 2023 v4.13.1 running PHP <PHP_VER>" \
|
|
160
|
+
--option-settings file:///tmp/eb-options.json
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### PRODUCTION template (dedicated public ALB)
|
|
164
|
+
|
|
165
|
+
Same shape, but in account `654654170868`, with the dedicated-LB differences:
|
|
166
|
+
|
|
167
|
+
- Drop the `SHARED_ALB_ARN` lookup and the `aws:elbv2:loadbalancer SharedLoadBalancer` line.
|
|
168
|
+
- `LoadBalancerIsShared` → `false`.
|
|
169
|
+
- `IamInstanceProfile` → `ElasticBeanstalk-EC2-Instance-Profile`.
|
|
170
|
+
- `ServiceRole` → `arn:aws:iam::654654170868:role/service-role/aws-elasticbeanstalk-service-role`.
|
|
171
|
+
- `RootVolumeType` → `gp2`.
|
|
172
|
+
- `VPCId` → `vpc-08030daf1c2dad280`; `Subnets` → the three prod private subnets.
|
|
173
|
+
- Add the public-facing LB lines:
|
|
174
|
+
```
|
|
175
|
+
{"Namespace":"aws:ec2:vpc","OptionName":"ELBScheme","Value":"public"},
|
|
176
|
+
{"Namespace":"aws:ec2:vpc","OptionName":"ELBSubnets","Value":"subnet-016ceecffa8c484dd,subnet-0a03a9af3536ab3e2,subnet-0f4272cd10edeee8d"},
|
|
177
|
+
{"Namespace":"aws:ec2:vpc","OptionName":"AssociatePublicIpAddress","Value":"false"},
|
|
178
|
+
```
|
|
179
|
+
- Defaults: `InstanceTypes=t4g.large`, count `2`.
|
|
180
|
+
- Everything else identical: AllAtOnce, `Unit=Percent` trigger 2000000–6000000, `EnableSpot=false`,
|
|
181
|
+
arm64, Apache, key pair, 10 GB root volume.
|
|
182
|
+
|
|
183
|
+
## After printing the command
|
|
184
|
+
|
|
185
|
+
Tell the developer, concisely:
|
|
186
|
+
|
|
187
|
+
1. **Confirm the account** — CloudShell must be in `654654170868` (prod) or `975050298201`
|
|
188
|
+
(non-prod). Paste fails or misbehaves if they're in the wrong account.
|
|
189
|
+
2. The application must already exist; this only creates the environment.
|
|
190
|
+
3. Watch it come up: `aws elasticbeanstalk describe-environments --environment-names "<ENV_NAME>" --query 'Environments[0].{Status:Status,Health:Health,CNAME:CNAME}' --output table`
|
|
191
|
+
4. If EB complains about a missing listener rule on a shared ALB, the env still creates; the rule is
|
|
192
|
+
added automatically once the environment is `Ready`.
|
|
193
|
+
|
|
194
|
+
## Reviewing / cloning an existing environment
|
|
195
|
+
|
|
196
|
+
To copy settings from a known-good environment, dump its full config (this is how this skill's
|
|
197
|
+
values were sourced):
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
aws elasticbeanstalk describe-configuration-settings \
|
|
201
|
+
--application-name "<APP>" --environment-name "<EXISTING_ENV>" \
|
|
202
|
+
--query 'ConfigurationSettings[0].OptionSettings[].{NS:Namespace,Opt:OptionName,Val:Value}' \
|
|
203
|
+
--output table
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
If the user is targeting an account other than the two above, ask them to run that command (plus
|
|
207
|
+
`aws ec2 describe-subnets` and `aws elbv2 describe-load-balancers`) and paste the output so you can
|
|
208
|
+
build a matching profile rather than guessing.
|
|
209
|
+
|
|
210
|
+
## Conventions baked in (don't silently change these)
|
|
211
|
+
|
|
212
|
+
- All-at-once deploys, arm64/t4g, Apache, private-subnet instances, public-subnet LBs, the
|
|
213
|
+
2000000–6000000 never-trigger CPU hack (`Unit=Percent`), pinned Min=Max instance count, the
|
|
214
|
+
`true-aws-infrastructure-production` key pair, 10 GB root volume (SSD `gp2` prod / magnetic
|
|
215
|
+
`standard` non-prod), On-Demand instances.
|
|
216
|
+
- If the user explicitly asks to deviate (e.g. enable Spot, change storage), honor it but call out
|
|
217
|
+
that it departs from the standard.
|