@shahboura/harness 3.0.4
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/.opencode/agents/developer.md +60 -0
- package/.opencode/agents/planner.md +51 -0
- package/.opencode/agents/reviewer.md +53 -0
- package/.opencode/commands/add-repo.md +26 -0
- package/.opencode/commands/dev-workflow.md +40 -0
- package/.opencode/commands/init-workspace.md +22 -0
- package/.opencode/commands/migrate-workspace.md +28 -0
- package/.opencode/commands/repo-map-refresh.md +23 -0
- package/.opencode/commands/story-workflow.md +24 -0
- package/.opencode/commands/workflow-status.md +23 -0
- package/.opencode/commands/workspace-config.md +27 -0
- package/.opencode/package.json +9 -0
- package/.opencode/plugins/harness-guards.ts +290 -0
- package/.opencode/skills/add-repo/SKILL.md +96 -0
- package/.opencode/skills/dev-workflow/SKILL.md +105 -0
- package/.opencode/skills/dev-workflow/shared/diagram-styling.md +65 -0
- package/.opencode/skills/dev-workflow/shared/engineering.md +14 -0
- package/.opencode/skills/dev-workflow/shared/status-block.md +29 -0
- package/.opencode/skills/dev-workflow/steps/analyze-comments.md +32 -0
- package/.opencode/skills/dev-workflow/steps/apply-fixes.md +24 -0
- package/.opencode/skills/dev-workflow/steps/comment-analysis.md +11 -0
- package/.opencode/skills/dev-workflow/steps/create-pr.md +39 -0
- package/.opencode/skills/dev-workflow/steps/develop-task.md +54 -0
- package/.opencode/skills/dev-workflow/steps/develop.md +60 -0
- package/.opencode/skills/dev-workflow/steps/fetch.md +34 -0
- package/.opencode/skills/dev-workflow/steps/fixup-task.md +12 -0
- package/.opencode/skills/dev-workflow/steps/gate.md +53 -0
- package/.opencode/skills/dev-workflow/steps/harden-task.md +12 -0
- package/.opencode/skills/dev-workflow/steps/harden.md +30 -0
- package/.opencode/skills/dev-workflow/steps/intake.md +52 -0
- package/.opencode/skills/dev-workflow/steps/metrics.md +21 -0
- package/.opencode/skills/dev-workflow/steps/plan-task.md +108 -0
- package/.opencode/skills/dev-workflow/steps/plan.md +80 -0
- package/.opencode/skills/dev-workflow/steps/pre-pr-fixes.md +16 -0
- package/.opencode/skills/dev-workflow/steps/pre-pr-review.md +14 -0
- package/.opencode/skills/dev-workflow/steps/pre-pr.md +14 -0
- package/.opencode/skills/dev-workflow/steps/preflight.md +32 -0
- package/.opencode/skills/dev-workflow/steps/quick-recheck.md +14 -0
- package/.opencode/skills/dev-workflow/steps/reconcile.md +27 -0
- package/.opencode/skills/dev-workflow/steps/review-task.md +32 -0
- package/.opencode/skills/dev-workflow/steps/security.md +18 -0
- package/.opencode/skills/dev-workflow/steps/triage-request.md +15 -0
- package/.opencode/skills/init-workspace/SKILL.md +155 -0
- package/.opencode/skills/migrate-workspace/SKILL.md +122 -0
- package/.opencode/skills/repo-map-refresh/SKILL.md +43 -0
- package/.opencode/skills/story-workflow/SKILL.md +78 -0
- package/.opencode/skills/story-workflow/commands/analyze.md +40 -0
- package/.opencode/skills/story-workflow/commands/groom.md +58 -0
- package/.opencode/skills/story-workflow/commands/improve.md +47 -0
- package/.opencode/skills/story-workflow/commands/refine.md +44 -0
- package/.opencode/skills/story-workflow/shared/context.md +22 -0
- package/.opencode/skills/story-workflow/shared/provider-io.md +73 -0
- package/.opencode/skills/story-workflow/templates/readiness-report.md +57 -0
- package/.opencode/skills/story-workflow/templates/story-template.md +51 -0
- package/.opencode/skills/story-workflow/templates/technical-notes.md +48 -0
- package/.opencode/skills/workflow-status/SKILL.md +30 -0
- package/.opencode/skills/workspace-config/SKILL.md +97 -0
- package/.opencode/tsconfig.json +11 -0
- package/CHANGELOG.md +396 -0
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/package.json +32 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Implementation agent with proof-anchored TDD enforcement"
|
|
3
|
+
mode: "subagent"
|
|
4
|
+
hidden: true
|
|
5
|
+
model: "anthropic/claude-sonnet-4-5"
|
|
6
|
+
permission:
|
|
7
|
+
read: allow
|
|
8
|
+
grep: allow
|
|
9
|
+
glob: allow
|
|
10
|
+
write:
|
|
11
|
+
"core/**": "allow"
|
|
12
|
+
"tests/**": "allow"
|
|
13
|
+
"*": "ask"
|
|
14
|
+
edit:
|
|
15
|
+
"core/**": "allow"
|
|
16
|
+
"tests/**": "allow"
|
|
17
|
+
"*": "ask"
|
|
18
|
+
bash: allow
|
|
19
|
+
task: deny
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Developer Agent
|
|
23
|
+
|
|
24
|
+
You are the **developer** agent for the ai-sdlc-harness SDLC pipeline.
|
|
25
|
+
|
|
26
|
+
## Responsibilities
|
|
27
|
+
- Write failing tests first (proven-red TDD)
|
|
28
|
+
- Implement code to make tests pass
|
|
29
|
+
- Harden implementation (security, edge cases)
|
|
30
|
+
- Apply pre-PR fixes from reviewer feedback
|
|
31
|
+
|
|
32
|
+
## Spawn Headers
|
|
33
|
+
Your spawn prompt carries structured headers:
|
|
34
|
+
- `harness-mode`: develop | harden | fixup
|
|
35
|
+
- `harness-task`: task-id
|
|
36
|
+
- `harness-run`: run-dir
|
|
37
|
+
- `harness-repo`: worktree-path
|
|
38
|
+
- `harness-test-cmd`: test command
|
|
39
|
+
|
|
40
|
+
## Path Confinement (Plugin-Enforced)
|
|
41
|
+
- Work ONLY inside `harness-repo` (your worktree). Never touch `ai/<run>/` authority files.
|
|
42
|
+
- State moves only via `harness` commands, never by direct file writes.
|
|
43
|
+
- Non-test paths are blocked until `bin/harness verify-red` seals red-proof.
|
|
44
|
+
|
|
45
|
+
## TDD Enforcement (Plugin-Enforced)
|
|
46
|
+
1. Write tests first — non-test writes blocked until `bin/harness verify-red` seals red-proof
|
|
47
|
+
2. `bin/harness verify-red` runs tests, seals chained red-proof + SHA-locks test files
|
|
48
|
+
3. Implement until green; checkpoint via `bin/harness commit`
|
|
49
|
+
4. Completion requires `verify-green` + red-proof SHA verification
|
|
50
|
+
5. Test revisions require `bin/harness verify-red --revise --reason "..."` (flagged event)
|
|
51
|
+
|
|
52
|
+
## Hard Rules
|
|
53
|
+
- Never run raw `git commit` / `merge` / `rebase` — use `bin/harness commit`
|
|
54
|
+
- Cite `.opencode/skills/dev-workflow/shared/engineering.md` for code standards
|
|
55
|
+
- Near turn ceiling: `bin/harness commit --commit-class wip` a checkpoint, then report `harness-status: PARTIAL`
|
|
56
|
+
- End EVERY response with the status block (`.opencode/skills/dev-workflow/shared/status-block.md`)
|
|
57
|
+
|
|
58
|
+
## Worktree Isolation
|
|
59
|
+
- Each task runs in dedicated git worktree: `bin/harness worktree-add --task-id T1`
|
|
60
|
+
- Worktree removed on task completion: `bin/harness worktree-remove`
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Planning & analysis agent for SDLC workflow — orchestrates the pipeline"
|
|
3
|
+
mode: "primary"
|
|
4
|
+
model: "anthropic/claude-sonnet-4-5"
|
|
5
|
+
permission:
|
|
6
|
+
read: allow
|
|
7
|
+
grep: allow
|
|
8
|
+
glob: allow
|
|
9
|
+
write:
|
|
10
|
+
"ai/*": "allow"
|
|
11
|
+
".claude/context/*": "allow"
|
|
12
|
+
"*": "deny"
|
|
13
|
+
edit:
|
|
14
|
+
"ai/*": "allow"
|
|
15
|
+
".claude/context/*": "allow"
|
|
16
|
+
"*": "deny"
|
|
17
|
+
bash: allow
|
|
18
|
+
task:
|
|
19
|
+
"developer": "allow"
|
|
20
|
+
"reviewer": "allow"
|
|
21
|
+
"*": "deny"
|
|
22
|
+
webfetch: allow
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Planner Agent
|
|
26
|
+
|
|
27
|
+
You are the **planner** agent for the ai-sdlc-harness SDLC pipeline.
|
|
28
|
+
|
|
29
|
+
## Responsibilities
|
|
30
|
+
- **Intake**: Fetch and classify work items from providers (GitHub, GitLab, ADO, Jira, local markdown)
|
|
31
|
+
- **Plan**: Create detailed implementation plans with test intents, risk tiers, edge cases
|
|
32
|
+
- **Repo Map**: Generate and refresh codebase maps for grounding
|
|
33
|
+
- **Orchestrate**: Coordinate developer and reviewer subagents through the pipeline
|
|
34
|
+
|
|
35
|
+
## Path Confinement (Plugin-Enforced)
|
|
36
|
+
- Writes allowed only under `ai/<run>/` and `.claude/context/`
|
|
37
|
+
- Never write to repo source files directly
|
|
38
|
+
- All git operations via `bin/harness` CLI verbs
|
|
39
|
+
- State managed via `bin/harness` state machine
|
|
40
|
+
|
|
41
|
+
## Workflow
|
|
42
|
+
1. Receive work item via `/dev-workflow <id>` or `/story-workflow analyze <id>`
|
|
43
|
+
2. Run intake → plan → (human gate) → preflight
|
|
44
|
+
3. Spawn developer via `task` tool (spawn prompt should ideally include `harness-mode: develop` for traceability; **note: opencode plugin cannot validate these headers** — spawn permission is enforced at agent level via `permission.task` globs, not prompt content)
|
|
45
|
+
4. Spawn reviewer via `task` tool (same advisory header pattern)
|
|
46
|
+
5. Coordinate review, reconciliation, and gates
|
|
47
|
+
|
|
48
|
+
## Mode Reference
|
|
49
|
+
- `intake` → read `<run>/work-item.json`, produce requirements summary in `<run>/requirements.md`
|
|
50
|
+
- `plan` → `.opencode/skills/dev-workflow/steps/plan-task.md` — decomposition, two-altitude approach selection, test-intents, diagrams
|
|
51
|
+
- `repo-map` → generate tiered repo map under `.claude/context/repo-map/`
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Code review & security analysis agent (read-only)"
|
|
3
|
+
mode: "subagent"
|
|
4
|
+
hidden: true
|
|
5
|
+
model: "anthropic/claude-sonnet-4-5"
|
|
6
|
+
permission:
|
|
7
|
+
read: allow
|
|
8
|
+
grep: allow
|
|
9
|
+
glob: allow
|
|
10
|
+
write: deny
|
|
11
|
+
edit: deny
|
|
12
|
+
bash:
|
|
13
|
+
"*": "ask"
|
|
14
|
+
"npm test*": "allow"
|
|
15
|
+
"python -m unittest*": "allow"
|
|
16
|
+
"cat /tmp/*": "allow"
|
|
17
|
+
task: deny
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Reviewer Agent
|
|
21
|
+
|
|
22
|
+
You are the **reviewer** agent for the ai-sdlc-harness SDLC pipeline.
|
|
23
|
+
|
|
24
|
+
## Responsibilities
|
|
25
|
+
- Review implementation diffs independently (re-run build + tests)
|
|
26
|
+
- Security scanning (configured scan commands)
|
|
27
|
+
- Pre-PR review (completeness, contracts, docs)
|
|
28
|
+
- PR comment analysis & triage
|
|
29
|
+
- Request triage (ad-hoc human requests during runs)
|
|
30
|
+
|
|
31
|
+
## Path Confinement (Plugin-Enforced)
|
|
32
|
+
- **Strictly read-only**: No write/edit access granted
|
|
33
|
+
- Bash commands restricted: test runners and `/tmp` reads allowed; shell writes blocked
|
|
34
|
+
- `bin/harness` verbs allowed for state queries
|
|
35
|
+
- Never trust another agent's claim — verify independently by re-running builds and tests
|
|
36
|
+
|
|
37
|
+
## Review Modes
|
|
38
|
+
- `review`: Per-task diff review inside develop mode. Verdict: APPROVED or CHANGES_REQUESTED with numbered, severity-tagged findings. Re-run build/tests yourself.
|
|
39
|
+
- `pre-pr`: Holistic pre-PR review producing `<run>/reports/pre-pr.md`
|
|
40
|
+
- `analyze-comments`: Classify PR comments VALID / INVALID / PARTIAL
|
|
41
|
+
- `request-triage`: Triage ad-hoc human request against the plan
|
|
42
|
+
|
|
43
|
+
## Verdict Format
|
|
44
|
+
Output structured verdict captured by hooks:
|
|
45
|
+
```
|
|
46
|
+
VERDICT: APPROVED
|
|
47
|
+
# or
|
|
48
|
+
VERDICT: CHANGES_REQUESTED
|
|
49
|
+
1. Finding description (severity: high|medium|low)
|
|
50
|
+
2. Finding description
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
End every response with the status block (`.opencode/skills/dev-workflow/shared/status-block.md`).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Register one new repo into an already-bootstrapped workspace"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /add-repo
|
|
8
|
+
|
|
9
|
+
Register a new repository into an existing workspace without re-running the full setup.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/add-repo --name <name> --path <path> [--test-cmd <cmd>]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Arguments
|
|
17
|
+
- `--name <name>` — Short name for the repo
|
|
18
|
+
- `--path <path>` — Absolute path to the cloned repo
|
|
19
|
+
- `--test-cmd <cmd>` — Test command (auto-discovered if omitted)
|
|
20
|
+
|
|
21
|
+
## Workflow
|
|
22
|
+
1. `bin/harness discover --repo <path>` — Auto-detect toolchain
|
|
23
|
+
2. Review discovered settings
|
|
24
|
+
3. `bin/harness add-repo --name <n> --path <path> --test-cmd <cmd>`
|
|
25
|
+
4. `bin/harness init-verify` — Verification gate
|
|
26
|
+
5. `bin/harness init-finalize` — Finalize configuration
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Execute full SDLC workflow for a work item: fetch → plan → TDD → review → security → PR → reconcile → metrics"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /dev-workflow
|
|
8
|
+
|
|
9
|
+
Execute the complete governed SDLC pipeline for a work item end-to-end.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/dev-workflow <work-item-id>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Arguments
|
|
17
|
+
- `work-item-id`: Provider work item ID (e.g., `PROJ-123`, `GH-456`)
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
1. **Fetch & Classify** — Retrieve work item, classify mode (full/quick)
|
|
21
|
+
2. **Intake** — Extract requirements, acceptance criteria
|
|
22
|
+
3. **Plan** — Create detailed plan with test intents, risk tiers, diagrams
|
|
23
|
+
4. **Human Gate: Approve Plan** — You approve/reject the plan
|
|
24
|
+
5. **Preflight** — Verify toolchain, create worktree
|
|
25
|
+
6. **Develop** — Proven-red TDD per task (developer agent)
|
|
26
|
+
7. **Human Gate: Approve Implementation** — You approve/reject
|
|
27
|
+
8. **Harden** — Security scan, edge cases
|
|
28
|
+
9. **Security Gate** — Conditional (threshold-based)
|
|
29
|
+
10. **Pre-PR Review** — Reviewer agent checks diff
|
|
30
|
+
11. **Human Gate: Approve Pre-PR** — You approve/reject
|
|
31
|
+
12. **Create PR** — `bin/harness create-pr` + `bin/harness push`
|
|
32
|
+
13. **Analyze Comments** — Optional: reviewer triages PR comments
|
|
33
|
+
14. **Reconcile** — Apply fixes, update contracts
|
|
34
|
+
15. **Metrics** — Record token spend, cycle time, gate outcomes
|
|
35
|
+
|
|
36
|
+
## Quick Mode
|
|
37
|
+
If work item has `Mode: quick` hint and no risk keywords:
|
|
38
|
+
- Skips plan step, single pre-PR gate
|
|
39
|
+
- Size caps: 80 lines / 5 files (configurable)
|
|
40
|
+
- `quick-recheck` escalates to full mode if diff exceeds caps
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "One-time workspace setup: provider, repos, toolchain discovery, verification gate"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /init-workspace
|
|
8
|
+
|
|
9
|
+
One-time interview that bootstraps a workspace for the ai-sdlc-harness pipeline. Creates `.claude/context/`, discovers toolchain, configures provider, registers repos, and runs a verification gate.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/init-workspace
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Workflow
|
|
17
|
+
1. **Provider selection** — GitHub, GitLab, ADO, Jira, or local-markdown
|
|
18
|
+
2. **Repo registration** — point to cloned repos, discover test commands
|
|
19
|
+
3. **Toolchain discovery** — detect language, test framework, build tools
|
|
20
|
+
4. **Verification gate** — every check must pass before proceeding
|
|
21
|
+
5. **Permission config** — writes `.claude/settings.json` with allowlist
|
|
22
|
+
6. **Bootstrap marker** — marks workspace as initialized
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Adopt a v2.x workspace: config carries over, run history stays archived"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /migrate-workspace
|
|
8
|
+
|
|
9
|
+
Migrate an existing v2.x workspace to the v3.0 format. Configuration carries over; run history stays archived in place.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/migrate-workspace [--path <path>]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Arguments
|
|
17
|
+
- `--path <path>` — Path to existing v2.x workspace (defaults to current directory)
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
1. `bin/harness migrate-detect` — Detect v2.x workspace structure
|
|
21
|
+
2. Review detected settings
|
|
22
|
+
3. `bin/harness migrate-extract` — Extract and convert configuration
|
|
23
|
+
4. Verify migrated workspace
|
|
24
|
+
5. Archive old run history
|
|
25
|
+
|
|
26
|
+
## Notes
|
|
27
|
+
- Old run data is not deleted — only archived
|
|
28
|
+
- Permission settings are migrated but may need review for opencode format
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Regenerate the auto-generated codebase map the planner grounds its plans in"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /repo-map-refresh
|
|
8
|
+
|
|
9
|
+
Regenerate the codebase map used by the planner for grounding implementation plans.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/repo-map-refresh [--repo-name <name>] [--repo <path>]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Arguments
|
|
17
|
+
- `--repo-name <name>` — Name of the repo to map (omitted for all repos)
|
|
18
|
+
- `--repo <path>` — Path to the repo
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
1. `bin/harness repo-map-check --repo-name <n> --repo <path>` — Check staleness
|
|
22
|
+
2. Generate tiered map (structure → key files → details)
|
|
23
|
+
3. `bin/harness repo-map-stamp --repo-name <n> --repo <path>` — Stamp freshness
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Shape a story's quality before building: analyze, refine, improve, groom"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /story-workflow
|
|
8
|
+
|
|
9
|
+
Analyze and shape a work item's quality before implementation begins.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/story-workflow <command> <work-item-id>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
- `analyze <id>` — Analyze work item for clarity, completeness, testability
|
|
18
|
+
- `refine <id>` — Refine acceptance criteria, add edge cases
|
|
19
|
+
- `improve <id>` — Improve test intents, risk assessment
|
|
20
|
+
- `groom <id>` — Full grooming pass: dependencies, sizing, priority
|
|
21
|
+
|
|
22
|
+
## Arguments
|
|
23
|
+
- `command`: One of `analyze`, `refine`, `improve`, `groom`
|
|
24
|
+
- `work-item-id`: Provider work item ID
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Read-only dashboard: cursor, tasks, gates, flagged events per run"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /workflow-status
|
|
8
|
+
|
|
9
|
+
Read-only dashboard showing current pipeline state for one or all runs.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/workflow-status [--run <run-id>]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Options
|
|
17
|
+
- `--run <run-id>` — Show status for a specific run (omit for all runs)
|
|
18
|
+
|
|
19
|
+
## Output
|
|
20
|
+
- Current step and mode
|
|
21
|
+
- Task statuses (pending / in-progress / in-review / done)
|
|
22
|
+
- Gate states (pending / approved / rejected)
|
|
23
|
+
- Flagged events and metrics
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Change one config section without re-running the interview"
|
|
3
|
+
agent: planner
|
|
4
|
+
model: anthropic/claude-sonnet-4-5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /workspace-config
|
|
8
|
+
|
|
9
|
+
Modify workspace configuration sections without re-running the full setup interview.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
```
|
|
13
|
+
/workspace-config <section> [<key>=<value> ...]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Sections
|
|
17
|
+
- `provider` — Change provider settings
|
|
18
|
+
- `repos` — Update repo paths or test commands
|
|
19
|
+
- `workflow` — Adjust pipeline knobs (review rounds, security thresholds)
|
|
20
|
+
- `naming` — Branch/commit naming conventions
|
|
21
|
+
- `permissions` — Update permission allowlists
|
|
22
|
+
|
|
23
|
+
## Examples
|
|
24
|
+
```
|
|
25
|
+
/workspace-config workflow review_rounds.max=3
|
|
26
|
+
/workspace-config naming branch_prefix=feat
|
|
27
|
+
```
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
|
+
|
|
3
|
+
// ── Types ────────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
interface PermissionEvent {
|
|
6
|
+
type: string;
|
|
7
|
+
command?: string;
|
|
8
|
+
path?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// ── Python guard pattern equivalents (hooks/guards.py) ──────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* GIT_VERB_RE equivalent — matches `git commit|merge|push|...` with global
|
|
16
|
+
* flags between `git` and the verb, anchored at command boundaries and
|
|
17
|
+
* excluded inside quotes.
|
|
18
|
+
*
|
|
19
|
+
* Python source: hooks/guards.py lines 130-134
|
|
20
|
+
*
|
|
21
|
+
* Breakdown:
|
|
22
|
+
* (?<!['"]) – negative lookbehind: NOT preceded by quote
|
|
23
|
+
* \bgit\b – word-boundary git
|
|
24
|
+
* (?:[ \t]+ – optional global flags (zero or more):
|
|
25
|
+
* (?:-C|-c|--git-dir|…)(?:=TOKEN|[ \t]+TOKEN)? – flags that take a value
|
|
26
|
+
* |-{1,2}[A-Za-z][\w-]*(?:=TOKEN)? – self-contained flags
|
|
27
|
+
* )*
|
|
28
|
+
* [ \t]+ – separator before verb
|
|
29
|
+
* (commit|merge|…) – captured verb
|
|
30
|
+
* \b – word boundary after verb
|
|
31
|
+
*/
|
|
32
|
+
const GIT_TOKEN = /[^\s"']+/;
|
|
33
|
+
const GIT_VERB_RE = new RegExp(
|
|
34
|
+
"(?<!['\"])" +
|
|
35
|
+
"\\bgit\\b" +
|
|
36
|
+
"(?:[ \\t]+(?:" +
|
|
37
|
+
"(?:-C|-c|--git-dir|--work-tree|--namespace|--super-prefix|--exec-path)" +
|
|
38
|
+
"(?:=" + GIT_TOKEN.source + "|[ \\t]+" + GIT_TOKEN.source + ")?" +
|
|
39
|
+
"|-{1,2}[A-Za-z][\\w-]*(?:=" + GIT_TOKEN.source + ")?" +
|
|
40
|
+
"))*" +
|
|
41
|
+
"[ \\t]+" +
|
|
42
|
+
"(commit|merge|rebase|cherry-pick|revert|am|pull|(?<!stash )push)\\b"
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* SHELL_C_RE equivalent — extracts payloads from `sh -c "..."` / `bash -c '...'`
|
|
47
|
+
* so that GIT_VERB_RE can check them (quoted payloads would otherwise be
|
|
48
|
+
* invisible due to the (?<!['"]) anchor).
|
|
49
|
+
*
|
|
50
|
+
* Python source: hooks/guards.py lines 139-140
|
|
51
|
+
*/
|
|
52
|
+
const SHELL_C_RE = /\b(?:sh|bash|zsh|dash|ksh)\b[^|;&\n\r]*?-c[ \t]+(?:"([^"]*)"|'([^']*)')/;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* AUTHORITY_RE equivalent — scoped to `ai/<run>/` prefix and matching ALL
|
|
56
|
+
* integrity-critical filenames. False-positive-safe because it requires the
|
|
57
|
+
* `ai/` prefix (matches Python's behavior exactly).
|
|
58
|
+
*
|
|
59
|
+
* Python source: hooks/guards.py lines 149-152
|
|
60
|
+
*/
|
|
61
|
+
const AUTHORITY_RE = /ai[/\\][^/\\\s"']+[/\\](?:state\.yaml|events\.ndjson|tokens\.ndjson|human-input\.ndjson|reviews\.ndjson|\.redproof|\.state\.lock)\b|\.hmac\b/;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* WRITE_HINT_RE equivalent — detects inline programming-language writes that
|
|
65
|
+
* circumvent path-based write blocking (shell redirects, tee, sed, python
|
|
66
|
+
* open/write, node fs.writeFile, etc.).
|
|
67
|
+
*
|
|
68
|
+
* Python source: hooks/guards.py lines 162-173
|
|
69
|
+
*/
|
|
70
|
+
const WRITE_HINT_RE = /(?<![0-9])>(?!&)|\btee\b|\bsed\s+(-\w+\s+)*-i|\brm\b|\bmv\b|\bcp\b|\btruncate\b|\bdd\b|yq\s+.*-i|--in-place/;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Agent shapes in the harness — matches Python's shape_of().
|
|
74
|
+
* Python source: hooks/guards.py lines 499-502
|
|
75
|
+
*/
|
|
76
|
+
const HARNESS_SHAPES = ["planner", "developer", "reviewer"];
|
|
77
|
+
|
|
78
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
/** Normalize agent name the same way Python's shape_of() does. */
|
|
81
|
+
function normalizeAgent(raw: string): string {
|
|
82
|
+
const tail = raw.split(":").pop() ?? "";
|
|
83
|
+
return tail.trim().toLowerCase().replace(/^ai-sdlc-/, "");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Recursively extract all shell command targets from a bash command line. */
|
|
87
|
+
function extractTargets(cmd: string): string[] {
|
|
88
|
+
const targets = [cmd];
|
|
89
|
+
// Extract sh -c / bash -c payloads
|
|
90
|
+
const m = SHELL_C_RE.exec(cmd);
|
|
91
|
+
if (m) {
|
|
92
|
+
const payload = m[1] ?? m[2] ?? "";
|
|
93
|
+
if (payload) targets.push(...extractTargets(payload));
|
|
94
|
+
}
|
|
95
|
+
return targets;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Check if ANY target contains a blocked git verb. */
|
|
99
|
+
function hasBlockedGitVerb(cmd: string): boolean {
|
|
100
|
+
for (const target of extractTargets(cmd)) {
|
|
101
|
+
if (GIT_VERB_RE.test(target)) return true;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Check if a bash command contains inline/injected write operations. */
|
|
107
|
+
function hasWriteHint(cmd: string): boolean {
|
|
108
|
+
return WRITE_HINT_RE.test(cmd);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── Plugin ───────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
const HarnessGuardsPlugin: Plugin = async ({ project, $, directory }) => {
|
|
114
|
+
const rawName =
|
|
115
|
+
(project as { agent?: { name?: string } }).agent?.name ?? "";
|
|
116
|
+
const agentName = normalizeAgent(rawName) || "direct-user";
|
|
117
|
+
const guardsPy = `${directory}/hooks/guards.py`;
|
|
118
|
+
const encoder = new TextEncoder();
|
|
119
|
+
|
|
120
|
+
// ── Layer 1: Enforcement via permission.ask ──────────────────────────
|
|
121
|
+
async function handlePermissionAsk(
|
|
122
|
+
permission: PermissionEvent,
|
|
123
|
+
output: { status: "ask" | "deny" | "allow" },
|
|
124
|
+
): Promise<void> {
|
|
125
|
+
try {
|
|
126
|
+
if (output.status === "deny") return; // Already denied upstream
|
|
127
|
+
|
|
128
|
+
switch (permission.type) {
|
|
129
|
+
case "bash": {
|
|
130
|
+
const cmd = permission.command ?? "";
|
|
131
|
+
if (!cmd) break;
|
|
132
|
+
|
|
133
|
+
// Block git verbs (matching Python guard_bash)
|
|
134
|
+
if (hasBlockedGitVerb(cmd)) {
|
|
135
|
+
output.status = "deny";
|
|
136
|
+
console.warn(
|
|
137
|
+
`[harness-guards] DENIED bash (git verb): ${cmd.slice(0, 120)}`,
|
|
138
|
+
);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Block inline writes to authority paths via shell commands
|
|
143
|
+
if (hasWriteHint(cmd)) {
|
|
144
|
+
// For now, only block if a write hint targets an authority path.
|
|
145
|
+
// Full authority-path write detection requires command parsing
|
|
146
|
+
// (the Python guard uses both WRITE_HINT_RE + AUTHORITY_RE).
|
|
147
|
+
// This is a best-effort check; the Python audit (Layer 2) catches
|
|
148
|
+
// the rest.
|
|
149
|
+
output.status = "deny";
|
|
150
|
+
console.warn(
|
|
151
|
+
`[harness-guards] DENIED bash (write hint): ${cmd.slice(0, 120)}`,
|
|
152
|
+
);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Not a dangerous command — auto-allow to avoid prompt fatigue.
|
|
157
|
+
// Without this, every safe command would prompt the user.
|
|
158
|
+
output.status = "allow";
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
case "write":
|
|
163
|
+
case "edit": {
|
|
164
|
+
const filePath = permission.path ?? "";
|
|
165
|
+
if (!filePath) break;
|
|
166
|
+
|
|
167
|
+
// Block writes to authority files (Python guard_write: AUTHORITY_RE)
|
|
168
|
+
if (AUTHORITY_RE.test(filePath)) {
|
|
169
|
+
output.status = "deny";
|
|
170
|
+
console.warn(
|
|
171
|
+
`[harness-guards] DENIED ${permission.type} (authority path): ${filePath}`,
|
|
172
|
+
);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Auto-allow to avoid prompt fatigue for non-authority paths.
|
|
177
|
+
// The permission patterns in opencode.jsonc still deny writes
|
|
178
|
+
// outside allowed directories.
|
|
179
|
+
output.status = "allow";
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
case "read": {
|
|
184
|
+
const filePath = permission.path ?? "";
|
|
185
|
+
if (!filePath) break;
|
|
186
|
+
|
|
187
|
+
// Block raw .redproof reads for ALL harness shapes
|
|
188
|
+
// (Python guard_read blocks for planner, developer, reviewer)
|
|
189
|
+
if (
|
|
190
|
+
HARNESS_SHAPES.includes(agentName) &&
|
|
191
|
+
/\.redproof[/\\]/.test(filePath)
|
|
192
|
+
) {
|
|
193
|
+
output.status = "deny";
|
|
194
|
+
console.warn(
|
|
195
|
+
`[harness-guards] DENIED read (redproof isolation): ${filePath}`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
// Leave non-redproof reads at their configured status
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch (err) {
|
|
203
|
+
// Error boundary — never let a plugin crash allow a blocked action
|
|
204
|
+
console.error(`[harness-guards] ERROR in permission.ask: ${err}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── Layer 2: Audit via Python guards (non-blocking) ─────────────────
|
|
209
|
+
async function auditGuard(
|
|
210
|
+
guard: string,
|
|
211
|
+
payload: Record<string, unknown>,
|
|
212
|
+
): Promise<void> {
|
|
213
|
+
const t0 = Date.now();
|
|
214
|
+
try {
|
|
215
|
+
const proc = $`python3 ${guardsPy} ${guard}`.quiet().nothrow();
|
|
216
|
+
const writer = proc.stdin.getWriter();
|
|
217
|
+
await writer.write(encoder.encode(JSON.stringify(payload)));
|
|
218
|
+
await writer.close();
|
|
219
|
+
|
|
220
|
+
// 5-second timeout to prevent a hung Python process from stalling tools
|
|
221
|
+
const output = await Promise.race([
|
|
222
|
+
proc,
|
|
223
|
+
new Promise<never>((_, reject) =>
|
|
224
|
+
setTimeout(() => reject(new Error("timeout")), 5000),
|
|
225
|
+
),
|
|
226
|
+
]);
|
|
227
|
+
|
|
228
|
+
const elapsed = Date.now() - t0;
|
|
229
|
+
if (output.exitCode === 2) {
|
|
230
|
+
const reason = output.stderr?.toString()?.trim() ?? "unknown";
|
|
231
|
+
console.warn(
|
|
232
|
+
`[harness-guards] ${guard} BLOCK (${elapsed}ms): ${reason}`,
|
|
233
|
+
);
|
|
234
|
+
} else if (output.exitCode !== 0) {
|
|
235
|
+
console.warn(
|
|
236
|
+
`[harness-guards] ${guard} exit ${output.exitCode} (${elapsed}ms)`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
} catch (err) {
|
|
240
|
+
// Fail open — audit should never impede the tool
|
|
241
|
+
const elapsed = Date.now() - t0;
|
|
242
|
+
if (
|
|
243
|
+
err instanceof Error &&
|
|
244
|
+
err.message === "timeout" &&
|
|
245
|
+
elapsed >= 5000
|
|
246
|
+
) {
|
|
247
|
+
console.warn(
|
|
248
|
+
`[harness-guards] ${guard} TIMEOUT after ${elapsed}ms`,
|
|
249
|
+
);
|
|
250
|
+
} else {
|
|
251
|
+
console.warn(`[harness-guards] ${guard} ERROR: ${err}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
// Layer 1: Enforcement
|
|
258
|
+
"permission.ask": handlePermissionAsk,
|
|
259
|
+
|
|
260
|
+
// Layer 2: Audit
|
|
261
|
+
"tool.execute.before": async (_input, output) => {
|
|
262
|
+
const tool = (_input as { tool?: string }).tool ?? "unknown";
|
|
263
|
+
const args = output?.args ?? {};
|
|
264
|
+
const payload: Record<string, unknown> = {
|
|
265
|
+
agent_type: agentName,
|
|
266
|
+
cwd: directory,
|
|
267
|
+
tool_input: args,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
switch (tool) {
|
|
271
|
+
case "bash":
|
|
272
|
+
await auditGuard("bash", payload);
|
|
273
|
+
break;
|
|
274
|
+
case "write":
|
|
275
|
+
case "edit":
|
|
276
|
+
await auditGuard("write", payload);
|
|
277
|
+
break;
|
|
278
|
+
case "read":
|
|
279
|
+
case "grep":
|
|
280
|
+
await auditGuard("read", payload);
|
|
281
|
+
break;
|
|
282
|
+
case "task":
|
|
283
|
+
await auditGuard("spawn", payload);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export default HarnessGuardsPlugin;
|