@erclx/aitk 0.37.0 → 0.38.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/claude-design-extract/REQUIREMENT.md +42 -0
- package/claude/skills/claude-diagram/REQUIREMENT.md +45 -0
- package/claude/skills/claude-groundwork/REQUIREMENT.md +44 -0
- package/claude/skills/claude-memory-capture/REQUIREMENT.md +40 -0
- package/claude/skills/claude-orchestrate/REQUIREMENT.md +44 -0
- package/claude/skills/claude-roadmap/REQUIREMENT.md +41 -0
- package/claude/skills/claude-screencast/REQUIREMENT.md +39 -0
- package/claude/skills/claude-slides-draft/REQUIREMENT.md +39 -0
- package/claude/skills/claude-tasks/REQUIREMENT.md +43 -0
- package/claude/skills/claude-ui-test/REQUIREMENT.md +40 -0
- package/claude/skills/claude-ux-audit/REQUIREMENT.md +39 -0
- package/claude/skills/claude-worktree/REQUIREMENT.md +41 -0
- package/claude/skills/cli-script/REQUIREMENT.md +40 -0
- package/claude/skills/create-rule/REQUIREMENT.md +42 -0
- package/docs/agents.md +12 -5
- package/docs/operating-model.md +1 -1
- package/docs/visual-design-workflow.md +1 -1
- package/package.json +1 -1
- package/src/commands/context.ts +76 -11
- package/src/context/folders.ts +85 -13
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-design-extract
|
|
3
|
+
description: Why a design system is drafted from what the tree already holds, and how a proposed value is kept distinguishable from a sourced one
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude design extract requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a session asked for a design system either invents token values with nothing behind them or refuses because the project ships no stylesheet. It writes over a `.claude/DESIGN.md` someone already filled. It fills every cell so the file looks complete, which leaves a reader unable to tell a hex sampled from the CLI apart from one the session made up, and a proposal nobody flagged gets built on. A trailing tag column added to mark that uncertainty breaks the render parser instead.
|
|
11
|
+
|
|
12
|
+
The greenfield case is the one that fails quietly. A project with no UI code still has a personality paragraph and a stated platform, so values can be proposed against a signal rather than pulled from a default palette. A session that does not know this reports there is nothing to extract.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Pick the source or greenfield path from what the project tree holds, never from a flag or an argument
|
|
17
|
+
- Announce which path ran, since the two produce different-looking output from one skill
|
|
18
|
+
- Fetch the seed template and keep every heading and table header intact, because the render parser reads them
|
|
19
|
+
- Mark any cell not traced to a source value inline inside the cell value, and take the uncertainty into the sentence when the section is prose
|
|
20
|
+
- Anchor every greenfield proposal to a personality or requirements signal rather than to a default
|
|
21
|
+
- Render the preview and report its path, so the proposals get looked at before they are committed
|
|
22
|
+
|
|
23
|
+
## Must not
|
|
24
|
+
|
|
25
|
+
- Overwrite a `.claude/DESIGN.md` that already carries content beyond the seed
|
|
26
|
+
- Guess a value on the source path when no signal exists. Leave the cell blank instead.
|
|
27
|
+
- Invent a non-goal. A proposed motion line is correct when nothing rules motion out.
|
|
28
|
+
- Put the verify tag in a trailing column, which breaks the row
|
|
29
|
+
- Rewrite the transcribed personality paragraph, which is the one section that is not a proposal
|
|
30
|
+
|
|
31
|
+
## Guards
|
|
32
|
+
|
|
33
|
+
- `.claude/DESIGN.md` already populated: stop rather than mutating it
|
|
34
|
+
- `aitk` not on PATH: stop, since the seed template and the render both need it
|
|
35
|
+
- Greenfield path with no `.claude/REQUIREMENTS.md` or no `## Personality` section: stop, because there is nothing to propose against. Evaluate this only after Step 1 picks the path.
|
|
36
|
+
|
|
37
|
+
## Out of scope
|
|
38
|
+
|
|
39
|
+
- Mutating an existing design system, which is a direct edit of the file rather than a skill
|
|
40
|
+
- Producing the HTML and CSS preview, which `aitk design render` owns
|
|
41
|
+
- Auditing the implemented UI against the tokens, which `claude-ux-audit` owns
|
|
42
|
+
- Architecture and flow diagrams, which `claude-diagram` owns
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-diagram
|
|
3
|
+
description: Why diagrams are a folder of per-kind entries rather than one file, and why the verified marker is spent only on a render that was read back
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude diagram requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a session asked to refresh one view rewrites the whole surface, so four diagrams nobody checked change in the same commit and the reviewer cannot tell which one the pass was about. It picks a filename of its own, and the next refresh writes a duplicate beside the old entry instead of finding it. It reaches for C4, state, or class diagrams that render differently in every viewer.
|
|
11
|
+
|
|
12
|
+
The verification failure is the expensive one. A mermaid source can satisfy every rule in the standard and still render as a picture that asserts something false about the system, so a session that judges the source and stamps the entry as verified has spent the only signal a reader has on a diagram nobody looked at. The standards hook toggles off inside a fenced block, so a banned character in a node label passes silently while the same character one line below is caught.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Write one entry per kind under the filename and category the standard fixes, so a later refresh finds its target
|
|
17
|
+
- Pick which entries this pass writes from whether the source signal exists and whether the pass has reason to touch it
|
|
18
|
+
- Stay inside `flowchart` and `sequenceDiagram`
|
|
19
|
+
- Render every entry this pass wrote and read the image back against what the entry means to say
|
|
20
|
+
- Stamp the verified marker only on an entry whose render was read back and judged correct
|
|
21
|
+
- Name every skipped render and every surviving defect in the output
|
|
22
|
+
- Regenerate the catalog from sibling frontmatter after the last entry is written
|
|
23
|
+
- Read the node labels by hand, since no hook checks inside a fence
|
|
24
|
+
|
|
25
|
+
## Must not
|
|
26
|
+
|
|
27
|
+
- Rewrite an entry this pass had no reason to touch, or report one as written when it was left alone
|
|
28
|
+
- Stamp an untouched entry, a skipped render, or an entry whose defect survived
|
|
29
|
+
- Report a clean verification when a check did not run
|
|
30
|
+
- Commit a render, which is a verification artifact rather than a deliverable
|
|
31
|
+
- Redraw a diagram during a migration pass, since a rewrite and a move landing together leaves no way to tell which one broke it
|
|
32
|
+
- Loop on a defect past two correction passes
|
|
33
|
+
|
|
34
|
+
## Guards
|
|
35
|
+
|
|
36
|
+
- No architecture file, no requirements file, and no folder structure to scan: stop, because nothing anchors a diagram
|
|
37
|
+
- Render fails for any reason: continue to the output and name the skipped check. A missing renderer degrades the loop rather than failing it.
|
|
38
|
+
- `aitk` not on PATH: report the catalog as stale rather than hand-editing it
|
|
39
|
+
|
|
40
|
+
## Out of scope
|
|
41
|
+
|
|
42
|
+
- Design tokens and the visual system, which `claude-design-extract` owns
|
|
43
|
+
- UI roughness, which `claude-ux-audit` owns
|
|
44
|
+
- Writing the catalog file, which the index regen owns from frontmatter
|
|
45
|
+
- Deleting a pre-split flat diagrams file after a migration, which stays the user's call
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-groundwork
|
|
3
|
+
description: Why a question that has not been measured gets a disposable folder instead of a plan, and the write scope that lets the track run without pausing
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude groundwork requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a question nobody has measured is answered with a plan. The session commits to an approach before the current state is known, and the reasoning that produced it lives in a conversation that compacts away. A research pass that does run spreads its findings across chat, so the next session re-measures what this one already counted, or worse carries a figure from recall and states it as current.
|
|
11
|
+
|
|
12
|
+
Two failure modes cost more than the rest. A track that fans out to subagents returns findings without the reasoning that makes the folder worth keeping, which turns a conversation into a search result. And an experiment fixture written under the project root loads that project's own instruction files through the ancestor chain, so the arm measures the repository instead of the question, and the result reads as evidence either way.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Detect open, resume, and close from the folder itself, matching the topic against tracks already there before deriving a slug
|
|
17
|
+
- Apply the qualifying test in open mode alone, so a track already measured is not refused by the test that admitted it
|
|
18
|
+
- Measure the current state now rather than carrying a figure from a previous session
|
|
19
|
+
- Carry a lean and the finding that would overturn it on every open question, or admit that a measurement is missing
|
|
20
|
+
- Confine writes to the track folder, with the close-time task file and the experiment fixture as the only exceptions
|
|
21
|
+
- Link every claim about a source outside the project, and list an unread source as a lead rather than citing it
|
|
22
|
+
- Put a fixture a headless run is pointed at outside the repository
|
|
23
|
+
- Write the next-session file self-contained, since the folder is unbacked and dies with the machine
|
|
24
|
+
|
|
25
|
+
## Must not
|
|
26
|
+
|
|
27
|
+
- Write a feature plan, a source change, a standard, a rule, or a reference doc
|
|
28
|
+
- Dispatch subagents. A search too large to run inline is a finding that the question is too broad.
|
|
29
|
+
- Close while an open question quietly fails an outcome, rather than resolving it or recording it as knowingly accepted
|
|
30
|
+
- Spawn more than three billed headless runs without asking
|
|
31
|
+
- Match a topic against the shared scratch directory, which holds folders that were never tracks
|
|
32
|
+
- Pause for approval between steps, which the write scope is what makes safe
|
|
33
|
+
|
|
34
|
+
## Guards
|
|
35
|
+
|
|
36
|
+
- No topic given: stop rather than inferring one
|
|
37
|
+
- Fewer than two of the three qualifying conditions hold in open mode: stop and route to the planning skill, because the decision is already made
|
|
38
|
+
- Resume and close are exempt from that test by definition
|
|
39
|
+
|
|
40
|
+
## Out of scope
|
|
41
|
+
|
|
42
|
+
- Writing the plan the track concludes toward, which `claude-feature` owns
|
|
43
|
+
- Implementing anything the track recommends
|
|
44
|
+
- Persisting the folder. It is gitignored and disposable, which is what makes it the right container for an unanswered question.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-memory-capture
|
|
3
|
+
description: What earns a memory file at the end of a session, the threshold that keeps the folder signal-bearing, and why capture never curates
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude memory capture requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, what a session learned dies with it, and the correction the user made in message four is re-earned next week. What does get written is a narrative of how the session went wrong rather than a rule that fires again, so a reader gets a story and no instruction. A new file lands beside one already holding the same topic, and the folder grows two entries per subject until nothing in it can be trusted as current.
|
|
11
|
+
|
|
12
|
+
The threshold is what the folder lives or dies on. A first-occurrence slip saved as feedback is noise that costs every later session a read, and a fact the repository already records is worse, because it duplicates a source that will be updated without it. From a linked worktree, writes resolved against `pwd` land in a second memory folder nothing else reads.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Resolve every read and write at the main worktree root, so parallel worktrees write one folder
|
|
17
|
+
- Classify each candidate as feedback, project, user, or reference before writing
|
|
18
|
+
- Apply the save threshold, firing feedback only on an explicit correction or a pattern that repeated twice
|
|
19
|
+
- Grep the folder for an existing file on the topic and update it in place rather than adding a second
|
|
20
|
+
- Keep a feedback or project body to the rule, one line of why, and one line of when it fires next
|
|
21
|
+
- Append the row to the index so the entry is discoverable
|
|
22
|
+
- Report that nothing was worth capturing rather than manufacturing an entry to show work
|
|
23
|
+
|
|
24
|
+
## Must not
|
|
25
|
+
|
|
26
|
+
- Write a session narrative or a recovery account in place of the pattern
|
|
27
|
+
- Save a first-occurrence slip as feedback
|
|
28
|
+
- Create a second file on a topic the folder already holds
|
|
29
|
+
- Save what the repository already records in code, git history, or its instruction files
|
|
30
|
+
- Curate, promote, or delete an existing entry
|
|
31
|
+
|
|
32
|
+
## Guards
|
|
33
|
+
|
|
34
|
+
- No correction, confirmation, or context disclosure worth persisting: stop with a pass
|
|
35
|
+
- Memory folder absent at the main root: create it rather than stopping
|
|
36
|
+
|
|
37
|
+
## Out of scope
|
|
38
|
+
|
|
39
|
+
- Curating what is already in the folder, which `claude-memory-review` owns
|
|
40
|
+
- Promoting an entry into an instruction file or a skill body, which mutates how the agent operates and ships as its own change
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-orchestrate
|
|
3
|
+
description: What the one warm control session owns, why it plans and reviews without building or merging, and the collision rule that binds parallelism
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude orchestrate requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, the session holding the cross-feature picture starts building, and the picture goes with it. Two features land on the same wiring seam because nobody listed their file sets against each other, and the second pull request rebases onto a tree it was never planned against. Findings from a merged pull request stay in a thread nobody re-reads, so the rule they should have changed never changes.
|
|
11
|
+
|
|
12
|
+
The queue fails in both directions. It empties and a free worker waits, or it fills with whatever is oldest rather than with what establishes a mechanism. A plan handed over unverified is the quiet one, since it goes stale from whatever merged after it was written, and a worker that trusts its account of the tree builds against a shape that no longer exists. A second orchestrator makes all of this unrecoverable, because the board is gitignored and neither session can read the other's writes.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Read the priority file for execution order, since the index sorts by filename and states no order
|
|
17
|
+
- Report the state of play so the human knows what to launch, what to review, and what to merge
|
|
18
|
+
- Verify a plan against the tree before handing it over, counting the sites it claims and opening the files it describes
|
|
19
|
+
- List a candidate's file set against every track in flight, and serialize when the sets are not disjoint
|
|
20
|
+
- Place every finding a merged pull request produced against the surface that owns it
|
|
21
|
+
- Date the roadmap line from that file's last commit, so an old sequence reads as old
|
|
22
|
+
- Keep one planned, non-conflicting task in reserve beyond what is running
|
|
23
|
+
|
|
24
|
+
## Must not
|
|
25
|
+
|
|
26
|
+
- Implement a feature or edit any tracked file from this session
|
|
27
|
+
- Merge. Recommend merge or changes and leave the gate to the human.
|
|
28
|
+
- Spawn worker sessions with agents, since every build is meant to be an independent steerable stream
|
|
29
|
+
- Hand a worker anything but a plan, because scope lives there
|
|
30
|
+
- Run a second orchestrator against the same board
|
|
31
|
+
- Promote a task to fill the queue when nothing qualifies. A thin queue is a real answer.
|
|
32
|
+
|
|
33
|
+
## Guards
|
|
34
|
+
|
|
35
|
+
- Priority file absent: report the queue and say the order is unrecorded rather than inferring it from the index
|
|
36
|
+
- Roadmap absent: omit the roadmap line rather than asserting a version the file does not state
|
|
37
|
+
- Roadmap present but never committed: report the date as uncommitted, since a blank reads as a formatting slip
|
|
38
|
+
|
|
39
|
+
## Out of scope
|
|
40
|
+
|
|
41
|
+
- Writing the plan itself, which `claude-feature` owns and this session runs rather than reimplements
|
|
42
|
+
- Reviewing a worker's pull request, which `claude-pr-review` owns
|
|
43
|
+
- Entering the worktree a build runs in, which the human opens
|
|
44
|
+
- The operating model this enacts, which the toolkit's own docs hold
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-roadmap
|
|
3
|
+
description: Why versions are sequenced from the MVP list alone, what makes a version a usable increment, and the lifecycle gate that stops a second pass
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude roadmap requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, versions are invented from a sense of what should come next rather than sequenced from the scope the requirements already fixed. Each one reads as a milestone nobody can use, because the ordering follows what feels foundational instead of what a user can then do, and a version that de-risks nothing sits ahead of the subsystem the whole plan rests on. The roadmap then drifts into task-level steps, which duplicates the plan folder and goes stale the first time a file moves.
|
|
11
|
+
|
|
12
|
+
An update is where the file quietly breaks. Rewriting rows that never changed hides the one line that moved, so a reader diffing the roadmap learns nothing from it. And a project whose MVP already shipped gets its later scope sequenced here, which puts a fresh requirements pass's work into a file that only ever sequenced the MVP.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Sequence from the MVP list in the requirements file and name it as the source
|
|
17
|
+
- Make every version a usable increment, stated as what the user can then do
|
|
18
|
+
- Order by dependency and by de-risking, placing an unproven subsystem inside the version that first needs it
|
|
19
|
+
- Preserve rows that still hold when updating, resequencing or splitting only where scope shifted
|
|
20
|
+
- Follow the reference for the document shape and version format rather than inventing one
|
|
21
|
+
- Report which versions changed on an update
|
|
22
|
+
|
|
23
|
+
## Must not
|
|
24
|
+
|
|
25
|
+
- Break a version into task-level steps or a file list
|
|
26
|
+
- Rewrite a row that did not change
|
|
27
|
+
- Sequence a later scope section without an explicit override from the caller
|
|
28
|
+
- Stage or commit the file, which is tracked but belongs to the git skills
|
|
29
|
+
|
|
30
|
+
## Guards
|
|
31
|
+
|
|
32
|
+
- Requirements file absent or carrying no MVP features: stop, because there is nothing to sequence
|
|
33
|
+
- A later scope section present after the MVP list: stop and name the override, since that scope belongs to a fresh requirements pass
|
|
34
|
+
- Neither copy of the requirements standard resolves: draft without the lifecycle gate. Refusing on a rule that could not be read stops more than it protects.
|
|
35
|
+
|
|
36
|
+
## Out of scope
|
|
37
|
+
|
|
38
|
+
- Task-level steps and file lists, which `claude-feature` and the task board own
|
|
39
|
+
- Writing the requirements being sequenced
|
|
40
|
+
- Committing the file, which the git skills own
|
|
41
|
+
- Deciding what to build next once the MVP list has shipped
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-screencast
|
|
3
|
+
description: Why a recording script ships pre-seeded rather than blank, what the four discovery questions buy, and why the draft stays stack-agnostic
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude screencast requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a recording plan arrives as a wall of blank fields, so the user fills a template instead of editing a draft and the work the skill was meant to do lands back on them. The opposite failure costs more. A session that skips discovery guesses the audience, the length, and the hero moment, then writes five beats for a video nobody asked for, and every one of them has to be thrown away rather than edited.
|
|
11
|
+
|
|
12
|
+
A draft that names the recording software, the editing software, or the window manager pins the script to one machine and one person's setup, so it stops being reusable the moment anyone else records. And re-invoking to refine silently overwrites the edits the user made in the file, which is the one failure that destroys work rather than wasting it.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Read project context and recent commits before proposing, since what shipped recently is usually the subject
|
|
17
|
+
- Ask exactly four discovery questions, each carrying a default derived from that context
|
|
18
|
+
- Wait for answers rather than reading silence as acceptance
|
|
19
|
+
- Pre-seed every section with concrete content so the draft is shippable as written and the user edits down
|
|
20
|
+
- Derive the slug from the topic and the discovery answers
|
|
21
|
+
- Write to the gitignored scratch path at the main worktree root and print that path on its own line
|
|
22
|
+
|
|
23
|
+
## Must not
|
|
24
|
+
|
|
25
|
+
- Name recording software, editing software, fonts, or window managers
|
|
26
|
+
- Record, edit video, or generate captions
|
|
27
|
+
- Re-run against an existing draft to refine it, since re-running overwrites
|
|
28
|
+
- Infer the hero moment without asking, which is the beat the whole script is built around
|
|
29
|
+
|
|
30
|
+
## Guards
|
|
31
|
+
|
|
32
|
+
- No topic provided: stop rather than guessing what is being recorded
|
|
33
|
+
|
|
34
|
+
## Out of scope
|
|
35
|
+
|
|
36
|
+
- Producing the recording, which stops at the script by design
|
|
37
|
+
- Refining an existing draft, which is a direct edit of the file
|
|
38
|
+
- Slide decks, which `claude-slides-draft` owns
|
|
39
|
+
- Where the recording ships, which the draft lists and the user decides
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-slides-draft
|
|
3
|
+
description: Why the skill owns deck content and the CLI owns layout, and why one render is read back before the deck is called done
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude slides draft requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a deck request turns into hand-built layout. The session invents layout names the renderer does not have, so the render falls back or fails, and it writes centered body text and bullet lines that overflow the slide because nothing told it the type scale is fixed. Every slide comes out as a title over bullets, which is the shape that needs no decision and reads as a document rather than a deck.
|
|
11
|
+
|
|
12
|
+
Reimplementing spacing and palette logic is the failure that survives the session. The CLI already owns both, so a deck that styles itself drifts from every other deck and stops being recognizable. And a first render shipped unread hands the audience the overlap and the overflow, since a source that looks correct says nothing about the picture it produces.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Read the layout catalog from the CLI before drafting and pick only from what it returns
|
|
17
|
+
- Match the content shape to the layout, since each one parses a different body
|
|
18
|
+
- Vary layouts across the deck rather than repeating one
|
|
19
|
+
- Size content to fit the fixed type scale, keeping titles short and bullet lines tight
|
|
20
|
+
- Shell out to the render command for the deck itself
|
|
21
|
+
- Run one QA pass over the rendered images and fix what it reports
|
|
22
|
+
|
|
23
|
+
## Must not
|
|
24
|
+
|
|
25
|
+
- Hardcode a layout name, which goes stale the moment the catalog changes
|
|
26
|
+
- Reimplement layout, palette, or type scale, which the CLI owns
|
|
27
|
+
- Write centered body text or content that overflows a slide
|
|
28
|
+
- Loop on aesthetics past a single fix pass
|
|
29
|
+
|
|
30
|
+
## Guards
|
|
31
|
+
|
|
32
|
+
- Image conversion tools missing: skip the image pass and say so. Do not fail the render over a verification step.
|
|
33
|
+
|
|
34
|
+
## Out of scope
|
|
35
|
+
|
|
36
|
+
- Rendering, which the CLI owns end to end
|
|
37
|
+
- The palette and the type scale, which live in the CLI rather than in the source
|
|
38
|
+
- Writing the content the deck is about, which the caller brings
|
|
39
|
+
- Recording a demo, which `claude-screencast` owns
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-tasks
|
|
3
|
+
description: Why creating and archiving a task file is one skill, the origin invariant enforceable only at creation, and why archiving routes through the command the merge hook calls
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude tasks requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a task file gets a filename and frontmatter invented on the spot, so the board sorts wrong and the regenerated index reads fields that are not there. A task arrives with no origin, which is either lost context or work nobody decided to do, and by the time anyone notices there is no way to recover which it was. The phase label gets derived from a version file rather than from what the board already means, and a single-digit phase sorts after a double-digit one because nobody padded it.
|
|
11
|
+
|
|
12
|
+
Archiving fails in two ways that both lose work. Moving the file, editing the ordering file, and regenerating the index as three separate acts drifts from the one command the merge hook calls, so the attended and unattended paths stop agreeing. And an all-`[x]` task gets archived while its pull request is still open, because marking outcomes happens on the branch as the first step of shipping. The board is gitignored, so nothing restores a task archived early.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Resolve the board at the main worktree root, since a linked worktree writing to `pwd` creates a second board nothing reads
|
|
17
|
+
- Read the tasks standard before writing, rather than working the filename and frontmatter from memory
|
|
18
|
+
- Require an origin at creation, because that is the only moment the invariant is enforceable
|
|
19
|
+
- Propose the phase label from what is already on the board and pad it to two digits
|
|
20
|
+
- Confirm the work reached the default branch before archiving
|
|
21
|
+
- Run the archive command and route on the reason it refuses, since each reason has one resolution
|
|
22
|
+
- Report an origin that carries no task, as a list rather than a prompt
|
|
23
|
+
|
|
24
|
+
## Must not
|
|
25
|
+
|
|
26
|
+
- Mark an outcome complete or archive a plan
|
|
27
|
+
- Hand-edit the index or the ordering file, or move the task file directly
|
|
28
|
+
- Write a pull request line at creation, when any number is a guess at someone else's work
|
|
29
|
+
- Archive around a refusal
|
|
30
|
+
- Split the legacy single-file archive into per-task files nobody wrote
|
|
31
|
+
|
|
32
|
+
## Guards
|
|
33
|
+
|
|
34
|
+
- No task board at the main root: stop
|
|
35
|
+
- The request fits neither creating nor archiving: stop rather than picking one
|
|
36
|
+
- Work not yet on the default branch: stop, because the board has no history to restore from
|
|
37
|
+
- No origin given and none offered: stop
|
|
38
|
+
|
|
39
|
+
## Out of scope
|
|
40
|
+
|
|
41
|
+
- Editing the contents of a task that already exists, which `claude-docs` owns along with marking outcomes and sweeping plans
|
|
42
|
+
- Deciding what the task should argue. This owns the file's existence and its shape, not its content.
|
|
43
|
+
- Relocating a plan, which one skill owns so two do not relocate it differently
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-ui-test
|
|
3
|
+
description: Why UI changes split into what a browser can assert and what only an eye can judge, and why the visual half is written to disk rather than printed
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude UI test requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, UI work ships on the confidence of the session that wrote it. What verification does happen mixes the two kinds without noticing, so a checklist item about spacing sits beside one about a form submission, and neither is done properly because they need different work from different people. Tests get written against a config the project does not use, or duplicate what a component test already covers.
|
|
11
|
+
|
|
12
|
+
The visual half is where the record is lost. A checklist printed into chat scrolls away before anyone verifies it, so the ship step has nothing to gate on and the work reads as verified because a list was produced. Written from a linked worktree against `pwd`, the file lands where the caller does not look.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Split every change into automatable and visual-only before writing anything
|
|
17
|
+
- Read the project's existing test config and patterns before writing a test against them
|
|
18
|
+
- Assert a user action and its outcome per test, covering the happy path and the key edge cases
|
|
19
|
+
- Run the tests after writing them and fix what fails
|
|
20
|
+
- Write a produced checklist to the branch-derived path at the main worktree root, overwriting
|
|
21
|
+
- Report that everything is covered rather than manufacturing a checklist to show work
|
|
22
|
+
|
|
23
|
+
## Must not
|
|
24
|
+
|
|
25
|
+
- Try to assert a visual property programmatically
|
|
26
|
+
- Re-test what unit or component tests written during implementation already cover
|
|
27
|
+
- Repeat the full checklist in chat, which is what made it evaporate
|
|
28
|
+
- Stage or commit the checklist, which is gitignored scratch
|
|
29
|
+
|
|
30
|
+
## Guards
|
|
31
|
+
|
|
32
|
+
- No implementation context in the session: stop, since there is nothing to derive tests from
|
|
33
|
+
- Every change automatable: skip the file write and report the coverage
|
|
34
|
+
|
|
35
|
+
## Out of scope
|
|
36
|
+
|
|
37
|
+
- Judging whether the UI is any good, which `claude-ux-audit` owns
|
|
38
|
+
- Unit and component tests, which belong to implementation
|
|
39
|
+
- Deciding whether an outstanding checklist blocks the ship, which the calling pipeline gates on
|
|
40
|
+
- Performing the visual verification, which needs a person
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-ux-audit
|
|
3
|
+
description: Why UI roughness is reported against stated intent rather than taste, and why the audit observes without fixing
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude UX audit requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, UI roughness is found by users. An audit that does run reports personal taste, because nothing anchors it to what the project intended, so the findings are arguable and get argued rather than fixed. It flags work the board already marks in progress, which spends the reader's attention on a surface nobody claimed was finished.
|
|
11
|
+
|
|
12
|
+
The pass slides into fixing what it finds, and then the audit and the change land together with no record of which observation drove which edit, so neither can be reviewed. Reported into chat, the whole thing evaporates before anyone acts on it, and the next audit rediscovers the same list.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Read the design system and the per-surface wireframes as ground truth for intent before flagging any drift
|
|
17
|
+
- Group findings by surface, since a surface is what a person opens and fixes
|
|
18
|
+
- Cover missing feedback states, unhandled edge cases, inconsistencies, and friction in daily use
|
|
19
|
+
- Write the full report to the branch-derived path at the main worktree root, overwriting
|
|
20
|
+
- Report no observations rather than filling the list to show work
|
|
21
|
+
|
|
22
|
+
## Must not
|
|
23
|
+
|
|
24
|
+
- Suggest an implementation or change any code
|
|
25
|
+
- Flag a surface the task board marks in progress
|
|
26
|
+
- Report a preference no stated intent supports
|
|
27
|
+
- Repeat the full report in chat, which is what loses it
|
|
28
|
+
- Stage or commit the report, which is gitignored scratch
|
|
29
|
+
|
|
30
|
+
## Guards
|
|
31
|
+
|
|
32
|
+
- No UI surfaces in the project: stop, since there is nothing to audit
|
|
33
|
+
|
|
34
|
+
## Out of scope
|
|
35
|
+
|
|
36
|
+
- Fixing what it found, which is a separate change with its own review
|
|
37
|
+
- Feature planning, which `claude-feature` owns
|
|
38
|
+
- Verifying one specific change, which `claude-ui-test` owns
|
|
39
|
+
- Defining the intent it audits against, which `claude-design-extract` and the wireframes own
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-worktree
|
|
3
|
+
description: Why worktree entry is wrapped rather than called directly, covering name derivation, the branch rename downstream slugs depend on, and the shared-config repair
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Claude worktree requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, the user names the worktree by hand, and a name matching no plan breaks every slug derived from it afterward. The entry tool then creates the branch as `worktree-<name>`, which diverges from the name the folder carries, so the ship chain looks for a plan file under a slug that does not exist and stops with nothing wrong except the name.
|
|
11
|
+
|
|
12
|
+
Entry also writes the bare flag into the shared config, which strands the main worktree. Every command run there fails while the files sit untouched on disk, and the linked worktree keeps working, so nothing surfaces until the operator returns to the main checkout and finds the repository broken. A rename onto a branch that already exists is the third failure, and it is the one that destroys work rather than blocking it.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Derive the name from the plan matched to the current branch, falling through the ordered sources rather than picking
|
|
17
|
+
- Validate and sanitize the derived name against what the entry tool accepts
|
|
18
|
+
- Preview the resolved name and which source produced it before entering
|
|
19
|
+
- Rename the created branch to match the worktree name, so downstream slug derivation resolves
|
|
20
|
+
- Read the bare flag before writing it, and repair it on both sides of entry
|
|
21
|
+
- Announce the repair only when a write actually happened
|
|
22
|
+
|
|
23
|
+
## Must not
|
|
24
|
+
|
|
25
|
+
- Pick between plans when more than one could match. Ask.
|
|
26
|
+
- Enter on a name inferred from session context without confirmation
|
|
27
|
+
- Delete or overwrite a branch that already carries the target name
|
|
28
|
+
- Rename when the worktree was entered by path, since that branch already has its own identity
|
|
29
|
+
- Invoke the exit path, which is the user's call
|
|
30
|
+
|
|
31
|
+
## Guards
|
|
32
|
+
|
|
33
|
+
- Already inside a linked worktree: stop rather than nesting
|
|
34
|
+
- Not a git repository and no creation hook configured: stop
|
|
35
|
+
- Target branch already exists: stop and leave it alone, since resolving it automatically risks the wrong branch
|
|
36
|
+
|
|
37
|
+
## Out of scope
|
|
38
|
+
|
|
39
|
+
- Listing, cleaning up, or rotating worktrees, which `git-worktree` owns
|
|
40
|
+
- Leaving a worktree, which the user decides
|
|
41
|
+
- What runs inside the worktree once entered, which the caller drives
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cli-script
|
|
3
|
+
description: What a non-interactive automation script owes its caller, and the stdout contract that separates it from the human-facing generator
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CLI script requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, an automation script is written with an interactive tool's habits. Progress frames, icons, and color land on stdout, so the script stops composing in a pipe and the caller parses a spinner as data. The failure is invisible in a terminal and total in CI.
|
|
11
|
+
|
|
12
|
+
The robustness failures are the ones that reach production. Without strict mode a failed stage inside a pipe exits zero and the script reports success on work that did not happen. An unset variable expands to nothing and a path built from it points somewhere nobody meant. Errors print and execution continues. And a single long `main()` holding every responsibility cannot be tested or reused, which is what turns a one-off script into one nobody will touch.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Open with the strict-mode preamble, so a failed stage stops the script
|
|
17
|
+
- Keep data on stdout and put every log line, progress message, and error on stderr
|
|
18
|
+
- Exit non-zero with actionable context through one error helper
|
|
19
|
+
- Default every variable expansion rather than relying on it being set
|
|
20
|
+
- Guard a command that returns non-zero on a valid empty result
|
|
21
|
+
- Decompose by responsibility, with the entry function orchestrating rather than doing
|
|
22
|
+
- Implement usage and the help flags when the script takes arguments
|
|
23
|
+
- Run the validation list before responding
|
|
24
|
+
|
|
25
|
+
## Must not
|
|
26
|
+
|
|
27
|
+
- Emit timeline frames, icons, color, or an interactive prompt
|
|
28
|
+
- Put a log or progress line on stdout
|
|
29
|
+
- Comment what the code already states
|
|
30
|
+
- Leave stdout formatted for a human to read
|
|
31
|
+
|
|
32
|
+
## Guards
|
|
33
|
+
|
|
34
|
+
- No refusal condition. Every request within scope generates, and the boundary against the interactive generator is a routing call made at invocation rather than a runtime stop.
|
|
35
|
+
|
|
36
|
+
## Out of scope
|
|
37
|
+
|
|
38
|
+
- A human-facing interactive tool with prompts or a visual timeline UI, which `bash-script` owns
|
|
39
|
+
- A GitHub Actions workflow file, which `ci-workflow` owns
|
|
40
|
+
- Running, installing, or scheduling what it generates
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-rule
|
|
3
|
+
description: Why a project-local rule needs a band, a number checked against two catalogs, and a scope key that decides when it fires
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create rule requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a project rule is written straight into the rules folder on a number already taken, so two rules collide and one loses. The subtler collision is with the toolkit itself. A number free in the target today can be the number a shipped rule lands on tomorrow, and the next install double-books it, so a rule the project wrote gets overwritten by one it never chose.
|
|
11
|
+
|
|
12
|
+
The band gets picked by feel, so a UI copy rule lands in the always-on range and loads on every session for the rest of the project's life. The scope key fails in both directions. An always-on rule carrying a path scope fires only on files it was never about, and a path-scoped rule missing it loads constantly. Written from memory rather than from the rule standard, the body comes out in a shape the rest of the catalog does not share.
|
|
13
|
+
|
|
14
|
+
## Must
|
|
15
|
+
|
|
16
|
+
- Resolve what the rule enforces and where it applies, asking only for what the request leaves missing
|
|
17
|
+
- Pick the band from the topic and take its number range and folder from that choice
|
|
18
|
+
- Check both the target's used prefixes and the toolkit catalog before taking a number
|
|
19
|
+
- Read the rule standard before writing the body
|
|
20
|
+
- Emit the path scope for a path-scoped rule and omit the key entirely for an always-on one
|
|
21
|
+
- Preview the resolved path, band, number, and frontmatter, then write without pausing
|
|
22
|
+
- Say when the rule loads, since path-scoped and always-on rules behave differently
|
|
23
|
+
|
|
24
|
+
## Must not
|
|
25
|
+
|
|
26
|
+
- Edit a toolkit source rule, which is authored in the toolkit and would be overwritten here
|
|
27
|
+
- Work the body shape or the frontmatter from memory
|
|
28
|
+
- Take a number without checking the toolkit catalog, which is the collision that surfaces later
|
|
29
|
+
- Write more than one topic into a single rule
|
|
30
|
+
|
|
31
|
+
## Guards
|
|
32
|
+
|
|
33
|
+
- No project Claude directory: stop, since there is nowhere for the rule to live
|
|
34
|
+
- The request names no behavior to enforce: stop rather than inventing one
|
|
35
|
+
- The toolkit CLI is not on PATH: scan the target alone and warn that a later install could collide
|
|
36
|
+
|
|
37
|
+
## Out of scope
|
|
38
|
+
|
|
39
|
+
- Editing a rule that already exists
|
|
40
|
+
- Toolkit source rules, which are authored in the toolkit rather than in a target
|
|
41
|
+
- Installing the rules the toolkit ships, which the governance setup path owns
|
|
42
|
+
- A standard or a snippet, which `create-standard` owns
|
package/docs/agents.md
CHANGED
|
@@ -377,16 +377,21 @@ aitk context audit
|
|
|
377
377
|
aitk context audit --json
|
|
378
378
|
aitk context audit --citations-only
|
|
379
379
|
aitk context audit --folder context,diagrams
|
|
380
|
+
aitk context audit --folder docs
|
|
380
381
|
```
|
|
381
382
|
|
|
382
|
-
| Option | Behavior
|
|
383
|
-
| ------------------ |
|
|
384
|
-
| `--json` | Add a machine-readable record on stdout, keeping the frame
|
|
385
|
-
| `--folder <list>` | Comma-separated folder names
|
|
386
|
-
| `--citations-only` | Run the gating citation check alone, printing nothing when it passes
|
|
383
|
+
| Option | Behavior |
|
|
384
|
+
| ------------------ | -------------------------------------------------------------------- |
|
|
385
|
+
| `--json` | Add a machine-readable record on stdout, keeping the frame |
|
|
386
|
+
| `--folder <list>` | Comma-separated folder names (default: the three below) |
|
|
387
|
+
| `--citations-only` | Run the gating citation check alone, printing nothing when it passes |
|
|
387
388
|
|
|
388
389
|
Scope defaults to `context`, `diagrams`, and `wireframes`, and a folder the project does not carry is skipped rather than reported. A domain that outgrew one file and split into `<domain>/` is audited as its own folder, so a split entry measures at the same grain as a flat one.
|
|
389
390
|
|
|
391
|
+
A name passed to `--folder` resolves under `.claude/` first and at the project root second, which is what puts `docs/` and any later corpus in reach of the same engine. The root base is reached only by a name the caller passes, so the default list still resolves under `.claude/` alone and a project holding a root `wireframes/` is not audited against a standard it never adopted. The scope line prints the resolved path, so a project carrying both spellings reads which one was taken. The JSON record carries the base per folder as `folders[].base`.
|
|
392
|
+
|
|
393
|
+
A run where no requested name resolves refuses, whichever list it read. Naming the absent ones narrows to `--folder`, since a project carrying one of the three default folders is the ordinary case and a name it never asked for is not a typo. The JSON record carries those names as `unresolvedFolders`.
|
|
394
|
+
|
|
390
395
|
Exit codes are `0` for a clean run, `1` for a refusal, and `2` for an unresolved citation. Only the citation check sets a failing code. Required-section, length, depth, bullet weight, table, provenance, and index findings print and return `0`, because each is a judgment and failing a push on one would make the check something to route around.
|
|
391
396
|
|
|
392
397
|
### What each check reports
|
|
@@ -419,6 +424,8 @@ Three exclusions keep it from firing on prose about paths. Fenced blocks are ski
|
|
|
419
424
|
|
|
420
425
|
What remains is a sentence naming a hypothetical entry to show the shape of a name, which no syntax separates from a real reference. Append `<!-- audit-ignore-citations -->` to that source line. The marker suppresses citation checking for its own line only.
|
|
421
426
|
|
|
427
|
+
The pattern spells the `.claude/` prefix, so a folder resolved at the project root is measured by every other check and contributes nothing here. Widening it to a bare `docs/x.md` would match prose that references nothing, which is a separate decision from where entries come from. A run whose folders all resolved at the root says the check is out of scope rather than reporting that zero paths resolved, and the same run under `--citations-only` refuses, because a gate exiting clean on a scope it could not build is the failure the gate exists to catch.
|
|
428
|
+
|
|
422
429
|
## Runtime catalogs
|
|
423
430
|
|
|
424
431
|
Use these to discover what's available instead of hardcoding names.
|
package/docs/operating-model.md
CHANGED
|
@@ -104,4 +104,4 @@ port-collision detail.
|
|
|
104
104
|
|
|
105
105
|
- [Claude Code and git worktrees](../wiki/claude-worktrees.md) for the isolation and fan-out mechanics
|
|
106
106
|
- [Claude Code subagents](../wiki/claude-subagents.md) for in-session parallelism without worktrees
|
|
107
|
-
- `.claude/context/claude-plugin/
|
|
107
|
+
- `.claude/context/claude-plugin/skill-strategy.md` for how the skills in the loop are categorized
|
|
@@ -130,7 +130,7 @@ Zero or one yes: tier 0. Two or three: tier 1. Four: tier 2. Resist over-tiering
|
|
|
130
130
|
|
|
131
131
|
- [Stitch](../wiki/stitch.md): Gemini-powered design product with the MCP server that anchors tier 1
|
|
132
132
|
- [Claude Design](../wiki/claude-design.md): first-party hosted design product and handoff bundle
|
|
133
|
-
- `.claude/context/claude-plugin/
|
|
133
|
+
- `.claude/context/claude-plugin/skill-strategy.md`: how to decide between workflow and domain-knowledge skills
|
|
134
134
|
- [Visual wireframes](../wiki/visual-wireframes.md): Excalidraw research and setup for the tier 1 wireframe companion
|
|
135
135
|
- [Community skills and plugins](../wiki/community-skills.md): catalog of frontend design skills and integrations
|
|
136
136
|
- [Community MCP servers](../wiki/community-mcp-servers.md): catalog of MCPs referenced across all tiers
|
package/package.json
CHANGED
package/src/commands/context.ts
CHANGED
|
@@ -59,7 +59,10 @@ export function register(program: Command): void {
|
|
|
59
59
|
.argument('[path]', 'Project root, defaulting to the current directory')
|
|
60
60
|
.helpOption('-h, --help', 'Show this help message')
|
|
61
61
|
.option('--json', 'Add a machine-readable record on stdout')
|
|
62
|
-
.option(
|
|
62
|
+
.option(
|
|
63
|
+
'--folder <list>',
|
|
64
|
+
'Comma-separated folder names, resolved under .claude/ then the project root',
|
|
65
|
+
)
|
|
63
66
|
.option('--citations-only', 'Run the gating citation check alone')
|
|
64
67
|
.addHelpText(
|
|
65
68
|
'after',
|
|
@@ -78,6 +81,7 @@ export function register(program: Command): void {
|
|
|
78
81
|
' aitk context audit --json',
|
|
79
82
|
' aitk context audit --citations-only',
|
|
80
83
|
' aitk context audit --folder context,diagrams',
|
|
84
|
+
' aitk context audit --folder docs',
|
|
81
85
|
'',
|
|
82
86
|
].join('\n'),
|
|
83
87
|
)
|
|
@@ -96,11 +100,11 @@ function parseFolders(list: string | undefined): string[] | string {
|
|
|
96
100
|
|
|
97
101
|
if (names.length === 0) return 'Empty --folder list. Pass at least one name.'
|
|
98
102
|
|
|
99
|
-
// `..` would resolve the audited folder above
|
|
100
|
-
// and the citation pattern outside the tree the audit describes.
|
|
103
|
+
// `..` would resolve the audited folder above the project root, taking the
|
|
104
|
+
// scan and the citation pattern outside the tree the audit describes.
|
|
101
105
|
const invalid = names.filter((name) => !FOLDER_NAME.test(name))
|
|
102
106
|
if (invalid.length > 0) {
|
|
103
|
-
return `--folder takes folder names
|
|
107
|
+
return `--folder takes folder names, not paths: ${invalid.join(', ')}`
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
return names
|
|
@@ -116,15 +120,38 @@ async function runAudit(
|
|
|
116
120
|
|
|
117
121
|
if (typeof names === 'string') return refuse(names, gateOnly)
|
|
118
122
|
|
|
119
|
-
|
|
123
|
+
// The root base is opt-in. A target carrying a root `wireframes/` would
|
|
124
|
+
// otherwise be audited against a standard it never adopted, on a bare run
|
|
125
|
+
// that named nothing.
|
|
126
|
+
const named = opts.folder !== undefined
|
|
127
|
+
const { folders, missing } = await resolveFolders(root, names, {
|
|
128
|
+
canResolveAtRoot: named,
|
|
129
|
+
})
|
|
120
130
|
if (folders.length === 0) {
|
|
121
131
|
return refuse(
|
|
122
|
-
`No audited folder found under .claude
|
|
132
|
+
`No audited folder found ${named ? 'under .claude/ or the project root' : 'under .claude/'}. Looked for: ${names.join(', ')}.`,
|
|
123
133
|
gateOnly,
|
|
124
134
|
)
|
|
125
135
|
}
|
|
126
136
|
|
|
127
|
-
|
|
137
|
+
// A default folder a project does not carry is the ordinary case and stays
|
|
138
|
+
// silent. A name passed by hand that resolves nowhere is a typo, and the run
|
|
139
|
+
// measuring the names that did resolve reads as a pass against a folder it
|
|
140
|
+
// never opened.
|
|
141
|
+
const unresolved = named ? missing : []
|
|
142
|
+
|
|
143
|
+
// The gate runs one check. Letting it exit 0 against a scope it could not
|
|
144
|
+
// build reports a pass on nothing measured, which is the outcome a gate is
|
|
145
|
+
// there to prevent.
|
|
146
|
+
const cited = presentNames(folders)
|
|
147
|
+
if (gateOnly && cited.length === 0) {
|
|
148
|
+
return refuse(
|
|
149
|
+
`The citation check spells the .claude/ prefix and no audited folder resolved there. Looked for: ${names.join(', ')}.`,
|
|
150
|
+
gateOnly,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const citations = await auditCitations(root, cited)
|
|
128
155
|
if (citations.kind === 'unavailable') {
|
|
129
156
|
return refuse(
|
|
130
157
|
'git could not list the tree, so no citation was checked. Run inside a git repository.',
|
|
@@ -140,8 +167,8 @@ async function runAudit(
|
|
|
140
167
|
reportGate(citations)
|
|
141
168
|
} else {
|
|
142
169
|
intro('aitk context audit')
|
|
143
|
-
reportScope(folders)
|
|
144
|
-
reportCitations(citations)
|
|
170
|
+
reportScope(folders, unresolved)
|
|
171
|
+
reportCitations(citations, cited)
|
|
145
172
|
reportSections(sections, folders)
|
|
146
173
|
reportLength(entries)
|
|
147
174
|
reportDepth(entries)
|
|
@@ -158,9 +185,11 @@ async function runAudit(
|
|
|
158
185
|
root,
|
|
159
186
|
folders: folders.map((folder) => ({
|
|
160
187
|
path: folder.rel,
|
|
188
|
+
base: folder.base,
|
|
161
189
|
entries: folder.entries.length,
|
|
162
190
|
governsContent: governsContent(folder),
|
|
163
191
|
})),
|
|
192
|
+
unresolvedFolders: unresolved,
|
|
164
193
|
citations: {
|
|
165
194
|
scanned: citations.scanned,
|
|
166
195
|
total: citations.total,
|
|
@@ -228,18 +257,54 @@ function plural(count: number, noun: string): string {
|
|
|
228
257
|
return `${count} ${noun}${count === 1 ? '' : 's'}`
|
|
229
258
|
}
|
|
230
259
|
|
|
231
|
-
|
|
260
|
+
/**
|
|
261
|
+
* Names the resolved path of every audited folder, plus the requested names
|
|
262
|
+
* that resolved nowhere.
|
|
263
|
+
*
|
|
264
|
+
* The path is what says which base a name was taken from, which matters once a
|
|
265
|
+
* name can resolve under `.claude/` or at the project root and a project may
|
|
266
|
+
* carry both.
|
|
267
|
+
*/
|
|
268
|
+
function reportScope(
|
|
269
|
+
folders: readonly AuditedFolder[],
|
|
270
|
+
unresolved: readonly string[],
|
|
271
|
+
): void {
|
|
232
272
|
logStep('Scope')
|
|
233
273
|
|
|
234
274
|
for (const folder of folders) {
|
|
235
275
|
logInfo(`${folder.rel}: ${folder.entries.length} entries`)
|
|
236
276
|
}
|
|
277
|
+
|
|
278
|
+
if (unresolved.length === 0) return
|
|
279
|
+
|
|
280
|
+
logWarn(
|
|
281
|
+
`Under neither .claude/ nor the project root: ${unresolved.join(', ')}`,
|
|
282
|
+
)
|
|
237
283
|
}
|
|
238
284
|
|
|
239
285
|
type ScannedCitations = Extract<CitationReport, { kind: 'scanned' }>
|
|
240
286
|
|
|
241
|
-
|
|
287
|
+
/**
|
|
288
|
+
* States the reach before the count, for the reason the provenance report
|
|
289
|
+
* states its own.
|
|
290
|
+
*
|
|
291
|
+
* A run auditing a folder at the project root builds no pattern, and a count of
|
|
292
|
+
* zero followed by a line saying every path resolves is indistinguishable from
|
|
293
|
+
* a corpus that cites nothing.
|
|
294
|
+
*/
|
|
295
|
+
function reportCitations(
|
|
296
|
+
report: ScannedCitations,
|
|
297
|
+
cited: readonly string[],
|
|
298
|
+
): void {
|
|
242
299
|
logStep('Citations')
|
|
300
|
+
|
|
301
|
+
if (cited.length === 0) {
|
|
302
|
+
logInfo(
|
|
303
|
+
'Out of scope. The pattern spells the .claude/ prefix, and no audited folder resolved there.',
|
|
304
|
+
)
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
|
|
243
308
|
logInfo(
|
|
244
309
|
`${plural(report.total, 'cited path')} across ${plural(report.scanned, 'file')}, fixtures and fenced examples excluded`,
|
|
245
310
|
)
|
package/src/context/folders.ts
CHANGED
|
@@ -17,6 +17,12 @@ export const DEFAULT_FOLDERS: readonly string[] = [
|
|
|
17
17
|
'wireframes',
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
+
/** The base every folder in the default list sits under. */
|
|
21
|
+
const CLAUDE_BASE = '.claude'
|
|
22
|
+
|
|
23
|
+
/** The project root, reached only by a name the caller asked for. */
|
|
24
|
+
const ROOT_BASE = '.'
|
|
25
|
+
|
|
20
26
|
export interface AuditedFolder {
|
|
21
27
|
/**
|
|
22
28
|
* The requested folder name this was resolved under, which is what says
|
|
@@ -25,6 +31,11 @@ export interface AuditedFolder {
|
|
|
25
31
|
* `.claude/context/claude-plugin` is governed as `context`.
|
|
26
32
|
*/
|
|
27
33
|
readonly name: string
|
|
34
|
+
/**
|
|
35
|
+
* The base the name resolved under, which is what says whether the folder is
|
|
36
|
+
* in the citation check's scope.
|
|
37
|
+
*/
|
|
38
|
+
readonly base: string
|
|
28
39
|
/** Repo-relative folder path, used verbatim in every report line. */
|
|
29
40
|
readonly rel: string
|
|
30
41
|
readonly indexPath: string
|
|
@@ -43,16 +54,28 @@ export interface AuditedFolder {
|
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
/**
|
|
46
|
-
* Names the requested folders that actually exist, which is the
|
|
47
|
-
* check's scope.
|
|
57
|
+
* Names the requested `.claude/` folders that actually exist, which is the
|
|
58
|
+
* citation check's scope.
|
|
48
59
|
*
|
|
49
60
|
* A skill or seed pointing into `.claude/wireframes/` is a live instruction for
|
|
50
61
|
* a project that carries the folder and says nothing about one that does not.
|
|
51
62
|
* Checking a path into an absent folder would fail eight shipped references
|
|
52
63
|
* here for the sole reason that this repository has no wireframes.
|
|
64
|
+
*
|
|
65
|
+
* A folder resolved at the project root is measured and stays out of this. The
|
|
66
|
+
* pattern the citation check builds spells the `.claude/` prefix, so admitting
|
|
67
|
+
* a root name there would check `.claude/<name>/` paths the audit never read.
|
|
68
|
+
* Widening the pattern to the root spelling is a separate change, since a bare
|
|
69
|
+
* `docs/x.md` appears in prose that references nothing.
|
|
53
70
|
*/
|
|
54
71
|
export function presentNames(folders: readonly AuditedFolder[]): string[] {
|
|
55
|
-
return [
|
|
72
|
+
return [
|
|
73
|
+
...new Set(
|
|
74
|
+
folders
|
|
75
|
+
.filter((folder) => folder.base === CLAUDE_BASE)
|
|
76
|
+
.map((folder) => folder.name),
|
|
77
|
+
),
|
|
78
|
+
]
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
async function readEntries(dir: string): Promise<string[]> {
|
|
@@ -70,6 +93,40 @@ async function readEntries(dir: string): Promise<string[]> {
|
|
|
70
93
|
return paths.sort()
|
|
71
94
|
}
|
|
72
95
|
|
|
96
|
+
export interface FolderResolution {
|
|
97
|
+
/** Every folder that resolved, with the nested splits beneath each. */
|
|
98
|
+
readonly folders: readonly AuditedFolder[]
|
|
99
|
+
/**
|
|
100
|
+
* Requested names that resolved under no base, reported rather than dropped.
|
|
101
|
+
* Which absences are worth saying out loud is the caller's judgment: a
|
|
102
|
+
* default folder a project does not carry is ordinary, and a name passed by
|
|
103
|
+
* hand that resolves nowhere is a typo that would otherwise read as a pass.
|
|
104
|
+
*/
|
|
105
|
+
readonly missing: readonly string[]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface ResolveOptions {
|
|
109
|
+
/**
|
|
110
|
+
* Whether a name may resolve at the project root when `.claude/` does not
|
|
111
|
+
* carry it. False for the default list, which names three folders a project
|
|
112
|
+
* is expected to hold under `.claude/` and nowhere else.
|
|
113
|
+
*/
|
|
114
|
+
readonly canResolveAtRoot?: boolean
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function locate(
|
|
118
|
+
root: string,
|
|
119
|
+
name: string,
|
|
120
|
+
bases: readonly string[],
|
|
121
|
+
): { readonly dir: string; readonly base: string } | undefined {
|
|
122
|
+
for (const base of bases) {
|
|
123
|
+
const dir = resolve(root, base, name)
|
|
124
|
+
if (existsSync(`${dir}/${INDEX_FILE}`)) return { dir, base }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
|
|
73
130
|
/**
|
|
74
131
|
* Resolves the folders to audit under `root`.
|
|
75
132
|
*
|
|
@@ -77,34 +134,49 @@ async function readEntries(dir: string): Promise<string[]> {
|
|
|
77
134
|
* folder beneath it, so a domain that outgrew one file and split is audited at
|
|
78
135
|
* the same grain as one that did not. Discovery of the nested folders runs
|
|
79
136
|
* through the shared walker, which is what keeps `.gitignore` and the vendored
|
|
80
|
-
* prune governing this scan as well as index regeneration.
|
|
137
|
+
* prune governing this scan as well as index regeneration. That prune is what
|
|
138
|
+
* lets a root folder be walked at all, since a name at the project root sits
|
|
139
|
+
* beside `node_modules` and a build output.
|
|
81
140
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
141
|
+
* The project root is reached only when the caller opts in, so a target holding
|
|
142
|
+
* a root `wireframes/` is not audited against a standard it never adopted by
|
|
143
|
+
* the mere act of running the command. `.claude/` still wins a name carried by
|
|
144
|
+
* both, and the scope line prints the resolved path so a caller reads which
|
|
145
|
+
* base was taken rather than inferring it.
|
|
146
|
+
*
|
|
147
|
+
* Nothing above this asks where a folder came from. A name that resolves at the
|
|
148
|
+
* root is measured by every rule that generalizes and gated out of the rules a
|
|
149
|
+
* single standard carries, which `governsContent` decides from the name.
|
|
85
150
|
*/
|
|
86
151
|
export async function resolveFolders(
|
|
87
152
|
root: string,
|
|
88
153
|
names: readonly string[] = DEFAULT_FOLDERS,
|
|
89
|
-
|
|
154
|
+
{ canResolveAtRoot = false }: ResolveOptions = {},
|
|
155
|
+
): Promise<FolderResolution> {
|
|
156
|
+
const bases = canResolveAtRoot ? [CLAUDE_BASE, ROOT_BASE] : [CLAUDE_BASE]
|
|
90
157
|
const folders: AuditedFolder[] = []
|
|
158
|
+
const missing: string[] = []
|
|
91
159
|
|
|
92
160
|
for (const name of names) {
|
|
93
|
-
const
|
|
94
|
-
if (!
|
|
161
|
+
const found = locate(root, name, bases)
|
|
162
|
+
if (!found) {
|
|
163
|
+
missing.push(name)
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
95
166
|
|
|
96
|
-
const dirs = [dir, ...(await listIndexes(dir)).map(dirname)]
|
|
167
|
+
const dirs = [found.dir, ...(await listIndexes(found.dir)).map(dirname)]
|
|
97
168
|
|
|
98
169
|
for (const each of [...new Set(dirs)].sort()) {
|
|
99
170
|
folders.push({
|
|
100
171
|
name,
|
|
172
|
+
base: found.base,
|
|
101
173
|
rel: relative(root, each),
|
|
102
174
|
indexPath: `${each}/${INDEX_FILE}`,
|
|
103
175
|
entries: await readEntries(each),
|
|
104
|
-
nested: each !== dir,
|
|
176
|
+
nested: each !== found.dir,
|
|
105
177
|
})
|
|
106
178
|
}
|
|
107
179
|
}
|
|
108
180
|
|
|
109
|
-
return folders
|
|
181
|
+
return { folders, missing }
|
|
110
182
|
}
|