@jeiemgi/cckit 0.1.6
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/.claude-plugin/plugin.json +22 -0
- package/AGENTS.md +101 -0
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +143 -0
- package/SECURITY.md +22 -0
- package/bin/cckit +215 -0
- package/cckit.config.json +34 -0
- package/commands/kit-add.md +42 -0
- package/commands/kit-docs.md +45 -0
- package/commands/kit-doctor.md +52 -0
- package/commands/kit-export-project.md +58 -0
- package/commands/kit-export-training.md +49 -0
- package/commands/kit-init.md +126 -0
- package/commands/kit-routines.md +59 -0
- package/commands/kit-update.md +132 -0
- package/docs/kit-annotate/01-explainer.html +225 -0
- package/docs/kit-annotate/02-implementation-plan.html +196 -0
- package/docs/media/.onboarding-capture.cast +5 -0
- package/docs/media/README.md +43 -0
- package/docs/media/build-demo.sh +63 -0
- package/docs/media/build-kit-init.sh +51 -0
- package/docs/media/build-onboarding.sh +51 -0
- package/docs/media/kit-dry-run.cast +107 -0
- package/docs/media/kit-dry-run.gif +0 -0
- package/docs/media/kit-init.cast +56 -0
- package/docs/media/kit-init.gif +0 -0
- package/docs/media/kit-onboarding.cast +148 -0
- package/docs/media/kit-onboarding.gif +0 -0
- package/githooks/pre-commit +18 -0
- package/kit.config.schema.json +105 -0
- package/package.json +54 -0
- package/privacy-denylist.example +8 -0
- package/profiles/automation.json +36 -0
- package/profiles/content.json +41 -0
- package/profiles/minimal.json +31 -0
- package/profiles/research.json +37 -0
- package/profiles/software.json +32 -0
- package/scripts/annotate-setup.sh +149 -0
- package/scripts/autopilot.sh +50 -0
- package/scripts/capture-project-ids.sh +53 -0
- package/scripts/check.sh +66 -0
- package/scripts/contribute.sh +48 -0
- package/scripts/debug.sh +54 -0
- package/scripts/init-upgrade-test.sh +99 -0
- package/scripts/init.sh +827 -0
- package/scripts/install.sh +24 -0
- package/scripts/kit-add-test.sh +62 -0
- package/scripts/kit-add.sh +115 -0
- package/scripts/kit-adopt-test.sh +61 -0
- package/scripts/kit-adopt.sh +122 -0
- package/scripts/kit-bump-version.sh +79 -0
- package/scripts/kit-digest.sh +126 -0
- package/scripts/kit-doctor.sh +663 -0
- package/scripts/kit-export-project-test.sh +82 -0
- package/scripts/kit-export-project.sh +245 -0
- package/scripts/kit-export-training-test.sh +51 -0
- package/scripts/kit-export-training.sh +175 -0
- package/scripts/kit-migrate-test.sh +80 -0
- package/scripts/kit-migrate.sh +190 -0
- package/scripts/kit-onboard-test.sh +63 -0
- package/scripts/kit-onboard.sh +69 -0
- package/scripts/kit-promote-test.sh +54 -0
- package/scripts/kit-promote.sh +102 -0
- package/scripts/kit-remove-test.sh +61 -0
- package/scripts/kit-remove.sh +84 -0
- package/scripts/kit-routines.sh +322 -0
- package/scripts/kit-version-check.sh +91 -0
- package/scripts/kit-wire-test.sh +54 -0
- package/scripts/kit-wire.sh +132 -0
- package/scripts/knowledge-lint.sh +96 -0
- package/scripts/lib/cckit-output.sh +36 -0
- package/scripts/lib/effort-metrics.sh +452 -0
- package/scripts/lib/effort-ops-test.sh +83 -0
- package/scripts/lib/effort-ops.sh +132 -0
- package/scripts/lib/effort-plan.sh +104 -0
- package/scripts/lib/effort.sh +191 -0
- package/scripts/lib/engine-adapter.sh +92 -0
- package/scripts/lib/gh-log.sh +58 -0
- package/scripts/lib/gh-project.sh +212 -0
- package/scripts/lib/handoff.sh +35 -0
- package/scripts/lib/kit-cli-test.sh +42 -0
- package/scripts/lib/kit-cli.sh +32 -0
- package/scripts/lib/kit-config-resolve.sh +145 -0
- package/scripts/lib/kit-config.sh +88 -0
- package/scripts/lib/kit-engine-test.sh +107 -0
- package/scripts/lib/kit-events.sh +62 -0
- package/scripts/lib/kit-gc.sh +117 -0
- package/scripts/lib/kit-interview-test.sh +77 -0
- package/scripts/lib/kit-interview.sh +203 -0
- package/scripts/lib/kit-local.sh +79 -0
- package/scripts/lib/kit-manifest.sh +127 -0
- package/scripts/lib/kit-mode-test.sh +49 -0
- package/scripts/lib/kit-mode.sh +67 -0
- package/scripts/lib/kit-operate.sh +105 -0
- package/scripts/lib/kit-profile-test.sh +62 -0
- package/scripts/lib/kit-profile.sh +115 -0
- package/scripts/lib/kit-task-ops-test.sh +63 -0
- package/scripts/lib/kit-task-ops.sh +341 -0
- package/scripts/lib/pr-evidence.sh +173 -0
- package/scripts/lib/project-scan.sh +16 -0
- package/scripts/lib/react-detect.sh +78 -0
- package/scripts/lib/role-identity.sh +47 -0
- package/scripts/lib/secret-guard.sh +96 -0
- package/scripts/lib/toon.sh +35 -0
- package/scripts/lib/ui.sh +42 -0
- package/scripts/lib/version-bump.sh +59 -0
- package/scripts/lib/worktree-issue-test.sh +45 -0
- package/scripts/lib/worktree-issue.sh +73 -0
- package/scripts/lib/worktree-start.sh +280 -0
- package/scripts/orchestrate.sh +160 -0
- package/scripts/portable-test.sh +53 -0
- package/scripts/publish.sh +94 -0
- package/scripts/setup-labels.sh +25 -0
- package/scripts/setup-milestones.sh +17 -0
- package/scripts/showcase.sh +64 -0
- package/scripts/status.sh +44 -0
- package/scripts/task-sync.sh +59 -0
- package/scripts/test.sh +48 -0
- package/scripts/web-install.sh +22 -0
- package/skills/kit-annotate/SKILL.md +107 -0
- package/skills/kit-autopilot/SKILL.md +108 -0
- package/skills/kit-contribute/SKILL.md +134 -0
- package/skills/kit-customize/SKILL.md +134 -0
- package/skills/kit-dev/SKILL.md +67 -0
- package/skills/kit-digest/SKILL.md +41 -0
- package/skills/kit-effort-close/SKILL.md +156 -0
- package/skills/kit-effort-new/SKILL.md +173 -0
- package/skills/kit-effort-pr/SKILL.md +139 -0
- package/skills/kit-effort-start/SKILL.md +85 -0
- package/skills/kit-gc/SKILL.md +80 -0
- package/skills/kit-onboard/SKILL.md +50 -0
- package/skills/kit-security-sweep/SKILL.md +57 -0
- package/skills/kit-ship/SKILL.md +43 -0
- package/skills/kit-task-close/SKILL.md +66 -0
- package/skills/kit-task-new/SKILL.md +51 -0
- package/skills/kit-task-pr/SKILL.md +43 -0
- package/skills/kit-task-pr-auto/SKILL.md +27 -0
- package/skills/kit-task-pr-merge/SKILL.md +53 -0
- package/skills/kit-task-start/SKILL.md +76 -0
- package/skills/kit-task-sync/SKILL.md +37 -0
- package/templates/CLAUDE.md.tmpl +106 -0
- package/templates/agents/analyst.md +55 -0
- package/templates/agents/auto-dev.md +93 -0
- package/templates/agents/backend.md +59 -0
- package/templates/agents/designer.md +73 -0
- package/templates/agents/devops.md +57 -0
- package/templates/agents/editor.md +48 -0
- package/templates/agents/frontend.md +81 -0
- package/templates/agents/generalist.md +46 -0
- package/templates/agents/local-delegate.md +70 -0
- package/templates/agents/n8n.md +65 -0
- package/templates/agents/pm.md +69 -0
- package/templates/agents/qa.md +66 -0
- package/templates/agents/researcher.md +57 -0
- package/templates/agents/security.md +65 -0
- package/templates/agents/tech-lead.md +75 -0
- package/templates/hooks/guard-base-branch-commit.sh.tmpl +45 -0
- package/templates/hooks/kit-local-status.sh.tmpl +34 -0
- package/templates/hooks/kit_version_check.sh.tmpl +6 -0
- package/templates/hooks/mempal_followup.sh.tmpl +97 -0
- package/templates/hooks/mempal_precompact.sh.tmpl +4 -0
- package/templates/hooks/mempal_save.sh.tmpl +4 -0
- package/templates/hooks/mempal_session_start.sh.tmpl +8 -0
- package/templates/hooks/prepush_gate.sh.tmpl +36 -0
- package/templates/hooks/repo-hygiene.sh.tmpl +72 -0
- package/templates/kit.config.json.tmpl +32 -0
- package/templates/knowledge-INDEX.md.tmpl +12 -0
- package/templates/lib/kit-sigil.sh.tmpl +124 -0
- package/templates/rules/branch-naming.md +104 -0
- package/templates/rules/communication-style.md +22 -0
- package/templates/rules/delegation-brief.md +40 -0
- package/templates/rules/design-routing.md +35 -0
- package/templates/rules/effort-model.md +122 -0
- package/templates/rules/knowledge-base.md +41 -0
- package/templates/rules/mempalace.md +110 -0
- package/templates/rules/plan-output-format.md +58 -0
- package/templates/rules/react-annotate.md +69 -0
- package/templates/rules/risk-tiered-review.md +62 -0
- package/templates/rules/skill-gaps.md +48 -0
- package/templates/rules/task-management.md +42 -0
- package/templates/settings/settings.local.json.tmpl +27 -0
- package/templates/skills/NAMESPACED +13 -0
- package/templates/skills/copywriting/SKILL.md +252 -0
- package/templates/skills/copywriting/references/copy-frameworks.md +344 -0
- package/templates/skills/copywriting/references/natural-transitions.md +272 -0
- package/templates/skills/feature-build-refine/SKILL.md +367 -0
- package/templates/skills/karpathy-guidelines/SKILL.md +69 -0
- package/templates/skills/morning-briefing/SKILL.md +46 -0
- package/templates/skills/speckit/SKILL.md +239 -0
- package/templates/skills/supabase-patterns/SKILL.md +88 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# orchestrate.sh - run N issue flows as live tmux panes, each an agent in its own worktree.
|
|
3
|
+
#
|
|
4
|
+
# For each issue: create an isolated worktree + branch off the configured base branch
|
|
5
|
+
# (cckit.config.json: github.repo + github.baseBranch), then open a tmux session with one tiled
|
|
6
|
+
# pane per flow running the agent command. Branches must be file-disjoint - the flows edit in
|
|
7
|
+
# parallel, so disjointness is the caller's responsibility.
|
|
8
|
+
#
|
|
9
|
+
# Agent-agnostic: the per-pane command defaults to `claude` but is overridable, so cckit drives any
|
|
10
|
+
# CLI agent that takes a prompt as its first argument.
|
|
11
|
+
# --agent <cmd> / CCKIT_AGENT=<cmd>
|
|
12
|
+
#
|
|
13
|
+
# Hardening:
|
|
14
|
+
# --dry-run resolve + print the launch plan; create no worktrees, start no panes
|
|
15
|
+
# --cap <N> concurrency cap: launch at most N flows (default 4); the rest are queued + reported
|
|
16
|
+
# blocked_by an issue whose native GitHub blocked_by edge points at an OPEN issue is skipped
|
|
17
|
+
# (override with --force)
|
|
18
|
+
# set -euo pipefail + explicit pipe handling throughout
|
|
19
|
+
#
|
|
20
|
+
# Usage:
|
|
21
|
+
# cckit orchestrate <issueA> <issueB> [<issueC> ...]
|
|
22
|
+
# cckit orchestrate --dry-run 6 7 8
|
|
23
|
+
# cckit orchestrate --cap 3 --agent codex 2 3 6 9
|
|
24
|
+
# cckit orchestrate --no-seed 6 7 # don't auto-prompt each agent
|
|
25
|
+
# cckit orchestrate --force 7 # launch even if blocked_by an open issue
|
|
26
|
+
# cckit orchestrate --session=sweep 1 2 # custom tmux session name
|
|
27
|
+
# cckit orchestrate --detach 6 7 # build the session, don't attach (testing/headless)
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
|
|
30
|
+
SESSION="orchestrate"
|
|
31
|
+
SEED=1
|
|
32
|
+
DRYRUN=0
|
|
33
|
+
FORCE=0
|
|
34
|
+
DETACH=0
|
|
35
|
+
CAP=4
|
|
36
|
+
AGENT="${CCKIT_AGENT:-claude}"
|
|
37
|
+
ISSUES=()
|
|
38
|
+
|
|
39
|
+
usage() { sed -n '2,28p' "$0" | sed 's/^# \{0,1\}//'; }
|
|
40
|
+
|
|
41
|
+
while [ "$#" -gt 0 ]; do
|
|
42
|
+
case "$1" in
|
|
43
|
+
--no-seed) SEED=0; shift ;;
|
|
44
|
+
--dry-run) DRYRUN=1; shift ;;
|
|
45
|
+
--force) FORCE=1; shift ;;
|
|
46
|
+
--detach) DETACH=1; shift ;;
|
|
47
|
+
--cap) CAP="$2"; shift 2 ;;
|
|
48
|
+
--cap=*) CAP="${1#*=}"; shift ;;
|
|
49
|
+
--agent) AGENT="$2"; shift 2 ;;
|
|
50
|
+
--agent=*) AGENT="${1#*=}"; shift ;;
|
|
51
|
+
--session=*) SESSION="${1#*=}"; shift ;;
|
|
52
|
+
-h|--help) usage; exit 0 ;;
|
|
53
|
+
[0-9]*) ISSUES+=("$1"); shift ;;
|
|
54
|
+
*) echo "orchestrate: unknown arg '$1'" >&2; exit 2 ;;
|
|
55
|
+
esac
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
[ "${#ISSUES[@]}" -ge 1 ] || { echo "orchestrate: pass at least one issue number" >&2; usage; exit 2; }
|
|
59
|
+
case "$CAP" in ''|*[!0-9]*) echo "orchestrate: --cap needs a number (got '$CAP')" >&2; exit 2 ;; esac
|
|
60
|
+
|
|
61
|
+
# Resolve the main worktree root + load config (repo + base branch drive everything).
|
|
62
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
63
|
+
ROOT="$(git -C "$SCRIPT_DIR" worktree list --porcelain | awk '/^worktree /{print $2; exit}')"
|
|
64
|
+
[ -n "$ROOT" ] || { echo "orchestrate: not in a git repo" >&2; exit 1; }
|
|
65
|
+
# shellcheck source=/dev/null
|
|
66
|
+
source "$ROOT/scripts/lib/kit-config.sh" && load_kit_config
|
|
67
|
+
REPO="$KIT_REPO"
|
|
68
|
+
|
|
69
|
+
# blocked_by gate: echo the OPEN blocker numbers of an issue (native GitHub dependency edge).
|
|
70
|
+
open_blockers() {
|
|
71
|
+
local n="$1" b st blk
|
|
72
|
+
blk="$(gh api "repos/$REPO/issues/$n/dependencies/blocked_by" --jq '.[].number' 2>/dev/null || true)"
|
|
73
|
+
for b in $blk; do
|
|
74
|
+
st="$(gh issue view "$b" --repo "$REPO" --json state --jq .state 2>/dev/null || echo OPEN)"
|
|
75
|
+
[ "$st" = "OPEN" ] && printf '%s ' "$b"
|
|
76
|
+
done
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Partition the requested issues into eligible / blocked / (later) queued.
|
|
80
|
+
ELIGIBLE=()
|
|
81
|
+
echo "orchestrate: repo $REPO (base ${KIT_BASE_BRANCH:-main}), cap $CAP, agent '$AGENT'"
|
|
82
|
+
for num in "${ISSUES[@]}"; do
|
|
83
|
+
blockers="$(open_blockers "$num")"
|
|
84
|
+
if [ -n "$blockers" ] && [ "$FORCE" -eq 0 ]; then
|
|
85
|
+
echo " #$num SKIP - blocked_by open: ${blockers% }"
|
|
86
|
+
else
|
|
87
|
+
[ -n "$blockers" ] && echo " #$num FORCED past open blockers: ${blockers% }"
|
|
88
|
+
ELIGIBLE+=("$num")
|
|
89
|
+
fi
|
|
90
|
+
done
|
|
91
|
+
[ "${#ELIGIBLE[@]}" -ge 1 ] || { echo "orchestrate: nothing eligible to launch" >&2; exit 1; }
|
|
92
|
+
|
|
93
|
+
# Concurrency cap: launch the first CAP eligible flows; queue + report the rest.
|
|
94
|
+
LAUNCH=()
|
|
95
|
+
QUEUE=()
|
|
96
|
+
i=0
|
|
97
|
+
for num in "${ELIGIBLE[@]}"; do
|
|
98
|
+
if [ "$i" -lt "$CAP" ]; then LAUNCH+=("$num"); else QUEUE+=("$num"); fi
|
|
99
|
+
i=$((i + 1))
|
|
100
|
+
done
|
|
101
|
+
[ "${#QUEUE[@]}" -eq 0 ] || echo " queued past cap (run a later wave): ${QUEUE[*]}"
|
|
102
|
+
|
|
103
|
+
if [ "$DRYRUN" -eq 1 ]; then
|
|
104
|
+
echo "orchestrate: DRY RUN - would launch ${#LAUNCH[@]} flow(s): ${LAUNCH[*]}"
|
|
105
|
+
echo " (no worktrees created, no panes started)"
|
|
106
|
+
exit 0
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
command -v tmux >/dev/null || { echo "orchestrate: tmux not installed (brew install tmux)" >&2; exit 1; }
|
|
110
|
+
command -v "$AGENT" >/dev/null || { echo "orchestrate: agent '$AGENT' not on PATH" >&2; exit 1; }
|
|
111
|
+
|
|
112
|
+
# shellcheck source=/dev/null
|
|
113
|
+
source "$ROOT/scripts/lib/worktree-start.sh"
|
|
114
|
+
|
|
115
|
+
# Headless seed: the agent runs with NO human present - it must never ask, decide autonomously,
|
|
116
|
+
# gauge difficulty + apply proportional effort, and close no-op issues itself. cckit verbs only.
|
|
117
|
+
seed_for() {
|
|
118
|
+
local num="$1" branch="$2"
|
|
119
|
+
printf '%s' "You are running HEADLESS inside a cckit orchestration: there is NO human to answer \
|
|
120
|
+
questions, so NEVER ask - decide autonomously and proceed. Gauge the task difficulty and apply \
|
|
121
|
+
proportional effort. Worktree + branch $branch for issue #$num are ready. Run: gh issue view $num, \
|
|
122
|
+
implement it, run bash scripts/check.sh until green, then open the PR with: cckit pr $num \"<summary>\". \
|
|
123
|
+
If it is a no-op (nothing to change), comment why and run: cckit close $num \"<reason>\". Do not wait for input."
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
ENTRIES=()
|
|
127
|
+
for num in "${LAUNCH[@]}"; do
|
|
128
|
+
entry="$(wt_start "$num")" || { echo "orchestrate: wt_start #$num failed" >&2; exit 1; }
|
|
129
|
+
ENTRIES+=("$entry")
|
|
130
|
+
done
|
|
131
|
+
|
|
132
|
+
tmux kill-session -t "$SESSION" 2>/dev/null || true
|
|
133
|
+
first=1
|
|
134
|
+
for entry in "${ENTRIES[@]}"; do
|
|
135
|
+
wt="${entry%%|*}"; rest="${entry#*|}"; branch="${rest%%|*}"; num="${rest##*|}"
|
|
136
|
+
if [ "$first" -eq 1 ]; then
|
|
137
|
+
tmux new-session -d -s "$SESSION" -n flows -c "$wt"
|
|
138
|
+
pane="$(tmux display -p -t "$SESSION:flows" '#{pane_id}')"
|
|
139
|
+
first=0
|
|
140
|
+
else
|
|
141
|
+
pane="$(tmux split-window -t "$SESSION:flows" -c "$wt" -P -F '#{pane_id}')"
|
|
142
|
+
tmux select-layout -t "$SESSION:flows" tiled >/dev/null
|
|
143
|
+
fi
|
|
144
|
+
if [ "$SEED" -eq 1 ]; then
|
|
145
|
+
tmux send-keys -t "$pane" "$AGENT \"$(seed_for "$num" "$branch")\"" C-m
|
|
146
|
+
else
|
|
147
|
+
tmux send-keys -t "$pane" "$AGENT" C-m
|
|
148
|
+
fi
|
|
149
|
+
done
|
|
150
|
+
tmux select-layout -t "$SESSION:flows" tiled >/dev/null
|
|
151
|
+
tmux set -t "$SESSION" mouse on
|
|
152
|
+
tmux select-window -t "$SESSION:flows" 2>/dev/null || true
|
|
153
|
+
|
|
154
|
+
hint="tabs/panes: click to focus, or Ctrl-b <number> - Ctrl-b d detaches"
|
|
155
|
+
if [ "$DETACH" -eq 1 ]; then
|
|
156
|
+
echo "orchestrate: session '$SESSION' built ($hint). Attach: tmux attach -t $SESSION"
|
|
157
|
+
else
|
|
158
|
+
echo "orchestrate: attaching to '$SESSION' - $hint"
|
|
159
|
+
tmux attach -t "$SESSION"
|
|
160
|
+
fi
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# portable-test.sh — cckit must operate on the project it is INVOKED in, not its install dir (#41).
|
|
3
|
+
# Regression test for the bug where `cckit sync` in another repo read cckit's own config and so
|
|
4
|
+
# reported an empty board `[]`. Hermetic: stubs `gh` (no network/auth). Run: bash scripts/portable-test.sh
|
|
5
|
+
set -u
|
|
6
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
7
|
+
CCKIT="$ROOT/bin/cckit"
|
|
8
|
+
fail=0
|
|
9
|
+
t() { if [ "$2" = "$3" ]; then echo "ok: $1"; else echo "FAIL: $1 -> got '[$2]' want '[$3]'"; fail=1; fi; }
|
|
10
|
+
|
|
11
|
+
command -v jq >/dev/null 2>&1 || { echo "portable-test: jq required" >&2; exit 1; }
|
|
12
|
+
|
|
13
|
+
# Stub gh on PATH: record the --repo it is asked for (to stderr), return an empty JSON array.
|
|
14
|
+
stub="$(mktemp -d)"
|
|
15
|
+
cat > "$stub/gh" <<'SH'
|
|
16
|
+
#!/usr/bin/env bash
|
|
17
|
+
prev=""
|
|
18
|
+
for a in "$@"; do
|
|
19
|
+
[ "$prev" = "--repo" ] && printf 'REPO=%s\n' "$a" >&2
|
|
20
|
+
prev="$a"
|
|
21
|
+
done
|
|
22
|
+
echo "[]"
|
|
23
|
+
SH
|
|
24
|
+
chmod +x "$stub/gh"
|
|
25
|
+
|
|
26
|
+
# A foreign project with its own config pointing at a DISTINCT repo.
|
|
27
|
+
fix="$(mktemp -d)"
|
|
28
|
+
cat > "$fix/cckit.config.json" <<'JSON'
|
|
29
|
+
{ "kitVersion": "9.9.9", "project": {"name":"fixture"}, "github": {"repo":"octo/fixture-repo","owner":"octo","baseBranch":"main"} }
|
|
30
|
+
JSON
|
|
31
|
+
( cd "$fix" && git init -q ) 2>/dev/null
|
|
32
|
+
|
|
33
|
+
# 1) From the fixture, sync resolves the fixture's config → targets the fixture's repo.
|
|
34
|
+
got_repo="$(cd "$fix" && PATH="$stub:$PATH" "$CCKIT" sync --llm 2>&1 >/dev/null | sed -n 's/^REPO=//p' | tail -1)"
|
|
35
|
+
t "sync targets the invoking project's repo" "$got_repo" "octo/fixture-repo"
|
|
36
|
+
|
|
37
|
+
# 2) From the fixture, scan reports the fixture as root (no cd to the install dir).
|
|
38
|
+
got_root="$(cd "$fix" && "$CCKIT" scan --llm 2>/dev/null | jq -r .root)"
|
|
39
|
+
t "scan root is the invoking dir" "$got_root" "$(cd "$fix" && pwd -P)"
|
|
40
|
+
|
|
41
|
+
# 3) Self-hosting: from the cckit repo, sync still targets cckit's own repo.
|
|
42
|
+
own_repo="$(jq -r '.github.repo' "$ROOT/cckit.config.json")"
|
|
43
|
+
got_own="$(cd "$ROOT" && PATH="$stub:$PATH" "$CCKIT" sync --llm 2>&1 >/dev/null | sed -n 's/^REPO=//p' | tail -1)"
|
|
44
|
+
t "self-host sync targets cckit's repo" "$got_own" "$own_repo"
|
|
45
|
+
|
|
46
|
+
# 4) `version` is the INSTALLED version regardless of where it is invoked.
|
|
47
|
+
inst_ver="$(jq -r '.kitVersion' "$ROOT/cckit.config.json")"
|
|
48
|
+
got_ver="$(cd "$fix" && "$CCKIT" version | awk '{print $2}')"
|
|
49
|
+
t "version is the install version, not the project's" "$got_ver" "$inst_ver"
|
|
50
|
+
|
|
51
|
+
rm -rf "$stub" "$fix"
|
|
52
|
+
[ "$fail" -eq 0 ] && echo "ALL OK (portable)" || echo "portable: FAILURES"
|
|
53
|
+
exit "$fail"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# publish.sh - cut a cckit release and (optionally) publish to the Homebrew tap + npm.
|
|
3
|
+
#
|
|
4
|
+
# SAFE BY DEFAULT: a dry run that changes nothing and prints every step. It only acts when you
|
|
5
|
+
# pass --publish. Built so you can hand it a version later and ship in one command.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# scripts/publish.sh 0.1.0 # DRY RUN - show the plan, change nothing
|
|
9
|
+
# scripts/publish.sh 0.1.0 --publish # execute: tag + GitHub release + formula + npm
|
|
10
|
+
# scripts/publish.sh 0.1.0 --publish --no-npm # skip npm (brew only)
|
|
11
|
+
# scripts/publish.sh 0.1.0 --publish --no-brew # skip brew (npm only)
|
|
12
|
+
# Env: TAP_DIR=~/Dev/jeiemgi/homebrew-cckit (where the tap repo is cloned)
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
VERSION="${1:-}"; shift || true
|
|
16
|
+
PUBLISH=0; DO_NPM=1; DO_BREW=1
|
|
17
|
+
for a in "$@"; do case "$a" in
|
|
18
|
+
--publish) PUBLISH=1 ;; --no-npm) DO_NPM=0 ;; --no-brew) DO_BREW=0 ;;
|
|
19
|
+
*) echo "unknown flag: $a" >&2; exit 2 ;;
|
|
20
|
+
esac; done
|
|
21
|
+
|
|
22
|
+
[ -n "$VERSION" ] || { echo "usage: scripts/publish.sh <version> [--publish] [--no-npm] [--no-brew]" >&2; exit 2; }
|
|
23
|
+
echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || { echo "version must be X.Y.Z" >&2; exit 2; }
|
|
24
|
+
|
|
25
|
+
root="$(cd "$(dirname "$0")/.." && pwd)"
|
|
26
|
+
repo="jeiemgi/cckit"
|
|
27
|
+
tag="v$VERSION"
|
|
28
|
+
tarball="https://github.com/$repo/archive/refs/tags/$tag.tar.gz"
|
|
29
|
+
tap_dir="${TAP_DIR:-$HOME/Dev/jeiemgi/homebrew-cckit}"
|
|
30
|
+
|
|
31
|
+
run() { if [ "$PUBLISH" = 1 ]; then echo "+ $*"; "$@"; else echo "DRY: $*"; fi; }
|
|
32
|
+
|
|
33
|
+
mode="DRY-RUN"; [ "$PUBLISH" = 1 ] && mode="PUBLISH"
|
|
34
|
+
echo "== cckit publish $tag (mode: $mode) =="
|
|
35
|
+
|
|
36
|
+
if [ "$PUBLISH" = 1 ]; then
|
|
37
|
+
[ -z "$(git -C "$root" status --porcelain)" ] || { echo "x working tree not clean" >&2; exit 1; }
|
|
38
|
+
[ "$(git -C "$root" branch --show-current)" = "main" ] || { echo "x not on main" >&2; exit 1; }
|
|
39
|
+
! git -C "$root" rev-parse "$tag" >/dev/null 2>&1 || { echo "x tag $tag already exists" >&2; exit 1; }
|
|
40
|
+
command -v gh >/dev/null || { echo "x gh required" >&2; exit 1; }
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# 1. Stamp version, tag, and cut the GitHub release (creates the tarball).
|
|
44
|
+
run bash -c "cd '$root' && command -v jq >/dev/null && jq '.kitVersion=\"$VERSION\"' cckit.config.json > .v.tmp && mv .v.tmp cckit.config.json || true"
|
|
45
|
+
run git -C "$root" add -A
|
|
46
|
+
run git -C "$root" commit -m "release: $tag"
|
|
47
|
+
run git -C "$root" tag "$tag"
|
|
48
|
+
run git -C "$root" push origin main --tags
|
|
49
|
+
run gh release create "$tag" --repo "$repo" --title "$tag" --generate-notes
|
|
50
|
+
|
|
51
|
+
# 2. Compute the tarball sha256 for the stable Homebrew formula.
|
|
52
|
+
if [ "$PUBLISH" = 1 ]; then
|
|
53
|
+
echo "+ fetching sha256 of $tarball"
|
|
54
|
+
SHA="$(curl -fsSL "$tarball" | shasum -a 256 | cut -d' ' -f1)"
|
|
55
|
+
echo " sha256=$SHA"
|
|
56
|
+
else
|
|
57
|
+
SHA="<computed-at-publish>"; echo "DRY: curl -fsSL $tarball | shasum -a 256"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# 3. Inject the stable url+sha256 block into the formula (idempotent).
|
|
61
|
+
formula="$root/Formula/cckit.rb"
|
|
62
|
+
if [ "$PUBLISH" = 1 ]; then
|
|
63
|
+
if grep -q 'url "https' "$formula"; then
|
|
64
|
+
perl -0pi -e "s{url \"https[^\"]*\"}{url \"$tarball\"}; s{sha256 \"[^\"]*\"}{sha256 \"$SHA\"}; s{version \"[^\"]*\"}{version \"$VERSION\"}" "$formula"
|
|
65
|
+
else
|
|
66
|
+
perl -0pi -e "s{( homepage[^\n]*\n)}{\$1 url \"$tarball\"\n sha256 \"$SHA\"\n version \"$VERSION\"\n}" "$formula"
|
|
67
|
+
fi
|
|
68
|
+
echo "+ updated $formula"
|
|
69
|
+
else
|
|
70
|
+
echo "DRY: inject url=$tarball sha256=$SHA version=$VERSION into Formula/cckit.rb"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# 4. Homebrew tap: copy the formula into the tap repo and push.
|
|
74
|
+
if [ "$DO_BREW" = 1 ]; then
|
|
75
|
+
if [ -d "$tap_dir/.git" ]; then
|
|
76
|
+
run mkdir -p "$tap_dir/Formula"
|
|
77
|
+
run cp "$formula" "$tap_dir/Formula/cckit.rb"
|
|
78
|
+
run git -C "$tap_dir" add Formula/cckit.rb
|
|
79
|
+
run git -C "$tap_dir" commit -m "cckit $VERSION"
|
|
80
|
+
run git -C "$tap_dir" push
|
|
81
|
+
echo " users: brew tap jeiemgi/cckit && brew install cckit"
|
|
82
|
+
else
|
|
83
|
+
echo " ! tap repo not at $tap_dir - create it: gh repo create jeiemgi/homebrew-cckit --public"
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# 5. npm publish (scoped, public).
|
|
88
|
+
if [ "$DO_NPM" = 1 ]; then
|
|
89
|
+
run npm publish --access public
|
|
90
|
+
echo " users: npm i -g @jeiemgi/cckit"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
done_msg="dry run - nothing changed"; [ "$PUBLISH" = 1 ] && done_msg="published"
|
|
94
|
+
echo "== done ($done_msg) =="
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Create the kind:/priority:/role: label families on the repo. Idempotent.
|
|
3
|
+
# Reads repo + roles from .claude/kit.config.json. Run from project root.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
source "$(dirname "$0")/lib/kit-config.sh" && load_kit_config
|
|
6
|
+
|
|
7
|
+
ensure_label() { # name color description
|
|
8
|
+
gh label create "$1" --repo "$KIT_REPO" --color "$2" --description "$3" --force >/dev/null 2>&1 \
|
|
9
|
+
&& echo " ✓ $1" || echo " • $1 (exists)"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
echo "→ Labels on $KIT_REPO"
|
|
13
|
+
echo "kinds:"
|
|
14
|
+
for k in task plan adr scaffold spike; do ensure_label "kind:$k" "5319e7" "Kind: $k"; done
|
|
15
|
+
echo "priorities:"
|
|
16
|
+
ensure_label "priority:p1" "b60205" "Now / blocking"
|
|
17
|
+
ensure_label "priority:p2" "fbca04" "Next"
|
|
18
|
+
ensure_label "priority:p3" "0e8a16" "Later"
|
|
19
|
+
echo "roles:"
|
|
20
|
+
while IFS= read -r role; do
|
|
21
|
+
[[ -z "$role" ]] && continue
|
|
22
|
+
slug=$(echo "$role" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
|
|
23
|
+
ensure_label "role:$slug" "1d76db" "Role: $role"
|
|
24
|
+
done <<< "$KIT_ROLES"
|
|
25
|
+
echo "✓ done"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Create the project's milestones on the repo. Idempotent.
|
|
3
|
+
# Reads repo + milestones from .claude/kit.config.json. Run from project root.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
source "$(dirname "$0")/lib/kit-config.sh" && load_kit_config
|
|
6
|
+
|
|
7
|
+
echo "→ Milestones on $KIT_REPO"
|
|
8
|
+
existing=$(gh api "repos/$KIT_REPO/milestones?state=all" --jq '.[].title' 2>/dev/null || echo "")
|
|
9
|
+
while IFS= read -r m; do
|
|
10
|
+
[[ -z "$m" ]] && continue
|
|
11
|
+
if echo "$existing" | grep -qxF "$m"; then
|
|
12
|
+
echo " • $m (exists)"
|
|
13
|
+
else
|
|
14
|
+
gh api "repos/$KIT_REPO/milestones" -f title="$m" >/dev/null && echo " ✓ $m"
|
|
15
|
+
fi
|
|
16
|
+
done <<< "$KIT_MILESTONES"
|
|
17
|
+
echo "✓ done"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# showcase.sh — render the showcase dataset to screenshots + a docs gallery.
|
|
3
|
+
#
|
|
4
|
+
# Reads docs-site/showcase/showcase.json (the dataset), runs each (read-only / dry-run) cckit
|
|
5
|
+
# command through charmbracelet/freeze with forced color, writes one PNG per command to
|
|
6
|
+
# docs-site/public/showcase/, and regenerates the gallery page docs-site/src/content/docs/showcase.md.
|
|
7
|
+
# The PNGs + page are committed artifacts, so the site builds without freeze; only regenerating
|
|
8
|
+
# needs `freeze` (brew install charmbracelet/tap/freeze) and `jq`.
|
|
9
|
+
#
|
|
10
|
+
# scripts/showcase.sh
|
|
11
|
+
set -uo pipefail
|
|
12
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
13
|
+
MANIFEST="$ROOT/docs-site/showcase/showcase.json"
|
|
14
|
+
IMG_DIR="$ROOT/docs-site/public/showcase"
|
|
15
|
+
GALLERY="$ROOT/docs-site/src/content/docs/showcase.md"
|
|
16
|
+
|
|
17
|
+
command -v jq >/dev/null || { echo "showcase: jq required" >&2; exit 1; }
|
|
18
|
+
command -v freeze >/dev/null || { echo "showcase: freeze required (brew install charmbracelet/tap/freeze)" >&2; exit 1; }
|
|
19
|
+
[ -f "$MANIFEST" ] || { echo "showcase: $MANIFEST not found" >&2; exit 1; }
|
|
20
|
+
|
|
21
|
+
# Use THIS checkout's cckit (so the captured behavior matches the branch), and force color so the
|
|
22
|
+
# screenshots show cckit's TTY-gated palette even though freeze captures without a real terminal.
|
|
23
|
+
# CCKIT_FORCE_COLOR is cckit-private (see ui.sh) so it never leaks into the gh/jq subprocesses.
|
|
24
|
+
export PATH="$ROOT/bin:$PATH"
|
|
25
|
+
export CCKIT_FORCE_COLOR=1
|
|
26
|
+
mkdir -p "$IMG_DIR"
|
|
27
|
+
|
|
28
|
+
# Gallery header.
|
|
29
|
+
{
|
|
30
|
+
echo '---'
|
|
31
|
+
echo 'title: Showcase'
|
|
32
|
+
echo 'description: Every cckit capability at a glance — real command output captured as screenshots.'
|
|
33
|
+
echo '---'
|
|
34
|
+
echo
|
|
35
|
+
echo 'A live tour of cckit, generated from a dataset (`docs-site/showcase/showcase.json`) by'
|
|
36
|
+
echo '`scripts/showcase.sh`: each command is run for real and its output captured as a screenshot.'
|
|
37
|
+
echo 'Every command here is read-only or a dry-run — safe to run yourself.'
|
|
38
|
+
echo
|
|
39
|
+
} > "$GALLERY"
|
|
40
|
+
|
|
41
|
+
prev_group=""
|
|
42
|
+
fail=0
|
|
43
|
+
# Stream the dataset as tab-separated rows (no tabs/newlines inside fields).
|
|
44
|
+
while IFS=$'\t' read -r name group title cmd show; do
|
|
45
|
+
[ -n "$name" ] || continue
|
|
46
|
+
out="$IMG_DIR/$name.png"
|
|
47
|
+
echo "==> $name : $cmd" >&2
|
|
48
|
+
# </dev/null so freeze never consumes this loop's stdin (the jq dataset stream).
|
|
49
|
+
if ! freeze --window --padding 20 --execute "$cmd" --output "$out" </dev/null >/dev/null 2>&1; then
|
|
50
|
+
echo " FAILED to render $name" >&2; fail=1; continue
|
|
51
|
+
fi
|
|
52
|
+
if [ "$group" != "$prev_group" ]; then
|
|
53
|
+
printf '## %s\n\n' "$group" >> "$GALLERY"
|
|
54
|
+
prev_group="$group"
|
|
55
|
+
fi
|
|
56
|
+
{
|
|
57
|
+
printf '### %s\n\n' "$title"
|
|
58
|
+
printf '```bash\n%s\n```\n\n' "$show"
|
|
59
|
+
printf '\n\n' "$title" "$name"
|
|
60
|
+
} >> "$GALLERY"
|
|
61
|
+
done < <(jq -r '.commands[] | [.name, .group, .title, .cmd, (.show // .cmd)] | @tsv' "$MANIFEST")
|
|
62
|
+
|
|
63
|
+
echo "==> wrote $GALLERY and PNGs to $IMG_DIR" >&2
|
|
64
|
+
exit "$fail"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# status.sh - a thin, pure-bash status viewer: the board, active worktrees/branches, and the resume
|
|
3
|
+
# handoff on one screen, using native terminal tools (ui.sh: color + TTY gating, glow/fzf when
|
|
4
|
+
# present). This is the lightweight cockpit; the rich opentui TUI is an OPTIONAL separate adapter -
|
|
5
|
+
# the core stays pure bash and dependency-light, so `cckit status` works in any terminal.
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
8
|
+
# shellcheck source=/dev/null
|
|
9
|
+
source "$ROOT/scripts/lib/ui.sh" 2>/dev/null || true
|
|
10
|
+
# shellcheck source=/dev/null
|
|
11
|
+
source "$ROOT/scripts/lib/kit-config.sh" && load_kit_config
|
|
12
|
+
|
|
13
|
+
hr() { printf '%s\n' "----------------------------------------------------------------"; }
|
|
14
|
+
sec() { if command -v ui_paint >/dev/null 2>&1; then ui_paint '1;36' "$1"; else printf '%s' "$1"; fi; echo; }
|
|
15
|
+
|
|
16
|
+
sec "cckit status - $KIT_REPO (base ${KIT_BASE_BRANCH:-main})"
|
|
17
|
+
hr
|
|
18
|
+
|
|
19
|
+
# Board: open issues, blocked count (reuse the read-only board JSON).
|
|
20
|
+
sec "Board"
|
|
21
|
+
board="$(bash "$ROOT/scripts/task-sync.sh" --llm 2>/dev/null || echo '[]')"
|
|
22
|
+
if command -v jq >/dev/null 2>&1; then
|
|
23
|
+
open="$(printf '%s' "$board" | jq 'length' 2>/dev/null || echo 0)"
|
|
24
|
+
blocked="$(printf '%s' "$board" | jq '[.[]|select(.blocked)]|length' 2>/dev/null || echo 0)"
|
|
25
|
+
echo " open issues: $open blocked: $blocked"
|
|
26
|
+
printf '%s' "$board" | jq -r '.[:8][] | " #\(.number) \(.title[0:56])"' 2>/dev/null || true
|
|
27
|
+
[ "$open" -gt 8 ] && echo " ... and $((open - 8)) more (cckit sync)"
|
|
28
|
+
else
|
|
29
|
+
echo " (jq absent - run cckit sync)"
|
|
30
|
+
fi
|
|
31
|
+
hr
|
|
32
|
+
|
|
33
|
+
# Worktrees + branches: SAFE-to-prune vs active (reuse the gc analysis).
|
|
34
|
+
sec "Worktrees and branches"
|
|
35
|
+
# shellcheck source=/dev/null
|
|
36
|
+
source "$ROOT/scripts/lib/kit-gc.sh"
|
|
37
|
+
kit_gc_analyze 2>/dev/null | grep -E 'SAFE|ACTIVE|PROTECTED|ORPHAN' | head -12 | sed 's/^/ /' || echo " (clean)"
|
|
38
|
+
hr
|
|
39
|
+
|
|
40
|
+
# Resume handoff, if one is saved.
|
|
41
|
+
sec "Resume handoff"
|
|
42
|
+
# shellcheck source=/dev/null
|
|
43
|
+
source "$ROOT/scripts/lib/handoff.sh"
|
|
44
|
+
handoff_read | sed 's/^/ /'
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Read-only board status, grouped by role. Pure gh + jq. bash 3.2 compatible.
|
|
3
|
+
# Reads repo from .claude/kit.config.json. Run from project root.
|
|
4
|
+
# ./scripts/task-sync.sh [--role <Name>] [--milestone <label>]
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
source "$(dirname "$0")/lib/kit-config.sh" && load_kit_config
|
|
7
|
+
|
|
8
|
+
ROLE_FILTER=""; MS_FILTER=""
|
|
9
|
+
while [[ $# -gt 0 ]]; do
|
|
10
|
+
case "$1" in
|
|
11
|
+
--role) ROLE_FILTER="$2"; shift 2 ;;
|
|
12
|
+
--milestone) MS_FILTER="$2"; shift 2 ;;
|
|
13
|
+
--llm|--output=json) CCKIT_OUTPUT=json; shift ;;
|
|
14
|
+
*) echo "unknown flag: $1" >&2; exit 1 ;;
|
|
15
|
+
esac
|
|
16
|
+
done
|
|
17
|
+
|
|
18
|
+
ISSUES=$(gh issue list --repo "$KIT_REPO" --state open --limit 200 \
|
|
19
|
+
--json number,title,labels,milestone,assignees,updatedAt,body)
|
|
20
|
+
|
|
21
|
+
# Structured output for agents: emit the open board as a JSON array and stop (--llm / CCKIT_OUTPUT).
|
|
22
|
+
if [ "${CCKIT_OUTPUT:-human}" = "json" ]; then
|
|
23
|
+
echo "$ISSUES" | jq -c '[.[] | {number, title, labels: [.labels[].name], milestone: (.milestone.title // null), blocked: ((.body // "") | test("Blocked by"))}]'
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
echo "## Board — $KIT_REPO"
|
|
28
|
+
echo ""
|
|
29
|
+
|
|
30
|
+
# Roles come from config; iterate in declared order, plus an "unlabeled" bucket.
|
|
31
|
+
print_role() {
|
|
32
|
+
local role="$1" slug rows
|
|
33
|
+
slug=$(echo "$role" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
|
|
34
|
+
rows=$(echo "$ISSUES" | jq -r --arg slug "role:$slug" --arg ms "$MS_FILTER" '
|
|
35
|
+
.[] | select(any(.labels[]; .name == $slug))
|
|
36
|
+
| select($ms == "" or (.milestone.title // "") == $ms)
|
|
37
|
+
| { n:.number, t:.title,
|
|
38
|
+
p:([.labels[].name | select(startswith("priority:"))][0] // "—" | sub("priority:";"")),
|
|
39
|
+
m:(.milestone.title // "—"),
|
|
40
|
+
blocked:(.body // "" | test("Blocked by")) }
|
|
41
|
+
| "| #\(.n) | \(.t) | \(.p) | \(.m) | \(if .blocked then "! blocked" else "—" end) |"')
|
|
42
|
+
[[ -z "$rows" ]] && return
|
|
43
|
+
echo "### $role"
|
|
44
|
+
echo "| # | Title | Pri | Milestone | Flag |"
|
|
45
|
+
echo "| - | ----- | --- | --------- | ---- |"
|
|
46
|
+
echo "$rows"
|
|
47
|
+
echo ""
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if [[ -n "$ROLE_FILTER" ]]; then
|
|
51
|
+
print_role "$ROLE_FILTER"
|
|
52
|
+
else
|
|
53
|
+
while IFS= read -r role; do [[ -n "$role" ]] && print_role "$role"; done <<< "$KIT_ROLES"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
BLOCKED=$(echo "$ISSUES" | jq -r '[.[] | select((.body // "") | test("Blocked by"))] | length')
|
|
57
|
+
echo "**Blocked:** ${BLOCKED} item(s)"
|
|
58
|
+
STALE=$(echo "$ISSUES" | jq -r '[.[] | .updatedAt] | length')
|
|
59
|
+
echo "**Open total:** ${STALE}"
|
package/scripts/test.sh
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test.sh — cckit behavioral test runner. Discovers every `*-test.sh` under bin/ and scripts/ and
|
|
3
|
+
# runs each as a self-contained test (a test exits non-zero on failure, prints its own ok/FAIL
|
|
4
|
+
# lines). Exits non-zero if any test fails. bash 3.2 compatible (no mapfile/associative arrays).
|
|
5
|
+
#
|
|
6
|
+
# scripts/test.sh run every discovered test
|
|
7
|
+
# scripts/test.sh <pattern> run only tests whose path matches <pattern> (grep -E)
|
|
8
|
+
#
|
|
9
|
+
# Folded into scripts/check.sh and run in CI (.github/workflows/test.yml).
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
cd "$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
+
|
|
13
|
+
PATTERN="${1:-}"
|
|
14
|
+
note() { printf '%s\n' "$*" >&2; }
|
|
15
|
+
|
|
16
|
+
# Discover tests deterministically. Newline-separated, sorted, optionally filtered.
|
|
17
|
+
tests=()
|
|
18
|
+
while IFS= read -r f; do
|
|
19
|
+
[ -n "$f" ] || continue
|
|
20
|
+
[ -z "$PATTERN" ] || printf '%s\n' "$f" | grep -qE "$PATTERN" || continue
|
|
21
|
+
tests+=("$f")
|
|
22
|
+
done < <(find bin scripts -type f -name '*-test.sh' 2>/dev/null | sort)
|
|
23
|
+
|
|
24
|
+
if [ "${#tests[@]}" -eq 0 ]; then
|
|
25
|
+
note "test: no *-test.sh found${PATTERN:+ matching '$PATTERN'}"
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
note "==> cckit tests — ${#tests[@]} file(s)${PATTERN:+ (filter: $PATTERN)}"
|
|
30
|
+
pass=0; failn=0; failed=""
|
|
31
|
+
for t in "${tests[@]}"; do
|
|
32
|
+
out="$(bash "$t" 2>&1)"; rc=$?
|
|
33
|
+
if [ "$rc" -eq 0 ]; then
|
|
34
|
+
pass=$((pass+1)); note " ok $t"
|
|
35
|
+
else
|
|
36
|
+
failn=$((failn+1)); failed="$failed$t"$'\n'
|
|
37
|
+
note " FAIL $t (rc=$rc)"
|
|
38
|
+
printf '%s\n' "$out" | sed 's/^/ /' >&2
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
note ""
|
|
43
|
+
note "==> $pass passed, $failn failed"
|
|
44
|
+
if [ "$failn" -ne 0 ]; then
|
|
45
|
+
note "failed:"; printf '%s' "$failed" | sed 's/^/ - /' >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
note "PASS all tests"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# cckit remote installer — curl -fsSL <url> | bash
|
|
3
|
+
# Clones (or updates) cckit into ~/.cckit and links `cckit` onto your PATH. No build, no npm/brew.
|
|
4
|
+
# Works on macOS, Linux, and Windows via WSL / Git Bash. Honors $CCKIT_HOME.
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
repo="https://github.com/jeiemgi/cckit.git"
|
|
8
|
+
dest="${CCKIT_HOME:-$HOME/.cckit}"
|
|
9
|
+
|
|
10
|
+
command -v git >/dev/null 2>&1 || { echo "cckit: git is required." >&2; exit 1; }
|
|
11
|
+
|
|
12
|
+
if [ -d "$dest/.git" ]; then
|
|
13
|
+
echo "cckit: updating $dest"
|
|
14
|
+
git -C "$dest" pull --ff-only --quiet || echo "cckit: could not fast-forward; using existing checkout" >&2
|
|
15
|
+
else
|
|
16
|
+
echo "cckit: cloning into $dest"
|
|
17
|
+
git clone --depth 1 --quiet "$repo" "$dest"
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Delegate PATH linking to the in-repo installer (single source of truth).
|
|
21
|
+
bash "$dest/scripts/install.sh"
|
|
22
|
+
echo "cckit: done. Run 'cckit help' to get started."
|