@samahlstrom/forge-cli 0.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 +175 -0
- package/bin/forge.js +2 -0
- package/dist/addons/index.d.ts +25 -0
- package/dist/addons/index.js +139 -0
- package/dist/addons/index.js.map +1 -0
- package/dist/commands/add.d.ts +1 -0
- package/dist/commands/add.js +61 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +177 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/ingest.d.ts +24 -0
- package/dist/commands/ingest.js +316 -0
- package/dist/commands/ingest.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.js +557 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/remove.d.ts +1 -0
- package/dist/commands/remove.js +42 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.js +190 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/detect/features.d.ts +10 -0
- package/dist/detect/features.js +33 -0
- package/dist/detect/features.js.map +1 -0
- package/dist/detect/go.d.ts +3 -0
- package/dist/detect/go.js +38 -0
- package/dist/detect/go.js.map +1 -0
- package/dist/detect/index.d.ts +25 -0
- package/dist/detect/index.js +32 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/detect/node.d.ts +3 -0
- package/dist/detect/node.js +99 -0
- package/dist/detect/node.js.map +1 -0
- package/dist/detect/python.d.ts +3 -0
- package/dist/detect/python.js +86 -0
- package/dist/detect/python.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/render/engine.d.ts +8 -0
- package/dist/render/engine.js +71 -0
- package/dist/render/engine.js.map +1 -0
- package/dist/render/merge.d.ts +5 -0
- package/dist/render/merge.js +33 -0
- package/dist/render/merge.js.map +1 -0
- package/dist/utils/fs.d.ts +8 -0
- package/dist/utils/fs.js +42 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/git.d.ts +3 -0
- package/dist/utils/git.js +31 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/hash.d.ts +8 -0
- package/dist/utils/hash.js +22 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/yaml.d.ts +3 -0
- package/dist/utils/yaml.js +12 -0
- package/dist/utils/yaml.js.map +1 -0
- package/package.json +53 -0
- package/templates/addons/beads-dolt-backend/files/dolt-setup.sh +267 -0
- package/templates/addons/beads-dolt-backend/manifest.yaml +13 -0
- package/templates/addons/browser-testing/files/browser-smoke.sh +196 -0
- package/templates/addons/browser-testing/files/visual-qa.md +103 -0
- package/templates/addons/browser-testing/manifest.yaml +20 -0
- package/templates/addons/compliance-hipaa/files/hipaa-checks.sh +184 -0
- package/templates/addons/compliance-hipaa/files/hipaa-context.md +91 -0
- package/templates/addons/compliance-hipaa/manifest.yaml +15 -0
- package/templates/addons/compliance-soc2/files/soc2-checks.sh +232 -0
- package/templates/addons/compliance-soc2/files/soc2-context.md +147 -0
- package/templates/addons/compliance-soc2/manifest.yaml +15 -0
- package/templates/core/CLAUDE.md.hbs +70 -0
- package/templates/core/agents/architect.md.hbs +68 -0
- package/templates/core/agents/backend.md.hbs +27 -0
- package/templates/core/agents/frontend.md.hbs +25 -0
- package/templates/core/agents/quality.md.hbs +40 -0
- package/templates/core/agents/security.md.hbs +53 -0
- package/templates/core/context/project.md.hbs +60 -0
- package/templates/core/forge.yaml.hbs +69 -0
- package/templates/core/hooks/post-edit.sh.hbs +8 -0
- package/templates/core/hooks/pre-edit.sh.hbs +41 -0
- package/templates/core/hooks/session-start.sh.hbs +34 -0
- package/templates/core/pipeline/classify.sh.hbs +159 -0
- package/templates/core/pipeline/decompose.md.hbs +100 -0
- package/templates/core/pipeline/deliver.sh.hbs +171 -0
- package/templates/core/pipeline/execute.md.hbs +138 -0
- package/templates/core/pipeline/intake.sh.hbs +152 -0
- package/templates/core/pipeline/orchestrator.sh.hbs +361 -0
- package/templates/core/pipeline/verify.sh.hbs +160 -0
- package/templates/core/settings.json.hbs +55 -0
- package/templates/core/skill-creator.md.hbs +151 -0
- package/templates/core/skill-deliver.md.hbs +46 -0
- package/templates/core/skill-ingest.md.hbs +245 -0
- package/templates/presets/go/stack.md.hbs +133 -0
- package/templates/presets/python-fastapi/stack.md.hbs +101 -0
- package/templates/presets/react-next-ts/stack.md.hbs +77 -0
- package/templates/presets/sveltekit-ts/stack.md.hbs +116 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# CLAUDE.md — Agent Harness Instructions
|
|
2
|
+
|
|
3
|
+
> Generated by forge. These instructions apply to all AI agents working in this codebase.
|
|
4
|
+
|
|
5
|
+
## Read First
|
|
6
|
+
|
|
7
|
+
- Read `forge.yaml` for project configuration
|
|
8
|
+
- Read `.forge/context/stack.md` for stack-specific conventions
|
|
9
|
+
- Read `.forge/context/project.md` for project-specific knowledge
|
|
10
|
+
|
|
11
|
+
## Project
|
|
12
|
+
|
|
13
|
+
- **Name**: {{project.name}}
|
|
14
|
+
- **Stack preset**: {{project.preset}}
|
|
15
|
+
{{#if onboarding.description}}
|
|
16
|
+
- **Description**: {{onboarding.description}}
|
|
17
|
+
{{/if}}
|
|
18
|
+
{{#if onboarding.architecture}}
|
|
19
|
+
- **Architecture**: {{onboarding.architecture}}
|
|
20
|
+
{{/if}}
|
|
21
|
+
|
|
22
|
+
## Workflow
|
|
23
|
+
|
|
24
|
+
All non-trivial work flows through the `/deliver` command:
|
|
25
|
+
1. `/deliver "description"` — starts tracked work
|
|
26
|
+
2. Pipeline: intake → classify → decompose → execute → verify → deliver
|
|
27
|
+
3. Every edit is tracked via beads (`.forge/beads/`)
|
|
28
|
+
4. Verification runs automatically before delivery
|
|
29
|
+
|
|
30
|
+
## Spec Ingestion
|
|
31
|
+
|
|
32
|
+
For large specs, PRDs, or requirements documents:
|
|
33
|
+
1. `/ingest <spec-id>` — parse a spec into epics, features, and atomic tasks
|
|
34
|
+
2. Multi-pass analysis: extract → map domains → decompose → identify skills
|
|
35
|
+
3. Review and refine the plan before writing any code
|
|
36
|
+
4. Generate custom skills for repetitive patterns
|
|
37
|
+
5. Execute phase-by-phase through `/deliver`
|
|
38
|
+
|
|
39
|
+
Specs are stored in `.forge/specs/`. Run `forge ingest <file>` to add a new spec.
|
|
40
|
+
|
|
41
|
+
## Quality Gates
|
|
42
|
+
|
|
43
|
+
All code must pass before delivery:
|
|
44
|
+
- **Typecheck**: `{{commands.typecheck}}`
|
|
45
|
+
- **Lint**: `{{commands.lint}}`
|
|
46
|
+
- **Test**: `{{commands.test}}`
|
|
47
|
+
|
|
48
|
+
## Anti-Patterns (Blockers)
|
|
49
|
+
|
|
50
|
+
Do NOT introduce these — verification will reject them:
|
|
51
|
+
- `TODO` / `FIXME` comments
|
|
52
|
+
- `console.log` / `debugger` statements
|
|
53
|
+
- Hardcoded secrets or API keys
|
|
54
|
+
- `any` type (TypeScript projects)
|
|
55
|
+
|
|
56
|
+
## Agent Dispatch
|
|
57
|
+
|
|
58
|
+
Available agents for subtask assignment:
|
|
59
|
+
{{#each agents}}
|
|
60
|
+
- `{{this}}` — see `.forge/agents/{{this}}.md`
|
|
61
|
+
{{/each}}
|
|
62
|
+
|
|
63
|
+
## Task Tracking (bd)
|
|
64
|
+
|
|
65
|
+
- Work is tracked via `bd` (steveyegge/beads) — a Dolt-backed issue tracker
|
|
66
|
+
- `bd ready` — see tasks with no open blockers
|
|
67
|
+
- `bd update <id> --claim` — atomically claim a task
|
|
68
|
+
- `bd close <id> --reason "..."` — mark work complete
|
|
69
|
+
- The pre-edit hook blocks file changes without an in-progress task
|
|
70
|
+
- Use `/deliver --quick` for lightweight changes with minimal ceremony
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Architect
|
|
2
|
+
|
|
3
|
+
> Analyzes work requests and produces subtask decompositions with dependency-ordered wave plans.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
You are the Architect agent. You analyze a work request and break it into atomic, testable subtasks that can be executed by specialist agents. You never write code — you plan.
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
1. **Understand the request**: Read the work description, risk tier, and project context.
|
|
12
|
+
|
|
13
|
+
2. **Impact analysis**: Determine which files need to be modified or created. Consider:
|
|
14
|
+
- Data model changes
|
|
15
|
+
- Service/API changes
|
|
16
|
+
- UI component changes
|
|
17
|
+
- Route changes
|
|
18
|
+
- Test files needed
|
|
19
|
+
|
|
20
|
+
3. **Subtask decomposition**: Break the work into subtasks where each:
|
|
21
|
+
- Is atomic (one agent, one concern)
|
|
22
|
+
- Is testable (has a clear verification)
|
|
23
|
+
- Is scoped to specific files
|
|
24
|
+
- Has an appropriate agent assignment
|
|
25
|
+
|
|
26
|
+
4. **Wave planning**: Order subtasks into waves where:
|
|
27
|
+
- All subtasks in a wave can run in parallel
|
|
28
|
+
- No two subtasks in the same wave touch the same file
|
|
29
|
+
- Dependencies between waves are respected (wave 2 depends on wave 1)
|
|
30
|
+
|
|
31
|
+
5. **Output the plan** as JSON matching the schema below.
|
|
32
|
+
|
|
33
|
+
## Agent Roster
|
|
34
|
+
|
|
35
|
+
Available agents for assignment:
|
|
36
|
+
{{#each agents}}
|
|
37
|
+
- `{{this}}`
|
|
38
|
+
{{/each}}
|
|
39
|
+
|
|
40
|
+
## Output Format
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"subtasks": [
|
|
45
|
+
{
|
|
46
|
+
"id": "ST-1",
|
|
47
|
+
"title": "Short description of what to build",
|
|
48
|
+
"agent": "frontend|backend|quality|security",
|
|
49
|
+
"files": ["path/to/file.ts"],
|
|
50
|
+
"dependsOn": [],
|
|
51
|
+
"verification": "How to verify this subtask works"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"waves": [
|
|
55
|
+
{ "wave": 1, "subtasks": ["ST-1", "ST-2"] },
|
|
56
|
+
{ "wave": 2, "subtasks": ["ST-3"] }
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Constraints
|
|
62
|
+
|
|
63
|
+
- Maximum {{pipeline.max_subtasks}} subtasks per decomposition (default 15)
|
|
64
|
+
- Maximum {{pipeline.max_waves}} waves (default 5)
|
|
65
|
+
- No circular dependencies
|
|
66
|
+
- No file conflicts within a wave (two subtasks in the same wave cannot touch the same file)
|
|
67
|
+
- Every file modification must be assigned to exactly one subtask
|
|
68
|
+
- If the scope exceeds limits, break into smaller work requests
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Backend
|
|
2
|
+
|
|
3
|
+
> Builds API routes, server logic, services, and data access patterns.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
You are the Backend agent. You build server-side code including API endpoints, services, data access, and server utilities. You follow the conventions in `context/stack.md` strictly.
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
1. **Read the subtask**: Understand what server logic needs to be built or modified.
|
|
12
|
+
2. **Read context**: Load `.forge/context/stack.md` for framework-specific patterns.
|
|
13
|
+
3. **Check existing patterns**: Look at similar routes/services in the codebase.
|
|
14
|
+
4. **Implement**: Write the route/service following stack conventions.
|
|
15
|
+
5. **Verify**: Run `{{commands.typecheck}}` and confirm no type errors.
|
|
16
|
+
|
|
17
|
+
## Constraints
|
|
18
|
+
|
|
19
|
+
- Follow all patterns in `context/stack.md` — routing, data access, error handling
|
|
20
|
+
- Validate all input on every endpoint
|
|
21
|
+
- Check authentication on protected routes
|
|
22
|
+
- Return proper HTTP status codes
|
|
23
|
+
- Never hardcode secrets — use environment variables or secret management
|
|
24
|
+
- Never expose internal error details in responses
|
|
25
|
+
- Never use `any` type
|
|
26
|
+
- Never import client-only code in server modules
|
|
27
|
+
- Use parameterized queries — never interpolate user input into queries
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Frontend
|
|
2
|
+
|
|
3
|
+
> Builds UI components, pages, and client-side logic using stack-specific patterns.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
You are the Frontend agent. You build user interface components, pages, layouts, and client-side interactions. You follow the conventions in `context/stack.md` strictly.
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
1. **Read the subtask**: Understand what UI needs to be built or modified.
|
|
12
|
+
2. **Read context**: Load `.forge/context/stack.md` for framework-specific patterns.
|
|
13
|
+
3. **Check existing patterns**: Look at similar components in the codebase for conventions.
|
|
14
|
+
4. **Implement**: Write the component/page following stack conventions.
|
|
15
|
+
5. **Verify**: Run `{{commands.typecheck}}` and confirm no type errors.
|
|
16
|
+
|
|
17
|
+
## Constraints
|
|
18
|
+
|
|
19
|
+
- Follow all patterns in `context/stack.md` — framework-specific syntax, styling, component structure
|
|
20
|
+
- Add `data-testid` attributes on all interactive elements
|
|
21
|
+
- Use the project's styling system (Tailwind, CSS modules, etc.) — no inline styles
|
|
22
|
+
- Ensure accessibility: semantic HTML, aria labels, keyboard navigation
|
|
23
|
+
- Never import server-only code in client components
|
|
24
|
+
- Never fetch data in low-level components (atoms, molecules) — data flows down from pages/layouts
|
|
25
|
+
- Never use `any` type
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Quality
|
|
2
|
+
|
|
3
|
+
> Writes tests, verifies coverage, and validates that code meets quality standards.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
You are the Quality agent. You write tests for code produced by other agents and verify that the codebase meets quality standards.
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
1. **Read the subtask**: Understand what code was written and what needs testing.
|
|
12
|
+
2. **Read the code under test**: Understand the implementation, inputs, outputs, edge cases.
|
|
13
|
+
3. **Write tests**: Create test files that cover:
|
|
14
|
+
- Happy path (expected behavior)
|
|
15
|
+
- Error cases (invalid input, missing data)
|
|
16
|
+
- Edge cases (empty arrays, null values, boundary conditions)
|
|
17
|
+
- Integration points (does it work with dependencies?)
|
|
18
|
+
4. **Run tests**: Execute `{{commands.test}}` and verify all pass.
|
|
19
|
+
5. **Check coverage**: Ensure new code has adequate test coverage.
|
|
20
|
+
|
|
21
|
+
## Test Structure
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
describe('ModuleName', () => {
|
|
25
|
+
describe('functionName', () => {
|
|
26
|
+
it('returns expected result for valid input', () => {});
|
|
27
|
+
it('throws when input is invalid', () => {});
|
|
28
|
+
it('handles edge case correctly', () => {});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Constraints
|
|
34
|
+
|
|
35
|
+
- Tests must pass (`{{commands.test}}` exits 0)
|
|
36
|
+
- No snapshot tests (brittle)
|
|
37
|
+
- No mocking internal modules (test real behavior)
|
|
38
|
+
- Descriptive test names that explain the scenario
|
|
39
|
+
- Each test should be independent (no shared mutable state)
|
|
40
|
+
- Add `data-testid` attributes when testing UI components
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
> Reviews code for security vulnerabilities and ensures safe coding practices.
|
|
4
|
+
|
|
5
|
+
## Role
|
|
6
|
+
|
|
7
|
+
You are the Security agent. You review code for security issues, validate that security best practices are followed, and flag any concerns before code reaches production.
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
1. **Review the code changes**: Read all modified files in the subtask.
|
|
12
|
+
2. **Check for vulnerabilities**: Scan for common security issues:
|
|
13
|
+
- Hardcoded secrets, API keys, or tokens
|
|
14
|
+
- SQL injection / NoSQL injection
|
|
15
|
+
- XSS (cross-site scripting)
|
|
16
|
+
- CSRF (cross-site request forgery)
|
|
17
|
+
- Unvalidated user input
|
|
18
|
+
- Missing authentication checks on protected endpoints
|
|
19
|
+
- Missing authorization checks (user can only access their own data)
|
|
20
|
+
- Insecure direct object references
|
|
21
|
+
- eval() or dynamic code execution
|
|
22
|
+
- Disabled security controls (eslint-disable, @ts-ignore for security rules)
|
|
23
|
+
3. **Validate patterns**:
|
|
24
|
+
- Secrets come from environment or secret manager, never hardcoded
|
|
25
|
+
- All endpoints validate input
|
|
26
|
+
- Auth checks on every protected route
|
|
27
|
+
- Error messages don't leak internal details
|
|
28
|
+
- Sensitive data is not logged
|
|
29
|
+
4. **Output findings**: List any issues found with severity and fix recommendations.
|
|
30
|
+
|
|
31
|
+
## Output Format
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"status": "pass|fail",
|
|
36
|
+
"findings": [
|
|
37
|
+
{
|
|
38
|
+
"severity": "critical|high|medium|low",
|
|
39
|
+
"file": "path/to/file.ts",
|
|
40
|
+
"line": 42,
|
|
41
|
+
"issue": "Description of the security issue",
|
|
42
|
+
"fix": "How to fix it"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Constraints
|
|
49
|
+
|
|
50
|
+
- Any `critical` or `high` finding fails the review
|
|
51
|
+
- Never approve code with hardcoded secrets
|
|
52
|
+
- Never approve code with missing auth on protected endpoints
|
|
53
|
+
- When in doubt, escalate to T3 (flag for human review)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Project Context — {{project.name}}
|
|
2
|
+
|
|
3
|
+
> Project-specific knowledge for agents. This file is yours — forge will never overwrite it on upgrade.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
{{#if onboarding.description}}
|
|
8
|
+
{{onboarding.description}}
|
|
9
|
+
{{else}}
|
|
10
|
+
<!-- Describe what you're building -->
|
|
11
|
+
{{/if}}
|
|
12
|
+
|
|
13
|
+
## Project Type
|
|
14
|
+
|
|
15
|
+
{{#if onboarding.projectType}}
|
|
16
|
+
**Type**: {{onboarding.projectType}}
|
|
17
|
+
{{/if}}
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
{{#if onboarding.architecture}}
|
|
22
|
+
**Style**: {{onboarding.architecture}}
|
|
23
|
+
{{else}}
|
|
24
|
+
<!-- Describe your architecture style (monolith, client-server, microservices, etc.) -->
|
|
25
|
+
{{/if}}
|
|
26
|
+
|
|
27
|
+
{{#if has_modules}}
|
|
28
|
+
## Key Modules
|
|
29
|
+
|
|
30
|
+
{{#each onboarding.modules}}
|
|
31
|
+
- **{{this}}**
|
|
32
|
+
{{/each}}
|
|
33
|
+
{{else}}
|
|
34
|
+
## Key Modules
|
|
35
|
+
|
|
36
|
+
<!-- List your main features or modules -->
|
|
37
|
+
{{/if}}
|
|
38
|
+
|
|
39
|
+
## Key Conventions
|
|
40
|
+
|
|
41
|
+
<!-- Anything specific to YOUR project not covered by the stack context -->
|
|
42
|
+
<!-- Example: "All API responses use our standard envelope: { data, error, meta }" -->
|
|
43
|
+
|
|
44
|
+
## Sensitive Paths
|
|
45
|
+
|
|
46
|
+
{{#if has_sensitive}}
|
|
47
|
+
{{onboarding.sensitivePaths}}
|
|
48
|
+
{{else}}
|
|
49
|
+
<!-- Paths that need extra care — auth, payments, PII, etc. -->
|
|
50
|
+
<!-- These will auto-escalate to T3 risk tier during classification -->
|
|
51
|
+
{{/if}}
|
|
52
|
+
|
|
53
|
+
## Domain Knowledge
|
|
54
|
+
|
|
55
|
+
{{#if has_domain_rules}}
|
|
56
|
+
{{onboarding.domainRules}}
|
|
57
|
+
{{else}}
|
|
58
|
+
<!-- Business logic that agents need to understand -->
|
|
59
|
+
<!-- Example: "Users have one of three roles: patient, provider, admin." -->
|
|
60
|
+
{{/if}}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# forge.yaml — Agent harness configuration
|
|
2
|
+
# Generated by: forge init v0.1.0
|
|
3
|
+
|
|
4
|
+
version: 1
|
|
5
|
+
|
|
6
|
+
project:
|
|
7
|
+
name: "{{project.name}}"
|
|
8
|
+
preset: "{{project.preset}}"
|
|
9
|
+
|
|
10
|
+
# Verification commands — what the pipeline calls
|
|
11
|
+
commands:
|
|
12
|
+
typecheck: "{{commands.typecheck}}"
|
|
13
|
+
lint: "{{commands.lint}}"
|
|
14
|
+
test: "{{commands.test}}"
|
|
15
|
+
{{#if has_format}}
|
|
16
|
+
format: "{{commands.format}}"
|
|
17
|
+
{{/if}}
|
|
18
|
+
dev: "{{commands.dev}}"
|
|
19
|
+
|
|
20
|
+
# Which agents are available for dispatch
|
|
21
|
+
agents:
|
|
22
|
+
{{#each agents}}
|
|
23
|
+
- {{this}}
|
|
24
|
+
{{/each}}
|
|
25
|
+
|
|
26
|
+
# What verification checks run and how
|
|
27
|
+
verification:
|
|
28
|
+
typecheck: true
|
|
29
|
+
lint: true
|
|
30
|
+
test: true
|
|
31
|
+
coverage:
|
|
32
|
+
enabled: false
|
|
33
|
+
threshold: 70
|
|
34
|
+
security:
|
|
35
|
+
enabled: false
|
|
36
|
+
command: "semgrep --config=auto"
|
|
37
|
+
anti_patterns:
|
|
38
|
+
enabled: true
|
|
39
|
+
blockers:
|
|
40
|
+
- "TODO"
|
|
41
|
+
- "FIXME"
|
|
42
|
+
- "console.log"
|
|
43
|
+
- "debugger"
|
|
44
|
+
browser:
|
|
45
|
+
enabled: false
|
|
46
|
+
|
|
47
|
+
# Pipeline behavior
|
|
48
|
+
pipeline:
|
|
49
|
+
max_retries: 3
|
|
50
|
+
max_subtasks: 15
|
|
51
|
+
max_waves: 5
|
|
52
|
+
auto_pr: {{auto_pr}}
|
|
53
|
+
|
|
54
|
+
# Task tracking via bd (steveyegge/beads)
|
|
55
|
+
tracking:
|
|
56
|
+
backend: bd
|
|
57
|
+
enforce_tracking: true
|
|
58
|
+
|
|
59
|
+
# Risk classification
|
|
60
|
+
risk:
|
|
61
|
+
t3_paths: []
|
|
62
|
+
t3_keywords:
|
|
63
|
+
- "authentication"
|
|
64
|
+
- "encryption"
|
|
65
|
+
- "PII"
|
|
66
|
+
- "payment"
|
|
67
|
+
|
|
68
|
+
# Installed addons
|
|
69
|
+
addons: []
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# .forge/hooks/post-edit.sh — PostToolUse hook for Edit|Write
|
|
3
|
+
# Lightweight hook — bd handles tracking via its own git hooks
|
|
4
|
+
|
|
5
|
+
# No-op: bd tracks work through its git integration (bd setup claude)
|
|
6
|
+
# and its own commit hooks. No need to manually update task state here.
|
|
7
|
+
|
|
8
|
+
exit 0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# .forge/hooks/pre-edit.sh — PreToolUse hook for Edit|Write
|
|
3
|
+
# Blocks file edits without an active (in-progress) bd task
|
|
4
|
+
|
|
5
|
+
INPUT=$(cat)
|
|
6
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.file_path // .path // empty' 2>/dev/null)
|
|
7
|
+
|
|
8
|
+
# If we can't determine the file path, allow (fail open)
|
|
9
|
+
if [ -z "$FILE_PATH" ]; then
|
|
10
|
+
exit 0
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
# Allow harness files, docs, and config — these don't need tracking
|
|
14
|
+
case "$FILE_PATH" in
|
|
15
|
+
*.md|*.yaml|*.yml|*.txt|*.gitignore|*.env*) exit 0 ;;
|
|
16
|
+
.forge/*|.claude/*|.beads/*|forge.yaml|CLAUDE.md) exit 0 ;;
|
|
17
|
+
package.json|package-lock.json|tsconfig.json) exit 0 ;;
|
|
18
|
+
pyproject.toml|requirements.txt|go.mod|go.sum) exit 0 ;;
|
|
19
|
+
Cargo.toml|Cargo.lock) exit 0 ;;
|
|
20
|
+
esac
|
|
21
|
+
|
|
22
|
+
# Check if bd is available
|
|
23
|
+
if ! command -v bd &>/dev/null; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Check if tracking is enforced
|
|
28
|
+
ENFORCE=$(grep -oP 'enforce_tracking:\s*\K\w+' forge.yaml 2>/dev/null || echo "true")
|
|
29
|
+
if [ "$ENFORCE" != "true" ]; then
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Check for in-progress tasks
|
|
34
|
+
IN_PROGRESS=$(bd list --status in_progress --json 2>/dev/null)
|
|
35
|
+
if [ -z "$IN_PROGRESS" ] || [ "$IN_PROGRESS" = "[]" ]; then
|
|
36
|
+
echo '{"decision":"block","reason":"No active task. Run /deliver to start tracked work, or use /deliver --quick for a lightweight change."}'
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Allow the edit
|
|
41
|
+
exit 0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# .forge/hooks/session-start.sh — SessionStart hook
|
|
3
|
+
# Verifies bd (beads) is available and shows active work
|
|
4
|
+
|
|
5
|
+
# Check that bd is available
|
|
6
|
+
if ! command -v bd &>/dev/null; then
|
|
7
|
+
echo "[forge] Warning: bd (beads) is not installed. Work tracking requires bd."
|
|
8
|
+
echo "[forge] Install with: brew install beads"
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# Check that forge.yaml exists
|
|
13
|
+
if [ ! -f "forge.yaml" ]; then
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Check that beads is initialized
|
|
18
|
+
if [ ! -d ".beads" ]; then
|
|
19
|
+
echo "[forge] Beads not initialized. Initializing..."
|
|
20
|
+
bd init --quiet 2>/dev/null
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Show ready tasks
|
|
24
|
+
READY=$(bd ready --json 2>/dev/null)
|
|
25
|
+
if [ -n "$READY" ] && [ "$READY" != "[]" ]; then
|
|
26
|
+
COUNT=$(echo "$READY" | jq 'length' 2>/dev/null)
|
|
27
|
+
echo "[forge] ${COUNT} task(s) ready to work on:"
|
|
28
|
+
echo "$READY" | jq -r '.[] | " \(.id) [P\(.priority)] \(.title)"' 2>/dev/null
|
|
29
|
+
echo "[forge] Claim one with: bd update <id> --claim"
|
|
30
|
+
else
|
|
31
|
+
echo "[forge] No tasks ready. Start work with: /deliver \"description\""
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
exit 0
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# forge pipeline — classify: risk tier assignment
|
|
3
|
+
# Generated by: forge init
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
FORGE_DIR=".forge"
|
|
7
|
+
CONFIG_FILE="${FORGE_DIR}/forge.yaml"
|
|
8
|
+
|
|
9
|
+
DESCRIPTION="${1:-}"
|
|
10
|
+
|
|
11
|
+
if [[ -z "$DESCRIPTION" ]]; then
|
|
12
|
+
echo '{"error":"No description provided"}' >&2
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
DESC_LOWER=$(echo "$DESCRIPTION" | tr '[:upper:]' '[:lower:]')
|
|
17
|
+
|
|
18
|
+
# --- Default risk patterns ---
|
|
19
|
+
|
|
20
|
+
# T3: Critical — auth, encryption, PII, payment, security
|
|
21
|
+
T3_PATTERNS=(
|
|
22
|
+
"auth" "authentication" "authorization" "login" "logout" "session"
|
|
23
|
+
"password" "credential" "secret" "token" "jwt" "oauth"
|
|
24
|
+
"encrypt" "decrypt" "hash" "salt" "certificate" "ssl" "tls"
|
|
25
|
+
"pii" "personal data" "gdpr" "hipaa" "phi" "ssn" "social security"
|
|
26
|
+
"payment" "stripe" "billing" "credit card" "charge" "subscription"
|
|
27
|
+
"security" "vulnerability" "xss" "csrf" "injection" "sanitiz"
|
|
28
|
+
"permission" "rbac" "role" "access control" "privilege"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# T2: Moderate — business logic, API, services, state, DB
|
|
32
|
+
T2_PATTERNS=(
|
|
33
|
+
"api" "endpoint" "route" "handler" "controller" "middleware"
|
|
34
|
+
"database" "migration" "schema" "query" "sql" "table" "column" "index"
|
|
35
|
+
"service" "repository" "model" "entity" "domain"
|
|
36
|
+
"state" "store" "reducer" "action" "dispatch" "context"
|
|
37
|
+
"business logic" "workflow" "validation" "transform"
|
|
38
|
+
"integration" "webhook" "event" "queue" "job" "worker"
|
|
39
|
+
"cache" "redis" "session" "cookie"
|
|
40
|
+
"server" "backend" "ssr" "hooks.server"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# T1: Low — UI, docs, styling, tests, config
|
|
44
|
+
T1_PATTERNS=(
|
|
45
|
+
"style" "css" "tailwind" "color" "font" "spacing" "margin" "padding"
|
|
46
|
+
"ui" "component" "layout" "responsive" "mobile" "desktop"
|
|
47
|
+
"doc" "readme" "comment" "changelog" "documentation"
|
|
48
|
+
"test" "spec" "vitest" "jest" "playwright" "e2e" "unit test"
|
|
49
|
+
"config" "eslint" "prettier" "tsconfig" "vite.config" "package.json"
|
|
50
|
+
"typo" "rename" "reformat" "lint" "cleanup"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# --- Read custom risk paths/keywords from forge.yaml ---
|
|
54
|
+
|
|
55
|
+
read_risk_keywords() {
|
|
56
|
+
local tier="$1"
|
|
57
|
+
local file="$CONFIG_FILE"
|
|
58
|
+
if [[ ! -f "$file" ]]; then
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
# Extract keywords under risk.<tier>.keywords as a simple list
|
|
62
|
+
sed -n "/^risk:/,/^[^ ]/p" "$file" | \
|
|
63
|
+
sed -n "/${tier}:/,/^ [^ ]/p" | \
|
|
64
|
+
sed -n '/keywords:/,/^ [^ ]/p' | \
|
|
65
|
+
grep '^ *- ' | sed 's/^ *- *//' | tr '[:upper:]' '[:lower:]'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
read_risk_paths() {
|
|
69
|
+
local tier="$1"
|
|
70
|
+
local file="$CONFIG_FILE"
|
|
71
|
+
if [[ ! -f "$file" ]]; then
|
|
72
|
+
return
|
|
73
|
+
fi
|
|
74
|
+
sed -n "/^risk:/,/^[^ ]/p" "$file" | \
|
|
75
|
+
sed -n "/${tier}:/,/^ [^ ]/p" | \
|
|
76
|
+
sed -n '/paths:/,/^ [^ ]/p' | \
|
|
77
|
+
grep '^ *- ' | sed 's/^ *- *//' | tr '[:upper:]' '[:lower:]'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# --- Classification ---
|
|
81
|
+
|
|
82
|
+
match_count() {
|
|
83
|
+
local count=0
|
|
84
|
+
for pattern in "$@"; do
|
|
85
|
+
if echo "$DESC_LOWER" | grep -qF "$pattern"; then
|
|
86
|
+
count=$((count + 1))
|
|
87
|
+
fi
|
|
88
|
+
done
|
|
89
|
+
echo "$count"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
t3_hits=$(match_count "${T3_PATTERNS[@]}")
|
|
93
|
+
t2_hits=$(match_count "${T2_PATTERNS[@]}")
|
|
94
|
+
t1_hits=$(match_count "${T1_PATTERNS[@]}")
|
|
95
|
+
|
|
96
|
+
# Check custom keywords from config
|
|
97
|
+
while IFS= read -r kw; do
|
|
98
|
+
[[ -z "$kw" ]] && continue
|
|
99
|
+
if echo "$DESC_LOWER" | grep -qF "$kw"; then
|
|
100
|
+
t3_hits=$((t3_hits + 2)) # custom keywords weighted higher
|
|
101
|
+
fi
|
|
102
|
+
done < <(read_risk_keywords "t3")
|
|
103
|
+
|
|
104
|
+
while IFS= read -r kw; do
|
|
105
|
+
[[ -z "$kw" ]] && continue
|
|
106
|
+
if echo "$DESC_LOWER" | grep -qF "$kw"; then
|
|
107
|
+
t2_hits=$((t2_hits + 2))
|
|
108
|
+
fi
|
|
109
|
+
done < <(read_risk_keywords "t2")
|
|
110
|
+
|
|
111
|
+
while IFS= read -r kw; do
|
|
112
|
+
[[ -z "$kw" ]] && continue
|
|
113
|
+
if echo "$DESC_LOWER" | grep -qF "$kw"; then
|
|
114
|
+
t1_hits=$((t1_hits + 2))
|
|
115
|
+
fi
|
|
116
|
+
done < <(read_risk_keywords "t1")
|
|
117
|
+
|
|
118
|
+
# Check custom paths from config
|
|
119
|
+
while IFS= read -r p; do
|
|
120
|
+
[[ -z "$p" ]] && continue
|
|
121
|
+
if echo "$DESC_LOWER" | grep -qF "$p"; then
|
|
122
|
+
t3_hits=$((t3_hits + 3))
|
|
123
|
+
fi
|
|
124
|
+
done < <(read_risk_paths "t3")
|
|
125
|
+
|
|
126
|
+
while IFS= read -r p; do
|
|
127
|
+
[[ -z "$p" ]] && continue
|
|
128
|
+
if echo "$DESC_LOWER" | grep -qF "$p"; then
|
|
129
|
+
t2_hits=$((t2_hits + 3))
|
|
130
|
+
fi
|
|
131
|
+
done < <(read_risk_paths "t2")
|
|
132
|
+
|
|
133
|
+
# Determine tier — T3 wins over T2 wins over T1
|
|
134
|
+
TIER="T1"
|
|
135
|
+
REASON="Default: no high-risk patterns detected"
|
|
136
|
+
|
|
137
|
+
if [[ $t3_hits -gt 0 ]]; then
|
|
138
|
+
TIER="T3"
|
|
139
|
+
REASON="Matched ${t3_hits} critical-risk pattern(s): auth, encryption, PII, payment, or security-related"
|
|
140
|
+
elif [[ $t2_hits -gt 0 ]]; then
|
|
141
|
+
TIER="T2"
|
|
142
|
+
REASON="Matched ${t2_hits} moderate-risk pattern(s): business logic, API, database, or state management"
|
|
143
|
+
elif [[ $t1_hits -gt 0 ]]; then
|
|
144
|
+
TIER="T1"
|
|
145
|
+
REASON="Matched ${t1_hits} low-risk pattern(s): UI, docs, styling, tests, or config"
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# T3 always wins if any T3 match, regardless of other hits
|
|
149
|
+
if [[ $t3_hits -gt 0 ]]; then
|
|
150
|
+
TIER="T3"
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
jq -n \
|
|
154
|
+
--arg tier "$TIER" \
|
|
155
|
+
--arg reason "$REASON" \
|
|
156
|
+
--argjson t3_hits "$t3_hits" \
|
|
157
|
+
--argjson t2_hits "$t2_hits" \
|
|
158
|
+
--argjson t1_hits "$t1_hits" \
|
|
159
|
+
'{tier:$tier, reason:$reason, hits:{t3:$t3_hits, t2:$t2_hits, t1:$t1_hits}}'
|