guild-agents 0.3.1 → 1.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/README.md +5 -1
- package/bin/guild.js +75 -1
- package/package.json +12 -5
- package/src/commands/doctor.js +70 -1
- package/src/commands/logs.js +63 -0
- package/src/commands/reset-learnings.js +44 -0
- package/src/commands/run.js +105 -0
- package/src/templates/agents/advisor.md +1 -0
- package/src/templates/agents/bugfix.md +1 -0
- package/src/templates/agents/code-reviewer.md +1 -0
- package/src/templates/agents/db-migration.md +1 -0
- package/src/templates/agents/developer.md +1 -0
- package/src/templates/agents/learnings-extractor.md +49 -0
- package/src/templates/agents/platform-expert.md +1 -0
- package/src/templates/agents/product-owner.md +1 -0
- package/src/templates/agents/qa.md +1 -0
- package/src/templates/agents/tech-lead.md +1 -0
- package/src/templates/skills/build-feature/SKILL.md +130 -26
- package/src/templates/skills/council/SKILL.md +51 -4
- package/src/templates/skills/create-pr/SKILL.md +32 -0
- package/src/templates/skills/dev-flow/SKILL.md +14 -0
- package/src/templates/skills/guild-specialize/SKILL.md +45 -3
- package/src/templates/skills/new-feature/SKILL.md +33 -0
- package/src/templates/skills/qa-cycle/SKILL.md +48 -5
- package/src/templates/skills/review/SKILL.md +22 -1
- package/src/templates/skills/session-end/SKILL.md +27 -0
- package/src/templates/skills/session-start/SKILL.md +32 -0
- package/src/templates/skills/status/SKILL.md +19 -0
- package/src/utils/dispatch-protocol.js +74 -0
- package/src/utils/dispatch.js +172 -0
- package/src/utils/executor.js +183 -0
- package/src/utils/learnings-io.js +76 -0
- package/src/utils/learnings.js +204 -0
- package/src/utils/orchestrator-io.js +356 -0
- package/src/utils/orchestrator.js +590 -0
- package/src/utils/providers/claude-code.js +43 -0
- package/src/utils/skill-loader.js +83 -0
- package/src/utils/trace.js +400 -0
- package/src/utils/version.js +90 -0
- package/src/utils/workflow-parser.js +225 -0
package/README.md
CHANGED
|
@@ -86,11 +86,14 @@ guild new-agent <name> # Create a custom agent
|
|
|
86
86
|
guild status # Show project status
|
|
87
87
|
guild doctor # Diagnose setup
|
|
88
88
|
guild list # List agents and skills
|
|
89
|
+
guild run <skill> # Preview a skill's execution plan (dry-run)
|
|
90
|
+
guild logs # View execution traces
|
|
91
|
+
guild logs clean # Remove old traces (--days N, --all)
|
|
89
92
|
```
|
|
90
93
|
|
|
91
94
|
## Under the Hood
|
|
92
95
|
|
|
93
|
-
Guild coordinates
|
|
96
|
+
Guild coordinates 10 specialized agents through the pipeline. Each agent handles one phase.
|
|
94
97
|
|
|
95
98
|
| Agent | Role |
|
|
96
99
|
| --- | --- |
|
|
@@ -103,6 +106,7 @@ Guild coordinates 9 specialized agents through the pipeline. Each agent handles
|
|
|
103
106
|
| bugfix | Bug diagnosis and resolution |
|
|
104
107
|
| db-migration | Schema changes and safe migrations |
|
|
105
108
|
| platform-expert | Diagnoses Claude Code integration issues |
|
|
109
|
+
| learnings-extractor | Extracts compound learnings from pipeline executions |
|
|
106
110
|
|
|
107
111
|
Agents are flat `.md` files with identity and expertise. Skills orchestrate agents through structured pipelines. Everything lives in `.claude/`, readable by humans, tracked by git.
|
|
108
112
|
|
package/bin/guild.js
CHANGED
|
@@ -14,14 +14,25 @@ import { program } from 'commander';
|
|
|
14
14
|
import { readFileSync } from 'fs';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
16
|
import { dirname, join } from 'path';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import { parseVersion, getPreReleaseWarning } from '../src/utils/version.js';
|
|
17
19
|
|
|
18
20
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
21
|
const pkg = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
20
22
|
|
|
23
|
+
const { channel } = parseVersion(pkg.version);
|
|
24
|
+
const prereleaseWarning = getPreReleaseWarning(pkg.version);
|
|
25
|
+
if (prereleaseWarning) {
|
|
26
|
+
const color = channel === 'snapshot' ? chalk.red : chalk.yellow;
|
|
27
|
+
console.error(color(`Guild v${pkg.version} -- ${prereleaseWarning}`));
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
program
|
|
22
31
|
.name('guild')
|
|
23
32
|
.description('Specification-driven development CLI for Claude Code')
|
|
24
|
-
.version(pkg.version)
|
|
33
|
+
.version(pkg.version)
|
|
34
|
+
.option('--verbose', 'Enable verbose logging (records decision trail)')
|
|
35
|
+
.option('--debug', 'Enable debug logging (records full prompts and responses)');
|
|
25
36
|
|
|
26
37
|
// guild init
|
|
27
38
|
program
|
|
@@ -94,4 +105,67 @@ program
|
|
|
94
105
|
}
|
|
95
106
|
});
|
|
96
107
|
|
|
108
|
+
// guild reset-learnings
|
|
109
|
+
program
|
|
110
|
+
.command('reset-learnings')
|
|
111
|
+
.description('Reset the compound learnings file')
|
|
112
|
+
.option('-f, --force', 'Skip confirmation prompt')
|
|
113
|
+
.action(async (options) => {
|
|
114
|
+
try {
|
|
115
|
+
const { runResetLearnings } = await import('../src/commands/reset-learnings.js');
|
|
116
|
+
await runResetLearnings(options);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.error(err.message);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// guild run
|
|
124
|
+
program
|
|
125
|
+
.command('run')
|
|
126
|
+
.description('Execute a skill workflow')
|
|
127
|
+
.argument('<skill>', 'Skill name to run')
|
|
128
|
+
.argument('[input]', 'Input text for the skill', '')
|
|
129
|
+
.option('--profile <profile>', 'Model profile (max, pro)', 'max')
|
|
130
|
+
.option('--dry-run', 'Display the execution plan without running it')
|
|
131
|
+
.action(async (skill, input, options) => {
|
|
132
|
+
try {
|
|
133
|
+
const { runRun } = await import('../src/commands/run.js');
|
|
134
|
+
await runRun(skill, input, options);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error(err.message);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// guild logs (list traces)
|
|
142
|
+
const logsCmd = program
|
|
143
|
+
.command('logs')
|
|
144
|
+
.description('View and manage execution traces')
|
|
145
|
+
.action(async (options) => {
|
|
146
|
+
try {
|
|
147
|
+
const { runLogs } = await import('../src/commands/logs.js');
|
|
148
|
+
await runLogs('list', options);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error(err.message);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// guild logs clean
|
|
156
|
+
logsCmd
|
|
157
|
+
.command('clean')
|
|
158
|
+
.description('Remove old execution traces')
|
|
159
|
+
.option('--days <days>', 'Remove traces older than N days', '30')
|
|
160
|
+
.option('--all', 'Remove all traces')
|
|
161
|
+
.action(async (options) => {
|
|
162
|
+
try {
|
|
163
|
+
const { runLogs } = await import('../src/commands/logs.js');
|
|
164
|
+
await runLogs('clean', options);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.error(err.message);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
97
171
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guild-agents",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Specification-driven development CLI for Claude Code — think before you build",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -17,11 +17,18 @@
|
|
|
17
17
|
"test": "vitest run",
|
|
18
18
|
"test:watch": "vitest",
|
|
19
19
|
"test:coverage": "vitest run --coverage",
|
|
20
|
-
"lint": "eslint src/ && markdownlint-cli2 'src/templates/**/*.md'",
|
|
21
|
-
"lint:js": "eslint src/",
|
|
20
|
+
"lint": "eslint src/ scripts/ && markdownlint-cli2 'src/templates/**/*.md'",
|
|
21
|
+
"lint:js": "eslint src/ scripts/",
|
|
22
22
|
"lint:md": "markdownlint-cli2 'src/templates/**/*.md'",
|
|
23
23
|
"dev": "node bin/guild.js",
|
|
24
|
-
"prepublishOnly": "npm test && npm run lint"
|
|
24
|
+
"prepublishOnly": "npm test && npm run lint",
|
|
25
|
+
"version:snapshot": "node scripts/version-snapshot.js",
|
|
26
|
+
"version:beta": "node scripts/version-beta.js",
|
|
27
|
+
"version:stable": "node scripts/version-stable.js",
|
|
28
|
+
"publish:snapshot": "npm run version:snapshot && npm publish --tag snapshot",
|
|
29
|
+
"publish:beta": "npm run version:beta && npm publish --tag beta",
|
|
30
|
+
"publish:stable": "npm run version:stable && npm publish --tag latest",
|
|
31
|
+
"publish:promote-beta": "npm dist-tag add guild-agents@$(node --input-type=commonjs -p \"require('./package.json').version\") beta"
|
|
25
32
|
},
|
|
26
33
|
"keywords": [
|
|
27
34
|
"claude",
|
|
@@ -54,7 +61,7 @@
|
|
|
54
61
|
"@clack/prompts": "^1.0.1",
|
|
55
62
|
"chalk": "^5.3.0",
|
|
56
63
|
"commander": "^14.0.3",
|
|
57
|
-
"
|
|
64
|
+
"yaml": "^2.8.2"
|
|
58
65
|
},
|
|
59
66
|
"engines": {
|
|
60
67
|
"node": ">=20.0.0"
|
package/src/commands/doctor.js
CHANGED
|
@@ -7,6 +7,7 @@ import chalk from 'chalk';
|
|
|
7
7
|
import { existsSync, readdirSync } from 'fs';
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import { resolveProjectRoot } from '../utils/files.js';
|
|
10
|
+
import { loadAllSkills } from '../utils/skill-loader.js';
|
|
10
11
|
|
|
11
12
|
export async function runDoctor() {
|
|
12
13
|
const root = resolveProjectRoot();
|
|
@@ -83,9 +84,77 @@ export async function runDoctor() {
|
|
|
83
84
|
healthy = false;
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
// Check workflow validation in skills
|
|
88
|
+
if (existsSync(skillsDir)) {
|
|
89
|
+
const skills = loadAllSkills(skillsDir);
|
|
90
|
+
let workflowCount = 0;
|
|
91
|
+
let workflowErrors = 0;
|
|
92
|
+
const errorDetails = [];
|
|
93
|
+
|
|
94
|
+
for (const [name, skill] of skills) {
|
|
95
|
+
if (skill.workflow) {
|
|
96
|
+
workflowCount++;
|
|
97
|
+
if (skill.errors.length > 0) {
|
|
98
|
+
workflowErrors++;
|
|
99
|
+
errorDetails.push(`${name}: ${skill.errors.join('; ')}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check that agent references exist
|
|
104
|
+
if (skill.workflow) {
|
|
105
|
+
for (const step of skill.workflow.steps) {
|
|
106
|
+
if (step.role !== 'system' && step.role !== 'dynamic') {
|
|
107
|
+
const agentPath = join(agentsDir, `${step.role}.md`);
|
|
108
|
+
if (!existsSync(agentPath)) {
|
|
109
|
+
errorDetails.push(`${name}: step "${step.id}" references agent "${step.role}" — agent not found`);
|
|
110
|
+
workflowErrors++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (workflowCount > 0 && workflowErrors === 0) {
|
|
118
|
+
checks.push({ name: `Workflows (${workflowCount} valid)`, pass: true });
|
|
119
|
+
} else if (workflowCount > 0 && workflowErrors > 0) {
|
|
120
|
+
checks.push({
|
|
121
|
+
name: `Workflows (${workflowErrors} issue(s))`,
|
|
122
|
+
pass: false,
|
|
123
|
+
fix: errorDetails.join('\n '),
|
|
124
|
+
});
|
|
125
|
+
healthy = false;
|
|
126
|
+
}
|
|
127
|
+
// If workflowCount === 0, don't add a check (no workflows to validate)
|
|
128
|
+
|
|
129
|
+
// Check for dual-format skills (workflow frontmatter + body step/phase headings)
|
|
130
|
+
// Matches "### Step 1", "## Phase 2", etc. — requires digit after Step/Phase
|
|
131
|
+
const STEP_PHASE_RE = /^#{1,3}\s+(Step|Phase)\s+\d/im;
|
|
132
|
+
const dualFormatWarnings = [];
|
|
133
|
+
|
|
134
|
+
for (const [name, skill] of skills) {
|
|
135
|
+
if (skill.workflow && skill.body && STEP_PHASE_RE.test(skill.body)) {
|
|
136
|
+
dualFormatWarnings.push(name);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (dualFormatWarnings.length > 0) {
|
|
141
|
+
checks.push({
|
|
142
|
+
name: `Dual-format skills (${dualFormatWarnings.length} warning(s))`,
|
|
143
|
+
pass: true,
|
|
144
|
+
warn: true,
|
|
145
|
+
detail: `Skills with both workflow frontmatter and body step/phase headings: ${dualFormatWarnings.join(', ')}. Workflow steps take precedence — consider removing prose steps from body.`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
86
150
|
// Display results
|
|
87
151
|
for (const check of checks) {
|
|
88
|
-
if (check.
|
|
152
|
+
if (check.warn) {
|
|
153
|
+
p.log.warn(`${chalk.yellow('⚠')} ${check.name}`);
|
|
154
|
+
if (check.detail) {
|
|
155
|
+
p.log.info(chalk.gray(` ${check.detail}`));
|
|
156
|
+
}
|
|
157
|
+
} else if (check.pass) {
|
|
89
158
|
p.log.success(`${chalk.green('✓')} ${check.name}`);
|
|
90
159
|
} else {
|
|
91
160
|
p.log.error(`${chalk.red('✗')} ${check.name}`);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* logs.js — View and manage Guild execution traces
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { ensureProjectRoot } from '../utils/files.js';
|
|
9
|
+
import { listTraces, cleanTraces } from '../utils/trace.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Runs the `guild logs` command.
|
|
13
|
+
*
|
|
14
|
+
* @param {'list'|'clean'} action - Subcommand to run
|
|
15
|
+
* @param {object} [options={}] - Options
|
|
16
|
+
* @param {string} [options.days='30'] - Days threshold for clean
|
|
17
|
+
* @param {boolean} [options.all] - Remove all traces
|
|
18
|
+
*/
|
|
19
|
+
export async function runLogs(action, options = {}) {
|
|
20
|
+
const root = ensureProjectRoot();
|
|
21
|
+
const tracesDir = join(root, '.claude', 'guild', 'traces');
|
|
22
|
+
|
|
23
|
+
if (action === 'clean') {
|
|
24
|
+
p.intro(chalk.bold.cyan('Guild — Clean Traces'));
|
|
25
|
+
|
|
26
|
+
let removed;
|
|
27
|
+
if (options.all) {
|
|
28
|
+
removed = cleanTraces(0, tracesDir);
|
|
29
|
+
} else {
|
|
30
|
+
const days = parseInt(options.days || '30', 10);
|
|
31
|
+
removed = cleanTraces(days, tracesDir);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
p.log.info(chalk.gray(`Removed ${removed} trace(s).`));
|
|
35
|
+
p.outro('');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Default: list traces
|
|
40
|
+
p.intro(chalk.bold.cyan('Guild — Traces'));
|
|
41
|
+
|
|
42
|
+
const traces = listTraces(tracesDir);
|
|
43
|
+
|
|
44
|
+
if (traces.length === 0) {
|
|
45
|
+
p.log.info(chalk.gray('No traces found.'));
|
|
46
|
+
p.outro('');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const trace of traces) {
|
|
51
|
+
const date = trace.date !== 'unknown' ? new Date(trace.date).toLocaleString() : 'unknown';
|
|
52
|
+
const result = trace.result === 'pass' ? chalk.green('pass') :
|
|
53
|
+
trace.result === 'fail' ? chalk.red('fail') :
|
|
54
|
+
chalk.gray(trace.result || 'unknown');
|
|
55
|
+
|
|
56
|
+
p.log.info(` ${chalk.white.bold(trace.workflow)} ${result}`);
|
|
57
|
+
p.log.info(chalk.gray(` ${date} | level: ${trace.level}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
p.log.info('');
|
|
61
|
+
p.log.info(chalk.gray(`Total: ${traces.length} trace(s)`));
|
|
62
|
+
p.outro('');
|
|
63
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* reset-learnings.js — Resets the compound learnings file
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { ensureProjectRoot } from '../utils/files.js';
|
|
9
|
+
import { learningsExist, deleteLearnings } from '../utils/learnings-io.js';
|
|
10
|
+
import { GUILD_LEARNINGS_PATH } from '../utils/learnings.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Runs the `guild reset-learnings` command.
|
|
14
|
+
* @param {object} [options]
|
|
15
|
+
* @param {boolean} [options.force] - Skip confirmation prompt
|
|
16
|
+
*/
|
|
17
|
+
export async function runResetLearnings(options = {}) {
|
|
18
|
+
const root = ensureProjectRoot();
|
|
19
|
+
const filePath = join(root, GUILD_LEARNINGS_PATH);
|
|
20
|
+
|
|
21
|
+
p.intro(chalk.bold.cyan('Guild — Reset Learnings'));
|
|
22
|
+
|
|
23
|
+
if (!learningsExist(filePath)) {
|
|
24
|
+
p.log.info('No learnings file found. Nothing to reset.');
|
|
25
|
+
p.outro('Done.');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!options.force) {
|
|
30
|
+
const confirmed = await p.confirm({
|
|
31
|
+
message: 'This will delete all accumulated learnings. Continue?',
|
|
32
|
+
initialValue: false,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
36
|
+
p.cancel('Reset cancelled.');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
deleteLearnings(filePath);
|
|
42
|
+
p.log.success(`${chalk.green('✓')} Learnings file deleted.`);
|
|
43
|
+
p.outro('Learnings have been reset. They will be regenerated on the next workflow execution.');
|
|
44
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* run.js — Executes a skill workflow (or displays the plan in dry-run mode)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { ensureProjectRoot } from '../utils/files.js';
|
|
8
|
+
import { orchestrate, finalizeWorkflowTrace } from '../utils/orchestrator-io.js';
|
|
9
|
+
import { execute } from '../utils/executor.js';
|
|
10
|
+
import { createClaudeCodeProvider } from '../utils/providers/claude-code.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Displays the execution plan without running it.
|
|
14
|
+
* @param {object} plan
|
|
15
|
+
* @param {object} dispatchInfoMap
|
|
16
|
+
*/
|
|
17
|
+
function displayPlan(plan, dispatchInfoMap) {
|
|
18
|
+
for (let i = 0; i < plan.groups.length; i++) {
|
|
19
|
+
const group = plan.groups[i];
|
|
20
|
+
const label = group.parallel ? `Group ${i + 1} (parallel)` : `Group ${i + 1}`;
|
|
21
|
+
p.log.step(chalk.bold(label));
|
|
22
|
+
|
|
23
|
+
for (const step of group.steps) {
|
|
24
|
+
const dispatch = dispatchInfoMap[step.id] || {};
|
|
25
|
+
const roleLabel = step.role === 'system' ? chalk.yellow('system') : chalk.blue(step.role);
|
|
26
|
+
const modelLabel = dispatch.model ? chalk.gray(` → ${dispatch.model}`) : '';
|
|
27
|
+
const gateLabel = step.gate ? chalk.red(' GATE') : '';
|
|
28
|
+
|
|
29
|
+
p.log.info(` ${chalk.white.bold(step.id)} ${roleLabel}${modelLabel}${gateLabel}`);
|
|
30
|
+
p.log.info(chalk.gray(` ${step.intent}`));
|
|
31
|
+
|
|
32
|
+
if (step.requires && step.requires.length > 0) {
|
|
33
|
+
p.log.info(chalk.gray(` requires: ${step.requires.join(', ')}`));
|
|
34
|
+
}
|
|
35
|
+
if (step.produces && step.produces.length > 0) {
|
|
36
|
+
p.log.info(chalk.gray(` produces: ${step.produces.join(', ')}`));
|
|
37
|
+
}
|
|
38
|
+
if (step.condition) {
|
|
39
|
+
p.log.info(chalk.gray(` condition: ${step.condition}`));
|
|
40
|
+
}
|
|
41
|
+
if (step.commands && step.commands.length > 0) {
|
|
42
|
+
p.log.info(chalk.gray(` commands: ${step.commands.join(', ')}`));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Runs the `guild run <skill>` command.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} skillName - Name of the skill to run
|
|
52
|
+
* @param {string} [input=''] - Optional input text for the skill
|
|
53
|
+
* @param {object} [options={}] - Options
|
|
54
|
+
* @param {string} [options.profile='max'] - Model profile
|
|
55
|
+
* @param {boolean} [options.dryRun=false] - Show plan without executing
|
|
56
|
+
*/
|
|
57
|
+
export async function runRun(skillName, input = '', options = {}) {
|
|
58
|
+
const root = ensureProjectRoot();
|
|
59
|
+
const { profile = 'max', dryRun = false } = options;
|
|
60
|
+
|
|
61
|
+
p.intro(chalk.bold.cyan(' Guild — Run: ' + skillName + ' '));
|
|
62
|
+
|
|
63
|
+
const { plan, trace, dispatchInfoMap } = await orchestrate(skillName, input, {
|
|
64
|
+
profile,
|
|
65
|
+
projectRoot: root,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
p.log.info(chalk.gray(`Profile: ${profile} | Steps: ${plan.totalSteps}`));
|
|
69
|
+
|
|
70
|
+
if (dryRun) {
|
|
71
|
+
displayPlan(plan, dispatchInfoMap);
|
|
72
|
+
p.outro(chalk.gray('Plan generated (dry-run). No steps were executed.'));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Real execution
|
|
77
|
+
const provider = createClaudeCodeProvider({ projectRoot: root });
|
|
78
|
+
|
|
79
|
+
const finalPlan = await execute(plan, dispatchInfoMap, {
|
|
80
|
+
provider,
|
|
81
|
+
skillBody: input,
|
|
82
|
+
trace,
|
|
83
|
+
projectRoot: root,
|
|
84
|
+
|
|
85
|
+
onStepStart(step, dispatch) {
|
|
86
|
+
const roleLabel = step.role === 'system' ? chalk.yellow('system') : chalk.blue(step.role);
|
|
87
|
+
const modelLabel = dispatch.model ? chalk.gray(` (${dispatch.model})`) : '';
|
|
88
|
+
p.log.step(`${chalk.bold(step.id)} ${roleLabel}${modelLabel}`);
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
onStepEnd(step, result) {
|
|
92
|
+
const icon = result.status === 'passed' ? chalk.green('✓') : chalk.red('✗');
|
|
93
|
+
p.log.info(` ${icon} ${result.status}`);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Finalize trace
|
|
98
|
+
const { executionSummary } = finalizeWorkflowTrace(trace, finalPlan);
|
|
99
|
+
|
|
100
|
+
const statusLabel = finalPlan.status === 'completed'
|
|
101
|
+
? chalk.green('completed')
|
|
102
|
+
: chalk.red(finalPlan.status);
|
|
103
|
+
|
|
104
|
+
p.outro(`${statusLabel} | ${executionSummary}`);
|
|
105
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: learnings-extractor
|
|
3
|
+
description: "Extracts compound learnings from pipeline executions"
|
|
4
|
+
tools: Read, Glob, Grep
|
|
5
|
+
permissionMode: plan
|
|
6
|
+
default-tier: routine
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Learnings Extractor
|
|
10
|
+
|
|
11
|
+
You are the Learnings Extractor for [PROJECT]. Your job is to analyze completed pipeline traces and extract reusable patterns, recurring issues, and optimization opportunities that improve future executions.
|
|
12
|
+
|
|
13
|
+
## Responsibilities
|
|
14
|
+
|
|
15
|
+
- Read pipeline trace files from `.claude/guild/traces/`
|
|
16
|
+
- Identify patterns across multiple executions (recurring review loops, common failures)
|
|
17
|
+
- Extract actionable learnings that reduce future iteration count
|
|
18
|
+
- Update `.claude/guild/learnings.md` with new findings
|
|
19
|
+
- Prioritize learnings by impact (reduces tokens, reduces loops, improves quality)
|
|
20
|
+
|
|
21
|
+
## What you do NOT do
|
|
22
|
+
|
|
23
|
+
- You do not implement features — that is the Developer's role
|
|
24
|
+
- You do not evaluate strategy — that is the Advisor's role
|
|
25
|
+
- You do not modify source code — you only read traces and write learnings
|
|
26
|
+
|
|
27
|
+
## Process
|
|
28
|
+
|
|
29
|
+
1. Read the pipeline trace provided as input
|
|
30
|
+
2. Analyze step results, loop counts, and failure patterns
|
|
31
|
+
3. Compare with existing learnings in `.claude/guild/learnings.md`
|
|
32
|
+
4. Extract new learnings or reinforce existing ones
|
|
33
|
+
5. Write updated learnings in a structured format
|
|
34
|
+
|
|
35
|
+
## Output format
|
|
36
|
+
|
|
37
|
+
Each learning should include:
|
|
38
|
+
|
|
39
|
+
- **Pattern**: What was observed
|
|
40
|
+
- **Frequency**: How often it occurs (first-seen or recurring)
|
|
41
|
+
- **Recommendation**: What to do differently next time
|
|
42
|
+
- **Impact**: Expected reduction in loops, tokens, or failures
|
|
43
|
+
|
|
44
|
+
## Behavior rules
|
|
45
|
+
|
|
46
|
+
- Never duplicate an existing learning — update its frequency instead
|
|
47
|
+
- Focus on actionable recommendations, not observations
|
|
48
|
+
- Keep learnings concise — one paragraph maximum per entry
|
|
49
|
+
- Tag learnings with the workflow they came from
|
|
@@ -3,6 +3,7 @@ name: platform-expert
|
|
|
3
3
|
description: "Diagnoses and resolves Claude Code integration issues -- permissions, subagents, hooks, settings"
|
|
4
4
|
tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
5
|
permissionMode: bypassPermissions
|
|
6
|
+
default-tier: execution
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Platform Expert
|