@guidobuilds/forge-ai 0.1.0 → 0.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 -6
- package/dist/src/cli.js +40 -6
- package/dist/src/manifest.js +101 -0
- package/dist/src/processor.js +5 -4
- package/package.json +1 -1
- package/skills/forge-grill/SKILL.md +92 -0
- package/skills/forge-build/SKILL.md +0 -80
- package/skills/forge-design/SKILL.md +0 -104
- package/skills/forge-explore/SKILL.md +0 -65
- package/skills/forge-helper/SKILL.md +0 -46
- package/skills/forge-plan/SKILL.md +0 -81
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ The same operating model is shared across all supported agents so the workflow s
|
|
|
77
77
|
The primary installer is the npm CLI:
|
|
78
78
|
|
|
79
79
|
```sh
|
|
80
|
-
npx forge-ai install
|
|
80
|
+
npx @guidobuilds/forge-ai install
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
The installer prompts for the target agent platform and whether Forge should be installed globally for your user or locally for the current project.
|
|
@@ -85,25 +85,25 @@ The installer prompts for the target agent platform and whether Forge should be
|
|
|
85
85
|
To update an existing install:
|
|
86
86
|
|
|
87
87
|
```sh
|
|
88
|
-
npx forge-ai update
|
|
88
|
+
npx @guidobuilds/forge-ai update
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
For non-interactive environments:
|
|
92
92
|
|
|
93
93
|
```sh
|
|
94
|
-
npx forge-ai install --platform all --scope user --yes
|
|
94
|
+
npx @guidobuilds/forge-ai install --platform all --scope user --yes
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
Preview the files without writing them:
|
|
98
98
|
|
|
99
99
|
```sh
|
|
100
|
-
npx forge-ai install --platform all --scope user --dry-run
|
|
100
|
+
npx @guidobuilds/forge-ai install --platform all --scope user --dry-run
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
Validate a local Forge source tree:
|
|
104
104
|
|
|
105
105
|
```sh
|
|
106
|
-
npx forge-ai validate --source .
|
|
106
|
+
npx @guidobuilds/forge-ai validate --source .
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
## Local Development
|
|
@@ -127,11 +127,13 @@ node bin/forge-ai.mjs install --source . --platform all --scope project --dry-ru
|
|
|
127
127
|
Run the npm updater:
|
|
128
128
|
|
|
129
129
|
```sh
|
|
130
|
-
npx forge-ai update
|
|
130
|
+
npx @guidobuilds/forge-ai update
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
Forge replaces its managed agent and skill definitions in your supported agent configuration directories.
|
|
134
134
|
|
|
135
|
+
Forge records installed files in manifests under `~/.forge-ai/` so updates can safely remove files that are no longer bundled. `update` prunes stale managed files by default only when the current file still matches the recorded checksum; use `--no-prune` to keep stale managed files. `--dry-run` previews writes and deletes without changing files or manifests.
|
|
136
|
+
|
|
135
137
|
## Uninstalling
|
|
136
138
|
|
|
137
139
|
Remove Forge from the agent configuration directories for OpenCode, Codex, or Claude Code by deleting the installed Forge agent and skill entries.
|
package/dist/src/cli.js
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
3
|
import pc from 'picocolors';
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
5
6
|
import path from 'node:path';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
7
8
|
import { formatDiagnostic, hasErrors } from './diagnostics.js';
|
|
9
|
+
import { buildManifest, classifyPruneEntries, loadManifest, pruneEntries, resolveManifestLocation, saveManifest, staleEntries } from './manifest.js';
|
|
8
10
|
import { buildWritePlan, parsePlatform, parseScope } from './processor.js';
|
|
9
11
|
import { writeOutputs } from './writer.js';
|
|
10
12
|
export async function main(argv = process.argv.slice(2), promptIO = {}) {
|
|
@@ -44,7 +46,9 @@ export async function main(argv = process.argv.slice(2), promptIO = {}) {
|
|
|
44
46
|
return 1;
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
|
-
|
|
49
|
+
const cwd = process.cwd();
|
|
50
|
+
const home = resolveHome(promptIO);
|
|
51
|
+
let plan = await buildWritePlan({ source: options.source, platform: options.platform, scope: options.scope, cwd, home, checkCollisions: install && !options.dryRun, force: options.force });
|
|
48
52
|
if (install && !options.dryRun && !options.force && canOfferUpdate(plan.diagnostics)) {
|
|
49
53
|
const accepted = await promptForUpdate(plan, promptIO);
|
|
50
54
|
if (accepted === undefined) {
|
|
@@ -54,10 +58,18 @@ export async function main(argv = process.argv.slice(2), promptIO = {}) {
|
|
|
54
58
|
}
|
|
55
59
|
if (accepted) {
|
|
56
60
|
options.force = true;
|
|
57
|
-
plan = await buildWritePlan({ source: options.source, platform: options.platform, scope: options.scope, checkCollisions: true, force: true });
|
|
61
|
+
plan = await buildWritePlan({ source: options.source, platform: options.platform, scope: options.scope, cwd, home, checkCollisions: true, force: true });
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
|
-
|
|
64
|
+
let prunePlan = { deletable: [], skipped: [] };
|
|
65
|
+
let manifestLocation;
|
|
66
|
+
if (install) {
|
|
67
|
+
manifestLocation = await resolveManifestLocation(options.scope, cwd, home);
|
|
68
|
+
const oldManifest = await loadManifest(manifestLocation.manifestPath);
|
|
69
|
+
if (command === 'update' && options.prune)
|
|
70
|
+
prunePlan = await classifyPruneEntries(staleEntries(oldManifest, plan.files));
|
|
71
|
+
}
|
|
72
|
+
printPlan(command, plan.sourceCount, plan.files, plan.diagnostics, prunePlan);
|
|
61
73
|
if (hasErrors(plan.diagnostics)) {
|
|
62
74
|
if (interactive)
|
|
63
75
|
p.outro(pc.red('Forge was not installed.'), clackIO(promptIO));
|
|
@@ -69,6 +81,9 @@ export async function main(argv = process.argv.slice(2), promptIO = {}) {
|
|
|
69
81
|
spinner.start(options.force ? 'Updating Forge files' : 'Installing Forge files');
|
|
70
82
|
try {
|
|
71
83
|
await writeOutputs(plan.files);
|
|
84
|
+
if (command === 'update' && options.prune)
|
|
85
|
+
await pruneEntries(prunePlan.deletable);
|
|
86
|
+
await saveManifest(manifestLocation.manifestPath, buildManifest(manifestLocation, plan.files));
|
|
72
87
|
spinner.stop(`Wrote ${plan.files.length} file(s).`);
|
|
73
88
|
}
|
|
74
89
|
catch (error) {
|
|
@@ -78,7 +93,13 @@ export async function main(argv = process.argv.slice(2), promptIO = {}) {
|
|
|
78
93
|
}
|
|
79
94
|
else {
|
|
80
95
|
await writeOutputs(plan.files);
|
|
96
|
+
if (command === 'update' && options.prune)
|
|
97
|
+
await pruneEntries(prunePlan.deletable);
|
|
98
|
+
await saveManifest(manifestLocation.manifestPath, buildManifest(manifestLocation, plan.files));
|
|
81
99
|
console.log(`Wrote ${plan.files.length} file(s).`);
|
|
100
|
+
if (command === 'update' && options.prune && prunePlan.deletable.length > 0)
|
|
101
|
+
console.log(`Deleted ${prunePlan.deletable.length} stale file(s).`);
|
|
102
|
+
console.log(`Updated manifest ${manifestLocation.manifestPath}.`);
|
|
82
103
|
}
|
|
83
104
|
}
|
|
84
105
|
else if (install && interactive) {
|
|
@@ -90,13 +111,15 @@ export async function main(argv = process.argv.slice(2), promptIO = {}) {
|
|
|
90
111
|
return 0;
|
|
91
112
|
}
|
|
92
113
|
function parseArgs(argv) {
|
|
93
|
-
const options = { command: argv[0], platform: 'all', scope: 'user', source: '.', dryRun: false, force: false, yes: false, platformExplicit: false, scopeExplicit: false, sourceExplicit: false };
|
|
114
|
+
const options = { command: argv[0], platform: 'all', scope: 'user', source: '.', dryRun: false, force: false, prune: true, yes: false, platformExplicit: false, scopeExplicit: false, sourceExplicit: false };
|
|
94
115
|
for (let index = 1; index < argv.length; index += 1) {
|
|
95
116
|
const arg = argv[index];
|
|
96
117
|
if (arg === '--dry-run')
|
|
97
118
|
options.dryRun = true;
|
|
98
119
|
else if (arg === '--force')
|
|
99
120
|
options.force = true;
|
|
121
|
+
else if (arg === '--no-prune')
|
|
122
|
+
options.prune = false;
|
|
100
123
|
else if (arg === '--yes' || arg === '-y')
|
|
101
124
|
options.yes = true;
|
|
102
125
|
else if (arg === '--platform') {
|
|
@@ -127,6 +150,8 @@ function parseArgs(argv) {
|
|
|
127
150
|
}
|
|
128
151
|
}
|
|
129
152
|
const command = normalizeCommand(options.command);
|
|
153
|
+
if (command !== 'update' && !options.prune)
|
|
154
|
+
return { error: '--no-prune is only accepted for update' };
|
|
130
155
|
if (command === 'validate' && (options.dryRun || options.force || options.yes || options.scopeExplicit))
|
|
131
156
|
return { error: 'validate only accepts --platform and --source' };
|
|
132
157
|
return { options };
|
|
@@ -209,6 +234,9 @@ function isInteractivePrompt(promptIO) {
|
|
|
209
234
|
function clackIO(promptIO) {
|
|
210
235
|
return { input: promptIO.input, output: promptIO.output };
|
|
211
236
|
}
|
|
237
|
+
function resolveHome(promptIO) {
|
|
238
|
+
return promptIO.env?.HOME || os.homedir();
|
|
239
|
+
}
|
|
212
240
|
function bundledSourceRoot() {
|
|
213
241
|
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
214
242
|
}
|
|
@@ -223,13 +251,19 @@ function readPackageVersion() {
|
|
|
223
251
|
}
|
|
224
252
|
function showUsage() {
|
|
225
253
|
console.log('Usage: forge-ai install [--platform opencode|claude|codex|all] [--scope user|project] [--source <dir>] [--dry-run] [--force] [--yes]');
|
|
226
|
-
console.log(' forge-ai update [--platform opencode|claude|codex|all] [--scope user|project] [--source <dir>] [--dry-run] [--yes]');
|
|
254
|
+
console.log(' forge-ai update [--platform opencode|claude|codex|all] [--scope user|project] [--source <dir>] [--dry-run] [--no-prune] [--yes]');
|
|
227
255
|
console.log(' forge-ai validate [--platform opencode|claude|codex|all] [--source <dir>]');
|
|
228
256
|
}
|
|
229
|
-
function printPlan(command, sourceCount, files, diagnostics) {
|
|
257
|
+
function printPlan(command, sourceCount, files, diagnostics, prunePlan = { deletable: [], skipped: [] }) {
|
|
230
258
|
console.log(`${command}: ${sourceCount} source(s), ${files.length} output(s)`);
|
|
231
259
|
for (const file of files)
|
|
232
260
|
console.log(`- ${file.platform} ${file.kind} ${file.name} -> ${file.path}`);
|
|
261
|
+
for (const file of prunePlan.deletable)
|
|
262
|
+
console.log(`- delete stale ${file.platform} ${file.kind} ${file.name} -> ${file.path}`);
|
|
263
|
+
for (const file of prunePlan.skipped) {
|
|
264
|
+
if (file.reason === 'checksum-mismatch')
|
|
265
|
+
console.log(`warning CHECKSUM_MISMATCH: Skipping stale managed file with local changes ${file.path}`);
|
|
266
|
+
}
|
|
233
267
|
for (const item of diagnostics)
|
|
234
268
|
console.log(formatDiagnostic(item));
|
|
235
269
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { constants } from 'node:fs';
|
|
3
|
+
import { access, mkdir, readFile, realpath, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
export async function resolveManifestLocation(scope, cwd = process.cwd(), home) {
|
|
6
|
+
const stateRoot = path.join(home, '.forge-ai');
|
|
7
|
+
if (scope === 'user')
|
|
8
|
+
return { stateRoot, manifestPath: path.join(stateRoot, 'user-manifest.json'), scope };
|
|
9
|
+
const projectPath = await canonicalProjectPath(cwd);
|
|
10
|
+
const projectPathHash = hashProjectPath(projectPath);
|
|
11
|
+
return { stateRoot, manifestPath: path.join(stateRoot, 'projects', projectPathHash, 'manifest.json'), scope, projectPath, projectPathHash };
|
|
12
|
+
}
|
|
13
|
+
export async function loadManifest(manifestPath) {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(await readFile(manifestPath, 'utf8'));
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error.code === 'ENOENT')
|
|
19
|
+
return undefined;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function buildManifest(location, files, now = new Date()) {
|
|
24
|
+
return {
|
|
25
|
+
schemaVersion: 1,
|
|
26
|
+
scope: location.scope,
|
|
27
|
+
projectPath: location.projectPath,
|
|
28
|
+
projectPathHash: location.projectPathHash,
|
|
29
|
+
updatedAt: now.toISOString(),
|
|
30
|
+
entries: files.map((file) => ({
|
|
31
|
+
platform: file.platform,
|
|
32
|
+
kind: file.kind,
|
|
33
|
+
name: file.name,
|
|
34
|
+
path: file.path,
|
|
35
|
+
sourcePath: file.sourcePath,
|
|
36
|
+
checksum: sha256(file.content)
|
|
37
|
+
}))
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export async function saveManifest(manifestPath, manifest) {
|
|
41
|
+
await mkdir(path.dirname(manifestPath), { recursive: true });
|
|
42
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
43
|
+
}
|
|
44
|
+
export function staleEntries(oldManifest, files) {
|
|
45
|
+
if (!oldManifest)
|
|
46
|
+
return [];
|
|
47
|
+
const currentPaths = new Set(files.map((file) => file.path));
|
|
48
|
+
return oldManifest.entries.filter((entry) => !currentPaths.has(entry.path));
|
|
49
|
+
}
|
|
50
|
+
export async function classifyPruneEntries(entries) {
|
|
51
|
+
const deletable = [];
|
|
52
|
+
const skipped = [];
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
let content;
|
|
55
|
+
try {
|
|
56
|
+
content = await readFile(entry.path, 'utf8');
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error.code === 'ENOENT')
|
|
60
|
+
skipped.push({ ...entry, reason: 'missing' });
|
|
61
|
+
else
|
|
62
|
+
throw error;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (sha256(content) === entry.checksum)
|
|
66
|
+
deletable.push(entry);
|
|
67
|
+
else
|
|
68
|
+
skipped.push({ ...entry, reason: 'checksum-mismatch' });
|
|
69
|
+
}
|
|
70
|
+
return { deletable, skipped };
|
|
71
|
+
}
|
|
72
|
+
export async function pruneEntries(entries) {
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
await rm(entry.path, { force: true });
|
|
75
|
+
if (entry.kind === 'skill')
|
|
76
|
+
await removeEmptyParent(path.dirname(entry.path));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function sha256(content) {
|
|
80
|
+
return createHash('sha256').update(content).digest('hex');
|
|
81
|
+
}
|
|
82
|
+
export function hashProjectPath(projectPath) {
|
|
83
|
+
return sha256(projectPath).slice(0, 32);
|
|
84
|
+
}
|
|
85
|
+
async function canonicalProjectPath(cwd) {
|
|
86
|
+
try {
|
|
87
|
+
return await realpath(cwd);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return path.resolve(cwd);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function removeEmptyParent(directory) {
|
|
94
|
+
try {
|
|
95
|
+
await access(directory, constants.F_OK);
|
|
96
|
+
await rm(directory);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Directory does not exist or is not empty; both are safe to ignore.
|
|
100
|
+
}
|
|
101
|
+
}
|
package/dist/src/processor.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access } from 'node:fs/promises';
|
|
2
2
|
import { constants } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
3
4
|
import { renderClaudeAgent, renderClaudeSkill } from './adapters/claude.js';
|
|
4
5
|
import { renderCodexAgent, renderCodexSkill } from './adapters/codex.js';
|
|
5
6
|
import { renderOpenCodeAgent, renderOpenCodeSkill } from './adapters/opencode.js';
|
|
@@ -29,7 +30,7 @@ export async function buildWritePlan(options) {
|
|
|
29
30
|
const seenAgents = new Set();
|
|
30
31
|
const seenSkills = new Set();
|
|
31
32
|
for (const source of sources) {
|
|
32
|
-
const converted = convertSource(source);
|
|
33
|
+
const converted = convertSource(source, options.source);
|
|
33
34
|
diagnostics.push(...converted.diagnostics);
|
|
34
35
|
if (!converted.item)
|
|
35
36
|
continue;
|
|
@@ -58,7 +59,7 @@ export async function buildWritePlan(options) {
|
|
|
58
59
|
}
|
|
59
60
|
return { files, diagnostics, sourceCount: sources.length };
|
|
60
61
|
}
|
|
61
|
-
function convertSource(source) {
|
|
62
|
+
function convertSource(source, sourceRoot) {
|
|
62
63
|
const diagnostics = [];
|
|
63
64
|
const data = source.data;
|
|
64
65
|
const name = typeof data.name === 'string' ? data.name : undefined;
|
|
@@ -99,7 +100,7 @@ function convertSource(source) {
|
|
|
99
100
|
diagnostics.push(diagnostic('error', 'EMPTY_BODY', `${source.kind} body is required`, { sourcePath: source.sourcePath }));
|
|
100
101
|
if (!name || !description || !source.body.trim() || diagnostics.some((item) => item.severity === 'error'))
|
|
101
102
|
return { diagnostics };
|
|
102
|
-
const base = { name, description, claude: productConfig(data.claude), opencode: productConfig(data.opencode), codex: productConfig(data.codex) };
|
|
103
|
+
const base = { name, description, sourcePath: path.relative(path.resolve(sourceRoot), source.sourcePath), claude: productConfig(data.claude), opencode: productConfig(data.opencode), codex: productConfig(data.codex) };
|
|
103
104
|
return { diagnostics, item: source.kind === 'agent' ? { ...base, definition: source.body } : { ...base, instructions: source.body } };
|
|
104
105
|
}
|
|
105
106
|
function productConfig(value) {
|
|
@@ -110,7 +111,7 @@ function renderFile(platform, kind, item, options, diagnostics) {
|
|
|
110
111
|
? platform === 'opencode' ? renderOpenCodeAgent(item) : platform === 'claude' ? renderClaudeAgent(item) : renderCodexAgent(item)
|
|
111
112
|
: platform === 'opencode' ? renderOpenCodeSkill(item) : platform === 'claude' ? renderClaudeSkill(item) : renderCodexSkill(item);
|
|
112
113
|
diagnostics.push(...rendered.diagnostics);
|
|
113
|
-
return { platform, kind, scope: options.scope, name: item.name, path: resolveOutputPath(platform, kind, options.scope, item.name, options.cwd, options.home), content: rendered.content };
|
|
114
|
+
return { platform, kind, scope: options.scope, name: item.name, sourcePath: item.sourcePath ?? '', path: resolveOutputPath(platform, kind, options.scope, item.name, options.cwd, options.home), content: rendered.content };
|
|
114
115
|
}
|
|
115
116
|
async function collisionDiagnostics(files, force) {
|
|
116
117
|
const diagnostics = [];
|
package/package.json
CHANGED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge-grill
|
|
3
|
+
description: Stress-test a plan or design through Forge orchestration, batching user questions while delegating codebase-answerable work to forge-worker.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Forge Grill Skill
|
|
7
|
+
|
|
8
|
+
## Role
|
|
9
|
+
|
|
10
|
+
Use this skill when the user wants Forge to stress-test, challenge, or "grill" a plan, design, proposal, or implementation approach.
|
|
11
|
+
|
|
12
|
+
You are still the Forge orchestrator: keep the user thread thin, delegate technical and operational work to `forge-worker`, and use the lightest safe workflow that reaches shared understanding.
|
|
13
|
+
|
|
14
|
+
## Core behavior
|
|
15
|
+
|
|
16
|
+
- Build a decision tree for the plan or design under review.
|
|
17
|
+
- Resolve dependencies between decisions in an order that prevents rework.
|
|
18
|
+
- Challenge assumptions, edge cases, scope boundaries, sequencing, risks, and validation strategy.
|
|
19
|
+
- Prefer shared understanding over volume: ask the fewest high-leverage questions that close the next meaningful branch.
|
|
20
|
+
- Do not perform worker work inline.
|
|
21
|
+
|
|
22
|
+
## Question policy
|
|
23
|
+
|
|
24
|
+
- Use the `question` tool for questions that require the user's judgment, preference, product intent, approval, or risk tolerance.
|
|
25
|
+
- Group related questions into small batches, usually 2-4 questions, instead of asking one question at a time.
|
|
26
|
+
- For each question or option, include Forge's recommended answer and a brief rationale so the user can accept or correct it quickly.
|
|
27
|
+
- Keep each batch focused on one decision branch or tightly related set of branches.
|
|
28
|
+
- Do not ask the user questions that can be answered by inspecting the repository, existing artifacts, logs, tests, or documentation available in the workspace.
|
|
29
|
+
|
|
30
|
+
## Delegation policy
|
|
31
|
+
|
|
32
|
+
When a question can be answered by exploring the codebase or existing Forge artifacts, launch `forge-worker` instead of asking the user.
|
|
33
|
+
|
|
34
|
+
Use `forge-worker` for:
|
|
35
|
+
|
|
36
|
+
- repository inspection
|
|
37
|
+
- artifact review
|
|
38
|
+
- validation checks
|
|
39
|
+
- feasibility or integration discovery
|
|
40
|
+
- implementation-plan consistency checks
|
|
41
|
+
- technical risk investigation
|
|
42
|
+
|
|
43
|
+
Keep each worker prompt bounded and explicit about:
|
|
44
|
+
|
|
45
|
+
- the plan/design branch being tested
|
|
46
|
+
- what facts to inspect
|
|
47
|
+
- what is out of scope
|
|
48
|
+
- expected validation or evidence
|
|
49
|
+
- that the worker must not ask the user directly
|
|
50
|
+
|
|
51
|
+
## Worker contract enforcement
|
|
52
|
+
|
|
53
|
+
Every delegated worker run must return exactly the Forge worker contract:
|
|
54
|
+
|
|
55
|
+
```text
|
|
56
|
+
STATUS: success|partial|blocked
|
|
57
|
+
WORK_TYPE: inspect|design|plan|build|operate|verify|mixed
|
|
58
|
+
FEATURE_SLUG: <kebab-case>
|
|
59
|
+
ARTIFACTS:
|
|
60
|
+
- <path or None>
|
|
61
|
+
SUMMARY:
|
|
62
|
+
- <point>
|
|
63
|
+
NEXT_RECOMMENDED: inspect|design|plan|build|operate|verify|ask-user|none
|
|
64
|
+
RISKS:
|
|
65
|
+
- <risk or None>
|
|
66
|
+
QUESTIONS:
|
|
67
|
+
1) <question>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`QUESTIONS` must appear only when `STATUS: blocked`.
|
|
71
|
+
|
|
72
|
+
If a worker response is malformed, request one reformat retry for the same task. If it is malformed again, stop and surface an actionable orchestration error.
|
|
73
|
+
|
|
74
|
+
## Grill workflow
|
|
75
|
+
|
|
76
|
+
1. Restate the goal, known constraints, and the plan/design surface being grilled.
|
|
77
|
+
2. Identify decision branches and separate them into:
|
|
78
|
+
- repo-answerable facts for `forge-worker`
|
|
79
|
+
- user-owned decisions for the `question` tool
|
|
80
|
+
- safe assumptions that can be stated and revisited
|
|
81
|
+
3. Delegate repo-answerable inspection before asking the user about the same branch.
|
|
82
|
+
4. Ask small batches of user-owned questions with a recommended answer for each.
|
|
83
|
+
5. After each batch or worker result, update the decision tree and resolve dependent branches.
|
|
84
|
+
6. Stop when the remaining unknowns are either resolved, explicitly accepted as risks, or safely deferred.
|
|
85
|
+
|
|
86
|
+
## Output style to the user
|
|
87
|
+
|
|
88
|
+
- Be direct and rigorous, but not performative.
|
|
89
|
+
- Explain why each question matters.
|
|
90
|
+
- Include recommendations in actionable language, such as "Recommended: choose A because...".
|
|
91
|
+
- Make unresolved risk visible before moving to build, plan, or approval-seeking work.
|
|
92
|
+
- If grilling reveals implementation work is needed, route it through the normal Forge worker model instead of doing it inline.
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: forge-build
|
|
3
|
-
description: Implement approved scope from design and plan artifacts, then write build-log.md.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Forge Build Skill
|
|
7
|
-
|
|
8
|
-
## Role
|
|
9
|
-
Implement only approved scope.
|
|
10
|
-
|
|
11
|
-
Build is for approved code implementation only.
|
|
12
|
-
|
|
13
|
-
## Inputs
|
|
14
|
-
|
|
15
|
-
- Orchestrator prompt with the approved implementation scope
|
|
16
|
-
- Optional: `.forge/<feature-slug>/explore.md`
|
|
17
|
-
- Optional: `.forge/<feature-slug>/design.md`
|
|
18
|
-
- Optional: `.forge/<feature-slug>/plan.md`
|
|
19
|
-
|
|
20
|
-
## Required output file
|
|
21
|
-
|
|
22
|
-
`.forge/<feature-slug>/build-log.md`
|
|
23
|
-
|
|
24
|
-
## Build log format
|
|
25
|
-
|
|
26
|
-
- Executed plan steps
|
|
27
|
-
- Files changed
|
|
28
|
-
- Validation run and result
|
|
29
|
-
- Deviations from plan and rationale
|
|
30
|
-
- Follow-ups
|
|
31
|
-
|
|
32
|
-
## Build rules
|
|
33
|
-
|
|
34
|
-
- If a plan exists, do not expand scope beyond plan.
|
|
35
|
-
- Before implementing, check whether `.forge/<feature-slug>/plan.md` exists.
|
|
36
|
-
- If a plan exists, review it critically before touching code.
|
|
37
|
-
- If a plan exists, require the orchestrator prompt to include evidence that the user explicitly approved starting build for that planned scope.
|
|
38
|
-
- If a plan exists and that approval is absent or ambiguous, stop and return `STATUS: blocked` with approval questions instead of implementing.
|
|
39
|
-
- If a plan exists and contains placeholders, missing dependencies, or non-buildable tasks, stop and return `STATUS: blocked` instead of guessing.
|
|
40
|
-
- If no plan exists, treat the orchestrator prompt as the approved scope and keep the change tightly bounded.
|
|
41
|
-
- If no plan exists, the direct-build path is allowed only when the orchestrator prompt clearly marks the request as a lightweight implementation.
|
|
42
|
-
- Non-development operational tasks are out of scope for build.
|
|
43
|
-
- Route non-development execution tasks such as git commit or git push to `forge-helper`.
|
|
44
|
-
- If a step is materially ambiguous during execution, stop and return blocked with questions.
|
|
45
|
-
- When a plan exists, record build-log progress against the reviewed plan rather than silently reshaping it.
|
|
46
|
-
- Implement the minimum code necessary to satisfy the approved design and plan.
|
|
47
|
-
- Do not perform adjacent refactors, cleanup passes, or abstraction work unless explicitly approved or required to complete the approved scope.
|
|
48
|
-
- Prefer existing patterns over introducing new layers, frameworks, or indirection.
|
|
49
|
-
|
|
50
|
-
## Pre-implementation checklist
|
|
51
|
-
|
|
52
|
-
Before editing files, confirm:
|
|
53
|
-
|
|
54
|
-
- the goal being implemented
|
|
55
|
-
- the files expected to change
|
|
56
|
-
- the validation that should prove the goal
|
|
57
|
-
|
|
58
|
-
Apply this checklist on both plan-backed builds and lightweight direct-build paths.
|
|
59
|
-
|
|
60
|
-
## Contract (strict)
|
|
61
|
-
|
|
62
|
-
Return only:
|
|
63
|
-
|
|
64
|
-
```text
|
|
65
|
-
STATUS: success|partial|blocked
|
|
66
|
-
PHASE: BUILD
|
|
67
|
-
FEATURE_SLUG: <kebab-case>
|
|
68
|
-
ARTIFACTS:
|
|
69
|
-
- .forge/<feature-slug>/build-log.md
|
|
70
|
-
SUMMARY:
|
|
71
|
-
- <brief point>
|
|
72
|
-
NEXT_RECOMMENDED: none
|
|
73
|
-
RISKS:
|
|
74
|
-
- <risk or None>
|
|
75
|
-
QUESTIONS:
|
|
76
|
-
1) <question>
|
|
77
|
-
2) <question>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Include `QUESTIONS` only when blocked.
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: forge-design
|
|
3
|
-
description: Create the canonical design artifact that merges product and technical design into design.md.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Forge Design Skill
|
|
7
|
-
|
|
8
|
-
## Role
|
|
9
|
-
Close critical design decisions and then produce the single design artifact for the work item.
|
|
10
|
-
|
|
11
|
-
`design.md` is the default source of truth for both intended behavior and technical shape. There is no separate `tech.md` in this flow.
|
|
12
|
-
|
|
13
|
-
## Inputs
|
|
14
|
-
|
|
15
|
-
- `.forge/<feature-slug>/explore.md`
|
|
16
|
-
- Feature request and user clarifications
|
|
17
|
-
|
|
18
|
-
## Required output file
|
|
19
|
-
|
|
20
|
-
`.forge/<feature-slug>/design.md`, but only after the clarification gate is fully closed.
|
|
21
|
-
|
|
22
|
-
## Clarification gate
|
|
23
|
-
|
|
24
|
-
Before writing `design.md`, review:
|
|
25
|
-
|
|
26
|
-
- `.forge/<feature-slug>/explore.md`
|
|
27
|
-
- the user request
|
|
28
|
-
- prior user clarifications in the current thread
|
|
29
|
-
|
|
30
|
-
Classify open questions into:
|
|
31
|
-
|
|
32
|
-
- critical decisions that materially change behavior, scope, interface, or technical shape
|
|
33
|
-
- non-critical details that can be fixed by a reasonable default
|
|
34
|
-
|
|
35
|
-
Rules:
|
|
36
|
-
|
|
37
|
-
- Resolve all critical decisions before writing `design.md`.
|
|
38
|
-
- Do not ask questions that can be answered from the repo, existing artifacts, or docs.
|
|
39
|
-
- Ask the smallest useful batch of independent questions.
|
|
40
|
-
- Every question must include:
|
|
41
|
-
- the decision to resolve
|
|
42
|
-
- a recommended answer
|
|
43
|
-
- brief impact of that recommendation
|
|
44
|
-
- If critical decisions remain, return `STATUS: blocked`, `NEXT_RECOMMENDED: design`, and do not write `design.md` yet.
|
|
45
|
-
|
|
46
|
-
## Design format
|
|
47
|
-
|
|
48
|
-
The document must stay compact and optimized for LLM consumption.
|
|
49
|
-
|
|
50
|
-
Expected content:
|
|
51
|
-
- Objective
|
|
52
|
-
- Non-objectives
|
|
53
|
-
- Decision Log
|
|
54
|
-
- Requirements with stable `TASK-*` identifiers
|
|
55
|
-
- Technical shape for those same `TASK-*` items
|
|
56
|
-
- Constraints and dependencies that materially affect implementation
|
|
57
|
-
- Acceptance checks
|
|
58
|
-
|
|
59
|
-
## Design rules
|
|
60
|
-
|
|
61
|
-
- Merge product and technical design into one artifact, but keep those concerns clearly separated by section.
|
|
62
|
-
- Include only resolved design-relevant decisions in the `Decision Log`.
|
|
63
|
-
- Reuse stable `TASK-*` identifiers across the artifact.
|
|
64
|
-
- Be concrete about files, modules, integration points, and constraints when they materially shape implementation.
|
|
65
|
-
- Do not turn `design.md` into an execution checklist; sequencing belongs in `plan.md`.
|
|
66
|
-
- Use reasonable defaults only for non-critical details.
|
|
67
|
-
- Do not write `design.md` with unresolved critical decisions.
|
|
68
|
-
- Prefer the simplest design that satisfies the requested outcome and acceptance checks.
|
|
69
|
-
- Avoid speculative abstractions or new indirection unless the request or current architecture requires them.
|
|
70
|
-
- Record the important tradeoffs and defaults that downstream planning and build must preserve.
|
|
71
|
-
- Escalate only decisions that materially change behavior, scope, interface, or technical shape.
|
|
72
|
-
- If a heavier alternative was considered and rejected because it would expand scope or complexity, note that briefly when it helps preserve the approved shape.
|
|
73
|
-
|
|
74
|
-
## Phase intent
|
|
75
|
-
|
|
76
|
-
- `design.md` answers: what should change, and what is the intended technical shape?
|
|
77
|
-
- It is the design source of truth for downstream planning and implementation after the clarification gate has been closed.
|
|
78
|
-
|
|
79
|
-
## Contract (strict)
|
|
80
|
-
|
|
81
|
-
Return only:
|
|
82
|
-
|
|
83
|
-
```text
|
|
84
|
-
STATUS: success|partial|blocked
|
|
85
|
-
PHASE: DESIGN
|
|
86
|
-
FEATURE_SLUG: <kebab-case>
|
|
87
|
-
ARTIFACTS:
|
|
88
|
-
- .forge/<feature-slug>/design.md | None
|
|
89
|
-
SUMMARY:
|
|
90
|
-
- <brief point>
|
|
91
|
-
NEXT_RECOMMENDED: design|plan
|
|
92
|
-
RISKS:
|
|
93
|
-
- <risk or None>
|
|
94
|
-
QUESTIONS:
|
|
95
|
-
1) Decision: <decision>
|
|
96
|
-
Recommendation: <recommended answer>
|
|
97
|
-
Impact: <brief why>
|
|
98
|
-
2) Decision: <decision>
|
|
99
|
-
Recommendation: <recommended answer>
|
|
100
|
-
Impact: <brief why>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Use `STATUS: blocked` when critical decisions still require user input.
|
|
104
|
-
Include `QUESTIONS` only when blocked.
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: forge-explore
|
|
3
|
-
description: Explore the requested feature and write the baseline exploration artifact.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Forge Explore Skill
|
|
7
|
-
|
|
8
|
-
## Role
|
|
9
|
-
Explore the repository and produce a compact baseline for downstream design, planning, or build work.
|
|
10
|
-
|
|
11
|
-
## Inputs
|
|
12
|
-
|
|
13
|
-
- Work item request from the orchestrator prompt
|
|
14
|
-
- Repository code and docs
|
|
15
|
-
|
|
16
|
-
## Required output file
|
|
17
|
-
|
|
18
|
-
`.forge/<feature-slug>/explore.md`
|
|
19
|
-
|
|
20
|
-
## Exploration rules
|
|
21
|
-
|
|
22
|
-
- Think before broadening the search. Prefer narrow reading and searching around likely files and symbols before wider repo scans.
|
|
23
|
-
- Distinguish observed facts from inferred conclusions.
|
|
24
|
-
- Capture assumptions, unknowns, tradeoffs, and critical decisions explicitly.
|
|
25
|
-
- Record only the repo intersections that materially shape later design or implementation.
|
|
26
|
-
- Escalate only missing information that meaningfully blocks design or safe execution.
|
|
27
|
-
- Do not redesign the solution in `explore`; identify what exists, what is missing, and what decisions remain.
|
|
28
|
-
|
|
29
|
-
## Explore format
|
|
30
|
-
|
|
31
|
-
Keep the artifact compact and optimized for downstream LLM consumption.
|
|
32
|
-
|
|
33
|
-
Expected content:
|
|
34
|
-
- Problem framing
|
|
35
|
-
- What already exists and current state
|
|
36
|
-
- Relevant codepaths, modules, systems, and docs
|
|
37
|
-
- Intersections with adjacent areas that may be affected
|
|
38
|
-
- Assumptions
|
|
39
|
-
- Unknowns
|
|
40
|
-
- Tradeoffs
|
|
41
|
-
- Critical decisions
|
|
42
|
-
- Non-critical unknowns
|
|
43
|
-
|
|
44
|
-
## Contract (strict)
|
|
45
|
-
|
|
46
|
-
Return only:
|
|
47
|
-
|
|
48
|
-
```text
|
|
49
|
-
STATUS: success|partial|blocked
|
|
50
|
-
PHASE: EXPLORE
|
|
51
|
-
FEATURE_SLUG: <kebab-case>
|
|
52
|
-
ARTIFACTS:
|
|
53
|
-
- .forge/<feature-slug>/explore.md
|
|
54
|
-
SUMMARY:
|
|
55
|
-
- <brief point>
|
|
56
|
-
NEXT_RECOMMENDED: design
|
|
57
|
-
RISKS:
|
|
58
|
-
- <risk or None>
|
|
59
|
-
QUESTIONS:
|
|
60
|
-
1) <question>
|
|
61
|
-
2) <question>
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Use `STATUS: blocked` only if missing information blocks meaningful exploration.
|
|
65
|
-
Include `QUESTIONS` only when blocked.
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: forge-helper
|
|
3
|
-
description: Execute non-development helper tasks for the orchestrator.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Forge Helper Skill
|
|
7
|
-
|
|
8
|
-
## Role
|
|
9
|
-
Execute bounded non-development tasks for the orchestrator.
|
|
10
|
-
|
|
11
|
-
## Scope rules
|
|
12
|
-
|
|
13
|
-
- Do only the requested operational action.
|
|
14
|
-
- Do not write code, edit source files, or broaden into software-development implementation work.
|
|
15
|
-
- If the request is actually explore, design, plan, or build work, stop and tell the orchestrator to route it to the appropriate phase agent.
|
|
16
|
-
- Keep execution tightly bounded to the requested helper task.
|
|
17
|
-
- If the action could mutate protected or remote state, require explicit confirmation unless the orchestrator prompt already contains clear user intent for that exact action.
|
|
18
|
-
- Do not broaden into workflow advice or extra repo operations unless asked.
|
|
19
|
-
|
|
20
|
-
## Typical examples
|
|
21
|
-
|
|
22
|
-
- create a git commit
|
|
23
|
-
- push a branch
|
|
24
|
-
- inspect non-development execution status needed by the orchestrator
|
|
25
|
-
|
|
26
|
-
## Contract (strict)
|
|
27
|
-
|
|
28
|
-
Return only:
|
|
29
|
-
|
|
30
|
-
```text
|
|
31
|
-
STATUS: success|partial|blocked
|
|
32
|
-
PHASE: HELPER
|
|
33
|
-
FEATURE_SLUG: <kebab-case>
|
|
34
|
-
ARTIFACTS:
|
|
35
|
-
- <path or None>
|
|
36
|
-
SUMMARY:
|
|
37
|
-
- <brief point>
|
|
38
|
-
NEXT_RECOMMENDED: none
|
|
39
|
-
RISKS:
|
|
40
|
-
- <risk or None>
|
|
41
|
-
QUESTIONS:
|
|
42
|
-
1) <question>
|
|
43
|
-
2) <question>
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Include `QUESTIONS` only when blocked.
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: forge-plan
|
|
3
|
-
description: Create an execution plan from explore and design artifacts.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Forge Plan Skill
|
|
7
|
-
|
|
8
|
-
## Role
|
|
9
|
-
Create the execution plan from the approved design work.
|
|
10
|
-
|
|
11
|
-
## Inputs
|
|
12
|
-
|
|
13
|
-
- `.forge/<feature-slug>/explore.md`
|
|
14
|
-
- `.forge/<feature-slug>/design.md`
|
|
15
|
-
|
|
16
|
-
## Required output file
|
|
17
|
-
|
|
18
|
-
`.forge/<feature-slug>/plan.md`
|
|
19
|
-
|
|
20
|
-
## Plan format
|
|
21
|
-
|
|
22
|
-
The plan defines execution order using building tasks.
|
|
23
|
-
|
|
24
|
-
A building task is a unit of work that must be implementable and testable.
|
|
25
|
-
|
|
26
|
-
The plan must include:
|
|
27
|
-
- Scope for the current delivery
|
|
28
|
-
- File Map covering the likely files or modules to touch and why
|
|
29
|
-
- Building tasks to execute
|
|
30
|
-
- Execution order of those building tasks
|
|
31
|
-
- Expected result of each building task
|
|
32
|
-
- Files or components touched by each building task
|
|
33
|
-
- Verification for each building task
|
|
34
|
-
- References to relevant `TASK-*` items when they clarify scope, sequencing, or dependencies
|
|
35
|
-
|
|
36
|
-
The plan must not include:
|
|
37
|
-
- Design definitions that belong in `design.md`
|
|
38
|
-
- Rationale for why a solution was chosen over another
|
|
39
|
-
- Code blocks
|
|
40
|
-
- Unresolved questions inside the plan document
|
|
41
|
-
- Placeholder language such as `TBD`, `TODO`, `implement later`, `adjust as needed`, or catch-all steps that hide concrete work
|
|
42
|
-
|
|
43
|
-
## Planning rules
|
|
44
|
-
|
|
45
|
-
- Use `design.md` as the source of truth for behavior and technical shape.
|
|
46
|
-
- Do not plan from a design artifact that still has unresolved critical decisions.
|
|
47
|
-
- Sequence delivery work without redefining the design.
|
|
48
|
-
- Make each building task buildable without guesswork.
|
|
49
|
-
- Prefer finer-grained tasks than the current format, but do not break work into trivial micro-steps.
|
|
50
|
-
- Ask questions only when uncertainty materially changes execution strategy or task ordering.
|
|
51
|
-
- A completed plan does not authorize implementation by itself.
|
|
52
|
-
- Default to `NEXT_RECOMMENDED: plan` when the plan is ready but waiting for user approval to build.
|
|
53
|
-
- Return `NEXT_RECOMMENDED: build` only when the orchestrator prompt explicitly states that the user has already approved implementation.
|
|
54
|
-
- Keep tasks surgically scoped to the approved goal; optional cleanup belongs outside the plan unless explicitly requested.
|
|
55
|
-
- The file map must justify why each listed file or module is expected to be touched.
|
|
56
|
-
- Prefer existing patterns and minimum necessary changes over broader structural rewrites.
|
|
57
|
-
- Each task must include minimal verification tied to the requested goal, not just a generic test step.
|
|
58
|
-
- If execution would require guesswork about assumptions, unknowns, or missing dependencies, stop and block instead of padding the plan with placeholders.
|
|
59
|
-
|
|
60
|
-
## Contract (strict)
|
|
61
|
-
|
|
62
|
-
Return only:
|
|
63
|
-
|
|
64
|
-
```text
|
|
65
|
-
STATUS: success|partial|blocked
|
|
66
|
-
PHASE: PLAN
|
|
67
|
-
FEATURE_SLUG: <kebab-case>
|
|
68
|
-
ARTIFACTS:
|
|
69
|
-
- .forge/<feature-slug>/plan.md
|
|
70
|
-
SUMMARY:
|
|
71
|
-
- <brief point>
|
|
72
|
-
NEXT_RECOMMENDED: plan|build
|
|
73
|
-
RISKS:
|
|
74
|
-
- <risk or None>
|
|
75
|
-
QUESTIONS:
|
|
76
|
-
1) <question>
|
|
77
|
-
2) <question>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Use `STATUS: blocked` when planning cannot continue due to missing critical decisions.
|
|
81
|
-
Include `QUESTIONS` only when blocked.
|