harness-flow 4.1.1__tar.gz
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.
- harness_flow-4.1.1/.github/workflows/ci.yml +81 -0
- harness_flow-4.1.1/.github/workflows/release.yml +181 -0
- harness_flow-4.1.1/.gitignore +31 -0
- harness_flow-4.1.1/ARCHITECTURE.md +221 -0
- harness_flow-4.1.1/CHANGELOG.md +40 -0
- harness_flow-4.1.1/LICENSE +21 -0
- harness_flow-4.1.1/PKG-INFO +294 -0
- harness_flow-4.1.1/README.md +256 -0
- harness_flow-4.1.1/README.zh-CN.md +198 -0
- harness_flow-4.1.1/docs/historical.md +64 -0
- harness_flow-4.1.1/install.sh +83 -0
- harness_flow-4.1.1/pyproject.toml +65 -0
- harness_flow-4.1.1/scripts/release.sh +141 -0
- harness_flow-4.1.1/src/harness/__init__.py +8 -0
- harness_flow-4.1.1/src/harness/__main__.py +5 -0
- harness_flow-4.1.1/src/harness/cli.py +91 -0
- harness_flow-4.1.1/src/harness/commands/__init__.py +0 -0
- harness_flow-4.1.1/src/harness/commands/init.py +273 -0
- harness_flow-4.1.1/src/harness/commands/install.py +44 -0
- harness_flow-4.1.1/src/harness/commands/status.py +177 -0
- harness_flow-4.1.1/src/harness/commands/update.py +147 -0
- harness_flow-4.1.1/src/harness/core/__init__.py +0 -0
- harness_flow-4.1.1/src/harness/core/config.py +184 -0
- harness_flow-4.1.1/src/harness/core/context.py +57 -0
- harness_flow-4.1.1/src/harness/core/events.py +107 -0
- harness_flow-4.1.1/src/harness/core/progress.py +162 -0
- harness_flow-4.1.1/src/harness/core/registry.py +334 -0
- harness_flow-4.1.1/src/harness/core/roles.py +26 -0
- harness_flow-4.1.1/src/harness/core/scanner.py +173 -0
- harness_flow-4.1.1/src/harness/core/state.py +104 -0
- harness_flow-4.1.1/src/harness/core/tracker.py +129 -0
- harness_flow-4.1.1/src/harness/core/ui.py +255 -0
- harness_flow-4.1.1/src/harness/i18n/__init__.py +47 -0
- harness_flow-4.1.1/src/harness/i18n/en.py +84 -0
- harness_flow-4.1.1/src/harness/i18n/zh.py +84 -0
- harness_flow-4.1.1/src/harness/integrations/__init__.py +0 -0
- harness_flow-4.1.1/src/harness/integrations/git_ops.py +136 -0
- harness_flow-4.1.1/src/harness/integrations/memverse.py +92 -0
- harness_flow-4.1.1/src/harness/native/__init__.py +1 -0
- harness_flow-4.1.1/src/harness/native/skill_gen.py +273 -0
- harness_flow-4.1.1/src/harness/templates/config.toml.j2 +30 -0
- harness_flow-4.1.1/src/harness/templates/native/agent-architect.md.j2 +90 -0
- harness_flow-4.1.1/src/harness/templates/native/agent-engineer.md.j2 +95 -0
- harness_flow-4.1.1/src/harness/templates/native/agent-product-owner.md.j2 +90 -0
- harness_flow-4.1.1/src/harness/templates/native/agent-project-manager.md.j2 +90 -0
- harness_flow-4.1.1/src/harness/templates/native/agent-qa.md.j2 +95 -0
- harness_flow-4.1.1/src/harness/templates/native/review-checklist.md +178 -0
- harness_flow-4.1.1/src/harness/templates/native/rule-fix-first.mdc.j2 +66 -0
- harness_flow-4.1.1/src/harness/templates/native/rule-safety-guardrails.mdc.j2 +73 -0
- harness_flow-4.1.1/src/harness/templates/native/rule-trust-boundary.mdc.j2 +14 -0
- harness_flow-4.1.1/src/harness/templates/native/rule-workflow.mdc.j2 +28 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_bypass-immunity.md.j2 +12 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_code-review.md.j2 +103 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_error-recovery.md.j2 +17 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_hook-points.md.j2 +14 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_plan-content.md.j2 +23 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_plan-core.md.j2 +78 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_plan-review.md.j2 +72 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_review-gate.md.j2 +60 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_trust-boundary.md.j2 +7 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_vision-content.md.j2 +9 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_vision-core.md.j2 +10 -0
- harness_flow-4.1.1/src/harness/templates/native/sections/_workspace-preflight.md.j2 +88 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-brainstorm.md.j2 +77 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-build.md.j2 +181 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-doc-release.md.j2 +145 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-eval.md.j2 +79 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-investigate.md.j2 +184 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-learn.md.j2 +176 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-plan.md.j2 +52 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-retro.md.j2 +280 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-ship.md.j2 +506 -0
- harness_flow-4.1.1/src/harness/templates/native/skill-vision.md.j2 +64 -0
- harness_flow-4.1.1/src/harness/templates/native/specialists/performance.md +63 -0
- harness_flow-4.1.1/src/harness/templates/native/specialists/red-team.md +61 -0
- harness_flow-4.1.1/src/harness/templates/native/specialists/security.md +65 -0
- harness_flow-4.1.1/src/harness/templates/native/specialists/testing.md +58 -0
- harness_flow-4.1.1/src/harness/templates/vision.md.j2 +20 -0
- harness_flow-4.1.1/src/harness/templates/vision.zh.md.j2 +20 -0
- harness_flow-4.1.1/src/harness/utils/__init__.py +1 -0
- harness_flow-4.1.1/tests/__init__.py +0 -0
- harness_flow-4.1.1/tests/fixtures/codex-output-sample.txt +9 -0
- harness_flow-4.1.1/tests/fixtures/cursor-stream-json-sample.jsonl +10 -0
- harness_flow-4.1.1/tests/test_cli.py +23 -0
- harness_flow-4.1.1/tests/test_config.py +92 -0
- harness_flow-4.1.1/tests/test_context.py +53 -0
- harness_flow-4.1.1/tests/test_env_config.py +61 -0
- harness_flow-4.1.1/tests/test_events.py +47 -0
- harness_flow-4.1.1/tests/test_gate_thresholds.py +182 -0
- harness_flow-4.1.1/tests/test_git_ops.py +165 -0
- harness_flow-4.1.1/tests/test_init.py +215 -0
- harness_flow-4.1.1/tests/test_install.py +47 -0
- harness_flow-4.1.1/tests/test_progress.py +203 -0
- harness_flow-4.1.1/tests/test_registry.py +238 -0
- harness_flow-4.1.1/tests/test_roles_extended.py +26 -0
- harness_flow-4.1.1/tests/test_scanner.py +145 -0
- harness_flow-4.1.1/tests/test_skill_gen_extended.py +891 -0
- harness_flow-4.1.1/tests/test_state.py +287 -0
- harness_flow-4.1.1/tests/test_status.py +194 -0
- harness_flow-4.1.1/tests/test_ui.py +156 -0
- harness_flow-4.1.1/tests/test_update.py +166 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_call:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v5
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Configure git for tests
|
|
28
|
+
run: |
|
|
29
|
+
git config --global user.name "CI"
|
|
30
|
+
git config --global user.email "ci@test"
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
pip install -e ".[dev]"
|
|
36
|
+
|
|
37
|
+
- name: Lint with ruff
|
|
38
|
+
run: |
|
|
39
|
+
ruff check src/ tests/
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: |
|
|
43
|
+
python -m pytest tests/ -v --tb=short
|
|
44
|
+
|
|
45
|
+
build:
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
needs: test
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/checkout@v5
|
|
50
|
+
|
|
51
|
+
- name: Set up Python
|
|
52
|
+
uses: actions/setup-python@v6
|
|
53
|
+
with:
|
|
54
|
+
python-version: "3.12"
|
|
55
|
+
|
|
56
|
+
- name: Install build tools
|
|
57
|
+
run: pip install build
|
|
58
|
+
|
|
59
|
+
- name: Build wheel and sdist
|
|
60
|
+
run: python -m build
|
|
61
|
+
|
|
62
|
+
- name: Verify wheel contents
|
|
63
|
+
run: |
|
|
64
|
+
python -c "
|
|
65
|
+
import zipfile, sys
|
|
66
|
+
whl = next(p for p in __import__('pathlib').Path('dist').iterdir() if p.suffix == '.whl')
|
|
67
|
+
with zipfile.ZipFile(whl) as z:
|
|
68
|
+
names = z.namelist()
|
|
69
|
+
templates = [n for n in names if 'templates/' in n]
|
|
70
|
+
py_files = [n for n in names if n.endswith('.py')]
|
|
71
|
+
print(f'Templates: {len(templates)}, Python: {len(py_files)}, Total: {len(names)}')
|
|
72
|
+
assert len(templates) >= 20, f'Expected >=20 template files, got {len(templates)}'
|
|
73
|
+
assert len(py_files) >= 15, f'Expected >=15 Python files, got {len(py_files)}'
|
|
74
|
+
print('Wheel contents verified.')
|
|
75
|
+
"
|
|
76
|
+
|
|
77
|
+
- name: Upload wheel artifact
|
|
78
|
+
uses: actions/upload-artifact@v5
|
|
79
|
+
with:
|
|
80
|
+
name: dist
|
|
81
|
+
path: dist/
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
name: CD — Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
publish_only:
|
|
9
|
+
description: 'Publish current version without bumping (for retry after failed publish)'
|
|
10
|
+
required: false
|
|
11
|
+
type: boolean
|
|
12
|
+
default: false
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: release
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: write
|
|
20
|
+
id-token: write
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
cd:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
environment:
|
|
26
|
+
name: pypi
|
|
27
|
+
url: https://pypi.org/p/harness-flow
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v5
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 0
|
|
32
|
+
fetch-tags: true
|
|
33
|
+
|
|
34
|
+
- name: Detect release intent from merged PR
|
|
35
|
+
id: intent
|
|
36
|
+
env:
|
|
37
|
+
GH_TOKEN: ${{ github.token }}
|
|
38
|
+
run: |
|
|
39
|
+
if [ "${{ inputs.publish_only }}" = "true" ]; then
|
|
40
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
41
|
+
echo "publish_only=true" >> "$GITHUB_OUTPUT"
|
|
42
|
+
echo "Manual publish-only mode — skipping bump"
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
COMMIT_MSG=$(git log -1 --format='%B')
|
|
47
|
+
|
|
48
|
+
if echo "$COMMIT_MSG" | grep -qi '\[skip release\]'; then
|
|
49
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
50
|
+
echo "Commit message contains [skip release] — skipping"
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
PR_NUMBER=$(gh api "repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" -q '.[0].number' 2>/dev/null || echo "")
|
|
55
|
+
|
|
56
|
+
BUMP_LEVEL="patch"
|
|
57
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
58
|
+
LABELS=$(gh pr view "$PR_NUMBER" --json labels -q '.labels[].name' 2>/dev/null || echo "")
|
|
59
|
+
if echo "$LABELS" | grep -q 'no-release'; then
|
|
60
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
61
|
+
echo "PR #$PR_NUMBER has no-release label — skipping"
|
|
62
|
+
exit 0
|
|
63
|
+
fi
|
|
64
|
+
if echo "$LABELS" | grep -q 'release:major'; then
|
|
65
|
+
BUMP_LEVEL="major"
|
|
66
|
+
elif echo "$LABELS" | grep -q 'release:minor'; then
|
|
67
|
+
BUMP_LEVEL="minor"
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
72
|
+
echo "publish_only=false" >> "$GITHUB_OUTPUT"
|
|
73
|
+
echo "bump_level=$BUMP_LEVEL" >> "$GITHUB_OUTPUT"
|
|
74
|
+
echo "Release intent: bump=$BUMP_LEVEL (PR #${PR_NUMBER:-none})"
|
|
75
|
+
|
|
76
|
+
- name: Set up Python
|
|
77
|
+
if: steps.intent.outputs.skip != 'true'
|
|
78
|
+
uses: actions/setup-python@v6
|
|
79
|
+
with:
|
|
80
|
+
python-version: "3.12"
|
|
81
|
+
|
|
82
|
+
- name: Configure git identity
|
|
83
|
+
if: steps.intent.outputs.skip != 'true'
|
|
84
|
+
run: |
|
|
85
|
+
git config user.name "github-actions[bot]"
|
|
86
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
87
|
+
|
|
88
|
+
- name: Bump version
|
|
89
|
+
if: steps.intent.outputs.skip != 'true' && steps.intent.outputs.publish_only != 'true'
|
|
90
|
+
id: bump
|
|
91
|
+
run: |
|
|
92
|
+
git pull --rebase origin main
|
|
93
|
+
chmod +x scripts/release.sh
|
|
94
|
+
./scripts/release.sh bump ${{ steps.intent.outputs.bump_level }}
|
|
95
|
+
VERSION=$(python3 -c "
|
|
96
|
+
import re
|
|
97
|
+
with open('pyproject.toml') as f:
|
|
98
|
+
m = re.search(r'version\s*=\s*\"([^\"]+)\"', f.read())
|
|
99
|
+
print(m.group(1))
|
|
100
|
+
")
|
|
101
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
102
|
+
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
|
103
|
+
|
|
104
|
+
- name: Read current version (publish-only mode)
|
|
105
|
+
if: steps.intent.outputs.publish_only == 'true'
|
|
106
|
+
id: current
|
|
107
|
+
run: |
|
|
108
|
+
VERSION=$(python3 -c "
|
|
109
|
+
import re
|
|
110
|
+
with open('pyproject.toml') as f:
|
|
111
|
+
m = re.search(r'version\s*=\s*\"([^\"]+)\"', f.read())
|
|
112
|
+
print(m.group(1))
|
|
113
|
+
")
|
|
114
|
+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
115
|
+
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
|
116
|
+
echo "Publish-only: version=$VERSION"
|
|
117
|
+
|
|
118
|
+
- name: Resolve version outputs
|
|
119
|
+
if: steps.intent.outputs.skip != 'true'
|
|
120
|
+
id: ver
|
|
121
|
+
run: |
|
|
122
|
+
if [ "${{ steps.intent.outputs.publish_only }}" = "true" ]; then
|
|
123
|
+
echo "version=${{ steps.current.outputs.version }}" >> "$GITHUB_OUTPUT"
|
|
124
|
+
echo "tag=${{ steps.current.outputs.tag }}" >> "$GITHUB_OUTPUT"
|
|
125
|
+
else
|
|
126
|
+
echo "version=${{ steps.bump.outputs.version }}" >> "$GITHUB_OUTPUT"
|
|
127
|
+
echo "tag=${{ steps.bump.outputs.tag }}" >> "$GITHUB_OUTPUT"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
- name: Check if already released
|
|
131
|
+
if: steps.intent.outputs.skip != 'true'
|
|
132
|
+
id: check
|
|
133
|
+
env:
|
|
134
|
+
GH_TOKEN: ${{ github.token }}
|
|
135
|
+
run: |
|
|
136
|
+
TAG="${{ steps.ver.outputs.tag }}"
|
|
137
|
+
if gh release view "$TAG" --json tagName &>/dev/null; then
|
|
138
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
139
|
+
echo "Release $TAG already exists — skipping"
|
|
140
|
+
else
|
|
141
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
142
|
+
echo "Will release $TAG"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
- name: Commit version bump
|
|
146
|
+
if: steps.intent.outputs.skip != 'true' && steps.check.outputs.skip != 'true' && steps.intent.outputs.publish_only != 'true'
|
|
147
|
+
run: |
|
|
148
|
+
git add pyproject.toml
|
|
149
|
+
git commit -m "chore: bump version to ${{ steps.ver.outputs.version }}"
|
|
150
|
+
git push origin main
|
|
151
|
+
|
|
152
|
+
- name: Create and push tag
|
|
153
|
+
if: steps.intent.outputs.skip != 'true' && steps.check.outputs.skip != 'true' && steps.intent.outputs.publish_only != 'true'
|
|
154
|
+
run: |
|
|
155
|
+
TAG="${{ steps.ver.outputs.tag }}"
|
|
156
|
+
if git rev-parse "$TAG" &>/dev/null; then
|
|
157
|
+
echo "Tag $TAG already exists — skipping"
|
|
158
|
+
else
|
|
159
|
+
git tag "$TAG"
|
|
160
|
+
git push origin "$TAG"
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
- name: Install build tools
|
|
164
|
+
if: steps.intent.outputs.skip != 'true' && steps.check.outputs.skip != 'true'
|
|
165
|
+
run: pip install build
|
|
166
|
+
|
|
167
|
+
- name: Build package
|
|
168
|
+
if: steps.intent.outputs.skip != 'true' && steps.check.outputs.skip != 'true'
|
|
169
|
+
run: python -m build
|
|
170
|
+
|
|
171
|
+
- name: Publish to PyPI
|
|
172
|
+
if: steps.intent.outputs.skip != 'true' && steps.check.outputs.skip != 'true'
|
|
173
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
174
|
+
|
|
175
|
+
- name: Create GitHub Release
|
|
176
|
+
if: steps.intent.outputs.skip != 'true' && steps.check.outputs.skip != 'true'
|
|
177
|
+
uses: softprops/action-gh-release@v2
|
|
178
|
+
with:
|
|
179
|
+
tag_name: ${{ steps.ver.outputs.tag }}
|
|
180
|
+
generate_release_notes: true
|
|
181
|
+
files: dist/*
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.eggs/
|
|
8
|
+
*.egg
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
env/
|
|
12
|
+
.env
|
|
13
|
+
*.so
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
htmlcov/
|
|
18
|
+
.coverage
|
|
19
|
+
*.log
|
|
20
|
+
.DS_Store
|
|
21
|
+
.idea/
|
|
22
|
+
.cursor/
|
|
23
|
+
|
|
24
|
+
# harness — .agents/ 是本地运行时目录,不纳入版本控制
|
|
25
|
+
# 包含 state.json、.stop、tasks/、archive/ 等运行时产物
|
|
26
|
+
# config.toml 和 vision.md 如需团队共享,可通过 git add -f 手动添加
|
|
27
|
+
.agents/
|
|
28
|
+
|
|
29
|
+
# harness — do not track runtime state
|
|
30
|
+
.agents/state.json
|
|
31
|
+
.agents/.stop
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Architecture (v4.0.0 — native-only)
|
|
2
|
+
|
|
3
|
+
This document explains **why** harness-flow is structured the way it is after the native-only refactor. Execution lives in **Cursor**: the Python package bootstraps configuration, generates IDE artifacts, and maintains local state—not an external orchestration loop.
|
|
4
|
+
|
|
5
|
+
For module-level behavior, read the code and docstrings. For day-to-day usage, see `README.md`.
|
|
6
|
+
|
|
7
|
+
## System overview
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
11
|
+
│ harness CLI (Typer) │
|
|
12
|
+
│ init · install · status · update │
|
|
13
|
+
└────────────────────────────┬────────────────────────────────────┘
|
|
14
|
+
│
|
|
15
|
+
┌───────────────────┼───────────────────┐
|
|
16
|
+
▼ ▼ ▼
|
|
17
|
+
.agents/* core/* native/skill_gen
|
|
18
|
+
config, vision, config, state, Jinja2 → .cursor/
|
|
19
|
+
state, progress scanner, ui, … skills, agents, rules
|
|
20
|
+
│
|
|
21
|
+
▼
|
|
22
|
+
integrations/
|
|
23
|
+
git_ops, memverse
|
|
24
|
+
│
|
|
25
|
+
▼
|
|
26
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
27
|
+
│ Cursor IDE — skills, subagents, rules execute the workflow │
|
|
28
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Principle:** Cursor is the execution engine. Harness generates and refreshes the artifacts Cursor runs; there is no separate process supervisor or IDE driver layer in this package.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## CLI layer (`src/harness/cli.py`)
|
|
36
|
+
|
|
37
|
+
Built with **Typer**. Four commands:
|
|
38
|
+
|
|
39
|
+
| Command | Purpose |
|
|
40
|
+
|-----------|---------|
|
|
41
|
+
| `init` | Interactive project bootstrap (see below). |
|
|
42
|
+
| `install` | Regenerate `.cursor/` native artifacts from current config. |
|
|
43
|
+
| `status` | Load session state and render a Rich dashboard. |
|
|
44
|
+
| `update` | Check PyPI, optional pip upgrade, reinstall artifacts, config migration hints. |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Commands (`src/harness/commands/`)
|
|
49
|
+
|
|
50
|
+
### `init.py`
|
|
51
|
+
|
|
52
|
+
Wizard (interactive by default; `--non-interactive` / `-y` for defaults):
|
|
53
|
+
|
|
54
|
+
1. **Language** — `en` or `zh`; drives i18n for the session.
|
|
55
|
+
2. **Project info** — name and description.
|
|
56
|
+
3. **Trunk branch** — detected from git when possible, user-confirmed.
|
|
57
|
+
4. **CI** — project scan (`scanner`) suggests commands; user picks or enters a custom gate command.
|
|
58
|
+
5. **Memverse** — enable/disable and domain prefix for the Memverse MCP integration.
|
|
59
|
+
6. **Vision** — optional immediate vision flow vs. deferred (placeholder file still created when skipped).
|
|
60
|
+
|
|
61
|
+
**Writes:** `.agents/config.toml` (from `templates/config.toml.j2`), `.agents/vision.md` when appropriate, then calls `generate_native_artifacts()` so `.cursor/` is populated. Updates `.gitignore` for harness-local files (e.g. `.agents/state.json`, `.agents/.stop`).
|
|
62
|
+
|
|
63
|
+
### `install.py`
|
|
64
|
+
|
|
65
|
+
Resolves language (CLI flag or project config), loads `HarnessConfig`, calls **`generate_native_artifacts()`** with optional `--force` to overwrite generated files.
|
|
66
|
+
|
|
67
|
+
### `status.py`
|
|
68
|
+
|
|
69
|
+
Loads **`SessionState`** from `.agents/state.json` and renders progress via **Rich** (`core/ui.py` patterns).
|
|
70
|
+
|
|
71
|
+
### `update.py`
|
|
72
|
+
|
|
73
|
+
Queries PyPI for newer versions, runs **`pip install --upgrade harness-flow`** when requested, reinstalls native artifacts, and runs lightweight **config migration** checks with user-visible warnings.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Core (`src/harness/core/`)
|
|
78
|
+
|
|
79
|
+
### `config.py`
|
|
80
|
+
|
|
81
|
+
**Pydantic** models: `ProjectConfig`, `CIConfig`, `ModelsConfig`, `NativeModeConfig`, `WorkflowConfig`, `HarnessConfig`, plus nested integration config (e.g. Memverse).
|
|
82
|
+
|
|
83
|
+
- **`HarnessConfig` uses `ConfigDict(extra="ignore")`** so older TOML keys do not break loading.
|
|
84
|
+
- **`HarnessConfig.load()`** builds the effective config by deep-merging, then validates:
|
|
85
|
+
- Start from **project** `.agents/config.toml` (if present).
|
|
86
|
+
- Merge **`~/.harness/config.toml`** under it so **project wins** on conflicts.
|
|
87
|
+
- Merge **`HARNESS_*` environment variables** on top (highest precedence).
|
|
88
|
+
- Missing keys fall back to **model defaults**.
|
|
89
|
+
|
|
90
|
+
`ModelsConfig` carries `default`, `role_overrides`, and `role_configs`; unknown keys under `[models]` are ignored. Native workflows primarily use `native.*` and project/CI/workflow fields.
|
|
91
|
+
|
|
92
|
+
### `roles.py`
|
|
93
|
+
|
|
94
|
+
Minimal constants only:
|
|
95
|
+
|
|
96
|
+
- **`ALL_ROLES`** — empty `frozenset` (no routed roles in native-only mode).
|
|
97
|
+
- **`NATIVE_REVIEW_ROLES`** — the five native review roles: `architect`, `product_owner`, `engineer`, `qa`, `project_manager`.
|
|
98
|
+
- **`SCORING_DIMENSIONS`** — evaluation dimension labels (used by tests for validation).
|
|
99
|
+
- **`DEFAULT_RUNTIME`** — default runtime label (`"cursor"`) for registry/events/tracker.
|
|
100
|
+
|
|
101
|
+
### `state.py`
|
|
102
|
+
|
|
103
|
+
**`SessionState`**, **`TaskRecord`**, **`CompletedTask`** (and related types) with **JSON** persistence under `.agents/state.json` for resume-friendly dashboards.
|
|
104
|
+
|
|
105
|
+
### `progress.py`
|
|
106
|
+
|
|
107
|
+
**`suggest_next_action`** and **`update_progress`** helpers for markdown progress narratives (e.g. `.agents/progress.md`) aligned with native workflows.
|
|
108
|
+
|
|
109
|
+
### `scanner.py`
|
|
110
|
+
|
|
111
|
+
Scans the repository layout to **suggest CI commands** during `init`.
|
|
112
|
+
|
|
113
|
+
### `ui.py`
|
|
114
|
+
|
|
115
|
+
**Rich** helpers for terminal output (tables, panels, styling) used by status and other commands.
|
|
116
|
+
|
|
117
|
+
### `events.py`
|
|
118
|
+
|
|
119
|
+
Structured **JSONL** event logging for observability of harness-adjacent activity.
|
|
120
|
+
|
|
121
|
+
### `registry.py`
|
|
122
|
+
|
|
123
|
+
**SQLite**-backed registry for agent run metadata (local audit trail).
|
|
124
|
+
|
|
125
|
+
### `context.py`
|
|
126
|
+
|
|
127
|
+
**Task execution context** shared by code paths that still need a unified “where is the task root / config” view.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Native mode generator (`src/harness/native/`)
|
|
132
|
+
|
|
133
|
+
### `skill_gen.py`
|
|
134
|
+
|
|
135
|
+
- Loads **Jinja2** templates from `src/harness/templates/native/`.
|
|
136
|
+
- Builds a **template context** from `HarnessConfig` (CI command, trunk branch, native gates, hooks, per-role model hints, etc.) plus small static principle blocks where templates expect them.
|
|
137
|
+
- **`generate_native_artifacts()`** writes:
|
|
138
|
+
- **10 skills** under `.cursor/skills/harness/<skill-name>/SKILL.md`
|
|
139
|
+
- **5 agents** under `.cursor/agents/*.md`
|
|
140
|
+
- **4 rules** under `.cursor/rules/*.mdc`
|
|
141
|
+
- **Eval resources** (checklist and specialist docs) under `.cursor/skills/harness/harness-eval/`
|
|
142
|
+
- **`.cursor/worktrees.json`** for parallel worktree setup (skipped if the file already exists unless `force`)
|
|
143
|
+
|
|
144
|
+
Idempotent by default for `worktrees.json`; skills/agents/rules are regenerated according to `install`/`force` behavior.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Templates (`src/harness/templates/`)
|
|
149
|
+
|
|
150
|
+
- **`config.toml.j2`** — project config emitted by `init`.
|
|
151
|
+
- **`native/`** — Jinja2 sources for skills, agents, rules, and shared **sections** (e.g. plan/review gates, trust boundary, CI verification).
|
|
152
|
+
- **`vision.md.j2` / `vision.zh.md.j2`** — initial vision stubs.
|
|
153
|
+
|
|
154
|
+
All user-visible harness **behavior** in the IDE is intended to flow from these templates plus `HarnessConfig`, so upgrades can refresh prompts without forking business logic across Python files.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Integrations (`src/harness/integrations/`)
|
|
159
|
+
|
|
160
|
+
- **`git_ops.py`** — git helpers (rebase, merge, cleanup) used where the workflow still touches branches.
|
|
161
|
+
- **`memverse.py`** — Memverse MCP integration for learnings and memory sync aligned with skills.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Design principles
|
|
166
|
+
|
|
167
|
+
1. **Cursor IDE is the execution engine** — Harness generates **skills, agents, and rules** that Cursor’s agent runtime executes. No in-package external CLI orchestration of other IDEs.
|
|
168
|
+
2. **Five-role adversarial review** — The five native roles review **plans and code** in parallel; templates encode how dispatch and aggregation behave.
|
|
169
|
+
3. **Fix-First auto-remediation** — Review output is classified into **AUTO-FIX** vs **ASK** before presentation (encoded in generated rules/skills, not in a Python state machine).
|
|
170
|
+
4. **Config cascade** — **Project** and **global** TOML merge with **project overriding global**; **`HARNESS_*` env vars** override both; Pydantic validates the result.
|
|
171
|
+
5. **Backward compatibility** — **`extra="ignore"`** on `HarnessConfig` allows stale keys from older installs to load safely.
|
|
172
|
+
6. **Template-driven generation** — Native artifacts are rendered from **Jinja2**; Python supplies context and file placement only.
|
|
173
|
+
7. **Local-first** — State, config, registry, and logs are **on disk**; PyPI is only needed for **package updates**, not for routine development.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Artifact layout (high level)
|
|
178
|
+
|
|
179
|
+
**Project (`.agents/`)**
|
|
180
|
+
|
|
181
|
+
- `config.toml` — harness configuration.
|
|
182
|
+
- `vision.md` — product/engineering vision for skills.
|
|
183
|
+
- `state.json` — session state (typically gitignored).
|
|
184
|
+
- `progress.md` — human-readable progress log.
|
|
185
|
+
- `.stop` — optional graceful stop flag (typically gitignored).
|
|
186
|
+
- `tasks/`, `archive/` — task artifacts and history (convention from harness workflow docs).
|
|
187
|
+
|
|
188
|
+
**Generated IDE (`.cursor/`)**
|
|
189
|
+
|
|
190
|
+
- `skills/harness/**` — generated skills and eval resources.
|
|
191
|
+
- `agents/*.md` — five review agents plus any future template outputs.
|
|
192
|
+
- `rules/*.mdc` — always-on rules (workflow, trust boundary, Fix-First, safety).
|
|
193
|
+
- `worktrees.json` — optional parallel-agent worktree bootstrap commands.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Internationalization
|
|
198
|
+
|
|
199
|
+
Module-level catalogs (`i18n/en.py`, `i18n/zh.py`) expose `t(key, **kwargs)`. Missing keys fall back to English. CLI and generator user-facing strings go through this layer when applicable.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Testing orientation
|
|
204
|
+
|
|
205
|
+
Tests are organized around **fast, local behavior**: configuration loading (including env overrides), state/progress, scanner suggestions, skill generation output, install/update flows, git helpers, registry, and UI pieces—without requiring a live Cursor session. Template and config drift is caught by tests that assert on generated files or loaded models.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Design decisions (native era)
|
|
210
|
+
|
|
211
|
+
### Why generate `.cursor/` instead of shipping static files?
|
|
212
|
+
|
|
213
|
+
Project-specific **CI command**, **trunk branch**, **review gates**, and **hooks** must flow into prompts. Templating from `HarnessConfig` keeps one SSOT and allows `harness install` to refresh IDE assets after config edits.
|
|
214
|
+
|
|
215
|
+
### Why keep `ALL_ROLES` empty?
|
|
216
|
+
|
|
217
|
+
Older configs and code paths referenced a unified role set for model validation. An empty `ALL_ROLES` preserves **compatibility** while native mode keys off **`NATIVE_REVIEW_ROLES`** only.
|
|
218
|
+
|
|
219
|
+
### Why SQLite for the registry?
|
|
220
|
+
|
|
221
|
+
A **local, queryable** history of runs supports debugging and audit without a hosted service—consistent with the local-first stance.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 4.1.0
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- **Project renamed** from `harness-orchestrator` to `harness-flow`. Install with `pip install harness-flow`. The Python package name (`harness`) and CLI command (`harness`) are unchanged.
|
|
8
|
+
- **Migration:** If upgrading from `harness-orchestrator`, run `pip uninstall harness-orchestrator && pip install harness-flow`.
|
|
9
|
+
|
|
10
|
+
## 4.0.0 (2026-04-02)
|
|
11
|
+
|
|
12
|
+
### Breaking Changes
|
|
13
|
+
|
|
14
|
+
- **Removed orchestrator mode** — The external CLI-driven orchestrator (`harness run`, `harness auto`, `harness stop`, `harness vision`) has been removed. Harness now operates exclusively in cursor-native mode.
|
|
15
|
+
- **Removed CLI commands**: `run`, `auto`, `stop`, `vision`
|
|
16
|
+
- **Removed modules**: `harness.orchestrator`, `harness.drivers`, `harness.methodology`, `harness.agents` (packaged agent definitions)
|
|
17
|
+
- **Removed config fields**: `workflow.mode`, `workflow.profile`, `workflow.dual_evaluation`, `[drivers]` section, `integrations.memverse.driver`
|
|
18
|
+
- **Removed role registry**: Orchestrator roles (planner, builder, evaluator, alignment_evaluator, strategist, reflector) removed from `harness.core.roles`
|
|
19
|
+
|
|
20
|
+
### Migration
|
|
21
|
+
|
|
22
|
+
- **Old configs are safe**: `HarnessConfig` now uses `extra="ignore"`, so `.agents/config.toml` files with `[drivers]` or removed `[workflow]` fields will load without errors.
|
|
23
|
+
- **Use Cursor skills instead of CLI**: `/harness-plan`, `/harness-vision`, `/harness-brainstorm` replace the removed CLI commands.
|
|
24
|
+
- **Run `harness install --force`** after upgrading to regenerate native artifacts.
|
|
25
|
+
|
|
26
|
+
### What's kept
|
|
27
|
+
|
|
28
|
+
- Full cursor-native mode: skill generation, 5-role review system, Fix-First auto-remediation
|
|
29
|
+
- CLI commands: `init`, `install`, `status`, `update`
|
|
30
|
+
- Core infrastructure: config, state, UI, events, registry, git ops, scanner
|
|
31
|
+
- All native templates and generated artifacts
|
|
32
|
+
|
|
33
|
+
### Simplified
|
|
34
|
+
|
|
35
|
+
- `harness init` wizard: 6 steps (was 9) — no IDE probing, no mode selection
|
|
36
|
+
- `harness install`: only generates native artifacts (no IDE agent copying)
|
|
37
|
+
- `config.py`: cleaner model without driver configs
|
|
38
|
+
- `roles.py`: minimal exports (NATIVE_REVIEW_ROLES, SCORING_DIMENSIONS)
|
|
39
|
+
- `state.py`: data models only (StateMachine removed)
|
|
40
|
+
- i18n catalogs: ~72 keys each (was ~270+)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arthas Zeng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|