toga-ai 1.0.45 → 1.0.47
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.js +195 -3
- package/package.json +1 -1
- package/skills/capture/SKILL.md +21 -81
- package/skills/kickoff/SKILL.md +34 -18
package/knowledge.js
CHANGED
|
@@ -14,10 +14,13 @@
|
|
|
14
14
|
* node /path/to/claude/knowledge.js <command> [--flags]
|
|
15
15
|
*
|
|
16
16
|
* Commands:
|
|
17
|
-
* search
|
|
17
|
+
* search --framework= --repo= --project= --client= --file= --q=
|
|
18
18
|
* index
|
|
19
|
-
* deps
|
|
19
|
+
* deps --repo=<repo>
|
|
20
20
|
* validate
|
|
21
|
+
* manifest
|
|
22
|
+
* kickoff-preflight --repos=a,b,c --client=<slug> --layer=<...> --q=<...>
|
|
23
|
+
* publish --msg= --mirror= --branch=
|
|
21
24
|
*/
|
|
22
25
|
|
|
23
26
|
'use strict';
|
|
@@ -170,6 +173,193 @@ function cmdManifest() {
|
|
|
170
173
|
console.log(JSON.stringify({ repos, clients }));
|
|
171
174
|
}
|
|
172
175
|
|
|
176
|
+
/* ------------------------------------------------------------------ */
|
|
177
|
+
/* command: kickoff-preflight — one JSON bundle for kickoff Steps 3+4. */
|
|
178
|
+
/* Given the chosen repos (+client, +layer), resolves the dependency */
|
|
179
|
+
/* load-set, every doc to read (with exists flags), and an ordered */
|
|
180
|
+
/* `reads` list — so kickoff stops running deps/search/path-probing in */
|
|
181
|
+
/* context. Prints minified JSON. Flags: */
|
|
182
|
+
/* --repos=a,b,c (required) --client=<slug> --layer=<back-end| */
|
|
183
|
+
/* front-end|hybrid> --q=<keywords to match feature docs> */
|
|
184
|
+
/* ------------------------------------------------------------------ */
|
|
185
|
+
|
|
186
|
+
function cmdPreflight(args) {
|
|
187
|
+
const registry = loadRegistry();
|
|
188
|
+
const chosen = String(args.repos || '').split(',').map(s => s.trim()).filter(Boolean);
|
|
189
|
+
if (!chosen.length) {
|
|
190
|
+
console.log(JSON.stringify({ error: 'PREFLIGHT: --repos=<repo,repo> required' }));
|
|
191
|
+
process.exitCode = 1;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const layer = String(args.layer || 'hybrid').toLowerCase();
|
|
195
|
+
const q = args.q ? String(args.q).toLowerCase() : null;
|
|
196
|
+
const unknown = chosen.filter(r => !registry.find(x => x.repo === r));
|
|
197
|
+
|
|
198
|
+
// frameworks involved (from the chosen repos)
|
|
199
|
+
const fwSet = new Set();
|
|
200
|
+
for (const r of chosen) { const e = registry.find(x => x.repo === r); if (e) fwSet.add(e.framework); }
|
|
201
|
+
const frameworks = [...fwSet];
|
|
202
|
+
|
|
203
|
+
// dependency load-set: framework core(s) first, then transitive dependsOn, then chosen
|
|
204
|
+
const seen = new Set();
|
|
205
|
+
const loadSet = [];
|
|
206
|
+
const visit = (r) => {
|
|
207
|
+
const e = registry.find(x => x.repo === r);
|
|
208
|
+
if (!e) return;
|
|
209
|
+
for (const d of (e.dependsOn || [])) visit(d);
|
|
210
|
+
if (!seen.has(r)) { seen.add(r); loadSet.push(r); }
|
|
211
|
+
};
|
|
212
|
+
for (const fw of frameworks) for (const core of coreReposFor(registry, fw)) visit(core);
|
|
213
|
+
for (const r of chosen) visit(r);
|
|
214
|
+
|
|
215
|
+
const docs = allDocs();
|
|
216
|
+
const chosenSet = new Set(chosen);
|
|
217
|
+
const fileInfo = (rel) => ({ path: rel, exists: fs.existsSync(path.join(ROOT, rel.split('/').join(path.sep))) });
|
|
218
|
+
const matchQ = (d) => !q || (((d.data.title || '') + ' ' + (d.data.files || []).join(' ') + ' ' + d.body).toLowerCase().includes(q));
|
|
219
|
+
|
|
220
|
+
// per-repo inventory; feature docs only for explicitly chosen repos
|
|
221
|
+
const repoOut = loadSet.map(repo => {
|
|
222
|
+
const e = registry.find(x => x.repo === repo);
|
|
223
|
+
const fw = e ? e.framework : '';
|
|
224
|
+
const repoDir = path.join(ROOT, fw, 'apps', repo);
|
|
225
|
+
const isChosen = chosenSet.has(repo);
|
|
226
|
+
let features = [];
|
|
227
|
+
if (isChosen) {
|
|
228
|
+
features = docs
|
|
229
|
+
.filter(d => d.file.startsWith(repoDir + path.sep) && d.data.type !== 'architecture' && matchQ(d))
|
|
230
|
+
.map(d => ({ path: d.rel, title: d.data.title || '', files: (d.data.files || []).join(', ') }));
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
repo, framework: fw, project: e ? e.project : '', role: e ? e.role : '', chosen: isChosen,
|
|
234
|
+
architecture: fileInfo(`${fw}/apps/${repo}/architecture.md`),
|
|
235
|
+
features,
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// standards per involved framework, filtered by layer
|
|
240
|
+
const wantBackend = layer === 'back-end' || layer === 'backend' || layer === 'hybrid';
|
|
241
|
+
const wantFrontend = layer === 'front-end' || layer === 'frontend' || layer === 'hybrid';
|
|
242
|
+
const standards = [];
|
|
243
|
+
for (const fw of frameworks) {
|
|
244
|
+
if (wantBackend) standards.push({ ...fileInfo(`${fw}/standards/backend-php.md`), framework: fw });
|
|
245
|
+
if (wantFrontend) standards.push({ ...fileInfo(`${fw}/standards/frontend.md`), framework: fw });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// client docs (profile + features/workflows filtered to involved frameworks)
|
|
249
|
+
let client = null;
|
|
250
|
+
const slug = args.client && args.client !== 'shared' && args.client !== true ? String(args.client) : null;
|
|
251
|
+
if (slug) {
|
|
252
|
+
const clientDir = path.join(ROOT, 'clients', slug);
|
|
253
|
+
const cDocs = docs.filter(d => d.file.startsWith(clientDir + path.sep));
|
|
254
|
+
const profileDoc = cDocs.find(d => path.basename(d.file) === 'profile.md');
|
|
255
|
+
const cFeatures = cDocs
|
|
256
|
+
.filter(d => path.basename(d.file) !== 'profile.md')
|
|
257
|
+
.filter(d => !frameworks.length || !d.data.framework || frameworks.includes(d.data.framework))
|
|
258
|
+
.filter(matchQ)
|
|
259
|
+
.map(d => ({ path: d.rel, title: d.data.title || '', framework: d.data.framework || '', type: d.data.type || '' }));
|
|
260
|
+
client = {
|
|
261
|
+
slug,
|
|
262
|
+
title: (profileDoc && profileDoc.data.title) || slug,
|
|
263
|
+
profile: fileInfo(`clients/${slug}/profile.md`),
|
|
264
|
+
docs: cFeatures,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ordered read list — mirrors kickoff Step 4: core arch → repo arch+features → standards → client
|
|
269
|
+
const reads = [];
|
|
270
|
+
for (const r of repoOut.filter(r => r.role === 'core')) {
|
|
271
|
+
reads.push({ label: 'core-architecture', repo: r.repo, ...r.architecture });
|
|
272
|
+
for (const f of r.features) reads.push({ label: 'feature', repo: r.repo, path: f.path, exists: true, title: f.title });
|
|
273
|
+
}
|
|
274
|
+
for (const r of repoOut.filter(r => r.role !== 'core')) {
|
|
275
|
+
reads.push({ label: r.chosen ? 'repo-architecture' : 'dep-architecture', repo: r.repo, ...r.architecture });
|
|
276
|
+
for (const f of r.features) reads.push({ label: 'feature', repo: r.repo, path: f.path, exists: true, title: f.title });
|
|
277
|
+
}
|
|
278
|
+
for (const s of standards) reads.push({ label: 'standard', path: s.path, exists: s.exists });
|
|
279
|
+
if (client) {
|
|
280
|
+
reads.push({ label: 'client-profile', path: client.profile.path, exists: client.profile.exists });
|
|
281
|
+
for (const d of client.docs) reads.push({ label: 'client-doc', path: d.path, exists: true, title: d.title });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
console.log(JSON.stringify({ frameworks, loadSet, unknown, repos: repoOut, standards, client, reads }));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/* ------------------------------------------------------------------ */
|
|
288
|
+
/* command: publish — capture's deterministic finish (validate, index, */
|
|
289
|
+
/* optional mirror, commit knowledge/, rebase-before-push with retry). */
|
|
290
|
+
/* Replaces ~80 lines of skill prose + 6 model-orchestrated git calls. */
|
|
291
|
+
/* Flags: --msg=<commit msg> --mirror=<project .claude dir> --branch= */
|
|
292
|
+
/* Prints one PUBLISH: <STATUS> line the skill can branch on. */
|
|
293
|
+
/* ------------------------------------------------------------------ */
|
|
294
|
+
|
|
295
|
+
function copyDir(src, dest) {
|
|
296
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
297
|
+
for (const e of fs.readdirSync(src, { withFileTypes: true })) {
|
|
298
|
+
const s = path.join(src, e.name), d = path.join(dest, e.name);
|
|
299
|
+
if (e.isDirectory()) copyDir(s, d);
|
|
300
|
+
else fs.copyFileSync(s, d);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function cmdPublish(args) {
|
|
305
|
+
const cp = require('child_process');
|
|
306
|
+
const TEAM = __dirname;
|
|
307
|
+
const node = process.argv[0];
|
|
308
|
+
const self = __filename;
|
|
309
|
+
const branch = args.branch || '_main';
|
|
310
|
+
const run = (cmd) => cp.execSync(cmd, { cwd: TEAM, encoding: 'utf8', stdio: 'pipe' });
|
|
311
|
+
|
|
312
|
+
// 0. must be a git clone
|
|
313
|
+
try { run('git rev-parse --git-dir'); }
|
|
314
|
+
catch { console.log('PUBLISH: NOT_GIT — knowledge written locally only; run `npx toga-ai` to create a pushable clone'); return; }
|
|
315
|
+
|
|
316
|
+
// 1. validate (inherit output so any ERROR: lines are visible); abort on failure
|
|
317
|
+
try { cp.execSync(`"${node}" "${self}" validate`, { cwd: TEAM, stdio: 'inherit' }); }
|
|
318
|
+
catch { console.log('PUBLISH: VALIDATE_FAILED — fix the errors above; nothing pushed'); process.exitCode = 1; return; }
|
|
319
|
+
|
|
320
|
+
// 2. rebuild indexes
|
|
321
|
+
cp.execSync(`"${node}" "${self}" index`, { cwd: TEAM, stdio: 'inherit' });
|
|
322
|
+
|
|
323
|
+
// 3. optional mirror back to the project bundle (keeps local reads fresh)
|
|
324
|
+
if (args.mirror && typeof args.mirror === 'string') {
|
|
325
|
+
const dest = path.join(args.mirror, 'knowledge');
|
|
326
|
+
if (path.resolve(dest) !== path.resolve(ROOT)) { copyDir(ROOT, dest); console.log('PUBLISH: mirrored knowledge/ -> ' + dest); }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 4. stage knowledge/ ONLY; bail if anything else slipped in
|
|
330
|
+
run('git add knowledge/');
|
|
331
|
+
const staged = run('git diff --cached --name-only').split('\n').map(s => s.trim()).filter(Boolean);
|
|
332
|
+
if (staged.length === 0) { console.log('PUBLISH: NO_CHANGES — nothing new to push'); return; }
|
|
333
|
+
const outside = staged.filter(f => !f.startsWith('knowledge/'));
|
|
334
|
+
if (outside.length) { run('git reset -q'); console.log('PUBLISH: ABORT_OUTSIDE_KNOWLEDGE — refusing to commit non-knowledge files: ' + outside.join(', ')); process.exitCode = 1; return; }
|
|
335
|
+
|
|
336
|
+
// 5. commit
|
|
337
|
+
const msg = String(args.msg || 'knowledge: capture session updates').replace(/["\\]/g, "'").slice(0, 200);
|
|
338
|
+
run(`git commit -m "${msg}"`);
|
|
339
|
+
|
|
340
|
+
// 6. rebase-before-push, retrying the CI version-bump race up to 3x
|
|
341
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
342
|
+
try {
|
|
343
|
+
run('git fetch origin');
|
|
344
|
+
run(`git rebase origin/${branch}`);
|
|
345
|
+
run(`git push origin HEAD:${branch}`);
|
|
346
|
+
console.log(`PUBLISH: PUSHED to ${branch} — CI publishes a new npm version; teammates get it on \`npx toga-ai\``);
|
|
347
|
+
return;
|
|
348
|
+
} catch (e) {
|
|
349
|
+
let status = '';
|
|
350
|
+
try { status = run('git status --porcelain'); } catch { /* ignore */ }
|
|
351
|
+
if (/^(UU|AA|DD|AU|UA|DU|UD) /m.test(status)) {
|
|
352
|
+
try { run('git rebase --abort'); } catch { /* ignore */ }
|
|
353
|
+
console.log(`PUBLISH: CONFLICT — rebase onto origin/${branch} hit a real conflict; resolve manually, then re-run publish`);
|
|
354
|
+
process.exitCode = 1; return;
|
|
355
|
+
}
|
|
356
|
+
// else: almost certainly a fresh CI bump (non-fast-forward) — loop and retry
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
console.log(`PUBLISH: PUSH_FAILED after 3 attempts — run manually: git -C "${TEAM}" push origin HEAD:${branch}`);
|
|
360
|
+
process.exitCode = 1;
|
|
361
|
+
}
|
|
362
|
+
|
|
173
363
|
/* ------------------------------------------------------------------ */
|
|
174
364
|
/* command: deps */
|
|
175
365
|
/* ------------------------------------------------------------------ */
|
|
@@ -405,8 +595,10 @@ function main() {
|
|
|
405
595
|
case 'deps': return cmdDeps(args);
|
|
406
596
|
case 'validate': return cmdValidate();
|
|
407
597
|
case 'manifest': return cmdManifest();
|
|
598
|
+
case 'kickoff-preflight': return cmdPreflight(args);
|
|
599
|
+
case 'publish': return cmdPublish(args);
|
|
408
600
|
default:
|
|
409
|
-
console.log('Usage: node knowledge.js <search|index|deps|validate|manifest> [--flags]');
|
|
601
|
+
console.log('Usage: node knowledge.js <search|index|deps|validate|manifest|kickoff-preflight|publish> [--flags]');
|
|
410
602
|
process.exitCode = 1;
|
|
411
603
|
}
|
|
412
604
|
}
|
package/package.json
CHANGED
package/skills/capture/SKILL.md
CHANGED
|
@@ -53,8 +53,8 @@ done
|
|
|
53
53
|
**`~/toga-tech` must be probed first** — `npx toga-ai` runs `initLocalRepo` there, which
|
|
54
54
|
creates a proper git repo seeded from the bundle even when the remote is private.
|
|
55
55
|
|
|
56
|
-
**If `IS_GIT_CLONE` is false** (only `.claude/` bundle found):
|
|
57
|
-
|
|
56
|
+
**If `IS_GIT_CLONE` is false** (only `.claude/` bundle found): you can still write docs
|
|
57
|
+
locally (Step 5), but the Step 6 publish will report `NOT_GIT` instead of pushing. Warn:
|
|
58
58
|
> "⚠ No git clone found at standard paths. Docs will be written locally only.
|
|
59
59
|
> Run `npx toga-ai` first (it creates `~/toga-tech` as a pushable git repo),
|
|
60
60
|
> then re-run /capture to push to GitHub."
|
|
@@ -143,94 +143,34 @@ For a client `profile.md`, the `title:` **is** the client's formal name (e.g. "C
|
|
|
143
143
|
there is no separate name field; `validate` requires `title`. See New-client onboarding.
|
|
144
144
|
Add `related:` cross-links. Append new repos to `<TEAM_REPO>/knowledge/registry.json`.
|
|
145
145
|
|
|
146
|
-
## Step 6 —
|
|
146
|
+
## Step 6 — Publish (validate → index → mirror → rebase-push, one command)
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
If `validate` reports any ERROR — stop and fix before continuing.
|
|
154
|
-
|
|
155
|
-
After both pass, **mirror back** to keep the project's local reads fresh:
|
|
156
|
-
```bash
|
|
157
|
-
# Only when TEAM_REPO != <project>/.claude
|
|
158
|
-
cp -r "<TEAM_REPO>/knowledge/" "<project>/.claude/knowledge/"
|
|
159
|
-
```
|
|
160
|
-
Skip mirror-back if the paths are the same directory.
|
|
161
|
-
|
|
162
|
-
## Step 7 — Push (only when IS_GIT_CLONE is true)
|
|
163
|
-
|
|
164
|
-
**This push is pre-authorized and mandatory — never ask the developer to confirm it.**
|
|
165
|
-
Running `/capture` IS the authorization to publish. After validate + index pass and the
|
|
166
|
-
staged set is confined to `knowledge/`, commit and push automatically as the final step of
|
|
167
|
-
every capture. Do not pause, do not present a "ready to push?" prompt, do not offer to "leave
|
|
168
|
-
it staged locally." The only reasons to stop are the guardrails already listed below: nothing
|
|
169
|
-
new to push (Check B empty), files staged outside `knowledge/`, or a push that errors.
|
|
170
|
-
|
|
171
|
-
If `IS_GIT_CLONE` is false — skip this step entirely and show:
|
|
172
|
-
> "⚠ Docs written to `.claude/knowledge/` only (no git clone). Run `npx toga-ai`
|
|
173
|
-
> to create `~/toga-tech`, then re-run /capture to push."
|
|
174
|
-
|
|
175
|
-
Otherwise:
|
|
176
|
-
|
|
177
|
-
**Check A — origin URL correct?**
|
|
178
|
-
```bash
|
|
179
|
-
git -C "<TEAM_REPO>" remote get-url origin
|
|
180
|
-
```
|
|
181
|
-
If wrong or missing: `git -C "<TEAM_REPO>" remote set-url origin https://github.com/agilantsolutions/claude`
|
|
182
|
-
|
|
183
|
-
**Check B — anything new to push?**
|
|
184
|
-
```bash
|
|
185
|
-
git -C "<TEAM_REPO>" status --short knowledge/
|
|
186
|
-
```
|
|
187
|
-
If empty — report "No new knowledge to push" and exit cleanly.
|
|
188
|
-
|
|
189
|
-
**Commit:**
|
|
190
|
-
```bash
|
|
191
|
-
git -C "<TEAM_REPO>" add knowledge/
|
|
192
|
-
git -C "<TEAM_REPO>" diff --cached --quiet || \
|
|
193
|
-
git -C "<TEAM_REPO>" commit -m "knowledge: <one-line summary>"
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Never stage files outside `knowledge/`. If `git diff --cached` includes anything outside
|
|
197
|
-
`knowledge/`, abort and warn before committing.
|
|
198
|
-
|
|
199
|
-
**Sync, then push (rebase BEFORE pushing — this prevents the version-bump conflict):**
|
|
200
|
-
|
|
201
|
-
Every push to this branch triggers a CI job that commits a `chore: bump version [skip ci]`
|
|
202
|
-
back to the branch. So after *any* prior push (e.g. an earlier capture in the same session),
|
|
203
|
-
your local branch is one commit behind the remote, and a naive push is rejected as a
|
|
204
|
-
non-fast-forward. Rebase your fresh commit onto the remote **before** pushing — this pulls in
|
|
205
|
-
commits that already exist (no waiting on CI), so the push fast-forwards cleanly:
|
|
148
|
+
Finish every capture with the deterministic publisher — **do not** run validate/index/git
|
|
149
|
+
by hand. The push is **pre-authorized and mandatory**: running `/capture` IS the
|
|
150
|
+
authorization, so never ask "ready to push?".
|
|
206
151
|
|
|
207
152
|
```bash
|
|
208
|
-
|
|
209
|
-
git -C "<TEAM_REPO>" rebase origin/_main
|
|
210
|
-
git -C "<TEAM_REPO>" push origin HEAD:_main
|
|
153
|
+
node "<TEAM_REPO>/knowledge.js" publish --msg="knowledge: <one-line summary>" --mirror="<project>/.claude"
|
|
211
154
|
```
|
|
212
155
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
**If the push is still rejected** (a bump commit landed in the brief window between your
|
|
219
|
-
rebase and push), simply repeat `fetch` → `rebase` → `push`, up to 2 more times. This is an
|
|
220
|
-
expected occasional race, not an error — do not report it as a failure unless all retries are
|
|
221
|
-
exhausted.
|
|
222
|
-
|
|
223
|
-
**If push succeeds:**
|
|
224
|
-
> "✓ Pushed to `_main` — CI publishes new npm version. Teammates get it on `npx toga-ai`."
|
|
156
|
+
This validates, rebuilds indexes, mirrors `knowledge/` back to the project bundle, then
|
|
157
|
+
stages **only** `knowledge/`, commits, and rebase-pushes to `_main` (auto-retrying the CI
|
|
158
|
+
version-bump race). Omit `--mirror` if TEAM_REPO already *is* the project bundle. Read the
|
|
159
|
+
final `PUBLISH:` line and act on it:
|
|
225
160
|
|
|
226
|
-
|
|
227
|
-
|
|
161
|
+
- **`PUSHED`** — done. Report: "✓ Pushed to `_main` — teammates get it on `npx toga-ai`."
|
|
162
|
+
- **`NO_CHANGES`** — report "No new knowledge to push" and finish.
|
|
163
|
+
- **`VALIDATE_FAILED`** — fix the printed `ERROR:` lines, then re-run publish. Never push around it.
|
|
164
|
+
- **`CONFLICT`** — a real rebase conflict; resolve manually, then re-run publish.
|
|
165
|
+
- **`ABORT_OUTSIDE_KNOWLEDGE`** — non-`knowledge/` files were staged; investigate before retrying.
|
|
166
|
+
- **`NOT_GIT`** — no git clone; docs are local only. Tell the dev to run `npx toga-ai` first.
|
|
167
|
+
- **`PUSH_FAILED`** — show the printed manual-push command.
|
|
228
168
|
|
|
229
|
-
## Step
|
|
169
|
+
## Step 7 — Report
|
|
230
170
|
|
|
231
171
|
List exactly what changed (clickable relative paths), and note any ⚠ ELEVATED docs the
|
|
232
172
|
developer approved so they remember those touched senior-owned standards/architecture.
|
|
233
|
-
If Step
|
|
173
|
+
If Step 6 published successfully (`PUSHED`), include the push confirmation in the report.
|
|
234
174
|
|
|
235
175
|
---
|
|
236
176
|
|
|
@@ -396,4 +336,4 @@ Suggest a slug from the work, e.g. `/session-save clickup-deploy-worker`.
|
|
|
396
336
|
|
|
397
337
|
`.claude/settings.json` runs `node knowledge.js validate` after every write to `knowledge/`,
|
|
398
338
|
so `ERROR:`/`OK` output appears inline as each doc is written — fix any error before writing
|
|
399
|
-
the next. Step 6's
|
|
339
|
+
the next. Step 6's `publish` re-runs `validate` as the final global-consistency gate before pushing.
|
package/skills/kickoff/SKILL.md
CHANGED
|
@@ -128,33 +128,49 @@ and silently truncates a 7-repo / 5-client list. Render the full list as a numbe
|
|
|
128
128
|
list and have the developer reply with numbers (e.g. "1, 3, 5") — no cap, supports
|
|
129
129
|
multi-select. Framework (q1) and layer (q2) have ≤4 options, so chips are fine there.
|
|
130
130
|
|
|
131
|
-
## Step 3 —
|
|
131
|
+
## Step 3 — Preflight: resolve the load-set and the exact read list (one call)
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
Run **one** command — it does the dependency resolution, feature-doc matching, standards
|
|
134
|
+
selection, and client-doc filtering that used to be several `deps`/`search` calls plus
|
|
135
|
+
in-context path-probing:
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
```bash
|
|
138
|
+
node "<TEAM_REPO>/knowledge.js" kickoff-preflight \
|
|
139
|
+
--repos=<comma-separated chosen repos> \
|
|
140
|
+
--client=<slug | omit if shared/internal> \
|
|
141
|
+
--layer=<back-end | front-end | hybrid> \
|
|
142
|
+
--q=<2-4 keywords from the developer's task sentence>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
It prints minified JSON:
|
|
146
|
+
- `loadSet` — framework core(s) + chosen repos + transitive `dependsOn`, in load order.
|
|
147
|
+
- `repos[]` — each with `architecture` `{path,exists}`, `role`, `chosen`, and matched
|
|
148
|
+
`features[]` (features are returned only for the repos the developer chose, not deps).
|
|
149
|
+
- `standards[]` and `client` `{title, profile, docs[]}` — both `{path,exists}`-flagged.
|
|
150
|
+
- `reads[]` — **the ordered list of knowledge files to read**, each `{label, path, exists}`.
|
|
151
|
+
- `unknown[]` — any `--repos` value not in the registry (run New-repo onboarding for each).
|
|
152
|
+
|
|
153
|
+
**Do not run `deps` or `search` separately, and do not probe the filesystem for these docs.**
|
|
154
|
+
The preflight already resolved everything; trust `reads[]`.
|
|
155
|
+
|
|
156
|
+
Then, for **each repo in `loadSet`**, get its local code path (so you can navigate the actual
|
|
157
|
+
source — this is the one thing preflight can't know):
|
|
138
158
|
- Look for a Claude `reference` memory named `repo-path-<repo>` (e.g. `repo-path-worker2`).
|
|
139
159
|
- If absent, **ask**: "What is the path to the `<repo>` repository?" Validate it exists,
|
|
140
160
|
then **write a `repo-path-<repo>` memory** (+ MEMORY.md pointer).
|
|
141
161
|
|
|
142
|
-
Never ask for a repo
|
|
162
|
+
Never ask for a repo not in `loadSet`.
|
|
143
163
|
|
|
144
164
|
## Step 4 — Load the knowledge
|
|
145
165
|
|
|
146
|
-
Read
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
`features/`/`workflows/` filtered to the selected framework(s)
|
|
155
|
-
(`search --client=<client> --framework=<fw>`).
|
|
156
|
-
|
|
157
|
-
For **both** frameworks, union across `1.0/` and `2.0/`.
|
|
166
|
+
Read every entry in the preflight `reads[]` array **in order**, resolving each `path`
|
|
167
|
+
against `<TEAM_REPO>/knowledge/`. Skip entries with `exists:false` — that doc isn't written
|
|
168
|
+
yet; note it in the Step 5 summary as "no knowledge captured yet."
|
|
169
|
+
|
|
170
|
+
The `reads[]` order already encodes the correct precedence (framework core architecture →
|
|
171
|
+
chosen-repo architecture + matched features → dep architecture → standards → client profile +
|
|
172
|
+
client docs), and already unions across `1.0/` and `2.0/` for a both-frameworks session. You
|
|
173
|
+
do not need to re-derive this order or run any further `search`.
|
|
158
174
|
|
|
159
175
|
**Do NOT create or modify any `CLAUDE.md` stub** — kickoff only reads. A blank session is a
|
|
160
176
|
valid choice; this skill is opt-in.
|