mirror of
https://github.com/openprose/prose.git
synced 2026-09-19 05:55:05 +08:00
9856ac5b12
* feat(spec): declared ### Skills section with fail-closed compile resolution Implements the spec from issue #60. Components declare required harness skills via a `### Skills` section (colon form, e.g. `document-skills:pdf`). `prose compile` resolves declared skills against ./skills/, ~/.claude/skills/, ~/.codex/skills/, and ~/.agents/skills/, and fails closed with `skill_unresolved` before forwarding to the agent harness when any are missing. - Spec: skills/open-prose/contract-markdown.md gains a ### Skills row in the Canonical Sections table and a ## Skills H2 covering colon naming, search order, the BYO-harness invariant, and fail-closed semantics. - Implementation: tools/cli/src/skills/declared.ts (parser + resolver + directory walker + DeclaredSkillsUnresolvedError). Pure functions; no I/O beyond readFile / readdir / stat. - Wiring: tools/cli/src/commands/compile.ts pre-checks declared skills before forwarding the compile prompt; fails closed with CompileValidationError when any are unresolved. Gated behind the existing skillPreflight option for test parity. - Example: skills/open-prose/examples/declared-skills/ shows the document-skills:pdf canonical pattern. - Tests: 19 new (18 in declared.test.ts covering parser/resolver/walker/ error formatter; 1 in cli.test.ts asserting compile fails closed before the harness is invoked when a declared skill is missing). - BYO harness: OpenProse never installs harness skills; resolution failure is the user's signal to install the named skill themselves. Resolves #60. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(spec): move skill resolution into compiler program; trim spec per review Address review feedback on #62: - contract-markdown.md: drop the "not duplicated in frontmatter" clause — the rejected alternative shouldn't propagate into the spec. - contract-markdown.md: fail-closed clause now mentions only `prose compile`; `prose run` enforcement is deferred per the PR description. - compiler/index.prose.md: add a `skills_resolver` agent that owns the search-path order, scope aggregation, BYO invariant, and fail-closed semantics. Skill resolution is now a compiler/program-level responsibility, not a harness responsibility, so other harnesses running the compiler get the same behavior. - skills/declared.ts: add a header comment pointing at the program-level spec; this module is the harness implementation of `skills_resolver`. - examples/declared-skills/README.md: update wording to reference the compiler agent. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(ci): make smoke artifacts case-specific * fix(ci): use deterministic audit policy --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
287 lines
8.0 KiB
YAML
287 lines
8.0 KiB
YAML
name: CLI Release Check
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- "tools/cli/**"
|
|
- ".github/workflows/cli-release-check.yml"
|
|
- ".github/workflows/cli-real-harness-smoke.yml"
|
|
- ".github/workflows/cli-skills-smoke.yml"
|
|
- ".github/workflows/openprose-smoke.yml"
|
|
- ".github/workflows/release.yml"
|
|
- ".github/scripts/openprose-smoke/**"
|
|
- "scripts/release-preflight.sh"
|
|
- "scripts/bump-version.sh"
|
|
- ".version-bump.json"
|
|
push:
|
|
branches:
|
|
- main
|
|
paths:
|
|
- "tools/cli/**"
|
|
- ".github/workflows/cli-release-check.yml"
|
|
- ".github/workflows/cli-real-harness-smoke.yml"
|
|
- ".github/workflows/cli-skills-smoke.yml"
|
|
- ".github/workflows/openprose-smoke.yml"
|
|
- ".github/workflows/release.yml"
|
|
- ".github/scripts/openprose-smoke/**"
|
|
- "scripts/release-preflight.sh"
|
|
- "scripts/bump-version.sh"
|
|
- ".version-bump.json"
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: cli-release-check-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
lint-shell-and-actions:
|
|
name: Lint shell and workflow files
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Go for actionlint
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: stable
|
|
cache: false
|
|
|
|
- name: Install actionlint
|
|
run: go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.7
|
|
|
|
- name: Lint GitHub Actions workflows
|
|
run: actionlint .github/workflows/cli-*.yml .github/workflows/plugin-manifest.yml .github/workflows/openprose-smoke.yml .github/workflows/release.yml
|
|
|
|
- name: Check installer shell syntax
|
|
run: bash -n tools/cli/install.sh
|
|
|
|
- name: Check release helper syntax
|
|
run: node --check tools/cli/scripts/build-release-tarball.mjs
|
|
|
|
- name: Check harness smoke helper syntax
|
|
run: node --check tools/cli/scripts/smoke-harness.mjs
|
|
|
|
- name: Check release shell syntax
|
|
run: |
|
|
bash -n scripts/bump-version.sh
|
|
bash -n scripts/release-preflight.sh
|
|
|
|
- name: OpenProse versions in sync
|
|
run: ./scripts/bump-version.sh --check
|
|
|
|
node-checks:
|
|
name: Node ${{ matrix.node-version }} checks
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
node-version:
|
|
- 18
|
|
- 20
|
|
- 22
|
|
defaults:
|
|
run:
|
|
working-directory: tools/cli
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: ${{ matrix.node-version }}
|
|
cache: npm
|
|
cache-dependency-path: tools/cli/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Typecheck
|
|
run: npm run typecheck
|
|
|
|
- name: Test
|
|
run: npm test
|
|
|
|
- name: Build
|
|
run: npm run build
|
|
|
|
- name: Smoke built CLI
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
node dist/index.js --help
|
|
node dist/index.js help
|
|
node dist/index.js run hello.prose.md --harness mock
|
|
PROSE_HARNESS=mock node dist/index.js status
|
|
|
|
npm-package-smoke:
|
|
name: npm package smoke
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
defaults:
|
|
run:
|
|
working-directory: tools/cli
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
cache-dependency-path: tools/cli/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Pack npm artifact
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p "$RUNNER_TEMP/npm-pack"
|
|
npm pack --pack-destination "$RUNNER_TEMP/npm-pack" --json | tee npm-pack.json
|
|
|
|
- name: Validate npm package manifest
|
|
run: |
|
|
node --input-type=module <<'EOF'
|
|
import { readFileSync } from "node:fs";
|
|
|
|
const [pack] = JSON.parse(readFileSync("npm-pack.json", "utf8"));
|
|
const paths = new Set(pack.files.map((file) => file.path));
|
|
const required = ["package.json", "README.md", "LICENSE", "dist/index.js", "dist/index.d.ts"];
|
|
const forbiddenPrefixes = ["src/", "tests/", "node_modules/"];
|
|
|
|
for (const path of required) {
|
|
if (!paths.has(path)) {
|
|
throw new Error(`npm package is missing ${path}`);
|
|
}
|
|
}
|
|
|
|
for (const path of paths) {
|
|
if (forbiddenPrefixes.some((prefix) => path.startsWith(prefix))) {
|
|
throw new Error(`npm package includes ${path}`);
|
|
}
|
|
if (path.endsWith(".map")) {
|
|
throw new Error(`npm package includes sourcemap ${path}`);
|
|
}
|
|
}
|
|
EOF
|
|
|
|
- name: Smoke npm package artifact
|
|
run: |
|
|
set -euo pipefail
|
|
pack_file="$(node --input-type=module <<'EOF'
|
|
import { readFileSync } from "node:fs";
|
|
const [pack] = JSON.parse(readFileSync("npm-pack.json", "utf8"));
|
|
console.log(`${process.env.RUNNER_TEMP}/npm-pack/${pack.filename}`);
|
|
EOF
|
|
)"
|
|
prefix="$RUNNER_TEMP/npm-prefix"
|
|
|
|
npm install --global --prefix "$prefix" "$pack_file"
|
|
"$prefix/bin/prose" --version
|
|
"$prefix/bin/prose" run hello.prose.md --harness mock
|
|
|
|
import_dir="$RUNNER_TEMP/npm-import-smoke"
|
|
mkdir -p "$import_dir"
|
|
cd "$import_dir"
|
|
npm init -y
|
|
npm install "$pack_file"
|
|
node --input-type=module -e "await import('@openprose/prose-cli')"
|
|
|
|
- name: Dry-run npm publish
|
|
run: npm publish --dry-run
|
|
|
|
- name: Upload npm package metadata
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: cli-npm-pack
|
|
path: tools/cli/npm-pack.json
|
|
if-no-files-found: error
|
|
|
|
release-tarball-smoke:
|
|
name: Release tarball smoke on ${{ matrix.os }}
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 25
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os:
|
|
- ubuntu-latest
|
|
- macos-latest
|
|
defaults:
|
|
run:
|
|
working-directory: tools/cli
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
cache-dependency-path: tools/cli/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Build
|
|
run: npm run build
|
|
|
|
- name: Build and smoke release tarball
|
|
run: |
|
|
set -euo pipefail
|
|
release_dir="$RUNNER_TEMP/prose-release"
|
|
install_dir="$RUNNER_TEMP/prose-install"
|
|
bin_dir="$RUNNER_TEMP/prose-bin"
|
|
|
|
npm run release:tarball -- --out-dir "$release_dir" --skip-build
|
|
|
|
env \
|
|
PROSE_VERSION="$(node -p 'require("./package.json").version')" \
|
|
PROSE_BASE_URL="file://$release_dir" \
|
|
PROSE_INSTALL_DIR="$install_dir" \
|
|
PROSE_BIN_DIR="$bin_dir" \
|
|
sh install.sh
|
|
|
|
"$bin_dir/prose" --version
|
|
"$bin_dir/prose" run hello.prose.md --harness mock
|
|
|
|
- name: Upload release tarball
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: cli-release-${{ runner.os }}-${{ runner.arch }}
|
|
path: ${{ runner.temp }}/prose-release/*
|
|
if-no-files-found: error
|
|
|
|
production-audit:
|
|
name: Production dependency audit
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
defaults:
|
|
run:
|
|
working-directory: tools/cli
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
cache-dependency-path: tools/cli/package-lock.json
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Audit production dependencies
|
|
run: npm run audit:policy
|