yadflow 2.6.0 → 2.7.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/CHANGELOG.md +4 -7
- package/README.md +23 -2
- package/bin/yad.mjs +22 -1
- package/cli/docs.mjs +298 -0
- package/cli/manifest.mjs +6 -1
- package/docs/index.html +4 -4
- package/package.json +2 -2
- package/skills/sdlc/config.yaml +19 -0
- package/skills/sdlc/install.sh +1 -1
- package/skills/sdlc/module-help.csv +4 -0
- package/skills/yad-connect-docs/SKILL.md +132 -0
- package/skills/yad-connect-docs/references/docs-registry.md +74 -0
- package/skills/yad-docs/SKILL.md +159 -0
- package/skills/yad-docs/references/data-mapping.md +75 -0
- package/skills/yad-docs/references/theme-map.md +69 -0
- package/skills/yad-docs/templates/app/README.md +31 -0
- package/skills/yad-docs/templates/app/eslint.config.js +23 -0
- package/skills/yad-docs/templates/app/index.html +17 -0
- package/skills/yad-docs/templates/app/package-lock.json +4030 -0
- package/skills/yad-docs/templates/app/package.json +35 -0
- package/skills/yad-docs/templates/app/public/favicon.svg +28 -0
- package/skills/yad-docs/templates/app/public/logo.svg +39 -0
- package/skills/yad-docs/templates/app/public/vite.svg +1 -0
- package/skills/yad-docs/templates/app/src/App.tsx +98 -0
- package/skills/yad-docs/templates/app/src/components/Auth/LoginPage.tsx +101 -0
- package/skills/yad-docs/templates/app/src/components/Canvas/AnimatedMessage.tsx +101 -0
- package/skills/yad-docs/templates/app/src/components/Canvas/ConnectionLine.tsx +90 -0
- package/skills/yad-docs/templates/app/src/components/Canvas/FlowCanvas.tsx +216 -0
- package/skills/yad-docs/templates/app/src/components/Canvas/SystemComponent.tsx +153 -0
- package/skills/yad-docs/templates/app/src/components/Controls/PlaybackBar.tsx +284 -0
- package/skills/yad-docs/templates/app/src/components/Controls/StepDetail.tsx +167 -0
- package/skills/yad-docs/templates/app/src/components/DetailPanel/HandlerLogicSnippet.tsx +41 -0
- package/skills/yad-docs/templates/app/src/components/DetailPanel/RequestPayloadPreview.tsx +46 -0
- package/skills/yad-docs/templates/app/src/components/DetailPanel/RightPanel.tsx +88 -0
- package/skills/yad-docs/templates/app/src/components/DetailPanel/StatusCard.tsx +76 -0
- package/skills/yad-docs/templates/app/src/components/DetailPanel/TriggerEventCard.tsx +45 -0
- package/skills/yad-docs/templates/app/src/components/DocLayout/DocPageShell.tsx +80 -0
- package/skills/yad-docs/templates/app/src/components/DocLayout/DocSectionCard.tsx +55 -0
- package/skills/yad-docs/templates/app/src/components/DocLayout/DocTableOfContents.tsx +79 -0
- package/skills/yad-docs/templates/app/src/components/DocLayout/RoleCard.tsx +67 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/ApiReferenceSection.tsx +108 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/CancelabilitySection.tsx +73 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/CriticalRunbookSection.tsx +177 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/DataMigrationSection.tsx +102 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/DbSchemaSection.tsx +98 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/DeploymentGuideSection.tsx +104 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/DriverIntegrationSection.tsx +127 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/ExecutiveSummarySection.tsx +69 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/FlowOverviewSection.tsx +73 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/FlowPathsChecklistSection.tsx +96 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/MiddlewareChainSection.tsx +107 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/MonitoringAlertingSection.tsx +106 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/NotificationLocalizationSection.tsx +102 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/PMRoadmapSection.tsx +133 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/PerformanceTestingSection.tsx +91 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/RiderIntegrationSection.tsx +99 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/SecuritySection.tsx +74 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/StatusMachineSection.tsx +90 -0
- package/skills/yad-docs/templates/app/src/components/DocSections/TestPlanSection.tsx +163 -0
- package/skills/yad-docs/templates/app/src/components/Logs/SystemLogsTerminal.tsx +126 -0
- package/skills/yad-docs/templates/app/src/components/Navigation/TopNavBar.tsx +90 -0
- package/skills/yad-docs/templates/app/src/components/Reference/BullMQJobsList.tsx +60 -0
- package/skills/yad-docs/templates/app/src/components/Reference/DecisionTreeView.tsx +49 -0
- package/skills/yad-docs/templates/app/src/components/Reference/DeeplinkActionsChips.tsx +69 -0
- package/skills/yad-docs/templates/app/src/components/Reference/DriverUIStatesTable.tsx +61 -0
- package/skills/yad-docs/templates/app/src/components/Reference/FeatureFlagMatrix.tsx +73 -0
- package/skills/yad-docs/templates/app/src/components/Reference/RiderUIStatesTable.tsx +61 -0
- package/skills/yad-docs/templates/app/src/components/Reference/RulesLegendPanel.tsx +217 -0
- package/skills/yad-docs/templates/app/src/components/Reference/StakeholderToggle.tsx +41 -0
- package/skills/yad-docs/templates/app/src/components/Reference/TroubleshootingSection.tsx +93 -0
- package/skills/yad-docs/templates/app/src/components/Sidebar/PathSelector.tsx +148 -0
- package/skills/yad-docs/templates/app/src/components/Sidebar/SidebarFooter.tsx +40 -0
- package/skills/yad-docs/templates/app/src/components/Sidebar/StepList.tsx +234 -0
- package/skills/yad-docs/templates/app/src/components/shared/Badge.tsx +28 -0
- package/skills/yad-docs/templates/app/src/components/shared/CommandPalette.tsx +213 -0
- package/skills/yad-docs/templates/app/src/components/shared/Icon.tsx +21 -0
- package/skills/yad-docs/templates/app/src/components/shared/Tooltip.tsx +42 -0
- package/skills/yad-docs/templates/app/src/data/components.ts +74 -0
- package/skills/yad-docs/templates/app/src/data/docSections.ts +231 -0
- package/skills/yad-docs/templates/app/src/data/paths.ts +2319 -0
- package/skills/yad-docs/templates/app/src/data/referenceData.ts +392 -0
- package/skills/yad-docs/templates/app/src/data/roles.ts +145 -0
- package/skills/yad-docs/templates/app/src/data/types.ts +79 -0
- package/skills/yad-docs/templates/app/src/hooks/useAnimationQueue.ts +41 -0
- package/skills/yad-docs/templates/app/src/hooks/usePlayback.ts +100 -0
- package/skills/yad-docs/templates/app/src/hooks/useStakeholderFilter.ts +10 -0
- package/skills/yad-docs/templates/app/src/index.css +121 -0
- package/skills/yad-docs/templates/app/src/main.tsx +13 -0
- package/skills/yad-docs/templates/app/src/pages/RoleSelectPage.tsx +34 -0
- package/skills/yad-docs/templates/app/src/pages/StakeholderDocPage.tsx +98 -0
- package/skills/yad-docs/templates/app/src/pages/SubPathDetailPage.tsx +282 -0
- package/skills/yad-docs/templates/app/src/store/useAuthStore.ts +42 -0
- package/skills/yad-docs/templates/app/src/store/useFlowStore.ts +197 -0
- package/skills/yad-docs/templates/app/src/utils/iconMap.ts +46 -0
- package/skills/yad-docs/templates/app/tsconfig.app.json +28 -0
- package/skills/yad-docs/templates/app/tsconfig.json +7 -0
- package/skills/yad-docs/templates/app/tsconfig.node.json +26 -0
- package/skills/yad-docs/templates/app/vite.config.ts +10 -0
- package/skills/yad-docs-overview/SKILL.md +129 -0
- package/skills/yad-docs-overview/references/pipeline-model.md +102 -0
- package/skills/yad-docs-sync/SKILL.md +99 -0
- package/skills/yad-docs-sync/references/staleness.md +81 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
# [2.
|
|
1
|
+
# [2.7.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.6.0...v2.7.0) (2026-06-14)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* let `yad ship` derive the PR title from the committed subject ([a5adba3](https://github.com/abdelrahmannasr/yadflow/commit/a5adba3212b236ffc5a2470b9ea50bf97c6c4138))
|
|
6
|
+
* drop unused today param from runDocs (lint) ([1c255f1](https://github.com/abdelrahmannasr/yadflow/commit/1c255f1c848b9571502e29b142a07366612d638c))
|
|
7
|
+
* publish per-epic docs sites in CI + check shell-version staleness ([9862646](https://github.com/abdelrahmannasr/yadflow/commit/9862646fbe910de8ad8248a5f1bb586a5604b18f))
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
### Features
|
|
12
11
|
|
|
13
|
-
* add
|
|
14
|
-
* add commit-message/pr-title/pr-template pattern gates (code + hub) ([6658837](https://github.com/abdelrahmannasr/yadflow/commit/6658837d7685b826884b665a5e7661fd6ae99828))
|
|
15
|
-
* add yad-commit/yad-open-pr/yad-ship skills; rename Step E to yad-engineer-review ([c566567](https://github.com/abdelrahmannasr/yadflow/commit/c5665679e45f24ea53c682aca3a78eb52c9f984f))
|
|
12
|
+
* add interactive documentation skills + yad docs CLI ([4bf7a25](https://github.com/abdelrahmannasr/yadflow/commit/4bf7a25c38e28ef47704a8e2f5acec2f724e4e29))
|
|
16
13
|
|
|
17
14
|
# [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-14)
|
|
18
15
|
|
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ simultaneous advancements can be lost; the next event or scheduled sweep re-sync
|
|
|
133
133
|
### What `setup` walks you through (10 steps)
|
|
134
134
|
|
|
135
135
|
1. **Preflight** — confirm the hub is a git repo (offers `git init`); check `git`/`node`/`npx`.
|
|
136
|
-
2. **Install the module** — copy all
|
|
136
|
+
2. **Install the module** — copy all 29 `yad-*` skills into the IDE skill dirs you pick
|
|
137
137
|
(`.claude/`, `.agents/`, `.zencoder/`, `.opencode/`) and register `_bmad/sdlc/`.
|
|
138
138
|
3. **Hub platform & roster** — detect GitHub/GitLab from the remote; record reviewers → `.sdlc/hub.json`.
|
|
139
139
|
4. **Connect a design tool** — record the design tool (Figma / pencil / none) → `.sdlc/design.json` so
|
|
@@ -189,7 +189,7 @@ with a fix-it hint per finding. Failures carry stable, greppable codes, also pri
|
|
|
189
189
|
|
|
190
190
|
Filing a bug? Attach `yad doctor --json` — it contains no secrets (names, paths, and check results only).
|
|
191
191
|
|
|
192
|
-
## Agent skills (all
|
|
192
|
+
## Agent skills (all 29)
|
|
193
193
|
|
|
194
194
|
The CLI **installs and wires** the module; the skills below are the **agents you invoke by name** in your
|
|
195
195
|
AI IDE (e.g. *“run `yad-epic`”*) to actually do the work. State lives in files you can also edit
|
|
@@ -218,6 +218,27 @@ directly. Each skill stops at a gate and never auto-advances unless a step has *
|
|
|
218
218
|
tokens), detecting the **DeepTutor CLI on PATH** (a subprocess like Repomix — DeepTutor ships no MCP)
|
|
219
219
|
and degrading to **harness-native** tutoring when absent. Idempotent and refreshable; one connection
|
|
220
220
|
per project.
|
|
221
|
+
- **`yad-connect-docs`** — Connects a docs/Pages target (GitHub Pages / GitLab Pages, auto-detected from
|
|
222
|
+
`hub.json`) so the generated documentation sites can deploy. Records the target + scope + base path in
|
|
223
|
+
`.sdlc/docs.json` (local-user auth, no stored tokens), degrading to **build-only** when no Pages host /
|
|
224
|
+
CLI is present. Idempotent and refreshable; one connection per project.
|
|
225
|
+
|
|
226
|
+
### Living documentation (generated, themed, auto-kept-fresh)
|
|
227
|
+
|
|
228
|
+
- **`yad-docs`** — Generates an **interactive documentation site** for an epic (a React + Vite + Tailwind
|
|
229
|
+
SPA: an animated front-stage flow canvas + role-based stakeholder doc pages) from the authored
|
|
230
|
+
artifacts — `epic.md`, `architecture.md`, the locked `contract.md`, `ui-design.md`, the stories — into
|
|
231
|
+
`epics/EP-<slug>/docs-site/`, themed by the **connected design system** (`DESIGN.md` / `design.json`
|
|
232
|
+
tokens → the site's CSS). The content lives in generated `src/data/*.ts`; the shell is a vendored
|
|
233
|
+
template. An **output enrichment, never a gate** — it never touches epic state, approvals, or the
|
|
234
|
+
contract lock. `generate` / `refresh` / `deploy`.
|
|
235
|
+
- **`yad-docs-overview`** — Generates the project **SDLC-overview site** (`docs/sdlc-site/`) — every
|
|
236
|
+
stage from setup → ship as flow paths / system components / stakeholder roles, reusing the same shell —
|
|
237
|
+
superseding the hand-maintained `docs/index.html`.
|
|
238
|
+
- **`yad-docs-sync`** — Keeps the sites fresh: detects staleness (a content hash of the authored
|
|
239
|
+
artifacts + the connected repos' HEAD shas vs each site's build manifest), regenerates + redeploys, and
|
|
240
|
+
can wire a CI job that rebuilds on push. Generalizes the rule that feature work must hand-update the
|
|
241
|
+
docs — the overview now regenerates whenever the skill set / pipeline changes.
|
|
221
242
|
|
|
222
243
|
### The learning layer (cross-cutting — any member, any stage)
|
|
223
244
|
|
package/bin/yad.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { runCommit } from '../cli/commit.mjs';
|
|
|
10
10
|
import { runOpenPr } from '../cli/openpr.mjs';
|
|
11
11
|
import { runShip } from '../cli/ship.mjs';
|
|
12
12
|
import { runRepo } from '../cli/repo.mjs';
|
|
13
|
+
import { runDocs } from '../cli/docs.mjs';
|
|
13
14
|
import { runDoctor } from '../cli/doctor.mjs';
|
|
14
15
|
|
|
15
16
|
const HELP = `${c.bold('yad')} — setup, review-gate & build helpers for the SDLC Workflow module ${c.dim('v' + VERSION)}
|
|
@@ -39,6 +40,12 @@ ${c.bold('Build helpers')}
|
|
|
39
40
|
yad repo list Show connected repos (fresh / stale)
|
|
40
41
|
yad repo refresh [name] Re-pack a stale repo (a human decision)
|
|
41
42
|
|
|
43
|
+
${c.bold('Interactive docs (generated sites)')}
|
|
44
|
+
yad docs list Show the docs target + per-site freshness
|
|
45
|
+
yad docs build [--epic <id>|--overview] npm-build a generated doc site
|
|
46
|
+
yad docs deploy [--epic <id>|--overview] Build + report the Pages deploy
|
|
47
|
+
yad docs sync [--check|--refresh|--wire] Staleness sweep; --wire installs the Pages CI
|
|
48
|
+
|
|
42
49
|
${c.bold('Options')}
|
|
43
50
|
--dir <path> Target project root (default: cwd)
|
|
44
51
|
--type <t> commit: feat|fix|docs|refactor|test|perf|build|ci|chore|revert
|
|
@@ -48,6 +55,9 @@ ${c.bold('Options')}
|
|
|
48
55
|
--contract-change commit/open-pr: mark the contract surface touched
|
|
49
56
|
--risk <level> open-pr: low|medium|high (default low)
|
|
50
57
|
--repo <name> open-pr: target a registered repo by name
|
|
58
|
+
--epic <id> docs: target one epic's site (EP-<slug>)
|
|
59
|
+
--overview docs: target the project SDLC-overview site
|
|
60
|
+
--check/--refresh/--wire docs sync: report stale / rebuild / install Pages CI
|
|
51
61
|
--dry-run commit: print the message, do not commit
|
|
52
62
|
--force commit: bypass the atomic-file guard / re-copy unchanged files
|
|
53
63
|
--branch <head> gate ci: the review PR/MR head branch (review/EP-<slug>/<artifact>)
|
|
@@ -56,7 +66,7 @@ ${c.bold('Options')}
|
|
|
56
66
|
-h, --help Show this help
|
|
57
67
|
-v, --version Print version`;
|
|
58
68
|
|
|
59
|
-
const VALUE_FLAGS = new Set(['--dir', '--type', '--message', '--task', '--ai', '--risk', '--repo', '--platform', '--base', '--title', '--scope', '--branch', '--pr']);
|
|
69
|
+
const VALUE_FLAGS = new Set(['--dir', '--type', '--message', '--task', '--ai', '--risk', '--repo', '--platform', '--base', '--title', '--scope', '--branch', '--pr', '--epic']);
|
|
60
70
|
|
|
61
71
|
function parseArgs(argv) {
|
|
62
72
|
const o = { _: [], dir: process.cwd(), fix: false, force: false, scope: 'all' };
|
|
@@ -66,6 +76,10 @@ function parseArgs(argv) {
|
|
|
66
76
|
else if (a === '--force') o.force = true;
|
|
67
77
|
else if (a === '--contract-change') o.contractChange = true;
|
|
68
78
|
else if (a === '--no-push') o.noPush = true;
|
|
79
|
+
else if (a === '--overview') o.overview = true;
|
|
80
|
+
else if (a === '--check') o.check = true;
|
|
81
|
+
else if (a === '--refresh') o.refresh = true;
|
|
82
|
+
else if (a === '--wire') o.wire = true;
|
|
69
83
|
else if (a === '--dry-run') o.dryRun = true;
|
|
70
84
|
else if (a === '--json') o.json = true;
|
|
71
85
|
else if (a === '-h' || a === '--help') o.help = true;
|
|
@@ -133,6 +147,13 @@ async function main() {
|
|
|
133
147
|
await runRepo(o.dir, { action: action || 'list', name, today });
|
|
134
148
|
break;
|
|
135
149
|
}
|
|
150
|
+
case 'docs': {
|
|
151
|
+
const [, action] = o._;
|
|
152
|
+
if (o.epic && !isValidEpicId(o.epic)) { log(c.red(`invalid epic id: ${o.epic} (expected EP-<slug>, [a-z0-9-] only)`)); process.exitCode = 1; break; }
|
|
153
|
+
const sync = o.wire ? 'wire' : o.refresh ? 'refresh' : 'check';
|
|
154
|
+
await runDocs(o.dir, { action: action || 'list', epic: o.epic, overview: o.overview, sync, today });
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
136
157
|
default:
|
|
137
158
|
log(c.red(`unknown command: ${cmd}`));
|
|
138
159
|
log(HELP);
|
package/cli/docs.mjs
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// `yad docs list|build|deploy|sync` — the build/deploy/staleness mechanics for the interactive
|
|
2
|
+
// documentation sites generated by the yad-docs / yad-docs-overview skills. The CONTENT generation
|
|
3
|
+
// (reading epic/architecture/contract/ui/stories → writing src/data/*.ts and theming index.css) is
|
|
4
|
+
// the AI step inside those skills; this module only does the mechanical parts: npm build (a
|
|
5
|
+
// subprocess, like yad-spec shelling npx repomix), the platform Pages wiring (reusing platform.mjs),
|
|
6
|
+
// and the manifest-hash staleness check (reusing the head-sha idea from repo.mjs). It NEVER touches
|
|
7
|
+
// epic state, approvals, or the contract lock — docs are an output enrichment, not a gate.
|
|
8
|
+
//
|
|
9
|
+
// Pure mapping fns (deployTargetFromHub / siteBasePath / docsArtifactHash / docsStale / pagesWorkflow)
|
|
10
|
+
// are exported for unit tests; the side-effecting runDocs orchestrates them.
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
import { c, log, ok, info, warn, hand, fail, readJSON, run, has, exists } from './lib.mjs';
|
|
15
|
+
import { PROJECT_FILES, VERSION } from './manifest.mjs';
|
|
16
|
+
import { detectPlatform, platformReady } from './platform.mjs';
|
|
17
|
+
import { gitHead } from './setup.mjs';
|
|
18
|
+
import { contractSurfaceHash } from './epic-state.mjs';
|
|
19
|
+
|
|
20
|
+
// ---- registry + site locations ------------------------------------------------------------------
|
|
21
|
+
function loadDocs(root) {
|
|
22
|
+
const regPath = path.join(root, PROJECT_FILES.docsConfig);
|
|
23
|
+
return { regPath, docs: readJSON(regPath, null) };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Per-epic site lives beside the epic artifacts; the overview is project-level under docs/.
|
|
27
|
+
export function siteDir(root, { epic, overview } = {}) {
|
|
28
|
+
if (overview) return path.join(root, 'docs/sdlc-site');
|
|
29
|
+
return path.join(root, 'epics', epic, 'docs-site');
|
|
30
|
+
}
|
|
31
|
+
export function manifestPath(root, { epic, overview } = {}) {
|
|
32
|
+
if (overview) return path.join(siteDir(root, { overview }), '.docs-build.json');
|
|
33
|
+
return path.join(root, 'epics', epic, '.sdlc/docs-build.json');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---- pure: platform/target + base path ----------------------------------------------------------
|
|
37
|
+
// hub.json platform -> the default Pages target (github-pages | gitlab-pages | none/build-only).
|
|
38
|
+
export function deployTargetFromHub(hub = {}) {
|
|
39
|
+
const platform = hub?.platform || detectPlatform(hub?.git_url || '');
|
|
40
|
+
if (platform === 'github') return 'github-pages';
|
|
41
|
+
if (platform === 'gitlab') return 'gitlab-pages';
|
|
42
|
+
return 'none';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// The Vite `base` for one site: join the project base path (from docs.json) with the per-site
|
|
46
|
+
// subpath. Per-epic sites nest under epics/<id>/; the overview is the Pages root. Always a
|
|
47
|
+
// leading+trailing slash so it works as a Vite base and a router basename.
|
|
48
|
+
export function siteBasePath(docs = {}, { epic, overview } = {}) {
|
|
49
|
+
const root = (docs.basePath || '/').replace(/\/+$/, '') || '';
|
|
50
|
+
const sub = overview ? '' : epic ? `/epics/${epic}` : '';
|
|
51
|
+
const joined = `${root}${sub}`.replace(/\/+/g, '/');
|
|
52
|
+
return joined ? `${joined.replace(/\/$/, '')}/` : '/';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---- pure: staleness ----------------------------------------------------------------------------
|
|
56
|
+
// The artifacts whose content a per-epic site is generated from (any that exist contribute).
|
|
57
|
+
// contract.md is deliberately EXCLUDED here: it is folded in via its CONTRACT-SURFACE block hash
|
|
58
|
+
// (docsArtifactHash's `extra`), so docs-staleness tracks the same locked surface as the contract
|
|
59
|
+
// lock — non-surface prose edits to contract.md don't needlessly mark the docs stale.
|
|
60
|
+
export function docsArtifactFiles(root, epic) {
|
|
61
|
+
const epicRoot = path.join(root, 'epics', epic);
|
|
62
|
+
const flat = ['epic.md', 'architecture.md', 'ui-design.md', 'DESIGN.md', 'test-cases.md']
|
|
63
|
+
.map((f) => path.join(epicRoot, f));
|
|
64
|
+
const storiesDir = path.join(epicRoot, 'stories');
|
|
65
|
+
const stories = exists(storiesDir)
|
|
66
|
+
? fs.readdirSync(storiesDir).filter((f) => f.endsWith('.md')).sort().map((f) => path.join(storiesDir, f))
|
|
67
|
+
: [];
|
|
68
|
+
return [...flat, ...stories].filter(exists).sort();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// sha256 over the (sorted) artifact files' bytes plus an optional `extra` string (the contract
|
|
72
|
+
// surface hash) — the content baseline a site was built from. Deterministic: the file list is
|
|
73
|
+
// sorted and each file is length-prefixed so concatenation is unambiguous (a + bc never collides
|
|
74
|
+
// with ab + c).
|
|
75
|
+
export function docsArtifactHash(files = [], extra = '') {
|
|
76
|
+
const h = createHash('sha256');
|
|
77
|
+
for (const f of [...files].sort()) {
|
|
78
|
+
const buf = fs.readFileSync(f);
|
|
79
|
+
h.update(`${path.basename(f)}:${buf.length}\n`);
|
|
80
|
+
h.update(buf);
|
|
81
|
+
}
|
|
82
|
+
if (extra) h.update(`\ncontract-surface:${extra}`);
|
|
83
|
+
return 'sha256:' + h.digest('hex');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Current HEAD sha per repo the epic touches (mirrors repo.mjs head-sha staleness).
|
|
87
|
+
export function repoHeadsFor(root, repos = [], registry = { repos: [] }) {
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const name of repos) {
|
|
90
|
+
const entry = (registry.repos || []).find((r) => r.name === name);
|
|
91
|
+
if (!entry) continue;
|
|
92
|
+
out[name] = gitHead(path.resolve(root, entry.path)) || null;
|
|
93
|
+
}
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Compare a build manifest to the current world; list the concrete reasons it is stale.
|
|
98
|
+
export function docsStale(manifest, { artifactHash, repoHeads = {}, templateVersion } = {}) {
|
|
99
|
+
const reasons = [];
|
|
100
|
+
if (!manifest) return { stale: true, reasons: ['never built'] };
|
|
101
|
+
if (artifactHash && manifest.artifactHash && artifactHash !== manifest.artifactHash) {
|
|
102
|
+
reasons.push('authored artifacts changed');
|
|
103
|
+
}
|
|
104
|
+
for (const [repo, head] of Object.entries(repoHeads)) {
|
|
105
|
+
const was = (manifest.repoHeads || {})[repo];
|
|
106
|
+
if (head && was && head !== was) reasons.push(`repo ${repo} HEAD advanced`);
|
|
107
|
+
}
|
|
108
|
+
if (templateVersion && manifest.templateVersion && templateVersion !== manifest.templateVersion) {
|
|
109
|
+
reasons.push(`doc shell upgraded (${manifest.templateVersion} → ${templateVersion})`);
|
|
110
|
+
}
|
|
111
|
+
return { stale: reasons.length > 0, reasons };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---- pure: the Pages CI workflow (committed by `yad docs sync --wire`) ---------------------------
|
|
115
|
+
// GitHub Actions deploy-pages, or a GitLab `pages` job. Both assemble a single `public/` tree: the
|
|
116
|
+
// overview at the root and every per-epic site under `epics/<id>/` — matching siteBasePath's nesting
|
|
117
|
+
// (overview at `<base>/`, epics at `<base>/epics/EP-<slug>/`). A concurrency group prevents the
|
|
118
|
+
// deploy from retriggering. The shared shell script keeps the two platforms byte-for-byte aligned.
|
|
119
|
+
const BUILD_PUBLIC = [
|
|
120
|
+
'mkdir -p public',
|
|
121
|
+
'if [ -d docs/sdlc-site ]; then (cd docs/sdlc-site && npm ci && npm run build) && cp -r docs/sdlc-site/dist/. public/; fi',
|
|
122
|
+
'for d in epics/*/docs-site; do [ -d "$d" ] || continue; id=$(basename "$(dirname "$d")"); (cd "$d" && npm ci && npm run build) && mkdir -p "public/epics/$id" && cp -r "$d/dist/." "public/epics/$id/"; done',
|
|
123
|
+
];
|
|
124
|
+
export function pagesWorkflow(platform) {
|
|
125
|
+
if (platform === 'gitlab') {
|
|
126
|
+
return `# yad-managed — built by \`yad docs sync --wire\`. include it from .gitlab-ci.yml:
|
|
127
|
+
# include: { local: .gitlab/ci/yad-docs.yml }
|
|
128
|
+
# Edit the skill, not this file.
|
|
129
|
+
pages:
|
|
130
|
+
stage: deploy
|
|
131
|
+
image: node:20
|
|
132
|
+
rules:
|
|
133
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
134
|
+
script:
|
|
135
|
+
${BUILD_PUBLIC.map((l) => ` - '${l.replace(/'/g, "'\\''")}'`).join('\n')}
|
|
136
|
+
artifacts:
|
|
137
|
+
paths: [public]
|
|
138
|
+
resource_group: pages
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
return `# yad-managed — built by \`yad docs sync --wire\`. Edit the skill, not this file.
|
|
142
|
+
name: yad-docs
|
|
143
|
+
on:
|
|
144
|
+
push:
|
|
145
|
+
branches: [main, master]
|
|
146
|
+
workflow_dispatch:
|
|
147
|
+
permissions:
|
|
148
|
+
contents: read
|
|
149
|
+
pages: write
|
|
150
|
+
id-token: write
|
|
151
|
+
concurrency:
|
|
152
|
+
group: yad-docs-pages
|
|
153
|
+
cancel-in-progress: true
|
|
154
|
+
jobs:
|
|
155
|
+
build-deploy:
|
|
156
|
+
runs-on: ubuntu-latest
|
|
157
|
+
environment:
|
|
158
|
+
name: github-pages
|
|
159
|
+
url: \${{ steps.deployment.outputs.page_url }}
|
|
160
|
+
steps:
|
|
161
|
+
- uses: actions/checkout@v4
|
|
162
|
+
- uses: actions/setup-node@v4
|
|
163
|
+
with:
|
|
164
|
+
node-version: 20
|
|
165
|
+
- name: Build the overview + per-epic sites into ./public
|
|
166
|
+
run: |
|
|
167
|
+
${BUILD_PUBLIC.map((l) => ` ${l}`).join('\n')}
|
|
168
|
+
- uses: actions/configure-pages@v5
|
|
169
|
+
- uses: actions/upload-pages-artifact@v3
|
|
170
|
+
with:
|
|
171
|
+
path: public
|
|
172
|
+
- id: deployment
|
|
173
|
+
uses: actions/deploy-pages@v4
|
|
174
|
+
`;
|
|
175
|
+
}
|
|
176
|
+
export function pagesWorkflowPath(platform) {
|
|
177
|
+
return platform === 'gitlab' ? '.gitlab/ci/yad-docs.yml' : '.github/workflows/yad-docs.yml';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ---- build (subprocess) -------------------------------------------------------------------------
|
|
181
|
+
function buildSite(dir) {
|
|
182
|
+
if (!exists(dir)) { warn(`no generated site at ${path.relative(process.cwd(), dir)} — run the yad-docs skill first`); return { ok: false, missing: true }; }
|
|
183
|
+
if (!has('npm')) { warn('npm not on PATH — cannot build; the CI workflow will build on push'); return { ok: false, noNpm: true }; }
|
|
184
|
+
const install = exists(path.join(dir, 'package-lock.json')) ? ['ci'] : ['install'];
|
|
185
|
+
log(` ${c.dim('$')} npm ${install[0]} ${c.dim(`(${path.relative(process.cwd(), dir)})`)}`);
|
|
186
|
+
const i = run('npm', install, { cwd: dir, stdio: 'inherit' });
|
|
187
|
+
if (!i.ok) { fail('npm install failed'); return { ok: false }; }
|
|
188
|
+
const b = run('npm', ['run', 'build'], { cwd: dir, stdio: 'inherit' });
|
|
189
|
+
if (!b.ok) { fail('npm run build failed'); return { ok: false }; }
|
|
190
|
+
return { ok: true, dist: path.join(dir, 'dist') };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ---- orchestration ------------------------------------------------------------------------------
|
|
194
|
+
export async function runDocs(root, { action = 'list', epic, overview, sync } = {}) {
|
|
195
|
+
const { docs } = loadDocs(root);
|
|
196
|
+
const targets = overview ? [{ overview: true }] : epic ? [{ epic }] : enumerateSites(root);
|
|
197
|
+
|
|
198
|
+
if (action === 'list') {
|
|
199
|
+
if (!docs) { warn('no docs target connected (.sdlc/docs.json) — run yad-connect-docs'); }
|
|
200
|
+
else {
|
|
201
|
+
log(c.bold('\ndocs target'));
|
|
202
|
+
info(`target ${c.cyan(docs.target)} scope ${docs.scope} base ${docs.basePath} ${docs.source === 'unavailable' ? c.yellow('(build-only)') : ''}`);
|
|
203
|
+
}
|
|
204
|
+
log(c.bold('\ngenerated sites'));
|
|
205
|
+
if (!targets.length) { info('none generated yet'); return { sites: 0 }; }
|
|
206
|
+
for (const t of targets) reportFreshness(root, t);
|
|
207
|
+
return { sites: targets.length };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (action === 'build' || action === 'deploy') {
|
|
211
|
+
let built = 0;
|
|
212
|
+
for (const t of targets) {
|
|
213
|
+
const r = buildSite(siteDir(root, t));
|
|
214
|
+
if (r.ok) { built++; ok(`built ${label(t)} ${c.dim('→ ' + path.relative(root, r.dist))}`); }
|
|
215
|
+
}
|
|
216
|
+
if (action === 'deploy') {
|
|
217
|
+
const platform = docs ? (docs.target === 'gitlab-pages' ? 'gitlab' : docs.target === 'github-pages' ? 'github' : null) : null;
|
|
218
|
+
if (!platform || !platformReady(platform)) {
|
|
219
|
+
hand('no Pages platform/CLI — built locally only; commit + push so the CI workflow can publish (yad docs sync --wire)');
|
|
220
|
+
} else {
|
|
221
|
+
ok(`deploy via the ${platform} Pages workflow on push (yad docs sync --wire installs it)`);
|
|
222
|
+
if (docs?.basePath) info(`will publish under ${docs.basePath}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { built };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (action === 'sync') {
|
|
229
|
+
if (sync === 'wire') return wirePages(root, docs);
|
|
230
|
+
// check (default) + refresh both compute staleness; refresh additionally rebuilds.
|
|
231
|
+
const registry = readJSON(path.join(root, PROJECT_FILES.reposRegistry), { repos: [] });
|
|
232
|
+
let stale = 0;
|
|
233
|
+
for (const t of targets) {
|
|
234
|
+
const s = freshness(root, t, registry);
|
|
235
|
+
if (s.stale) {
|
|
236
|
+
stale++;
|
|
237
|
+
warn(`${label(t)} — ${c.yellow('stale')}: ${s.reasons.join('; ')}`);
|
|
238
|
+
if (sync === 'refresh') buildSite(siteDir(root, t));
|
|
239
|
+
} else ok(`${label(t)} ${c.dim('— fresh')}`);
|
|
240
|
+
}
|
|
241
|
+
if (stale && sync !== 'refresh') hand('regenerate content with the yad-docs / yad-docs-overview skill (the AI step), then `yad docs deploy`');
|
|
242
|
+
return { stale };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
fail(`unknown docs action: ${action} (list | build | deploy | sync)`);
|
|
246
|
+
process.exitCode = 1;
|
|
247
|
+
return {};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---- helpers ------------------------------------------------------------------------------------
|
|
251
|
+
function enumerateSites(root) {
|
|
252
|
+
const out = [];
|
|
253
|
+
if (exists(siteDir(root, { overview: true }))) out.push({ overview: true });
|
|
254
|
+
const epicsDir = path.join(root, 'epics');
|
|
255
|
+
if (exists(epicsDir)) {
|
|
256
|
+
for (const e of fs.readdirSync(epicsDir).sort()) {
|
|
257
|
+
if (exists(path.join(epicsDir, e, 'docs-site'))) out.push({ epic: e });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return out;
|
|
261
|
+
}
|
|
262
|
+
function label(t) { return t.overview ? 'overview (docs/sdlc-site)' : `epic ${t.epic}`; }
|
|
263
|
+
|
|
264
|
+
function freshness(root, t, registry) {
|
|
265
|
+
const manifest = readJSON(manifestPath(root, t), null);
|
|
266
|
+
if (t.overview) {
|
|
267
|
+
// The overview is generated from the project pipeline definition, not an epic's artifacts.
|
|
268
|
+
const files = ['skills/sdlc/config.yaml', 'skills/sdlc/module-help.csv', 'docs/diagrams/sdlc-overview.mmd']
|
|
269
|
+
.map((f) => path.join(root, f)).filter(exists);
|
|
270
|
+
return docsStale(manifest, { artifactHash: docsArtifactHash(files), templateVersion: VERSION });
|
|
271
|
+
}
|
|
272
|
+
const epicMeta = readJSON(path.join(root, 'epics', t.epic, '.sdlc/state.json'), {});
|
|
273
|
+
const repos = epicMeta.repos || [];
|
|
274
|
+
const surface = contractSurfaceHash(path.join(root, 'epics', t.epic)); // null when no locked surface
|
|
275
|
+
return docsStale(manifest, {
|
|
276
|
+
artifactHash: docsArtifactHash(docsArtifactFiles(root, t.epic), surface || ''),
|
|
277
|
+
repoHeads: repoHeadsFor(root, repos, registry),
|
|
278
|
+
templateVersion: VERSION,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function reportFreshness(root, t) {
|
|
282
|
+
const registry = readJSON(path.join(root, PROJECT_FILES.reposRegistry), { repos: [] });
|
|
283
|
+
const s = freshness(root, t, registry);
|
|
284
|
+
if (s.stale) warn(`${label(t)} — ${c.yellow('stale')}: ${s.reasons.join('; ')}`);
|
|
285
|
+
else ok(`${label(t)} ${c.dim('— fresh')}`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function wirePages(root, docs) {
|
|
289
|
+
const platform = docs?.target === 'gitlab-pages' ? 'gitlab' : 'github';
|
|
290
|
+
const rel = pagesWorkflowPath(platform);
|
|
291
|
+
const dest = path.join(root, rel);
|
|
292
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
293
|
+
fs.writeFileSync(dest, pagesWorkflow(platform));
|
|
294
|
+
ok(`wired ${rel} ${c.dim(`(${platform} Pages)`)}`);
|
|
295
|
+
if (platform === 'gitlab') hand(`include it from .gitlab-ci.yml: include: { local: ${rel} }`);
|
|
296
|
+
else hand('commit it; set the repo Pages source to "GitHub Actions" so it publishes on push');
|
|
297
|
+
return { wired: rel };
|
|
298
|
+
}
|
package/cli/manifest.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { readFileSync } from 'node:fs';
|
|
|
10
10
|
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
11
11
|
export const VERSION = version;
|
|
12
12
|
|
|
13
|
-
// The
|
|
13
|
+
// The 29 hand-authored yad-* skills (mirrors skills/sdlc/install.sh).
|
|
14
14
|
export const SKILLS = [
|
|
15
15
|
'yad-analysis',
|
|
16
16
|
'yad-epic',
|
|
@@ -22,6 +22,10 @@ export const SKILLS = [
|
|
|
22
22
|
'yad-connect-design',
|
|
23
23
|
'yad-connect-testing',
|
|
24
24
|
'yad-connect-learning',
|
|
25
|
+
'yad-connect-docs',
|
|
26
|
+
'yad-docs',
|
|
27
|
+
'yad-docs-overview',
|
|
28
|
+
'yad-docs-sync',
|
|
25
29
|
'yad-learn',
|
|
26
30
|
'yad-spec',
|
|
27
31
|
'yad-implement',
|
|
@@ -121,6 +125,7 @@ export const PROJECT_FILES = {
|
|
|
121
125
|
designConfig: '.sdlc/design.json',
|
|
122
126
|
testingConfig: '.sdlc/testing.json',
|
|
123
127
|
learningConfig: '.sdlc/learning.json',
|
|
128
|
+
docsConfig: '.sdlc/docs.json',
|
|
124
129
|
version: '.sdlc/cli-version.json',
|
|
125
130
|
};
|
|
126
131
|
|
package/docs/index.html
CHANGED
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
<div class="lane">
|
|
285
285
|
<div class="lane-title">0 · One-time setup (team lead, per project)</div>
|
|
286
286
|
<div class="flow-v">
|
|
287
|
-
<div class="node plain"><strong>Install the module</strong><small><code>npx yadflow setup</code> — copies the
|
|
287
|
+
<div class="node plain"><strong>Install the module</strong><small><code>npx yadflow setup</code> — copies the 29 skills into your IDE dirs</small></div>
|
|
288
288
|
<div class="arrow-v">▼</div>
|
|
289
289
|
<div class="node plain"><strong>Wire each repo</strong><small><code>yad-checks</code> · <code>yad-pr-template</code> · <code>yad-review-comments</code> (CI gates, PR template, comment scaffold)</small></div>
|
|
290
290
|
<div class="arrow-v">▼</div>
|
|
@@ -447,7 +447,7 @@ each code repo/
|
|
|
447
447
|
<div class="term"><strong>State machine</strong><span class="badge b-core">core</span><br>The core design: the workflow is a fixed set of <em>states</em> (steps). Each state does its work and waits at a gate. The states never change — only the <em>trigger</em> (who moves work forward) changes. That is what lets the team start fully manual and automate gradually without rebuilding anything.</div>
|
|
448
448
|
<div class="term"><strong>Product hub (product repo)</strong><span class="badge b-core">core</span><br>The git repo that holds the shared “thinking”: epics, architecture, the contract, UI design, stories, reviews and all state. The brain of the project. Code never lives here.</div>
|
|
449
449
|
<div class="term"><strong>Code repo</strong><span class="badge b-core">core</span><br>A separate git repo holding real application code (e.g. backend, mobile, dashboard). Each story’s spec lives inside the code repo it belongs to, with a link back to the story in the hub.</div>
|
|
450
|
-
<div class="term"><strong>Skills source (this repo)</strong><span class="badge b-core">core</span><br>The <code>yadflow</code> repo itself — where the
|
|
450
|
+
<div class="term"><strong>Skills source (this repo)</strong><span class="badge b-core">core</span><br>The <code>yadflow</code> repo itself — where the 29 skills live and where you pull updates from. No real product work happens inside it.</div>
|
|
451
451
|
<div class="term"><strong>Front half (“decide” / the brain)</strong><span class="badge b-core">core</span><br>The first half of the workflow, run once per epic in the product hub: analysis (optional) → epic → architecture + contract → UI design → stories — each followed by a human review gate, ending at <code>ready-for-build</code>. <strong>Test cases</strong> is a parallel, non-blocking track that opens at the stories gate and runs alongside the build half. Permanently human-approved.</div>
|
|
452
452
|
<div class="term"><strong>Back half / build half (“build”)</strong><span class="badge b-core">core</span><br>The second half, run once per story per code repo, inside that repo: spec → implement → checks → PR → ship. These mechanical steps may earn automation over time.</div>
|
|
453
453
|
<div class="term"><strong>Gate</strong><span class="badge b-core">core</span><br>A stopping point after a step. The step writes its output and <em>waits</em>; a human must approve before the workflow moves on. “Every step stops at a gate” is the heart of the whole system.</div>
|
|
@@ -684,7 +684,7 @@ each code repo/
|
|
|
684
684
|
|
|
685
685
|
<!-- ======================================================= -->
|
|
686
686
|
<section id="skills">
|
|
687
|
-
<h2>6. The
|
|
687
|
+
<h2>6. The 29 skills — what each does, how to use it, and when</h2>
|
|
688
688
|
<p>A <strong>skill</strong> is an agent you invoke <em>by name</em> in your AI IDE — you just ask in
|
|
689
689
|
plain words, e.g. <em>“run <code>yad-epic</code>”</em>, adding any inputs the skill needs
|
|
690
690
|
(<code>repo: backend</code>, <code>story: EP-…-S01</code>, <code>action: wire</code>, …).
|
|
@@ -979,7 +979,7 @@ yad-status EP-istifta-inquiries # one epic in detail</code></pre>
|
|
|
979
979
|
|
|
980
980
|
<div class="skillcard">
|
|
981
981
|
<h4><code>npx yadflow setup</code></h4>
|
|
982
|
-
<div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Ten steps: preflight (git/node check), install all
|
|
982
|
+
<div class="row"><span class="lbl lbl-what">What</span> The guided first-run wizard. Ten steps: preflight (git/node check), install all 29 skills into the IDE dirs you pick, detect the hub’s platform + record the reviewer roster, connect a design tool (Figma-first; optional), connect a testing tool (Playwright-first; optional), connect a learning tool (DeepTutor-first; optional), connect your code repos, wire each one (CI gates, PR template, comment scaffold), optionally write the AI-review config, and stamp the installed version.</div>
|
|
983
983
|
<div class="row"><span class="lbl lbl-how">How</span></div>
|
|
984
984
|
<pre><code>cd <product-hub-repo>
|
|
985
985
|
npx yadflow setup</code></pre>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yadflow",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, ship, repo). A BMAD module +
|
|
3
|
+
"version": "2.7.0",
|
|
4
|
+
"description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, ship, repo). A BMAD module + 29 yad-* skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "AbdelRahman Nasr",
|
|
7
7
|
"license": "MIT",
|
package/skills/sdlc/config.yaml
CHANGED
|
@@ -176,6 +176,25 @@ learning:
|
|
|
176
176
|
# tokens in the registry (kb name + sources are plain references, never credentials). Opt-in, never a gate.
|
|
177
177
|
auth: user
|
|
178
178
|
|
|
179
|
+
# Interactive documentation (yad-connect-docs / yad-docs / yad-docs-overview / yad-docs-sync) — the
|
|
180
|
+
# generated React+Vite+Tailwind doc SITES. yad-docs builds a per-epic site (epics/EP-<slug>/docs-site/)
|
|
181
|
+
# from the authored artifacts, themed by the connected design system; yad-docs-overview builds the
|
|
182
|
+
# project SDLC-overview site (docs/sdlc-site/) from the pipeline definition, superseding docs/index.html.
|
|
183
|
+
# The deploy TARGET is a Pages host, auto-detected from hub.platform (github->github-pages,
|
|
184
|
+
# gitlab->gitlab-pages, null->build-only). The yad CLI does the npm build + Pages wiring + staleness;
|
|
185
|
+
# the content generation is the AI step. Docs are an OUTPUT ENRICHMENT — never a gate, never touch state.
|
|
186
|
+
docs:
|
|
187
|
+
registry: "{project-root}/.sdlc/docs.json" # project-wide docs/Pages connection (NOT per-epic)
|
|
188
|
+
targets: [github-pages, gitlab-pages] # supported Pages hosts; auto-detected from hub.platform
|
|
189
|
+
scope: hub # publish from: hub (default) | <repo-name> | dedicated
|
|
190
|
+
degrade: build-only # no Pages host / no gh|glab => build the dist, publish via CI
|
|
191
|
+
login_gate: false # public docs by default (the client-side gate is presentational only)
|
|
192
|
+
manifest: "{project-root}/epics/EP-<slug>/.sdlc/docs-build.json" # per-epic staleness baseline (yad-docs)
|
|
193
|
+
overview: "{project-root}/docs/sdlc-site/" # the project SDLC-overview site (yad-docs-overview)
|
|
194
|
+
# Auth: `yad-connect-docs` records only how to reach the Pages host (platform + base path); it stores NO
|
|
195
|
+
# tokens — publishing runs as the LOCAL user's own gh/glab or the platform CI. Idempotent, refreshable.
|
|
196
|
+
auth: user
|
|
197
|
+
|
|
179
198
|
# Hub platform + front-half review bridge (yad-connect-repos `detect-hub`; yad-review-gate + yad-hub-bridge).
|
|
180
199
|
# The product hub is itself a git repo on a platform. With the bridge enabled, the front-half review/
|
|
181
200
|
# comment/approval cycle runs through a real PR/MR on the hub: a review PR is opened per artifact, reviewers
|
package/skills/sdlc/install.sh
CHANGED
|
@@ -11,7 +11,7 @@ set -euo pipefail
|
|
|
11
11
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
12
12
|
cd "$ROOT"
|
|
13
13
|
|
|
14
|
-
SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-connect-design yad-connect-testing yad-connect-learning yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-status)
|
|
14
|
+
SKILLS=(yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-connect-design yad-connect-testing yad-connect-learning yad-connect-docs yad-docs yad-docs-overview yad-docs-sync yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-review-comments yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-status)
|
|
15
15
|
|
|
16
16
|
echo "Installing sdlc module from $ROOT/skills ..."
|
|
17
17
|
|
|
@@ -24,3 +24,7 @@ SDLC Workflow,yad-engineer-review,Engineer Review & Merge,ER,"Build-half Step E:
|
|
|
24
24
|
SDLC Workflow,yad-backfill,Backfill Specs,BF,"Build-half Step G: generate specs for already-built features in an existing repo. Confirm Repomix (npx repomix CLI), pack ONE feature (compress + git logs, secret-scan), feed to AI with a 'describe what exists, do not invent' prompt, write a DRAFT spec marked verified: false. Human approval (reuse yad-review-gate) makes it real. Boundary auto-proposed and human-confirmed. A change is blocked only until the features it touches have approved specs. Never auto-advances.",,{repo: <repo>} {feature: <name + globs>} {action: pack|draft|approve|gate},3-build,,,false,demo-repos/<repo>/specs/backfill/<feature>/,spec.md backfill-check.sh
|
|
25
25
|
SDLC Workflow,yad-run,Run (Automation),RN,"Phase 4 orchestrator: drive a story's back-half loop (spec→tasks→implement→checks) in one code repo, reading each step's automation dial from build-state — on machine_advance it advances on its own, on human_approve it stops for a human. Records every run in the trust log. Realizes Step B (a clean checks pass auto-advances to the engineer review when earned; any FAIL / scope overrun / contract touch HALTS). set-dial earns/reverts a step's automation (machine_advance gated by the trust threshold; front states and engineer-review refused); kill/unkill toggles the system-wide kill switch. Front states and the human merge gate never auto-advance.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <repo>} {action: run|set-dial|kill|unkill} {step: <back-step>} {to: human_approve|machine_advance},4-automate,yad-spec,yad-engineer-review,false,epics/EP-<slug>/.sdlc/,build-state/<story-id>.json trust-log.json
|
|
26
26
|
SDLC Workflow,yad-status,SDLC Status,SS,"Read-only: print the full front-state chain, per-step dials, contract lock, story repo tags, and pending approvals at the active gate. For stories in the build half, also print each back-half step's automation dial and status, the trust record (runs / % approved-unchanged / earned vs gathering evidence), and the system-wide kill-switch state.",,{epic: EP-<slug>},1-front,,,false,,
|
|
27
|
+
SDLC Workflow,yad-connect-docs,Connect Docs Target,DX,"Setup/maintenance: connect a docs/Pages publishing target so the interactive-docs steps can deploy the generated site (not just commit its source). Auto-detects the platform from .sdlc/hub.json (github->github-pages, gitlab->gitlab-pages, null->build-only), resolves the Vite base path, and records the target in .sdlc/docs.json (local-user auth, no stored tokens). Degrades to build-only when gh/glab is absent. Idempotent and refreshable; one connection per project. Not a gated state — never touches epic state or approvals.",,{action: connect|refresh|list|disconnect} {target: github-pages|gitlab-pages|none} {scope: hub|<repo>|dedicated} {base_path: <override>},0-setup,,yad-docs,false,.sdlc/,docs.json
|
|
28
|
+
SDLC Workflow,yad-docs,Author Docs Site,DS,"Generate the per-epic interactive documentation SPA (animated flow canvas + role-based stakeholder doc pages) from the epic's approved artifacts (epic, architecture, locked contract, ui-design, stories, code-context, test-cases), themed by the connected design system, into epics/EP-<slug>/docs-site/. Copies the vendored React/Vite/Tailwind shell verbatim and generates src/data/*.ts deterministically; drives the yad docs build/deploy CLI to publish to Pages (or build-only). An OUTPUT ENRICHMENT — never a gate; never mutates state.json, approvals, or the contract lock.",,{epic: EP-<slug>} {action: generate|refresh|deploy} {login_gate: true|false},,yad-review-gate,,false,epics/EP-<slug>/docs-site/,docs-site/ docs-build.json
|
|
29
|
+
SDLC Workflow,yad-docs-overview,Docs Overview Site,DO,"Generate the project SDLC-overview interactive site (docs/sdlc-site/) — every stage from setup to ship modeled as flow paths, system components, and stakeholder roles — reusing the same vendored shell, themed with yadflow's brand palette. Reads config.yaml + module-help.csv + the overview diagram as the pipeline source. Supersedes the hand-maintained docs/index.html (left as a thin redirect for one release). Not a gate — a project-level enrichment that regenerates whenever the skill set / pipeline changes.",,{action: generate|deploy},,,,false,docs/sdlc-site/,sdlc-site/ .docs-build.json
|
|
30
|
+
SDLC Workflow,yad-docs-sync,Docs Sync,DY,"Maintenance/CI: keep the generated doc sites fresh. Detect staleness (a content hash of the approved artifacts + the connected repos' HEAD shas + the doc-shell version vs each site's build manifest), report which sites drifted and why, regenerate + redeploy the stale ones, and wire a CI job that rebuilds on push (carrying [skip ci] + a concurrency group to prevent deploy loops). Generalizes the rule that feature work must hand-update docs/index.html + diagrams + skill counts. Refresh is always a human/CI decision; never a gate.",,{action: check|refresh|wire} {epic: EP-<slug>},,,,false,epics/EP-<slug>/.sdlc/,docs-build.json yad-docs.yml
|