toga-ai 1.0.95 → 1.0.96
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/CONVENTIONS.md +39 -0
- package/knowledge/clients/compass-canada/profile.md +6 -0
- package/knowledge/clients/compass-usa/profile.md +6 -0
- package/knowledge/clients/elite/profile.md +3 -0
- package/knowledge/clients/nycdoe/profile.md +3 -0
- package/knowledge/clients/office-depot/profile.md +2 -0
- package/knowledge/clients/prudential/profile.md +4 -0
- package/knowledge/clients/rate/profile.md +4 -0
- package/knowledge/clients/tow-foundation/profile.md +2 -0
- package/knowledge.js +31 -6
- package/package.json +1 -1
- package/skills/capture/SKILL.md +26 -0
- package/skills/kickoff/SKILL.md +22 -2
package/knowledge/CONVENTIONS.md
CHANGED
|
@@ -58,6 +58,45 @@ The shared identity/topology of every repo. Maintained **only by the skills**.
|
|
|
58
58
|
(`_underscore` for 2.0, `library` for 1.0), plus any transitive `dependsOn`.
|
|
59
59
|
`node knowledge.js deps --repo=<repo>` resolves the full load order.
|
|
60
60
|
|
|
61
|
+
## Client app-scope (`apps:` on `profile.md`)
|
|
62
|
+
|
|
63
|
+
A single client's work routinely spans **several** repos/frameworks that are not obvious
|
|
64
|
+
from the client's name — e.g. Compass USA touches `_underscore`, `api2`, `toga2-supply`,
|
|
65
|
+
`worker` (1.0), and `dbchanges2`. A developer (or a teammate who only says *"I'm fixing a
|
|
66
|
+
Compass bug"*) should **not** have to know or list those. So each client's `profile.md`
|
|
67
|
+
declares its in-scope repos in an **`apps:`** frontmatter list:
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
---
|
|
71
|
+
title: Compass USA
|
|
72
|
+
framework: "2.0"
|
|
73
|
+
apps: # repos auto-loaded whenever this client is mentioned
|
|
74
|
+
- _underscore
|
|
75
|
+
- api2
|
|
76
|
+
- toga2-supply
|
|
77
|
+
- worker
|
|
78
|
+
- dbchanges2
|
|
79
|
+
client: compass-usa
|
|
80
|
+
type: profile
|
|
81
|
+
...
|
|
82
|
+
---
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **Auto-load.** `node knowledge.js kickoff-preflight --client=<slug>` unions a client's
|
|
86
|
+
`apps` into the chosen repos, so they are loaded **in full** (architecture + matched
|
|
87
|
+
features) even when the developer names no repos. The command works with `--client` alone.
|
|
88
|
+
The preflight output's `clientScope` block reports which repos came from the client vs. were
|
|
89
|
+
named by the developer. List a `core` repo (e.g. `dbchanges2`, `_underscore`) in `apps` when
|
|
90
|
+
its **full** doc matters for that client's work — otherwise core repos load only as summaries,
|
|
91
|
+
and detail-only rules (e.g. dbchanges2's mandatory `YYYY-MM-DD<letter>` filename suffix) can
|
|
92
|
+
be missed.
|
|
93
|
+
- **Every entry must be a real, registered repo** — `validate` fails on an unknown `apps` repo.
|
|
94
|
+
- **Auto-maintenance (capture's job — see the capture skill).** When a session's work for a
|
|
95
|
+
client touches a repo **not** in that client's `apps`, `capture` **adds it automatically** and
|
|
96
|
+
notes it — a teammate never has to tell us "also load app X for this client." Conversely, a
|
|
97
|
+
developer may explicitly ask to **add** or **remove** a repo from a client's scope, and that
|
|
98
|
+
instruction wins. Removal is the only way a repo leaves `apps`; detection only ever adds.
|
|
99
|
+
|
|
61
100
|
## Frontmatter (every doc)
|
|
62
101
|
|
|
63
102
|
```yaml
|
package/knowledge.js
CHANGED
|
@@ -161,13 +161,14 @@ function cmdManifest() {
|
|
|
161
161
|
if (fs.existsSync(clientsDir)) {
|
|
162
162
|
for (const slug of fs.readdirSync(clientsDir, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name)) {
|
|
163
163
|
const profile = path.join(clientsDir, slug, 'profile.md');
|
|
164
|
-
let title = slug, framework = '';
|
|
164
|
+
let title = slug, framework = '', apps = [];
|
|
165
165
|
if (fs.existsSync(profile)) {
|
|
166
166
|
const { data } = parseFrontmatter(fs.readFileSync(profile, 'utf8'));
|
|
167
167
|
title = data.title || slug;
|
|
168
168
|
framework = data.framework || '';
|
|
169
|
+
apps = Array.isArray(data.apps) ? data.apps : [];
|
|
169
170
|
}
|
|
170
|
-
clients.push({ slug, title, framework });
|
|
171
|
+
clients.push({ slug, title, framework, apps });
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
console.log(JSON.stringify({ repos, clients }));
|
|
@@ -198,9 +199,23 @@ function extractSection(body, heading) {
|
|
|
198
199
|
|
|
199
200
|
function cmdPreflight(args) {
|
|
200
201
|
const registry = loadRegistry();
|
|
201
|
-
const
|
|
202
|
+
const devChosen = String(args.repos || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
203
|
+
// Client-scoped apps: when a client is named, auto-include the repos listed in its
|
|
204
|
+
// profile.md `apps:` so a teammate who only mentions the client still gets every
|
|
205
|
+
// app/framework that client's work spans — without having to name them. Unknown
|
|
206
|
+
// repo names are ignored (validate flags them separately).
|
|
207
|
+
const slug = args.client && args.client !== 'shared' && args.client !== true ? String(args.client) : null;
|
|
208
|
+
let clientApps = [];
|
|
209
|
+
if (slug) {
|
|
210
|
+
const profilePath = path.join(ROOT, 'clients', slug, 'profile.md');
|
|
211
|
+
if (fs.existsSync(profilePath)) {
|
|
212
|
+
const { data } = parseFrontmatter(fs.readFileSync(profilePath, 'utf8'));
|
|
213
|
+
clientApps = (Array.isArray(data.apps) ? data.apps : []).filter(r => registry.find(x => x.repo === r));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const chosen = [...new Set([...devChosen, ...clientApps])];
|
|
202
217
|
if (!chosen.length) {
|
|
203
|
-
console.log(JSON.stringify({ error: 'PREFLIGHT: --repos=<repo,repo>
|
|
218
|
+
console.log(JSON.stringify({ error: 'PREFLIGHT: provide --repos=<repo,repo> and/or --client=<slug> whose profile lists apps' }));
|
|
204
219
|
process.exitCode = 1;
|
|
205
220
|
return;
|
|
206
221
|
}
|
|
@@ -266,7 +281,6 @@ function cmdPreflight(args) {
|
|
|
266
281
|
|
|
267
282
|
// client docs (profile + features/workflows filtered to involved frameworks)
|
|
268
283
|
let client = null;
|
|
269
|
-
const slug = args.client && args.client !== 'shared' && args.client !== true ? String(args.client) : null;
|
|
270
284
|
if (slug) {
|
|
271
285
|
const clientDir = path.join(ROOT, 'clients', slug);
|
|
272
286
|
const cDocs = docs.filter(d => d.file.startsWith(clientDir + path.sep));
|
|
@@ -306,7 +320,11 @@ function cmdPreflight(args) {
|
|
|
306
320
|
for (const d of client.docs) reads.push({ label: 'client-doc', path: d.path, exists: true, title: d.title });
|
|
307
321
|
}
|
|
308
322
|
|
|
309
|
-
|
|
323
|
+
// clientScope tells the kickoff skill which repos were auto-pulled from the client's
|
|
324
|
+
// profile.md `apps:` (vs. named by the developer) so it can show "loaded because you
|
|
325
|
+
// mentioned <client>" and the dev can still add/remove.
|
|
326
|
+
const clientScope = { client: slug, fromClient: clientApps, fromDev: devChosen };
|
|
327
|
+
console.log(JSON.stringify({ frameworks, loadSet, unknown, clientScope, repos: repoOut, standards, client, reads }));
|
|
310
328
|
}
|
|
311
329
|
|
|
312
330
|
/* ------------------------------------------------------------------ */
|
|
@@ -635,6 +653,13 @@ function cmdValidate() {
|
|
|
635
653
|
if (entry && d.data.project !== entry.project) { fail(`${d.rel}: project "${d.data.project}" != registry "${entry.project}"`); ok = false; }
|
|
636
654
|
}
|
|
637
655
|
}
|
|
656
|
+
// client profile `apps:` must reference real, registered repos (the client-scope
|
|
657
|
+
// load-set the kickoff skill auto-loads when this client is mentioned).
|
|
658
|
+
if (d.data.type === 'profile' && Array.isArray(d.data.apps)) {
|
|
659
|
+
for (const app of d.data.apps) {
|
|
660
|
+
if (!registry.find(r => r.repo === app)) { fail(`${d.rel}: apps lists unknown repo "${app}"`); ok = false; }
|
|
661
|
+
}
|
|
662
|
+
}
|
|
638
663
|
if (ELEVATED_TYPES.includes(d.data.type)) elevated.push(d.rel);
|
|
639
664
|
for (const s of scanSecrets(fs.readFileSync(d.file, 'utf8'))) {
|
|
640
665
|
fail(`${d.rel}:${s.line}: possible secret committed — ${s.reason}; redact it (reference the config/constant location, never paste credential values into knowledge docs)`);
|
package/package.json
CHANGED
package/skills/capture/SKILL.md
CHANGED
|
@@ -189,6 +189,30 @@ config key, the file) but not its value. `validate` (run by Step 6's publish and
|
|
|
189
189
|
pre-commit hook) scans for credential literals and **fails the publish** if it finds one, so
|
|
190
190
|
a leaked secret blocks the whole capture until redacted. Reference, never reproduce.
|
|
191
191
|
|
|
192
|
+
## Step 5b — Maintain the client's app-scope (`apps:` on `profile.md`)
|
|
193
|
+
|
|
194
|
+
When this session's work was **for a specific client**, keep that client's `apps` list (the
|
|
195
|
+
repos auto-loaded whenever the client is mentioned — see `CONVENTIONS.md` → *Client app-scope*)
|
|
196
|
+
in sync. **This is automatic — a teammate must never have to tell us "also load app X for this
|
|
197
|
+
client."**
|
|
198
|
+
|
|
199
|
+
1. **Detect.** Collect every repo this session actually touched (from the files changed and the
|
|
200
|
+
`files:` you wrote on docs this capture). Read the client's `clients/<slug>/profile.md`
|
|
201
|
+
`apps:` list.
|
|
202
|
+
2. **Auto-add.** For any touched repo **not** already in `apps`, add it to the profile's `apps`
|
|
203
|
+
list (and bump `updated`). Detection only ever **adds** — never silently removes. Briefly note
|
|
204
|
+
each addition in the Step 7 report (e.g. "Added `dbchanges2` to Compass USA scope"). Add a
|
|
205
|
+
`core` repo (e.g. `dbchanges2`, `_underscore`) when its **full** doc — not just its summary —
|
|
206
|
+
mattered to the work, so the next session loads it in full.
|
|
207
|
+
3. **Honor explicit instructions (these win).** If the developer said to **add** or **remove** a
|
|
208
|
+
repo from the client's scope this session, apply it: add even if untouched, or remove even if
|
|
209
|
+
touched. Removal happens **only** on an explicit request.
|
|
210
|
+
4. **New client with no `apps` yet?** Seed it from the repos this session touched.
|
|
211
|
+
|
|
212
|
+
The `apps` change is an ordinary `profile.md` edit (not elevated) — apply it without asking,
|
|
213
|
+
then it publishes with everything else in Step 6. `validate` rejects an `apps` entry that is not
|
|
214
|
+
a registered repo, so only add real repos (run New-repo onboarding first if needed).
|
|
215
|
+
|
|
192
216
|
## Step 6 — Publish (validate → index → mirror → rebase-push, one command)
|
|
193
217
|
|
|
194
218
|
Finish every capture with the deterministic publisher — **do not** run validate/index/git
|
|
@@ -366,6 +390,8 @@ any client docs:
|
|
|
366
390
|
---
|
|
367
391
|
title: "<Formal Client Name>" # the client's formal name IS the title; slug is only the folder
|
|
368
392
|
framework: "<1.0|2.0>"
|
|
393
|
+
apps: # repos auto-loaded when this client is mentioned (seed from
|
|
394
|
+
- <repo> # the repos this session touched; maintained per Step 5b)
|
|
369
395
|
project: <Project>
|
|
370
396
|
client: <slug>
|
|
371
397
|
type: profile
|
package/skills/kickoff/SKILL.md
CHANGED
|
@@ -138,7 +138,7 @@ When resolved, if there was no `team-repo-path` memory, **write one** (memory ty
|
|
|
138
138
|
## Step 2 — Interview the developer (ask the work questions FIRST)
|
|
139
139
|
|
|
140
140
|
Get the pickable lists in **one call**: `node "<TEAM_REPO>/knowledge.js" manifest` returns
|
|
141
|
-
minified JSON `{repos:[{repo,project,framework,role}], clients:[{slug,title,framework}]}`.
|
|
141
|
+
minified JSON `{repos:[{repo,project,framework,role}], clients:[{slug,title,framework,apps}]}`.
|
|
142
142
|
**Do not read `registry.json` or any `profile.md` to build these lists.** Ask all questions
|
|
143
143
|
in one message:
|
|
144
144
|
|
|
@@ -147,12 +147,25 @@ in one message:
|
|
|
147
147
|
3. **Repo(s) / project(s)** — list EVERY manifest repo for the chosen framework(s).
|
|
148
148
|
**Multi-select (checkbox), not single choice** — a session often spans several repos. If
|
|
149
149
|
they name a repo not listed, run **New-repo onboarding** (below) for each before continuing.
|
|
150
|
+
**This question is OPTIONAL when a client is chosen** — that client's `apps` are auto-loaded
|
|
151
|
+
(see the client-scope note below), so a developer who only knows the client need not pick
|
|
152
|
+
repos. Use it to let them *add* repos beyond the client's scope.
|
|
150
153
|
4. **Client** — list EVERY manifest client by its **`title`** (the formal name, not the slug),
|
|
151
154
|
plus "shared / internal" and "a new client".
|
|
152
155
|
5. **What are you building or changing?** (a sentence — used to pick relevant feature docs.)
|
|
153
156
|
|
|
154
157
|
If any answer is unclear, ask again. Do not guess framework, repo, project, or client.
|
|
155
158
|
|
|
159
|
+
**Client app-scope (do this automatically — never make the developer enumerate apps).** Each
|
|
160
|
+
client in the manifest carries an `apps` list — the repos/frameworks that client's work spans
|
|
161
|
+
(e.g. Compass USA → `_underscore, api2, toga2-supply, worker, dbchanges2`). When a client is
|
|
162
|
+
chosen, **all of its `apps` are loaded for you by preflight** even if the developer named no
|
|
163
|
+
repos — so "I'm fixing a Compass bug" is enough. After loading, briefly tell the developer
|
|
164
|
+
which repos were pulled in because of the client (from preflight's `clientScope.fromClient`),
|
|
165
|
+
and that they can say **"also add `<repo>`"** or **"drop `<repo>` for this session"** to adjust.
|
|
166
|
+
A session-only add/remove does **not** change the client's stored scope; a request to change it
|
|
167
|
+
**for this client going forward** is a profile edit captured at `/capture` time.
|
|
168
|
+
|
|
156
169
|
**Presenting q3 and q4:** do NOT use the option-chip / radio control — it caps at ~4 options
|
|
157
170
|
and silently truncates a 7-repo / 5-client list. Render the full list as a numbered markdown
|
|
158
171
|
list and have the developer reply with numbers (e.g. "1, 3, 5") — no cap, supports
|
|
@@ -172,8 +185,15 @@ node "<TEAM_REPO>/knowledge.js" kickoff-preflight \
|
|
|
172
185
|
--q=<2-4 keywords from the developer's task sentence>
|
|
173
186
|
```
|
|
174
187
|
|
|
188
|
+
`--repos` is **optional when `--client` is given** — the client's `apps` are auto-unioned into
|
|
189
|
+
the load-set, so `--client=<slug>` alone resolves the full set. Pass `--repos` only to add repos
|
|
190
|
+
the developer named beyond the client's scope (or for a shared/internal session with no client).
|
|
191
|
+
|
|
175
192
|
It prints minified JSON:
|
|
176
|
-
- `loadSet` — framework core(s) + chosen repos + transitive `dependsOn`,
|
|
193
|
+
- `loadSet` — framework core(s) + chosen repos + the client's `apps` + transitive `dependsOn`,
|
|
194
|
+
in load order.
|
|
195
|
+
- `clientScope` — `{client, fromClient[], fromDev[]}`: which repos were auto-loaded from the
|
|
196
|
+
client's profile vs. named by the developer. Surface `fromClient` to the developer in Step 5.
|
|
177
197
|
- `repos[]` — each with `architecture` `{path,exists}`, `role`, `chosen`, an inline
|
|
178
198
|
`summary` (for non-chosen repos), and matched `features[]` (features returned only for the
|
|
179
199
|
repos the developer chose, not deps).
|