feat(P03): multi-project support, NFR milestone versioning, phase context reset, install scripts
---ci---
phase: 3
milestone: v0.3.0
status: complete
decisions:
- id: D-006
decision: Multi-project via .ci/<slug>/ subdirectories and config.json registry
rationale: Backward compatible migration from flat files; slug-based namespacing for branches and commits
confidence: 0.92
alternatives: [Git worktrees, Separate repos with subtrees]
- id: D-007
decision: NFR milestones use progressive patch versioning (no minor tag)
rationale: NFR phases (fix/chore/docs/perf/refactor/test) don't represent feature delivery; patch increments reflect incremental improvement only
confidence: 0.90
alternatives: [Treat all milestones uniformly, Skip versioning for NFR]
- id: D-008
decision: Phase context reset via git checkpoint + fresh agent spawn
rationale: Git-native architecture makes full state serialization safe; fresh context prevents accumulated conversation drift
confidence: 0.88
alternatives: [Context compaction, Sliding window summarization]
- id: D-009
decision: Install via both npm postinstall and standalone bash script
rationale: Postinstall only fires on npm install -g; standalone script covers manual/cloned installs
confidence: 0.95
alternatives: [Postinstall only, Makefile target]
---/ci---
Source code:
- Added ProjectEntry, projects[], active_project to CIConfig
- Added project?: string to CiMetadata, CommitScope, all commit input types
- CiFiles: multi-project support (projectSlug, listProjects, addProject, migrateFlatToProject, isNfrMilestone)
- GitContext: projectSlug support, detectProjectFromCommit(), isNfrMilestone()
- GitBranch: project-prefixed branch naming via prefix()
- commit-builder/parser: project field in ---ci--- blocks
- config.ts: initCI() accepts projectSlug/projectName
- Implemented parseRoadmapMd phase parsing
- 284 tests passing (66 new tests)
Install scripts:
- scripts/install.sh: Standalone bash installer
- scripts/postinstall.js: npm postinstall (global installs only)
OpenCode integration:
- All 18 agents updated with multi-project project_context
- All 11 workflows updated with Step 0: Confirm Active Project
- All 5 references updated (branch-strategy, ci-files-discipline, commit-schema, decision-engine, git-context-loading)
- All 3 contexts updated (dev, research, review)
- VERSION bumped to 0.3.0
Package:
- Added files field, postinstall script, install script alias
- Version bumped to 0.3.0
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
OPENCODE_DIR="${HOME}/.config/opencode"
|
||||
CI_DIR="$(cd "$(dirname "$0")/.." && pwd)/opencode"
|
||||
|
||||
UNINSTALL=false
|
||||
FORCE=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--uninstall) UNINSTALL=true ;;
|
||||
--force) FORCE=true ;;
|
||||
--help|-h)
|
||||
echo "Usage: $(basename "$0") [--uninstall] [--force]"
|
||||
echo ""
|
||||
echo "Install CI opencode integration files to ~/.config/opencode/"
|
||||
echo ""
|
||||
echo " --uninstall Remove CI integration files"
|
||||
echo " --force Overwrite existing files without prompting"
|
||||
echo " --help Show this help"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$UNINSTALL" = true ]; then
|
||||
echo "Uninstalling CI opencode integration..."
|
||||
|
||||
rm -f "${OPENCODE_DIR}/agents/ci-"*.md 2>/dev/null || true
|
||||
rm -f "${OPENCODE_DIR}/command/ci-"*.md 2>/dev/null || true
|
||||
rm -rf "${OPENCODE_DIR}/ci/" 2>/dev/null || true
|
||||
|
||||
echo "CI integration files removed."
|
||||
echo "Note: opencode.json permissions entry preserved (edit manually if needed)."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -d "$CI_DIR" ]; then
|
||||
echo "Error: opencode/ directory not found at ${CI_DIR}"
|
||||
echo "Ensure you're running from the CI repository root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing CI opencode integration..."
|
||||
echo " Source: ${CI_DIR}"
|
||||
echo " Target: ${OPENCODE_DIR}"
|
||||
echo ""
|
||||
|
||||
mkdir -p "${OPENCODE_DIR}/agents"
|
||||
mkdir -p "${OPENCODE_DIR}/command"
|
||||
mkdir -p "${OPENCODE_DIR}/ci/contexts"
|
||||
mkdir -p "${OPENCODE_DIR}/ci/references"
|
||||
mkdir -p "${OPENCODE_DIR}/ci/workflows"
|
||||
|
||||
COPIED=0
|
||||
SKIPPED=0
|
||||
|
||||
copy_file() {
|
||||
local src="$1"
|
||||
local dest="$2"
|
||||
|
||||
if [ -f "$dest" ] && [ "$FORCE" = false ]; then
|
||||
if cmp -s "$src" "$dest" 2>/dev/null; then
|
||||
SKIPPED=$((SKIPPED + 1))
|
||||
return
|
||||
fi
|
||||
echo " Conflict: $(basename "$src") already exists. Use --force to overwrite."
|
||||
SKIPPED=$((SKIPPED + 1))
|
||||
return
|
||||
fi
|
||||
|
||||
cp "$src" "$dest"
|
||||
COPIED=$((COPIED + 1))
|
||||
}
|
||||
|
||||
echo "Installing agents..."
|
||||
for f in "${CI_DIR}/agents/ci-"*.md; do
|
||||
[ -f "$f" ] && copy_file "$f" "${OPENCODE_DIR}/agents/$(basename "$f")"
|
||||
done
|
||||
|
||||
echo "Installing commands..."
|
||||
for f in "${CI_DIR}/command/ci-"*.md; do
|
||||
[ -f "$f" ] && copy_file "$f" "${OPENCODE_DIR}/command/$(basename "$f")"
|
||||
done
|
||||
|
||||
echo "Installing contexts..."
|
||||
for f in "${CI_DIR}/ci/contexts/"*.md; do
|
||||
[ -f "$f" ] && copy_file "$f" "${OPENCODE_DIR}/ci/contexts/$(basename "$f")"
|
||||
done
|
||||
|
||||
echo "Installing references..."
|
||||
for f in "${CI_DIR}/ci/references/"*.md; do
|
||||
[ -f "$f" ] && copy_file "$f" "${OPENCODE_DIR}/ci/references/$(basename "$f")"
|
||||
done
|
||||
|
||||
echo "Installing workflows..."
|
||||
for f in "${CI_DIR}/ci/workflows/"*.md; do
|
||||
[ -f "$f" ] && copy_file "$f" "${OPENCODE_DIR}/ci/workflows/$(basename "$f")"
|
||||
done
|
||||
|
||||
echo "Installing VERSION..."
|
||||
[ -f "${CI_DIR}/ci/VERSION" ] && copy_file "${CI_DIR}/ci/VERSION" "${OPENCODE_DIR}/ci/VERSION"
|
||||
|
||||
echo ""
|
||||
echo "Merging opencode.json permissions..."
|
||||
OPENCODE_JSON="${OPENCODE_DIR}/opencode.json"
|
||||
CI_JSON="${CI_DIR}/opencode.json"
|
||||
|
||||
if [ -f "$CI_JSON" ]; then
|
||||
if [ ! -f "$OPENCODE_JSON" ]; then
|
||||
cp "$CI_JSON" "$OPENCODE_JSON"
|
||||
echo " Created opencode.json"
|
||||
else
|
||||
if command -v node &>/dev/null; then
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const existing = JSON.parse(fs.readFileSync('${OPENCODE_JSON}', 'utf8'));
|
||||
const ci = JSON.parse(fs.readFileSync('${CI_JSON}', 'utf8'));
|
||||
const merged = { ...existing };
|
||||
merged.permission = merged.permission || {};
|
||||
merged.permission.read = merged.permission.read || {};
|
||||
merged.permission.external_directory = merged.permission.external_directory || {};
|
||||
for (const [k, v] of Object.entries(ci.permission?.read || {})) {
|
||||
if (!merged.permission.read[k]) merged.permission.read[k] = v;
|
||||
}
|
||||
for (const [k, v] of Object.entries(ci.permission?.external_directory || {})) {
|
||||
if (!merged.permission.external_directory[k]) merged.permission.external_directory[k] = v;
|
||||
}
|
||||
fs.writeFileSync('${OPENCODE_JSON}', JSON.stringify(merged, null, 2));
|
||||
console.log(' Merged permissions (preserved existing entries)');
|
||||
"
|
||||
else
|
||||
echo " Warning: node not found. Manually merge opencode.json permissions."
|
||||
echo " Add to opencode.json:"
|
||||
echo ' "~/.config/opencode/ci/*": "allow" (in permission.read and permission.external_directory)'
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " CI ► INSTALL COMPLETE"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo " Copied: ${COPIED} files"
|
||||
echo " Skipped: ${SKIPPED} files"
|
||||
echo ""
|
||||
echo " Commands available: ci-init, ci-run, ci-quick, ci-status,"
|
||||
echo " ci-audit, ci-verify, ci-debug, ci-review, ci-ship,"
|
||||
echo " ci-rollback, ci-clarify"
|
||||
echo ""
|
||||
echo " Run --uninstall to remove."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
Reference in New Issue
Block a user