@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.
Files changed (191) hide show
  1. package/.claude-plugin/plugin.json +22 -0
  2. package/AGENTS.md +101 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/LICENSE-MIT +21 -0
  5. package/README.md +143 -0
  6. package/SECURITY.md +22 -0
  7. package/bin/cckit +215 -0
  8. package/cckit.config.json +34 -0
  9. package/commands/kit-add.md +42 -0
  10. package/commands/kit-docs.md +45 -0
  11. package/commands/kit-doctor.md +52 -0
  12. package/commands/kit-export-project.md +58 -0
  13. package/commands/kit-export-training.md +49 -0
  14. package/commands/kit-init.md +126 -0
  15. package/commands/kit-routines.md +59 -0
  16. package/commands/kit-update.md +132 -0
  17. package/docs/kit-annotate/01-explainer.html +225 -0
  18. package/docs/kit-annotate/02-implementation-plan.html +196 -0
  19. package/docs/media/.onboarding-capture.cast +5 -0
  20. package/docs/media/README.md +43 -0
  21. package/docs/media/build-demo.sh +63 -0
  22. package/docs/media/build-kit-init.sh +51 -0
  23. package/docs/media/build-onboarding.sh +51 -0
  24. package/docs/media/kit-dry-run.cast +107 -0
  25. package/docs/media/kit-dry-run.gif +0 -0
  26. package/docs/media/kit-init.cast +56 -0
  27. package/docs/media/kit-init.gif +0 -0
  28. package/docs/media/kit-onboarding.cast +148 -0
  29. package/docs/media/kit-onboarding.gif +0 -0
  30. package/githooks/pre-commit +18 -0
  31. package/kit.config.schema.json +105 -0
  32. package/package.json +54 -0
  33. package/privacy-denylist.example +8 -0
  34. package/profiles/automation.json +36 -0
  35. package/profiles/content.json +41 -0
  36. package/profiles/minimal.json +31 -0
  37. package/profiles/research.json +37 -0
  38. package/profiles/software.json +32 -0
  39. package/scripts/annotate-setup.sh +149 -0
  40. package/scripts/autopilot.sh +50 -0
  41. package/scripts/capture-project-ids.sh +53 -0
  42. package/scripts/check.sh +66 -0
  43. package/scripts/contribute.sh +48 -0
  44. package/scripts/debug.sh +54 -0
  45. package/scripts/init-upgrade-test.sh +99 -0
  46. package/scripts/init.sh +827 -0
  47. package/scripts/install.sh +24 -0
  48. package/scripts/kit-add-test.sh +62 -0
  49. package/scripts/kit-add.sh +115 -0
  50. package/scripts/kit-adopt-test.sh +61 -0
  51. package/scripts/kit-adopt.sh +122 -0
  52. package/scripts/kit-bump-version.sh +79 -0
  53. package/scripts/kit-digest.sh +126 -0
  54. package/scripts/kit-doctor.sh +663 -0
  55. package/scripts/kit-export-project-test.sh +82 -0
  56. package/scripts/kit-export-project.sh +245 -0
  57. package/scripts/kit-export-training-test.sh +51 -0
  58. package/scripts/kit-export-training.sh +175 -0
  59. package/scripts/kit-migrate-test.sh +80 -0
  60. package/scripts/kit-migrate.sh +190 -0
  61. package/scripts/kit-onboard-test.sh +63 -0
  62. package/scripts/kit-onboard.sh +69 -0
  63. package/scripts/kit-promote-test.sh +54 -0
  64. package/scripts/kit-promote.sh +102 -0
  65. package/scripts/kit-remove-test.sh +61 -0
  66. package/scripts/kit-remove.sh +84 -0
  67. package/scripts/kit-routines.sh +322 -0
  68. package/scripts/kit-version-check.sh +91 -0
  69. package/scripts/kit-wire-test.sh +54 -0
  70. package/scripts/kit-wire.sh +132 -0
  71. package/scripts/knowledge-lint.sh +96 -0
  72. package/scripts/lib/cckit-output.sh +36 -0
  73. package/scripts/lib/effort-metrics.sh +452 -0
  74. package/scripts/lib/effort-ops-test.sh +83 -0
  75. package/scripts/lib/effort-ops.sh +132 -0
  76. package/scripts/lib/effort-plan.sh +104 -0
  77. package/scripts/lib/effort.sh +191 -0
  78. package/scripts/lib/engine-adapter.sh +92 -0
  79. package/scripts/lib/gh-log.sh +58 -0
  80. package/scripts/lib/gh-project.sh +212 -0
  81. package/scripts/lib/handoff.sh +35 -0
  82. package/scripts/lib/kit-cli-test.sh +42 -0
  83. package/scripts/lib/kit-cli.sh +32 -0
  84. package/scripts/lib/kit-config-resolve.sh +145 -0
  85. package/scripts/lib/kit-config.sh +88 -0
  86. package/scripts/lib/kit-engine-test.sh +107 -0
  87. package/scripts/lib/kit-events.sh +62 -0
  88. package/scripts/lib/kit-gc.sh +117 -0
  89. package/scripts/lib/kit-interview-test.sh +77 -0
  90. package/scripts/lib/kit-interview.sh +203 -0
  91. package/scripts/lib/kit-local.sh +79 -0
  92. package/scripts/lib/kit-manifest.sh +127 -0
  93. package/scripts/lib/kit-mode-test.sh +49 -0
  94. package/scripts/lib/kit-mode.sh +67 -0
  95. package/scripts/lib/kit-operate.sh +105 -0
  96. package/scripts/lib/kit-profile-test.sh +62 -0
  97. package/scripts/lib/kit-profile.sh +115 -0
  98. package/scripts/lib/kit-task-ops-test.sh +63 -0
  99. package/scripts/lib/kit-task-ops.sh +341 -0
  100. package/scripts/lib/pr-evidence.sh +173 -0
  101. package/scripts/lib/project-scan.sh +16 -0
  102. package/scripts/lib/react-detect.sh +78 -0
  103. package/scripts/lib/role-identity.sh +47 -0
  104. package/scripts/lib/secret-guard.sh +96 -0
  105. package/scripts/lib/toon.sh +35 -0
  106. package/scripts/lib/ui.sh +42 -0
  107. package/scripts/lib/version-bump.sh +59 -0
  108. package/scripts/lib/worktree-issue-test.sh +45 -0
  109. package/scripts/lib/worktree-issue.sh +73 -0
  110. package/scripts/lib/worktree-start.sh +280 -0
  111. package/scripts/orchestrate.sh +160 -0
  112. package/scripts/portable-test.sh +53 -0
  113. package/scripts/publish.sh +94 -0
  114. package/scripts/setup-labels.sh +25 -0
  115. package/scripts/setup-milestones.sh +17 -0
  116. package/scripts/showcase.sh +64 -0
  117. package/scripts/status.sh +44 -0
  118. package/scripts/task-sync.sh +59 -0
  119. package/scripts/test.sh +48 -0
  120. package/scripts/web-install.sh +22 -0
  121. package/skills/kit-annotate/SKILL.md +107 -0
  122. package/skills/kit-autopilot/SKILL.md +108 -0
  123. package/skills/kit-contribute/SKILL.md +134 -0
  124. package/skills/kit-customize/SKILL.md +134 -0
  125. package/skills/kit-dev/SKILL.md +67 -0
  126. package/skills/kit-digest/SKILL.md +41 -0
  127. package/skills/kit-effort-close/SKILL.md +156 -0
  128. package/skills/kit-effort-new/SKILL.md +173 -0
  129. package/skills/kit-effort-pr/SKILL.md +139 -0
  130. package/skills/kit-effort-start/SKILL.md +85 -0
  131. package/skills/kit-gc/SKILL.md +80 -0
  132. package/skills/kit-onboard/SKILL.md +50 -0
  133. package/skills/kit-security-sweep/SKILL.md +57 -0
  134. package/skills/kit-ship/SKILL.md +43 -0
  135. package/skills/kit-task-close/SKILL.md +66 -0
  136. package/skills/kit-task-new/SKILL.md +51 -0
  137. package/skills/kit-task-pr/SKILL.md +43 -0
  138. package/skills/kit-task-pr-auto/SKILL.md +27 -0
  139. package/skills/kit-task-pr-merge/SKILL.md +53 -0
  140. package/skills/kit-task-start/SKILL.md +76 -0
  141. package/skills/kit-task-sync/SKILL.md +37 -0
  142. package/templates/CLAUDE.md.tmpl +106 -0
  143. package/templates/agents/analyst.md +55 -0
  144. package/templates/agents/auto-dev.md +93 -0
  145. package/templates/agents/backend.md +59 -0
  146. package/templates/agents/designer.md +73 -0
  147. package/templates/agents/devops.md +57 -0
  148. package/templates/agents/editor.md +48 -0
  149. package/templates/agents/frontend.md +81 -0
  150. package/templates/agents/generalist.md +46 -0
  151. package/templates/agents/local-delegate.md +70 -0
  152. package/templates/agents/n8n.md +65 -0
  153. package/templates/agents/pm.md +69 -0
  154. package/templates/agents/qa.md +66 -0
  155. package/templates/agents/researcher.md +57 -0
  156. package/templates/agents/security.md +65 -0
  157. package/templates/agents/tech-lead.md +75 -0
  158. package/templates/hooks/guard-base-branch-commit.sh.tmpl +45 -0
  159. package/templates/hooks/kit-local-status.sh.tmpl +34 -0
  160. package/templates/hooks/kit_version_check.sh.tmpl +6 -0
  161. package/templates/hooks/mempal_followup.sh.tmpl +97 -0
  162. package/templates/hooks/mempal_precompact.sh.tmpl +4 -0
  163. package/templates/hooks/mempal_save.sh.tmpl +4 -0
  164. package/templates/hooks/mempal_session_start.sh.tmpl +8 -0
  165. package/templates/hooks/prepush_gate.sh.tmpl +36 -0
  166. package/templates/hooks/repo-hygiene.sh.tmpl +72 -0
  167. package/templates/kit.config.json.tmpl +32 -0
  168. package/templates/knowledge-INDEX.md.tmpl +12 -0
  169. package/templates/lib/kit-sigil.sh.tmpl +124 -0
  170. package/templates/rules/branch-naming.md +104 -0
  171. package/templates/rules/communication-style.md +22 -0
  172. package/templates/rules/delegation-brief.md +40 -0
  173. package/templates/rules/design-routing.md +35 -0
  174. package/templates/rules/effort-model.md +122 -0
  175. package/templates/rules/knowledge-base.md +41 -0
  176. package/templates/rules/mempalace.md +110 -0
  177. package/templates/rules/plan-output-format.md +58 -0
  178. package/templates/rules/react-annotate.md +69 -0
  179. package/templates/rules/risk-tiered-review.md +62 -0
  180. package/templates/rules/skill-gaps.md +48 -0
  181. package/templates/rules/task-management.md +42 -0
  182. package/templates/settings/settings.local.json.tmpl +27 -0
  183. package/templates/skills/NAMESPACED +13 -0
  184. package/templates/skills/copywriting/SKILL.md +252 -0
  185. package/templates/skills/copywriting/references/copy-frameworks.md +344 -0
  186. package/templates/skills/copywriting/references/natural-transitions.md +272 -0
  187. package/templates/skills/feature-build-refine/SKILL.md +367 -0
  188. package/templates/skills/karpathy-guidelines/SKILL.md +69 -0
  189. package/templates/skills/morning-briefing/SKILL.md +46 -0
  190. package/templates/skills/speckit/SKILL.md +239 -0
  191. 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 '![%s](/showcase/%s.png)\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}"
@@ -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."