outcomeci-cli 0.1.0__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.
- outcomeci_cli-0.1.0/.github/workflows/dependency-scan.yml +23 -0
- outcomeci_cli-0.1.0/.github/workflows/homebrew.yml +62 -0
- outcomeci_cli-0.1.0/.github/workflows/publish.yml +95 -0
- outcomeci_cli-0.1.0/.github/workflows/secret-scan.yml +44 -0
- outcomeci_cli-0.1.0/.github/workflows/semantic-release.yml +56 -0
- outcomeci_cli-0.1.0/.github/workflows/test.yml +43 -0
- outcomeci_cli-0.1.0/.gitignore +7 -0
- outcomeci_cli-0.1.0/PKG-INFO +72 -0
- outcomeci_cli-0.1.0/README.md +60 -0
- outcomeci_cli-0.1.0/packaging/outcomeci-cli.rb +19 -0
- outcomeci_cli-0.1.0/pyproject.toml +38 -0
- outcomeci_cli-0.1.0/scripts/refresh-homebrew-resources.py +34 -0
- outcomeci_cli-0.1.0/scripts/release-range.sh +21 -0
- outcomeci_cli-0.1.0/specs/001-standup-runtime/plan.md +11 -0
- outcomeci_cli-0.1.0/specs/001-standup-runtime/spec.md +30 -0
- outcomeci_cli-0.1.0/specs/001-standup-runtime/tasks.md +14 -0
- outcomeci_cli-0.1.0/src/outcomeci/__init__.py +7 -0
- outcomeci_cli-0.1.0/src/outcomeci/cli.py +140 -0
- outcomeci_cli-0.1.0/src/outcomeci/config.py +142 -0
- outcomeci_cli-0.1.0/src/outcomeci/local.py +318 -0
- outcomeci_cli-0.1.0/src/outcomeci/manifest.py +52 -0
- outcomeci_cli-0.1.0/src/outcomeci/outcome.py +265 -0
- outcomeci_cli-0.1.0/src/outcomeci/process.py +87 -0
- outcomeci_cli-0.1.0/src/outcomeci/repository.py +51 -0
- outcomeci_cli-0.1.0/src/outcomeci/templates.py +126 -0
- outcomeci_cli-0.1.0/src/outcomeci/twin.py +40 -0
- outcomeci_cli-0.1.0/tests/test_cli.py +72 -0
- outcomeci_cli-0.1.0/tests/test_local.py +80 -0
- outcomeci_cli-0.1.0/tests/test_manifest.py +44 -0
- outcomeci_cli-0.1.0/tests/test_outcome.py +60 -0
- outcomeci_cli-0.1.0/tests/test_release.py +33 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Dependency scan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "0 7 * * 1"
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
audit:
|
|
13
|
+
name: pip-audit
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
|
+
- uses: actions/setup-python@v7
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- run: pip install -e '.[test]' pip-audit
|
|
23
|
+
- run: pip-audit --skip-editable
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Update Homebrew tap
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
tag:
|
|
9
|
+
description: "Released tag to point the formula at, e.g. v0.1.0"
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
bump:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- name: Resolve version
|
|
18
|
+
id: version
|
|
19
|
+
env:
|
|
20
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
21
|
+
run: echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
|
|
22
|
+
- name: Wait for PyPI sdist
|
|
23
|
+
id: pypi
|
|
24
|
+
run: |
|
|
25
|
+
VERSION="${{ steps.version.outputs.version }}"
|
|
26
|
+
for i in $(seq 1 30); do
|
|
27
|
+
JSON=$(curl -fsSL "https://pypi.org/pypi/outcomeci-cli/${VERSION}/json" || true)
|
|
28
|
+
URL=$(printf '%s' "$JSON" | jq -r '.urls[]? | select(.packagetype=="sdist") | .url')
|
|
29
|
+
SHA=$(printf '%s' "$JSON" | jq -r '.urls[]? | select(.packagetype=="sdist") | .digests.sha256')
|
|
30
|
+
if [ -n "$URL" ] && [ "$URL" != "null" ]; then break; fi
|
|
31
|
+
sleep 10
|
|
32
|
+
done
|
|
33
|
+
if [ -z "$URL" ] || [ "$URL" = "null" ]; then exit 1; fi
|
|
34
|
+
echo "url=$URL" >> "$GITHUB_OUTPUT"
|
|
35
|
+
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
|
36
|
+
- uses: actions/checkout@v7
|
|
37
|
+
with:
|
|
38
|
+
ref: ${{ inputs.tag || github.ref_name }}
|
|
39
|
+
path: source
|
|
40
|
+
- uses: actions/checkout@v7
|
|
41
|
+
with:
|
|
42
|
+
repository: outcomeci/homebrew-tap
|
|
43
|
+
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
44
|
+
path: tap
|
|
45
|
+
- name: Update formula and resources
|
|
46
|
+
env:
|
|
47
|
+
VERSION: ${{ steps.version.outputs.version }}
|
|
48
|
+
URL: ${{ steps.pypi.outputs.url }}
|
|
49
|
+
SHA: ${{ steps.pypi.outputs.sha }}
|
|
50
|
+
run: |
|
|
51
|
+
F=tap/Formula/outcomeci-cli.rb
|
|
52
|
+
mkdir -p tap/Formula
|
|
53
|
+
if [ ! -f "$F" ]; then cp source/packaging/outcomeci-cli.rb "$F"; fi
|
|
54
|
+
sed -i -E "s#^ url \".*\"# url \"${URL}\"#" "$F"
|
|
55
|
+
sed -i -E "s#^ sha256 \".*\"# sha256 \"${SHA}\"#" "$F"
|
|
56
|
+
python3 source/scripts/refresh-homebrew-resources.py "$F" "$VERSION"
|
|
57
|
+
git -C tap config user.name "github-actions[bot]"
|
|
58
|
+
git -C tap config user.email "github-actions[bot]@users.noreply.github.com"
|
|
59
|
+
if git -C tap diff --quiet; then exit 0; fi
|
|
60
|
+
git -C tap add Formula/outcomeci-cli.rb
|
|
61
|
+
git -C tap commit -m "outcomeci-cli ${VERSION}"
|
|
62
|
+
git -C tap push
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
tag:
|
|
9
|
+
description: "Tag to release, e.g. v0.1.0"
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
publish:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
environment: pypi
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
with:
|
|
24
|
+
ref: ${{ inputs.tag || github.ref_name }}
|
|
25
|
+
- uses: actions/setup-python@v7
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
- run: pip install build
|
|
29
|
+
- run: python -m build
|
|
30
|
+
- name: Verify tag matches package
|
|
31
|
+
env:
|
|
32
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
33
|
+
run: |
|
|
34
|
+
python - <<'PY'
|
|
35
|
+
import email, os, pathlib, sys, zipfile
|
|
36
|
+
expected = os.environ["TAG"].removeprefix("v")
|
|
37
|
+
wheel = next(pathlib.Path("dist").glob("*.whl"))
|
|
38
|
+
with zipfile.ZipFile(wheel) as archive:
|
|
39
|
+
path = next(name for name in archive.namelist() if name.endswith(".dist-info/METADATA"))
|
|
40
|
+
actual = email.message_from_bytes(archive.read(path))["Version"]
|
|
41
|
+
if expected != actual:
|
|
42
|
+
sys.exit(f"tag {expected} does not match built package version {actual}")
|
|
43
|
+
PY
|
|
44
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
45
|
+
|
|
46
|
+
release:
|
|
47
|
+
needs: publish
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
permissions:
|
|
50
|
+
contents: write
|
|
51
|
+
id-token: write
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v7
|
|
54
|
+
with:
|
|
55
|
+
ref: ${{ inputs.tag || github.ref_name }}
|
|
56
|
+
fetch-depth: 0
|
|
57
|
+
- name: Resolve release range
|
|
58
|
+
id: release-range
|
|
59
|
+
env:
|
|
60
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
61
|
+
run: scripts/release-range.sh "$TAG" "$GITHUB_OUTPUT"
|
|
62
|
+
- name: Resolve version
|
|
63
|
+
id: version
|
|
64
|
+
env:
|
|
65
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
66
|
+
run: echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
|
|
67
|
+
- uses: aws-actions/configure-aws-credentials@v6
|
|
68
|
+
with:
|
|
69
|
+
role-to-assume: ${{ vars.CHANGELOG_AWS_ROLE_ARN }}
|
|
70
|
+
aws-region: ${{ vars.AWS_REGION || 'us-east-1' }}
|
|
71
|
+
- name: Render release notes
|
|
72
|
+
id: changelog
|
|
73
|
+
uses: outcomeci/spareparts-changelog@v0
|
|
74
|
+
with:
|
|
75
|
+
provider: anthropic
|
|
76
|
+
instructions: ${{ vars.CHANGELOG_INSTRUCTIONS }}
|
|
77
|
+
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
78
|
+
from: ${{ steps.release-range.outputs.from }}
|
|
79
|
+
to: ${{ steps.release-range.outputs.to }}
|
|
80
|
+
title: ${{ inputs.tag || github.ref_name }}
|
|
81
|
+
write-repository: "false"
|
|
82
|
+
publish-s3: "true"
|
|
83
|
+
s3-bucket: ${{ vars.CHANGELOG_S3_BUCKET }}
|
|
84
|
+
s3-key: releases/outcomeci-cli/${{ steps.version.outputs.version }}.md
|
|
85
|
+
s3-region: ${{ vars.AWS_REGION || 'us-east-1' }}
|
|
86
|
+
publish-linkedin: "false"
|
|
87
|
+
- name: Create GitHub release
|
|
88
|
+
env:
|
|
89
|
+
RELEASE_NOTES: ${{ steps.changelog.outputs.markdown }}
|
|
90
|
+
GH_TOKEN: ${{ github.token }}
|
|
91
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
92
|
+
run: |
|
|
93
|
+
test -n "$RELEASE_NOTES"
|
|
94
|
+
printf '%s\n' "$RELEASE_NOTES" > release-notes.md
|
|
95
|
+
gh release create "$TAG" --notes-file release-notes.md --verify-tag
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Secret scan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
secrets:
|
|
10
|
+
name: trufflehog
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
env:
|
|
15
|
+
TRUFFLEHOG_VERSION: "3.95.9"
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
- name: Resolve scan base
|
|
21
|
+
id: base
|
|
22
|
+
env:
|
|
23
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
24
|
+
PR_BASE: ${{ github.event.pull_request.base.sha }}
|
|
25
|
+
PUSH_BEFORE: ${{ github.event.before }}
|
|
26
|
+
run: |
|
|
27
|
+
if [ "$EVENT_NAME" = "pull_request" ]; then
|
|
28
|
+
echo "since=$PR_BASE" >> "$GITHUB_OUTPUT"
|
|
29
|
+
elif [ "$PUSH_BEFORE" = "0000000000000000000000000000000000000000" ] || [ -z "$PUSH_BEFORE" ]; then
|
|
30
|
+
echo "since=" >> "$GITHUB_OUTPUT"
|
|
31
|
+
else
|
|
32
|
+
echo "since=$PUSH_BEFORE" >> "$GITHUB_OUTPUT"
|
|
33
|
+
fi
|
|
34
|
+
- name: Install TruffleHog
|
|
35
|
+
run: |
|
|
36
|
+
curl -sSfL https://raw.githubusercontent.com/trufflesecurity/trufflehog/main/scripts/install.sh \
|
|
37
|
+
| sh -s -- -b /usr/local/bin "v${TRUFFLEHOG_VERSION}"
|
|
38
|
+
- name: Scan for secrets
|
|
39
|
+
env:
|
|
40
|
+
SINCE: ${{ steps.base.outputs.since }}
|
|
41
|
+
run: |
|
|
42
|
+
set -- git "file://." --results=verified,unknown --fail --no-update --exclude-detectors=Lob
|
|
43
|
+
if [ -n "$SINCE" ]; then set -- "$@" --since-commit "$SINCE"; fi
|
|
44
|
+
trufflehog "$@"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Semantic release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
concurrency:
|
|
8
|
+
group: semantic-release
|
|
9
|
+
cancel-in-progress: false
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write
|
|
13
|
+
actions: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
release:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
with:
|
|
21
|
+
fetch-depth: 0
|
|
22
|
+
- uses: actions/setup-python@v7
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
- run: pip install "commitizen>=4,<5"
|
|
26
|
+
- name: Create next semantic version tag
|
|
27
|
+
id: version
|
|
28
|
+
shell: bash
|
|
29
|
+
run: |
|
|
30
|
+
set +e
|
|
31
|
+
OUTPUT=$(cz bump --get-next 2>&1)
|
|
32
|
+
STATUS=$?
|
|
33
|
+
set -e
|
|
34
|
+
if [ "$STATUS" -eq 3 ] || [ "$STATUS" -eq 21 ]; then
|
|
35
|
+
echo "released=false" >> "$GITHUB_OUTPUT"
|
|
36
|
+
exit 0
|
|
37
|
+
fi
|
|
38
|
+
if [ "$STATUS" -ne 0 ]; then printf '%s\n' "$OUTPUT"; exit "$STATUS"; fi
|
|
39
|
+
NEXT_VERSION=$(printf '%s\n' "$OUTPUT" | tail -n 1)
|
|
40
|
+
if ! [[ "$NEXT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$ ]]; then
|
|
41
|
+
echo "::error::Commitizen returned an invalid version: $NEXT_VERSION"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
TAG="v${NEXT_VERSION}"
|
|
45
|
+
git tag "$TAG"
|
|
46
|
+
git push origin "$TAG"
|
|
47
|
+
echo "released=true" >> "$GITHUB_OUTPUT"
|
|
48
|
+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
|
|
49
|
+
- name: Publish package and update Homebrew
|
|
50
|
+
if: steps.version.outputs.released == 'true'
|
|
51
|
+
env:
|
|
52
|
+
GH_TOKEN: ${{ github.token }}
|
|
53
|
+
TAG: ${{ steps.version.outputs.tag }}
|
|
54
|
+
run: |
|
|
55
|
+
gh workflow run publish.yml --ref main -f tag="$TAG"
|
|
56
|
+
gh workflow run homebrew.yml --ref main -f tag="$TAG"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
pytest:
|
|
10
|
+
name: pytest on Python ${{ matrix.python-version }}
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
- uses: actions/setup-python@v7
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
- run: pip install -e '.[test]'
|
|
24
|
+
- run: pytest -q
|
|
25
|
+
|
|
26
|
+
build:
|
|
27
|
+
name: build and inspect distribution
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v7
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 0
|
|
33
|
+
- uses: actions/setup-python@v7
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.12"
|
|
36
|
+
- run: pip install build twine
|
|
37
|
+
- run: python -m build
|
|
38
|
+
- run: twine check dist/*
|
|
39
|
+
- name: Verify installed command
|
|
40
|
+
run: |
|
|
41
|
+
pip install --force-reinstall dist/*.whl
|
|
42
|
+
oci --version
|
|
43
|
+
oci outcome --help
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: outcomeci-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Portable Standup and outcome workflow runtime for OutcomeCI
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: pyyaml>=6.0
|
|
8
|
+
Provides-Extra: test
|
|
9
|
+
Requires-Dist: build>=1.2; extra == 'test'
|
|
10
|
+
Requires-Dist: pytest>=8.3; extra == 'test'
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# OutcomeCI CLI
|
|
14
|
+
|
|
15
|
+
`oci` installs and executes the open-source Standup framework used by OutcomeCI.
|
|
16
|
+
|
|
17
|
+
```console
|
|
18
|
+
pip install outcomeci-cli
|
|
19
|
+
oci init
|
|
20
|
+
oci validate
|
|
21
|
+
oci outcome run --claim /workspace/outcome-claim.json --workspace /workspace
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Repository context and workflow instructions live in `.outcomeci/`. The same
|
|
25
|
+
`outcome.yml` contract can run locally or use OutcomeCI Cloud for managed state,
|
|
26
|
+
Digital Twin context, expert messaging, credentials, and runners.
|
|
27
|
+
|
|
28
|
+
To run the discovery phases with a locally installed Codex or Claude Code:
|
|
29
|
+
|
|
30
|
+
```console
|
|
31
|
+
oci init --backend filesystem
|
|
32
|
+
oci outcome compile outcome.yml
|
|
33
|
+
oci outcome start "Describe the outcome you want"
|
|
34
|
+
oci outcome status
|
|
35
|
+
oci outcome continue <run-id> --approve
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`start` performs intake. Each approved `continue` advances to plan and then
|
|
39
|
+
tasks. Local state, artifacts, manifests, transcripts, and usage records are
|
|
40
|
+
written beneath `.outcomeci/outcomes/<run-id>/`. Runner and model defaults come
|
|
41
|
+
from `outcome.yml`; `--agent` and `--model` provide per-invocation overrides.
|
|
42
|
+
|
|
43
|
+
`oci init` also installs repository-local Outcome skills for Codex and Claude
|
|
44
|
+
Code. They use the current interactive session rather than launching a child
|
|
45
|
+
process:
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
Codex: $outcome Describe the outcome you want
|
|
49
|
+
Claude Code: /outcome Describe the outcome you want
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The skill coordinates `outcome begin`, `compile`, `validate-artifacts`,
|
|
53
|
+
`advance`, and `status`. Managed OutcomeCI runners continue to use `outcome
|
|
54
|
+
run --claim ...`; both modes share workflow compilation and artifact schemas.
|
|
55
|
+
|
|
56
|
+
Filesystem workflows can pin repository evidence explicitly:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
spec:
|
|
60
|
+
context:
|
|
61
|
+
provider: filesystem
|
|
62
|
+
include:
|
|
63
|
+
- .outcomeci/context/**
|
|
64
|
+
- docs/**
|
|
65
|
+
exclude:
|
|
66
|
+
- node_modules/**
|
|
67
|
+
- dist/**
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Compiled context records each matched path, size, and SHA-256 hash. Context
|
|
71
|
+
changes therefore produce a new workflow revision. Files remain in place and
|
|
72
|
+
are read by the active local agent; they are not copied into `outcome.yml`.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# OutcomeCI CLI
|
|
2
|
+
|
|
3
|
+
`oci` installs and executes the open-source Standup framework used by OutcomeCI.
|
|
4
|
+
|
|
5
|
+
```console
|
|
6
|
+
pip install outcomeci-cli
|
|
7
|
+
oci init
|
|
8
|
+
oci validate
|
|
9
|
+
oci outcome run --claim /workspace/outcome-claim.json --workspace /workspace
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Repository context and workflow instructions live in `.outcomeci/`. The same
|
|
13
|
+
`outcome.yml` contract can run locally or use OutcomeCI Cloud for managed state,
|
|
14
|
+
Digital Twin context, expert messaging, credentials, and runners.
|
|
15
|
+
|
|
16
|
+
To run the discovery phases with a locally installed Codex or Claude Code:
|
|
17
|
+
|
|
18
|
+
```console
|
|
19
|
+
oci init --backend filesystem
|
|
20
|
+
oci outcome compile outcome.yml
|
|
21
|
+
oci outcome start "Describe the outcome you want"
|
|
22
|
+
oci outcome status
|
|
23
|
+
oci outcome continue <run-id> --approve
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`start` performs intake. Each approved `continue` advances to plan and then
|
|
27
|
+
tasks. Local state, artifacts, manifests, transcripts, and usage records are
|
|
28
|
+
written beneath `.outcomeci/outcomes/<run-id>/`. Runner and model defaults come
|
|
29
|
+
from `outcome.yml`; `--agent` and `--model` provide per-invocation overrides.
|
|
30
|
+
|
|
31
|
+
`oci init` also installs repository-local Outcome skills for Codex and Claude
|
|
32
|
+
Code. They use the current interactive session rather than launching a child
|
|
33
|
+
process:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
Codex: $outcome Describe the outcome you want
|
|
37
|
+
Claude Code: /outcome Describe the outcome you want
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The skill coordinates `outcome begin`, `compile`, `validate-artifacts`,
|
|
41
|
+
`advance`, and `status`. Managed OutcomeCI runners continue to use `outcome
|
|
42
|
+
run --claim ...`; both modes share workflow compilation and artifact schemas.
|
|
43
|
+
|
|
44
|
+
Filesystem workflows can pin repository evidence explicitly:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
spec:
|
|
48
|
+
context:
|
|
49
|
+
provider: filesystem
|
|
50
|
+
include:
|
|
51
|
+
- .outcomeci/context/**
|
|
52
|
+
- docs/**
|
|
53
|
+
exclude:
|
|
54
|
+
- node_modules/**
|
|
55
|
+
- dist/**
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Compiled context records each matched path, size, and SHA-256 hash. Context
|
|
59
|
+
changes therefore produce a new workflow revision. Files remain in place and
|
|
60
|
+
are read by the active local agent; they are not copied into `outcome.yml`.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class OutcomeciCli < Formula
|
|
2
|
+
include Language::Python::Virtualenv
|
|
3
|
+
|
|
4
|
+
desc "Portable Standup and outcome workflow runtime"
|
|
5
|
+
homepage "https://github.com/outcomeci/cli"
|
|
6
|
+
url "https://files.pythonhosted.org/packages/source/o/outcomeci-cli/outcomeci_cli-0.1.0.tar.gz"
|
|
7
|
+
sha256 "0000000000000000000000000000000000000000000000000000000000000000"
|
|
8
|
+
|
|
9
|
+
depends_on "python@3.12"
|
|
10
|
+
|
|
11
|
+
def install
|
|
12
|
+
virtualenv_install_with_resources
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
test do
|
|
16
|
+
assert_match "oci", shell_output("#{bin}/oci --help")
|
|
17
|
+
assert_match version.to_s, shell_output("#{bin}/oci --version")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27", "hatch-vcs"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "outcomeci-cli"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Portable Standup and outcome workflow runtime for OutcomeCI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "Apache-2.0" }
|
|
12
|
+
dependencies = ["PyYAML>=6.0"]
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
oci = "outcomeci.cli:main"
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
test = ["pytest>=8.3", "build>=1.2"]
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/outcomeci"]
|
|
22
|
+
|
|
23
|
+
[tool.hatch.version]
|
|
24
|
+
source = "vcs"
|
|
25
|
+
|
|
26
|
+
[tool.hatch.version.raw-options]
|
|
27
|
+
version_scheme = "no-guess-dev"
|
|
28
|
+
|
|
29
|
+
[tool.commitizen]
|
|
30
|
+
name = "cz_conventional_commits"
|
|
31
|
+
version_provider = "scm"
|
|
32
|
+
version_scheme = "semver2"
|
|
33
|
+
tag_format = "v${version}"
|
|
34
|
+
major_version_zero = true
|
|
35
|
+
|
|
36
|
+
[tool.pytest.ini_options]
|
|
37
|
+
testpaths = ["tests"]
|
|
38
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Refresh Homebrew Python resource blocks from a published OutcomeCI release."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import urllib.request
|
|
11
|
+
import venv
|
|
12
|
+
|
|
13
|
+
formula, version = sys.argv[1], sys.argv[2]
|
|
14
|
+
temporary = tempfile.mkdtemp()
|
|
15
|
+
venv.create(f"{temporary}/venv", with_pip=True)
|
|
16
|
+
pip = f"{temporary}/venv/bin/pip"
|
|
17
|
+
subprocess.check_call([pip, "install", "-q", f"outcomeci-cli=={version}"])
|
|
18
|
+
freeze = subprocess.check_output([pip, "list", "--format=freeze"], text=True)
|
|
19
|
+
dependencies = []
|
|
20
|
+
for line in freeze.splitlines():
|
|
21
|
+
if "==" in line:
|
|
22
|
+
name, dependency_version = line.split("==", 1)
|
|
23
|
+
if name.casefold() not in {"outcomeci-cli", "pip", "setuptools", "wheel"}:
|
|
24
|
+
dependencies.append((name, dependency_version))
|
|
25
|
+
blocks = []
|
|
26
|
+
for name, dependency_version in sorted(dependencies, key=lambda item: item[0].casefold()):
|
|
27
|
+
with urllib.request.urlopen(f"https://pypi.org/pypi/{name}/{dependency_version}/json") as response:
|
|
28
|
+
metadata = json.load(response)
|
|
29
|
+
sdist = next(item for item in metadata["urls"] if item["packagetype"] == "sdist")
|
|
30
|
+
blocks.append(f' resource "{metadata["info"]["name"]}" do\n url "{sdist["url"]}"\n sha256 "{sdist["digests"]["sha256"]}"\n end')
|
|
31
|
+
text = open(formula, encoding="utf-8").read()
|
|
32
|
+
text = re.sub(r'\n resource "[^"]+" do\n(?:.*\n)*? end\n', '\n', text)
|
|
33
|
+
text = re.sub(r'( depends_on "python@[^"]+"\n)', r'\1\n' + "\n\n".join(blocks) + "\n", text, count=1)
|
|
34
|
+
open(formula, "w", encoding="utf-8").write(re.sub(r'\n{3,}', '\n\n', text))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
current_tag=${1:-}
|
|
5
|
+
output_file=${2:-}
|
|
6
|
+
if [[ ! $current_tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || [[ -z $output_file ]]; then
|
|
7
|
+
echo "usage: release-range.sh vMAJOR.MINOR.PATCH OUTPUT" >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
git rev-parse --verify "${current_tag}^{commit}" >/dev/null
|
|
11
|
+
previous_tag=$(git tag --merged "$current_tag" --list 'v*' | awk '$0 ~ /^v[0-9]+\.[0-9]+\.[0-9]+$/' | awk -v current="$current_tag" '$0 != current' | sort -V -r | head -n 1)
|
|
12
|
+
if [[ -n $previous_tag ]]; then
|
|
13
|
+
from_revision=$previous_tag
|
|
14
|
+
else
|
|
15
|
+
from_revision=$(git rev-list --max-parents=0 "$current_tag" | tail -n 1)
|
|
16
|
+
fi
|
|
17
|
+
if [[ -z $from_revision ]] || [[ $(git rev-parse "${from_revision}^{commit}") == $(git rev-parse "${current_tag}^{commit}") ]]; then
|
|
18
|
+
echo "release range is empty" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
printf 'from=%s\nto=%s\n' "$from_revision" "$current_tag" >> "$output_file"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Implementation Plan: Standup Runtime
|
|
2
|
+
|
|
3
|
+
1. Create a dependency-light Python package and lazy command router.
|
|
4
|
+
2. Add deterministic repository initialization and update behavior.
|
|
5
|
+
3. Validate and compile `outcome.yml` plus referenced instruction content.
|
|
6
|
+
4. Port the bounded Outcome executor, GitHub checkout, agent adapters,
|
|
7
|
+
transcript normalization, and Digital Twin client under OutcomeCI naming.
|
|
8
|
+
5. Add contract and package tests, build a wheel, and install it into the local
|
|
9
|
+
Outcome runner image.
|
|
10
|
+
6. Change the API-issued command and runner environment to `oci` / `OUTCOMECI_*`.
|
|
11
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Feature Specification: Standup Runtime
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Publish a clean OutcomeCI CLI that lets engineers initialize and inspect the
|
|
6
|
+
Standup framework while allowing OutcomeCI Cloud to delegate bounded outcome
|
|
7
|
+
phases to Codex or Claude Code through the same workflow contract.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Expose `oci init`, `oci update`, `oci validate`, `oci status`, `oci outcome`,
|
|
12
|
+
and `oci twin` without legacy Spare Parts command aliases.
|
|
13
|
+
- Store repository context under `.outcomeci/` and call the framework Standup.
|
|
14
|
+
- Make `outcome.yml` the orchestration policy and Markdown the readable
|
|
15
|
+
instruction body included in its deterministic compiled revision.
|
|
16
|
+
- Run intake, plan, and tasks against immutable product checkouts while writing
|
|
17
|
+
versioned artifacts and evidence only to the state repository.
|
|
18
|
+
- Accept only bounded, revision-pinned tools and credentials supplied by the
|
|
19
|
+
OutcomeCI API claim.
|
|
20
|
+
- Capture native agent transcripts and normalized token usage.
|
|
21
|
+
|
|
22
|
+
## Acceptance Criteria
|
|
23
|
+
|
|
24
|
+
- A fresh repository can be initialized and validated.
|
|
25
|
+
- Editing referenced YAML or Markdown changes the compiled workflow revision.
|
|
26
|
+
- A local wheel exposes the `oci` executable.
|
|
27
|
+
- The Outcome runner container can install that wheel and successfully invoke
|
|
28
|
+
`oci outcome run` from an API claim.
|
|
29
|
+
- No public interface writes `.sp/`, invokes `sp`, or generates Huddle naming.
|
|
30
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Tasks: Standup Runtime
|
|
2
|
+
|
|
3
|
+
- [x] Scaffold the `outcomeci-cli` distribution and `oci` entrypoint.
|
|
4
|
+
- [x] Implement `.outcomeci/` initialization, update, validation, and status.
|
|
5
|
+
- [x] Add default `outcome.yml` and Standup/phase instruction templates.
|
|
6
|
+
- [x] Compile referenced instructions into the workflow revision.
|
|
7
|
+
- [x] Port the Digital Twin job-scoped search client.
|
|
8
|
+
- [x] Port agent invocation and bounded phase execution for intake, plan, and tasks.
|
|
9
|
+
- [x] Generate `standup.md`, manifests, transcripts, and usage records.
|
|
10
|
+
- [x] Add tests and build the local wheel.
|
|
11
|
+
- [x] Update API claim command and OutcomeCI environment names.
|
|
12
|
+
- [x] Build the local agent-runner image with the wheel and run initialization/validation smoke checks.
|
|
13
|
+
- [ ] Initialize the development state repository and run a live API-issued claim.
|
|
14
|
+
- [ ] Fold implementation and PR phases into the OutcomeCI executor.
|