@alexandrealvaro/agentic 0.2.0-beta.1 → 0.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/WORKFLOW.md +14 -1
- package/package.json +2 -2
- package/src/commands/init.js +14 -1
- package/src/commands/update.js +251 -0
- package/src/index.js +10 -0
- package/src/lib/install.js +257 -52
- package/src/lib/state.js +80 -0
- package/src/skills/claude-code/agentic-adr/SKILL.md +7 -0
- package/src/skills/claude-code/agentic-architecture/SKILL.md +8 -0
- package/src/skills/claude-code/agentic-audit/SKILL.md +16 -1
- package/src/skills/claude-code/agentic-bootstrap/SKILL.md +8 -0
- package/src/skills/claude-code/agentic-design/SKILL.md +8 -0
- package/src/skills/claude-code/agentic-philosophy/SKILL.md +17 -2
- package/src/skills/claude-code/agentic-review/SKILL.md +11 -5
- package/src/skills/claude-code/agentic-review/agents/fresh-context-reviewer.md +1 -3
- package/src/skills/claude-code/agentic-task/SKILL.md +8 -0
- package/src/skills/codex/agentic-adr/SKILL.md +6 -0
- package/src/skills/codex/agentic-architecture/SKILL.md +7 -0
- package/src/skills/codex/agentic-audit/SKILL.md +12 -1
- package/src/skills/codex/agentic-bootstrap/SKILL.md +7 -0
- package/src/skills/codex/agentic-design/SKILL.md +7 -0
- package/src/skills/codex/agentic-philosophy/SKILL.md +15 -2
- package/src/skills/codex/agentic-review/SKILL.md +14 -5
- package/src/skills/codex/agentic-task/SKILL.md +7 -0
package/README.md
CHANGED
|
@@ -43,6 +43,42 @@ A short TUI shows the detected mode, agent, and feature signals (frontend / `.cl
|
|
|
43
43
|
|
|
44
44
|
If your project already has an `AGENTS.md` (or `CLAUDE.md`), the installer appends a managed `Skills installed by agentic` section bracketed by `<!-- agentic-managed-skills:start -->` / `:end -->` markers. User content outside those markers is byte-preserved; re-runs update only the managed block.
|
|
45
45
|
|
|
46
|
+
## Updating an existing project
|
|
47
|
+
|
|
48
|
+
To pull upstream kit changes into a project that already has agentic skills installed:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd your-project
|
|
52
|
+
npx @alexandrealvaro/agentic@beta update
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`update` is a separate command from `init` (clearer intent) and runs a three-way diff against a state file the kit writes at install time:
|
|
56
|
+
|
|
57
|
+
* `.claude/agentic-state.json` — for Claude Code installs.
|
|
58
|
+
* `.agents/agentic-state.json` — for Codex installs.
|
|
59
|
+
|
|
60
|
+
These state files are committed to your repo so the whole team shares one view of what skill version is in place. They record kit version, per-skill version, and the SHA of every shipped file at the time of last install. The three-way diff uses those SHAs to distinguish *user-edited* files from *kit-changed* files and acts accordingly:
|
|
61
|
+
|
|
62
|
+
| File state | Action |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| New file in the kit | install |
|
|
65
|
+
| Kit unchanged, you didn't touch it | report unchanged |
|
|
66
|
+
| Kit unchanged, you edited it | keep your edits |
|
|
67
|
+
| Kit changed, you didn't touch it | silent update |
|
|
68
|
+
| Kit changed, you also edited it | prompt with diff (default: skip) |
|
|
69
|
+
| Skill removed from the kit or de-selected | prompt before removing your file (default: keep) |
|
|
70
|
+
|
|
71
|
+
Useful flags:
|
|
72
|
+
|
|
73
|
+
* `--dry-run` — print the action plan without writing anything. Always start here when you're not sure what will happen.
|
|
74
|
+
* `--force` — overwrite user-edited files on conflict (non-interactive default: no). Escape hatch when you genuinely want kit-side content to win.
|
|
75
|
+
* `--agent claude-code | codex | both` — restrict the update to one agent.
|
|
76
|
+
* `--yes` — non-interactive, accepts defaults (skip on conflict, keep orphans). Combine with `--force` if you want overwrites in CI.
|
|
77
|
+
|
|
78
|
+
If the project was installed with a kit version older than v0.3 (no state file present), the first `update` falls back to today's byte-compare behavior, then writes the state file so subsequent runs use the three-way diff.
|
|
79
|
+
|
|
80
|
+
The `agentic-review` skill writes the assembled WORKFLOW §10 handoff to `.agentic/reviews/<ISO-timestamp>-<scope>.md` before delegating to the fresh-context reviewer (Claude Code) or before instructing you to `/clear` and paste (Codex). These files are ephemeral audit artifacts — add `.agentic/reviews/` to your `.gitignore`.
|
|
81
|
+
|
|
46
82
|
For persistent install:
|
|
47
83
|
|
|
48
84
|
```bash
|
|
@@ -100,10 +136,14 @@ your-project/
|
|
|
100
136
|
│ │ └── NNNN-<title>.md
|
|
101
137
|
│ └── tasks/
|
|
102
138
|
│ └── NNNN-<slug>.md
|
|
139
|
+
├── .agentic/
|
|
140
|
+
│ └── reviews/ (gitignored — ephemeral §10 review handoffs)
|
|
103
141
|
├── .claude/ (Claude Code targets)
|
|
142
|
+
│ ├── agentic-state.json (kit install state — committed)
|
|
104
143
|
│ ├── skills/agentic-*/SKILL.md
|
|
105
144
|
│ └── agents/fresh-context-reviewer.md
|
|
106
145
|
└── .agents/ (Codex targets, cc-sdd convention)
|
|
146
|
+
├── agentic-state.json (kit install state — committed)
|
|
107
147
|
└── skills/agentic-*/{SKILL.md, agents/openai.yaml}
|
|
108
148
|
```
|
|
109
149
|
|
package/WORKFLOW.md
CHANGED
|
@@ -49,7 +49,20 @@ Avoid putting implementation code in docs unless it's executable, generated, or
|
|
|
49
49
|
|
|
50
50
|
The split is simple. **Docs are for the *why*** — decisions, not history. Git tracks history; docs explain the reasoning that won't survive otherwise. **Code is for the *what*** — clean naming and small units make logic self-evident, and the more your code does this work, the less your docs need to.
|
|
51
51
|
|
|
52
|
-
Comments are exceptions. They justify *why* a non-obvious choice was made — never *what* the line does. No commented-out code, and no orphan `TODO` or `FIXME`: every deferred item references
|
|
52
|
+
Comments are exceptions. They justify *why* a non-obvious choice was made — never *what* the line does. No commented-out code, and no orphan `TODO` or `FIXME`: every deferred item references a tracked work item — a GitHub Issue or a per-task file under `doc/tasks/NNNN-*.md`.
|
|
53
|
+
|
|
54
|
+
### Documentation Discipline
|
|
55
|
+
|
|
56
|
+
Eight rules apply to every document the agent or the human writes in the project. They are framed against the failure modes the kit has actually seen — bloated `AGENTS.md`, README pages that drift into changelogs, decision artifacts diluted by speculation.
|
|
57
|
+
|
|
58
|
+
1. **Definitions and decisions only.** Documentation captures what is true now and the decisions that brought it there. Speculation, history, and unfounded plans are out. A deferred decision is in scope when it is *recorded* — an accepted ADR or a task file is fundamentação; "we might do X later" without a record is speculation and is cut.
|
|
59
|
+
2. **No dates, version stamps, `DRAFT` markers, or changelogs in narrative documents.** Applies to `README.md`, `AGENTS.md` / `CLAUDE.md`, `ARCHITECTURE.md`, `DESIGN.md`, specs, and any prose page. **Decision-record artifacts are exempt** — ADRs under `doc/adr/` (Nygard `Status` lifecycle requires a date) and tasks under `doc/tasks/` (append-only `Notes` log is the auditability surface). Use git history for narrative-doc evolution; keep the dated lifecycle inside the artifacts that need it.
|
|
60
|
+
3. **No emoji anywhere.** Not in docs, code, source comments, commit messages, PR bodies, or skill outputs. Severity tags use plain words (`Blocker / Concern / Note`); status uses words (`accepted / superseded`); structural cues use Markdown.
|
|
61
|
+
4. **Business context first.** Every document opens with *why* — the problem, the constraint, the user — before *what* and *how*. The first paragraph answers "what would break if this document didn't exist".
|
|
62
|
+
5. **One scope per document. No duplication.** If two documents would say the same thing, link instead of copying. The canonical location owns the content; everywhere else references it.
|
|
63
|
+
6. **Code is the primary documentation of behavior.** Comments justify *why* a non-obvious choice was made — never restate *what* the line does. If the comment is needed to explain *what*, rename or refactor.
|
|
64
|
+
7. **No commented-out code; no orphan `TODO` / `FIXME` in source.** Every deferred item references a tracked work item — a GitHub Issue, or a per-task file under `doc/tasks/NNNN-*.md` (the kit's file-based tracking surface). The trace must be addressable from the source line.
|
|
65
|
+
8. **Tests are living documentation of behavior.** Test names and assertions should read as the spec they enforce. When the spec changes, the test changes; when the test changes, the spec it documents must already have changed.
|
|
53
66
|
|
|
54
67
|
## 3. Format by Evidence
|
|
55
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexandrealvaro/agentic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-beta.1",
|
|
4
4
|
"description": "Bootstrap and audit AGENTS.md, ARCHITECTURE.md, ADRs, skills, and subagents for engineering production code with LLMs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"start": "node bin/agentic.js",
|
|
23
|
-
"test": "node bin/agentic.js --help && node bin/agentic.js init --help && node --test test/*.test.js",
|
|
23
|
+
"test": "node bin/agentic.js --help && node bin/agentic.js init --help && node bin/agentic.js update --help && node --test test/*.test.js",
|
|
24
24
|
"prepublishOnly": "npm test"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
package/src/commands/init.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
2
5
|
import { detectAgents, detectFeatures, detectMode } from '../lib/detect.js';
|
|
3
6
|
import { installSkills } from '../lib/install.js';
|
|
7
|
+
import { saveState, loadState } from '../lib/state.js';
|
|
4
8
|
import { updateRootDoc } from '../lib/rootdoc.js';
|
|
5
9
|
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const pkg = JSON.parse(
|
|
12
|
+
readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8')
|
|
13
|
+
);
|
|
14
|
+
|
|
6
15
|
const VALID_AGENTS = ['claude-code', 'codex'];
|
|
7
16
|
const AGENT_FLAG_VALUES = ['claude-code', 'codex', 'both'];
|
|
8
17
|
|
|
@@ -206,13 +215,17 @@ export async function initCommand(opts) {
|
|
|
206
215
|
for (const agent of agents) {
|
|
207
216
|
const agentSkills = skillsForAgent(agent, optedSkills);
|
|
208
217
|
for (const s of agentSkills) installedSkillSet.add(s);
|
|
209
|
-
const {
|
|
218
|
+
const previousStates = { [agent]: loadState(cwd, agent) };
|
|
219
|
+
const { actions, nextStates } = await installSkills({
|
|
210
220
|
cwd,
|
|
211
221
|
agents: [agent],
|
|
212
222
|
skills: agentSkills,
|
|
213
223
|
confirmReplace,
|
|
224
|
+
previousStates,
|
|
225
|
+
kitVersion: pkg.version,
|
|
214
226
|
});
|
|
215
227
|
allActions.push(...actions);
|
|
228
|
+
saveState(cwd, agent, nextStates[agent]);
|
|
216
229
|
}
|
|
217
230
|
|
|
218
231
|
const skillDisplayOrder = [
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { detectAgents, detectFeatures } from '../lib/detect.js';
|
|
6
|
+
import { installSkills, removeOrphanSkills } from '../lib/install.js';
|
|
7
|
+
import { loadState, saveState } from '../lib/state.js';
|
|
8
|
+
import { updateRootDoc } from '../lib/rootdoc.js';
|
|
9
|
+
import { CONDITIONAL_SKILLS, REQUIRED_SKILLS } from './init.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const pkg = JSON.parse(
|
|
13
|
+
readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8')
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const VALID_AGENTS = ['claude-code', 'codex'];
|
|
17
|
+
const AGENT_FLAG_VALUES = ['claude-code', 'codex', 'both'];
|
|
18
|
+
|
|
19
|
+
const AGENT_LABEL = {
|
|
20
|
+
'claude-code': 'Claude Code',
|
|
21
|
+
codex: 'Codex',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const ACTION_SYMBOL = {
|
|
25
|
+
created: '+',
|
|
26
|
+
updated: '~',
|
|
27
|
+
replaced: '~',
|
|
28
|
+
unchanged: '·',
|
|
29
|
+
kept: '·',
|
|
30
|
+
skipped: '!',
|
|
31
|
+
removed: '-',
|
|
32
|
+
'orphan-kept': '?',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const ROOT_DOC_LABEL = {
|
|
36
|
+
appended: '+ ',
|
|
37
|
+
updated: '~ ',
|
|
38
|
+
unchanged: '· ',
|
|
39
|
+
skipped: '! ',
|
|
40
|
+
absent: '',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function resolveAgents(flagValue, detectedAgents, previousAgents) {
|
|
44
|
+
if (flagValue === 'both') return ['claude-code', 'codex'];
|
|
45
|
+
if (flagValue) return [flagValue];
|
|
46
|
+
if (previousAgents.length > 0) return previousAgents;
|
|
47
|
+
if (detectedAgents.length > 0) return detectedAgents;
|
|
48
|
+
return ['claude-code'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function pickConditionalAuto(features, targetAgents) {
|
|
52
|
+
return CONDITIONAL_SKILLS.filter(
|
|
53
|
+
(s) => s.autoIf(features) && s.agents.some((a) => targetAgents.includes(a))
|
|
54
|
+
).map((s) => s.name);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function skillsForAgent(agent, optedSkills) {
|
|
58
|
+
const conditional = optedSkills.filter((skillName) => {
|
|
59
|
+
const def = CONDITIONAL_SKILLS.find((s) => s.name === skillName);
|
|
60
|
+
return def && def.agents.includes(agent);
|
|
61
|
+
});
|
|
62
|
+
return [...REQUIRED_SKILLS, ...conditional];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function previousAgentsFromStates(cwd) {
|
|
66
|
+
const out = [];
|
|
67
|
+
for (const agent of VALID_AGENTS) {
|
|
68
|
+
const state = loadState(cwd, agent);
|
|
69
|
+
if (state) out.push(agent);
|
|
70
|
+
}
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function previouslyOptedConditional(previousStates, currentAgents) {
|
|
75
|
+
const opted = new Set();
|
|
76
|
+
for (const agent of currentAgents) {
|
|
77
|
+
const prev = previousStates[agent];
|
|
78
|
+
if (!prev) continue;
|
|
79
|
+
for (const skill of Object.keys(prev.skills ?? {})) {
|
|
80
|
+
if (CONDITIONAL_SKILLS.some((s) => s.name === skill)) {
|
|
81
|
+
opted.add(skill);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return [...opted];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function updateCommand(opts) {
|
|
89
|
+
if (opts.agent && !AGENT_FLAG_VALUES.includes(opts.agent)) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`invalid agent "${opts.agent}". Use one of: ${AGENT_FLAG_VALUES.join(', ')}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const cwd = process.cwd();
|
|
96
|
+
const interactive = process.stdout.isTTY && !opts.yes && !opts.agent;
|
|
97
|
+
const dryRun = Boolean(opts.dryRun);
|
|
98
|
+
const force = Boolean(opts.force);
|
|
99
|
+
|
|
100
|
+
const detectedAgents = detectAgents(cwd);
|
|
101
|
+
const features = detectFeatures(cwd);
|
|
102
|
+
const previousAgents = previousAgentsFromStates(cwd);
|
|
103
|
+
|
|
104
|
+
const agents = resolveAgents(opts.agent, detectedAgents, previousAgents);
|
|
105
|
+
const previousStates = {};
|
|
106
|
+
for (const agent of agents) {
|
|
107
|
+
previousStates[agent] = loadState(cwd, agent);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const previousOpted = previouslyOptedConditional(previousStates, agents);
|
|
111
|
+
const autoOpted = pickConditionalAuto(features, agents);
|
|
112
|
+
const defaultOpted = previousOpted.length ? previousOpted : autoOpted;
|
|
113
|
+
|
|
114
|
+
let optedSkills;
|
|
115
|
+
if (interactive) {
|
|
116
|
+
p.intro(`agentic update${dryRun ? ' (dry-run)' : ''}${force ? ' (force)' : ''}`);
|
|
117
|
+
const previousLine = previousAgents.length
|
|
118
|
+
? previousAgents.map((a) => AGENT_LABEL[a]).join(', ')
|
|
119
|
+
: 'none — first update on a legacy install';
|
|
120
|
+
p.note(
|
|
121
|
+
`Previous install: ${previousLine}\n` +
|
|
122
|
+
`Updating for: ${agents.map((a) => AGENT_LABEL[a]).join(' + ')}\n` +
|
|
123
|
+
`Kit version: ${pkg.version}`,
|
|
124
|
+
'Update plan'
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const conditionalOptions = CONDITIONAL_SKILLS.filter((s) =>
|
|
128
|
+
s.agents.some((a) => agents.includes(a))
|
|
129
|
+
).map((s) => ({
|
|
130
|
+
value: s.name,
|
|
131
|
+
label: s.name,
|
|
132
|
+
hint: s.autoIf(features) ? s.hintWhenAuto : s.hintWhenManual,
|
|
133
|
+
}));
|
|
134
|
+
|
|
135
|
+
if (conditionalOptions.length > 0) {
|
|
136
|
+
const picked = await p.multiselect({
|
|
137
|
+
message: 'Optional skills (toggle to include or exclude):',
|
|
138
|
+
options: conditionalOptions,
|
|
139
|
+
initialValues: defaultOpted,
|
|
140
|
+
required: false,
|
|
141
|
+
});
|
|
142
|
+
if (p.isCancel(picked)) {
|
|
143
|
+
p.cancel('Cancelled.');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
optedSkills = picked;
|
|
147
|
+
} else {
|
|
148
|
+
optedSkills = [];
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
optedSkills = defaultOpted;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const confirmReplace = interactive
|
|
155
|
+
? async (question) => {
|
|
156
|
+
const answer = await p.confirm({ message: question, initialValue: false });
|
|
157
|
+
if (p.isCancel(answer)) return false;
|
|
158
|
+
return answer;
|
|
159
|
+
}
|
|
160
|
+
: async () => Boolean(force);
|
|
161
|
+
|
|
162
|
+
const confirmRemove = interactive
|
|
163
|
+
? async (question) => {
|
|
164
|
+
const answer = await p.confirm({ message: question, initialValue: false });
|
|
165
|
+
if (p.isCancel(answer)) return false;
|
|
166
|
+
return answer;
|
|
167
|
+
}
|
|
168
|
+
: async () => Boolean(force);
|
|
169
|
+
|
|
170
|
+
const installedSkillSet = new Set();
|
|
171
|
+
const allActions = [];
|
|
172
|
+
const nextStates = {};
|
|
173
|
+
|
|
174
|
+
for (const agent of agents) {
|
|
175
|
+
const agentSkills = skillsForAgent(agent, optedSkills);
|
|
176
|
+
for (const s of agentSkills) installedSkillSet.add(s);
|
|
177
|
+
|
|
178
|
+
const orphanResult = await removeOrphanSkills({
|
|
179
|
+
cwd,
|
|
180
|
+
agent,
|
|
181
|
+
previousState: previousStates[agent],
|
|
182
|
+
currentSkills: agentSkills,
|
|
183
|
+
confirmRemove,
|
|
184
|
+
dryRun,
|
|
185
|
+
});
|
|
186
|
+
allActions.push(...orphanResult.actions);
|
|
187
|
+
|
|
188
|
+
const result = await installSkills({
|
|
189
|
+
cwd,
|
|
190
|
+
agents: [agent],
|
|
191
|
+
skills: agentSkills,
|
|
192
|
+
confirmReplace,
|
|
193
|
+
previousStates: { [agent]: previousStates[agent] ?? null },
|
|
194
|
+
kitVersion: pkg.version,
|
|
195
|
+
dryRun,
|
|
196
|
+
force,
|
|
197
|
+
});
|
|
198
|
+
allActions.push(...result.actions);
|
|
199
|
+
nextStates[agent] = result.nextStates[agent];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!dryRun) {
|
|
203
|
+
for (const agent of agents) {
|
|
204
|
+
saveState(cwd, agent, nextStates[agent]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const skillDisplayOrder = [
|
|
209
|
+
...REQUIRED_SKILLS,
|
|
210
|
+
...CONDITIONAL_SKILLS.map((s) => s.name),
|
|
211
|
+
].filter((s) => installedSkillSet.has(s));
|
|
212
|
+
|
|
213
|
+
const confirmAppend = interactive
|
|
214
|
+
? async (path) => {
|
|
215
|
+
const answer = await p.confirm({
|
|
216
|
+
message: `Append a managed "Skills installed by agentic" section to ${path}? (existing content preserved)`,
|
|
217
|
+
initialValue: true,
|
|
218
|
+
});
|
|
219
|
+
if (p.isCancel(answer)) return false;
|
|
220
|
+
return answer;
|
|
221
|
+
}
|
|
222
|
+
: async () => true;
|
|
223
|
+
|
|
224
|
+
const rootDocAction = !dryRun
|
|
225
|
+
? await updateRootDoc({
|
|
226
|
+
cwd,
|
|
227
|
+
skills: skillDisplayOrder,
|
|
228
|
+
confirmAppend,
|
|
229
|
+
})
|
|
230
|
+
: { type: 'absent', path: null };
|
|
231
|
+
|
|
232
|
+
const lines = allActions.map((a) => {
|
|
233
|
+
const sym = ACTION_SYMBOL[a.type] ?? '?';
|
|
234
|
+
return `${sym} [${a.agent}] ${a.path}`;
|
|
235
|
+
});
|
|
236
|
+
if (rootDocAction.type !== 'absent') {
|
|
237
|
+
lines.push(`${ROOT_DOC_LABEL[rootDocAction.type]}${rootDocAction.path}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (interactive) {
|
|
241
|
+
p.note(lines.join('\n') || '(no changes)', dryRun ? 'Plan' : 'Result');
|
|
242
|
+
const closing = dryRun
|
|
243
|
+
? 'Dry-run only — nothing written. Re-run without --dry-run to apply.'
|
|
244
|
+
: `Updated to ${pkg.version}. State saved at .claude/agentic-state.json / .agents/agentic-state.json.`;
|
|
245
|
+
p.outro(closing);
|
|
246
|
+
} else {
|
|
247
|
+
for (const line of lines) {
|
|
248
|
+
process.stderr.write(`${line}\n`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
package/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
5
|
import { initCommand } from './commands/init.js';
|
|
6
|
+
import { updateCommand } from './commands/update.js';
|
|
6
7
|
|
|
7
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
9
|
const pkg = JSON.parse(
|
|
@@ -24,5 +25,14 @@ export async function run(argv) {
|
|
|
24
25
|
.option('-y, --yes', 'skip confirmation prompts (non-interactive)')
|
|
25
26
|
.action(initCommand);
|
|
26
27
|
|
|
28
|
+
program
|
|
29
|
+
.command('update')
|
|
30
|
+
.description('Pull upstream kit changes into an installed project (three-way diff against the saved state)')
|
|
31
|
+
.option('-a, --agent <agent>', 'restrict update to a specific agent: claude-code | codex | both')
|
|
32
|
+
.option('-y, --yes', 'skip confirmation prompts (non-interactive)')
|
|
33
|
+
.option('--dry-run', 'preview the action plan without writing any files')
|
|
34
|
+
.option('--force', 'overwrite user-edited files on conflict (non-interactive default: no)')
|
|
35
|
+
.action(updateCommand);
|
|
36
|
+
|
|
27
37
|
await program.parseAsync(argv);
|
|
28
38
|
}
|
package/src/lib/install.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import {
|
|
2
3
|
copyFileSync,
|
|
3
4
|
existsSync,
|
|
4
5
|
mkdirSync,
|
|
5
6
|
readFileSync,
|
|
6
7
|
readdirSync,
|
|
8
|
+
rmSync,
|
|
7
9
|
statSync,
|
|
10
|
+
unlinkSync,
|
|
8
11
|
} from 'node:fs';
|
|
9
12
|
import { fileURLToPath } from 'node:url';
|
|
10
13
|
import { basename, dirname, join, relative } from 'node:path';
|
|
@@ -26,6 +29,12 @@ const AGENT_LAYOUT = {
|
|
|
26
29
|
},
|
|
27
30
|
};
|
|
28
31
|
|
|
32
|
+
export function agentLayout(agent) {
|
|
33
|
+
const layout = AGENT_LAYOUT[agent];
|
|
34
|
+
if (!layout) throw new Error(`unknown agent "${agent}"`);
|
|
35
|
+
return layout;
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
function walkSkill(srcRoot) {
|
|
30
39
|
const out = [];
|
|
31
40
|
function walk(dir, prefix) {
|
|
@@ -55,94 +64,290 @@ function loadManifest(srcRoot) {
|
|
|
55
64
|
return { subagents: Array.isArray(raw.subagents) ? raw.subagents : [] };
|
|
56
65
|
}
|
|
57
66
|
|
|
67
|
+
function sha256Of(path) {
|
|
68
|
+
return createHash('sha256').update(readFileSync(path)).digest('hex');
|
|
69
|
+
}
|
|
70
|
+
|
|
58
71
|
function sameFile(a, b) {
|
|
59
72
|
if (!existsSync(b)) return false;
|
|
60
73
|
return readFileSync(a).equals(readFileSync(b));
|
|
61
74
|
}
|
|
62
75
|
|
|
76
|
+
function targetForRel(rel, layout, targetRoot, cwd, subagentSet) {
|
|
77
|
+
if (subagentSet.has(rel)) {
|
|
78
|
+
if (!layout.agentsDir) return null;
|
|
79
|
+
return join(cwd, layout.agentsDir, basename(rel));
|
|
80
|
+
}
|
|
81
|
+
return join(targetRoot, rel);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function resolveSkillSource(agent, skill) {
|
|
85
|
+
const layout = agentLayout(agent);
|
|
86
|
+
const srcRoot = join(KIT_ROOT, layout.sourceDir, skill);
|
|
87
|
+
if (!existsSync(srcRoot)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`skill "${skill}" not found for agent "${agent}" (expected at ${srcRoot})`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const manifest = loadManifest(srcRoot);
|
|
93
|
+
const subagentSet = new Set(manifest.subagents);
|
|
94
|
+
const walked = walkSkill(srcRoot);
|
|
95
|
+
const walkedRels = new Set(walked.map(({ rel }) => rel));
|
|
96
|
+
for (const declared of subagentSet) {
|
|
97
|
+
if (!walkedRels.has(declared)) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`manifest at ${join(srcRoot, MANIFEST_FILE)} declares subagent "${declared}" but no such file exists in the skill source`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { layout, srcRoot, subagentSet, walked };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function planFile({
|
|
107
|
+
src,
|
|
108
|
+
rel,
|
|
109
|
+
target,
|
|
110
|
+
relForReport,
|
|
111
|
+
prevSha,
|
|
112
|
+
force,
|
|
113
|
+
}) {
|
|
114
|
+
const sourceSha = sha256Of(src);
|
|
115
|
+
if (!existsSync(target)) {
|
|
116
|
+
return { type: 'create', sourceSha };
|
|
117
|
+
}
|
|
118
|
+
const targetSha = sha256Of(target);
|
|
119
|
+
|
|
120
|
+
if (sourceSha === targetSha) {
|
|
121
|
+
return { type: 'unchanged', sourceSha };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (prevSha === undefined || prevSha === null) {
|
|
125
|
+
return { type: 'legacy-divergent', sourceSha, targetSha };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (sourceSha === prevSha) {
|
|
129
|
+
// kit unchanged since last install; differ vs. target → user edited; keep
|
|
130
|
+
return { type: 'user-edited-keep', sourceSha };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (targetSha === prevSha) {
|
|
134
|
+
// user untouched; kit changed → silent update
|
|
135
|
+
return { type: 'kit-changed-update', sourceSha };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// both changed → conflict
|
|
139
|
+
if (force) return { type: 'conflict-force', sourceSha };
|
|
140
|
+
return { type: 'conflict-prompt', sourceSha, targetSha };
|
|
141
|
+
}
|
|
142
|
+
|
|
63
143
|
/**
|
|
64
|
-
* Install one or more skills for one or more agents
|
|
144
|
+
* Install one or more skills for one or more agents, with optional state-aware
|
|
145
|
+
* three-way diff against the prior install.
|
|
146
|
+
*
|
|
147
|
+
* Legacy contract (no state): default-skip on divergent files, prompt via
|
|
148
|
+
* confirmReplace. Matches v0.2 behavior.
|
|
149
|
+
*
|
|
150
|
+
* State-aware contract (previousStates supplied): three-way diff using the
|
|
151
|
+
* previous source SHA, target SHA, current source SHA. See ADR-0009.
|
|
65
152
|
*
|
|
66
153
|
* @param {object} opts
|
|
67
|
-
* @param {string} opts.cwd
|
|
68
|
-
* @param {string[]} opts.agents
|
|
69
|
-
* @param {string[]} opts.skills
|
|
154
|
+
* @param {string} opts.cwd
|
|
155
|
+
* @param {string[]} opts.agents
|
|
156
|
+
* @param {string[]} opts.skills
|
|
70
157
|
* @param {(question: string) => Promise<boolean>} [opts.confirmReplace]
|
|
71
|
-
*
|
|
72
|
-
*
|
|
158
|
+
* Callback for divergent files (legacy path or three-way conflict). Default: skip.
|
|
159
|
+
* @param {Record<string, object|null>} [opts.previousStates]
|
|
160
|
+
* Per-agent previous state from src/lib/state.js. Missing/null entries fall
|
|
161
|
+
* back to legacy byte-compare.
|
|
162
|
+
* @param {string} [opts.kitVersion]
|
|
163
|
+
* Stamped into the returned nextStates as kitVersion and per-skill version.
|
|
164
|
+
* @param {boolean} [opts.dryRun]
|
|
165
|
+
* If true, no files are written. Actions reflect what would happen.
|
|
166
|
+
* @param {boolean} [opts.force]
|
|
167
|
+
* If true, three-way conflicts overwrite without prompting.
|
|
73
168
|
*
|
|
74
|
-
* @returns {Promise<{ actions: Array<{type
|
|
169
|
+
* @returns {Promise<{ actions: Array<{type, path, agent}>, nextStates: Record<string, object> }>}
|
|
75
170
|
*/
|
|
76
171
|
export async function installSkills({
|
|
77
172
|
cwd,
|
|
78
173
|
agents,
|
|
79
174
|
skills,
|
|
80
175
|
confirmReplace = async () => false,
|
|
176
|
+
previousStates = {},
|
|
177
|
+
kitVersion = null,
|
|
178
|
+
dryRun = false,
|
|
179
|
+
force = false,
|
|
81
180
|
}) {
|
|
82
181
|
const actions = [];
|
|
182
|
+
const nextStates = {};
|
|
83
183
|
|
|
84
184
|
for (const agent of agents) {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
throw new Error(`unknown agent "${agent}"`);
|
|
88
|
-
}
|
|
185
|
+
const prev = previousStates[agent] ?? null;
|
|
186
|
+
const nextSkills = {};
|
|
89
187
|
|
|
90
188
|
for (const skill of skills) {
|
|
91
|
-
const srcRoot =
|
|
92
|
-
if (!existsSync(srcRoot)) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
`skill "${skill}" not found for agent "${agent}" (expected at ${srcRoot})`
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
189
|
+
const { layout, srcRoot, subagentSet, walked } = resolveSkillSource(agent, skill);
|
|
98
190
|
const targetRoot = join(cwd, layout.skillsDir, skill);
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (!walkedRels.has(declared)) {
|
|
105
|
-
throw new Error(
|
|
106
|
-
`manifest at ${join(srcRoot, MANIFEST_FILE)} declares subagent "${declared}" but no such file exists in the skill source`
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
191
|
+
const prevSkill = prev?.skills?.[skill] ?? null;
|
|
192
|
+
const prevByPath = new Map(
|
|
193
|
+
(prevSkill?.files ?? []).map((f) => [f.path, f.sourceSha])
|
|
194
|
+
);
|
|
195
|
+
const skillFiles = [];
|
|
110
196
|
|
|
111
197
|
for (const { src, rel } of walked) {
|
|
112
198
|
if (rel === MANIFEST_FILE) continue;
|
|
113
199
|
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
if (!layout.agentsDir) continue;
|
|
117
|
-
target = join(cwd, layout.agentsDir, basename(rel));
|
|
118
|
-
} else {
|
|
119
|
-
target = join(targetRoot, rel);
|
|
120
|
-
}
|
|
200
|
+
const target = targetForRel(rel, layout, targetRoot, cwd, subagentSet);
|
|
201
|
+
if (!target) continue;
|
|
121
202
|
const relForReport = relative(cwd, target);
|
|
203
|
+
const prevSha = prevByPath.has(relForReport)
|
|
204
|
+
? prevByPath.get(relForReport)
|
|
205
|
+
: null;
|
|
206
|
+
|
|
207
|
+
const decision = planFile({
|
|
208
|
+
src,
|
|
209
|
+
rel,
|
|
210
|
+
target,
|
|
211
|
+
relForReport,
|
|
212
|
+
prevSha,
|
|
213
|
+
force,
|
|
214
|
+
});
|
|
122
215
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
216
|
+
let actionType;
|
|
217
|
+
let writeFile = false;
|
|
218
|
+
|
|
219
|
+
switch (decision.type) {
|
|
220
|
+
case 'create':
|
|
221
|
+
actionType = 'created';
|
|
222
|
+
writeFile = true;
|
|
223
|
+
break;
|
|
224
|
+
case 'unchanged':
|
|
225
|
+
actionType = 'unchanged';
|
|
226
|
+
break;
|
|
227
|
+
case 'kit-changed-update':
|
|
228
|
+
actionType = 'updated';
|
|
229
|
+
writeFile = true;
|
|
230
|
+
break;
|
|
231
|
+
case 'user-edited-keep':
|
|
232
|
+
actionType = 'kept';
|
|
233
|
+
break;
|
|
234
|
+
case 'conflict-force':
|
|
235
|
+
actionType = 'replaced';
|
|
236
|
+
writeFile = true;
|
|
237
|
+
break;
|
|
238
|
+
case 'conflict-prompt': {
|
|
239
|
+
const replace = await confirmReplace(
|
|
240
|
+
`${relForReport}: kit and your edits both changed since last install. Overwrite?`
|
|
241
|
+
);
|
|
242
|
+
if (replace) {
|
|
243
|
+
actionType = 'replaced';
|
|
244
|
+
writeFile = true;
|
|
245
|
+
} else {
|
|
246
|
+
actionType = 'skipped';
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
127
249
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
250
|
+
case 'legacy-divergent': {
|
|
251
|
+
const replace = await confirmReplace(
|
|
252
|
+
`Replace ${relForReport}? (target differs from the kit version)`
|
|
253
|
+
);
|
|
254
|
+
if (replace) {
|
|
255
|
+
actionType = 'replaced';
|
|
256
|
+
writeFile = true;
|
|
257
|
+
} else {
|
|
258
|
+
actionType = 'skipped';
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
134
261
|
}
|
|
262
|
+
default:
|
|
263
|
+
throw new Error(`unhandled plan decision: ${decision.type}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (writeFile && !dryRun) {
|
|
135
267
|
mkdirSync(dirname(target), { recursive: true });
|
|
136
268
|
copyFileSync(src, target);
|
|
137
|
-
actions.push({ type: 'replaced', path: relForReport });
|
|
138
|
-
} else {
|
|
139
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
140
|
-
copyFileSync(src, target);
|
|
141
|
-
actions.push({ type: 'created', path: relForReport });
|
|
142
269
|
}
|
|
270
|
+
|
|
271
|
+
actions.push({ type: actionType, path: relForReport, agent });
|
|
272
|
+
skillFiles.push({ path: relForReport, sourceSha: decision.sourceSha });
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
nextSkills[skill] = {
|
|
276
|
+
version: kitVersion ?? prevSkill?.version ?? null,
|
|
277
|
+
files: skillFiles,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
nextStates[agent] = {
|
|
282
|
+
schemaVersion: 1,
|
|
283
|
+
kitVersion: kitVersion ?? prev?.kitVersion ?? null,
|
|
284
|
+
agent,
|
|
285
|
+
skills: nextSkills,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return { actions, nextStates };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Identify and optionally remove orphan skills — skills present in the prior
|
|
294
|
+
* state but absent from the current opted skill list. Default keep.
|
|
295
|
+
*
|
|
296
|
+
* @param {object} opts
|
|
297
|
+
* @param {string} opts.cwd
|
|
298
|
+
* @param {string} opts.agent
|
|
299
|
+
* @param {object|null} opts.previousState
|
|
300
|
+
* @param {string[]} opts.currentSkills
|
|
301
|
+
* @param {(question: string) => Promise<boolean>} [opts.confirmRemove]
|
|
302
|
+
* @param {boolean} [opts.dryRun]
|
|
303
|
+
*
|
|
304
|
+
* @returns {Promise<{ actions: Array<{type, path, agent}>, removedSkills: string[] }>}
|
|
305
|
+
*/
|
|
306
|
+
export async function removeOrphanSkills({
|
|
307
|
+
cwd,
|
|
308
|
+
agent,
|
|
309
|
+
previousState,
|
|
310
|
+
currentSkills,
|
|
311
|
+
confirmRemove = async () => false,
|
|
312
|
+
dryRun = false,
|
|
313
|
+
}) {
|
|
314
|
+
const actions = [];
|
|
315
|
+
const removedSkills = [];
|
|
316
|
+
if (!previousState?.skills) return { actions, removedSkills };
|
|
317
|
+
|
|
318
|
+
const layout = agentLayout(agent);
|
|
319
|
+
const currentSet = new Set(currentSkills);
|
|
320
|
+
|
|
321
|
+
for (const [skill, entry] of Object.entries(previousState.skills)) {
|
|
322
|
+
if (currentSet.has(skill)) continue;
|
|
323
|
+
const remove = await confirmRemove(
|
|
324
|
+
`Remove orphan skill "${skill}" for ${agent}? (no longer in your install set)`
|
|
325
|
+
);
|
|
326
|
+
if (!remove) {
|
|
327
|
+
for (const f of entry.files) {
|
|
328
|
+
actions.push({ type: 'orphan-kept', path: f.path, agent });
|
|
329
|
+
}
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for (const f of entry.files) {
|
|
334
|
+
const abs = join(cwd, f.path);
|
|
335
|
+
if (existsSync(abs) && !dryRun) {
|
|
336
|
+
unlinkSync(abs);
|
|
337
|
+
}
|
|
338
|
+
actions.push({ type: 'removed', path: f.path, agent });
|
|
339
|
+
}
|
|
340
|
+
// Try to drop the empty skill directory under skillsDir/<skill>.
|
|
341
|
+
const skillDir = join(cwd, layout.skillsDir, skill);
|
|
342
|
+
if (!dryRun && existsSync(skillDir)) {
|
|
343
|
+
try {
|
|
344
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
345
|
+
} catch {
|
|
346
|
+
// best-effort cleanup
|
|
143
347
|
}
|
|
144
348
|
}
|
|
349
|
+
removedSkills.push(skill);
|
|
145
350
|
}
|
|
146
351
|
|
|
147
|
-
return { actions };
|
|
352
|
+
return { actions, removedSkills };
|
|
148
353
|
}
|
package/src/lib/state.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const SCHEMA_VERSION = 1;
|
|
5
|
+
export const STATE_FILE = 'agentic-state.json';
|
|
6
|
+
|
|
7
|
+
export const STATE_DIRS = {
|
|
8
|
+
'claude-code': '.claude',
|
|
9
|
+
codex: '.agents',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function statePath(cwd, agent) {
|
|
13
|
+
const dir = STATE_DIRS[agent];
|
|
14
|
+
if (!dir) throw new Error(`unknown agent "${agent}"`);
|
|
15
|
+
return join(cwd, dir, STATE_FILE);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function emptyState(agent, kitVersion) {
|
|
19
|
+
return {
|
|
20
|
+
schemaVersion: SCHEMA_VERSION,
|
|
21
|
+
kitVersion,
|
|
22
|
+
agent,
|
|
23
|
+
skills: {},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function loadState(cwd, agent) {
|
|
28
|
+
const path = statePath(cwd, agent);
|
|
29
|
+
if (!existsSync(path)) return null;
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
33
|
+
} catch (err) {
|
|
34
|
+
throw new Error(`malformed state at ${path}: ${err.message}`);
|
|
35
|
+
}
|
|
36
|
+
if (typeof raw.schemaVersion !== 'number') {
|
|
37
|
+
throw new Error(`state at ${path} missing schemaVersion`);
|
|
38
|
+
}
|
|
39
|
+
if (raw.schemaVersion > SCHEMA_VERSION) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`state at ${path} has schemaVersion ${raw.schemaVersion}; this kit only knows ${SCHEMA_VERSION}. Upgrade the kit.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (raw.agent && raw.agent !== agent) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`state at ${path} declares agent "${raw.agent}" but expected "${agent}"`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
schemaVersion: raw.schemaVersion,
|
|
51
|
+
kitVersion: raw.kitVersion ?? null,
|
|
52
|
+
agent,
|
|
53
|
+
skills: raw.skills ?? {},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function saveState(cwd, agent, state) {
|
|
58
|
+
const path = statePath(cwd, agent);
|
|
59
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
60
|
+
const ordered = orderState(state);
|
|
61
|
+
writeFileSync(path, JSON.stringify(ordered, null, 2) + '\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function orderState(state) {
|
|
65
|
+
const skills = {};
|
|
66
|
+
for (const skillName of Object.keys(state.skills).sort()) {
|
|
67
|
+
const entry = state.skills[skillName];
|
|
68
|
+
const files = [...entry.files].sort((a, b) => a.path.localeCompare(b.path));
|
|
69
|
+
skills[skillName] = {
|
|
70
|
+
version: entry.version,
|
|
71
|
+
files: files.map((f) => ({ path: f.path, sourceSha: f.sourceSha })),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
schemaVersion: state.schemaVersion,
|
|
76
|
+
kitVersion: state.kitVersion,
|
|
77
|
+
agent: state.agent,
|
|
78
|
+
skills,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -60,3 +60,10 @@ Stop after writing. Do **not** flip status to `accepted` — that requires user
|
|
|
60
60
|
## Output contract
|
|
61
61
|
|
|
62
62
|
A single new file at `doc/adr/<NNNN>-<short-slug>.md`. Status `proposed`. No existing ADRs modified. No invented content.
|
|
63
|
+
|
|
64
|
+
ADRs are decision-record artifacts and are **exempt** from the no-dates rule (Documentation Discipline §2): the `**Status:**` lifecycle and `**Date:**` field are required for Nygard supersession ordering. The remaining Documentation Discipline rules (`WORKFLOW.md` §2) apply at write time:
|
|
65
|
+
|
|
66
|
+
- No emoji anywhere in the file.
|
|
67
|
+
- `Context` is the business-context-first section — the *forces* and *problem* before the *decision*.
|
|
68
|
+
- One scope: one decision per ADR. If the user's request implies multiple decisions, ask which one to write first; the others become follow-up ADRs.
|
|
69
|
+
- No speculation. `Decision` is a directive ("We will…"); rejected paths go in `Alternatives Considered`, not the body.
|
|
@@ -102,3 +102,11 @@ Currently-binding decisions. Link each to `doc/adr/`.
|
|
|
102
102
|
## Output contract
|
|
103
103
|
|
|
104
104
|
A single `ARCHITECTURE.md` at the repo root. Every line locks a binding pattern. ADR candidates flagged in the response, not written. In audit mode: drift list only, no file written.
|
|
105
|
+
|
|
106
|
+
`ARCHITECTURE.md` is a narrative document, so the Documentation Discipline rules in `WORKFLOW.md` §2 apply at write time:
|
|
107
|
+
|
|
108
|
+
- No emoji anywhere in the file.
|
|
109
|
+
- No dates, version stamps, `DRAFT` markers, or changelog blocks. Architecture is the current binding pattern; the lifecycle of "when did this become true" lives in ADRs and git history.
|
|
110
|
+
- `Overview` is the business-context-first paragraph — *what the system does* and *what would break without it* before layers and patterns.
|
|
111
|
+
- One scope: system-level patterns and boundaries. Per-decision rationale lives in ADRs; per-project operations live in `AGENTS.md`. Link, do not copy.
|
|
112
|
+
- No speculation. If a layer has no observed signal, write `<TODO: not yet wired>` once; do not propose patterns that aren't already in the code.
|
|
@@ -34,6 +34,21 @@ If the user names an artifact (`AGENTS.md`, `ARCHITECTURE.md`, ADRs), audit only
|
|
|
34
34
|
* Status field — every ADR has one of `proposed | accepted | deprecated | superseded by ADR-NNNN`.
|
|
35
35
|
* Superseded chains — every "superseded by ADR-NNNN" target exists.
|
|
36
36
|
|
|
37
|
+
### Documentation discipline drift (`WORKFLOW.md` §2 / ADR-0008)
|
|
38
|
+
|
|
39
|
+
Audit narrative documents — `README.md`, `AGENTS.md` / `CLAUDE.md`, `ARCHITECTURE.md`, `DESIGN.md`, any prose page under `doc/` that is not a decision-record artifact under `doc/adr/` or `doc/tasks/`:
|
|
40
|
+
|
|
41
|
+
* Emoji — any present? Rule 3 forbids emoji anywhere (docs, code, comments, commits, skill outputs).
|
|
42
|
+
* Dates / version stamps / `DRAFT` markers / changelog blocks in narrative documents — Rule 2 forbids these. Decision-record artifacts under `doc/adr/` and `doc/tasks/` are exempt.
|
|
43
|
+
* Business context first — does the first paragraph answer *why* the document exists, before *what* and *how*? Rule 4.
|
|
44
|
+
* Scope duplication — does the document copy material that is canonically owned by another file (`AGENTS.md` repeating `ARCHITECTURE.md` patterns; `README.md` re-stating ADR rationale)? Rule 5 requires linking, not copying.
|
|
45
|
+
* Speculation — phrases like "we might", "in the future", "could be added", or roadmaps without an ADR / task reference. Rule 1 forbids unfounded plans.
|
|
46
|
+
|
|
47
|
+
Source code (sample, not exhaustive — flag findings, not every match):
|
|
48
|
+
|
|
49
|
+
* Orphan `TODO` / `FIXME` — Rule 7. A reference to a GitHub Issue or a `doc/tasks/NNNN-*.md` task file makes it not orphan.
|
|
50
|
+
* Commented-out code blocks — Rule 7. Removed code lives in git history.
|
|
51
|
+
|
|
37
52
|
## Step 3 — Output
|
|
38
53
|
|
|
39
54
|
One line per finding, formatted:
|
|
@@ -42,7 +57,7 @@ One line per finding, formatted:
|
|
|
42
57
|
[file or section]: spec says X, code says Y. Suggested resolution: change spec / change code / discuss.
|
|
43
58
|
```
|
|
44
59
|
|
|
45
|
-
Group by artifact. If a category has no drift, print one line: `AGENTS.md — no drift.` etc. If an audited artifact does not exist, say so explicitly rather than reporting zero findings.
|
|
60
|
+
Group by artifact. If a category has no drift, print one line: `AGENTS.md — no drift.` etc. If an audited artifact does not exist, say so explicitly rather than reporting zero findings. The Documentation discipline drift category groups its own findings under `Documentation discipline — <category>: ...`.
|
|
46
61
|
|
|
47
62
|
If something the user says contradicts what the code shows, surface the conflict. Don't silently trust the user; don't silently trust the code.
|
|
48
63
|
|
|
@@ -151,3 +151,11 @@ Real traps. Each one should map to an incident or to specific code.
|
|
|
151
151
|
## Output contract
|
|
152
152
|
|
|
153
153
|
A single `AGENTS.md` at the repo root, ≤150 lines, every line operational. No "External Resources" section. No appended Universal Agent Behavior block — that lives in the `agentic-philosophy` skill. No meta-prose explaining gaps. In audit mode: a drift list, no file written.
|
|
154
|
+
|
|
155
|
+
`AGENTS.md` is a narrative document, so the Documentation Discipline rules in `WORKFLOW.md` §2 apply at write time:
|
|
156
|
+
|
|
157
|
+
- No emoji anywhere in the file.
|
|
158
|
+
- No dates, version stamps, `DRAFT` markers, or changelog blocks. Stack versions are facts (`Node ≥18`); release timelines are not.
|
|
159
|
+
- `Project Overview` is the business-context-first paragraph — *why* the project exists before *what* it does.
|
|
160
|
+
- One scope: this file is the operational guide for agents. Do not duplicate `ARCHITECTURE.md` patterns or ADR rationale here; link instead.
|
|
161
|
+
- No speculation. If a section has no signal, write `<TODO: not yet wired>` once; do not narrate "this could be added later".
|
|
@@ -61,3 +61,11 @@ If the source has tokens DESIGN.md doesn't document, list those too as additions
|
|
|
61
61
|
## Output contract
|
|
62
62
|
|
|
63
63
|
A single `DESIGN.md` at the repo root. YAML frontmatter uses W3C `$value`/`$type` shape. Markdown body has one section per token group present in the source. No invented tokens. No "External Resources" section. In audit mode: a drift list, no file written.
|
|
64
|
+
|
|
65
|
+
`DESIGN.md` is a narrative document, so the Documentation Discipline rules in `WORKFLOW.md` §2 apply at write time:
|
|
66
|
+
|
|
67
|
+
- No emoji anywhere — including the Markdown body's do's and don'ts.
|
|
68
|
+
- No dates, version stamps, `DRAFT` markers, or changelog blocks. Token revisions live in git history; DESIGN.md is the current visual contract.
|
|
69
|
+
- The Markdown body opens with the *why* of each token group — the visual constraint or product principle — before listing rules.
|
|
70
|
+
- One scope: visual contract. Component anatomy and interaction patterns belong elsewhere; link, do not copy.
|
|
71
|
+
- No speculation. If a group has no source token, mark `<TODO: not yet wired>` and move on.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentic-philosophy
|
|
3
|
-
description: Universal agent behavior — think before coding, ground in real patterns, prefer simplicity, make surgical changes, define verifiable goals, verify before claiming done. Auto-invokes on non-trivial changes, refactors, debugging, "think before coding", "ground before coding", "verify done", "before implementing", or whenever the task is ambiguous enough that guardrails matter.
|
|
3
|
+
description: Universal agent behavior and documentation discipline — think before coding, ground in real patterns, prefer simplicity, make surgical changes, define verifiable goals, verify before claiming done, and write documentation that captures only definitions and decisions. Auto-invokes on non-trivial changes, refactors, debugging, "think before coding", "ground before coding", "verify done", "before implementing", on documentation work — "writing docs", "writing readme", "writing architecture", "writing adr", "writing task", "audit docs" — or whenever the task is ambiguous enough that guardrails matter.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /agentic-philosophy
|
|
7
7
|
|
|
8
|
-
Six behaviors apply to every non-trivial change. Bias toward caution over speed; for trivial diffs, use judgment.
|
|
8
|
+
Six behaviors apply to every non-trivial change. Bias toward caution over speed; for trivial diffs, use judgment. A separate Documentation Discipline section at the end applies to every document the agent writes.
|
|
9
9
|
|
|
10
10
|
## Think Before Coding
|
|
11
11
|
|
|
@@ -86,3 +86,18 @@ Strong success criteria let you loop independently. Weak criteria ("make it work
|
|
|
86
86
|
- For UI/runtime changes, exercise the feature in a browser.
|
|
87
87
|
- Can't verify it? Say so. Don't claim success.
|
|
88
88
|
- Never bypass gates (`--no-verify`, skipped hooks, deleted failing tests).
|
|
89
|
+
|
|
90
|
+
## Documentation Discipline
|
|
91
|
+
|
|
92
|
+
**Every document the agent writes obeys these eight rules.** The canonical source is `WORKFLOW.md` §2. ADR-0008 records the decision and its reconciliations.
|
|
93
|
+
|
|
94
|
+
1. **Definitions and decisions only.** Capture what is true now and the decisions that brought it there. No speculation, no history, no unfounded plans. A deferred decision is in scope when it is *recorded* — an accepted ADR or a task file is fundamentação; "we might do X later" without a record is speculation and is cut.
|
|
95
|
+
2. **No dates, version stamps, `DRAFT` markers, or changelogs in narrative documents.** Applies to `README.md`, `AGENTS.md` / `CLAUDE.md`, `ARCHITECTURE.md`, `DESIGN.md`, specs, and any prose page. **Decision-record artifacts are exempt** — ADRs under `doc/adr/` keep their `**Status:**` and `**Date:**` fields (Nygard supersession requires ordering); tasks under `doc/tasks/` keep their `**Created:**` field and append-only dated `Notes` log. Outside those artifacts, use git history.
|
|
96
|
+
3. **No emoji anywhere.** Not in docs, code, source comments, commit messages, PR bodies, or skill outputs. Severity and status use words; structural cues use Markdown.
|
|
97
|
+
4. **Business context first.** Open every document with *why* — the problem, the constraint, the user — before *what* and *how*. The first paragraph must answer "what would break if this document didn't exist".
|
|
98
|
+
5. **One scope per document. No duplication.** If two documents would say the same thing, link instead of copying. The canonical location owns the content; everywhere else references it.
|
|
99
|
+
6. **Code is the primary documentation of behavior.** Comments justify *why* a non-obvious choice was made — never restate *what*. If the comment is needed to explain *what*, rename or refactor.
|
|
100
|
+
7. **No commented-out code; no orphan `TODO` / `FIXME` in source.** Every deferred item references a tracked work item — a GitHub Issue, or a per-task file under `doc/tasks/NNNN-*.md`. The trace must be addressable from the source line.
|
|
101
|
+
8. **Tests are living documentation of behavior.** Test names and assertions read as the spec they enforce. Spec changes drive test changes; never the reverse.
|
|
102
|
+
|
|
103
|
+
When generating or auditing a document, walk this list before declaring done.
|
|
@@ -31,16 +31,22 @@ The reviewer subagent will get **only** what you assemble here. No conversation
|
|
|
31
31
|
|
|
32
32
|
Build the handoff as a single message: a short framing paragraph (what's being reviewed, what spec applies), followed by the diff and the spec slice. No prose summary of what you think the diff does — the reviewer reads the diff itself.
|
|
33
33
|
|
|
34
|
-
## Step 2 —
|
|
34
|
+
## Step 2 — Persist the handoff to disk
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Write the assembled handoff to `.agentic/reviews/<ISO-timestamp>-<scope-slug>.md` at the repo root. The path encodes both the moment of review and a short slug for the scope (`branch-vs-main`, `pr-42`, `commit-abc1234`, `working-tree`). Create the directory if it does not exist. The file is the audit trail — the user can read it later, replay the review against an updated diff, or share it with a teammate.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
This directory is ephemeral; advise the user to add `.agentic/reviews/` to their `.gitignore` if it is not already. Handoffs are per-review artifacts, not committed history.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
## Step 3 — Delegate to the subagent
|
|
41
|
+
|
|
42
|
+
Invoke the bundled `fresh-context-reviewer` subagent via the `Task` tool. Pass the assembled handoff as the prompt — the same content you wrote to disk. The subagent has read-only tools (`Read, Glob, Grep, Bash`) and no write access; it cannot accidentally modify the code under review.
|
|
43
|
+
|
|
44
|
+
## Step 4 — Surface findings
|
|
45
|
+
|
|
46
|
+
Relay the subagent's findings to the user verbatim, grouped by severity (Blocker / Concern / Note). Do **not** add commentary defending the code. Do **not** synthesize an "approve" verdict — §10 frames the reviewer as adversarial; approval is the user's call after weighing the findings. Reference the persisted handoff path in your reply so the user can audit what was sent.
|
|
41
47
|
|
|
42
48
|
If the subagent reports zero findings across all severities, say so explicitly ("no issues found in <range>"). Empty results are real signal, not a gap.
|
|
43
49
|
|
|
44
50
|
## Output contract
|
|
45
51
|
|
|
46
|
-
A structured findings list grouped Blocker / Concern / Note, each finding `file:line: <
|
|
52
|
+
A structured findings list grouped Blocker / Concern / Note, each finding `file:line: <severity>: <problem>. <fix>.`. The path of the persisted handoff under `.agentic/reviews/` is reported alongside. No "approve" verdict, no defending of the code, no rewrite of the diff. Empty result is reported explicitly.
|
|
@@ -37,9 +37,7 @@ Group findings by severity:
|
|
|
37
37
|
- **Concern** — worth a follow-up task. Real issue, not blocking the current change.
|
|
38
38
|
- **Note** — informational, no action expected. Includes "no issues found in this section".
|
|
39
39
|
|
|
40
|
-
Each finding: one line, `file:line: <
|
|
41
|
-
|
|
42
|
-
Use `🚨` for Blocker, `⚠️` for Concern, `ℹ️` for Note.
|
|
40
|
+
Each finding: one line, `file:line: <severity>: <problem>. <fix>.` Severity is the literal word `Blocker`, `Concern`, or `Note`.
|
|
43
41
|
|
|
44
42
|
End with a one-line bottom-line: `Ship as-is`, `Ship with the Concerns logged as follow-up tasks`, or `Don't ship until Blockers resolved`.
|
|
45
43
|
|
|
@@ -91,3 +91,11 @@ All Acceptance Criteria checked, plus:
|
|
|
91
91
|
## Output contract
|
|
92
92
|
|
|
93
93
|
A single new file at `doc/tasks/<NNNN>-<short-slug>.md`. Status `proposed`. Notes empty. No existing tasks modified. No invented values.
|
|
94
|
+
|
|
95
|
+
Task files are decision-record artifacts and are **exempt** from the no-dates rule (Documentation Discipline §2): the `**Created:**` field anchors the task in time and the append-only `Notes` log is dated per entry by design. The remaining Documentation Discipline rules (`WORKFLOW.md` §2) apply at write time:
|
|
96
|
+
|
|
97
|
+
- No emoji anywhere in the file.
|
|
98
|
+
- `Context` is the business-context-first section — *why this task exists* and *what would break without it* before *Acceptance Criteria*.
|
|
99
|
+
- One scope: one task per file. If the user's request implies multiple deliverables, ask which to write first; the others become follow-up tasks.
|
|
100
|
+
- No speculation. Acceptance criteria must be measurable; do not list aspirational items ("loads in under 2s", not "fast enough").
|
|
101
|
+
- `Notes` is append-only and dated per entry — that is the auditability primitive, not a violation of Rule 2.
|
|
@@ -50,4 +50,10 @@ Stop after writing. Do NOT flip status to accepted — that requires user review
|
|
|
50
50
|
|
|
51
51
|
<output_contract>
|
|
52
52
|
A single new file at `doc/adr/<NNNN>-<short-slug>.md`. Status proposed. No existing ADRs modified. No invented content.
|
|
53
|
+
|
|
54
|
+
ADRs are decision-record artifacts and are exempt from the no-dates rule (Documentation Discipline §2): `**Status:**` and `**Date:**` are required for Nygard supersession ordering. Remaining Documentation Discipline rules (`WORKFLOW.md` §2) apply at write time:
|
|
55
|
+
- No emoji anywhere in the file.
|
|
56
|
+
- `Context` is the business-context-first section — *forces* and *problem* before the *decision*.
|
|
57
|
+
- One scope: one decision per ADR.
|
|
58
|
+
- No speculation. `Decision` is a directive; rejected paths go in `Alternatives Considered`.
|
|
53
59
|
</output_contract>
|
|
@@ -85,4 +85,11 @@ Currently-binding decisions. Link each to `doc/adr/`.
|
|
|
85
85
|
|
|
86
86
|
<output_contract>
|
|
87
87
|
A single `ARCHITECTURE.md` at the repo root. Every line locks a binding pattern. ADR candidates flagged in the response, not written. In audit mode: drift list only, no file written.
|
|
88
|
+
|
|
89
|
+
`ARCHITECTURE.md` is a narrative document, so the Documentation Discipline rules in `WORKFLOW.md` §2 apply at write time:
|
|
90
|
+
- No emoji anywhere in the file.
|
|
91
|
+
- No dates, version stamps, `DRAFT` markers, or changelog blocks. Architecture is the current binding pattern; lifecycle lives in ADRs and git history.
|
|
92
|
+
- `Overview` is the business-context-first paragraph — *what the system does* and *what would break without it* before layers and patterns.
|
|
93
|
+
- One scope: system-level patterns and boundaries. Per-decision rationale lives in ADRs; per-project operations live in `AGENTS.md`. Link, do not copy.
|
|
94
|
+
- No speculation. If a layer has no observed signal, write `<TODO: not yet wired>` once; do not propose patterns absent from the code.
|
|
88
95
|
</output_contract>
|
|
@@ -29,10 +29,21 @@ ADR drift (if `doc/adr/` exists):
|
|
|
29
29
|
- Status field — every ADR has one of `proposed | accepted | deprecated | superseded by ADR-NNNN`.
|
|
30
30
|
- Superseded chains — every "superseded by ADR-NNNN" target exists.
|
|
31
31
|
|
|
32
|
+
Documentation discipline drift (`WORKFLOW.md` §2 / ADR-0008). Audit narrative documents — `README.md`, `AGENTS.md` / `CLAUDE.md`, `ARCHITECTURE.md`, `DESIGN.md`, any prose page under `doc/` that is not a decision-record artifact under `doc/adr/` or `doc/tasks/`:
|
|
33
|
+
- Emoji — any present? Rule 3 forbids emoji anywhere (docs, code, comments, commits, skill outputs).
|
|
34
|
+
- Dates / version stamps / `DRAFT` markers / changelog blocks in narrative documents — Rule 2 forbids these. Decision-record artifacts under `doc/adr/` and `doc/tasks/` are exempt.
|
|
35
|
+
- Business context first — does the first paragraph answer *why* the document exists, before *what* and *how*? Rule 4.
|
|
36
|
+
- Scope duplication — does the document copy material that is canonically owned by another file? Rule 5 requires linking, not copying.
|
|
37
|
+
- Speculation — phrases like "we might", "in the future", "could be added", or roadmaps without an ADR / task reference. Rule 1 forbids unfounded plans.
|
|
38
|
+
|
|
39
|
+
Source code (sample, not exhaustive — flag findings, not every match):
|
|
40
|
+
- Orphan `TODO` / `FIXME` — Rule 7. A reference to a GitHub Issue or a `doc/tasks/NNNN-*.md` task file makes it not orphan.
|
|
41
|
+
- Commented-out code blocks — Rule 7. Removed code lives in git history.
|
|
42
|
+
|
|
32
43
|
Step 3 — output. One line per finding, formatted:
|
|
33
44
|
`[file or section]: spec says X, code says Y. Suggested resolution: change spec / change code / discuss.`
|
|
34
45
|
|
|
35
|
-
Group by artifact. If a category has no drift, print one line: `AGENTS.md — no drift.` etc. If an audited artifact does not exist, say so explicitly rather than reporting zero findings.
|
|
46
|
+
Group by artifact. If a category has no drift, print one line: `AGENTS.md — no drift.` etc. If an audited artifact does not exist, say so explicitly rather than reporting zero findings. The Documentation discipline drift category groups findings under `Documentation discipline — <category>: ...`.
|
|
36
47
|
|
|
37
48
|
If something the user says contradicts what the code shows, surface the conflict. Don't silently trust the user; don't silently trust the code.
|
|
38
49
|
</instructions>
|
|
@@ -135,4 +135,11 @@ Real traps. Each one should map to an incident or to specific code.
|
|
|
135
135
|
|
|
136
136
|
<output_contract>
|
|
137
137
|
A single `AGENTS.md` at the repo root, ≤150 lines, every line operational. No "External Resources" section. No appended Universal Agent Behavior block — that lives in the `agentic-philosophy` skill. No meta-prose explaining gaps. In audit mode: a drift list, no file written.
|
|
138
|
+
|
|
139
|
+
`AGENTS.md` is a narrative document, so the Documentation Discipline rules in `WORKFLOW.md` §2 apply at write time:
|
|
140
|
+
- No emoji anywhere in the file.
|
|
141
|
+
- No dates, version stamps, `DRAFT` markers, or changelog blocks. Stack versions are facts (`Node ≥18`); release timelines are not.
|
|
142
|
+
- `Project Overview` is the business-context-first paragraph — *why* the project exists before *what* it does.
|
|
143
|
+
- One scope: this file is the operational guide for agents. Do not duplicate `ARCHITECTURE.md` patterns or ADR rationale here; link instead.
|
|
144
|
+
- No speculation. If a section has no signal, write `<TODO: not yet wired>` once; do not narrate "this could be added later".
|
|
138
145
|
</output_contract>
|
|
@@ -45,4 +45,11 @@ If the source has tokens DESIGN.md doesn't document, list those as additions. If
|
|
|
45
45
|
|
|
46
46
|
<output_contract>
|
|
47
47
|
A single `DESIGN.md` at the repo root. YAML frontmatter uses W3C `$value`/`$type` shape. Markdown body has one section per token group present in the source. No invented tokens. No "External Resources" section. In audit mode: a drift list, no file written.
|
|
48
|
+
|
|
49
|
+
`DESIGN.md` is a narrative document, so the Documentation Discipline rules in `WORKFLOW.md` §2 apply at write time:
|
|
50
|
+
- No emoji anywhere — including do's and don'ts.
|
|
51
|
+
- No dates, version stamps, `DRAFT` markers, or changelog blocks. Token revisions live in git history; DESIGN.md is the current visual contract.
|
|
52
|
+
- The Markdown body opens with the *why* of each token group — the visual constraint or product principle — before listing rules.
|
|
53
|
+
- One scope: visual contract. Component anatomy and interaction patterns live elsewhere; link, do not copy.
|
|
54
|
+
- No speculation. If a group has no source token, mark `<TODO: not yet wired>` and move on.
|
|
48
55
|
</output_contract>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentic-philosophy
|
|
3
|
-
description: Universal agent behavior — think before coding, ground in real patterns, prefer simplicity, make surgical changes, define verifiable goals, verify before claiming done. Auto-invokes on non-trivial changes, refactors, debugging, "think before coding", "ground before coding", "verify done", "before implementing", or whenever the task is ambiguous enough that guardrails matter.
|
|
3
|
+
description: Universal agent behavior and documentation discipline — think before coding, ground in real patterns, prefer simplicity, make surgical changes, define verifiable goals, verify before claiming done, and write documentation that captures only definitions and decisions. Auto-invokes on non-trivial changes, refactors, debugging, "think before coding", "ground before coding", "verify done", "before implementing", on documentation work — "writing docs", "writing readme", "writing architecture", "writing adr", "writing task", "audit docs" — or whenever the task is ambiguous enough that guardrails matter.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<background_information>
|
|
7
|
-
Six behaviors apply to every non-trivial change. Bias toward caution over speed; for trivial diffs, use judgment.
|
|
7
|
+
Six behaviors apply to every non-trivial change. Bias toward caution over speed; for trivial diffs, use judgment. A separate Documentation Discipline block applies to every document the agent writes.
|
|
8
8
|
</background_information>
|
|
9
9
|
|
|
10
10
|
<instructions>
|
|
@@ -19,6 +19,19 @@ Six behaviors apply to every non-trivial change. Bias toward caution over speed;
|
|
|
19
19
|
**Goal-Driven Execution.** Define success criteria. Loop until verified. Transform vague tasks into verifiable goals ("Add validation" → "Write tests for invalid inputs, then make them pass"). Before modifying a file, list which tests cover it; run, modify, run; if none, write one first. For multi-step tasks, state a brief plan with a verify step per item.
|
|
20
20
|
|
|
21
21
|
**Verify Before Claiming Done.** Type-check and tests verify code, not feature. For UI/runtime changes, exercise in a browser. Can't verify? Say so — don't claim success. Never bypass gates (`--no-verify`, skipped hooks, deleted failing tests).
|
|
22
|
+
|
|
23
|
+
**Documentation Discipline.** Every document the agent writes obeys eight rules. Canonical source: `WORKFLOW.md` §2. Decision: ADR-0008.
|
|
24
|
+
|
|
25
|
+
1. **Definitions and decisions only.** What is true now, plus the decisions that brought it there. No speculation, no history, no unfounded plans. A deferred decision is in scope when *recorded* — an accepted ADR or a task file is fundamentação; "we might do X later" without a record is cut.
|
|
26
|
+
2. **No dates, version stamps, `DRAFT` markers, or changelogs in narrative documents.** Applies to `README.md`, `AGENTS.md` / `CLAUDE.md`, `ARCHITECTURE.md`, `DESIGN.md`, specs, prose pages. **Decision-record artifacts are exempt** — ADRs under `doc/adr/` keep `**Status:**` / `**Date:**`; tasks under `doc/tasks/` keep `**Created:**` and the append-only dated `Notes` log. Outside those, use git history.
|
|
27
|
+
3. **No emoji anywhere.** Not in docs, code, source comments, commit messages, PR bodies, or skill outputs. Severity and status use words; structural cues use Markdown.
|
|
28
|
+
4. **Business context first.** Open with *why* — the problem, the constraint, the user — before *what* and *how*. First paragraph answers "what would break if this document didn't exist".
|
|
29
|
+
5. **One scope per document. No duplication.** Link instead of copying. The canonical location owns the content; everywhere else references it.
|
|
30
|
+
6. **Code is the primary documentation of behavior.** Comments justify *why* a non-obvious choice was made — never restate *what*. If the comment explains *what*, rename or refactor.
|
|
31
|
+
7. **No commented-out code; no orphan `TODO` / `FIXME` in source.** Every deferred item references a tracked work item — a GitHub Issue, or a per-task file under `doc/tasks/NNNN-*.md`. The trace must be addressable from the source line.
|
|
32
|
+
8. **Tests are living documentation of behavior.** Test names and assertions read as the spec they enforce. Spec changes drive test changes; never the reverse.
|
|
33
|
+
|
|
34
|
+
Walk this list before declaring any documentation task done.
|
|
22
35
|
</instructions>
|
|
23
36
|
|
|
24
37
|
<output_contract>
|
|
@@ -25,7 +25,7 @@ Step 1 — assemble the handoff. The fresh session will get only what you print
|
|
|
25
25
|
- Relevant task file under `doc/tasks/` — if the diff or recent commit messages reference `Task NNNN`, read its Acceptance Criteria and Plan.
|
|
26
26
|
- Recent commit messages for the range (`git log <range> --format=%B`).
|
|
27
27
|
|
|
28
|
-
Step 2 —
|
|
28
|
+
Step 2 — write the handoff to disk. Save the assembled handoff at `.agentic/reviews/<ISO-timestamp>-<scope-slug>.md` at the repo root. Create the directory if missing. The slug encodes the scope (`branch-vs-main`, `pr-42`, `commit-abc1234`, `working-tree`). The file body is exactly the block below — no prose summary of what you think the diff does, the reviewer reads the diff itself.
|
|
29
29
|
|
|
30
30
|
```
|
|
31
31
|
=== AGENTIC-REVIEW HANDOFF ===
|
|
@@ -41,7 +41,7 @@ Review focus, in priority order:
|
|
|
41
41
|
|
|
42
42
|
Skip formatting, naming opinions, stylistic preferences. Skip praise.
|
|
43
43
|
|
|
44
|
-
Output: group findings by severity (Blocker / Concern / Note). Each finding one line: `file:line: <
|
|
44
|
+
Output: group findings by severity (Blocker / Concern / Note). Each finding one line: `file:line: <severity>: <problem>. <fix>.` Severity is the literal word `Blocker`, `Concern`, or `Note` — no emoji.
|
|
45
45
|
|
|
46
46
|
End with a one-line bottom-line: "Ship as-is", "Ship with the Concerns logged as follow-up tasks", or "Don't ship until Blockers resolved". Do NOT synthesize an "approve" verdict.
|
|
47
47
|
|
|
@@ -54,15 +54,24 @@ End with a one-line bottom-line: "Ship as-is", "Ship with the Concerns logged as
|
|
|
54
54
|
=== END HANDOFF ===
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Advise the user to add `.agentic/reviews/` to their `.gitignore` if it is not already — handoffs are ephemeral per-review artifacts, not committed history.
|
|
58
|
+
|
|
59
|
+
Step 3 — instruct the user. After writing the handoff, output exactly this and stop:
|
|
58
60
|
|
|
59
61
|
```
|
|
60
|
-
|
|
62
|
+
Handoff saved at <path>.
|
|
63
|
+
|
|
64
|
+
Load it into a fresh Codex session:
|
|
65
|
+
|
|
66
|
+
cat <path> | pbcopy # macOS
|
|
67
|
+
xclip -selection clipboard < <path> # Linux
|
|
68
|
+
|
|
69
|
+
Then in Codex: run `/clear`, paste from the clipboard, send. Codex will produce the structured findings.
|
|
61
70
|
```
|
|
62
71
|
|
|
63
72
|
Do not proceed past this point in the current session.
|
|
64
73
|
</instructions>
|
|
65
74
|
|
|
66
75
|
<output_contract>
|
|
67
|
-
|
|
76
|
+
A handoff file at `.agentic/reviews/<ISO-timestamp>-<scope-slug>.md`, plus a short instruction telling the user how to load it into a fresh Codex session via `/clear` and paste. The current session does not produce findings — the fresh session does, after the user re-loads the handoff. This honors WORKFLOW §10 by enforcing context isolation through `/clear` rather than via a subagent primitive Codex lacks; the persisted file replaces the chat-scroll copy step that previously made the round-trip fragile.
|
|
68
77
|
</output_contract>
|
|
@@ -79,4 +79,11 @@ All Acceptance Criteria checked, plus:
|
|
|
79
79
|
|
|
80
80
|
<output_contract>
|
|
81
81
|
A single new file at `doc/tasks/<NNNN>-<short-slug>.md`. Status proposed. Notes empty. No existing tasks modified. No invented values.
|
|
82
|
+
|
|
83
|
+
Task files are decision-record artifacts and are exempt from the no-dates rule (Documentation Discipline §2): `**Created:**` and the dated `Notes` log are required by design. Remaining Documentation Discipline rules (`WORKFLOW.md` §2) apply at write time:
|
|
84
|
+
- No emoji anywhere in the file.
|
|
85
|
+
- `Context` is the business-context-first section — *why this task exists* before *Acceptance Criteria*.
|
|
86
|
+
- One scope: one task per file.
|
|
87
|
+
- No speculation. Acceptance criteria must be measurable; do not list aspirational items.
|
|
88
|
+
- `Notes` is append-only and dated per entry — that is the auditability primitive, not a violation of Rule 2.
|
|
82
89
|
</output_contract>
|