@guidobuilds/forge-ai 0.1.0 → 0.3.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.
@@ -1,18 +1,21 @@
1
- import { access } from 'node:fs/promises';
1
+ import { access, readFile } 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';
6
7
  import { diagnostic } from './diagnostics.js';
7
8
  import { discoverSources } from './discovery.js';
9
+ import { lookupEntryByPath, resolveBackupPath, sha256 } from './manifest.js';
8
10
  import { resolveOutputPath } from './paths.js';
9
11
  import { isPlatform, platforms } from './model.js';
10
12
  const namePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
11
13
  const platformKeys = new Set(['claude', 'opencode', 'codex']);
12
- const allowedTopLevel = new Set(['name', 'description', 'claude', 'opencode', 'codex']);
13
- const allowedProductKeys = new Set(['permissions', 'model']);
14
+ const allowedTopLevel = new Set(['name', 'description', 'kind', 'claude', 'opencode', 'codex']);
15
+ const allowedProductKeys = new Set(['permissions', 'model', 'kind']);
14
16
  const allowedOpenCodeKeys = new Set([...allowedProductKeys, 'mode']);
15
17
  const openCodeModes = new Set(['primary', 'subagent', 'all']);
18
+ const artifactKinds = new Set(['agent', 'skill']);
16
19
  export function resolvePlatforms(platform) {
17
20
  return platform === 'all' ? platforms : [platform];
18
21
  }
@@ -24,45 +27,43 @@ export function parseScope(value) {
24
27
  }
25
28
  export async function buildWritePlan(options) {
26
29
  const { sources, diagnostics } = await discoverSources(options.source);
27
- const agents = [];
28
- const skills = [];
29
- const seenAgents = new Set();
30
- const seenSkills = new Set();
30
+ const artifacts = [];
31
+ const seen = 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;
36
- const seen = source.kind === 'agent' ? seenAgents : seenSkills;
37
37
  if (seen.has(converted.item.name)) {
38
- diagnostics.push(diagnostic('error', 'DUPLICATE_NAME', `Duplicate ${source.kind} name ${converted.item.name}`, { sourcePath: source.sourcePath }));
38
+ diagnostics.push(diagnostic('error', 'DUPLICATE_NAME', `Duplicate artifact name ${converted.item.name}`, { sourcePath: source.sourcePath }));
39
39
  continue;
40
40
  }
41
41
  seen.add(converted.item.name);
42
- if (source.kind === 'agent')
43
- agents.push(converted.item);
44
- else
45
- skills.push(converted.item);
42
+ artifacts.push(converted.item);
46
43
  }
47
44
  const files = [];
45
+ const pending = { modifiedOverwrites: [], foreignOverwrites: [] };
48
46
  if (!diagnostics.some((item) => item.severity === 'error')) {
49
47
  for (const platform of resolvePlatforms(options.platform)) {
50
- for (const agent of agents)
51
- files.push(renderFile(platform, 'agent', agent, options, diagnostics));
52
- for (const skill of skills)
53
- files.push(renderFile(platform, 'skill', skill, options, diagnostics));
48
+ for (const artifact of artifacts) {
49
+ const effectiveKind = artifact[platform]?.kind ?? artifact.kind;
50
+ files.push(renderFile(platform, effectiveKind, artifact, options, diagnostics));
51
+ }
54
52
  }
55
53
  files.sort((a, b) => `${a.platform}:${a.kind}:${a.name}`.localeCompare(`${b.platform}:${b.kind}:${b.name}`));
56
- if (options.checkCollisions)
57
- diagnostics.push(...await collisionDiagnostics(files, Boolean(options.force)));
54
+ if (options.checkCollisions) {
55
+ const anchor = options.scope === 'user' ? (options.home ?? '') : (options.cwd ?? '');
56
+ diagnostics.push(...await classifyDestinations(files, options.manifest, options.backupRoot, anchor, pending));
57
+ }
58
58
  }
59
- return { files, diagnostics, sourceCount: sources.length };
59
+ return { files, diagnostics, pending, sourceCount: sources.length };
60
60
  }
61
- function convertSource(source) {
61
+ function convertSource(source, sourceRoot) {
62
62
  const diagnostics = [];
63
63
  const data = source.data;
64
64
  const name = typeof data.name === 'string' ? data.name : undefined;
65
65
  const description = typeof data.description === 'string' ? data.description : undefined;
66
+ const kind = data.kind;
66
67
  for (const key of Object.keys(data)) {
67
68
  if (!allowedTopLevel.has(key))
68
69
  diagnostics.push(diagnostic('error', 'UNSUPPORTED_FIELD', `Unsupported canonical field ${key}`, { sourcePath: source.sourcePath }));
@@ -75,53 +76,95 @@ function convertSource(source) {
75
76
  diagnostics.push(diagnostic('error', 'INVALID_PLATFORM_BLOCK', `${platform} must be an object`, { sourcePath: source.sourcePath, platform: platform }));
76
77
  continue;
77
78
  }
78
- for (const key of Object.keys(config)) {
79
- const allowedKeys = platform === 'opencode' && source.kind === 'agent' ? allowedOpenCodeKeys : allowedProductKeys;
79
+ const record = config;
80
+ for (const key of Object.keys(record)) {
81
+ const allowedKeys = platform === 'opencode' ? allowedOpenCodeKeys : allowedProductKeys;
80
82
  if (!allowedKeys.has(key))
81
83
  diagnostics.push(diagnostic('error', 'UNSUPPORTED_PLATFORM_FIELD', `${platform}.${key} is not supported in the MVP`, { sourcePath: source.sourcePath, platform: platform }));
82
84
  }
83
- if ('model' in config && typeof config.model !== 'string') {
85
+ if ('model' in record && typeof record.model !== 'string') {
84
86
  diagnostics.push(diagnostic('error', 'INVALID_PLATFORM_MODEL', `${platform}.model must be a string`, { sourcePath: source.sourcePath, platform: platform }));
85
87
  }
86
- if (platform === 'opencode' && source.kind === 'agent' && 'mode' in config && !openCodeModes.has(config.mode)) {
87
- diagnostics.push(diagnostic('error', 'INVALID_OPENCODE_MODE', 'opencode.mode must be one of primary, subagent, all', { sourcePath: source.sourcePath, platform: 'opencode' }));
88
+ if ('kind' in record && !artifactKinds.has(record.kind)) {
89
+ diagnostics.push(diagnostic('error', 'INVALID_PLATFORM_KIND', `${platform}.kind must be one of agent, skill`, { sourcePath: source.sourcePath, platform: platform }));
90
+ }
91
+ if (platform === 'opencode' && 'mode' in record) {
92
+ if (!openCodeModes.has(record.mode)) {
93
+ diagnostics.push(diagnostic('error', 'INVALID_OPENCODE_MODE', 'opencode.mode must be one of primary, subagent, all', { sourcePath: source.sourcePath, platform: 'opencode' }));
94
+ }
95
+ const effectiveKind = record.kind ?? kind;
96
+ if (effectiveKind !== 'agent') {
97
+ diagnostics.push(diagnostic('error', 'OPENCODE_MODE_ON_SKILL', 'opencode.mode is only valid when the OpenCode artifact kind is agent', { sourcePath: source.sourcePath, platform: 'opencode' }));
98
+ }
88
99
  }
89
100
  }
90
101
  if (!name)
91
- diagnostics.push(diagnostic('error', 'MISSING_NAME', `${source.kind} name is required`, { sourcePath: source.sourcePath }));
102
+ diagnostics.push(diagnostic('error', 'MISSING_NAME', 'artifact name is required', { sourcePath: source.sourcePath }));
92
103
  if (name && !namePattern.test(name))
93
- diagnostics.push(diagnostic('error', 'INVALID_NAME', `${source.kind} name must be kebab-case`, { sourcePath: source.sourcePath }));
104
+ diagnostics.push(diagnostic('error', 'INVALID_NAME', 'artifact name must be kebab-case', { sourcePath: source.sourcePath }));
94
105
  if (name && name !== source.expectedName)
95
- diagnostics.push(diagnostic('error', 'NAME_MISMATCH', `${source.kind} name must match ${source.expectedName}`, { sourcePath: source.sourcePath }));
106
+ diagnostics.push(diagnostic('error', 'NAME_MISMATCH', `artifact name must match ${source.expectedName}`, { sourcePath: source.sourcePath }));
96
107
  if (!description)
97
- diagnostics.push(diagnostic('error', 'MISSING_DESCRIPTION', `${source.kind} description is required`, { sourcePath: source.sourcePath }));
108
+ diagnostics.push(diagnostic('error', 'MISSING_DESCRIPTION', 'artifact description is required', { sourcePath: source.sourcePath }));
109
+ if (kind === undefined)
110
+ diagnostics.push(diagnostic('error', 'MISSING_KIND', 'artifact kind is required (agent or skill)', { sourcePath: source.sourcePath }));
111
+ else if (!artifactKinds.has(kind))
112
+ diagnostics.push(diagnostic('error', 'INVALID_KIND', 'artifact kind must be one of agent, skill', { sourcePath: source.sourcePath }));
98
113
  if (!source.body.trim())
99
- diagnostics.push(diagnostic('error', 'EMPTY_BODY', `${source.kind} body is required`, { sourcePath: source.sourcePath }));
100
- if (!name || !description || !source.body.trim() || diagnostics.some((item) => item.severity === 'error'))
114
+ diagnostics.push(diagnostic('error', 'EMPTY_BODY', 'artifact body is required', { sourcePath: source.sourcePath }));
115
+ if (!name || !description || !artifactKinds.has(kind) || !source.body.trim() || diagnostics.some((item) => item.severity === 'error'))
101
116
  return { diagnostics };
102
- const base = { name, description, claude: productConfig(data.claude), opencode: productConfig(data.opencode), codex: productConfig(data.codex) };
103
- return { diagnostics, item: source.kind === 'agent' ? { ...base, definition: source.body } : { ...base, instructions: source.body } };
117
+ return {
118
+ diagnostics,
119
+ item: {
120
+ name,
121
+ description,
122
+ kind: kind,
123
+ body: source.body,
124
+ sourcePath: path.relative(path.resolve(sourceRoot), source.sourcePath),
125
+ claude: productConfig(data.claude),
126
+ opencode: productConfig(data.opencode),
127
+ codex: productConfig(data.codex)
128
+ }
129
+ };
104
130
  }
105
131
  function productConfig(value) {
106
132
  return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
107
133
  }
108
- function renderFile(platform, kind, item, options, diagnostics) {
134
+ function renderFile(platform, kind, artifact, options, diagnostics) {
109
135
  const rendered = kind === 'agent'
110
- ? platform === 'opencode' ? renderOpenCodeAgent(item) : platform === 'claude' ? renderClaudeAgent(item) : renderCodexAgent(item)
111
- : platform === 'opencode' ? renderOpenCodeSkill(item) : platform === 'claude' ? renderClaudeSkill(item) : renderCodexSkill(item);
136
+ ? platform === 'opencode' ? renderOpenCodeAgent(artifact) : platform === 'claude' ? renderClaudeAgent(artifact) : renderCodexAgent(artifact)
137
+ : platform === 'opencode' ? renderOpenCodeSkill(artifact) : platform === 'claude' ? renderClaudeSkill(artifact) : renderCodexSkill(artifact);
112
138
  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 };
139
+ return { platform, kind, scope: options.scope, name: artifact.name, sourcePath: artifact.sourcePath, path: resolveOutputPath(platform, kind, options.scope, artifact.name, options.cwd, options.home), content: rendered.content };
114
140
  }
115
- async function collisionDiagnostics(files, force) {
141
+ async function classifyDestinations(files, manifest, backupRoot, anchor, pending) {
116
142
  const diagnostics = [];
117
143
  for (const file of files) {
118
- try {
119
- await access(file.path, constants.F_OK);
120
- diagnostics.push(diagnostic(force ? 'warning' : 'error', force ? 'OVERWRITE_FORCED' : 'DESTINATION_EXISTS', force ? `--force will overwrite ${file.path}` : `Destination exists; use --force to overwrite ${file.path}`, { platform: file.platform }));
144
+ const status = await classifyFile(file.path, manifest);
145
+ file.status = status;
146
+ if (status === 'managed-modified' && backupRoot) {
147
+ file.backupPath = resolveBackupPath(backupRoot, file.path, anchor);
148
+ pending.modifiedOverwrites.push(file);
149
+ diagnostics.push(diagnostic('warning', 'MANAGED_FILE_OVERWRITE', `Will overwrite locally edited Forge file ${file.path}; backup → ${file.backupPath}`, { platform: file.platform }));
121
150
  }
122
- catch {
123
- // Missing destination is safe.
151
+ else if (status === 'foreign') {
152
+ pending.foreignOverwrites.push(file);
153
+ diagnostics.push(diagnostic('warning', 'FOREIGN_FILE_OVERWRITE', `Will overwrite untracked file at ${file.path}`, { platform: file.platform }));
124
154
  }
125
155
  }
126
156
  return diagnostics;
127
157
  }
158
+ async function classifyFile(filePath, manifest) {
159
+ try {
160
+ await access(filePath, constants.F_OK);
161
+ }
162
+ catch {
163
+ return 'new';
164
+ }
165
+ const entry = lookupEntryByPath(manifest, filePath);
166
+ if (!entry)
167
+ return 'foreign';
168
+ const content = await readFile(filePath, 'utf8');
169
+ return sha256(content) === entry.checksum ? 'managed-unmodified' : 'managed-modified';
170
+ }
@@ -1,7 +1,19 @@
1
- import { mkdir, writeFile } from 'node:fs/promises';
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { backupFile } from './manifest.js';
3
4
  export async function writeOutputs(files) {
4
5
  for (const file of files) {
6
+ if (file.backupPath) {
7
+ try {
8
+ const existing = await readFile(file.path, 'utf8');
9
+ await backupFile(file.backupPath, existing);
10
+ }
11
+ catch (error) {
12
+ if (error.code !== 'ENOENT')
13
+ throw error;
14
+ // Original file disappeared between classification and write; no backup needed.
15
+ }
16
+ }
5
17
  await mkdir(path.dirname(file.path), { recursive: true });
6
18
  await writeFile(file.path, file.content, 'utf8');
7
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guidobuilds/forge-ai",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Forge AI framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,18 +10,11 @@
10
10
  "files": [
11
11
  "bin",
12
12
  "dist/src",
13
- "agents",
14
- "skills",
13
+ "artifacts",
15
14
  "README.md",
15
+ "CHANGELOG.md",
16
16
  "LICENSE"
17
17
  ],
18
- "scripts": {
19
- "build": "tsc -p tsconfig.build.json",
20
- "build:test": "tsc -p tsconfig.json",
21
- "typecheck": "tsc -p tsconfig.json --noEmit",
22
- "test": "npm run build:test && node --test dist/tests/*.test.js",
23
- "prepack": "npm run build"
24
- },
25
18
  "engines": {
26
19
  "node": ">=20"
27
20
  },
@@ -31,6 +24,13 @@
31
24
  },
32
25
  "dependencies": {
33
26
  "@clack/prompts": "^1.2.0",
34
- "picocolors": "^1.1.1"
27
+ "picocolors": "^1.1.1",
28
+ "yaml": "^2.8.4"
29
+ },
30
+ "scripts": {
31
+ "build": "tsc -p tsconfig.build.json",
32
+ "build:test": "tsc -p tsconfig.json",
33
+ "typecheck": "tsc -p tsconfig.json --noEmit",
34
+ "test": "npm run build:test && node --test dist/tests/*.test.js"
35
35
  }
36
- }
36
+ }
@@ -1,60 +0,0 @@
1
- ---
2
- name: forge-worker
3
- description: Forge universal worker for inspect, design, plan, build, operate, and verify work
4
- claude:
5
- permissions:
6
- tools: [TodoWrite, Read, Write, Edit, Bash, Glob, Grep, LS, MultiEdit, WebFetch]
7
- opencode:
8
- mode: subagent
9
- permissions:
10
- todowrite: true
11
- read: true
12
- write: true
13
- edit: true
14
- bash: true
15
- glob: true
16
- grep: true
17
- list: true
18
- patch: true
19
- skill: true
20
- webfetch: true
21
- ---
22
-
23
- You are the Forge worker.
24
-
25
- Load and follow the `forge-worker` skill before doing work.
26
-
27
- You are the only worker type in Forge. The orchestrator may launch multiple instances of you in parallel or sequence.
28
-
29
- ## Inputs
30
- - Orchestrator prompt with the assigned subgoal, expected boundaries, and any approval context.
31
- - Optional: `.forge/<feature-slug>/explore.md`
32
- - Optional: `.forge/<feature-slug>/design.md`
33
- - Optional: `.forge/<feature-slug>/plan.md`
34
- - Optional: `.forge/<feature-slug>/build-log.md`
35
-
36
- The skill defines routing by work type, artifact guidance, approval handling, bounded execution, escalation rules, and validation expectations.
37
-
38
- ## Contract (strict)
39
- Return only:
40
-
41
- ```text
42
- STATUS: success|partial|blocked
43
- WORK_TYPE: inspect|design|plan|build|operate|verify|mixed
44
- FEATURE_SLUG: <kebab-case>
45
- ARTIFACTS:
46
- - <path or None>
47
- SUMMARY:
48
- - <brief point>
49
- NEXT_RECOMMENDED: inspect|design|plan|build|operate|verify|ask-user|none
50
- RISKS:
51
- - <risk or None>
52
- QUESTIONS:
53
- 1) <question>
54
- 2) <question>
55
- ```
56
-
57
- Include `QUESTIONS` only when blocked.
58
-
59
- Do not interact directly with the user. Escalate open decisions back to the orchestrator through the contract.
60
- Do not add extra format outside the defined worker contract.
@@ -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.