@eric0117/agentforge 0.1.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/LICENSE +21 -0
- package/README.md +275 -0
- package/dist/add-agent.js +145 -0
- package/dist/add-skill.js +185 -0
- package/dist/agent-prompt.js +211 -0
- package/dist/agentforge-config.js +106 -0
- package/dist/agents/claude.js +46 -0
- package/dist/agents/codex.js +67 -0
- package/dist/agents/cursor.js +54 -0
- package/dist/agents/index.js +15 -0
- package/dist/agents/io.js +252 -0
- package/dist/agents/types.js +1 -0
- package/dist/cli.js +374 -0
- package/dist/confirm.js +20 -0
- package/dist/doctor.js +223 -0
- package/dist/enter.js +85 -0
- package/dist/init.js +272 -0
- package/dist/lang-prompt.js +88 -0
- package/dist/list-skills.js +120 -0
- package/dist/logo.js +181 -0
- package/dist/path-prompt.js +148 -0
- package/dist/remove-agent.js +63 -0
- package/dist/remove-skill.js +88 -0
- package/dist/rename.js +222 -0
- package/dist/skill-prompt.js +199 -0
- package/dist/skills-data.js +727 -0
- package/dist/sync-skills.js +59 -0
- package/dist/templates/CLAUDE.md.tpl +141 -0
- package/dist/templates/context-handoff.SKILL.md.tpl +222 -0
- package/dist/templates/cross-repo-impact.SKILL.md.tpl +241 -0
- package/dist/templates/feature-retro.SKILL.md.tpl +312 -0
- package/dist/templates/feature-start.SKILL.md.tpl +631 -0
- package/dist/templates/history.SKILL.md.tpl +165 -0
- package/dist/templates/incident-context.SKILL.md.tpl +260 -0
- package/dist/templates/pr-create.SKILL.md.tpl +403 -0
- package/dist/templates/pr-review-analyze.SKILL.md.tpl +303 -0
- package/dist/templates/pre-deploy-check.SKILL.md.tpl +350 -0
- package/dist/templates/project-router.SKILL.md.tpl +55 -0
- package/dist/templates/release-coordinate.SKILL.md.tpl +209 -0
- package/package.json +54 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { readMasterDir } from "./agents/io.js";
|
|
4
|
+
import { getAgent } from "./agents/index.js";
|
|
5
|
+
import { masterDir, requireWorkspace } from "./agentforge-config.js";
|
|
6
|
+
import { SKILLS } from "./skills-data.js";
|
|
7
|
+
const DIM = "\x1b[2m";
|
|
8
|
+
const GREEN = "\x1b[32m";
|
|
9
|
+
const YELLOW = "\x1b[33m";
|
|
10
|
+
const CYAN = "\x1b[36m";
|
|
11
|
+
const BOLD = "\x1b[1m";
|
|
12
|
+
const RED = "\x1b[31m";
|
|
13
|
+
const RESET = "\x1b[0m";
|
|
14
|
+
export async function runSyncSkills(opts) {
|
|
15
|
+
const root = resolve(opts.pathArg ?? process.cwd());
|
|
16
|
+
const cfg = requireWorkspace(root);
|
|
17
|
+
if (!existsSync(masterDir(root))) {
|
|
18
|
+
process.stderr.write(`\n${YELLOW}⚠${RESET} No master skills directory at ${DIM}${masterDir(root)}${RESET}\n\n` +
|
|
19
|
+
` Run ${CYAN}agentforge init${RESET} here first to set up the workspace.\n\n`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const { skills: masterSkills, skipped, warnings } = readMasterDir(masterDir(root));
|
|
23
|
+
if (skipped.length > 0) {
|
|
24
|
+
console.log(`${YELLOW}skipped invalid master files:${RESET}`);
|
|
25
|
+
for (const sk of skipped) {
|
|
26
|
+
console.log(` ${DIM}- ${sk.file}: ${sk.reason}${RESET}`);
|
|
27
|
+
}
|
|
28
|
+
console.log("");
|
|
29
|
+
}
|
|
30
|
+
if (warnings.length > 0) {
|
|
31
|
+
console.log(`${YELLOW}master file warnings:${RESET}`);
|
|
32
|
+
for (const w of warnings) {
|
|
33
|
+
console.log(` ${DIM}- ${w.file}: ${w.warning}${RESET}`);
|
|
34
|
+
}
|
|
35
|
+
console.log("");
|
|
36
|
+
}
|
|
37
|
+
if (masterSkills.length === 0) {
|
|
38
|
+
console.log(`${YELLOW}no valid master skills found — nothing to sync.${RESET}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
console.log(`${BOLD}${GREEN}↻${RESET} syncing ${masterSkills.length} skill(s) → ${cfg.agents.length} agent(s) in ${CYAN}${root}${RESET}`);
|
|
42
|
+
console.log("");
|
|
43
|
+
// sync-skills is propagate-by-design: always back up and overwrite. Pass
|
|
44
|
+
// forceSkills=true unconditionally; forceClaude only when explicit.
|
|
45
|
+
for (const id of cfg.agents) {
|
|
46
|
+
const adapter = getAgent(id);
|
|
47
|
+
console.log(`${BOLD}${CYAN}▸ ${adapter.label}${RESET}`);
|
|
48
|
+
adapter.install({
|
|
49
|
+
root,
|
|
50
|
+
masterSkills,
|
|
51
|
+
skillCatalog: SKILLS.slice(),
|
|
52
|
+
lang: cfg.lang,
|
|
53
|
+
forceSkills: true,
|
|
54
|
+
forceClaude: opts.forceClaude,
|
|
55
|
+
});
|
|
56
|
+
console.log("");
|
|
57
|
+
}
|
|
58
|
+
console.log(`${BOLD}${GREEN}✓${RESET} sync complete ${DIM}(skills: ${masterSkills.map((s) => s.id).join(", ")})${RESET}`);
|
|
59
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Multi-Repo Workspace
|
|
2
|
+
|
|
3
|
+
This directory is an [agentforge](https://github.com/) workspace — a bootstrapped layout
|
|
4
|
+
for working across multiple repositories with Claude Code, designed for the case where
|
|
5
|
+
one feature spans several repos and several features need to be developed in parallel.
|
|
6
|
+
|
|
7
|
+
## Directory layout
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
.
|
|
11
|
+
├── repos/ # main branch of each repo (read / explore only)
|
|
12
|
+
├── anvil/ # per-feature worktrees — ONLY in-progress work
|
|
13
|
+
│ └── <slug>/
|
|
14
|
+
│ ├── <repo>/ # a worktree of <repo> for this feature
|
|
15
|
+
│ └── CLAUDE.md # feature description + repos in scope
|
|
16
|
+
├── artifacts/ # closed features, grouped by completion date
|
|
17
|
+
│ └── <YYYYMMDD>/
|
|
18
|
+
│ └── <slug>/
|
|
19
|
+
│ ├── CLAUDE.md # original feature metadata (moved here on retro)
|
|
20
|
+
│ ├── RETRO.md # retrospective
|
|
21
|
+
│ ├── sessions/ # Claude Code transcripts
|
|
22
|
+
│ ├── plans/ # plan files
|
|
23
|
+
│ └── refs.json # per-repo branch + HEAD + PR pointers
|
|
24
|
+
├── agentforge/ # workspace metadata + master skills (the single source of truth)
|
|
25
|
+
│ ├── config.json
|
|
26
|
+
│ ├── skills/
|
|
27
|
+
│ └── log.jsonl # append-only activity log
|
|
28
|
+
└── .claude/skills/ .cursor/rules/ .agents/skills/ # per-agent generated files
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- **`repos/<name>/`** — you `git clone` your repos here yourself. Used for
|
|
32
|
+
read / explore only. Don't edit code here.
|
|
33
|
+
- **`anvil/<slug>/<repo>/`** — feature work happens in worktrees. The
|
|
34
|
+
`agentforge-feature-start` skill creates these.
|
|
35
|
+
- **`artifacts/<YYYYMMDD>/<slug>/`** — once `feature-retro` closes a feature, the
|
|
36
|
+
worktree is removed and the retrospective + supporting artifacts land here.
|
|
37
|
+
`ls artifacts/` shows every completed feature, newest dates first.
|
|
38
|
+
- **`agentforge/skills/<id>.md`** — master copy of every skill. Edit a file here
|
|
39
|
+
and run `agentforge sync-skills` to push the change to every installed agent.
|
|
40
|
+
|
|
41
|
+
## Installed skills
|
|
42
|
+
|
|
43
|
+
All skills below are workspace-local (under `.claude/skills/`) and are auto-loaded by
|
|
44
|
+
Claude Code when you run `claude` from this directory or any subdirectory. Trigger them
|
|
45
|
+
by describing what you want in natural language — you don't have to remember the names.
|
|
46
|
+
|
|
47
|
+
### Day-to-day
|
|
48
|
+
|
|
49
|
+
#### Ask a question / explore code
|
|
50
|
+
From the workspace root, just describe what you want.
|
|
51
|
+
**`agentforge-project-router`** picks the right `repos/<name>/` to look in, or asks you
|
|
52
|
+
when ambiguous.
|
|
53
|
+
|
|
54
|
+
> "Where is the auth handler in the backend API?"
|
|
55
|
+
> "What does the admin's user list page do?"
|
|
56
|
+
|
|
57
|
+
### Feature lifecycle
|
|
58
|
+
|
|
59
|
+
#### 1. Start a new feature
|
|
60
|
+
> "Let's start a new feature: search ranking."
|
|
61
|
+
|
|
62
|
+
**`agentforge-feature-start`** proposes a kebab-case slug, asks which repos the feature
|
|
63
|
+
touches, and creates `git worktree`s under `anvil/<slug>/<repo>/`.
|
|
64
|
+
|
|
65
|
+
#### 2. Work on the feature
|
|
66
|
+
```bash
|
|
67
|
+
cd anvil/<slug>/
|
|
68
|
+
claude
|
|
69
|
+
```
|
|
70
|
+
A single session at that path sees all worktrees of the feature at once.
|
|
71
|
+
|
|
72
|
+
#### 3. Check what a change would break (before touching shared code)
|
|
73
|
+
> "Where else is `doSomething` used?" · "Blast radius of removing `/v1/things`?"
|
|
74
|
+
|
|
75
|
+
**`agentforge-cross-repo-impact`** searches every other `repos/*` for call sites,
|
|
76
|
+
imports, HTTP callers, type usages, etc. and flags breaking changes.
|
|
77
|
+
|
|
78
|
+
#### 4. Pre-deploy sanity check
|
|
79
|
+
> "Anything ops needs before I ship this?" · "Pre-deploy check."
|
|
80
|
+
|
|
81
|
+
**`agentforge-pre-deploy-check`** scans the worktree diff for non-code changes —
|
|
82
|
+
DB migrations, env vars, cache keys, message-queue schemas, dependency locks, infra
|
|
83
|
+
config, feature flags, API surface, cron jobs — and outputs a checklist + deploy
|
|
84
|
+
order.
|
|
85
|
+
|
|
86
|
+
#### 5. Open PRs for the feature
|
|
87
|
+
> "Open PRs for this feature." · "Make the PRs."
|
|
88
|
+
|
|
89
|
+
**`agentforge-pr-create`** detects which worktrees have commits ahead, lets you
|
|
90
|
+
multi-select, then opens one PR per repo via `gh`. Drafts titles + bodies from the
|
|
91
|
+
feature's `CLAUDE.md` and the per-repo diff stat, and cross-links the PRs to each
|
|
92
|
+
other.
|
|
93
|
+
|
|
94
|
+
#### 6. Audit review comments
|
|
95
|
+
> "Audit the PR comments." · "What do we need to fix from the review?"
|
|
96
|
+
|
|
97
|
+
**`agentforge-pr-review-analyze`** pulls every review thread, verifies each against the
|
|
98
|
+
live code, classifies impact (Critical → Discussion), traces the call path, and notes
|
|
99
|
+
test coverage. Returns a prioritized action list.
|
|
100
|
+
|
|
101
|
+
#### 7. Wrap up a finished feature
|
|
102
|
+
> "We're done with this feature." · "Write a retro and clean up."
|
|
103
|
+
|
|
104
|
+
**`agentforge-feature-retro`**:
|
|
105
|
+
1. Creates `artifacts/<YYYYMMDD>/<slug>/` (close-date directory).
|
|
106
|
+
2. Copies Claude Code session logs (`~/.claude/projects/.../*.jsonl`) into
|
|
107
|
+
`artifacts/<YYYYMMDD>/<slug>/sessions/`.
|
|
108
|
+
3. Copies relevant plan files (`~/.claude/plans/*.md`) into the same archive's
|
|
109
|
+
`plans/`.
|
|
110
|
+
4. Captures per-repo branch + HEAD + PR pointers into `refs.json`.
|
|
111
|
+
5. Writes `RETRO.md` — what was asked, decided, built, learned.
|
|
112
|
+
6. Moves `anvil/<slug>/CLAUDE.md` into the archive directory.
|
|
113
|
+
7. Removes each `anvil/<slug>/<repo>/` worktree (only if no dirty / unpushed
|
|
114
|
+
changes), then deletes the now-empty `anvil/<slug>/` directory so `anvil/`
|
|
115
|
+
only contains in-progress features.
|
|
116
|
+
|
|
117
|
+
### Looking back
|
|
118
|
+
|
|
119
|
+
#### Recall past features
|
|
120
|
+
> "How did we handle the retry logic last time?" · "Which feature touched
|
|
121
|
+
> mobile-app for queue config?" · "Find that PR about search rankings."
|
|
122
|
+
|
|
123
|
+
**`agentforge-history`** searches every `artifacts/<YYYYMMDD>/<slug>/` (RETROs, plan
|
|
124
|
+
files, `refs.json` with branch/HEAD/PR pointers) and answers with grounded
|
|
125
|
+
references — file paths, commit hashes, PR URLs — so you can verify and dig
|
|
126
|
+
deeper. Filters by keyword, repo, date window, or PR/commit. Read-only.
|
|
127
|
+
|
|
128
|
+
### Operations
|
|
129
|
+
|
|
130
|
+
#### Incident context (you just got paged)
|
|
131
|
+
> "Got an alert about `NullPointerException at Foo.process` — pull context."
|
|
132
|
+
|
|
133
|
+
**`agentforge-incident-context`** searches every repo for matching code, identifies
|
|
134
|
+
recent merged PRs that touched it, names the last committers, traces the call path
|
|
135
|
+
(highlighting async entry points), and proposes next steps. Optimized for a 30-second
|
|
136
|
+
read.
|
|
137
|
+
|
|
138
|
+
## Current repos
|
|
139
|
+
|
|
140
|
+
Run `ls repos/` to see them. agentforge does not maintain a separate metadata file —
|
|
141
|
+
the filesystem is the source of truth.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentforge-context-handoff
|
|
3
|
+
description: Packages a feature's current state into a single handoff document so another developer (or future-self) can pick up without context loss. Gathers per-worktree git state, open PRs and their review state, unaddressed comments, pending plan items, recent decisions, recently touched files, open TODOs and questions. Writes anvil/<slug>/HANDOFF.md. Optionally posts the same summary as a comment on each related PR — only with explicit user confirmation. Never modifies CLAUDE.md, never pushes commits. Triggers on "hand this off", "휴가 가니 정리해줘", "leave notes for next session", "다음 사람한테 넘길 패키지", "package this up".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# context-handoff
|
|
7
|
+
|
|
8
|
+
Packages the current state of a feature into a single document — `HANDOFF.md` —
|
|
9
|
+
so another developer (or future-you, three weeks from now) can resume without
|
|
10
|
+
having to reconstruct context from git, PR threads, and memory.
|
|
11
|
+
|
|
12
|
+
Read-mostly: writes one new file in the feature directory and, only with
|
|
13
|
+
explicit confirmation, posts the same summary as a comment on each related PR.
|
|
14
|
+
Never touches existing CLAUDE.md, never pushes commits, never modifies code.
|
|
15
|
+
|
|
16
|
+
## When to apply
|
|
17
|
+
|
|
18
|
+
Trigger phrases:
|
|
19
|
+
- "Hand this off." / "Package this up."
|
|
20
|
+
- "Leave notes for next session."
|
|
21
|
+
- "휴가 가니 정리해줘." / "다음 사람한테 넘길 패키지."
|
|
22
|
+
- "I'm switching pairs — write a handoff."
|
|
23
|
+
|
|
24
|
+
Also a good fit before a long pause (vacation, parental leave, sprint
|
|
25
|
+
boundary), or before genuinely handing the feature to a different developer.
|
|
26
|
+
|
|
27
|
+
## Resolve scope
|
|
28
|
+
|
|
29
|
+
The handoff is **always per-feature**. Resolve the slug from cwd:
|
|
30
|
+
|
|
31
|
+
- `…/anvil/<slug>/` (or anywhere inside it) → that feature.
|
|
32
|
+
- Workspace root, no obvious context → ask the user for the slug, or list
|
|
33
|
+
active features (`ls anvil/`).
|
|
34
|
+
|
|
35
|
+
If the named slug has no `anvil/<slug>/` directory, stop and tell the user —
|
|
36
|
+
either the feature was already wrapped up (check `artifacts/`) or the slug is
|
|
37
|
+
wrong.
|
|
38
|
+
|
|
39
|
+
## Step 1 — Gather feature metadata
|
|
40
|
+
|
|
41
|
+
Read `anvil/<slug>/CLAUDE.md`:
|
|
42
|
+
- Feature description (the heading + first paragraph)
|
|
43
|
+
- `Started:` / `Expanded:` dates
|
|
44
|
+
- `Repos in scope:` list with per-repo branch names
|
|
45
|
+
|
|
46
|
+
If `anvil/<slug>/PLAN.md` exists (from `feature-plan` or hand-written), read
|
|
47
|
+
its pending items.
|
|
48
|
+
|
|
49
|
+
## Step 2 — Per-worktree state
|
|
50
|
+
|
|
51
|
+
For each repo in scope (`ls -d anvil/<slug>/*/`):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
branch=$(git -C anvil/<slug>/<repo> rev-parse --abbrev-ref HEAD)
|
|
55
|
+
base=$(git -C anvil/<slug>/<repo> symbolic-ref --quiet refs/remotes/origin/HEAD \
|
|
56
|
+
| sed 's@^refs/remotes/origin/@@')
|
|
57
|
+
|
|
58
|
+
# Recent commits on this branch (since base)
|
|
59
|
+
git -C anvil/<slug>/<repo> log --oneline "origin/$base..HEAD" | head -20
|
|
60
|
+
|
|
61
|
+
# Working-tree state — uncommitted work is the most important thing to flag
|
|
62
|
+
git -C anvil/<slug>/<repo> status --porcelain
|
|
63
|
+
|
|
64
|
+
# Unpushed commits
|
|
65
|
+
git -C anvil/<slug>/<repo> log @{u}..HEAD --oneline 2>/dev/null
|
|
66
|
+
|
|
67
|
+
# Files recently touched (signal for "where I left off")
|
|
68
|
+
git -C anvil/<slug>/<repo> diff --name-only "origin/$base"...HEAD
|
|
69
|
+
git -C anvil/<slug>/<repo> diff --name-only # unstaged
|
|
70
|
+
git -C anvil/<slug>/<repo> diff --cached --name-only # staged
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Capture per repo:
|
|
74
|
+
- branch + base
|
|
75
|
+
- # of commits ahead of base
|
|
76
|
+
- dirty? (uncommitted changes — itemize files)
|
|
77
|
+
- unpushed? (local commits not on remote)
|
|
78
|
+
- last commit message + date (a one-line "where I was")
|
|
79
|
+
|
|
80
|
+
## Step 3 — Open PRs and their state
|
|
81
|
+
|
|
82
|
+
For each repo's branch, look up the PR:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
gh -R <owner>/<repo> pr list --head "$branch" --state open \
|
|
86
|
+
--json number,url,isDraft,mergeStateStatus,statusCheckRollup,reviews,reviewRequests
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
If a PR exists, also pull:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
gh -R <owner>/<repo> pr view <num> --json title,body,comments,reviewDecision
|
|
93
|
+
gh api repos/<owner>/<repo>/pulls/<num>/comments # inline review threads
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
For each PR, capture:
|
|
97
|
+
- number + URL + draft/ready
|
|
98
|
+
- CI status (failing / pending / green)
|
|
99
|
+
- review state (approved / changes-requested / pending)
|
|
100
|
+
- **unaddressed comments**: review threads where the last reply is from a
|
|
101
|
+
reviewer and not the author, and the thread is not marked resolved. These are
|
|
102
|
+
the most important things for the next person to handle.
|
|
103
|
+
|
|
104
|
+
Note repos with no PR yet — that's a follow-up item.
|
|
105
|
+
|
|
106
|
+
## Step 4 — Decisions, TODOs, open questions
|
|
107
|
+
|
|
108
|
+
Extract from sources in this order:
|
|
109
|
+
|
|
110
|
+
1. `anvil/<slug>/CLAUDE.md` — any free-text decisions the user wrote.
|
|
111
|
+
2. `anvil/<slug>/PLAN.md` — pending plan items (unchecked boxes).
|
|
112
|
+
3. Recent commits with subjects like `chore:`, `fixup!`, `WIP:` — flag these
|
|
113
|
+
as "needs cleanup before merge."
|
|
114
|
+
4. `git -C anvil/<slug>/<repo> diff "origin/$base"...HEAD | grep -E '^\+.*(TODO|FIXME|XXX)'`
|
|
115
|
+
— TODOs the user introduced in this feature.
|
|
116
|
+
5. The most recent Claude Code session transcript for this feature (under
|
|
117
|
+
`~/.claude/projects/.../*.jsonl` matching this workspace), if accessible —
|
|
118
|
+
skim the last summary block for "next steps" / "blocked on" phrases. Don't
|
|
119
|
+
re-read the entire session.
|
|
120
|
+
|
|
121
|
+
If extraction yields nothing concrete, leave the section as
|
|
122
|
+
"(no explicit open questions captured — ask the original author)."
|
|
123
|
+
|
|
124
|
+
## Step 5 — Write HANDOFF.md
|
|
125
|
+
|
|
126
|
+
Write to `anvil/<slug>/HANDOFF.md` (overwrite if it exists; back up the old one
|
|
127
|
+
to `HANDOFF.md.bak` first). Structure:
|
|
128
|
+
|
|
129
|
+
```markdown
|
|
130
|
+
# Handoff: <slug>
|
|
131
|
+
|
|
132
|
+
> Generated <YYYY-MM-DD HH:MM> by agentforge-context-handoff.
|
|
133
|
+
> This file is informational. Source of truth is git + the open PRs.
|
|
134
|
+
|
|
135
|
+
## Feature
|
|
136
|
+
|
|
137
|
+
<one-paragraph description from CLAUDE.md>
|
|
138
|
+
|
|
139
|
+
Started <date>. <N> repo(s) in scope.
|
|
140
|
+
|
|
141
|
+
## State at handoff time
|
|
142
|
+
|
|
143
|
+
| Repo | Branch | PR | CI | Reviews | Dirty? | Unpushed? |
|
|
144
|
+
|---|---|---|---|---|---|---|
|
|
145
|
+
| <repo-1> | <branch-1> | #<N> | <icon> | <state> | <yes/no> | <yes/no> |
|
|
146
|
+
| ... |
|
|
147
|
+
|
|
148
|
+
## What's done
|
|
149
|
+
|
|
150
|
+
- <repo-1>: <N> commits ahead — <high-level summary of what the diff does>
|
|
151
|
+
- <repo-2>: ...
|
|
152
|
+
|
|
153
|
+
## What's pending
|
|
154
|
+
|
|
155
|
+
### Unaddressed review comments
|
|
156
|
+
- <repo-1> #<N>: <comment thread summary + file:line + reviewer> → <link>
|
|
157
|
+
- ...
|
|
158
|
+
|
|
159
|
+
### Open questions
|
|
160
|
+
- <extracted question 1>
|
|
161
|
+
- ...
|
|
162
|
+
|
|
163
|
+
### TODO markers introduced in this feature
|
|
164
|
+
- <file:line>: <TODO text> (<repo>)
|
|
165
|
+
- ...
|
|
166
|
+
|
|
167
|
+
### Plan items not yet done
|
|
168
|
+
- [ ] <item from PLAN.md>
|
|
169
|
+
- ...
|
|
170
|
+
|
|
171
|
+
## Where to look next
|
|
172
|
+
|
|
173
|
+
- <repo-1>/<file>:<line> — most recently edited; <last commit message>
|
|
174
|
+
- ...
|
|
175
|
+
|
|
176
|
+
## How to resume
|
|
177
|
+
|
|
178
|
+
1. `cd anvil/<slug>/` and start a session (`claude` or your CLI).
|
|
179
|
+
2. Read this file first.
|
|
180
|
+
3. Read each open PR's unaddressed comments.
|
|
181
|
+
4. Run `agentforge-feature-resume` (if available) for an AI briefing.
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
After writing, print the path and a one-line summary to the user.
|
|
185
|
+
|
|
186
|
+
## Step 6 — (Optional) PR comments
|
|
187
|
+
|
|
188
|
+
Ask the user explicitly: "Post a handoff summary as a comment on each open PR?
|
|
189
|
+
(default: no)"
|
|
190
|
+
|
|
191
|
+
Only on "yes":
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Summarize for PR audience (different from the full HANDOFF.md — shorter,
|
|
195
|
+
# focused on what reviewers need to know to keep the PR moving)
|
|
196
|
+
gh -R <owner>/<repo> pr comment <num> --body "<summary>"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The PR comment should include:
|
|
200
|
+
- "Handoff posted on <date>" + link to `anvil/<slug>/HANDOFF.md` path (relative)
|
|
201
|
+
- A short list of "what's pending on this PR specifically"
|
|
202
|
+
- Who the next person to contact is, if known
|
|
203
|
+
|
|
204
|
+
**Never post comments without explicit user confirmation** — comments are
|
|
205
|
+
visible to teammates and can confuse them if posted prematurely.
|
|
206
|
+
|
|
207
|
+
## Rules
|
|
208
|
+
|
|
209
|
+
- **Read-only on existing files** — never edit `CLAUDE.md`, `PLAN.md`, code, or
|
|
210
|
+
PR descriptions. Only write the new `HANDOFF.md`.
|
|
211
|
+
- **No git push, no PR merge, no PR review.**
|
|
212
|
+
- **PR comments only with explicit confirmation.**
|
|
213
|
+
- **Back up before overwrite** — if `HANDOFF.md` exists, move it to
|
|
214
|
+
`HANDOFF.md.bak` first (don't silently overwrite).
|
|
215
|
+
- **Branch names from worktrees, not slug** — per-repo branches may differ.
|
|
216
|
+
- **Dirty worktrees are the #1 thing to flag** — uncommitted work is invisible
|
|
217
|
+
to anyone but the original author; the handoff exists primarily to surface
|
|
218
|
+
this.
|
|
219
|
+
|
|
220
|
+
## Output language
|
|
221
|
+
|
|
222
|
+
{{OUTPUT_LANGUAGE_INSTRUCTION}}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentforge-cross-repo-impact
|
|
3
|
+
description: Traces the blast radius of a change across every repo in the workspace. Given a symbol (function, class, type, API endpoint, env var, config key) or the current worktree's diff, finds all the places in other repos that depend on it, classifies each usage (call site / import / HTTP caller / string reference), and flags breaking-change risks. Triggers on "what's the impact of this change?", "where else is X used?", "if I rename this, what breaks?", "blast radius of …", "any callers in other repos?".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# cross-repo-impact
|
|
7
|
+
|
|
8
|
+
This is the multi-repo workspace's blast-radius tool. When the user changes something in
|
|
9
|
+
one repo, this skill finds every other repo in the workspace that touches it and
|
|
10
|
+
reports the impact, grouped by repo and by usage kind.
|
|
11
|
+
|
|
12
|
+
**Read-only.** This skill never modifies code.
|
|
13
|
+
|
|
14
|
+
## When to apply
|
|
15
|
+
|
|
16
|
+
Trigger on questions like:
|
|
17
|
+
- "What's the impact of this change?"
|
|
18
|
+
- "Where else is `<symbol>` used?"
|
|
19
|
+
- "If I rename / change the signature of `<symbol>`, what breaks?"
|
|
20
|
+
- "Blast radius of removing endpoint `/v1/things`?"
|
|
21
|
+
- "Anything in admin-web that calls this?"
|
|
22
|
+
|
|
23
|
+
If the user is mid-implementation and asks "before I touch this, who uses it?" — that's
|
|
24
|
+
this skill.
|
|
25
|
+
|
|
26
|
+
## Locate the workspace
|
|
27
|
+
|
|
28
|
+
This skill needs the workspace layout (`repos/` and possibly `anvil/<slug>/`). Walk up
|
|
29
|
+
from cwd until you find a directory containing `repos/`. If no such directory is found,
|
|
30
|
+
tell the user: "Not inside an agentforge workspace — falling back to current repo only,"
|
|
31
|
+
and proceed with cwd as the single search target.
|
|
32
|
+
|
|
33
|
+
The source repo (where the change lives) is determined from cwd:
|
|
34
|
+
- `…/anvil/<slug>/<repo>/` → source = that `<repo>`; search the **other** `repos/*`
|
|
35
|
+
(and other `anvil/<slug>/*` worktrees) for usages.
|
|
36
|
+
- `…/repos/<name>/` → source = `<name>`; search the other `repos/*`.
|
|
37
|
+
- Elsewhere → ask the user which repo is the source.
|
|
38
|
+
|
|
39
|
+
The search set is every `repos/<other>/` plus every `anvil/<other-slug>/<other-repo>/`
|
|
40
|
+
the user wants included (default: just `repos/*` to avoid duplicate hits from sibling
|
|
41
|
+
worktrees of the same repo).
|
|
42
|
+
|
|
43
|
+
## Determine what to analyze
|
|
44
|
+
|
|
45
|
+
Two modes:
|
|
46
|
+
|
|
47
|
+
### Mode A — user names a target
|
|
48
|
+
|
|
49
|
+
The user gives one or more of:
|
|
50
|
+
- A symbol name (`doSomething`, `User`, `SomeType`)
|
|
51
|
+
- An HTTP endpoint (`POST /v1/things`, `/api/users/:id`)
|
|
52
|
+
- A config / env key (`KAFKA_BROKER_URL`, `feature.search-ranking.enabled`)
|
|
53
|
+
- A message / event name (`ItemCreated`, `something.changed`)
|
|
54
|
+
|
|
55
|
+
Use this verbatim.
|
|
56
|
+
|
|
57
|
+
### Mode B — auto from worktree diff
|
|
58
|
+
|
|
59
|
+
If the user says "what does this change touch?" without naming a target, derive
|
|
60
|
+
candidates from the current diff:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git -C <source-repo> diff --unified=0 <main-branch>...HEAD
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Extract candidate targets:
|
|
67
|
+
- **Function / method renames or signature changes** — look for hunks where a `def`,
|
|
68
|
+
`function`, `fun`, `func` line was modified.
|
|
69
|
+
- **Newly exported / removed symbols** — look for changes in `export`, `pub`, public
|
|
70
|
+
visibility.
|
|
71
|
+
- **HTTP route changes** — look for hunks containing route decorators / annotations
|
|
72
|
+
(`@RequestMapping`, `@Get(`, `app.get(`, `@router.get`, Gin/Fiber/Express patterns).
|
|
73
|
+
- **Removed env / config keys** — look for `process.env.X`, `System.getenv("X")`,
|
|
74
|
+
`os.getenv("X")`, `viper.GetString("X")` patterns that disappeared.
|
|
75
|
+
- **Type / schema changes** — modified `interface`, `type`, `class`, `data class`,
|
|
76
|
+
`struct`, proto fields.
|
|
77
|
+
|
|
78
|
+
Show the user the candidate list and ask them to pick which ones to analyze. Don't
|
|
79
|
+
silently analyze everything — the diff may contain noise.
|
|
80
|
+
|
|
81
|
+
## Detect languages per target repo
|
|
82
|
+
|
|
83
|
+
For each repo in the search set, sample a few source files to infer language(s):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
find <repo> -maxdepth 3 -type f \( -name '*.ts' -o -name '*.tsx' -o -name '*.kt' \
|
|
87
|
+
-o -name '*.java' -o -name '*.py' -o -name '*.go' -o -name '*.rs' \
|
|
88
|
+
-o -name '*.rb' -o -name '*.php' \) 2>/dev/null | head -10
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Per-language search idioms (use whichever applies):
|
|
92
|
+
|
|
93
|
+
| Language | Call / use patterns |
|
|
94
|
+
|---|---|
|
|
95
|
+
| TypeScript / JavaScript | `\bX\(` , `import {[^}]*X[^}]*}` , `from ['"][^'"]*['"]` |
|
|
96
|
+
| Kotlin / Java | `\.X\(` , `import .*\.X` |
|
|
97
|
+
| Python | `\bX\(` , `from .* import .*X` , `import .*X` |
|
|
98
|
+
| Go | `\.X\(` , `X\(` (after dot-less import) |
|
|
99
|
+
| Rust | `::X\(` , `\.X\(` , `use .*::X` |
|
|
100
|
+
| Ruby | `\.X\b` , `X\(` |
|
|
101
|
+
| PHP | `->X\(` , `::X\(` , `use .*\\X` |
|
|
102
|
+
|
|
103
|
+
For HTTP endpoints, search for the literal path in any source/config file (route
|
|
104
|
+
strings are usually written as-is in client code), not just the framework idiom. For
|
|
105
|
+
config / env keys, grep the literal key name.
|
|
106
|
+
|
|
107
|
+
## Standard exclusions (apply to every grep / search in this skill)
|
|
108
|
+
|
|
109
|
+
Always exclude these from any code search to avoid noise + speed up big repos:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
:!node_modules :!dist :!build :!target :!.next :!.nuxt :!.venv :!__pycache__
|
|
113
|
+
:!*.lock :!package-lock.json :!yarn.lock :!pnpm-lock.yaml :!Cargo.lock
|
|
114
|
+
:!*.min.js :!*.bundle.js :!coverage :!.git :!vendor
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
With `git grep`, pass them as pathspec exclusions:
|
|
118
|
+
```bash
|
|
119
|
+
git -C <repo> grep -nE '<pattern>' \
|
|
120
|
+
-- ':!node_modules' ':!dist' ':!build' ':!target' ':!.next' \
|
|
121
|
+
':!*.lock' ':!coverage' ':!vendor'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
With plain `grep -r`, use `--exclude-dir=` / `--exclude=` repeatedly. If the user has
|
|
125
|
+
a `.gitignore`, prefer `git grep` (which already honors it).
|
|
126
|
+
|
|
127
|
+
## Search & classify
|
|
128
|
+
|
|
129
|
+
For each (target, target-repo) pair, run the searches and classify each hit into one
|
|
130
|
+
of these kinds:
|
|
131
|
+
|
|
132
|
+
| Kind | Signal |
|
|
133
|
+
|---|---|
|
|
134
|
+
| 📞 **Call site** | symbol invoked: `X(...)` |
|
|
135
|
+
| 📥 **Import** | imported but not yet shown to be called in the surrounding hunk |
|
|
136
|
+
| 🌐 **HTTP caller** | code that opens the URL (axios/fetch/http client) targeting the endpoint string |
|
|
137
|
+
| 🔤 **String reference** | literal name appears in a log message, config, schema |
|
|
138
|
+
| 🧩 **Type usage** | the changed type appears in a signature, declaration, or generic parameter |
|
|
139
|
+
| 📜 **Schema / proto** | reference in a `.proto`, OpenAPI, JSON schema, SQL migration |
|
|
140
|
+
| 🧪 **Test reference** | hit is inside a test file (`*test*` , `__tests__`, etc.) |
|
|
141
|
+
|
|
142
|
+
Test hits are reported separately because they signal regression coverage rather than
|
|
143
|
+
production risk.
|
|
144
|
+
|
|
145
|
+
## Breaking-change assessment
|
|
146
|
+
|
|
147
|
+
For each target, decide what kind of change it is and what that means for callers:
|
|
148
|
+
|
|
149
|
+
| Change kind | Effect on call sites |
|
|
150
|
+
|---|---|
|
|
151
|
+
| **Renamed** | All non-test call sites break — they need updating. |
|
|
152
|
+
| **Removed** | Hard break for all kinds. Highest urgency. |
|
|
153
|
+
| **Signature changed** (params added / removed / reordered / typed) | Call sites with the old shape break. Required vs optional parameters matter — note the difference. |
|
|
154
|
+
| **Return type changed** | Callers consuming the return value need updating. |
|
|
155
|
+
| **Behavior changed (same signature)** | Compiler / type checker won't catch it. Manual review per call site. |
|
|
156
|
+
| **New symbol** | No callers can break yet, but document it for discoverability. |
|
|
157
|
+
| **Endpoint URL changed** | HTTP callers using the old path break. Headers / methods matter too. |
|
|
158
|
+
| **Env / config key renamed** | Deployments need re-configuring before code roll-out. Mention deployment ordering. |
|
|
159
|
+
|
|
160
|
+
Note the trickiest case: **behavior changed, signature unchanged**. Static search can
|
|
161
|
+
list call sites, but the caller code still compiles. Flag this explicitly so the user
|
|
162
|
+
knows manual review is needed.
|
|
163
|
+
|
|
164
|
+
## Output format
|
|
165
|
+
|
|
166
|
+
```markdown
|
|
167
|
+
# Cross-repo impact: <target description>
|
|
168
|
+
|
|
169
|
+
> Source: `<source-repo>` (from `<cwd>`)
|
|
170
|
+
> Change kind: <renamed / removed / signature / behavior / new / endpoint / config>
|
|
171
|
+
> Search set: 3 repos (`admin-web`, `worker-service`, `mobile-app`)
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 💥 Breaking call sites
|
|
176
|
+
|
|
177
|
+
### `admin-web` — 4 hits
|
|
178
|
+
|
|
179
|
+
| File | Line | Kind | Snippet |
|
|
180
|
+
|---|---|---|---|
|
|
181
|
+
| `src/pages/things.tsx` | 42 | 📞 Call site | `doSomething(id)` |
|
|
182
|
+
| `src/api/things.ts` | 11 | 📥 Import | `import { doSomething } from '@/api'` |
|
|
183
|
+
| `src/components/SomeButton.tsx` | 88 | 📞 Call site | `await doSomething(...)` |
|
|
184
|
+
| `src/__tests__.test.ts` | 19 | 🧪 Test | `doSomething(mockInput)` |
|
|
185
|
+
|
|
186
|
+
### `mobile-app` — 1 hit
|
|
187
|
+
|
|
188
|
+
| File | Line | Kind | Snippet |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| `src/handlers/things.kt` | 102 | 📞 Call site | `someClient.doSomething(...)` |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## ⚠️ Same-signature behavior changes
|
|
195
|
+
|
|
196
|
+
If applicable, list call sites that won't be caught by the compiler but may behave
|
|
197
|
+
differently. Same table layout.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## ✅ Safe references (no action needed)
|
|
202
|
+
|
|
203
|
+
Things that match the symbol name but are unrelated (different scope, comment, log
|
|
204
|
+
string). Keep this list short — only include hits that the user might worry about.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Summary
|
|
209
|
+
|
|
210
|
+
- `admin-web`: 3 production + 1 test → must update before merge
|
|
211
|
+
- `mobile-app`: 1 production → must update before merge
|
|
212
|
+
- `worker-service`: no hits
|
|
213
|
+
|
|
214
|
+
Suggested next steps:
|
|
215
|
+
1. Update `admin-web` first (largest blast radius).
|
|
216
|
+
2. Coordinate deploy ordering: backend-api before clients.
|
|
217
|
+
3. Add a deprecation shim if you can't update all clients in one go.
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Group by repo, then by file. Always include the snippet (one line). Don't truncate
|
|
221
|
+
file paths.
|
|
222
|
+
|
|
223
|
+
## Rules
|
|
224
|
+
|
|
225
|
+
- **Never edit code.** Report only.
|
|
226
|
+
- **Don't claim a hit is broken without seeing it.** When the snippet alone is
|
|
227
|
+
ambiguous (e.g. the symbol name is a common word), open the file and confirm scope.
|
|
228
|
+
- **Respect the workspace boundary.** Only search `repos/*` (and explicitly-included
|
|
229
|
+
worktrees). Don't escape into the user's home directory or unrelated dirs.
|
|
230
|
+
- **Distinguish production vs test hits.** Both matter, but test hits are coverage
|
|
231
|
+
signal, not regression risk.
|
|
232
|
+
- **Acknowledge unknowns.** If a language in the workspace isn't in the table above,
|
|
233
|
+
fall back to a plain symbol-name grep and tell the user the analysis is best-effort.
|
|
234
|
+
- **Common names need extra care.** A symbol called `get` or `update` will match
|
|
235
|
+
noise — narrow the search using import paths or enclosing scope before reporting.
|
|
236
|
+
- **Don't truncate.** If there are too many hits, summarize counts per file but still
|
|
237
|
+
list every file.
|
|
238
|
+
|
|
239
|
+
## Output language
|
|
240
|
+
|
|
241
|
+
{{OUTPUT_LANGUAGE_INSTRUCTION}}
|