@the-agenticflow/openflows 0.1.3 → 0.1.6
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/.env.example +60 -0
- package/README.md +156 -113
- package/bin/LICENSE +21 -0
- package/bin/README.md +535 -0
- package/bin/agentflow-bin +0 -0
- package/bin/agentflow-dashboard-bin +0 -0
- package/bin/agentflow-doctor-bin +0 -0
- package/bin/agentflow-setup-bin +0 -0
- package/bin/openflows.js +285 -3
- package/bin/orchestration/agent/agents/forge.agent.md +110 -0
- package/bin/orchestration/agent/agents/lore.agent.md +27 -0
- package/bin/orchestration/agent/agents/nexus.agent.md +201 -0
- package/bin/orchestration/agent/agents/sentinel.agent.md +96 -0
- package/bin/orchestration/agent/agents/vessel.agent.md +38 -0
- package/bin/orchestration/agent/registry.json +10 -0
- package/bin/orchestration/agent/standards/CODING.md +22 -0
- package/bin/orchestration/agent/standards/REVIEW.md +15 -0
- package/bin/orchestration/agent/standards/SECURITY.md +72 -0
- package/bin/orchestration/plugin/commands/assign.md +45 -0
- package/bin/orchestration/plugin/commands/check-ci.md +26 -0
- package/bin/orchestration/plugin/commands/document-pr.md +32 -0
- package/bin/orchestration/plugin/commands/gate-approve.md +39 -0
- package/bin/orchestration/plugin/commands/handoff.md +75 -0
- package/bin/orchestration/plugin/commands/merge.md +47 -0
- package/bin/orchestration/plugin/commands/plan.md +66 -0
- package/bin/orchestration/plugin/commands/segment-done.md +50 -0
- package/bin/orchestration/plugin/commands/status-check.md +28 -0
- package/bin/orchestration/plugin/commands/status.md +94 -0
- package/bin/orchestration/plugin/commands/update-changelog.md +37 -0
- package/bin/orchestration/plugin/hooks/forge/post_write_lint.sh +76 -0
- package/bin/orchestration/plugin/hooks/forge/pre_bash_guard.sh +81 -0
- package/bin/orchestration/plugin/hooks/forge/pre_compact_handoff.sh +28 -0
- package/bin/orchestration/plugin/hooks/forge/pre_write_check.sh +77 -0
- package/bin/orchestration/plugin/hooks/forge/session_start.sh +59 -0
- package/bin/orchestration/plugin/hooks/forge/stop_require_artifact.sh +75 -0
- package/bin/orchestration/plugin/hooks/lore/session-start.sh +13 -0
- package/bin/orchestration/plugin/hooks/nexus/init-session.sh +23 -0
- package/bin/orchestration/plugin/hooks/nexus/log-decision.sh +10 -0
- package/bin/orchestration/plugin/hooks/sentinel/post_write_validate.sh +59 -0
- package/bin/orchestration/plugin/hooks/sentinel/pre_bash_readonly_guard.sh +107 -0
- package/bin/orchestration/plugin/hooks/sentinel/session_start.sh +74 -0
- package/bin/orchestration/plugin/hooks/sentinel/stop_require_eval.sh +57 -0
- package/bin/orchestration/plugin/hooks/vessel/log-merge-status.sh +7 -0
- package/bin/orchestration/plugin/hooks/vessel/session-start.sh +14 -0
- package/bin/orchestration/plugin/mcp/mcp.json.template +26 -0
- package/bin/orchestration/plugin/plugin.json +66 -0
- package/bin/orchestration/plugin/skills/forge-algorithmic-art.md +24 -0
- package/bin/orchestration/plugin/skills/forge-canvas-design.md +25 -0
- package/bin/orchestration/plugin/skills/forge-coding.md +161 -0
- package/bin/orchestration/plugin/skills/forge-frontend-design.md +30 -0
- package/bin/orchestration/plugin/skills/forge-mcp-builder.md +37 -0
- package/bin/orchestration/plugin/skills/forge-planning.md +102 -0
- package/bin/orchestration/plugin/skills/forge-skill-creator.md +25 -0
- package/bin/orchestration/plugin/skills/forge-web-artifacts-builder.md +29 -0
- package/bin/orchestration/plugin/skills/lore-brand-guidelines.md +33 -0
- package/bin/orchestration/plugin/skills/lore-changelog.md +69 -0
- package/bin/orchestration/plugin/skills/lore-doc-coauthoring.md +33 -0
- package/bin/orchestration/plugin/skills/lore-documentation.md +57 -0
- package/bin/orchestration/plugin/skills/lore-docx.md +20 -0
- package/bin/orchestration/plugin/skills/lore-pdf.md +20 -0
- package/bin/orchestration/plugin/skills/lore-pptx.md +23 -0
- package/bin/orchestration/plugin/skills/lore-theme-factory.md +20 -0
- package/bin/orchestration/plugin/skills/lore-xlsx.md +20 -0
- package/bin/orchestration/plugin/skills/nexus-doc-coauthoring.md +21 -0
- package/bin/orchestration/plugin/skills/nexus-internal-comms.md +28 -0
- package/bin/orchestration/plugin/skills/nexus-orchestration.md +63 -0
- package/bin/orchestration/plugin/skills/nexus-skill-creator.md +15 -0
- package/bin/orchestration/plugin/skills/nexus-slack-gif-creator.md +21 -0
- package/bin/orchestration/plugin/skills/nexus-triage.md +56 -0
- package/bin/orchestration/plugin/skills/nexus-xlsx.md +20 -0
- package/bin/orchestration/plugin/skills/sentinel-algorithmic-art.md +20 -0
- package/bin/orchestration/plugin/skills/sentinel-criteria.md +115 -0
- package/bin/orchestration/plugin/skills/sentinel-frontend-design.md +20 -0
- package/bin/orchestration/plugin/skills/sentinel-review.md +124 -0
- package/bin/orchestration/plugin/skills/sentinel-web-artifacts-builder.md +20 -0
- package/bin/orchestration/plugin/skills/sentinel-webapp-testing.md +34 -0
- package/bin/orchestration/plugin/skills/shared-claude-api.md +25 -0
- package/bin/orchestration/plugin/skills/vessel-ci-gate.md +68 -0
- package/bin/orchestration/plugin/skills/vessel-internal-comms.md +20 -0
- package/bin/orchestration/plugin/skills/vessel-mcp-builder.md +21 -0
- package/bin/orchestration/plugin/skills/vessel-merge-protocol.md +113 -0
- package/bin/orchestration/plugin/skills/vessel-pdf.md +20 -0
- package/bin/orchestration/plugin/skills/vessel-webapp-testing.md +34 -0
- package/package.json +8 -3
- package/scripts/install.js +162 -9
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: status
|
|
3
|
+
description: Signal terminal status to the harness
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /status Command
|
|
7
|
+
|
|
8
|
+
Signal terminal status to the harness. Use when work is complete or blocked.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
/status <status> [reason]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Status Values
|
|
17
|
+
|
|
18
|
+
### Terminal statuses (ends the pair lifecycle)
|
|
19
|
+
- `PR_OPENED` - Work complete, PR created
|
|
20
|
+
- `COMPLETE` - All work done, PR creation deferred to harness
|
|
21
|
+
- `BLOCKED` - Cannot proceed, needs intervention
|
|
22
|
+
- `FUEL_EXHAUSTED` - Budget/tokens exhausted, need more allocation
|
|
23
|
+
|
|
24
|
+
### Non-terminal statuses (continues the event loop)
|
|
25
|
+
- `PENDING_REVIEW` - Work paused, waiting for review
|
|
26
|
+
- `AWAITING_SENTINEL_REVIEW` - Segment done, waiting for SENTINEL evaluation
|
|
27
|
+
- `APPROVED_READY` - Changes requested by SENTINEL have been addressed
|
|
28
|
+
- `SEGMENT_N_DONE` - Segment N complete (e.g. `SEGMENT_1_DONE`)
|
|
29
|
+
|
|
30
|
+
### IMPORTANT
|
|
31
|
+
Do NOT use any other status value. Values like `AWAITING_REVIEW`, `DONE`, `FINISHED`, `SUCCESS`, `IMPLEMENTATION_COMPLETE` will be treated as `BLOCKED` and your work will be wasted.
|
|
32
|
+
|
|
33
|
+
## What it does
|
|
34
|
+
|
|
35
|
+
1. Writes STATUS.json with current state
|
|
36
|
+
2. Lists all files changed
|
|
37
|
+
3. Provides reason/explanation
|
|
38
|
+
4. Harness reads STATUS.json and takes appropriate action
|
|
39
|
+
|
|
40
|
+
## STATUS.json Structure
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"status": "PR_OPENED | COMPLETE | BLOCKED | FUEL_EXHAUSTED | PENDING_REVIEW | AWAITING_SENTINEL_REVIEW | APPROVED_READY | SEGMENT_N_DONE",
|
|
45
|
+
"pair": "pair-{N}",
|
|
46
|
+
"ticket_id": "T-{id}",
|
|
47
|
+
"branch": "forge-{N}/T-{id}",
|
|
48
|
+
"files_changed": [
|
|
49
|
+
"src/auth.rs",
|
|
50
|
+
"tests/auth_test.rs"
|
|
51
|
+
],
|
|
52
|
+
"segments_completed": 3,
|
|
53
|
+
"pr_url": "https://github.com/owner/repo/pull/42",
|
|
54
|
+
"reason": "Optional reason for BLOCKED or FUEL_EXHAUSTED",
|
|
55
|
+
"timestamp": "2025-03-24T10:00:00Z"
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Examples
|
|
60
|
+
|
|
61
|
+
### Work Complete
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
/status PR_OPENED
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Then provide the PR URL when prompted.
|
|
68
|
+
|
|
69
|
+
### Blocked
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
/status BLOCKED Cannot proceed due to API rate limit
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Fuel Exhausted
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
/status FUEL_EXHAUSTED Need 50k more tokens to complete
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## After STATUS.json
|
|
82
|
+
|
|
83
|
+
The harness will:
|
|
84
|
+
|
|
85
|
+
- **PR_OPENED**: Notify VESSEL to check CI and merge
|
|
86
|
+
- **BLOCKED**: Alert NEXUS for human intervention
|
|
87
|
+
- **FUEL_EXHAUSTED**: Request more budget allocation
|
|
88
|
+
|
|
89
|
+
## Important
|
|
90
|
+
|
|
91
|
+
- This is a terminal state - you cannot continue after writing STATUS.json
|
|
92
|
+
- For temporary pauses, use `/segment-done` instead
|
|
93
|
+
- For context reset, use `/handoff` instead
|
|
94
|
+
- Always list ALL files changed across all segments
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update-changelog
|
|
3
|
+
description: Add entry to CHANGELOG.md
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /update-changelog Command
|
|
7
|
+
|
|
8
|
+
Adds an entry to CHANGELOG.md for a merged PR.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Read PR Description**
|
|
13
|
+
Get the PR description from the merge.
|
|
14
|
+
|
|
15
|
+
2. **Categorize Change**
|
|
16
|
+
Determine category:
|
|
17
|
+
- Added: New features
|
|
18
|
+
- Changed: Modified behavior
|
|
19
|
+
- Fixed: Bug fixes
|
|
20
|
+
- Security: Security fixes
|
|
21
|
+
|
|
22
|
+
3. **Write Entry**
|
|
23
|
+
Add to CHANGELOG.md under [Unreleased]:
|
|
24
|
+
```markdown
|
|
25
|
+
### Added
|
|
26
|
+
- New feature X (#42)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
4. **Commit**
|
|
30
|
+
Commit with message:
|
|
31
|
+
```
|
|
32
|
+
docs: update CHANGELOG for T-{id}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Output
|
|
36
|
+
|
|
37
|
+
CHANGELOG.md updated with new entry.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs after every Write, Edit, MultiEdit tool call
|
|
3
|
+
# Validates atomic writes for shared/ artifacts and runs linter on source files
|
|
4
|
+
#
|
|
5
|
+
# Environment:
|
|
6
|
+
# CLAUDE_TOOL_INPUT_FILE_PATH - the file that was written
|
|
7
|
+
# SPRINTLESS_WORKTREE - the worktree directory
|
|
8
|
+
|
|
9
|
+
FILE="${CLAUDE_TOOL_INPUT_FILE_PATH}"
|
|
10
|
+
WORKTREE="${SPRINTLESS_WORKTREE}"
|
|
11
|
+
|
|
12
|
+
# For shared/ artifacts, ensure atomic write was used (.tmp + rename pattern)
|
|
13
|
+
case "$FILE" in
|
|
14
|
+
*/orchestration/pairs/*/shared/*)
|
|
15
|
+
# Verify file was written atomically (should never see .tmp files at this point)
|
|
16
|
+
if [[ "$FILE" == *.tmp ]]; then
|
|
17
|
+
echo "ERROR: Temporary file leaked to filesystem: ${FILE}"
|
|
18
|
+
echo "All shared/ writes must be atomic (write to .tmp, then rename)."
|
|
19
|
+
exit 2
|
|
20
|
+
fi
|
|
21
|
+
# Validate JSON structure for specific artifact types
|
|
22
|
+
case "$FILE" in
|
|
23
|
+
*/STATUS.json)
|
|
24
|
+
if command -v python3 &> /dev/null; then
|
|
25
|
+
python3 -c "import json, sys; json.load(open('$FILE'))" 2>&1
|
|
26
|
+
if [ $? -ne 0 ]; then
|
|
27
|
+
echo "INVALID: STATUS.json is not valid JSON"
|
|
28
|
+
exit 2
|
|
29
|
+
fi
|
|
30
|
+
fi
|
|
31
|
+
;;
|
|
32
|
+
esac
|
|
33
|
+
exit 0
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
# Only lint source files (not config, docs, etc.)
|
|
38
|
+
case "$FILE" in
|
|
39
|
+
*.ts|*.tsx)
|
|
40
|
+
if command -v npx &> /dev/null; then
|
|
41
|
+
OUTPUT=$(cd "$WORKTREE" && npx eslint "$FILE" --quiet 2>&1)
|
|
42
|
+
if [ $? -ne 0 ]; then
|
|
43
|
+
echo "Lint failed on ${FILE}:"
|
|
44
|
+
echo "$OUTPUT"
|
|
45
|
+
echo ""
|
|
46
|
+
echo "Fix these lint errors before continuing."
|
|
47
|
+
exit 2
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
;;
|
|
51
|
+
*.rs)
|
|
52
|
+
if command -v cargo &> /dev/null; then
|
|
53
|
+
OUTPUT=$(cd "$WORKTREE" && cargo clippy --quiet --message-format=short 2>&1 | grep -A5 "$FILE" || true)
|
|
54
|
+
if [ -n "$OUTPUT" ]; then
|
|
55
|
+
echo "Clippy warnings for ${FILE}:"
|
|
56
|
+
echo "$OUTPUT"
|
|
57
|
+
echo ""
|
|
58
|
+
echo "Fix these warnings before continuing."
|
|
59
|
+
# Clippy warnings don't fail the build, but we want clean code
|
|
60
|
+
# exit 2 # Uncomment to enforce zero warnings
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
;;
|
|
64
|
+
*.py)
|
|
65
|
+
if command -v ruff &> /dev/null; then
|
|
66
|
+
OUTPUT=$(cd "$WORKTREE" && ruff check "$FILE" 2>&1)
|
|
67
|
+
if [ $? -ne 0 ]; then
|
|
68
|
+
echo "Ruff failed on ${FILE}:"
|
|
69
|
+
echo "$OUTPUT"
|
|
70
|
+
exit 2
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
;;
|
|
74
|
+
esac
|
|
75
|
+
|
|
76
|
+
exit 0
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs before every Bash tool call
|
|
3
|
+
# Blocks dangerous commands and access to other pairs' worktrees
|
|
4
|
+
#
|
|
5
|
+
# Environment:
|
|
6
|
+
# CLAUDE_TOOL_INPUT_COMMAND - the command being executed
|
|
7
|
+
# SPRINTLESS_PAIR_ID - the current pair ID
|
|
8
|
+
|
|
9
|
+
CMD="${CLAUDE_TOOL_INPUT_COMMAND}"
|
|
10
|
+
|
|
11
|
+
# Block direct git push to non-forge branches - must use MCP tools for PR creation
|
|
12
|
+
if echo "$CMD" | grep -qE '^git push|^git push '; then
|
|
13
|
+
# Allow pushing to the pair's own branch
|
|
14
|
+
BRANCH_PATTERN="forge-${SPRINTLESS_PAIR_ID}"
|
|
15
|
+
if echo "$CMD" | grep -q "$BRANCH_PATTERN"; then
|
|
16
|
+
# Allow pushing own branch
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
echo "BLOCKED: Cannot push to branches other than your own."
|
|
20
|
+
echo ""
|
|
21
|
+
echo "Your branch: forge-${SPRINTLESS_PAIR_ID}/${SPRINTLESS_TICKET_ID}"
|
|
22
|
+
echo ""
|
|
23
|
+
echo "After pushing, create a PR using GitHub MCP tools:"
|
|
24
|
+
echo " 1. Use create_pull_request from github MCP server"
|
|
25
|
+
echo " 2. Write STATUS.json with PR_OPENED status and PR URL"
|
|
26
|
+
exit 2
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Block writes to other pairs' worktrees
|
|
30
|
+
if echo "$CMD" | grep -qE 'worktrees/pair-[0-9]+/' ; then
|
|
31
|
+
REFERENCED=$(echo "$CMD" | grep -oE 'pair-[0-9]+' | head -1)
|
|
32
|
+
if [ "$REFERENCED" != "${SPRINTLESS_PAIR_ID}" ]; then
|
|
33
|
+
echo "BLOCKED: Cannot access ${REFERENCED}'s worktree."
|
|
34
|
+
echo "You are ${SPRINTLESS_PAIR_ID}."
|
|
35
|
+
echo ""
|
|
36
|
+
echo "Each pair works in isolation. You cannot read or write"
|
|
37
|
+
echo "to another pair's worktree."
|
|
38
|
+
exit 2
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Block writes to main branch
|
|
43
|
+
if echo "$CMD" | grep -qE 'checkout main|checkout origin/main'; then
|
|
44
|
+
echo "BLOCKED: Cannot checkout main. Work on your branch only."
|
|
45
|
+
echo ""
|
|
46
|
+
echo "Your branch: forge-${SPRINTLESS_PAIR_ID}/${SPRINTLESS_TICKET_ID}"
|
|
47
|
+
exit 2
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Block dangerous commands
|
|
51
|
+
DANGEROUS_PATTERNS="rm -rf /|sudo rm|:(){ :|:& };:|mkfs|dd if="
|
|
52
|
+
if echo "$CMD" | grep -qE "$DANGEROUS_PATTERNS"; then
|
|
53
|
+
echo "BLOCKED: Dangerous command detected."
|
|
54
|
+
echo "This command is not allowed for safety reasons."
|
|
55
|
+
exit 2
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Block network operations (MCP tools should be used instead)
|
|
59
|
+
NETWORK_PATTERNS="^curl |^wget |^nc |^ncat |^telnet |^ssh "
|
|
60
|
+
if echo "$CMD" | grep -qE "$NETWORK_PATTERNS"; then
|
|
61
|
+
echo "BLOCKED: Network commands are not allowed."
|
|
62
|
+
echo ""
|
|
63
|
+
echo "Use MCP tools instead:"
|
|
64
|
+
echo " - For GitHub API: use github MCP server"
|
|
65
|
+
echo " - For HTTP requests: use appropriate MCP tool"
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Block package installation (could introduce unreviewed dependencies)
|
|
70
|
+
INSTALL_PATTERNS="npm install |yarn add |pip install |cargo install |go get "
|
|
71
|
+
if echo "$CMD" | grep -qE "$INSTALL_PATTERNS"; then
|
|
72
|
+
echo "BLOCKED: Package installation is not allowed."
|
|
73
|
+
echo ""
|
|
74
|
+
echo "If a new dependency is needed:"
|
|
75
|
+
echo " 1. Document it in PLAN.md"
|
|
76
|
+
echo " 2. Get SENTINEL approval"
|
|
77
|
+
echo " 3. Have a human add it to package.json/Cargo.toml"
|
|
78
|
+
exit 2
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
exit 0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs on PreCompact - converts compaction to clean context reset
|
|
3
|
+
# This hook fires when the context window is approaching its limit
|
|
4
|
+
#
|
|
5
|
+
# Environment:
|
|
6
|
+
# SPRINTLESS_SHARED - the shared directory
|
|
7
|
+
|
|
8
|
+
SHARED="${SPRINTLESS_SHARED}"
|
|
9
|
+
|
|
10
|
+
echo "=============================================="
|
|
11
|
+
echo " CONTEXT RESET REQUIRED"
|
|
12
|
+
echo "=============================================="
|
|
13
|
+
echo ""
|
|
14
|
+
echo "Your context window is approaching its limit."
|
|
15
|
+
echo "Before this session ends, you must write a handoff."
|
|
16
|
+
echo ""
|
|
17
|
+
echo "Use the /handoff command now. It will:"
|
|
18
|
+
echo " 1. Collect everything needed for the handoff"
|
|
19
|
+
echo " 2. Write ${SHARED}/HANDOFF.md"
|
|
20
|
+
echo " 3. Update WORKLOG.md with current state"
|
|
21
|
+
echo " 4. Exit cleanly"
|
|
22
|
+
echo ""
|
|
23
|
+
echo "A fresh FORGE session will read your handoff and continue."
|
|
24
|
+
echo ""
|
|
25
|
+
echo "DO NOT attempt to continue working - write the handoff now."
|
|
26
|
+
echo ""
|
|
27
|
+
|
|
28
|
+
exit 2
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs before every Write, Edit, MultiEdit tool call
|
|
3
|
+
# Enforces dynamic file ownership locking via flock
|
|
4
|
+
#
|
|
5
|
+
# Environment:
|
|
6
|
+
# CLAUDE_TOOL_INPUT_FILE_PATH - the file being written
|
|
7
|
+
# SPRINTLESS_PAIR_ID - the current pair ID
|
|
8
|
+
# SPRINTLESS_SHARED - the shared directory
|
|
9
|
+
|
|
10
|
+
FILE="${CLAUDE_TOOL_INPUT_FILE_PATH}"
|
|
11
|
+
PAIR_ID="${SPRINTLESS_PAIR_ID}"
|
|
12
|
+
LOCKS_DIR="${SPRINTLESS_SHARED}/../locks"
|
|
13
|
+
|
|
14
|
+
# Skip lock check for shared/ artifacts - those are pair-scoped already
|
|
15
|
+
case "$FILE" in
|
|
16
|
+
*/orchestration/pairs/*/shared/*)
|
|
17
|
+
exit 0
|
|
18
|
+
;;
|
|
19
|
+
esac
|
|
20
|
+
|
|
21
|
+
# Also skip if FILE is relative and matches shared pattern
|
|
22
|
+
case "$FILE" in
|
|
23
|
+
shared/*|./shared/*)
|
|
24
|
+
exit 0
|
|
25
|
+
;;
|
|
26
|
+
esac
|
|
27
|
+
|
|
28
|
+
# Create locks directory if needed
|
|
29
|
+
mkdir -p "$LOCKS_DIR" 2>/dev/null
|
|
30
|
+
|
|
31
|
+
# Generate lock filename (sha256 hash of filepath to avoid path issues)
|
|
32
|
+
LOCK_HASH=$(echo -n "$FILE" | sha256sum | cut -d' ' -f1)
|
|
33
|
+
LOCK_FILE="${LOCKS_DIR}/${LOCK_HASH}.lock"
|
|
34
|
+
LOCK_JSON="${LOCKS_DIR}/${LOCK_HASH}.json"
|
|
35
|
+
|
|
36
|
+
# Attempt atomic lock acquisition using flock
|
|
37
|
+
{
|
|
38
|
+
# Acquire exclusive lock on .lock file (non-blocking)
|
|
39
|
+
flock -x -n 200 || {
|
|
40
|
+
echo "BLOCKED: Another process is currently locking ${FILE}"
|
|
41
|
+
echo "Waiting for lock to be released..."
|
|
42
|
+
exit 2
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# Check if lock JSON exists and who owns it
|
|
46
|
+
if [ -f "$LOCK_JSON" ]; then
|
|
47
|
+
OWNER=$(cat "$LOCK_JSON" | grep -o '"pair"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
48
|
+
if [ "$OWNER" != "$PAIR_ID" ]; then
|
|
49
|
+
echo "BLOCKED: ${FILE} is currently locked by ${OWNER}."
|
|
50
|
+
echo ""
|
|
51
|
+
echo "This file is being modified by another pair."
|
|
52
|
+
echo ""
|
|
53
|
+
echo "Options:"
|
|
54
|
+
echo " 1. Find an alternative implementation that avoids this file"
|
|
55
|
+
echo " 2. Wait for ${OWNER} to complete and release the lock"
|
|
56
|
+
echo " 3. Set STATUS.json to BLOCKED with reason FILE_LOCK_CONFLICT"
|
|
57
|
+
echo ""
|
|
58
|
+
echo "Lock details in: ${LOCK_JSON}"
|
|
59
|
+
|
|
60
|
+
exit 2
|
|
61
|
+
fi
|
|
62
|
+
# Lock belongs to us - proceed
|
|
63
|
+
else
|
|
64
|
+
# No lock exists - acquire it
|
|
65
|
+
cat > "$LOCK_JSON" << EOF
|
|
66
|
+
{
|
|
67
|
+
"pair": "${PAIR_ID}",
|
|
68
|
+
"file": "${FILE}",
|
|
69
|
+
"acquired_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
70
|
+
}
|
|
71
|
+
EOF
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
} 200>"$LOCK_FILE"
|
|
75
|
+
|
|
76
|
+
# Lock acquired or already owned by us - proceed with write
|
|
77
|
+
exit 0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs when FORGE starts a new session
|
|
3
|
+
# This hook runs at the beginning of every FORGE session
|
|
4
|
+
|
|
5
|
+
PAIR_ID="${SPRINTLESS_PAIR_ID}"
|
|
6
|
+
TICKET_ID="${SPRINTLESS_TICKET_ID}"
|
|
7
|
+
SHARED="${SPRINTLESS_SHARED}"
|
|
8
|
+
WORKTREE="${SPRINTLESS_WORKTREE}"
|
|
9
|
+
|
|
10
|
+
# Log session start to shared event log
|
|
11
|
+
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] forge-${PAIR_ID} session_start ticket=${TICKET_ID}" \
|
|
12
|
+
>> "${SHARED}/../events.log"
|
|
13
|
+
|
|
14
|
+
# Check if this is a resume (HANDOFF.md exists)
|
|
15
|
+
if [ -f "${SHARED}/HANDOFF.md" ]; then
|
|
16
|
+
echo "=========================================="
|
|
17
|
+
echo " RESUME MODE: HANDOFF.md found"
|
|
18
|
+
echo "=========================================="
|
|
19
|
+
echo ""
|
|
20
|
+
echo "Read ${SHARED}/HANDOFF.md before doing anything else."
|
|
21
|
+
echo "Continue from the exact next step described in the handoff."
|
|
22
|
+
echo ""
|
|
23
|
+
echo "Key things to check:"
|
|
24
|
+
echo " 1. Which segments are already complete"
|
|
25
|
+
echo " 2. Which segment is in progress"
|
|
26
|
+
echo " 3. Decisions already made (do not contradict)"
|
|
27
|
+
echo " 4. Files already written (do not rewrite)"
|
|
28
|
+
echo " 5. Exact next step to take"
|
|
29
|
+
echo ""
|
|
30
|
+
else
|
|
31
|
+
echo "=========================================="
|
|
32
|
+
echo " NEW SESSION: No handoff found"
|
|
33
|
+
echo "=========================================="
|
|
34
|
+
echo ""
|
|
35
|
+
echo "Starting fresh on ticket ${TICKET_ID}"
|
|
36
|
+
echo ""
|
|
37
|
+
echo "IMPORTANT - Directory Structure:"
|
|
38
|
+
echo " CURRENT DIR (worktree): ${WORKTREE}"
|
|
39
|
+
echo " -> Write ALL source code, tests, package.json here"
|
|
40
|
+
echo " SHARED DIR: ${SHARED}"
|
|
41
|
+
echo " -> Write PLAN.md, WORKLOG.md, STATUS.json here"
|
|
42
|
+
echo ""
|
|
43
|
+
echo "First steps:"
|
|
44
|
+
echo " 1. Read ${SHARED}/TICKET.md to understand the task"
|
|
45
|
+
echo " 2. Read ${SHARED}/TASK.md for specific instructions"
|
|
46
|
+
echo " 3. Use /plan command to create ${SHARED}/PLAN.md"
|
|
47
|
+
echo " 4. Wait for CONTRACT.md from SENTINEL"
|
|
48
|
+
echo ""
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Show current state
|
|
52
|
+
echo "Environment:"
|
|
53
|
+
echo " PAIR_ID: ${PAIR_ID}"
|
|
54
|
+
echo " TICKET_ID: ${TICKET_ID}"
|
|
55
|
+
echo " WORKTREE: ${WORKTREE}"
|
|
56
|
+
echo " SHARED: ${SHARED}"
|
|
57
|
+
echo ""
|
|
58
|
+
|
|
59
|
+
exit 0
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Runs on Stop - FORGE cannot exit without a terminal artifact
|
|
3
|
+
# This ensures FORGE always leaves a clear state for the harness
|
|
4
|
+
#
|
|
5
|
+
# Environment:
|
|
6
|
+
# SPRINTLESS_SHARED - the shared directory
|
|
7
|
+
|
|
8
|
+
SHARED="${SPRINTLESS_SHARED}"
|
|
9
|
+
|
|
10
|
+
# Accept: STATUS.json written (done or blocked)
|
|
11
|
+
if [ -f "${SHARED}/STATUS.json" ]; then
|
|
12
|
+
# Validate it has required fields
|
|
13
|
+
if command -v python3 &> /dev/null; then
|
|
14
|
+
VALID=$(python3 -c "
|
|
15
|
+
import json, sys
|
|
16
|
+
try:
|
|
17
|
+
s = json.load(open('${SHARED}/STATUS.json'))
|
|
18
|
+
required = ['status','pair','ticket_id','files_changed']
|
|
19
|
+
missing = [k for k in required if k not in s]
|
|
20
|
+
valid_statuses = ['PR_OPENED','BLOCKED','FUEL_EXHAUSTED','PENDING_REVIEW']
|
|
21
|
+
# Note: IMPLEMENTATION_COMPLETE and COMPLETED are NOT valid terminal statuses
|
|
22
|
+
# FORGE must push and create PR (PR_OPENED) or explicitly block
|
|
23
|
+
if missing:
|
|
24
|
+
print(f'missing: {missing}')
|
|
25
|
+
sys.exit(1)
|
|
26
|
+
if s['status'] not in valid_statuses:
|
|
27
|
+
print(f'invalid status: {s[\"status\"]}')
|
|
28
|
+
sys.exit(1)
|
|
29
|
+
except Exception as e:
|
|
30
|
+
print(str(e))
|
|
31
|
+
sys.exit(1)
|
|
32
|
+
" 2>&1)
|
|
33
|
+
if [ $? -ne 0 ]; then
|
|
34
|
+
echo "STATUS.json exists but is invalid: ${VALID}"
|
|
35
|
+
echo "Fix STATUS.json before exiting."
|
|
36
|
+
echo ""
|
|
37
|
+
echo "Required fields: status, pair, ticket_id, files_changed"
|
|
38
|
+
echo "Valid statuses: PR_OPENED, BLOCKED, FUEL_EXHAUSTED, PENDING_REVIEW"
|
|
39
|
+
exit 2
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Accept: HANDOFF.md written (context reset in progress)
|
|
46
|
+
if [ -f "${SHARED}/HANDOFF.md" ]; then
|
|
47
|
+
# Verify HANDOFF.md has required sections
|
|
48
|
+
if grep -q "## Exact next step" "${SHARED}/HANDOFF.md"; then
|
|
49
|
+
exit 0
|
|
50
|
+
else
|
|
51
|
+
echo "HANDOFF.md is incomplete. It must contain '## Exact next step'."
|
|
52
|
+
echo ""
|
|
53
|
+
echo "Required sections in HANDOFF.md:"
|
|
54
|
+
echo " - ## Completed Segments"
|
|
55
|
+
echo " - ## Decisions"
|
|
56
|
+
echo " - ## Files Changed"
|
|
57
|
+
echo " - ## Exact next step"
|
|
58
|
+
exit 2
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Neither exists - block exit
|
|
63
|
+
echo "=============================================="
|
|
64
|
+
echo " BLOCKED: Cannot exit without terminal artifact"
|
|
65
|
+
echo "=============================================="
|
|
66
|
+
echo ""
|
|
67
|
+
echo "You must write either:"
|
|
68
|
+
echo " - ${SHARED}/STATUS.json (if done or blocked)"
|
|
69
|
+
echo " - ${SHARED}/HANDOFF.md (if context reset needed)"
|
|
70
|
+
echo ""
|
|
71
|
+
echo "Use /status command if your work is complete."
|
|
72
|
+
echo "Use /handoff command if you need a context reset."
|
|
73
|
+
echo ""
|
|
74
|
+
|
|
75
|
+
exit 2
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# LORE Session Start Hook
|
|
3
|
+
# Initializes the documenter session
|
|
4
|
+
|
|
5
|
+
echo "=========================================="
|
|
6
|
+
echo "LORE Session Starting"
|
|
7
|
+
echo "=========================================="
|
|
8
|
+
echo ""
|
|
9
|
+
echo "Checking for recently merged PRs..."
|
|
10
|
+
|
|
11
|
+
# Lore monitors merged PRs and updates documentation
|
|
12
|
+
|
|
13
|
+
exit 0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# NEXUS Session Start Hook
|
|
3
|
+
# Initializes the orchestrator session
|
|
4
|
+
|
|
5
|
+
echo "NEXUS session starting..."
|
|
6
|
+
echo "Reading registry from: ${AGENTFLOW_REGISTRY:-.agent/registry.json}"
|
|
7
|
+
echo "Store path: ${AGENTFLOW_STORE:-.agent/store.json}"
|
|
8
|
+
|
|
9
|
+
# Check if jq is installed
|
|
10
|
+
if ! command -v jq &> /dev/null; then
|
|
11
|
+
echo "ERROR: jq is not installed. Please install it to use NEXUS."
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Check for pending command gate items
|
|
16
|
+
if [ -n "${AGENTFLOW_STORE}" ] && [ -f "${AGENTFLOW_STORE}" ]; then
|
|
17
|
+
GATE_PENDING=$(jq -r '.command_gate | length // 0' "${AGENTFLOW_STORE}" 2>/dev/null || echo "0")
|
|
18
|
+
if [ "$GATE_PENDING" -gt 0 ]; then
|
|
19
|
+
echo "WARNING: ${GATE_PENDING} pending command(s) awaiting approval"
|
|
20
|
+
fi
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
exit 0
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SENTINEL Post-Write Validation Hook
|
|
3
|
+
# Validates that eval files have correct structure
|
|
4
|
+
#
|
|
5
|
+
# Environment:
|
|
6
|
+
# SPRINTLESS_SHARED - the shared directory
|
|
7
|
+
# CLAUDE_FILE - the file that was just written (injected by Claude Code)
|
|
8
|
+
|
|
9
|
+
SHARED="${SPRINTLESS_SHARED}"
|
|
10
|
+
FILE="${CLAUDE_FILE:-}"
|
|
11
|
+
|
|
12
|
+
# Only validate eval files
|
|
13
|
+
if [[ ! "$FILE" =~ -eval\.md$ ]] && [[ ! "$FILE" =~ final-review\.md$ ]]; then
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
echo "Validating evaluation file: ${FILE}"
|
|
18
|
+
echo ""
|
|
19
|
+
|
|
20
|
+
# Check required sections
|
|
21
|
+
MISSING=""
|
|
22
|
+
|
|
23
|
+
if ! grep -q "## Summary" "$FILE"; then
|
|
24
|
+
MISSING="${MISSING}Summary, "
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
if ! grep -q "## Tests Run" "$FILE" && ! grep -q "## Test Results" "$FILE"; then
|
|
28
|
+
MISSING="${MISSING}Tests Run, "
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if ! grep -q "## Verdict" "$FILE"; then
|
|
32
|
+
MISSING="${MISSING}Verdict, "
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Check verdict is valid
|
|
36
|
+
VERDICT=$(grep -oP '(?<=## Verdict\n)[A-Z_]+' "$FILE" 2>/dev/null || echo "")
|
|
37
|
+
if [ -n "$VERDICT" ]; then
|
|
38
|
+
if [ "$VERDICT" != "APPROVED" ] && [ "$VERDICT" != "NEEDS_WORK" ]; then
|
|
39
|
+
echo "ERROR: Invalid verdict '${VERDICT}'. Must be APPROVED or NEEDS_WORK."
|
|
40
|
+
exit 2
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
if [ -n "$MISSING" ]; then
|
|
45
|
+
echo "ERROR: Missing required sections: ${MISSING%,*}"
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Required sections for eval files:"
|
|
48
|
+
echo " - ## Summary"
|
|
49
|
+
echo " - ## Tests Run (or ## Test Results)"
|
|
50
|
+
echo " - ## Verdict (APPROVED or NEEDS_WORK)"
|
|
51
|
+
echo ""
|
|
52
|
+
echo "If NEEDS_WORK, also include:"
|
|
53
|
+
echo " - ## Issues Found"
|
|
54
|
+
echo " - ## Required Fixes"
|
|
55
|
+
exit 2
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
echo "Validation passed."
|
|
59
|
+
exit 0
|