start-vibing-stacks 2.22.0 → 2.23.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/README.md +42 -5
- package/dist/setup.js +67 -3
- package/package.json +1 -1
- package/stacks/_shared/commands/peers.md +37 -0
- package/stacks/_shared/hooks/_state.README.md +55 -0
- package/stacks/_shared/hooks/_state.ts +444 -0
- package/stacks/_shared/hooks/peers.ts +250 -0
- package/stacks/_shared/hooks/post-tool-use.ts +79 -0
- package/stacks/_shared/hooks/pre-tool-use.ts +175 -0
- package/stacks/_shared/hooks/session-start.ts +124 -0
- package/stacks/_shared/hooks/stop-validator.ts +37 -0
- package/stacks/_shared/hooks/user-prompt-submit.ts +96 -19
- package/stacks/_shared/skills/multi-instance-coordination/SKILL.md +90 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Multi-stack AI workflow for **Claude Code** & **Cursor**. One command installs a
|
|
|
6
6
|
npx start-vibing-stacks
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
> Latest: **v2.
|
|
9
|
+
> Latest: **v2.23.0** — multi-instance coordination layer. Two or more Claude Code sessions in the same folder auto-discover each other through `.claude/state/`, share file-edit awareness, and refuse to overwrite each other's uncommitted work. New `/peers` slash command + `peers list/notify/locks/cleanup` CLI.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -16,8 +16,8 @@ npx start-vibing-stacks
|
|
|
16
16
|
|---|---|---|
|
|
17
17
|
| Agents | **7** universal | `research-web` (MCP-first) · `documenter` (memory layer) · `domain-updater` · `commit-manager` · `tester` · `claude-md-compactor` (compaction) · **`security-auditor` (VETO)** |
|
|
18
18
|
| Skills | **22** shared + **7–14** stack-specific + **2–7** frontend | Versioned (`version:` frontmatter), upgradable via `migrate` |
|
|
19
|
-
| Hooks | `
|
|
20
|
-
| Commands | `/feature` · `/fix` · `/research` · `/validate` | Slash commands |
|
|
19
|
+
| Hooks | `session-start` · `user-prompt-submit` · `pre-tool-use` · `post-tool-use` · `stop-validator` · `final-check` | Multi-instance coordination + git/docs/secrets/code-quality gates |
|
|
20
|
+
| Commands | `/feature` · `/fix` · `/research` · `/validate` · `/peers` | Slash commands |
|
|
21
21
|
| Workflows | `ci.yml` + `security.yml` per stack | Copied to `.github/workflows/` when target is empty |
|
|
22
22
|
| Configs | `active-project.json` · `domain-mapping.json` · `security-rules.json` · `standards-review.json` | Drives every agent's stack detection |
|
|
23
23
|
|
|
@@ -82,14 +82,51 @@ your-project/
|
|
|
82
82
|
│ │ │ ├── _index.json # machine-readable, regenerated by documenter
|
|
83
83
|
│ │ │ └── domains/ # one file per domain (≤ 8 KB each)
|
|
84
84
|
│ │ └── <other skills>/
|
|
85
|
-
│ ├── hooks/ # stop-validator, final-check, prompt-submit
|
|
86
|
-
│ ├── commands/ # /feature, /fix, /research, /validate
|
|
85
|
+
│ ├── hooks/ # session-start, pre/post-tool-use, stop-validator, final-check, prompt-submit, peers (CLI)
|
|
86
|
+
│ ├── commands/ # /feature, /fix, /research, /validate, /peers
|
|
87
|
+
│ ├── state/ # multi-instance coordination (gitignored, runtime-managed)
|
|
87
88
|
│ └── config/ # active-project, domain-mapping, security-rules, ...
|
|
88
89
|
└── .github/workflows/ # ci.yml + security.yml (if dir was empty)
|
|
89
90
|
```
|
|
90
91
|
|
|
91
92
|
---
|
|
92
93
|
|
|
94
|
+
## Multiple Claude instances in the same folder
|
|
95
|
+
|
|
96
|
+
When two or more Claude Code sessions run in the same project, the installed hooks coordinate automatically through `.claude/state/` (gitignored, per-host).
|
|
97
|
+
|
|
98
|
+
What you get:
|
|
99
|
+
|
|
100
|
+
- **Auto-discovery** — `SessionStart` registers each session, captures the same title that appears in `claude --resume`, and tells you who else is around.
|
|
101
|
+
- **File-edit collision shield** — `PreToolUse` BLOCKS Edit/Write on a file a peer is actively editing (heartbeat <60s, touched <5min ago). Idle peers (60s–30min) downgrade to a warning; stale peers are ignored.
|
|
102
|
+
- **Cross-instance messaging** — `peers notify <id-prefix> "msg"` queues a message that surfaces in the OTHER instance at its next prompt. The user-prompt-submit hook drains the inbox automatically.
|
|
103
|
+
|
|
104
|
+
### `/peers` CLI
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Inside Claude:
|
|
108
|
+
/peers # delegates to the script
|
|
109
|
+
|
|
110
|
+
# Or from any terminal:
|
|
111
|
+
npx tsx .claude/hooks/peers.ts list
|
|
112
|
+
npx tsx .claude/hooks/peers.ts notify a1b2c3d4 "I just committed auth changes"
|
|
113
|
+
npx tsx .claude/hooks/peers.ts locks --minutes 10
|
|
114
|
+
npx tsx .claude/hooks/peers.ts cleanup
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Heartbeat thresholds
|
|
118
|
+
|
|
119
|
+
| Last activity | State | Effect |
|
|
120
|
+
| ------------- | -------- | -------------------------------------------------------- |
|
|
121
|
+
| < 60s | active | Counts for collision detection; BLOCKS conflicting edits |
|
|
122
|
+
| 60s – 30min | idle | Surfaced as a warning; edits NOT blocked |
|
|
123
|
+
| > 30min | stale | Auto-archived |
|
|
124
|
+
| > 24h | removed | Deleted |
|
|
125
|
+
|
|
126
|
+
Single-host coordination only. It does **not** replace `git` for cross-machine collaboration; it prevents two live sessions on the same laptop from stepping on the same uncommitted file.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
93
130
|
## CLI
|
|
94
131
|
|
|
95
132
|
```bash
|
package/dist/setup.js
CHANGED
|
@@ -68,6 +68,10 @@ export async function setupProject(projectDir, config, options = {}) {
|
|
|
68
68
|
mkdirSync(join(claudeDir, 'commands'), { recursive: true });
|
|
69
69
|
mkdirSync(join(claudeDir, 'rules'), { recursive: true });
|
|
70
70
|
mkdirSync(join(claudeDir, 'skills', 'codebase-knowledge', 'domains'), { recursive: true });
|
|
71
|
+
// Multi-instance coordination state (gitignored, runtime-managed)
|
|
72
|
+
mkdirSync(join(claudeDir, 'state', 'sessions', '_archive'), { recursive: true });
|
|
73
|
+
mkdirSync(join(claudeDir, 'state', 'inbox'), { recursive: true });
|
|
74
|
+
mkdirSync(join(claudeDir, 'state', 'file-touches', '_archive'), { recursive: true });
|
|
71
75
|
spinner.text = 'Directory structure created';
|
|
72
76
|
// 2. Copy shared agents (universal)
|
|
73
77
|
const sharedAgentsDir = join(PACKAGE_ROOT, 'stacks', '_shared', 'agents');
|
|
@@ -79,6 +83,12 @@ export async function setupProject(projectDir, config, options = {}) {
|
|
|
79
83
|
// 4. Copy shared hooks
|
|
80
84
|
const sharedHooksDir = join(PACKAGE_ROOT, 'stacks', '_shared', 'hooks');
|
|
81
85
|
const hookCount = copyDirRecursive(sharedHooksDir, join(claudeDir, 'hooks'), options.force);
|
|
86
|
+
// 4b. Multi-instance coordination README in state dir
|
|
87
|
+
const stateReadmeSrc = join(PACKAGE_ROOT, 'stacks', '_shared', 'hooks', '_state.README.md');
|
|
88
|
+
const stateReadmeDest = join(claudeDir, 'state', 'README.md');
|
|
89
|
+
if (existsSync(stateReadmeSrc) && (!existsSync(stateReadmeDest) || options.force)) {
|
|
90
|
+
writeFileSync(stateReadmeDest, readFileSync(stateReadmeSrc, 'utf8'));
|
|
91
|
+
}
|
|
82
92
|
// 5. Copy shared config
|
|
83
93
|
const sharedConfigDir = join(PACKAGE_ROOT, 'stacks', '_shared', 'config');
|
|
84
94
|
copyDirRecursive(sharedConfigDir, join(claudeDir, 'config'), options.force);
|
|
@@ -140,13 +150,21 @@ export async function setupProject(projectDir, config, options = {}) {
|
|
|
140
150
|
spinner.text = 'Imported .cursorrules into Claude config';
|
|
141
151
|
}
|
|
142
152
|
}
|
|
143
|
-
// 11b. Ensure CLAUDE.local.md
|
|
153
|
+
// 11b. Ensure CLAUDE.local.md and .claude/state/ are gitignored
|
|
144
154
|
const gitignorePath = join(projectDir, '.gitignore');
|
|
145
155
|
if (existsSync(gitignorePath)) {
|
|
146
|
-
|
|
156
|
+
let gitignore = readFileSync(gitignorePath, 'utf8');
|
|
157
|
+
let changed = false;
|
|
147
158
|
if (!gitignore.includes('CLAUDE.local.md')) {
|
|
148
|
-
|
|
159
|
+
gitignore = gitignore.trimEnd() + '\n\n# Claude Code local preferences\nCLAUDE.local.md\n';
|
|
160
|
+
changed = true;
|
|
161
|
+
}
|
|
162
|
+
if (!gitignore.match(/^\.claude\/state\/?$/m) && !gitignore.match(/^\.claude\/?$/m)) {
|
|
163
|
+
gitignore = gitignore.trimEnd() + '\n\n# Claude Code multi-instance coordination state (runtime, per-host)\n.claude/state/\n';
|
|
164
|
+
changed = true;
|
|
149
165
|
}
|
|
166
|
+
if (changed)
|
|
167
|
+
writeFileSync(gitignorePath, gitignore);
|
|
150
168
|
}
|
|
151
169
|
// 11c. Save standards review results
|
|
152
170
|
if (config.standardsReview) {
|
|
@@ -230,6 +248,17 @@ export async function setupProject(projectDir, config, options = {}) {
|
|
|
230
248
|
deny: [],
|
|
231
249
|
},
|
|
232
250
|
hooks: {
|
|
251
|
+
SessionStart: [
|
|
252
|
+
{
|
|
253
|
+
hooks: [
|
|
254
|
+
{
|
|
255
|
+
type: 'command',
|
|
256
|
+
command: 'npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.ts"',
|
|
257
|
+
timeout: 10,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
],
|
|
233
262
|
UserPromptSubmit: [
|
|
234
263
|
{
|
|
235
264
|
matcher: '',
|
|
@@ -242,6 +271,30 @@ export async function setupProject(projectDir, config, options = {}) {
|
|
|
242
271
|
],
|
|
243
272
|
},
|
|
244
273
|
],
|
|
274
|
+
PreToolUse: [
|
|
275
|
+
{
|
|
276
|
+
matcher: 'Edit|Write|MultiEdit|NotebookEdit',
|
|
277
|
+
hooks: [
|
|
278
|
+
{
|
|
279
|
+
type: 'command',
|
|
280
|
+
command: 'npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-use.ts"',
|
|
281
|
+
timeout: 5,
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
PostToolUse: [
|
|
287
|
+
{
|
|
288
|
+
matcher: 'Edit|Write|MultiEdit|NotebookEdit',
|
|
289
|
+
hooks: [
|
|
290
|
+
{
|
|
291
|
+
type: 'command',
|
|
292
|
+
command: 'npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-use.ts"',
|
|
293
|
+
timeout: 5,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
],
|
|
245
298
|
Stop: [
|
|
246
299
|
{
|
|
247
300
|
hooks: [
|
|
@@ -253,6 +306,17 @@ export async function setupProject(projectDir, config, options = {}) {
|
|
|
253
306
|
],
|
|
254
307
|
},
|
|
255
308
|
],
|
|
309
|
+
SessionEnd: [
|
|
310
|
+
{
|
|
311
|
+
hooks: [
|
|
312
|
+
{
|
|
313
|
+
type: 'command',
|
|
314
|
+
command: 'npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-validator.ts"',
|
|
315
|
+
timeout: 10,
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
},
|
|
319
|
+
],
|
|
256
320
|
},
|
|
257
321
|
agents: {
|
|
258
322
|
'research-web': {
|
package/package.json
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: peers
|
|
3
|
+
description: Inspect or talk to other Claude Code instances running in this same project (multi-instance coordination layer).
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /peers — Multi-Instance Coordination
|
|
8
|
+
|
|
9
|
+
Use this when the user asks something like:
|
|
10
|
+
|
|
11
|
+
- "is there another Claude running in this folder?"
|
|
12
|
+
- "tell the other instance I finished the auth work"
|
|
13
|
+
- "who touched `src/x.ts` recently?"
|
|
14
|
+
- "clean up dead sessions"
|
|
15
|
+
|
|
16
|
+
Run the `peers` CLI via Bash. The script lives at `.claude/hooks/peers.ts` and is installed by `start-vibing-stacks`.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
| Goal | Command |
|
|
21
|
+
|---|---|
|
|
22
|
+
| List active + idle peer sessions | `npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/peers.ts" list` |
|
|
23
|
+
| Notify another instance | `npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/peers.ts" notify <id-prefix> "message"` |
|
|
24
|
+
| See who touched what file recently | `npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/peers.ts" locks --minutes 10` |
|
|
25
|
+
| Remove stale sessions | `npx tsx "$CLAUDE_PROJECT_DIR/.claude/hooks/peers.ts" cleanup` |
|
|
26
|
+
|
|
27
|
+
The `<id-prefix>` is the 8-character short id printed by `peers list`, or any substring of the peer's title.
|
|
28
|
+
|
|
29
|
+
## Output rules
|
|
30
|
+
|
|
31
|
+
- Always quote the short id (first 8 chars), the title, and the idle time when reporting peers to the user.
|
|
32
|
+
- If `peers notify` succeeds, tell the user the message will surface in the OTHER instance at the start of its next prompt (drained by `user-prompt-submit.ts`).
|
|
33
|
+
- If `peers list` says "No peer sessions", confirm that to the user and DO NOT suggest workarounds.
|
|
34
|
+
|
|
35
|
+
## Background
|
|
36
|
+
|
|
37
|
+
State lives in `.claude/state/` (gitignored). Hooks `session-start.ts`, `user-prompt-submit.ts`, `pre-tool-use.ts`, `post-tool-use.ts` and `stop-validator.ts` keep it in sync. See `multi-instance-coordination` skill for the full protocol.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# `.claude/state/` — Multi-Instance Coordination
|
|
2
|
+
|
|
3
|
+
> This directory is **gitignored** and managed automatically by hooks. Safe to delete; it will be recreated by the next Claude session.
|
|
4
|
+
|
|
5
|
+
When two or more Claude Code instances run in the same project folder, the hooks under `.claude/hooks/` use this directory to:
|
|
6
|
+
|
|
7
|
+
1. announce themselves to each other (`sessions/<id>.json`)
|
|
8
|
+
2. record file edits as they happen (`file-touches.jsonl`)
|
|
9
|
+
3. prevent simultaneous writes to the same file (PreToolUse hook reads the touch log)
|
|
10
|
+
4. exchange messages (`inbox/<id>.jsonl` — drained at next prompt)
|
|
11
|
+
|
|
12
|
+
## Layout
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
.claude/state/
|
|
16
|
+
sessions/
|
|
17
|
+
<session-id>.json one record per active instance (heartbeat-tracked)
|
|
18
|
+
_archive/ sessions ended or stale (>30min idle)
|
|
19
|
+
inbox/
|
|
20
|
+
<session-id>.jsonl queued messages for that session
|
|
21
|
+
file-touches.jsonl append-only Edit/Write log (rotates every 1000 lines)
|
|
22
|
+
file-touches/_archive/ rotated logs
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Heartbeat thresholds
|
|
26
|
+
|
|
27
|
+
| Last activity | State | Effect |
|
|
28
|
+
| ------------- | -------- | --------------------------------------------------------------------------- |
|
|
29
|
+
| < 60s | active | Counts for collision detection. PreToolUse may **block** Edit/Write. |
|
|
30
|
+
| 60s – 30min | idle | Surfaced as a warning in `systemMessage`. Edits are **not** blocked. |
|
|
31
|
+
| > 30min | stale | Auto-archived on the next sweep. |
|
|
32
|
+
| > 24h | removed | Deleted entirely. |
|
|
33
|
+
|
|
34
|
+
## Viewing peers
|
|
35
|
+
|
|
36
|
+
From inside Claude or a terminal:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
/peers
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
or directly:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
npx tsx .claude/hooks/peers.ts list
|
|
46
|
+
npx tsx .claude/hooks/peers.ts notify <id-prefix> "message"
|
|
47
|
+
npx tsx .claude/hooks/peers.ts locks --minutes 10
|
|
48
|
+
npx tsx .claude/hooks/peers.ts cleanup
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Safety
|
|
52
|
+
|
|
53
|
+
- All state writes are atomic (`.tmp` + `rename`).
|
|
54
|
+
- All reads are tolerant of corruption — hooks never block Claude on a malformed file.
|
|
55
|
+
- This is a single-host coordination layer. It does **not** replace git; it only prevents two live sessions from stepping on the same uncommitted edit.
|