yadflow 3.18.1 → 4.0.0-next.1
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/CHANGELOG.md +355 -0
- package/README.md +79 -26
- package/bin/commands.mjs +41 -0
- package/bin/yad.mjs +437 -124
- package/cli/artifact-status.mjs +34 -15
- package/cli/checkpoint.mjs +69 -49
- package/cli/codeowners-command.mjs +170 -0
- package/cli/codeowners.mjs +397 -0
- package/cli/commit.mjs +13 -9
- package/cli/companion.mjs +2 -2
- package/cli/dial.mjs +183 -0
- package/cli/docs.mjs +88 -32
- package/cli/doctor.mjs +1472 -97
- package/cli/epic-state.mjs +3478 -232
- package/cli/epic.mjs +506 -0
- package/cli/errors.mjs +4 -1
- package/cli/gate.mjs +1002 -209
- package/cli/history.mjs +556 -0
- package/cli/hook.mjs +266 -55
- package/cli/hubcommit.mjs +6 -17
- package/cli/index-command.mjs +87 -0
- package/cli/ledger.mjs +57 -7
- package/cli/lib.mjs +184 -18
- package/cli/manifest.mjs +367 -56
- package/cli/migrate.mjs +726 -53
- package/cli/mode.mjs +170 -0
- package/cli/next.mjs +349 -90
- package/cli/openpr.mjs +191 -39
- package/cli/people.mjs +654 -0
- package/cli/plan.mjs +417 -132
- package/cli/platform.mjs +110 -129
- package/cli/product-index.mjs +287 -0
- package/cli/protection.mjs +706 -0
- package/cli/reconcile.mjs +38 -12
- package/cli/repo-publish.mjs +24 -26
- package/cli/repo.mjs +23 -14
- package/cli/report.mjs +21 -15
- package/cli/review.mjs +24 -27
- package/cli/riskmap-command.mjs +289 -0
- package/cli/riskmap.mjs +373 -0
- package/cli/setup.mjs +139 -287
- package/cli/ship.mjs +7 -6
- package/cli/skill.mjs +180 -0
- package/cli/skip.mjs +211 -30
- package/cli/thread.mjs +42 -17
- package/cli/tidy.mjs +20 -20
- package/cli/update-commit.mjs +22 -22
- package/cli/usage.mjs +115 -109
- package/package.json +3 -3
- package/skills/sdlc/config.yaml +166 -87
- package/skills/sdlc/module-help.csv +35 -35
- package/skills/yad-analysis/SKILL.md +125 -65
- package/skills/yad-architecture/SKILL.md +34 -23
- package/skills/yad-architecture/references/contract-format.md +10 -8
- package/skills/yad-backfill/SKILL.md +14 -8
- package/skills/yad-backfill/references/backfill.md +1 -1
- package/skills/yad-change/SKILL.md +127 -52
- package/skills/yad-change/references/triage.md +42 -28
- package/skills/yad-checks/SKILL.md +89 -45
- package/skills/yad-checks/references/check-gates.md +315 -92
- package/skills/yad-checks/templates/checks/build-test-lint.sh +25 -7
- package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
- package/skills/yad-checks/templates/checks/contract-check.sh +58 -2
- package/skills/yad-checks/templates/checks/epic-open.sh +3 -3
- package/skills/yad-checks/templates/checks/install-deps.sh +46 -0
- package/skills/yad-checks/templates/checks/ledger-guard.sh +94 -18
- package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
- package/skills/yad-checks/templates/checks/package-manager.sh +140 -0
- package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +4 -4
- package/skills/yad-checks/templates/checks/risk-map-check.sh +438 -0
- package/skills/yad-checks/templates/checks/verified-commits.sh +20 -46
- package/skills/yad-checks/templates/github/yad-checks.yml +37 -5
- package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
- package/skills/yad-checks/templates/github/yad-update-guard.yml +3 -4
- package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
- package/skills/yad-checks/templates/gitlab/.gitlab-ci.yml +7 -1
- package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +22 -4
- package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +5 -5
- package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +4 -4
- package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
- package/skills/yad-checks/templates/hooks/ledger-guard.sh +38 -7
- package/skills/yad-commit/SKILL.md +6 -6
- package/skills/yad-connect-design/SKILL.md +6 -6
- package/skills/yad-connect-design/references/design-context.md +1 -1
- package/skills/yad-connect-design/references/design-registry.md +2 -2
- package/skills/yad-connect-docs/SKILL.md +12 -12
- package/skills/yad-connect-docs/references/docs-registry.md +1 -1
- package/skills/yad-connect-learning/SKILL.md +5 -5
- package/skills/yad-connect-learning/references/learning-registry.md +2 -2
- package/skills/yad-connect-repos/SKILL.md +92 -54
- package/skills/yad-connect-repos/references/code-context.md +6 -6
- package/skills/yad-connect-repos/references/hub-config.md +68 -58
- package/skills/yad-connect-repos/references/repos-registry.md +10 -9
- package/skills/yad-connect-repos/references/risk-map.md +81 -0
- package/skills/yad-connect-testing/SKILL.md +6 -6
- package/skills/yad-connect-testing/references/testing-context.md +3 -4
- package/skills/yad-connect-testing/references/testing-registry.md +2 -2
- package/skills/yad-defects/SKILL.md +8 -8
- package/skills/yad-discovery/SKILL.md +130 -94
- package/skills/yad-discovery/references/discovery-schema.md +23 -7
- package/skills/yad-discovery/references/foundation-schema.md +374 -0
- package/skills/yad-docs/SKILL.md +16 -11
- package/skills/yad-docs/references/data-mapping.md +9 -7
- package/skills/yad-docs/templates/app/package-lock.json +3 -3
- package/skills/yad-docs-overview/SKILL.md +32 -17
- package/skills/yad-docs-overview/references/pipeline-model.md +47 -28
- package/skills/yad-docs-sync/SKILL.md +10 -5
- package/skills/yad-docs-sync/references/staleness.md +8 -7
- package/skills/yad-engineer-review/SKILL.md +88 -24
- package/skills/yad-engineer-review/references/ship-and-record.md +25 -16
- package/skills/yad-epic/SKILL.md +178 -100
- package/skills/yad-epic/references/state-schema.md +626 -117
- package/skills/yad-hub-bridge/SKILL.md +66 -48
- package/skills/yad-hub-bridge/references/bridge.md +110 -83
- package/skills/yad-hub-bridge/references/login-roster.md +163 -70
- package/skills/yad-hub-bridge/templates/checks/hub-route.sh +22 -19
- package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +34 -14
- package/skills/yad-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +2 -2
- package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +22 -12
- package/skills/yad-implement/SKILL.md +29 -15
- package/skills/yad-implement/references/implement-conventions.md +2 -2
- package/skills/yad-learn/SKILL.md +9 -9
- package/skills/yad-learn/references/learning-state.md +2 -2
- package/skills/yad-open-pr/SKILL.md +64 -29
- package/skills/yad-pair-review/SKILL.md +18 -16
- package/skills/yad-pair-review/references/session-state.md +4 -4
- package/skills/yad-pr-template/SKILL.md +48 -27
- package/skills/yad-pr-template/references/risk-routing.md +97 -24
- package/skills/yad-pr-template/templates/checks/pr-template.sh +37 -15
- package/skills/yad-pr-template/templates/checks/pr-title.sh +27 -13
- package/skills/yad-pr-template/templates/checks/risk-route.sh +107 -14
- package/skills/yad-pr-template/templates/github/pull_request_template.md +7 -5
- package/skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md +7 -5
- package/skills/yad-pr-template/templates/hub/github/pull_request_template.md +15 -14
- package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +15 -13
- package/skills/yad-reconcile/SKILL.md +3 -3
- package/skills/yad-report/SKILL.md +5 -5
- package/skills/yad-review-companion/SKILL.md +12 -9
- package/skills/yad-review-gate/SKILL.md +198 -79
- package/skills/yad-review-gate/references/gating.md +230 -54
- package/skills/yad-run/SKILL.md +86 -56
- package/skills/yad-run/references/run-loop.md +67 -45
- package/skills/yad-ship/SKILL.md +18 -14
- package/skills/yad-spec/SKILL.md +31 -17
- package/skills/yad-spec/references/spec-handoff.md +17 -5
- package/skills/yad-status/SKILL.md +114 -56
- package/skills/yad-stories/SKILL.md +42 -27
- package/skills/yad-stories/references/story-schema.md +10 -9
- package/skills/yad-stub/SKILL.md +59 -48
- package/skills/yad-sync-repos/SKILL.md +3 -3
- package/skills/yad-test-cases/SKILL.md +37 -30
- package/skills/yad-test-cases/references/test-cases-schema.md +8 -5
- package/skills/yad-timeline/SKILL.md +8 -7
- package/skills/yad-ui/SKILL.md +46 -25
- package/cli/roster.mjs +0 -164
- package/skills/sdlc/install.sh +0 -68
package/cli/docs.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import path from 'node:path';
|
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import { createHash } from 'node:crypto';
|
|
14
14
|
import { c, log, ok, info, warn, hand, fail, readJSON, run, has, exists } from './lib.mjs';
|
|
15
|
-
import { PROJECT_FILES
|
|
15
|
+
import { PROJECT_FILES } from './manifest.mjs';
|
|
16
16
|
import { detectPlatform, platformReady } from './platform.mjs';
|
|
17
17
|
import { gitHead } from './setup.mjs';
|
|
18
18
|
import { contractSurfaceHash } from './epic-state.mjs';
|
|
@@ -95,8 +95,33 @@ export function repoHeadsFor(root, repos = [], registry = { repos: [] }) {
|
|
|
95
95
|
return out;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// The version of the docs shell a per-epic site is copied from: the `version` in the template's own
|
|
99
|
+
// package.json. It moves only when the shell changes — cli/test.mjs pins it to a fingerprint of the
|
|
100
|
+
// template, so an edit to the shell cannot land without a new version.
|
|
101
|
+
//
|
|
102
|
+
// It replaced the yad CLI VERSION here, which moves on EVERY release: each publish marked every docs site
|
|
103
|
+
// "doc shell upgraded" whether or not the shell had changed. A manifest written before this carries that
|
|
104
|
+
// CLI version as `templateVersion`, which is never compared. What such a manifest is read as instead is
|
|
105
|
+
// LEGACY_SHELL_VERSION, below.
|
|
106
|
+
//
|
|
107
|
+
// Read when a freshness check asks, never at import: every `yad` command loads this module, and a copy of
|
|
108
|
+
// the CLI without `skills/` beside it (a test harness, a partial checkout) must still start. No template
|
|
109
|
+
// found means no shell version, so that one comparison is skipped.
|
|
110
|
+
export function shellVersion() {
|
|
111
|
+
try {
|
|
112
|
+
return JSON.parse(fs.readFileSync(new URL('../skills/yad-docs/templates/app/package.json', import.meta.url), 'utf8')).version || null;
|
|
113
|
+
} catch { return null; }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// The shell version every site built before `shellVersion` existed was built on. Exact, not a guess: the
|
|
117
|
+
// template's package.json has said `0.0.0` since its first commit, so a manifest that records only the old
|
|
118
|
+
// `templateVersion` came from that shell. Reading it this way reports nothing today, and still reports
|
|
119
|
+
// the first real shell upgrade — ignoring the old manifest instead would have missed that for ever,
|
|
120
|
+
// because only a skill rewrites a manifest and nothing prompts one to.
|
|
121
|
+
export const LEGACY_SHELL_VERSION = '0.0.0';
|
|
122
|
+
|
|
98
123
|
// Compare a build manifest to the current world; list the concrete reasons it is stale.
|
|
99
|
-
export function docsStale(manifest, { artifactHash, repoHeads = {},
|
|
124
|
+
export function docsStale(manifest, { artifactHash, repoHeads = {}, shellVersion } = {}) {
|
|
100
125
|
const reasons = [];
|
|
101
126
|
if (!manifest) return { stale: true, reasons: ['never built'] };
|
|
102
127
|
if (artifactHash && manifest.artifactHash && artifactHash !== manifest.artifactHash) {
|
|
@@ -106,8 +131,9 @@ export function docsStale(manifest, { artifactHash, repoHeads = {}, templateVers
|
|
|
106
131
|
const was = (manifest.repoHeads || {})[repo];
|
|
107
132
|
if (head && was && head !== was) reasons.push(`repo ${repo} HEAD advanced`);
|
|
108
133
|
}
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
const builtOn = manifest.shellVersion || (manifest.templateVersion ? LEGACY_SHELL_VERSION : null);
|
|
135
|
+
if (shellVersion && builtOn && shellVersion !== builtOn) {
|
|
136
|
+
reasons.push(`doc shell upgraded (${builtOn} → ${shellVersion})`);
|
|
111
137
|
}
|
|
112
138
|
return { stale: reasons.length > 0, reasons };
|
|
113
139
|
}
|
|
@@ -181,16 +207,36 @@ export function pagesWorkflowPath(platform) {
|
|
|
181
207
|
}
|
|
182
208
|
|
|
183
209
|
// ---- build (subprocess) -------------------------------------------------------------------------
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
210
|
+
// Builds one site and says what happened: `{ site, built, error }`, where `error` is why it was not
|
|
211
|
+
// built (null when it was). Every caller — build, deploy, sync --refresh — reads that one row, so a
|
|
212
|
+
// failure is said the same way on each path. A failure sets exit 1 and is a `fail` with its `hand`
|
|
213
|
+
// right under it: under --json that pair IS the refusal, and a later `hand` (deploy's "no Pages
|
|
214
|
+
// platform") can never become a failed build's hint. The one miss that is only a warning is npm not on
|
|
215
|
+
// PATH for a deploy or a refresh, which the CI workflow builds on push (`npmOptional`).
|
|
216
|
+
function buildSite(root, t, { npmOptional = false } = {}) {
|
|
217
|
+
const dir = siteDir(root, t);
|
|
218
|
+
const rel = path.relative(root, dir) || '.';
|
|
219
|
+
const site = label(t);
|
|
220
|
+
const failed = (error, next) => {
|
|
221
|
+
fail(error);
|
|
222
|
+
hand(next);
|
|
223
|
+
process.exitCode = 1;
|
|
224
|
+
return { site, built: false, error };
|
|
225
|
+
};
|
|
226
|
+
if (!exists(dir)) return failed(`${site}: no generated site at ${rel}`, `run the ${t.overview ? 'yad-docs-overview' : 'yad-docs'} skill first`);
|
|
227
|
+
if (!has('npm')) {
|
|
228
|
+
const error = `${site}: npm not on PATH — cannot build`;
|
|
229
|
+
if (!npmOptional) return failed(error, 'install npm (or put it on PATH), then run the command again');
|
|
230
|
+
warn(`${error}; the CI workflow will build on push`);
|
|
231
|
+
return { site, built: false, error };
|
|
232
|
+
}
|
|
187
233
|
const install = exists(path.join(dir, 'package-lock.json')) ? ['ci'] : ['install'];
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if (!
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return {
|
|
234
|
+
const again = 'fix the error npm printed above, then run the command again';
|
|
235
|
+
log(` ${c.dim('$')} npm ${install[0]} ${c.dim(`(${rel})`)}`);
|
|
236
|
+
if (!run('npm', install, { cwd: dir, stdio: 'inherit' }).ok) return failed(`${site}: npm ${install[0]} failed`, again);
|
|
237
|
+
if (!run('npm', ['run', 'build'], { cwd: dir, stdio: 'inherit' }).ok) return failed(`${site}: npm run build failed`, again);
|
|
238
|
+
ok(`built ${site} ${c.dim('→ ' + path.relative(root, path.join(dir, 'dist')))}`);
|
|
239
|
+
return { site, built: true, error: null };
|
|
194
240
|
}
|
|
195
241
|
|
|
196
242
|
// ---- orchestration ------------------------------------------------------------------------------
|
|
@@ -205,49 +251,57 @@ export async function runDocs(root, { action = 'list', epic, overview, sync } =
|
|
|
205
251
|
info(`target ${c.cyan(docs.target)} scope ${docs.scope} base ${docs.basePath} ${docs.source === 'unavailable' ? c.yellow('(build-only)') : ''}`);
|
|
206
252
|
}
|
|
207
253
|
log(c.bold('\ngenerated sites'));
|
|
208
|
-
if (!targets.length) { info('none generated yet'); return { sites:
|
|
254
|
+
if (!targets.length) { info('none generated yet'); return { action, target: docs?.target ?? null, sites: [] }; }
|
|
209
255
|
for (const t of targets) reportFreshness(root, t);
|
|
210
|
-
return { sites: targets.
|
|
256
|
+
return { action, target: docs?.target ?? null, sites: targets.map((t) => label(t)) };
|
|
211
257
|
}
|
|
212
258
|
|
|
213
259
|
if (action === 'build' || action === 'deploy') {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (r.ok) { built++; ok(`built ${label(t)} ${c.dim('→ ' + path.relative(root, r.dist))}`); }
|
|
218
|
-
}
|
|
260
|
+
// Every site is tried, even after one fails; the run exits 1 if any did (buildSite sets it).
|
|
261
|
+
const sites = targets.map((t) => buildSite(root, t, { npmOptional: action === 'deploy' }));
|
|
262
|
+
const built = sites.filter((s) => s.built).length;
|
|
219
263
|
if (action === 'deploy') {
|
|
220
264
|
const platform = docs ? (docs.target === 'gitlab-pages' ? 'gitlab' : docs.target === 'github-pages' ? 'github' : null) : null;
|
|
265
|
+
// Both arms say the same thing about what was built here; only a run where every site built ends on ✓.
|
|
266
|
+
const all = built > 0 && built === sites.length;
|
|
267
|
+
const here = all ? null : built ? `only ${built} of ${sites.length} sites built here` : 'nothing was built here';
|
|
221
268
|
if (!platform || !platformReady(platform)) {
|
|
222
|
-
hand(
|
|
269
|
+
hand(`no Pages platform/CLI — ${here ?? 'built locally only'}; commit + push so the CI workflow can publish (yad docs sync --wire)`);
|
|
223
270
|
} else {
|
|
224
|
-
ok(`deploy via the ${platform} Pages workflow on push (yad docs sync --wire installs it)`);
|
|
271
|
+
(all ? ok : info)(`${here ? `${here}; ` : ''}deploy via the ${platform} Pages workflow on push (yad docs sync --wire installs it)`);
|
|
225
272
|
if (docs?.basePath) info(`will publish under ${docs.basePath}`);
|
|
226
273
|
}
|
|
227
274
|
}
|
|
228
|
-
return { built };
|
|
275
|
+
return { action, built, sites };
|
|
229
276
|
}
|
|
230
277
|
|
|
231
278
|
if (action === 'sync') {
|
|
232
279
|
if (sync === 'wire') return wirePages(root, docs);
|
|
233
280
|
// check (default) + refresh both compute staleness; refresh additionally rebuilds.
|
|
234
281
|
const registry = readJSON(path.join(root, PROJECT_FILES.reposRegistry), { repos: [] });
|
|
235
|
-
|
|
282
|
+
// One row per site. `built` is null when no build was tried (a check, or a fresh site), so it never
|
|
283
|
+
// reads as a failed build; a refresh that could not build a stale site exits 1, as a deploy does.
|
|
284
|
+
const sites = [];
|
|
236
285
|
for (const t of targets) {
|
|
237
286
|
const s = freshness(root, t, registry);
|
|
238
287
|
if (s.stale) {
|
|
239
|
-
stale++;
|
|
240
288
|
warn(`${label(t)} — ${c.yellow('stale')}: ${s.reasons.join('; ')}`);
|
|
241
|
-
|
|
242
|
-
|
|
289
|
+
const b = sync === 'refresh' ? buildSite(root, t, { npmOptional: true }) : { built: null, error: null };
|
|
290
|
+
sites.push({ site: label(t), stale: true, built: b.built, error: b.error });
|
|
291
|
+
} else {
|
|
292
|
+
ok(`${label(t)} ${c.dim('— fresh')}`);
|
|
293
|
+
sites.push({ site: label(t), stale: false, built: null, error: null });
|
|
294
|
+
}
|
|
243
295
|
}
|
|
296
|
+
const stale = sites.filter((s) => s.stale).length;
|
|
297
|
+
const built = sites.filter((s) => s.built).length;
|
|
244
298
|
if (stale && sync !== 'refresh') hand('regenerate content with the yad-docs / yad-docs-overview skill (the AI step), then `yad docs deploy`');
|
|
245
|
-
return { stale };
|
|
299
|
+
return { action, sync, stale, built, sites };
|
|
246
300
|
}
|
|
247
301
|
|
|
248
302
|
fail(`unknown docs action: ${action} (list | build | deploy | sync)`);
|
|
249
303
|
process.exitCode = 1;
|
|
250
|
-
return {};
|
|
304
|
+
return { action };
|
|
251
305
|
}
|
|
252
306
|
|
|
253
307
|
// ---- helpers ------------------------------------------------------------------------------------
|
|
@@ -267,10 +321,12 @@ function label(t) { return t.overview ? 'overview (docs/sdlc-site)' : `epic ${t.
|
|
|
267
321
|
function freshness(root, t, registry) {
|
|
268
322
|
const manifest = readJSON(manifestPath(root, t), null);
|
|
269
323
|
if (t.overview) {
|
|
270
|
-
// The overview is generated from the project pipeline definition, not an epic's artifacts.
|
|
324
|
+
// The overview is generated from the project pipeline definition, not an epic's artifacts. No shell
|
|
325
|
+
// version: the overview is copied from the shell once and updated in place after that
|
|
326
|
+
// (yad-docs-overview, Step 3), so a shell upgrade is not a reason to rebuild it.
|
|
271
327
|
const files = ['skills/sdlc/config.yaml', 'skills/sdlc/module-help.csv', 'docs/diagrams/sdlc-overview.mmd']
|
|
272
328
|
.map((f) => path.join(root, f)).filter(exists);
|
|
273
|
-
return docsStale(manifest, { artifactHash: docsArtifactHash(files)
|
|
329
|
+
return docsStale(manifest, { artifactHash: docsArtifactHash(files) });
|
|
274
330
|
}
|
|
275
331
|
const epicMeta = readJSON(path.join(root, 'epics', t.epic, '.sdlc/state.json'), {});
|
|
276
332
|
const repos = epicMeta.repos || [];
|
|
@@ -278,7 +334,7 @@ function freshness(root, t, registry) {
|
|
|
278
334
|
return docsStale(manifest, {
|
|
279
335
|
artifactHash: docsArtifactHash(docsArtifactFiles(root, t.epic), surface || ''),
|
|
280
336
|
repoHeads: repoHeadsFor(root, repos, registry),
|
|
281
|
-
|
|
337
|
+
shellVersion: shellVersion(),
|
|
282
338
|
});
|
|
283
339
|
}
|
|
284
340
|
function reportFreshness(root, t) {
|