@s2bp/ai-led-framework
v0.20.0
Published
AI-led workflow framework for Claude Code: persistent memory, ailed-* prefixed agents and skills, installable via npx @s2bp/ai-led-framework init.
Maintainers
Readme
🌍 Language: English · Français
AI-Led — workflow framework for Claude Code
A ready-to-use template that turns any project (new or existing) into an AI-agent-driven project, with:
- 🧠 a persistent memory (
memory/) kept up to date and used as the source of truth; - 🤖 21 agents prefixed
ailed-*covering 4 workflows (Discovery, Feature, Incident, Security); - 🛠️ 14 reusable skills for recurring tasks (status dashboard, memory-change review, ADR, git-flow, quality-gate, design-system, screen contact sheet…).
One-command install:
npx @s2bp/ai-led-framework initWhat init installs
| Folder | Contents |
| ------------------- | -------------------------------------------------------------- |
| .claude/agents/ | 21 ailed-*.md agents (callable via @ailed-<name>) |
| .claude/skills/ | 14 ailed-* skills (callable via /ailed-<name>) |
| .claude/commands/ | /ailed-bootstrap slash-command (framework bootstrap) |
| .claude/hooks/ | ailed-runtime-hook.js + PreToolUse/PostToolUse hooks in settings.json, feeding the live progress sidebar (watch/dashboard) and the ticket transition journal (.ailed/journal.jsonl) |
| memory/ | 17 project memory files (including config.md, process.md, conventions.md, writing-rules.md, market-watch.md and observations.md), in the chosen language |
| CLAUDE.md | the framework contract, in a delimited ai-led block — written even when the file already exists |
Existing files are never overwritten unless you pass --force (the settings.json hooks are
merged in non-destructively, and .ailed/ plus the generated reports are added to .gitignore).
CLAUDE.md is the one deliberate exception. The framework writes a delimited block into it:
<!-- ai-led:begin — bloc géré par le framework, ne pas éditer à la main -->
## AI-Led framework
…
<!-- ai-led:end -->Only that block is rewritten on each update; the rest of the file stays yours. Without it,
nothing makes memory/ the source of truth in an ordinary session, and the framework only applies
to the agents you invoke by hand. If your CLAUDE.md is just a pointer (@AGENTS.md), the block
is written into its target — where it will actually be read.
Configuration (memory/config.md)
init generates memory/config.md, the source of truth for tooling that agents read
before acting. It configures the language, trigram, output style and integrations:
Language of the memory/ files
Memory files are installed in the chosen language to ease human review. French by default, English available:
npx @s2bp/ai-led-framework init --lang=enOnly the memory/ files are translated; agents/skills stay in French. The sentinel value of a
disabled integration follows the language (aucun in fr, none in en) and stays consistent
between config.md and the agents.
Ticket trigram
The dev ticket prefix (e.g. ZZM-000001) is a trigram derived from the project name
(first 3 letters of the folder), overridable:
npx @s2bp/ai-led-framework init --trigram=ZZMOutput style
The verbosity level of agents and reports — concise · standard (default) · detailed.
This setting drives presentation only (the Claude Code display, /ailed-status syntheses, text
pushed to Jira/Confluence); memory/ always stays complete and git-versioned whatever you pick.
npx @s2bp/ai-led-framework init --style=conciseconcise— "get to the point" mode: no preamble or restating, short bullets, tables over prose, and on Jira a title + acceptance criteria as bullets. It never hides a risk, decision or blocker — cut the fluff, not the substance.standard— clear, structured summary (default).detailed— full explanations: reasoning, discarded alternatives, extended context.
Editable any time in memory/config.md, or forced for a single run: ai-led status --style=detailed.
Writing standard (ASD-STE100-derived profile)
Every text the framework produces follows a writing standard: memory, tickets, SPECs, ADRs, reports, changelog, and text pushed to Jira / Confluence. Goal: a text understood at once, and still understood in six months.
The profile derives from ASD-STE100 (Simplified Technical English), the aerospace standard for simplified technical English. It reuses the principles, not the dictionary — the ASD specification stays the property of its publisher. The French profile applies the same structural rules with its own list of forbidden phrases.
The 12 rules live in memory/writing-rules.md. The first five already change the result:
- one idea per sentence, 20 words at most (25 for an instruction);
- active voice, present tense, explicit subject; imperative for an instruction;
- one term = one meaning:
memory/glossary.mdis the authority, synonyms are forbidden; - no acronym the glossary does not define; three words per noun cluster at most;
- measured facts (number, ID, file name) instead of adjectives.
Language. The text uses the language of the memory/ files. The fr and en profiles share
the 12 rules; only the lists of forbidden phrases differ.
Two distinct dials. Output style sets the volume of a text
(concise / standard / detailed). The standard sets the shape of the sentences, whatever
that volume: a detailed report still uses short, active sentences.
Three levels of enforcement. The install injects the rule block into the 21 agents and the 13
skills in scope (one source, rendered at install time). memory/writing-rules.md carries the
detail and the alternatives. And an automatic checker verifies the measurable rules:
npx @s2bp/ai-led-framework lint # every memory/ file
npx @s2bp/ai-led-framework lint --strict # warnings become blocking
npx @s2bp/ai-led-framework lint <path> # one file or one folderThe report gives file:line, the rule and the offending extract. The exit code is 1 from the
first error, so the check fits in CI. /ailed-quality-gate includes it in its ticket-closing
checklist. The check applies to prose: code, tables, headings and Mermaid diagrams are skipped.
Out of scope. Promotional content (/ailed-promo, promo outputs of @ailed-communication)
follows the brand voice. Code and commit messages follow memory/conventions.md.
Disable it at install time (--writing=none) or later in the Writing section of
memory/config.md: agents then follow the Output style alone, and lint checks nothing.
LLM model per agent (token savings)
Each of the 21 agents runs on a model chosen for its function, so simple agents don't burn premium tokens:
opus— reasoning / judgment / critical review, where a bad output causes downstream rework (brainstorm,architect,planner,pm,analyst,review,security-review,rca);sonnet— capable standard execution at volume (dev,ux,test,communication,release,fact-check,check-secu,seo-aso,monetization,knowledge-audit,init-memory);haiku— mechanical collection / extraction (scout,check-log).
The mapping is the “LLM model per agent” table in memory/config.md (source of truth). The
harness reads the model from each agent's frontmatter, so after editing the table apply it:
npx @s2bp/ai-led-framework models # print the effective mapping
npx @s2bp/ai-led-framework models sync # apply the table to .claude/agents/*.mdYou can also set a model at install time: init --model-dev=opus --model-scout=sonnet
(tier: opus · sonnet · haiku · inherit). update re-applies whatever the table says.
Integrations (optional)
Monitoring, E2E tests and promo generation are disabled by default (none). As long as an
integration is set to none, the matching agent flags the missing prerequisite and stops
cleanly instead of assuming a tool. Enable them at install time or later by editing config.md:
npx @s2bp/ai-led-framework init \
--trigram=ZZM \
--monitoring=Sentry \
--e2e=Playwright \
--promo=Remotion \
--watch="MCP web search" \
--seo-aso="Search Console + Ahrefs" \
--ticketing=Jira \
--docs=Confluence| Area | Agent / skill involved | Example tool |
| --------------------- | ----------------------------------------------------------------------- | --------------------- |
| Monitoring / logs | @ailed-check-log | Sentry |
| End-to-end tests | @ailed-test, @ailed-dev | Playwright |
| Promo generation | /ailed-promo, @ailed-communication | Remotion |
| Market watch | @ailed-scout, @ailed-fact-check, @ailed-analyst, @ailed-monetization | MCP web / URLs |
| SEO / ASO | @ailed-seo-aso | Search Console, Ahrefs, App Store Connect |
| External ticketing | @ailed-pm, @ailed-planner, @ailed-dev | Jira (Atlassian MCP) |
| External documentation | @ailed-communication | Confluence (Atlassian MCP) |
@ailed-monetizationuses the Watch channel (no dedicated integration).@ailed-seo-asodegrades to Watch at low confidence when SEO / ASO is disabled.External ticketing / documentation — mirror principle:
memory/stays the local source of truth; whenJira/Confluenceis enabled, agents additionally sync to the tool via its MCP (a single Atlassian MCP covers both Jira and Confluence). Prerequisite: that MCP must be connected in the project's Claude Code, otherwise agents stay in local-file mode. When external ticketing is active, the ticket ID is the Jira key (e.g.ZZM-123, the trigram being the project key); theZZM-000001format is only the local-file convention.Where are tickets & docs created? Everything lives in
memory/config.md(Tool coordinates section). For Jira: project key = trigram by default, type Task for features (@ailed-planner) and Bug for incidents (@ailed-check-log) and CRITICAL/HIGH vulnerabilities (@ailed-check-secu). For Confluence: you provide a single root page URL;@ailed-communicationcreates anAI LED FRAMEWORKsub-page under it (if absent) and maintains one page permemory/*.mdfile (mirror, one-waymemory/→ Confluence). While a coordinate is missing, the agent asks for the value (or lists it via the MCP), then writes it back toconfig.md— never creation against a guessed target. See the examples below.
Existing technical conventions (optional). If the project already has a document describing its
coding conventions and technical organization, import it with --conventions=<path>: its content lands
under the In-place conventions part of memory/conventions.md (with a Source: line), its headings
demoted so that the part keeps it whole. The
two other parts, DDD audit and Target for new modules, stay empty for @ailed-architect (see
Project mode and the DDD path below). Omit the flag and TODO stubs are installed instead — the file
may stay partially empty and be filled later by hand or via @ailed-init-memory.
Local app (optional). config.md also carries the local app base URL (plus, optionally,
its start command and a test account). That is the only coordinate /ailed-screens needs to
capture the screens a dev touched. While it reads to set, the skill asks for the value then
rewrites it into config.md.
Project mode and the DDD path
init records a project mode in memory/config.md § Project mode: brownfield when the
repository already holds sources outside the ai-led files, greenfield otherwise. --mode= forces
it for a CI install, and a human corrects it in config.md at any time. doctor blocks while the
value is absent, to set or unknown, so every agent knows which branch to follow.
For every SPEC that creates a module or restructures an area, @ailed-architect delivers:
- a conventions audit.
memory/conventions.mdhas three parts: In-place conventions, DDD audit, Target for new modules. Each in-place convention gets a verdict —compatible,confinedorreplaced— that cites a numbered item of/ailed-ddd-checklist(33 anti-patterns). The target holds only compatible and replacement conventions, and it is the only convention a new context follows; legacy code keeps the in-place ones. - a context model per bounded context (
/ailed-context-model,memory/contexts/): language, aggregates with their invariants and owning method, value objects, use cases tied to acceptance criteria, ports, anti-corruption layer, target tree. Onbrownfieldthe model covers the SPEC's scope, and legacy code sits in the context map as a context of its own. - a boundary contract run by a dependency tool, locally and in CI. The architect compares at
least two candidates on an eight-criteria grid, tries the chosen one on the repository, traces
the choice in an ADR, and generates a baseline of the violations tolerated at adoption
(empty on
greenfield). When no candidate passes the grid, the contract becomes an architecture test in the project's own test runner.
A human validation point after Architect covers the audit, the target and the model
(PROPOSED → VALIDATED). @ailed-planner waits for VALIDATED and cuts each context into
vertical slices, one use case per ticket; the first ticket of a context installs the tool when
its ADR is still provisoire. @ailed-dev reads the model before coding, writes one test per
invariant and runs the contract before the MR. @ailed-review applies the checklist and cites the
rule and the location on every finding. /ailed-quality-gate gains two criteria: a VALIDATED
model, and no violation outside the baseline.
On an existing install, update adds the Project mode section, with the mode deduced from the
repository as init would, and the three parts of conventions.md: the rules the file already holds move under In-place conventions, headings
demoted, so the architect audits them. doctor warns on a ## section outside the three parts.
Two steps stay yours: run
update --refresh-rules=process.md to receive the new validation point in the Feature workflow,
and add the Context column to the approved-terms table of memory/glossary.md, which
--refresh-rules never rewrites.
init options
--lang=fr|en Language of the memory/ files (default: fr)
--trigram=XYZ Ticket prefix (default: 3 letters of the folder name)
--monitoring=NAME Monitoring tool, or disabled (default)
--e2e=NAME E2E testing tool, or disabled (default)
--promo=NAME Promo generation tool, or disabled (default)
--watch=NAME Market-watch channel (web search MCP / URLs), or disabled (default)
--seo-aso=NAME SEO / ASO tool (Search Console, Ahrefs, App Store Connect), or disabled (default)
--ticketing=NAME External ticketing (e.g. Jira, via MCP), or disabled (default)
--docs=NAME External documentation (e.g. Confluence, via MCP), or disabled (default)
--style=LEVEL Agent/report output style: concise | standard | detailed (default: standard)
--writing=NORM Writing standard for produced text: ste | none (default: ste)
--conventions=PATH Import an existing conventions / technical-organization file into memory/conventions.md (optional)
--mode=MODE Project mode: greenfield | brownfield (default: deduced from the repository, brownfield as soon as it holds sources)
-y, --yes Non-interactive mode (otherwise questions are asked in the terminal)
-f, --force Overwrite existing filesUpdating an existing project
When a project already uses an older version of the framework, bump it to the latest with:
npx @s2bp/ai-led-framework@latest updateupdate is the safe counterpart of init for projects already on board:
| Target | update behaviour |
| ----------------------------------------------- | ------------------------------------------- |
| .claude/agents/, .claude/skills/, .claude/commands/ | always rewritten to the new version |
| .claude/hooks/ailed-runtime-hook.js | always rewritten; the framework's settings.json entries are re-wired (an older matcher is upgraded in place, with no duplicate) and your own hooks, permissions and env vars are preserved |
| .gitignore | missing entries are appended inside the existing AI-Led block (no stacked headers) |
| memory/config.md, memory/process.md, memory/conventions.md (scaffold files) | additive section merge: sections the template gained are appended; your existing sections are never touched — except under --refresh-rules (see below) |
| memory/*.md (project data) never edited | cleanly rewritten to the new version (detected via .ailed/manifest.json) |
| memory/*.md (project data) edited | preserved as-is |
| New memory/ files | added |
| CLAUDE.md | only the ai-led block is rewritten; the rest of the file is preserved |
The config (trigram, integrations, language) is re-read from memory/config.md, so the
{{TICKET_PREFIX}}, {{MONITORING}}, … placeholders are re-applied correctly — you don't pass the
init flags again. Your own non-ailed- agents/skills/commands are left alone.
An unreadable config now stops the update. These values are baked into each agent's text as it is written. A row deleted from the Integrations table therefore installed its agent on the disabled word in silence — and
statuscould not even report it as disabled, since the row was gone. An annotated cell (**none** — everything stays in .md files) was worse: taken whole as the tool name, it matched neither the tool nor the disabled word, so every agent's "if ≠none" guard read as integration active. Values are now stripped of emphasis and trailing commentary, and a missing row aborts theupdatenaming which one (--forceoverrides).
--refresh-rules — letting reworded rules land
The section merge is additive: it only appends missing sections. When the headings already
match on both sides, a rule whose body the framework rewrote can never reach your project.
Over several versions, process.md ends up describing rules the framework no longer applies.
npx @s2bp/ai-led-framework@latest update --refresh-rulesWith no value, the flag only touches the pure rule files: process.md, writing-rules.md,
observations.md. config.md and glossary.md are excluded — they hold your values and your
terms, and restoring the template over them would destroy real content. To target one anyway,
deliberately: --refresh-rules=config.md.
How does
updateknow what you edited?init/updaterecord a hash of everymemory/file they write into.ailed/manifest.json(gitignored, local). On the nextupdate, a file whose hash is unchanged is deemed pristine → rewritten cleanly; otherwise it's preserved (data) or merged section-by-section (scaffold filesconfig.md/process.md). Sections present on both sides but diverging are reported, never overwritten. Aconventions.mdimported via--conventions=is excluded from the manifest, so it's never treated as pristine and never overwritten.
Generated reports already committed. Up to 0.15.0,
status --htmlwrote a timestamped file at the project root that git did not ignore, so plenty of projects committed a few of them over time.updatenow adds the right patterns to.gitignore, but a.gitignoredoes not untrack an already-indexed file:updatetherefore lists the reports still tracked and hands you the command to take them out of the repo (git rm --cached …). It never touches them itself — deleting versioned files is not an update's job.
Ticket history starts at install time. The journal (
.ailed/journal.jsonl) fills up as agents move tickets around. On an updated project, tickets already shipped therefore have no history: the popup shows "not recorded" for the earlier steps instead of inventing dates. The hook's first run records a baseline without fabricating any transition.
Why
@latest?npxreuses a cached copy of the package; the@latesttag forces a fetch of the newest published version instead of re-running the one already cached.Caveat: an agent or skill that was removed or renamed in a newer version is not auto-deleted (it would risk deleting your own files — plenty of projects keep their own skills in
.claude/skills/).doctorlists them, telling the ones the framework installed apart from yours. If you want a pristine framework tree, remove only the framework folders first, then re-run update:rm -rf .claude/agents .claude/skills .claude/commands npx @s2bp/ai-led-framework@latest update
memory/andCLAUDE.mdstay safe — they live outside the deleted folders.
Checking that an install is healthy (doctor)
npx @s2bp/ai-led-framework doctorupdate rewrites files without ever saying whether the result works. doctor answers that
question. It is read-only: it repairs nothing, it names the command that does.
| Check | What it catches |
| ----------- | --------------- |
| version | the gap between the version stamped in memory/config.md and the CLI's own |
| config | what the parser actually reads from the Integrations table, row by row: a missing row, an annotated cell |
| mode | a project mode absent, left to set or unknown — the agents would not know which branch to follow |
| contract | a missing ai-led block in CLAUDE.md (or in its target) |
| kanban | statuses no parser can read — the ticket is then invisible everywhere |
| memory | files with no baseline, which update will no longer refresh |
| rules | rule files trailing the template |
| volume | the weight of memory/ in tokens, files over budget, oversized kanban rows |
| archive | finished tickets still inline |
| orphans | agents/skills installed by an older version and removed since |
| hook | the runtime hook missing or not wired into settings.json |
| leftovers | memory/.ailed/, generated reports still tracked by git |
Exit code 1 while a problem remains, so it drops straight into CI. --quiet prints only
problems and warnings.
Cleaning up the backlog (archive)
npx @s2bp/ai-led-framework archive # dry run: says what it would do
npx @s2bp/ai-led-framework archive --apply # actually moves the rowsThe rotation rule has lived in memory/process.md from the start. What was missing is anything
that runs it: it depended on @ailed-release, and on a threshold counted in active entries
(40) that a file of essay-sized rows never crosses. An 85-ticket kanban can weigh 300 KB, i.e.
~79,000 tokens on every agent read, without a single guard firing.
| Option | Effect |
| ----------------- | ------ |
| (none) | dry run: how many tickets, bytes and tokens would be removed |
| --apply | actually writes |
| --status=A,B | statuses to move (default DONE; e.g. DONE,SUPERSEDED) |
| --keep=N | keeps the N most recent tickets in the live file |
| --older-than=Nd | only moves tickets created more than N days ago |
Rows move to memory/archive/kanban.md, grouped under the very section they came from.
Nothing is deleted: the process.md principle holds — nothing disappears, everything moves. If
features.md is older than kanban.md, the command says so before acting: a DONE ticket whose
functionality was never captured elsewhere should not leave the live file.
Not a
reset. The command deletes nothing and never touches live tickets. A backlog that swells almost never needs emptying: it needs what shipped to leave.
Unrequested findings (memory/observations.md)
A working agent sees things: debt, a minor bug, an improvement idea, a documentation gap. Turning them into tickets looks rigorous — in practice the backlog becomes unreadable, and the decision to do the work, which belongs to a human, ends up making itself.
A transverse rule, injected into every acting agent: a finding outside your mission does not become a ticket.
| Finding | Destination |
| -------------------------------------- | ----------- |
| Explicitly requested by a human | memory/kanban.md |
| CRITICAL / HIGH severity | memory/kanban.md and memory/observations.md |
| Everything else | memory/observations.md only |
This is exactly the pattern already applied to market watch: @ailed-scout collects widely into
market-watch.md, and only a human promotes a topic into the Feature workflow. The technical
side now follows the same discipline. An agent proposes a promotion; it does not decide it. An
open observation older than 90 days turns stale.
What changed in the agents:
@ailed-knowledge-auditrecords gaps instead of ticketing every score below 70 %;@ailed-check-loggained the severity gate@ailed-check-secualready had;@ailed-init-memorycaps itself at 10CK-tickets and proposes the rest.
What actually makes a kanban grow
Two measures, calibrated on a real 85-ticket project whose kanban weighed 300 KB:
- A cell past 1,500 bytes is a specification, not a ticket. The threshold is measured per
cell, not per row: a row legitimately carries a description, a scope and its acceptance
criteria — the observed median row is 1.8 KB, and capping the row would push the acceptance
criteria out of the ticket, which is worse than the disease. The median cell is 390 bytes and
the 90th percentile 1.3 KB, so past 1,500 it is an essay. It belongs in
memory/specs/, with the cell linking to it. - The prose share. On that project, 61 % of the file was not a table row: EPIC narratives,
review reports, dated comments piled under the tables. That was the dominant cost, and no guard
could see it.
doctorreports it past 40 %. An EPIC narrative belongs inmemory/specs/, the history of what shipped inmemory/archive/kanban.md.
An update is made in place: fix the cell, do not pile one more dated comment. doctor also
flags memory/ files over their byte budget — counting entries alone never measured what actually
costs.
The agents (prefix @ailed-)
| Agent | Role |
| ------------------------ | ---------------------------------------------------------- |
| @ailed-scout | Sourced market-watch collection (competitors, trends) |
| @ailed-seo-aso | SEO (web) / ASO (mobile) audit + competitor benchmark |
| @ailed-monetization | Challenges monetization (current/upcoming/absent) vs competitors |
| @ailed-fact-check | Anti-hallucination gate for the watch |
| @ailed-analyst | Watch → scored candidate topics |
| @ailed-brainstorm | Business need → challenged SPEC |
| @ailed-ux | SPEC → 3 wireframes + final mockup |
| @ailed-pm | SPEC → EPICs + roadmap |
| @ailed-architect | Conventions audit · context model · boundary contract · ADR |
| @ailed-planner | EPICs → atomic tickets <TRIGRAM>-* |
| @ailed-dev | Implements a ticket (branch + MR, never merges) |
| @ailed-review | MR review → PASS / CHANGES REQUESTED |
| @ailed-test | E2E tests (nominal, edge cases, regressions) |
| @ailed-communication | Changelog, features, release notes |
| @ailed-release | Quality gates → tag → close-out |
| @ailed-check-log | Logs/errors monitoring (24 h) · tickets CRITICAL/HIGH only |
| @ailed-rca | Root Cause Analysis of an incident |
| @ailed-check-secu | Vulnerability scan (deps, code, config) |
| @ailed-security-review | Security review of an MR (OWASP) |
| @ailed-init-memory | Rebuilds the memory of an existing project |
| @ailed-knowledge-audit | Measures memory completeness → observations.md |
The skills (prefix /ailed-)
ailed-status, ailed-memory-diff, ailed-adr, ailed-architecture-map, ailed-context-model,
ailed-ddd-checklist, ailed-git-flow, ailed-quality-gate, ailed-release-flow, ailed-promo,
ailed-design-system, ailed-wireframe, ailed-mockup-preview, ailed-screens.
ailed-design-system, ailed-wireframe and ailed-mockup-preview enrich @ailed-ux (shared
design baseline, 3 wireframe variants, mockup render + screenshots). They rely on the native
Claude Code skills frontend-design and chrome-devtools when present in the target
environment, and degrade gracefully otherwise.
/ailed-memory-diff — review what the agents changed in memory/
The memory is the source of truth, and the agents rewrite it constantly. Before a human validates
a SPEC, an ADR or a release, the useful question is not "what does the file say" but "what did
the agent change, and where". git diff answers it in raw hunks; /ailed-memory-diff
renders the same data grouped by markdown section:
npx @s2bp/ai-led-framework memory-diff # HEAD → working tree
npx @s2bp/ai-led-framework memory-diff --since=HEAD~1 --until=HEAD # the last commit
npx @s2bp/ai-led-framework memory-diff --since=develop # the whole branch delta
npx @s2bp/ai-led-framework memory-diff --html # self-contained HTML report
npx @s2bp/ai-led-framework memory-diff --clip # rich text on the clipboard- Grouped by section, not by hunk: for each file, file → section (an
H2 › H3breadcrumb) → added / removed lines, plus the tickets it touches (trigram read frommemory/config.md). - Review flags raised automatically: deleted section,
Last Updatednot bumped,memory/file not committed (invisible togit diff, yet a change as far as review goes). - What adds nothing gets summarized, not unfolded: an entirely new file (a fresh SPEC —
everything is a
+, over thousands of lines) is rendered as its table of contents; a non-Markdownmemory/file (an@ailed-uxHTML mockup) as its size alone.--fullunfolds both. Without this, a 2,500-line SPEC produces an unreadable report. - Deterministic and zero-token: the command does the collecting, the skill only adds the
interpretation (consistency across
kanban.md/features.md/decisions.md, content deleted without archiving, unsourced figures) and a verdict —RAS·à vérifier·à corriger. --htmlwrites a static, self-contained report (<timestamp>_ailed-memory-diff.html): no CDN, no script, no data sent, light/dark following the system theme.--cliploads the same report astext/htmlonto the clipboard, so it pastes formatted into Teams / Slack / Outlook / Confluence, which do not accept Markdown. Backends:wl-copy(Wayland),xclip(X11),osascript(macOS); with no backend it says so instead of pasting raw HTML. Nopandocdependency.
The skill is strictly read-only: it never fixes a diff. Corrections go through the agent that
owns the file (@ailed-pm, @ailed-architect, @ailed-planner, @ailed-release).
/ailed-screens — the end-of-dev contact sheet
Where ailed-mockup-preview renders the mockup before the dev, /ailed-screens renders
the real app after: a single HTML page gathering the screens the ticket touched,
desktop and mobile side by side, with one shot per state (empty list, validation error,
confirmed action…). Enough to review the wording, styling and actual behavior at a glance,
without opening the app screen by screen.
- Shot list deduced, then confirmed: screens come from the branch diff, states from the ticket's acceptance criteria; the list is shown to the human before any capture, because the file → route deduction does get it wrong (shared component, dynamic routing, modal with no URL of its own).
- For human eyes, not for the agent: the images are never read back by an agent — the skill costs close to zero tokens and returns no verdict.
- Ephemeral and ticket-scoped: everything lands in
.ailed/screens/<ticket>/<timestamp>/(git-ignored) — one PNG per shot, ameta.jsoncaptioning them (screen, route, state, acceptance criterion, viewport) and asheet.htmlto open. Nothing entersmemory/. Those shots surface on their own in the ticket popup ofailed-status.html, next to its history. - Non-blocking: it is not a quality gate. Without the
chrome-devtoolsMCP or a reachable app, the skill flags the missing prerequisite and stops cleanly.
@ailed-dev calls it automatically before opening the MR when the ticket touches the UI,
@ailed-test offers it after a PASS on a UI ticket (an offer, never a quality gate), and
it stays callable by hand. The app base URL is set in memory/config.md § Local app.
Project status & dashboard
Two complementary ways to get a read-only snapshot of the project (state, roadmap, kanban, features, market watch, process):
/ailed-status(in Claude Code) — an intelligent synthesis ofmemory/that highlights what needs a decision (pending human validations, candidate topics to promote, stale watch, disabled integrations).ai-led status(CLI) — a deterministic, zero-token terminal snapshot: progress bar, kanban counts and a "watch" list. Add--htmlto generateailed-status.html, a dashboard that opens on a visual synthesis: two pie charts (global progress + current milestone, approximate), three action counters (bugs to handle, open vulnerabilities, product arbitrations discovery → roadmap), an EPIC timeline whose every node is the same status donut as Overall progress (its tickets split by status, same colours, with the share ofDONEat the centre andn/totalunderneath — an EPIC with no linked ticket reads "no ticket" instead of a fake 0%), and a kanban board — one column per non-DONEstatus plus the 5 latestDONEtasks on the right, each card showing milestone → EPIC → ID → title and opening its detail in a popup. Popups stack: from an EPIC (or a status list), clicking a task row opens its detail on top, the list staying visible underneath —✕/Esc/ a click outside closes that level and returns to it. Archived tickets (memory/archive/kanban.md) count in too. Eachmemory/file's raw detail stays available, collapsed at the bottom — no server, no project data sent:
npx @s2bp/ai-led-framework status # terminal snapshot
npx @s2bp/ai-led-framework status --html # → ailed-status.html, the live report
npx @s2bp/ai-led-framework status --html --live # + regenerate whenever memory/ changes
npx @s2bp/ai-led-framework status --html --snapshot # → timestamped self-contained file, to shareBoth honor the Output style from config.md (concise tightens the output, detailed adds
milestones and in-progress tickets and expands the HTML accordions); --style=… forces it for a
run. The HTML loads marked + mermaid from a CDN to render the accordions' markdown and diagrams
(internet needed at view time).
A ticket's history and screens
A kanban card's popup gives, under its description:
- a dated history — created, development started, handed to test, finalised — with the
elapsed time between steps and the total lead time. Those dates are not typed in by
hand: the runtime hook re-diffs the statuses on every write to
memory/kanban.mdand appends the transitions to.ailed/journal.jsonl. Deterministic, zero tokens, and no agent discipline required — asedrun through Bash is seen just like anEdit, because it is the file's state that gets compared. A step that predates the journal reads not recorded rather than being guessed; - the screenshots
/ailed-screensproduced at test time, grouped by screen × state, desktop and mobile side by side, with the acceptance criterion they came from; click for full screen. Shots that could not be reached (a missing test account, say) and the console errors picked up are listed below rather than quietly dropped.
A live report rather than a timestamped one
The report carries a stable name: keep it open in a tab, bookmark it, and it redraws itself whenever the data changes — scroll position and open popup preserved, the popup even being replayed against the fresh data.
The design point that makes this work with no server: the HTML shell (~70 KB, rewritten on every
run) is separated from its payload (.ailed/status/data.js), and that payload is reloaded
through a <script> tag rather than fetch() — over file://, fetch() is blocked by CORS
while a script tag is not. A timestamp query acts as a cache-buster, and the page only redraws
when the content fingerprint changes (otherwise it would flicker on every poll).
The corollary on size: a single file rewritten on every run does not grow over time — its size is a function of the current state, not of how many times it was generated. What used to accumulate was the old timestamped name (one file per run, at the project root). The one item that really does grow is the screenshots, so they stay PNG files on disk referenced relatively, never inlined — inlining one shot costs ~300 KB of report.
npx @s2bp/ai-led-framework clean # prune earlier sheets + compact the journal
npx @s2bp/ai-led-framework clean --screens # keep only the latest sheet per ticket.ailed/ is derived and git-ignored: clean never touches anything versioned. And to
share a review, --snapshot does the opposite on demand — a timestamped, 100%
self-contained file with the shots inlined as base64, ready to send as-is.
Live progress sidebar (watch / dashboard)
To follow progress during a Claude Code session — which epic / task is in flight, which agent
just finished, is working, or is about to work — without re-running status, the framework ships a
continuously refreshed vertical sidebar:
npx @s2bp/ai-led-framework watch # the sidebar alone (drop it in a left-hand terminal)
npx @s2bp/ai-led-framework dashboard # tmux/zellij split: sidebar on the left · claude on the rightTop to bottom, the sidebar shows exactly the requested hierarchy:
AI-LED · progress
────────────────────────────
✓ EPIC-1 Foundations ← last treated epic
▶ EPIC-2 Payments ← current epic
✓ ZZM-000011 Payment model ← last treated task
▶ ZZM-000012 Checkout flow ← current task
✓ @architect (done) ← last agent
▶ @dev impl checkout ← current agent
· @review ← upcoming agents (workflow chain)
· @test
· @communication
· ZZM-000013 Refund ← upcoming tasks
· ZZM-000014 Webhooks
· EPIC-3 Reporting ← next epic
2/6 tickets DONE · featureGlyph legend: ✓ done (green) · ▶ in progress (blue) · · upcoming (dimmed). Status
detection is tolerant: IN_PROGRESS, `IN_PROGRESS` (backticks), in progress, WIP… are
all recognized (the Status column of memory/kanban.md / memory/epics.md).
⚠️ Why a separate pane and not a frozen zone inside the Claude Code window? Claude Code is a closed TUI whose rendering we don't control: a frozen in-window column can't be injected. A true frozen vertical left column is therefore obtained via a terminal split (tmux or zellij), with Claude Code on the right — hence
dashboard.
Data sources:
- Epics / tasks: read from
memory/epics.mdandmemory/kanban.md(statusesDONE,IN_PROGRESS,TODO…). Works even without the hook. - Agents (last / current / upcoming): fed by the
.claude/hooks/ailed-runtime-hook.jshook (installed byinit/update), wired via.claude/settings.json(PreToolUseandPostToolUse, matcher*). On every subagent call it writes the active agent to.ailed/runtime.json(gitignored); the running agent shows a live chrono (▶ @dev impl · 2m14s) so the panel breathes even during a long agent run. Upcoming agents are projected from the detected workflow chain (Discovery / Feature / Incident / Security, seememory/process.md). - Ticket journal: when a tool finishes, the hook compares
memory/kanban.md's signature (mtime + size) against its last snapshot; if it moved, it re-diffs the statuses and appends the transitions to.ailed/journal.jsonl— the source of the history shown in a ticket's popup on the dashboard. Hence the*matcher onPostToolUse: a ticket can move throughEdit,Writeor asedrun inBash. When nothing changed, the hook costs onestat(). - Main-loop heartbeat:
PreToolUserecords the last tool the main loop touched, shown as⋯ Edit · 3swhen no subagent is running — so the panel stays live during direct work, not only at agent boundaries. (These fire the hook on every tool call; remove the matcher*entries from.claude/settings.jsonto opt out — the panel then loses agents and the dashboard loses ticket history.)
Tilix / GNOME Terminal (VTE): the refresh clears the scrollback (
\x1b[3J) on each redraw, so the live pane no longer stacks stale frames in your scroll history.
Options: --width=N (sidebar width), --once (render once and exit),
dashboard --cmd="…" (command launched on the right of the split, default claude). A zellij
layout is generated at .ailed/dashboard.kdl.
The 4 workflows (see memory/process.md)
Discovery : (scout · seo-aso · monetization) → fact-check → analyst → (human validation) → brainstorm
Feature : brainstorm → ux → pm → architect → planner → dev → review → test → communication → release
Incident : check-log → rca → dev → review → test → communication
Security : check-secu → security-review → dev → review → test → communicationMandatory human validation points: after analyst (promoting a topic), after brainstorm
(SPEC), after ux (mockup), before release (tag).
Discovery workflow (competitive intelligence)
An exploratory workflow that feeds memory/market-watch.md to surface new topics, without
ever creating a ticket or writing to the roadmap:
- Enable the watch: set the
Watchintegration inmemory/config.md(a watch channel: web search MCP, or a curated list of competitor URLs/feeds). While it isaucun/none, the agents stop cleanly. - Specialist collectors, all writing to Raw observations (sourced + dated):
@ailed-scout: market/feature/competitor signals;@ailed-seo-aso: discoverability — SEO (web) or ASO (mobile) audit of our product + gaps vs competitors;@ailed-monetization: current/upcoming/absent monetization model challenged vs competitors (pricing grids). The detail (keyword matrices, pricing grids) goes into the Specialised analyses section.
@ailed-fact-checkverifies/downgrades/rejects each observation, whatever its origin (anti-hallucination gate).@ailed-analystclusters, deduplicates againstfeatures.md/roadmap.mdand produces a scored candidate topics backlog (Impact/Effort/Alignment).- Human validation: you move a topic from
candidatetovalidated→brainstorm. It then joins the Feature workflow via@ailed-brainstorm.
Continuous improvement: re-run (scout · seo-aso · monetization) → fact-check → analyst on a
cadence (e.g. monthly, via /loop or a scheduled agent) to refresh the watch and propose a new
shortlist. Discovery runs in a loop; promotion to the roadmap and deployment remain a human
decision — that is the framework's safeguard.
Quick start
cd my-project
npx @s2bp/ai-led-framework initThen in Claude Code, run the /ailed-bootstrap slash-command (installed by init), which
routes automatically based on context:
- Existing project →
@ailed-init-memory(rebuilds the memory) then@ailed-knowledge-audit. - New project →
@ailed-brainstormto frame the first SPEC.
Then, at regular intervals:
npx @s2bp/ai-led-framework doctor # is the install actually operational?
npx @s2bp/ai-led-framework archive # has what shipped left the kanban?Concrete examples: Jira & Confluence on an existing project
Shared prerequisite: the Atlassian MCP (covers both Jira and Confluence) is connected in
the project's Claude Code. memory/ stays the local source of truth; Jira/Confluence are its
shareable mirror.
Example 1 — From a need to a Jira ticket (Feature workflow)
cd my-existing-project
npx @s2bp/ai-led-framework init --trigram=ZZM --ticketing=Jira --docs=ConfluenceThen in Claude Code:
/ailed-bootstrap→ since the project already exists, it chains@ailed-init-memorythen@ailed-knowledge-auditto rebuild memory from the code.@ailed-brainstorm: you describe the need ("allow PDF export of reports"). The agent produces a challenged SPEC. → human validation of the SPEC.@ailed-pmturns the SPEC into EPICs (and creates/updates the Jira epics via the MCP).@ailed-architectaudits the in-place conventions, writes the context model and the boundary contract, and records ADRs inmemory/decisions.md(mirrored to Confluence later by@ailed-communication). → human validation of the audit, the target and the model. The Jira project key already defaults toZZM— no other coordinate is needed at this stage.@ailed-plannersplits the EPIC into atomic tickets. For each ticket: write tomemory/kanban.mdthen create the Jira issue via the MCP. The issue comes back with its keyZZM-123, which becomes the ticket ID mirrored inmemory/kanban.md.
Result: a Jira ticket ZZM-123 created, tracked locally, linked to its EPIC and the SPEC.
Example 2 — Develop an existing Jira ticket
cd my-existing-project
npx @s2bp/ai-led-framework init --trigram=ZZM --ticketing=Jira --docs=ConfluenceThen in Claude Code:
/ailed-bootstrap(rebuilds memory if not done yet).@ailed-dev ZZM-123: the issue already exists in Jira (created by another team, say). The agent pulls it via the MCP (title, description, acceptance criteria) and mirrors it intomemory/kanban.mdif absent. It moves the issueTODO → IN_PROGRESS, creates the branchfeat/ZZM-123-..., develops, opens the MR (never merges), links the MR URL to the issue and moves it toTO_TEST.@ailed-reviewthen@ailed-testvalidate the MR;@ailed-communicationupdates the local changelog and syncs the Confluence mirror: anAI LED FRAMEWORKsub-page (created if absent under the root page), one page permemory/*.mdfile. On the first pass, since the root page isto set, the agent asks for the Confluence URL (e.g.…/wiki/spaces/SPACE/pages/PAGE_ID/Page+Title) and saves it toconfig.md.
If the Atlassian MCP is not connected, each agent flags it and continues in local-file mode: no blocking, just no external sync.
Developing the framework itself
templates/claude/agents/ # agent sources (placeholders {{TICKET_PREFIX}}, {{E2E}}…)
templates/claude/skills/ # skill sources
templates/claude/commands/ # slash-command sources (/ailed-bootstrap)
templates/memory/fr/ # French memory source (default)
templates/memory/en/ # English memory source
# (add a language folder here to offer a new one)
bin/ai-led.js # install CLI (Node, zero dependencies)Test the install locally without publishing:
node bin/ai-led.js init --trigram=TST -y # from a target project
# or
npm link && ai-led initLicense
MIT.
