guild-agents 1.1.0 → 1.2.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 +8 -1
- package/bin/guild.js +57 -0
- package/package.json +1 -1
- package/src/commands/init.js +8 -1
- package/src/commands/workspace.js +55 -0
- package/src/templates/skills/debug/SKILL.md +145 -0
- package/src/templates/skills/guild-specialize/SKILL.md +20 -0
- package/src/templates/skills/re-specialize/SKILL.md +153 -0
- package/src/templates/skills/tdd/SKILL.md +159 -0
- package/src/templates/skills/verify/SKILL.md +114 -0
- package/src/utils/generators.js +11 -9
- package/src/utils/workspace.js +82 -0
- package/src/utils/zones.js +39 -0
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ Six phases: **evaluate**, **specify**, **plan**, **implement**, **review**, **va
|
|
|
62
62
|
|
|
63
63
|
## Skills Reference
|
|
64
64
|
|
|
65
|
-
All
|
|
65
|
+
All 15 skills, grouped by function:
|
|
66
66
|
|
|
67
67
|
| Skill | Group | Description |
|
|
68
68
|
| --- | --- | --- |
|
|
@@ -72,7 +72,11 @@ All 11 skills, grouped by function:
|
|
|
72
72
|
| `/council` | Decision | Multi-perspective deliberation on a decision or feature |
|
|
73
73
|
| `/review` | Quality | Code review on the current diff |
|
|
74
74
|
| `/qa-cycle` | Quality | QA and bugfix loop until clean |
|
|
75
|
+
| `/tdd` | Discipline | TDD red-green-refactor cycle |
|
|
76
|
+
| `/debug` | Discipline | Systematic 4-phase debugging |
|
|
77
|
+
| `/verify` | Discipline | Evidence-before-claims verification |
|
|
75
78
|
| `/guild-specialize` | Context | Explore codebase, enrich CLAUDE.md with real conventions |
|
|
79
|
+
| `/re-specialize` | Context | Incremental update of auto-generated CLAUDE.md zones |
|
|
76
80
|
| `/session-start` | Context | Load context and resume work |
|
|
77
81
|
| `/session-end` | Context | Save state to SESSION.md |
|
|
78
82
|
| `/status` | Context | Project and session state overview |
|
|
@@ -89,6 +93,9 @@ guild list # List agents and skills
|
|
|
89
93
|
guild run <skill> # Preview a skill's execution plan (dry-run)
|
|
90
94
|
guild logs # View execution traces
|
|
91
95
|
guild logs clean # Remove old traces (--days N, --all)
|
|
96
|
+
guild workspace init <name> <members...> # Create a workspace
|
|
97
|
+
guild workspace add <path> # Add a member repo
|
|
98
|
+
guild workspace status # Show workspace state
|
|
92
99
|
```
|
|
93
100
|
|
|
94
101
|
## Under the Hood
|
package/bin/guild.js
CHANGED
|
@@ -168,4 +168,61 @@ logsCmd
|
|
|
168
168
|
}
|
|
169
169
|
});
|
|
170
170
|
|
|
171
|
+
// guild workspace
|
|
172
|
+
const workspaceCmd = program
|
|
173
|
+
.command('workspace')
|
|
174
|
+
.description('Manage multi-repo workspaces');
|
|
175
|
+
|
|
176
|
+
// guild workspace init
|
|
177
|
+
workspaceCmd
|
|
178
|
+
.command('init')
|
|
179
|
+
.description('Initialize a workspace in the current directory')
|
|
180
|
+
.argument('<name>', 'Workspace name')
|
|
181
|
+
.argument('<members...>', 'Paths to member repos (e.g., ./backend ./frontend)')
|
|
182
|
+
.action(async (name, members) => {
|
|
183
|
+
try {
|
|
184
|
+
const { createWorkspaceFile } = await import('../src/commands/workspace.js');
|
|
185
|
+
await createWorkspaceFile(name, members);
|
|
186
|
+
console.log(`Workspace "${name}" created with ${members.length} member(s).`);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.error(err.message);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// guild workspace add
|
|
194
|
+
workspaceCmd
|
|
195
|
+
.command('add')
|
|
196
|
+
.description('Add a member repo to the workspace')
|
|
197
|
+
.argument('<path>', 'Path to the member repo')
|
|
198
|
+
.action(async (memberPath) => {
|
|
199
|
+
try {
|
|
200
|
+
const { addWorkspaceMember } = await import('../src/commands/workspace.js');
|
|
201
|
+
await addWorkspaceMember(memberPath);
|
|
202
|
+
console.log(`Added "${memberPath}" to workspace.`);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.error(err.message);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// guild workspace status
|
|
210
|
+
workspaceCmd
|
|
211
|
+
.command('status')
|
|
212
|
+
.description('Show workspace members and their state')
|
|
213
|
+
.action(async () => {
|
|
214
|
+
try {
|
|
215
|
+
const { getWorkspaceStatus } = await import('../src/commands/workspace.js');
|
|
216
|
+
const status = await getWorkspaceStatus();
|
|
217
|
+
console.log(`Workspace: ${status.name}`);
|
|
218
|
+
for (const m of status.members) {
|
|
219
|
+
const state = m.initialized ? 'initialized' : 'not initialized';
|
|
220
|
+
console.log(` ${m.name} (${m.path}) — ${state}`);
|
|
221
|
+
}
|
|
222
|
+
} catch (err) {
|
|
223
|
+
console.error(err.message);
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
171
228
|
program.parse();
|
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -14,11 +14,18 @@ import chalk from 'chalk';
|
|
|
14
14
|
import { existsSync } from 'fs';
|
|
15
15
|
import { generateProjectMd, generateSessionMd, generateClaudeMd } from '../utils/generators.js';
|
|
16
16
|
import { copyTemplates, getAgentNames, getSkillNames } from '../utils/files.js';
|
|
17
|
+
import { loadWorkspace } from '../utils/workspace.js';
|
|
17
18
|
|
|
18
19
|
export async function runInit() {
|
|
19
20
|
console.log('');
|
|
20
21
|
p.intro(chalk.bold.cyan('Guild v1 — New project'));
|
|
21
22
|
|
|
23
|
+
// Detect workspace membership
|
|
24
|
+
const workspace = loadWorkspace();
|
|
25
|
+
if (workspace) {
|
|
26
|
+
p.log.info(chalk.gray(`Workspace detected: ${workspace.name} (${workspace.root})`));
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
// Check for existing installation
|
|
23
30
|
if (existsSync('.claude/agents')) {
|
|
24
31
|
const overwrite = await p.confirm({
|
|
@@ -107,7 +114,7 @@ export async function runInit() {
|
|
|
107
114
|
try {
|
|
108
115
|
await copyTemplates();
|
|
109
116
|
spinner.message('Generating CLAUDE.md...');
|
|
110
|
-
await generateClaudeMd(projectData);
|
|
117
|
+
await generateClaudeMd(projectData, workspace, name);
|
|
111
118
|
|
|
112
119
|
spinner.message('Generating PROJECT.md...');
|
|
113
120
|
await generateProjectMd(projectData);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { basename, join } from 'path';
|
|
3
|
+
import { findWorkspaceRoot, WORKSPACE_FILE } from '../utils/workspace.js';
|
|
4
|
+
|
|
5
|
+
export async function createWorkspaceFile(name, memberPaths) {
|
|
6
|
+
const members = memberPaths.map(p => ({
|
|
7
|
+
name: basename(p),
|
|
8
|
+
path: p,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
const config = {
|
|
12
|
+
name,
|
|
13
|
+
members,
|
|
14
|
+
shared: {
|
|
15
|
+
agents: '.guild/agents',
|
|
16
|
+
skills: '.guild/skills',
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
writeFileSync(WORKSPACE_FILE, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
21
|
+
mkdirSync(join('.guild', 'agents'), { recursive: true });
|
|
22
|
+
mkdirSync(join('.guild', 'skills'), { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function addWorkspaceMember(memberPath) {
|
|
26
|
+
const root = findWorkspaceRoot();
|
|
27
|
+
if (!root) throw new Error('No workspace found. Run `guild workspace init` first.');
|
|
28
|
+
|
|
29
|
+
const filePath = join(root, WORKSPACE_FILE);
|
|
30
|
+
const config = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
31
|
+
const name = basename(memberPath);
|
|
32
|
+
|
|
33
|
+
if (config.members.some(m => m.path === memberPath || m.name === name)) {
|
|
34
|
+
throw new Error(`"${name}" is already a member of this workspace.`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
config.members.push({ name, path: memberPath });
|
|
38
|
+
writeFileSync(filePath, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function getWorkspaceStatus() {
|
|
42
|
+
const root = findWorkspaceRoot();
|
|
43
|
+
if (!root) throw new Error('No workspace found. Run `guild workspace init` first.');
|
|
44
|
+
|
|
45
|
+
const filePath = join(root, WORKSPACE_FILE);
|
|
46
|
+
const config = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
47
|
+
|
|
48
|
+
const members = config.members.map(m => {
|
|
49
|
+
const absPath = join(root, m.path);
|
|
50
|
+
const initialized = existsSync(join(absPath, '.claude'));
|
|
51
|
+
return { ...m, absolutePath: absPath, initialized };
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return { name: config.name, root, members };
|
|
55
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debug
|
|
3
|
+
description: "Discipline skill — systematic debugging process. Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Systematic Debugging
|
|
8
|
+
|
|
9
|
+
Random fixes waste time and create new bugs. Quick patches mask underlying issues.
|
|
10
|
+
|
|
11
|
+
**Core principle:** ALWAYS find root cause before attempting fixes.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
`/debug`
|
|
16
|
+
|
|
17
|
+
Invoke this skill when encountering any bug, test failure, or unexpected behavior. Follow the four phases in order.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- Test failures
|
|
22
|
+
- Bugs in production
|
|
23
|
+
- Unexpected behavior
|
|
24
|
+
- Performance problems
|
|
25
|
+
- Build failures
|
|
26
|
+
- Integration issues
|
|
27
|
+
|
|
28
|
+
**Use this ESPECIALLY when:**
|
|
29
|
+
|
|
30
|
+
- Under time pressure (emergencies make guessing tempting)
|
|
31
|
+
- "Just one quick fix" seems obvious
|
|
32
|
+
- You've already tried multiple fixes
|
|
33
|
+
- Previous fix didn't work
|
|
34
|
+
|
|
35
|
+
## The Iron Law
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If you haven't completed Phase 1, you cannot propose fixes.
|
|
42
|
+
|
|
43
|
+
## The Four Phases
|
|
44
|
+
|
|
45
|
+
Complete each phase before proceeding to the next.
|
|
46
|
+
|
|
47
|
+
### Phase 1: Root Cause Investigation
|
|
48
|
+
|
|
49
|
+
**BEFORE attempting ANY fix:**
|
|
50
|
+
|
|
51
|
+
1. **Read Error Messages Carefully**
|
|
52
|
+
- Don't skip past errors or warnings
|
|
53
|
+
- Read stack traces completely
|
|
54
|
+
- Note line numbers, file paths, error codes
|
|
55
|
+
|
|
56
|
+
2. **Reproduce Consistently**
|
|
57
|
+
- Can you trigger it reliably?
|
|
58
|
+
- What are the exact steps?
|
|
59
|
+
- If not reproducible, gather more data — don't guess
|
|
60
|
+
|
|
61
|
+
3. **Check Recent Changes**
|
|
62
|
+
- What changed that could cause this?
|
|
63
|
+
- Git diff, recent commits
|
|
64
|
+
- New dependencies, config changes
|
|
65
|
+
|
|
66
|
+
4. **Gather Evidence in Multi-Component Systems**
|
|
67
|
+
- Log what data enters each component boundary
|
|
68
|
+
- Log what data exits each component boundary
|
|
69
|
+
- Verify environment/config propagation
|
|
70
|
+
- Run once to gather evidence showing WHERE it breaks
|
|
71
|
+
- THEN investigate that specific component
|
|
72
|
+
|
|
73
|
+
5. **Trace Data Flow**
|
|
74
|
+
- Where does the bad value originate?
|
|
75
|
+
- What called this with the bad value?
|
|
76
|
+
- Keep tracing up until you find the source
|
|
77
|
+
- Fix at source, not at symptom
|
|
78
|
+
|
|
79
|
+
### Phase 2: Pattern Analysis
|
|
80
|
+
|
|
81
|
+
1. **Find Working Examples** — locate similar working code in the same codebase
|
|
82
|
+
2. **Compare Against References** — read reference implementations COMPLETELY, don't skim
|
|
83
|
+
3. **Identify Differences** — list every difference between working and broken, however small
|
|
84
|
+
4. **Understand Dependencies** — what components, settings, environment does it need?
|
|
85
|
+
|
|
86
|
+
### Phase 3: Hypothesis and Testing
|
|
87
|
+
|
|
88
|
+
1. **Form Single Hypothesis** — "I think X is the root cause because Y"
|
|
89
|
+
2. **Test Minimally** — smallest possible change, one variable at a time
|
|
90
|
+
3. **Verify** — did it work? Yes = Phase 4. No = new hypothesis. DON'T stack fixes.
|
|
91
|
+
|
|
92
|
+
### Phase 4: Implementation
|
|
93
|
+
|
|
94
|
+
1. **Create Failing Test Case** — simplest possible reproduction, automated if possible. Use `/tdd` for proper failing tests.
|
|
95
|
+
2. **Implement Single Fix** — address the root cause, ONE change at a time, no "while I'm here" improvements.
|
|
96
|
+
3. **Verify Fix** — test passes? No other tests broken? Issue actually resolved? Use `/verify` before claiming done.
|
|
97
|
+
|
|
98
|
+
**If fix doesn't work:**
|
|
99
|
+
|
|
100
|
+
- If < 3 attempts: return to Phase 1, re-analyze with new information
|
|
101
|
+
- **If >= 3 attempts: STOP and question the architecture**
|
|
102
|
+
|
|
103
|
+
**3+ failed fixes indicate an architectural problem:**
|
|
104
|
+
|
|
105
|
+
- Each fix reveals new coupling in different places
|
|
106
|
+
- Fixes require massive refactoring
|
|
107
|
+
- Each fix creates new symptoms elsewhere
|
|
108
|
+
- Discuss with the user before attempting more fixes
|
|
109
|
+
|
|
110
|
+
## Red Flags - STOP and Follow Process
|
|
111
|
+
|
|
112
|
+
- "Quick fix for now, investigate later"
|
|
113
|
+
- "Just try changing X and see if it works"
|
|
114
|
+
- "Add multiple changes, run tests"
|
|
115
|
+
- "It's probably X, let me fix that"
|
|
116
|
+
- "I don't fully understand but this might work"
|
|
117
|
+
- Proposing solutions before tracing data flow
|
|
118
|
+
- "One more fix attempt" (when already tried 2+)
|
|
119
|
+
|
|
120
|
+
**ALL of these mean: STOP. Return to Phase 1.**
|
|
121
|
+
|
|
122
|
+
## Common Rationalizations
|
|
123
|
+
|
|
124
|
+
| Excuse | Reality |
|
|
125
|
+
| ----------------------------------------- | ------------------------------------------------------------------------ |
|
|
126
|
+
| "Issue is simple, don't need process" | Simple issues have root causes too. Process is fast for simple bugs. |
|
|
127
|
+
| "Emergency, no time for process" | Systematic debugging is FASTER than guess-and-check thrashing. |
|
|
128
|
+
| "Just try this first, then investigate" | First fix sets the pattern. Do it right from the start. |
|
|
129
|
+
| "I'll write test after confirming fix" | Untested fixes don't stick. Test first proves it. |
|
|
130
|
+
| "Multiple fixes at once saves time" | Can't isolate what worked. Causes new bugs. |
|
|
131
|
+
| "I see the problem, let me fix it" | Seeing symptoms is not understanding root cause. |
|
|
132
|
+
|
|
133
|
+
## Quick Reference
|
|
134
|
+
|
|
135
|
+
| Phase | Key Activities | Success Criteria |
|
|
136
|
+
| ---------------------- | ------------------------------------------------------ | --------------------------- |
|
|
137
|
+
| **1. Root Cause** | Read errors, reproduce, check changes, gather evidence | Understand WHAT and WHY |
|
|
138
|
+
| **2. Pattern** | Find working examples, compare | Identify differences |
|
|
139
|
+
| **3. Hypothesis** | Form theory, test minimally | Confirmed or new hypothesis |
|
|
140
|
+
| **4. Implementation** | Create test, fix, verify | Bug resolved, tests pass |
|
|
141
|
+
|
|
142
|
+
## Related Skills
|
|
143
|
+
|
|
144
|
+
- `/tdd` — TDD cycle for creating failing test cases in Phase 4
|
|
145
|
+
- `/verify` — verification before claiming the fix is complete
|
|
@@ -113,6 +113,13 @@ Invoke the Tech Lead agent using Task tool with `model: "opus"` (reasoning tier)
|
|
|
113
113
|
- **Visible limitations and technical debt**: outdated dependencies, TODOs found
|
|
114
114
|
- **Useful project commands**: detected npm/make/cargo scripts
|
|
115
115
|
|
|
116
|
+
CLAUDE.md now uses zone markers (`<!-- guild:auto-start:ID -->` / `<!-- guild:auto-end:ID -->`) to delimit auto-generated sections. When enriching:
|
|
117
|
+
|
|
118
|
+
- Replace content BETWEEN the markers, preserving the markers themselves
|
|
119
|
+
- The following zones exist: `structure`, `architecture`, `conventions`, `env-vars`
|
|
120
|
+
- Do NOT modify content outside of zone markers (user-owned sections)
|
|
121
|
+
- If markers are missing (legacy project), replace `[PENDING: guild-specialize]` placeholders directly
|
|
122
|
+
|
|
116
123
|
### Step 4 — Specialize agents
|
|
117
124
|
|
|
118
125
|
Invoke the Tech Lead agent using Task tool with `model: "sonnet"` (execution tier) to add project-specific context for each agent in `.claude/agents/*.md`:
|
|
@@ -127,6 +134,19 @@ Invoke the Tech Lead agent using Task tool with `model: "sonnet"` (execution tie
|
|
|
127
134
|
- **db-migration.md**: ORM, migration tool, current schema (if applicable)
|
|
128
135
|
- **platform-expert.md**: Claude Code version, known permission bugs, hook configuration
|
|
129
136
|
|
|
137
|
+
When specializing agents, append a zone at the bottom of each agent file:
|
|
138
|
+
|
|
139
|
+
```markdown
|
|
140
|
+
<!-- guild:auto-start:agent-context -->
|
|
141
|
+
## Project-Specific Context
|
|
142
|
+
- Stack: [detected stack]
|
|
143
|
+
- Architecture: [detected patterns]
|
|
144
|
+
- Conventions: [detected conventions]
|
|
145
|
+
<!-- guild:auto-end:agent-context -->
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
This zone allows `guild-re-specialize` to update agent context later without touching the agent's core role definition.
|
|
149
|
+
|
|
130
150
|
Use the `Task` tool with `model: "sonnet"` to invoke each agent by reading their `.claude/agents/[name].md` if you need a specialized perspective to enrich their configuration.
|
|
131
151
|
|
|
132
152
|
The `model` parameter is resolved from the step's `model-tier`: reasoning→`"opus"`, execution→`"sonnet"`. System/gate steps run inline (no Task tool).
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: re-specialize
|
|
3
|
+
description: "Incremental re-specialization — re-scans the project and updates only auto-generated zones in CLAUDE.md and agents"
|
|
4
|
+
user-invocable: true
|
|
5
|
+
workflow:
|
|
6
|
+
version: 1
|
|
7
|
+
steps:
|
|
8
|
+
- id: read-current
|
|
9
|
+
role: system
|
|
10
|
+
intent: "Read current CLAUDE.md and agent files to identify existing zones."
|
|
11
|
+
commands: [cat CLAUDE.md]
|
|
12
|
+
produces: [current-claude-md, current-agents]
|
|
13
|
+
- id: explore-project
|
|
14
|
+
role: system
|
|
15
|
+
intent: "Scan project for current stack, dependencies, architecture, conventions."
|
|
16
|
+
commands: [ls -R src/, cat package.json]
|
|
17
|
+
produces: [detected-stack, detected-architecture, detected-conventions]
|
|
18
|
+
gate: true
|
|
19
|
+
- id: check-zones
|
|
20
|
+
role: system
|
|
21
|
+
intent: "Check if CLAUDE.md has guild zone markers. If not, offer to inject them."
|
|
22
|
+
requires: [current-claude-md]
|
|
23
|
+
produces: [zone-status]
|
|
24
|
+
gate: true
|
|
25
|
+
- id: regenerate-zones
|
|
26
|
+
role: tech-lead
|
|
27
|
+
intent: "Generate new content for each auto zone based on fresh project scan. Present diff to user."
|
|
28
|
+
requires: [current-claude-md, detected-stack, detected-architecture, detected-conventions, zone-status]
|
|
29
|
+
produces: [updated-claude-md, zone-diffs]
|
|
30
|
+
model-tier: reasoning
|
|
31
|
+
- id: update-agents
|
|
32
|
+
role: tech-lead
|
|
33
|
+
intent: "Update agent-context zones in agent files with fresh project context."
|
|
34
|
+
requires: [detected-stack, detected-architecture, detected-conventions]
|
|
35
|
+
produces: [updated-agents]
|
|
36
|
+
model-tier: execution
|
|
37
|
+
- id: confirm
|
|
38
|
+
role: system
|
|
39
|
+
intent: "Present summary of changes and get user confirmation."
|
|
40
|
+
requires: [zone-diffs, updated-agents]
|
|
41
|
+
produces: [confirmation]
|
|
42
|
+
gate: true
|
|
43
|
+
- id: commit
|
|
44
|
+
role: system
|
|
45
|
+
intent: "Commit re-specialized files."
|
|
46
|
+
commands: [git add CLAUDE.md .claude/agents/*.md, git commit -m "chore: re-specialize via guild-re-specialize"]
|
|
47
|
+
requires: [updated-claude-md, updated-agents, confirmation]
|
|
48
|
+
produces: [re-specialize-commit]
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# Re-Specialize
|
|
52
|
+
|
|
53
|
+
Incrementally updates auto-generated content in CLAUDE.md and agent files
|
|
54
|
+
without touching user customizations. Uses protected zone markers to identify
|
|
55
|
+
what can be safely regenerated.
|
|
56
|
+
|
|
57
|
+
## When to use
|
|
58
|
+
|
|
59
|
+
- When project dependencies have changed (new framework, updated versions)
|
|
60
|
+
- When architecture has evolved (new patterns, restructured folders)
|
|
61
|
+
- When agents need refreshed context about the project
|
|
62
|
+
- Periodically to keep CLAUDE.md in sync with the actual codebase
|
|
63
|
+
|
|
64
|
+
## Process
|
|
65
|
+
|
|
66
|
+
### Step 1 -- Read current state
|
|
67
|
+
|
|
68
|
+
Read CLAUDE.md and all agent files in `.claude/agents/`:
|
|
69
|
+
|
|
70
|
+
- Identify existing zone markers (`<!-- guild:auto-start:ID -->` / `<!-- guild:auto-end:ID -->`)
|
|
71
|
+
- Note which zones exist and their current content
|
|
72
|
+
- Identify any user customizations outside of zones
|
|
73
|
+
|
|
74
|
+
### Step 2 -- Explore the project
|
|
75
|
+
|
|
76
|
+
Same exploration as guild-specialize:
|
|
77
|
+
|
|
78
|
+
- Scan dependency files for current stack and versions
|
|
79
|
+
- Analyze project structure and architecture patterns
|
|
80
|
+
- Detect code conventions from linter/formatter configs
|
|
81
|
+
- Check environment variable examples
|
|
82
|
+
|
|
83
|
+
### Step 3 -- Check zone markers
|
|
84
|
+
|
|
85
|
+
If CLAUDE.md has zone markers, proceed to regeneration.
|
|
86
|
+
|
|
87
|
+
If CLAUDE.md does NOT have zone markers (legacy project):
|
|
88
|
+
|
|
89
|
+
- Offer to inject markers around the auto-generated sections
|
|
90
|
+
- Show the user where markers would be placed
|
|
91
|
+
- Require explicit confirmation before modifying
|
|
92
|
+
- If user declines, abort gracefully
|
|
93
|
+
|
|
94
|
+
### Step 4 -- Regenerate zone content
|
|
95
|
+
|
|
96
|
+
Invoke the Tech Lead agent using Task tool with `model: "opus"` (reasoning tier):
|
|
97
|
+
|
|
98
|
+
- Generate fresh content for each zone based on the project scan
|
|
99
|
+
- Compare new content with existing zone content
|
|
100
|
+
- Present a diff for each zone to the user
|
|
101
|
+
- Only replace zones where content has actually changed
|
|
102
|
+
|
|
103
|
+
Zones to regenerate:
|
|
104
|
+
|
|
105
|
+
| Zone ID | Content |
|
|
106
|
+
|----------------|--------------------------------------------|
|
|
107
|
+
| `structure` | Project folder structure with descriptions |
|
|
108
|
+
| `architecture` | Architecture patterns and design decisions |
|
|
109
|
+
| `conventions` | Code conventions from linter/formatter |
|
|
110
|
+
| `env-vars` | Environment variables from .env.example |
|
|
111
|
+
|
|
112
|
+
### Step 5 -- Update agent context
|
|
113
|
+
|
|
114
|
+
Invoke the Tech Lead agent using Task tool with `model: "sonnet"` (execution tier):
|
|
115
|
+
|
|
116
|
+
- For each agent in `.claude/agents/*.md`, update the `agent-context` zone
|
|
117
|
+
- If no `agent-context` zone exists, append one at the bottom
|
|
118
|
+
- Preserve everything outside the zone (role definition, process, rules)
|
|
119
|
+
|
|
120
|
+
### Step 6 -- Confirm changes
|
|
121
|
+
|
|
122
|
+
Present a summary:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
Re-specialization complete for [project-name]
|
|
126
|
+
|
|
127
|
+
Zones updated:
|
|
128
|
+
- structure: [changed/unchanged]
|
|
129
|
+
- architecture: [changed/unchanged]
|
|
130
|
+
- conventions: [changed/unchanged]
|
|
131
|
+
- env-vars: [changed/unchanged]
|
|
132
|
+
|
|
133
|
+
Agents updated: [count] of [total]
|
|
134
|
+
|
|
135
|
+
Review the changes above. Confirm to commit.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Step 7 -- Commit
|
|
139
|
+
|
|
140
|
+
Commit all changes as an atomic commit:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
git add CLAUDE.md .claude/agents/*.md
|
|
144
|
+
git commit -m "chore: re-specialize via guild-re-specialize"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Important Notes
|
|
148
|
+
|
|
149
|
+
- NEVER modify content outside of zone markers
|
|
150
|
+
- NEVER read real `.env` files -- only `.env.example`
|
|
151
|
+
- If a zone's content hasn't changed, skip it (no-op)
|
|
152
|
+
- Present diffs before applying changes
|
|
153
|
+
- User confirmation is required before committing
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd
|
|
3
|
+
description: "Discipline skill — TDD red-green-refactor cycle. Use when implementing any feature or bugfix, before writing implementation code."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Test-Driven Development (TDD)
|
|
8
|
+
|
|
9
|
+
Write the test first. Watch it fail. Write minimal code to pass.
|
|
10
|
+
|
|
11
|
+
**Core principle:** If you didn't watch the test fail, you don't know if it tests the right thing.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
`/tdd`
|
|
16
|
+
|
|
17
|
+
Invoke this skill before implementing any feature or bugfix. It establishes the discipline for your implementation session.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
- New features
|
|
22
|
+
- Bug fixes
|
|
23
|
+
- Refactoring
|
|
24
|
+
- Behavior changes
|
|
25
|
+
|
|
26
|
+
**Exceptions (ask the user):** throwaway prototypes, generated code, configuration files.
|
|
27
|
+
|
|
28
|
+
## The Iron Law
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Write code before the test? Delete it. Start over.
|
|
35
|
+
|
|
36
|
+
- Don't keep it as "reference"
|
|
37
|
+
- Don't "adapt" it while writing tests
|
|
38
|
+
- Don't look at it
|
|
39
|
+
- Delete means delete
|
|
40
|
+
|
|
41
|
+
## Red-Green-Refactor
|
|
42
|
+
|
|
43
|
+
### RED - Write Failing Test
|
|
44
|
+
|
|
45
|
+
Write one minimal test showing what should happen.
|
|
46
|
+
|
|
47
|
+
**Requirements:**
|
|
48
|
+
|
|
49
|
+
- One behavior per test
|
|
50
|
+
- Clear name that describes behavior
|
|
51
|
+
- Real code (no mocks unless unavoidable)
|
|
52
|
+
|
|
53
|
+
**Run the test. Confirm:**
|
|
54
|
+
|
|
55
|
+
- Test fails (not errors)
|
|
56
|
+
- Failure message is expected
|
|
57
|
+
- Fails because feature missing (not typos)
|
|
58
|
+
|
|
59
|
+
Test passes? You're testing existing behavior. Fix the test.
|
|
60
|
+
|
|
61
|
+
### GREEN - Minimal Code
|
|
62
|
+
|
|
63
|
+
Write the simplest code to pass the test.
|
|
64
|
+
|
|
65
|
+
Don't add features, refactor other code, or "improve" beyond the test.
|
|
66
|
+
|
|
67
|
+
**Run the test. Confirm:**
|
|
68
|
+
|
|
69
|
+
- Test passes
|
|
70
|
+
- Other tests still pass
|
|
71
|
+
- Output pristine (no errors, warnings)
|
|
72
|
+
|
|
73
|
+
Test fails? Fix code, not test.
|
|
74
|
+
|
|
75
|
+
### REFACTOR - Clean Up
|
|
76
|
+
|
|
77
|
+
After green only:
|
|
78
|
+
|
|
79
|
+
- Remove duplication
|
|
80
|
+
- Improve names
|
|
81
|
+
- Extract helpers
|
|
82
|
+
|
|
83
|
+
Keep tests green. Don't add behavior.
|
|
84
|
+
|
|
85
|
+
### Repeat
|
|
86
|
+
|
|
87
|
+
Next failing test for next behavior.
|
|
88
|
+
|
|
89
|
+
## Good Tests
|
|
90
|
+
|
|
91
|
+
| Quality | Good | Bad |
|
|
92
|
+
| ---------------- | ----------------------------------- | --------------------------------------------------- |
|
|
93
|
+
| **Minimal** | One thing. "and" in name? Split it. | `test('validates email and domain and whitespace')` |
|
|
94
|
+
| **Clear** | Name describes behavior | `test('test1')` |
|
|
95
|
+
| **Shows intent** | Demonstrates desired API | Obscures what code should do |
|
|
96
|
+
|
|
97
|
+
## Common Rationalizations
|
|
98
|
+
|
|
99
|
+
| Excuse | Reality |
|
|
100
|
+
| -------------------------------- | ----------------------------------------------------------------------- |
|
|
101
|
+
| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |
|
|
102
|
+
| "I'll test after" | Tests passing immediately prove nothing. |
|
|
103
|
+
| "Tests after achieve same goals" | Tests-after = "what does this do?" Tests-first = "what should this do?" |
|
|
104
|
+
| "Already manually tested" | Ad-hoc is not systematic. No record, can't re-run. |
|
|
105
|
+
| "Deleting X hours is wasteful" | Sunk cost fallacy. Keeping unverified code is technical debt. |
|
|
106
|
+
| "Need to explore first" | Fine. Throw away exploration, start with TDD. |
|
|
107
|
+
| "Test hard = design unclear" | Listen to the test. Hard to test = hard to use. |
|
|
108
|
+
| "TDD will slow me down" | TDD is faster than debugging. |
|
|
109
|
+
|
|
110
|
+
## Red Flags - STOP and Start Over
|
|
111
|
+
|
|
112
|
+
- Code before test
|
|
113
|
+
- Test after implementation
|
|
114
|
+
- Test passes immediately
|
|
115
|
+
- Can't explain why test failed
|
|
116
|
+
- Rationalizing "just this once"
|
|
117
|
+
- "I already manually tested it"
|
|
118
|
+
- "Keep as reference"
|
|
119
|
+
|
|
120
|
+
**All of these mean: Delete code. Start over with TDD.**
|
|
121
|
+
|
|
122
|
+
## Bug Fix Flow
|
|
123
|
+
|
|
124
|
+
1. Write failing test reproducing the bug
|
|
125
|
+
2. Verify RED (test fails as expected)
|
|
126
|
+
3. Implement minimal fix
|
|
127
|
+
4. Verify GREEN (test passes, all tests pass)
|
|
128
|
+
5. Refactor if needed
|
|
129
|
+
|
|
130
|
+
Never fix bugs without a test.
|
|
131
|
+
|
|
132
|
+
## When Stuck
|
|
133
|
+
|
|
134
|
+
| Problem | Solution |
|
|
135
|
+
| ------------------------ | ----------------------------------------------------- |
|
|
136
|
+
| Don't know how to test | Write wished-for API. Write assertion first. |
|
|
137
|
+
| Test too complicated | Design too complicated. Simplify interface. |
|
|
138
|
+
| Must mock everything | Code too coupled. Use dependency injection. |
|
|
139
|
+
| Test setup huge | Extract helpers. Still complex? Simplify design. |
|
|
140
|
+
|
|
141
|
+
## Verification Checklist
|
|
142
|
+
|
|
143
|
+
Before marking work complete:
|
|
144
|
+
|
|
145
|
+
- [ ] Every new function/method has a test
|
|
146
|
+
- [ ] Watched each test fail before implementing
|
|
147
|
+
- [ ] Each test failed for expected reason
|
|
148
|
+
- [ ] Wrote minimal code to pass each test
|
|
149
|
+
- [ ] All tests pass
|
|
150
|
+
- [ ] Output pristine (no errors, warnings)
|
|
151
|
+
- [ ] Tests use real code (mocks only if unavoidable)
|
|
152
|
+
- [ ] Edge cases and errors covered
|
|
153
|
+
|
|
154
|
+
Can't check all boxes? You skipped TDD. Start over.
|
|
155
|
+
|
|
156
|
+
## Related Skills
|
|
157
|
+
|
|
158
|
+
- `/debug` — systematic debugging when tests reveal unexpected failures
|
|
159
|
+
- `/verify` — verification before claiming work is complete
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify
|
|
3
|
+
description: "Discipline skill — verification before completion. Use when about to claim work is complete, fixed, or passing, before committing or creating PRs."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Verification Before Completion
|
|
8
|
+
|
|
9
|
+
Claiming work is complete without verification is dishonesty, not efficiency.
|
|
10
|
+
|
|
11
|
+
**Core principle:** Evidence before claims, always.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
`/verify`
|
|
16
|
+
|
|
17
|
+
Invoke this skill before claiming any work is done, before committing, and before creating PRs.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
**ALWAYS before:**
|
|
22
|
+
|
|
23
|
+
- Any success or completion claim
|
|
24
|
+
- Committing, pushing, or creating PRs
|
|
25
|
+
- Moving to the next task
|
|
26
|
+
- Expressing satisfaction about work state
|
|
27
|
+
|
|
28
|
+
## The Iron Law
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If you haven't run the verification command in this step, you cannot claim it passes.
|
|
35
|
+
|
|
36
|
+
## The Gate Function
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
BEFORE claiming any status:
|
|
40
|
+
|
|
41
|
+
1. IDENTIFY: What command proves this claim?
|
|
42
|
+
2. RUN: Execute the FULL command (fresh, complete)
|
|
43
|
+
3. READ: Full output, check exit code, count failures
|
|
44
|
+
4. VERIFY: Does output confirm the claim?
|
|
45
|
+
- If NO: State actual status with evidence
|
|
46
|
+
- If YES: State claim WITH evidence
|
|
47
|
+
5. ONLY THEN: Make the claim
|
|
48
|
+
|
|
49
|
+
Skip any step = lying, not verifying
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## What Each Claim Requires
|
|
53
|
+
|
|
54
|
+
| Claim | Requires | Not Sufficient |
|
|
55
|
+
| ---------------- | ------------------------------- | ------------------------------ |
|
|
56
|
+
| Tests pass | Test command output: 0 failures | Previous run, "should pass" |
|
|
57
|
+
| Linter clean | Linter output: 0 errors | Partial check, extrapolation |
|
|
58
|
+
| Build succeeds | Build command: exit 0 | Linter passing, logs look good |
|
|
59
|
+
| Bug fixed | Test original symptom: passes | Code changed, assumed fixed |
|
|
60
|
+
| Regression test | Red-green cycle verified | Test passes once |
|
|
61
|
+
| Requirements met | Line-by-line checklist | Tests passing |
|
|
62
|
+
|
|
63
|
+
## Red Flags - STOP
|
|
64
|
+
|
|
65
|
+
- Using "should", "probably", "seems to"
|
|
66
|
+
- Expressing satisfaction before verification ("Great!", "Perfect!", "Done!")
|
|
67
|
+
- About to commit/push/PR without verification
|
|
68
|
+
- Relying on partial verification
|
|
69
|
+
- Thinking "just this once"
|
|
70
|
+
- ANY wording implying success without having run verification
|
|
71
|
+
|
|
72
|
+
## Common Rationalizations
|
|
73
|
+
|
|
74
|
+
| Excuse | Reality |
|
|
75
|
+
| ---------------------------- | ------------------------------ |
|
|
76
|
+
| "Should work now" | RUN the verification |
|
|
77
|
+
| "I'm confident" | Confidence is not evidence |
|
|
78
|
+
| "Just this once" | No exceptions |
|
|
79
|
+
| "Linter passed" | Linter is not compiler |
|
|
80
|
+
| "Partial check is enough" | Partial proves nothing |
|
|
81
|
+
|
|
82
|
+
## Verification Patterns
|
|
83
|
+
|
|
84
|
+
**Tests:**
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
OK: [Run test command] [See: 34/34 pass] "All tests pass"
|
|
88
|
+
BAD: "Should pass now" / "Looks correct"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Build:**
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
OK: [Run build] [See: exit 0] "Build passes"
|
|
95
|
+
BAD: "Linter passed" (linter doesn't check compilation)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Requirements:**
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
OK: Re-read plan -> Create checklist -> Verify each -> Report gaps or completion
|
|
102
|
+
BAD: "Tests pass, phase complete"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## The Bottom Line
|
|
106
|
+
|
|
107
|
+
Run the command. Read the output. THEN claim the result.
|
|
108
|
+
|
|
109
|
+
No shortcuts. Non-negotiable.
|
|
110
|
+
|
|
111
|
+
## Related Skills
|
|
112
|
+
|
|
113
|
+
- `/tdd` — TDD ensures tests exist before claiming code works
|
|
114
|
+
- `/debug` — systematic debugging when verification reveals failures
|
package/src/utils/generators.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { writeFileSync } from 'fs';
|
|
6
|
+
import { wrapZone } from './zones.js';
|
|
7
|
+
import { generateWorkspaceContext } from './workspace.js';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Generates PROJECT.md with the onboarding data.
|
|
@@ -95,7 +97,10 @@ export function inferEnvVars(type, stack) {
|
|
|
95
97
|
/**
|
|
96
98
|
* Generates CLAUDE.md — central document with placeholders for guild-specialize.
|
|
97
99
|
*/
|
|
98
|
-
export async function generateClaudeMd(data) {
|
|
100
|
+
export async function generateClaudeMd(data, workspace = null, currentMemberName = null) {
|
|
101
|
+
const wsContext = generateWorkspaceContext(workspace, currentMemberName);
|
|
102
|
+
const workspaceSection = wsContext ? `\n${wsContext}\n` : '';
|
|
103
|
+
|
|
99
104
|
const content = `# ${data.name}
|
|
100
105
|
|
|
101
106
|
## Framework
|
|
@@ -105,20 +110,17 @@ This project uses Guild. Read SESSION.md at the start of each session.
|
|
|
105
110
|
${data.stack}
|
|
106
111
|
|
|
107
112
|
## Project structure
|
|
108
|
-
[PENDING: guild-specialize]
|
|
109
|
-
|
|
110
|
-
docs/
|
|
111
|
-
specs/ # Design documents (SDD specs)
|
|
113
|
+
${wrapZone('structure', '[PENDING: guild-specialize]\n\ndocs/\n specs/ # Design documents (SDD specs)')}
|
|
112
114
|
|
|
113
115
|
## Code conventions
|
|
114
|
-
${inferCodeConventions(data.type, data.stack)}
|
|
116
|
+
${wrapZone('conventions', inferCodeConventions(data.type, data.stack))}
|
|
115
117
|
|
|
116
118
|
## Architecture patterns
|
|
117
|
-
[PENDING: guild-specialize]
|
|
119
|
+
${wrapZone('architecture', '[PENDING: guild-specialize]')}
|
|
118
120
|
|
|
119
121
|
## Environment variables
|
|
120
|
-
${inferEnvVars(data.type, data.stack)}
|
|
121
|
-
|
|
122
|
+
${wrapZone('env-vars', inferEnvVars(data.type, data.stack))}
|
|
123
|
+
${workspaceSection}
|
|
122
124
|
## Global rules
|
|
123
125
|
- Do not implement without an approved plan
|
|
124
126
|
- Update SESSION.md at the end of each session
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
|
+
import { join, dirname, resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
export const WORKSPACE_FILE = 'guild-workspace.json';
|
|
5
|
+
|
|
6
|
+
export function findWorkspaceRoot(startDir = process.cwd()) {
|
|
7
|
+
let dir = resolve(startDir);
|
|
8
|
+
while (true) {
|
|
9
|
+
if (existsSync(join(dir, WORKSPACE_FILE))) {
|
|
10
|
+
return dir;
|
|
11
|
+
}
|
|
12
|
+
const parent = dirname(dir);
|
|
13
|
+
if (parent === dir) return null;
|
|
14
|
+
dir = parent;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function loadWorkspace(startDir = process.cwd()) {
|
|
19
|
+
const root = findWorkspaceRoot(startDir);
|
|
20
|
+
if (!root) return null;
|
|
21
|
+
|
|
22
|
+
const filePath = join(root, WORKSPACE_FILE);
|
|
23
|
+
const raw = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
...raw,
|
|
27
|
+
root,
|
|
28
|
+
members: (raw.members || []).map(m => ({
|
|
29
|
+
...m,
|
|
30
|
+
absolutePath: resolve(root, m.path),
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveWorkspaceAgents(workspace, localAgentsDir) {
|
|
36
|
+
const agents = new Map();
|
|
37
|
+
|
|
38
|
+
if (workspace?.shared?.agents) {
|
|
39
|
+
const sharedDir = resolve(workspace.root, workspace.shared.agents);
|
|
40
|
+
if (existsSync(sharedDir)) {
|
|
41
|
+
for (const file of readdirSync(sharedDir).filter(f => f.endsWith('.md'))) {
|
|
42
|
+
const name = file.replace('.md', '');
|
|
43
|
+
agents.set(name, { name, path: join(sharedDir, file), source: 'workspace' });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (existsSync(localAgentsDir)) {
|
|
49
|
+
for (const file of readdirSync(localAgentsDir).filter(f => f.endsWith('.md'))) {
|
|
50
|
+
const name = file.replace('.md', '');
|
|
51
|
+
agents.set(name, { name, path: join(localAgentsDir, file), source: 'local' });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return [...agents.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function generateWorkspaceContext(workspace, currentMemberName) {
|
|
59
|
+
if (!workspace) return '';
|
|
60
|
+
|
|
61
|
+
const otherMembers = workspace.members.filter(m => m.name !== currentMemberName);
|
|
62
|
+
if (otherMembers.length === 0) return '';
|
|
63
|
+
|
|
64
|
+
const lines = [
|
|
65
|
+
'## Workspace context',
|
|
66
|
+
`- **Workspace:** ${workspace.name}`,
|
|
67
|
+
`- **Members:** ${workspace.members.map(m => m.name === currentMemberName ? `${m.name} (this)` : m.name).join(', ')}`,
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
for (const member of otherMembers) {
|
|
71
|
+
const projectMdPath = join(member.absolutePath, 'PROJECT.md');
|
|
72
|
+
if (existsSync(projectMdPath)) {
|
|
73
|
+
const content = readFileSync(projectMdPath, 'utf8');
|
|
74
|
+
const stackMatch = content.match(/\*\*Stack:\*\*\s*(.+)/);
|
|
75
|
+
if (stackMatch) {
|
|
76
|
+
lines.push(`- **${member.name} stack:** ${stackMatch[1].trim()}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return lines.join('\n');
|
|
82
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const MARKER_START = 'guild:auto-start';
|
|
2
|
+
const MARKER_END = 'guild:auto-end';
|
|
3
|
+
|
|
4
|
+
export function wrapZone(zoneId, content) {
|
|
5
|
+
const trimmed = content.endsWith('\n') ? content.slice(0, -1) : content;
|
|
6
|
+
return `<!-- ${MARKER_START}:${zoneId} -->\n${trimmed}\n<!-- ${MARKER_END}:${zoneId} -->`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function extractZones(content) {
|
|
10
|
+
const zones = new Map();
|
|
11
|
+
const startPattern = /^<!-- guild:auto-start:(\S+) -->$/gm;
|
|
12
|
+
let match;
|
|
13
|
+
|
|
14
|
+
while ((match = startPattern.exec(content)) !== null) {
|
|
15
|
+
const zoneId = match[1];
|
|
16
|
+
const contentStart = match.index + match[0].length + 1; // +1 for newline
|
|
17
|
+
const endMarker = `<!-- ${MARKER_END}:${zoneId} -->`;
|
|
18
|
+
const endIndex = content.indexOf(endMarker, contentStart);
|
|
19
|
+
if (endIndex === -1) continue;
|
|
20
|
+
|
|
21
|
+
zones.set(zoneId, {
|
|
22
|
+
start: match.index,
|
|
23
|
+
end: endIndex + endMarker.length,
|
|
24
|
+
content: content.slice(contentStart, endIndex - 1), // -1 to trim newline before end marker
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return zones;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function replaceZone(content, zoneId, newContent) {
|
|
32
|
+
const zones = extractZones(content);
|
|
33
|
+
if (!zones.has(zoneId)) return content;
|
|
34
|
+
|
|
35
|
+
const zone = zones.get(zoneId);
|
|
36
|
+
const before = content.slice(0, zone.start);
|
|
37
|
+
const after = content.slice(zone.end);
|
|
38
|
+
return before + wrapZone(zoneId, newContent) + after;
|
|
39
|
+
}
|