ai-catapult 0.1.0
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/LICENSE +21 -0
- package/README.md +139 -0
- package/bin/ai-catapult.js +229 -0
- package/dist/claude-plugin/.claude-plugin/marketplace.json +28 -0
- package/dist/claude-plugin/.claude-plugin/plugin.json +21 -0
- package/dist/claude-plugin/skills/ai-catapult-init/REFERENCE.md +1284 -0
- package/dist/claude-plugin/skills/ai-catapult-init/SKILL.md +79 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/README.md +48 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/archgate.md +42 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/brd-prd-traceability.md +64 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/cascade.md +110 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/ci-policy.md +107 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/documentation-blueprint.md +185 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/evals.md +93 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/foundation.md +19 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/host-policy-automation.md +151 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/language-packs.md +63 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/mcp-a2a.md +63 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/memory.md +102 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/migration.md +107 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/phases/01-discover-decide.md +33 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/phases/README.md +33 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/readme-documentation.md +120 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/release-versioning.md +188 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/skill-modernization.md +72 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/sync.md +111 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/topology.md +102 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/traceability.md +136 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/tracker-adapters.md +51 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/validation.md +276 -0
- package/dist/claude-plugin/skills/ai-catapult-init/modules/workflow.md +45 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/AGENTS.md +69 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/CLAUDE.md +3 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/GEMINI.md +3 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/boundary-manifest.json +247 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/drift/backups/.gitkeep +0 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/drift/last-drift.json +7 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/evals/.gitkeep +0 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/evals/coverage-exceptions.json +1 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/handoff/.gitkeep +0 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/matrix.json +19 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/mcp/a2a-handoff.md +51 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/mcp/registry.json +27 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/observability/audit-checklist.md +32 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/observability/conventions.md +35 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/phases/01-discover-decide/status.json +16 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/phases/02-govern-plan/status.json +15 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/phases/03-configure-generate/status.json +22 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/phases/04-validate-handoff/status.json +18 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/policies/model-routing.json +29 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/reviews/ai-failure-modes.md +42 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/rules/security.md +38 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/rules/technical-bounds.md +38 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/skills/git-ops.json +6 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/skills/workspace-sync.json +6 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/system-prompts/architect.md +31 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/system-prompts/developer.md +31 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/system-prompts/qa-engineer.md +31 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/traceability/.gitkeep +0 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/workflows/repo-workflow.json +42 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-ai/workflows/repo-workflow.md +52 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-github/workflows/ci-prek.yml +21 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/dot-rules.ts +178 -0
- package/dist/claude-plugin/skills/ai-catapult-init/templates/prek.toml +13 -0
- package/dist/codex-plugin/.codex-plugin/plugin.json +11 -0
- package/dist/codex-plugin/skills/ai-catapult-init/REFERENCE.md +1284 -0
- package/dist/codex-plugin/skills/ai-catapult-init/SKILL.md +79 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/README.md +48 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/archgate.md +42 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/brd-prd-traceability.md +64 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/cascade.md +110 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/ci-policy.md +107 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/documentation-blueprint.md +185 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/evals.md +93 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/foundation.md +19 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/host-policy-automation.md +151 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/language-packs.md +63 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/mcp-a2a.md +63 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/memory.md +102 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/migration.md +107 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/phases/01-discover-decide.md +33 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/phases/README.md +33 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/readme-documentation.md +120 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/release-versioning.md +188 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/skill-modernization.md +72 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/sync.md +111 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/topology.md +102 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/traceability.md +136 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/tracker-adapters.md +51 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/validation.md +276 -0
- package/dist/codex-plugin/skills/ai-catapult-init/modules/workflow.md +45 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/AGENTS.md +69 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/CLAUDE.md +3 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/GEMINI.md +3 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/boundary-manifest.json +247 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/drift/backups/.gitkeep +0 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/drift/last-drift.json +7 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/evals/.gitkeep +0 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/evals/coverage-exceptions.json +1 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/handoff/.gitkeep +0 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/matrix.json +19 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/mcp/a2a-handoff.md +51 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/mcp/registry.json +27 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/observability/audit-checklist.md +32 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/observability/conventions.md +35 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/phases/01-discover-decide/status.json +16 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/phases/02-govern-plan/status.json +15 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/phases/03-configure-generate/status.json +22 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/phases/04-validate-handoff/status.json +18 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/policies/model-routing.json +29 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/reviews/ai-failure-modes.md +42 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/rules/security.md +38 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/rules/technical-bounds.md +38 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/skills/git-ops.json +6 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/skills/workspace-sync.json +6 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/system-prompts/architect.md +31 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/system-prompts/developer.md +31 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/system-prompts/qa-engineer.md +31 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/traceability/.gitkeep +0 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/workflows/repo-workflow.json +42 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-ai/workflows/repo-workflow.md +52 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-github/workflows/ci-prek.yml +21 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/dot-rules.ts +178 -0
- package/dist/codex-plugin/skills/ai-catapult-init/templates/prek.toml +13 -0
- package/package.json +53 -0
- package/scripts/build-claude-plugin.sh +179 -0
- package/scripts/build-codex-plugin.sh +104 -0
- package/scripts/snapshot-dist.sh +26 -0
- package/setup.sh +63 -0
- package/skills.lock.json +6 -0
- package/src/install.js +380 -0
- package/src/scaffold.js +220 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# build-codex-plugin.sh — assembles the Codex plugin into dist/codex-plugin/.
|
|
3
|
+
#
|
|
4
|
+
# Output layout:
|
|
5
|
+
# dist/codex-plugin/
|
|
6
|
+
# .codex-plugin/plugin.json — plugin manifest
|
|
7
|
+
# skills/ai-catapult-init/ — bundled skill copied from vendor/
|
|
8
|
+
#
|
|
9
|
+
# Deterministic: always wipes and rebuilds dist/codex-plugin/ for idempotence.
|
|
10
|
+
# Fail-closed: exits non-zero if vendor/skills is absent or incomplete.
|
|
11
|
+
#
|
|
12
|
+
# Accepts VENDOR_ROOT env override (for tests) — defaults to <repo>/vendor.
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
18
|
+
|
|
19
|
+
VENDOR_ROOT="${VENDOR_ROOT:-${REPO_ROOT}/vendor}"
|
|
20
|
+
VENDOR_SKILLS="${VENDOR_ROOT}/skills"
|
|
21
|
+
SKILL_NAME="ai-catapult-init"
|
|
22
|
+
SKILL_SRC="${VENDOR_SKILLS}/${SKILL_NAME}"
|
|
23
|
+
DIST_DIR="${REPO_ROOT}/dist/codex-plugin"
|
|
24
|
+
PLUGIN_JSON_DIR="${DIST_DIR}/.codex-plugin"
|
|
25
|
+
SKILLS_DEST="${DIST_DIR}/skills"
|
|
26
|
+
|
|
27
|
+
# --- Fail closed if vendor missing ---
|
|
28
|
+
if [[ ! -d "${VENDOR_SKILLS}" ]]; then
|
|
29
|
+
echo "ERROR: vendor/skills directory not found at ${VENDOR_SKILLS}" >&2
|
|
30
|
+
echo " Run setup.sh first to vendor skills." >&2
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [[ ! -f "${SKILL_SRC}/SKILL.md" ]]; then
|
|
35
|
+
echo "ERROR: ${SKILL_SRC}/SKILL.md not found — vendor may be incomplete" >&2
|
|
36
|
+
echo " Run setup.sh to re-vendor skills." >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# --- Read version from package.json (node already required by project) ---
|
|
41
|
+
VERSION="$(PKG="${REPO_ROOT}/package.json" node -e "process.stdout.write(JSON.parse(require('fs').readFileSync(process.env.PKG,'utf8')).version)")"
|
|
42
|
+
if [[ -z "${VERSION}" ]]; then
|
|
43
|
+
echo "ERROR: could not read version from package.json" >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
echo "Building Codex plugin ai-catapult@${VERSION}..."
|
|
48
|
+
|
|
49
|
+
# --- Wipe + recreate output dirs for determinism ---
|
|
50
|
+
rm -rf "${DIST_DIR}"
|
|
51
|
+
mkdir -p "${PLUGIN_JSON_DIR}"
|
|
52
|
+
mkdir -p "${SKILLS_DEST}"
|
|
53
|
+
|
|
54
|
+
# --- Write plugin.json ---
|
|
55
|
+
cat > "${PLUGIN_JSON_DIR}/plugin.json" <<PLUGIN_JSON
|
|
56
|
+
{
|
|
57
|
+
"name": "ai-catapult",
|
|
58
|
+
"version": "${VERSION}",
|
|
59
|
+
"description": "CLI + Claude Code and Codex plugins for init-ai-repo v3 AI-SDLC governance scaffolding",
|
|
60
|
+
"skills": "./skills/",
|
|
61
|
+
"interface": {
|
|
62
|
+
"displayName": "ai-catapult",
|
|
63
|
+
"shortDescription": "Scaffold init-ai-repo v3 AI-SDLC governance structure in any repo.",
|
|
64
|
+
"category": "Developer Tools"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
PLUGIN_JSON
|
|
68
|
+
|
|
69
|
+
# --- Copy vendored skill ---
|
|
70
|
+
cp -r "${SKILL_SRC}" "${SKILLS_DEST}/${SKILL_NAME}"
|
|
71
|
+
|
|
72
|
+
# --- Validate output ---
|
|
73
|
+
if [[ ! -f "${PLUGIN_JSON_DIR}/plugin.json" ]]; then
|
|
74
|
+
echo "ERROR: plugin.json was not written" >&2
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Validate JSON parses
|
|
79
|
+
node -e "JSON.parse(require('fs').readFileSync('${PLUGIN_JSON_DIR}/plugin.json','utf8'))" \
|
|
80
|
+
|| { echo "ERROR: plugin.json is not valid JSON" >&2; exit 1; }
|
|
81
|
+
|
|
82
|
+
# Validate required fields
|
|
83
|
+
node -e "
|
|
84
|
+
const p = JSON.parse(require('fs').readFileSync('${PLUGIN_JSON_DIR}/plugin.json','utf8'));
|
|
85
|
+
const required = ['name','version','description','skills','interface'];
|
|
86
|
+
for (const f of required) {
|
|
87
|
+
if (!p[f]) { process.stderr.write('ERROR: plugin.json missing required field: ' + f + '\n'); process.exit(1); }
|
|
88
|
+
}
|
|
89
|
+
if (!p.interface.displayName) { process.stderr.write('ERROR: plugin.json interface.displayName missing\n'); process.exit(1); }
|
|
90
|
+
"
|
|
91
|
+
|
|
92
|
+
if [[ ! -d "${SKILLS_DEST}/${SKILL_NAME}" ]]; then
|
|
93
|
+
echo "ERROR: skills/${SKILL_NAME} directory not present in output" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
if [[ ! -f "${SKILLS_DEST}/${SKILL_NAME}/SKILL.md" ]]; then
|
|
98
|
+
echo "ERROR: skills/${SKILL_NAME}/SKILL.md not present in output" >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
echo "OK: dist/codex-plugin assembled"
|
|
103
|
+
echo " .codex-plugin/plugin.json"
|
|
104
|
+
echo " skills/${SKILL_NAME}/SKILL.md"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# snapshot-dist.sh — copy dist/ to dist-snapshot/ for stable test isolation.
|
|
3
|
+
#
|
|
4
|
+
# The install command respects AI_CATAPULT_DIST_ROOT; npm test sets it to
|
|
5
|
+
# dist-snapshot/ so install tests (and the finish-prompt drift-guard) always
|
|
6
|
+
# read from a stable snapshot rather than the live dist/ that claude-plugin
|
|
7
|
+
# tests wipe and rebuild concurrently.
|
|
8
|
+
#
|
|
9
|
+
# Run by: npm run pretest (before node --test)
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
15
|
+
|
|
16
|
+
SRC="${REPO_ROOT}/dist"
|
|
17
|
+
DEST="${REPO_ROOT}/dist-snapshot"
|
|
18
|
+
|
|
19
|
+
if [[ ! -d "${SRC}" ]]; then
|
|
20
|
+
echo "ERROR: dist/ not found at ${SRC} — run build scripts first" >&2
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
rm -rf "${DEST}"
|
|
25
|
+
cp -R "${SRC}" "${DEST}"
|
|
26
|
+
echo "OK: dist-snapshot/ created from dist/"
|
package/setup.sh
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# setup.sh — vendors r3dlex/skills at the SHA pinned in skills.lock.json.
|
|
3
|
+
# Idempotent: safe to re-run. Never commits vendor/ (it is gitignored).
|
|
4
|
+
# Does NOT use git submodules.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
LOCK_FILE="${REPO_ROOT}/skills.lock.json"
|
|
10
|
+
VENDOR_DIR="${REPO_ROOT}/vendor/skills"
|
|
11
|
+
|
|
12
|
+
# --- Read lock ---
|
|
13
|
+
if [[ ! -f "${LOCK_FILE}" ]]; then
|
|
14
|
+
echo "ERROR: ${LOCK_FILE} not found" >&2
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
SKILLS_REPO="$(LOCK_FILE="${LOCK_FILE}" node -e "process.stdout.write(JSON.parse(require('fs').readFileSync(process.env.LOCK_FILE,'utf8')).repo)")"
|
|
19
|
+
LOCKED_SHA="$(LOCK_FILE="${LOCK_FILE}" node -e "process.stdout.write(JSON.parse(require('fs').readFileSync(process.env.LOCK_FILE,'utf8')).sha)")"
|
|
20
|
+
LOCKED_REF="$(LOCK_FILE="${LOCK_FILE}" node -e "process.stdout.write(JSON.parse(require('fs').readFileSync(process.env.LOCK_FILE,'utf8')).ref)")"
|
|
21
|
+
|
|
22
|
+
echo "skills lock: ${SKILLS_REPO}@${LOCKED_REF} (${LOCKED_SHA})"
|
|
23
|
+
|
|
24
|
+
# --- Idempotency check ---
|
|
25
|
+
if [[ -f "${VENDOR_DIR}/HEAD_SHA" ]]; then
|
|
26
|
+
CURRENT_SHA="$(tr -d '[:space:]' < "${VENDOR_DIR}/HEAD_SHA")"
|
|
27
|
+
if [[ "${CURRENT_SHA}" == "${LOCKED_SHA}" ]]; then
|
|
28
|
+
echo "vendor/skills already at ${LOCKED_SHA} — nothing to do."
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
echo "vendor/skills is at ${CURRENT_SHA}, re-vendoring to ${LOCKED_SHA}..."
|
|
32
|
+
rm -rf "${VENDOR_DIR}"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Clear any stale/partial vendor dir left by a previously interrupted run.
|
|
36
|
+
# (The idempotency short-circuit above already returned for a healthy, SHA-matched dir.)
|
|
37
|
+
[[ -e "${VENDOR_DIR}" ]] && rm -rf "${VENDOR_DIR}"
|
|
38
|
+
|
|
39
|
+
mkdir -p "$(dirname "${VENDOR_DIR}")"
|
|
40
|
+
|
|
41
|
+
# --- Clone or fetch at exact SHA ---
|
|
42
|
+
# Use a shallow clone of the ref, then checkout the exact SHA.
|
|
43
|
+
echo "Cloning ${SKILLS_REPO} (ref: ${LOCKED_REF})..."
|
|
44
|
+
git clone --depth=1 --branch "${LOCKED_REF}" "${SKILLS_REPO}" "${VENDOR_DIR}"
|
|
45
|
+
|
|
46
|
+
# Deepen/fetch the exact commit in case shallow tip differs (should match for main)
|
|
47
|
+
ACTUAL_SHA="$(git -C "${VENDOR_DIR}" rev-parse HEAD)"
|
|
48
|
+
if [[ "${ACTUAL_SHA}" != "${LOCKED_SHA}" ]]; then
|
|
49
|
+
echo "Shallow tip is ${ACTUAL_SHA}, fetching exact locked SHA ${LOCKED_SHA}..."
|
|
50
|
+
git -C "${VENDOR_DIR}" fetch --depth=1 origin "${LOCKED_SHA}"
|
|
51
|
+
git -C "${VENDOR_DIR}" checkout "${LOCKED_SHA}"
|
|
52
|
+
ACTUAL_SHA="$(git -C "${VENDOR_DIR}" rev-parse HEAD)"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if [[ "${ACTUAL_SHA}" != "${LOCKED_SHA}" ]]; then
|
|
56
|
+
echo "ERROR: checked out SHA ${ACTUAL_SHA} does not match locked SHA ${LOCKED_SHA}" >&2
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Write HEAD_SHA sentinel so verify-vendor.sh can check without running git
|
|
61
|
+
echo "${LOCKED_SHA}" > "${VENDOR_DIR}/HEAD_SHA"
|
|
62
|
+
|
|
63
|
+
echo "OK: vendor/skills vendored at ${LOCKED_SHA}"
|
package/skills.lock.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "https://github.com/r3dlex/skills.git",
|
|
3
|
+
"ref": "main",
|
|
4
|
+
"sha": "45ceec0c4f59c1b81a497ab0952c0a832aba4989",
|
|
5
|
+
"_note": "Pin to skills main post-Slice-2b. This SHA includes the ai-catapult-init rename and v3 templates under ai-catapult-init/templates/ plus boundary-manifest.json."
|
|
6
|
+
}
|
package/src/install.js
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* install.js — wire assembled plugins into Claude Code and/or Codex harnesses.
|
|
3
|
+
*
|
|
4
|
+
* Install targets:
|
|
5
|
+
* Claude Code: copies payload to
|
|
6
|
+
* ${HOME}/.claude/plugins/ai-catapult/
|
|
7
|
+
* then prints the two-step manual registration:
|
|
8
|
+
* /plugin marketplace add <payload-path>
|
|
9
|
+
* /plugin install ai-catapult@<marketplace-name>
|
|
10
|
+
*
|
|
11
|
+
* Codex: copies payload to
|
|
12
|
+
* ${CODEX_HOME}/plugins/cache/ai-catapult-local/ai-catapult/local/
|
|
13
|
+
* then prints the TOML block the user must add to config.toml
|
|
14
|
+
*
|
|
15
|
+
* Neither handler writes to Claude Code's internal installed_plugins.json nor
|
|
16
|
+
* to Codex's config.toml — those mutations are too invasive and carry corruption
|
|
17
|
+
* risk. We copy the payload and tell the user exactly what to do.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
existsSync,
|
|
22
|
+
mkdirSync,
|
|
23
|
+
rmSync,
|
|
24
|
+
cpSync,
|
|
25
|
+
readFileSync,
|
|
26
|
+
readdirSync,
|
|
27
|
+
} from 'node:fs';
|
|
28
|
+
import { spawnSync } from 'node:child_process';
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
30
|
+
import { dirname, join } from 'node:path';
|
|
31
|
+
import { homedir } from 'node:os';
|
|
32
|
+
|
|
33
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const REPO_ROOT = join(__dirname, '..');
|
|
35
|
+
|
|
36
|
+
// DIST_ROOT may be overridden via env (used by tests to point at a stable pre-built copy)
|
|
37
|
+
const DIST_ROOT = process.env.AI_CATAPULT_DIST_ROOT ?? join(REPO_ROOT, 'dist');
|
|
38
|
+
|
|
39
|
+
// Marketplace / plugin identifiers (stable across installs)
|
|
40
|
+
const MARKETPLACE_NAME = 'ai-catapult-local';
|
|
41
|
+
const PLUGIN_NAME = 'ai-catapult';
|
|
42
|
+
const VERSION_DIR = 'local'; // version dir for local installs
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Internal helpers
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Ensure the plugin dist is present, building it if needed.
|
|
50
|
+
* @param {string} script - shell script filename under scripts/
|
|
51
|
+
* @param {string} distDir - dist output directory; rebuilds if absent
|
|
52
|
+
* @param {boolean} dryRun - if true, skip
|
|
53
|
+
*/
|
|
54
|
+
function ensureBuilt(script, distDir, dryRun) {
|
|
55
|
+
if (dryRun) return;
|
|
56
|
+
|
|
57
|
+
// If DIST_ROOT was overridden via env and the distDir is missing, fail with
|
|
58
|
+
// a clear message rather than silently rebuilding into the wrong place.
|
|
59
|
+
if (process.env.AI_CATAPULT_DIST_ROOT && !existsSync(distDir)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`AI_CATAPULT_DIST_ROOT is set but the expected dist directory is not populated.\n` +
|
|
62
|
+
`Expected: ${distDir}\n` +
|
|
63
|
+
`Run the build scripts first, or unset AI_CATAPULT_DIST_ROOT to use dist/.`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check for either harness manifest to determine if already built
|
|
68
|
+
const claudeManifest = join(distDir, '.claude-plugin', 'plugin.json');
|
|
69
|
+
const codexManifest = join(distDir, '.codex-plugin', 'plugin.json');
|
|
70
|
+
if (existsSync(claudeManifest) || existsSync(codexManifest)) return;
|
|
71
|
+
|
|
72
|
+
// dist absent or empty — build now
|
|
73
|
+
const r = spawnSync('bash', [join(REPO_ROOT, 'scripts', script)], {
|
|
74
|
+
encoding: 'utf8',
|
|
75
|
+
cwd: REPO_ROOT,
|
|
76
|
+
timeout: 60000,
|
|
77
|
+
});
|
|
78
|
+
if (r.status !== 0) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Plugin not built and build script failed.\n` +
|
|
81
|
+
`Run: bash scripts/${script}\n\n${r.stderr}\n${r.stdout}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if an existing directory is a prior ai-catapult install (or empty).
|
|
88
|
+
* Returns true if safe to overwrite without --force.
|
|
89
|
+
*
|
|
90
|
+
* Rules:
|
|
91
|
+
* - Dir does not exist → safe
|
|
92
|
+
* - Dir exists and is empty → safe
|
|
93
|
+
* - Dir exists and carries our plugin.json name → safe
|
|
94
|
+
* - Dir exists and is non-empty without our plugin.json → NOT safe
|
|
95
|
+
*/
|
|
96
|
+
function isSafeToOverwrite(dir) {
|
|
97
|
+
if (!existsSync(dir)) return true;
|
|
98
|
+
|
|
99
|
+
// Check for .claude-plugin/plugin.json
|
|
100
|
+
const claudeManifest = join(dir, '.claude-plugin', 'plugin.json');
|
|
101
|
+
if (existsSync(claudeManifest)) {
|
|
102
|
+
try {
|
|
103
|
+
const p = JSON.parse(readFileSync(claudeManifest, 'utf8'));
|
|
104
|
+
return p.name === PLUGIN_NAME;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Check for .codex-plugin/plugin.json
|
|
111
|
+
const codexManifest = join(dir, '.codex-plugin', 'plugin.json');
|
|
112
|
+
if (existsSync(codexManifest)) {
|
|
113
|
+
try {
|
|
114
|
+
const p = JSON.parse(readFileSync(codexManifest, 'utf8'));
|
|
115
|
+
return p.name === PLUGIN_NAME;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Dir exists but has no manifest — only safe if it is empty
|
|
122
|
+
try {
|
|
123
|
+
return readdirSync(dir).length === 0;
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Wipe and recreate a directory.
|
|
131
|
+
*/
|
|
132
|
+
function resetDir(dir) {
|
|
133
|
+
rmSync(dir, { recursive: true, force: true });
|
|
134
|
+
mkdirSync(dir, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Claude Code install
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Install the Claude Code plugin.
|
|
143
|
+
*
|
|
144
|
+
* Copies the plugin payload to a stable path under ~/.claude/plugins/ai-catapult/
|
|
145
|
+
* and prints the two-step manual registration the user must run inside Claude Code.
|
|
146
|
+
* We do NOT write installed_plugins.json — that is a Claude Code internal file
|
|
147
|
+
* and hand-writing it is brittle.
|
|
148
|
+
*
|
|
149
|
+
* @param {object} opts
|
|
150
|
+
* @param {string} opts.claudeDir - ${HOME}/.claude/
|
|
151
|
+
* @param {boolean} opts.dryRun
|
|
152
|
+
* @param {boolean} opts.force
|
|
153
|
+
*/
|
|
154
|
+
function installClaude({ claudeDir, dryRun, force }) {
|
|
155
|
+
// Stable payload path (not inside the cache hierarchy — avoids collision with
|
|
156
|
+
// Claude Code's own cache management).
|
|
157
|
+
const payloadPath = join(claudeDir, 'plugins', PLUGIN_NAME);
|
|
158
|
+
|
|
159
|
+
if (dryRun) {
|
|
160
|
+
process.stdout.write(`[dry-run] claude: would copy payload to ${payloadPath}\n`);
|
|
161
|
+
process.stdout.write(`[dry-run] claude: would print /plugin registration instructions\n`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Safety check
|
|
166
|
+
if (!isSafeToOverwrite(payloadPath)) {
|
|
167
|
+
if (!force) {
|
|
168
|
+
process.stderr.write(
|
|
169
|
+
`Error: ${payloadPath} exists and is not a prior ai-catapult install.\n` +
|
|
170
|
+
`Use --force to overwrite.\n`,
|
|
171
|
+
);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
process.stdout.write(`Warning: overwriting foreign plugin dir (--force)\n`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Build the plugin if dist is not already assembled
|
|
178
|
+
const distDir = join(DIST_ROOT, 'claude-plugin');
|
|
179
|
+
ensureBuilt('build-claude-plugin.sh', distDir, dryRun);
|
|
180
|
+
|
|
181
|
+
// Copy dist/claude-plugin → payloadPath
|
|
182
|
+
resetDir(payloadPath);
|
|
183
|
+
cpSync(distDir, payloadPath, { recursive: true });
|
|
184
|
+
|
|
185
|
+
// Read version from installed plugin.json
|
|
186
|
+
const pluginJson = join(payloadPath, '.claude-plugin', 'plugin.json');
|
|
187
|
+
const manifest = JSON.parse(readFileSync(pluginJson, 'utf8'));
|
|
188
|
+
const marketplaceName = manifest.name ?? PLUGIN_NAME;
|
|
189
|
+
|
|
190
|
+
process.stdout.write(`Installed Claude Code plugin ai-catapult@${manifest.version}\n`);
|
|
191
|
+
process.stdout.write(` payload: ${payloadPath}\n`);
|
|
192
|
+
process.stdout.write(`\nTo register the plugin in Claude Code, run these two commands inside Claude Code:\n`);
|
|
193
|
+
process.stdout.write(`\n /plugin marketplace add ${payloadPath}\n`);
|
|
194
|
+
process.stdout.write(` /plugin install ${marketplaceName}@${MARKETPLACE_NAME}\n`);
|
|
195
|
+
process.stdout.write(`\nThen reload Claude Code for the plugin to take effect.\n`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
// Codex install
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Install the Codex plugin.
|
|
204
|
+
*
|
|
205
|
+
* Copies the plugin payload to the standard Codex cache path and prints
|
|
206
|
+
* the TOML block the user must add to their config.toml. We do NOT
|
|
207
|
+
* auto-mutate config.toml — that carries corruption risk.
|
|
208
|
+
*
|
|
209
|
+
* Codex discovers plugins via config.toml tables:
|
|
210
|
+
* [marketplaces.<name>] with source_type/source
|
|
211
|
+
* [plugins."<plugin>@<marketplace>"] with enabled = true
|
|
212
|
+
*
|
|
213
|
+
* @param {object} opts
|
|
214
|
+
* @param {string} opts.codexHome - ${CODEX_HOME:-~/.codex}
|
|
215
|
+
* @param {boolean} opts.dryRun
|
|
216
|
+
* @param {boolean} opts.force
|
|
217
|
+
*/
|
|
218
|
+
function installCodex({ codexHome, dryRun, force }) {
|
|
219
|
+
const pluginRoot = join(codexHome, 'plugins', 'cache', MARKETPLACE_NAME, PLUGIN_NAME, VERSION_DIR);
|
|
220
|
+
|
|
221
|
+
if (dryRun) {
|
|
222
|
+
process.stdout.write(`[dry-run] codex: would copy payload to ${pluginRoot}\n`);
|
|
223
|
+
process.stdout.write(`[dry-run] codex: would print TOML block for config.toml registration\n`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Safety check
|
|
228
|
+
if (!isSafeToOverwrite(pluginRoot)) {
|
|
229
|
+
if (!force) {
|
|
230
|
+
process.stderr.write(
|
|
231
|
+
`Error: ${pluginRoot} exists and is not a prior ai-catapult install.\n` +
|
|
232
|
+
`Use --force to overwrite.\n`,
|
|
233
|
+
);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
process.stdout.write(`Warning: overwriting foreign plugin dir (--force)\n`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Build the plugin if dist is not already assembled
|
|
240
|
+
const distDir = join(DIST_ROOT, 'codex-plugin');
|
|
241
|
+
ensureBuilt('build-codex-plugin.sh', distDir, dryRun);
|
|
242
|
+
|
|
243
|
+
// Copy dist/codex-plugin → pluginRoot
|
|
244
|
+
resetDir(pluginRoot);
|
|
245
|
+
cpSync(distDir, pluginRoot, { recursive: true });
|
|
246
|
+
|
|
247
|
+
// Read version from installed plugin.json
|
|
248
|
+
const pluginJsonPath = join(pluginRoot, '.codex-plugin', 'plugin.json');
|
|
249
|
+
const manifest = JSON.parse(readFileSync(pluginJsonPath, 'utf8'));
|
|
250
|
+
|
|
251
|
+
process.stdout.write(`Installed Codex plugin ai-catapult@${manifest.version}\n`);
|
|
252
|
+
process.stdout.write(` payload: ${pluginRoot}\n`);
|
|
253
|
+
process.stdout.write(`\nTo register the plugin, add the following block to your Codex config.toml\n`);
|
|
254
|
+
process.stdout.write(`(typically at \${CODEX_HOME:-~/.codex}/config.toml):\n`);
|
|
255
|
+
process.stdout.write(`\n`);
|
|
256
|
+
process.stdout.write(`[marketplaces.${MARKETPLACE_NAME}]\n`);
|
|
257
|
+
process.stdout.write(`source_type = "local"\n`);
|
|
258
|
+
process.stdout.write(`source = "${pluginRoot}"\n`);
|
|
259
|
+
process.stdout.write(`\n`);
|
|
260
|
+
process.stdout.write(`[plugins."${PLUGIN_NAME}@${MARKETPLACE_NAME}"]\n`);
|
|
261
|
+
process.stdout.write(`enabled = true\n`);
|
|
262
|
+
process.stdout.write(`\nThen restart Codex for the plugin to take effect.\n`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
// Public entry point
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
const INSTALL_HELP = `Usage: ai-catapult install [options]
|
|
270
|
+
|
|
271
|
+
Install the ai-catapult plugin into detected AI coding harnesses.
|
|
272
|
+
|
|
273
|
+
Detected harnesses:
|
|
274
|
+
Claude Code ~/.claude/ present
|
|
275
|
+
Codex \${CODEX_HOME:-~/.codex}/ present
|
|
276
|
+
|
|
277
|
+
Options:
|
|
278
|
+
--harness <claude|codex|all> Select harness(es) to install into (default: auto-detect)
|
|
279
|
+
--dry-run Print what would happen without writing
|
|
280
|
+
--force Overwrite existing dirs even if not a prior ai-catapult install
|
|
281
|
+
-h, --help Show this help`;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Main install handler.
|
|
285
|
+
* @param {string[]} argv - arguments after "install" (already sliced)
|
|
286
|
+
* @param {object} [envOverride] - override HOME / CODEX_HOME (for tests)
|
|
287
|
+
*/
|
|
288
|
+
export function runInstall(argv, envOverride = {}) {
|
|
289
|
+
// Parse flags
|
|
290
|
+
const flags = new Map();
|
|
291
|
+
const positionals = [];
|
|
292
|
+
let i = 0;
|
|
293
|
+
while (i < argv.length) {
|
|
294
|
+
const arg = argv[i];
|
|
295
|
+
if (arg === '--') { positionals.push(...argv.slice(i + 1)); break; }
|
|
296
|
+
if (arg.startsWith('--')) {
|
|
297
|
+
const key = arg.slice(2);
|
|
298
|
+
const next = argv[i + 1];
|
|
299
|
+
if (next !== undefined && !next.startsWith('-')) {
|
|
300
|
+
flags.set(key, next);
|
|
301
|
+
i += 2;
|
|
302
|
+
} else {
|
|
303
|
+
flags.set(key, true);
|
|
304
|
+
i += 1;
|
|
305
|
+
}
|
|
306
|
+
} else if (arg.startsWith('-') && arg.length === 2) {
|
|
307
|
+
flags.set(arg.slice(1), true);
|
|
308
|
+
i += 1;
|
|
309
|
+
} else {
|
|
310
|
+
positionals.push(arg);
|
|
311
|
+
i += 1;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (flags.has('help') || flags.has('h')) {
|
|
316
|
+
process.stdout.write(INSTALL_HELP + '\n');
|
|
317
|
+
process.exit(0);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const dryRun = flags.has('dry-run');
|
|
321
|
+
const force = flags.has('force');
|
|
322
|
+
const harnessFlag = flags.get('harness'); // 'claude' | 'codex' | 'all' | undefined
|
|
323
|
+
|
|
324
|
+
// Resolve dirs
|
|
325
|
+
const home = envOverride.HOME ?? process.env.HOME ?? homedir();
|
|
326
|
+
const codexHome = envOverride.CODEX_HOME
|
|
327
|
+
?? process.env.CODEX_HOME
|
|
328
|
+
?? join(home, '.codex');
|
|
329
|
+
|
|
330
|
+
const claudeDir = join(home, '.claude');
|
|
331
|
+
const claudeDetected = existsSync(claudeDir);
|
|
332
|
+
const codexDetected = existsSync(codexHome);
|
|
333
|
+
|
|
334
|
+
// Determine which harnesses to run
|
|
335
|
+
let runClaude = false;
|
|
336
|
+
let runCodex = false;
|
|
337
|
+
|
|
338
|
+
if (harnessFlag === 'claude') {
|
|
339
|
+
runClaude = true;
|
|
340
|
+
} else if (harnessFlag === 'codex') {
|
|
341
|
+
runCodex = true;
|
|
342
|
+
} else if (harnessFlag === 'all') {
|
|
343
|
+
runClaude = true;
|
|
344
|
+
runCodex = true;
|
|
345
|
+
} else {
|
|
346
|
+
// Auto-detect
|
|
347
|
+
runClaude = claudeDetected;
|
|
348
|
+
runCodex = codexDetected;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (!runClaude && !runCodex) {
|
|
352
|
+
process.stdout.write(
|
|
353
|
+
'No supported harness detected.\n' +
|
|
354
|
+
' Claude Code: ~/.claude/ not found\n' +
|
|
355
|
+
` Codex: ${codexHome} not found\n` +
|
|
356
|
+
'\nUse --harness claude|codex|all to force a harness.\n',
|
|
357
|
+
);
|
|
358
|
+
process.exit(0);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (dryRun) {
|
|
362
|
+
process.stdout.write('[dry-run] No changes will be made.\n');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (runClaude) {
|
|
366
|
+
if (!claudeDetected && !dryRun) {
|
|
367
|
+
process.stdout.write(`Skipping Claude Code: ${claudeDir} not found\n`);
|
|
368
|
+
} else {
|
|
369
|
+
installClaude({ claudeDir, dryRun, force });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (runCodex) {
|
|
374
|
+
if (!codexDetected && !dryRun) {
|
|
375
|
+
process.stdout.write(`Skipping Codex: ${codexHome} not found\n`);
|
|
376
|
+
} else {
|
|
377
|
+
installCodex({ codexHome, dryRun, force });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|