@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,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install.sh — put cckit on your PATH. Symlinks bin/cckit into a bin dir (no copy, no build).
|
|
3
|
+
# Usage: ./scripts/install.sh [target-bin-dir] (default: ~/.local/bin, then /usr/local/bin)
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
root="$(cd "$(dirname "$0")/.." && pwd)"
|
|
7
|
+
src="$root/bin/cckit"
|
|
8
|
+
[ -x "$src" ] || { echo "cckit: $src not found or not executable" >&2; exit 1; }
|
|
9
|
+
|
|
10
|
+
# Pick a writable bin dir on PATH.
|
|
11
|
+
target="${1:-}"
|
|
12
|
+
if [ -z "$target" ]; then
|
|
13
|
+
for d in "$HOME/.local/bin" "/usr/local/bin" "$HOME/bin"; do
|
|
14
|
+
if [ -d "$d" ] && [ -w "$d" ]; then target="$d"; break; fi
|
|
15
|
+
done
|
|
16
|
+
[ -z "$target" ] && { mkdir -p "$HOME/.local/bin"; target="$HOME/.local/bin"; }
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
ln -sf "$src" "$target/cckit"
|
|
20
|
+
echo "cckit: linked $target/cckit -> $src"
|
|
21
|
+
case ":$PATH:" in
|
|
22
|
+
*":$target:"*) echo "cckit: '$target' is on your PATH. Run: cckit help" ;;
|
|
23
|
+
*) echo "cckit: add '$target' to your PATH, then run: cckit help" ;;
|
|
24
|
+
esac
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-add-test.sh — self-test for kit-add module stacking (#372). Runs under bash AND zsh.
|
|
3
|
+
# Run: bash scripts/kit-add-test.sh
|
|
4
|
+
dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
|
|
5
|
+
|
|
6
|
+
if [ -n "${KIT_ADD_TEST_INNER:-}" ]; then
|
|
7
|
+
set -u
|
|
8
|
+
fail=0
|
|
9
|
+
t() { if [ "$2" != "$3" ]; then echo "FAIL($KIT_ADD_TEST_INNER): $1 -> got '[$2]' want '[$3]'"; fail=1; else echo "ok($KIT_ADD_TEST_INNER): $1"; fi; }
|
|
10
|
+
ADD="$dir/kit-add.sh"
|
|
11
|
+
|
|
12
|
+
export KIT_PROFILE_HOME="$(mktemp -d)"
|
|
13
|
+
export KIT_ASSUME_YES=1 KIT_VERSION=9.9.9
|
|
14
|
+
ws="$(mktemp -d)"; trap 'rm -rf "$KIT_PROFILE_HOME" "$ws"' EXIT
|
|
15
|
+
mkdir -p "$ws/myapp"
|
|
16
|
+
printf '%s\n' '{"workspace":{"name":"ws"},"mode":"guided"}' > "$ws/kit.workspace.json"
|
|
17
|
+
parent_before="$(cat "$ws/kit.workspace.json")"
|
|
18
|
+
|
|
19
|
+
# unknown module fails
|
|
20
|
+
"$ADD" doesnotexist --dir "$ws/myapp" >/dev/null 2>&1; t "unknown module rc" "$?" "1"
|
|
21
|
+
|
|
22
|
+
# add software (defaults) — island config created
|
|
23
|
+
"$ADD" software --dir "$ws/myapp" >/dev/null 2>&1; t "add rc" "$?" "0"
|
|
24
|
+
cfg="$ws/myapp/.claude/kit.config.json"
|
|
25
|
+
t "island created" "$([ -f "$cfg" ] && echo yes)" "yes"
|
|
26
|
+
t "modules" "$(jq -c .modules "$cfg")" '["software"]'
|
|
27
|
+
t "github wizard" "$(jq -r .github.projectsV2 "$cfg")" "true"
|
|
28
|
+
t "ci wizard" "$(jq -r .ci.provider "$cfg")" "github-actions"
|
|
29
|
+
t "manifest tracks" "$(jq -r '.entries["'.claude/kit.config.json'"] | has("hash")' "$ws/myapp/.claude/kit.manifest.json")" "true"
|
|
30
|
+
|
|
31
|
+
# parent workspace untouched
|
|
32
|
+
t "parent untouched" "$(cat "$ws/kit.workspace.json")" "$parent_before"
|
|
33
|
+
|
|
34
|
+
# idempotent: a 2nd identical run leaves the file byte-identical
|
|
35
|
+
before="$(cat "$cfg")"
|
|
36
|
+
"$ADD" software --dir "$ws/myapp" >/dev/null 2>&1
|
|
37
|
+
t "idempotent file" "$(cat "$cfg")" "$before"
|
|
38
|
+
|
|
39
|
+
# --set override changes only that field; modules stay deduped
|
|
40
|
+
"$ADD" software --dir "$ws/myapp" --set deploy=vercel >/dev/null 2>&1
|
|
41
|
+
t "override deploy" "$(jq -r .deploy.target "$cfg")" "vercel"
|
|
42
|
+
t "modules still one" "$(jq -c .modules "$cfg")" '["software"]'
|
|
43
|
+
|
|
44
|
+
# dry-run writes nothing new
|
|
45
|
+
fresh="$(mktemp -d)"; mkdir -p "$fresh/app"
|
|
46
|
+
KIT_DRY_RUN=1 "$ADD" software --dir "$fresh/app" >/dev/null 2>&1
|
|
47
|
+
t "dry-run no write" "$([ -f "$fresh/app/.claude/kit.config.json" ] && echo wrote || echo clean)" "clean"
|
|
48
|
+
rm -rf "$fresh"
|
|
49
|
+
|
|
50
|
+
[ "$fail" -eq 0 ] && echo "ALL OK($KIT_ADD_TEST_INNER)"
|
|
51
|
+
exit "$fail"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
rc=0; ran=0
|
|
55
|
+
for sh in bash zsh; do
|
|
56
|
+
command -v "$sh" >/dev/null 2>&1 || continue
|
|
57
|
+
ran=$((ran+1)); echo "--- $sh ---"
|
|
58
|
+
rcflag=""; [ "$sh" = "zsh" ] && rcflag="--no-rcs"
|
|
59
|
+
KIT_ADD_TEST_INNER="$sh" PATH="$PATH" "$sh" $rcflag "$0" || rc=1
|
|
60
|
+
done
|
|
61
|
+
[ "$ran" -eq 0 ] && { echo "no shell"; exit 1; }
|
|
62
|
+
exit "$rc"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-add.sh — stack a MODULE onto a project (D1 "modules apilables", #372). e.g. `kit add software`.
|
|
3
|
+
#
|
|
4
|
+
# THE MOMENT: a free workspace ("build me an app") becomes a GitHub-managed software island WITHOUT
|
|
5
|
+
# restructuring anything above it (D4/D17). A module is identity, not a file dump (D14): kit-add
|
|
6
|
+
# writes the island's .claude/kit.config.json (modules[] + wizard answers) through the F0 operation
|
|
7
|
+
# machine, so it is manifest-tracked, idempotent (2x = no-op), dry-runnable, and removable. The
|
|
8
|
+
# engine (agents/skills) is the installed plugin singleton — it is never copied per project.
|
|
9
|
+
#
|
|
10
|
+
# The ASKING (the wizard) is interactive and portable, so the kit-onboard skill / the /kit-add
|
|
11
|
+
# command render it via AskUserQuestion and pass answers here as a file. This script also takes a
|
|
12
|
+
# minimal terminal fallback so it stands alone.
|
|
13
|
+
#
|
|
14
|
+
# Run:
|
|
15
|
+
# scripts/kit-add.sh software [--dir DIR] --answers FILE # non-interactive (skill/CI/tests)
|
|
16
|
+
# scripts/kit-add.sh software --set versioning=github --set deploy=vercel --set ci=github-actions
|
|
17
|
+
# KIT_ASSUME_YES=1 scripts/kit-add.sh software # accept every recommended default
|
|
18
|
+
# KIT_DRY_RUN=1 scripts/kit-add.sh software # preview only
|
|
19
|
+
# Requires: jq.
|
|
20
|
+
|
|
21
|
+
set -uo pipefail
|
|
22
|
+
_add_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
23
|
+
# shellcheck source=/dev/null
|
|
24
|
+
. "$_add_dir/lib/kit-interview.sh" # catalog/apply (pulls kit-cli + kit-profile)
|
|
25
|
+
# shellcheck source=/dev/null
|
|
26
|
+
. "$_add_dir/lib/kit-operate.sh" # four-beat machine + manifest
|
|
27
|
+
|
|
28
|
+
# kit_add_module_exists <module> -> rc 0 if a module spec is installed
|
|
29
|
+
kit_add_module_exists() { [ -f "$(kit_interview_catalog_file "$1")" ]; }
|
|
30
|
+
|
|
31
|
+
# kit_add <module> <answersfile> [targetdir]
|
|
32
|
+
# Merges the module's wizard answers onto the island config and persists via kit-operate.
|
|
33
|
+
kit_add() {
|
|
34
|
+
command -v jq >/dev/null 2>&1 || { kit_die "jq required"; }
|
|
35
|
+
local module="$1" answers="$2" target="${3:-$PWD}"
|
|
36
|
+
[ -n "$module" ] || kit_die "usage: kit-add.sh <module> [--dir DIR] [--answers FILE | --set k=v ...]"
|
|
37
|
+
kit_add_module_exists "$module" || kit_die "unknown module '$module' (no $(kit_interview_catalog_file "$module"))"
|
|
38
|
+
[ -f "$answers" ] || kit_die "internal: answers file missing ($answers)"
|
|
39
|
+
|
|
40
|
+
# Operate + manifest are relative to the island root — work from there.
|
|
41
|
+
target="$(cd "$target" 2>/dev/null && pwd)" || kit_die "no such dir: $target"
|
|
42
|
+
cd "$target" || kit_die "cannot enter $target"
|
|
43
|
+
|
|
44
|
+
local cfg=".claude/kit.config.json" base merged tmp
|
|
45
|
+
base=""; [ -f "$cfg" ] && base="$cfg"
|
|
46
|
+
merged="$(kit_interview_apply "$module" "$answers" "$base")" || kit_die "deriving module config failed"
|
|
47
|
+
# Belt-and-suspenders: guarantee the module name is in modules[] even if the spec omitted it.
|
|
48
|
+
merged="$(printf '%s' "$merged" | jq --arg m "$module" '.modules = ((.modules // []) + [$m] | unique)')"
|
|
49
|
+
|
|
50
|
+
kit_say "kit add $module -> $target/$cfg"
|
|
51
|
+
printf '%s\n' "$merged" | kit_op_write_content "$cfg" A "add:$module"
|
|
52
|
+
local rc=$?
|
|
53
|
+
case $rc in
|
|
54
|
+
0) kit_say "module '$module' active here. modules: $(printf '%s' "$merged" | jq -c '.modules')";;
|
|
55
|
+
10) kit_say "no change (already applied / declined / dry-run).";;
|
|
56
|
+
*) kit_die "failed to write $cfg";;
|
|
57
|
+
esac
|
|
58
|
+
return 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# --- collect answers from flags / env / minimal terminal fallback ---------
|
|
62
|
+
# Builds a {key:value} JSON. Precedence: --answers FILE, then --set overrides, then for any unset
|
|
63
|
+
# question: KIT_ASSUME_YES/non-tty -> default; tty -> ask once with the recommended default.
|
|
64
|
+
_kit_add_collect() {
|
|
65
|
+
local module="$1" answers_file="$2"; shift 2
|
|
66
|
+
# All locals up front — a bare `local x` re-run in a loop prints the var under zsh (typeset-like).
|
|
67
|
+
local sets=("$@")
|
|
68
|
+
local catalog acc kv k v tmp nq i key def have pick qtext r
|
|
69
|
+
catalog="$(kit_interview_catalog "$module")" || return 1
|
|
70
|
+
acc="$(mktemp)"
|
|
71
|
+
if [ -n "$answers_file" ] && [ -f "$answers_file" ]; then cp "$answers_file" "$acc"; else printf '{}\n' > "$acc"; fi
|
|
72
|
+
# apply --set k=v
|
|
73
|
+
for kv in ${sets[@]+"${sets[@]}"}; do
|
|
74
|
+
k="${kv%%=*}"; v="${kv#*=}"
|
|
75
|
+
tmp="$(mktemp)"; jq --arg k "$k" --arg v "$v" '.[$k]=$v' "$acc" > "$tmp" && mv "$tmp" "$acc"
|
|
76
|
+
done
|
|
77
|
+
# fill the rest
|
|
78
|
+
nq="$(printf '%s' "$catalog" | jq '.questions | length')"; i=0
|
|
79
|
+
while [ "$i" -lt "$nq" ]; do
|
|
80
|
+
key="$(printf '%s' "$catalog" | jq -r ".questions[$i].key")"
|
|
81
|
+
def="$(printf '%s' "$catalog" | jq -r ".questions[$i].default // \"\"")"
|
|
82
|
+
have="$(jq -r --arg k "$key" '.[$k] // empty' "$acc")"
|
|
83
|
+
if [ -z "$have" ]; then
|
|
84
|
+
pick="$def"
|
|
85
|
+
if [ -z "${KIT_ASSUME_YES:-}" ] && [ -z "${KIT_DRY_RUN:-}" ] && [ -t 0 ]; then
|
|
86
|
+
qtext="$(printf '%s' "$catalog" | jq -r ".questions[$i].question")"
|
|
87
|
+
printf '%s [%s] ' "$qtext" "$def" >&2
|
|
88
|
+
r=""; IFS= read -r r </dev/tty 2>/dev/null || r=""
|
|
89
|
+
[ -n "$r" ] && pick="$r"
|
|
90
|
+
fi
|
|
91
|
+
tmp="$(mktemp)"; jq --arg k "$key" --arg v "$pick" '.[$k]=$v' "$acc" > "$tmp" && mv "$tmp" "$acc"
|
|
92
|
+
fi
|
|
93
|
+
i=$((i+1))
|
|
94
|
+
done
|
|
95
|
+
printf '%s\n' "$acc" # caller reads the path, then removes it
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# CLI
|
|
99
|
+
if kit_is_main; then
|
|
100
|
+
_module=""; _target="$PWD"; _answers=""; _sets=()
|
|
101
|
+
while [ $# -gt 0 ]; do case "$1" in
|
|
102
|
+
--dir) _target="$2"; shift 2;;
|
|
103
|
+
--answers) _answers="$2"; shift 2;;
|
|
104
|
+
--set) _sets+=("$2"); shift 2;;
|
|
105
|
+
-h|--help) echo "usage: kit-add.sh <module> [--dir DIR] [--answers FILE] [--set k=v ...] (env: KIT_ASSUME_YES, KIT_DRY_RUN)"; exit 0;;
|
|
106
|
+
-*) kit_die "unknown flag: $1";;
|
|
107
|
+
*) [ -z "$_module" ] && _module="$1" || kit_die "unexpected arg: $1"; shift;;
|
|
108
|
+
esac; done
|
|
109
|
+
[ -n "$_module" ] || kit_die "usage: kit-add.sh <module> [--dir DIR] [--answers FILE] [--set k=v ...]"
|
|
110
|
+
kit_add_module_exists "$_module" || kit_die "unknown module '$_module'"
|
|
111
|
+
_af="$(_kit_add_collect "$_module" "$_answers" ${_sets[@]+"${_sets[@]}"})" || kit_die "collecting answers failed"
|
|
112
|
+
kit_add "$_module" "$_af" "$_target"; _rc=$?
|
|
113
|
+
rm -f "$_af"
|
|
114
|
+
exit $_rc
|
|
115
|
+
fi
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-adopt-test.sh — self-test for kit-adopt (#373). Runs under bash AND zsh.
|
|
3
|
+
# Run: bash scripts/kit-adopt-test.sh
|
|
4
|
+
PLUGIN_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
5
|
+
|
|
6
|
+
if [ -n "${KIT_ADOPT_TEST_INNER:-}" ]; then
|
|
7
|
+
set -u
|
|
8
|
+
fail=0
|
|
9
|
+
t() { if [ "$2" != "$3" ]; then echo "FAIL($KIT_ADOPT_TEST_INNER): $1 -> got '[$2]' want '[$3]'"; fail=1; else echo "ok($KIT_ADOPT_TEST_INNER): $1"; fi; }
|
|
10
|
+
|
|
11
|
+
proj="$(mktemp -d)"; trap 'rm -rf "$proj"' EXIT
|
|
12
|
+
cd "$proj"
|
|
13
|
+
. "$PLUGIN_DIR/scripts/kit-adopt.sh"
|
|
14
|
+
. "$PLUGIN_DIR/scripts/lib/kit-manifest.sh"
|
|
15
|
+
|
|
16
|
+
# A project that imported kit files by hand: skills + a rule + statusline (unmanaged) + user content.
|
|
17
|
+
mkdir -p .claude/skills/kit-task-pr .claude/rules knowledge
|
|
18
|
+
printf 'pr skill\n' > .claude/skills/kit-task-pr/SKILL.md
|
|
19
|
+
printf 'a rule\n' > .claude/rules/branch-naming.md
|
|
20
|
+
printf '#!/bin/sh\n' > .claude/statusline.sh
|
|
21
|
+
printf 'my notes\n' > knowledge/doc.md # user content — must NEVER be a candidate
|
|
22
|
+
|
|
23
|
+
# --check: 3 kit-shaped files unmanaged -> rc 1, knowledge/ excluded.
|
|
24
|
+
out="$(kit_adopt_check 2>&1)"; rc=$?
|
|
25
|
+
t "check rc=1 when unmanaged exist" "$rc" "1"
|
|
26
|
+
t "check counts the 3 kit files" "$(printf '%s' "$out" | grep -c '?')" "3"
|
|
27
|
+
t "check never lists knowledge/" "$(printf '%s' "$out" | grep -c 'knowledge/')" "0"
|
|
28
|
+
|
|
29
|
+
# dry-run records nothing.
|
|
30
|
+
KIT_DRY_RUN=1 kit_adopt_all >/dev/null 2>&1
|
|
31
|
+
t "dry-run writes no manifest" "$([ -f .claude/kit.manifest.json ] && echo yes || echo no)" "no"
|
|
32
|
+
|
|
33
|
+
# real adopt (assume-yes): all 3 recorded, knowledge untouched.
|
|
34
|
+
KIT_ASSUME_YES=1 kit_adopt_all >/dev/null 2>&1
|
|
35
|
+
t "skill now tracked" "$(kit_manifest_verify .claude/skills/kit-task-pr/SKILL.md)" "intact"
|
|
36
|
+
t "rule now tracked" "$(kit_manifest_verify .claude/rules/branch-naming.md)" "intact"
|
|
37
|
+
t "statusline now tracked" "$(kit_manifest_verify .claude/statusline.sh)" "intact"
|
|
38
|
+
t "statusline tier=B" "$(kit_manifest_get .claude/statusline.sh | jq -r .tier)" "B"
|
|
39
|
+
t "skill tier=A" "$(kit_manifest_get .claude/skills/kit-task-pr/SKILL.md | jq -r .tier)" "A"
|
|
40
|
+
t "op=adopt" "$(kit_manifest_get .claude/rules/branch-naming.md | jq -r .op)" "adopt"
|
|
41
|
+
t "knowledge NOT tracked" "$(kit_manifest_verify knowledge/doc.md)" "untracked"
|
|
42
|
+
|
|
43
|
+
# idempotent: a second adopt finds nothing new; --check is clean (rc 0).
|
|
44
|
+
out2="$(KIT_ASSUME_YES=1 kit_adopt_all 2>&1)"
|
|
45
|
+
t "2nd adopt no-op" "$(printf '%s' "$out2" | grep -c 'nothing to adopt')" "1"
|
|
46
|
+
kit_adopt_check >/dev/null 2>&1
|
|
47
|
+
t "check clean after adopt rc=0" "$?" "0"
|
|
48
|
+
|
|
49
|
+
[ "$fail" -eq 0 ] && echo "ALL OK($KIT_ADOPT_TEST_INNER)"
|
|
50
|
+
exit "$fail"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
rc=0; ran=0
|
|
54
|
+
for sh in bash zsh; do
|
|
55
|
+
command -v "$sh" >/dev/null 2>&1 || continue
|
|
56
|
+
ran=$((ran+1)); echo "--- $sh ---"
|
|
57
|
+
rcflag=""; [ "$sh" = "zsh" ] && rcflag="--no-rcs"
|
|
58
|
+
KIT_ADOPT_TEST_INNER="$sh" PATH="$PATH" PLUGIN_DIR="$PLUGIN_DIR" "$sh" $rcflag "$0" || rc=1
|
|
59
|
+
done
|
|
60
|
+
[ "$ran" -eq 0 ] && { echo "no shell"; exit 1; }
|
|
61
|
+
exit "$rc"
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-adopt.sh — adopt kit-shaped files a project ALREADY has into the ownership manifest (#373).
|
|
3
|
+
#
|
|
4
|
+
# THE MOMENT: a project imported the kit by hand (copied a .claude/ from another repo, ran an old
|
|
5
|
+
# init, or a teammate pasted skills/rules in) — the files are PRESENT but the manifest doesn't know
|
|
6
|
+
# them, so update/remove can't manage them safely (kit_manifest_verify says `untracked` -> "never
|
|
7
|
+
# touch"). kit-adopt walks the kit-shaped paths that exist on disk, shows what it would adopt
|
|
8
|
+
# (dry-run diff), and — on confirm — RECORDS each into the manifest at its current hash. It writes
|
|
9
|
+
# NO file content; it only takes ownership of what's already there. After adopting, /kit-update can
|
|
10
|
+
# refresh them and kit-remove can leave clean.
|
|
11
|
+
#
|
|
12
|
+
# It is the read-only counterpart to kit-wire: wire WRITES the kit's own files; adopt CLAIMS files
|
|
13
|
+
# the project brought. It never claims a file that is not kit-shaped (your knowledge/, plans/,
|
|
14
|
+
# drafts/, app code are off-limits) and never overwrites — adoption is hash-recording only.
|
|
15
|
+
#
|
|
16
|
+
# Run:
|
|
17
|
+
# scripts/kit-adopt.sh # interactive: list candidates, confirm, record
|
|
18
|
+
# scripts/kit-adopt.sh --dry-run # preview only
|
|
19
|
+
# scripts/kit-adopt.sh --check # report unmanaged kit-shaped files, write nothing (rc 1 if any)
|
|
20
|
+
# KIT_ASSUME_YES=1 scripts/kit-adopt.sh # non-interactive (init/CI/Cowork)
|
|
21
|
+
# Requires: jq + a sha256 tool.
|
|
22
|
+
|
|
23
|
+
set -uo pipefail
|
|
24
|
+
_adopt_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
25
|
+
# shellcheck source=/dev/null
|
|
26
|
+
. "$_adopt_dir/lib/kit-operate.sh" # four-beat machine + kit-manifest.sh
|
|
27
|
+
# shellcheck source=/dev/null
|
|
28
|
+
. "$_adopt_dir/lib/kit-cli.sh" # kit_is_main
|
|
29
|
+
|
|
30
|
+
# Tier-classify a kit-shaped path. Statusline shim + settings + hooks are CLI-only (tier B); the
|
|
31
|
+
# portable skills/rules/agents/commands are tier A (work in Cowork/claude.ai too). Mirrors kit-wire.
|
|
32
|
+
_kit_adopt_tier() {
|
|
33
|
+
case "$1" in
|
|
34
|
+
.claude/statusline.sh|.claude/settings.json|.claude/hooks/*|.claude/lib/*) printf 'B' ;;
|
|
35
|
+
*) printf 'A' ;;
|
|
36
|
+
esac
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Emit the set of kit-shaped paths that EXIST under the project but are NOT yet in the manifest.
|
|
40
|
+
# "kit-shaped" = the directories the kit owns (skills/rules/agents/commands/hooks/lib) + the two
|
|
41
|
+
# wired singletons. Never the user's content trees (knowledge/, plans/, drafts/, apps/, src/).
|
|
42
|
+
# One relpath per line.
|
|
43
|
+
kit_adopt_candidates() {
|
|
44
|
+
local p verdict
|
|
45
|
+
# Files under the kit-owned .claude subtrees.
|
|
46
|
+
while IFS= read -r p; do
|
|
47
|
+
[ -n "$p" ] || continue
|
|
48
|
+
p="${p#./}"
|
|
49
|
+
verdict="$(kit_manifest_verify "$p" 2>/dev/null || true)"
|
|
50
|
+
[ "$verdict" = "untracked" ] && printf '%s\n' "$p"
|
|
51
|
+
done < <(
|
|
52
|
+
find .claude/skills .claude/rules .claude/agents .claude/commands \
|
|
53
|
+
.claude/hooks .claude/lib -type f 2>/dev/null
|
|
54
|
+
)
|
|
55
|
+
# The two wired singletons, if present.
|
|
56
|
+
for p in .claude/statusline.sh .claude/settings.json; do
|
|
57
|
+
[ -f "$p" ] || continue
|
|
58
|
+
verdict="$(kit_manifest_verify "$p" 2>/dev/null || true)"
|
|
59
|
+
[ "$verdict" = "untracked" ] && printf '%s\n' "$p"
|
|
60
|
+
done
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# kit_adopt_all — record every unmanaged kit-shaped file into the manifest at its current hash.
|
|
64
|
+
# Honors KIT_DRY_RUN (report only) and KIT_ASSUME_YES (skip the confirm). Returns 0 always (a clean
|
|
65
|
+
# project with nothing to adopt is success).
|
|
66
|
+
kit_adopt_all() {
|
|
67
|
+
command -v jq >/dev/null 2>&1 || { echo "kit-adopt: jq required" >&2; return 1; }
|
|
68
|
+
|
|
69
|
+
# Collect candidates into an array first (a per-file confirm's read must not eat the loop's stdin).
|
|
70
|
+
local arr=() line
|
|
71
|
+
while IFS= read -r line; do [ -n "$line" ] && arr+=("$line"); done < <(kit_adopt_candidates)
|
|
72
|
+
|
|
73
|
+
local n="${#arr[@]}"
|
|
74
|
+
if [ "$n" -eq 0 ]; then _kit_op_say "kit-adopt: nothing to adopt — every kit-shaped file is already tracked (or none present)."; return 0; fi
|
|
75
|
+
|
|
76
|
+
_kit_op_say "kit-adopt: $n unmanaged kit-shaped file(s) found:"
|
|
77
|
+
local p; for p in ${arr[@]+"${arr[@]}"}; do _kit_op_say " + adopt $p ($(_kit_adopt_tier "$p"))"; done
|
|
78
|
+
|
|
79
|
+
if _kit_op_dry; then _kit_op_say " [dry-run] would record $n file(s) into the manifest"; return 0; fi
|
|
80
|
+
|
|
81
|
+
if [ -z "${KIT_ASSUME_YES:-}" ]; then
|
|
82
|
+
printf 'kit-adopt: take ownership of these %s file(s) in the manifest? [y/N] ' "$n" >&2
|
|
83
|
+
local _c=""; IFS= read -r _c </dev/tty 2>/dev/null || IFS= read -r _c 2>/dev/null || _c=""
|
|
84
|
+
case "$_c" in y|Y|yes|YES) ;; *) _kit_op_say "kit-adopt: aborted (nothing recorded)."; return 0;; esac
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
kit_manifest_init || return 1
|
|
88
|
+
local adopted=0
|
|
89
|
+
for p in ${arr[@]+"${arr[@]}"}; do
|
|
90
|
+
kit_manifest_record "$p" "$(_kit_adopt_tier "$p")" adopt >/dev/null && {
|
|
91
|
+
adopted=$((adopted+1)); _kit_op_say " adopted: $p"
|
|
92
|
+
}
|
|
93
|
+
done
|
|
94
|
+
_kit_op_say "kit-adopt: $adopted file(s) now manifest-managed (update/remove can handle them safely)."
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# --check: report unmanaged kit-shaped files, write nothing, rc 1 if any exist (SessionStart nudge).
|
|
98
|
+
kit_adopt_check() {
|
|
99
|
+
local arr=() line
|
|
100
|
+
while IFS= read -r line; do [ -n "$line" ] && arr+=("$line"); done < <(kit_adopt_candidates)
|
|
101
|
+
local n="${#arr[@]}"
|
|
102
|
+
[ "$n" -eq 0 ] && return 0
|
|
103
|
+
_kit_op_say "kit-adopt --check: $n kit-shaped file(s) present but unmanaged — run 'kit-adopt' to take ownership:"
|
|
104
|
+
local p; for p in ${arr[@]+"${arr[@]}"}; do _kit_op_say " ? $p"; done
|
|
105
|
+
return 1
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# CLI (direct execution only)
|
|
109
|
+
if kit_is_main; then
|
|
110
|
+
_mode=run
|
|
111
|
+
while [ $# -gt 0 ]; do case "$1" in
|
|
112
|
+
--dry-run) export KIT_DRY_RUN=1; shift;;
|
|
113
|
+
--yes|-y) export KIT_ASSUME_YES=1; shift;;
|
|
114
|
+
--check) _mode=check; shift;;
|
|
115
|
+
-h|--help) echo "usage: kit-adopt.sh [--check] [--dry-run] [--yes] (env: KIT_ASSUME_YES, KIT_DRY_RUN)"; exit 0;;
|
|
116
|
+
*) echo "unknown arg: $1" >&2; exit 2;;
|
|
117
|
+
esac; done
|
|
118
|
+
case "$_mode" in
|
|
119
|
+
check) kit_adopt_check;;
|
|
120
|
+
*) kit_adopt_all;;
|
|
121
|
+
esac
|
|
122
|
+
fi
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Bump the claude-kit plugin version in EVERY place that must stay in sync:
|
|
3
|
+
# <plugin>/.claude-plugin/plugin.json .version
|
|
4
|
+
# <plugin>/.claude-plugin/version.json .version + .channels.<beta|stable>.version
|
|
5
|
+
# <plugin>/package.json .version
|
|
6
|
+
# <plugin>/.claude-plugin/marketplace.json .metadata.version + .plugins[claude-kit].version
|
|
7
|
+
# <repo-root>/.claude-plugin/marketplace.json (when present) — same two fields
|
|
8
|
+
#
|
|
9
|
+
# Usage: kit-bump-version.sh [beta|patch|minor|<explicit-version>]
|
|
10
|
+
# beta (default) 0.7.0-beta.1 -> 0.7.0-beta.2 ; 0.7.0 -> 0.7.1-beta.1 (-> channels.beta)
|
|
11
|
+
# patch 0.7.0-beta.N -> 0.7.0 ; 0.7.0 -> 0.7.1 (-> channels.stable)
|
|
12
|
+
# minor 0.7.x[-*] -> 0.8.0 (-> channels.stable)
|
|
13
|
+
# 1.2.3-rc.1 set verbatim
|
|
14
|
+
#
|
|
15
|
+
# version.json is the canonical channel descriptor: a `-beta.N` build advances channels.beta,
|
|
16
|
+
# a clean release advances channels.stable. /kit-dev ship runs this so the installed plugin
|
|
17
|
+
# (deduped by version) sees every release.
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
# Resolve the plugin dir from this script's location (BASH_SOURCE), so it works run from the
|
|
21
|
+
# plugin checkout OR the monorepo. Repo root is best-effort (for the optional root marketplace).
|
|
22
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
plugin_dir="$(cd "$script_dir/.." && pwd)"
|
|
24
|
+
repo_root="$(git -C "$plugin_dir" rev-parse --show-toplevel 2>/dev/null || true)"
|
|
25
|
+
|
|
26
|
+
plugin_json="$plugin_dir/.claude-plugin/plugin.json"
|
|
27
|
+
version_json="$plugin_dir/.claude-plugin/version.json"
|
|
28
|
+
package_json="$plugin_dir/package.json"
|
|
29
|
+
sub_marketplace_json="$plugin_dir/.claude-plugin/marketplace.json"
|
|
30
|
+
root_marketplace_json="${repo_root:+$repo_root/.claude-plugin/marketplace.json}"
|
|
31
|
+
[[ -f $plugin_json ]] || { echo "✗ plugin.json not found at $plugin_json" >&2; exit 1; }
|
|
32
|
+
|
|
33
|
+
mode="${1:-beta}"
|
|
34
|
+
current="$(jq -r .version "$plugin_json")"
|
|
35
|
+
|
|
36
|
+
core="${current%%-*}"
|
|
37
|
+
pre=""; [[ $current == *-* ]] && pre="${current#*-}"
|
|
38
|
+
IFS=. read -r maj min pat <<<"$core"
|
|
39
|
+
|
|
40
|
+
channel="stable" # which channel this bump advances
|
|
41
|
+
case "$mode" in
|
|
42
|
+
beta)
|
|
43
|
+
channel="beta"
|
|
44
|
+
if [[ $pre == beta.* ]]; then
|
|
45
|
+
next="$core-beta.$(( ${pre#beta.} + 1 ))"
|
|
46
|
+
else
|
|
47
|
+
next="$maj.$min.$((pat + 1))-beta.1"
|
|
48
|
+
fi
|
|
49
|
+
;;
|
|
50
|
+
patch)
|
|
51
|
+
if [[ -n $pre ]]; then next="$core"; else next="$maj.$min.$((pat + 1))"; fi
|
|
52
|
+
;;
|
|
53
|
+
minor)
|
|
54
|
+
next="$maj.$((min + 1)).0"
|
|
55
|
+
;;
|
|
56
|
+
*)
|
|
57
|
+
[[ $mode =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.]+)?$ ]] || { echo "✗ '$mode' is not beta|patch|minor or a semver" >&2; exit 1; }
|
|
58
|
+
next="$mode"
|
|
59
|
+
[[ $next == *-* ]] && channel="beta"
|
|
60
|
+
;;
|
|
61
|
+
esac
|
|
62
|
+
|
|
63
|
+
_set() { local f="$1" filter="$2"; [[ -f "$f" ]] || return 0; local t; t="$(mktemp)"; jq "$filter" "$f" >"$t" && mv "$t" "$f"; }
|
|
64
|
+
|
|
65
|
+
_set "$plugin_json" '.version = "'"$next"'"'
|
|
66
|
+
_set "$package_json" '.version = "'"$next"'"'
|
|
67
|
+
# version.json: top-level version + the advancing channel's version.
|
|
68
|
+
_set "$version_json" '.version = "'"$next"'" | .channels.'"$channel"'.version = "'"$next"'"'
|
|
69
|
+
|
|
70
|
+
mkt_bumped=0
|
|
71
|
+
for mkt in "$root_marketplace_json" "$sub_marketplace_json"; do
|
|
72
|
+
[[ -n "$mkt" && -f "$mkt" ]] || continue
|
|
73
|
+
t="$(mktemp)"
|
|
74
|
+
jq --arg v "$next" '.metadata.version = $v | (.plugins[] | select(.name == "claude-kit") | .version) = $v' \
|
|
75
|
+
"$mkt" >"$t" && mv "$t" "$mkt"
|
|
76
|
+
mkt_bumped=$((mkt_bumped + 1))
|
|
77
|
+
done
|
|
78
|
+
|
|
79
|
+
echo "✓ claude-kit $current -> $next (channel: $channel; plugin.json + version.json + package.json + $mkt_bumped marketplace.json)"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-digest.sh — pre-digest long inputs (transcripts, CI logs, big files, YouTube URLs)
|
|
3
|
+
# with the LOCAL model (scripts/lib/kit-local.sh + mlx_lm.server) so the Claude session
|
|
4
|
+
# reads a <=1500-token digest + a pointer to the original instead of the full content.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ./scripts/kit-digest.sh <path|url> [--focus "<topic>"] [--lang es|en]
|
|
8
|
+
#
|
|
9
|
+
# Exit codes: 0 digest printed · 2 local server down (caller falls back to reading the
|
|
10
|
+
# original directly) · 1 input/usage error.
|
|
11
|
+
#
|
|
12
|
+
# Chunking: inputs are split into ~2500-word chunks (map), each digested locally, then
|
|
13
|
+
# merged in a final reduce pass when there is more than one chunk.
|
|
14
|
+
set -uo pipefail
|
|
15
|
+
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
# Plugin-direct: stay in the caller's CWD so the project's .claude/kit.config.json
|
|
18
|
+
# (.local model/port) is read; the helper + inputs resolve by absolute path.
|
|
19
|
+
|
|
20
|
+
# shellcheck source=lib/kit-local.sh
|
|
21
|
+
source "$SCRIPT_DIR/lib/kit-local.sh"
|
|
22
|
+
|
|
23
|
+
INPUT="${1:-}"
|
|
24
|
+
FOCUS=""
|
|
25
|
+
LANG_OUT="es"
|
|
26
|
+
shift || true
|
|
27
|
+
while [[ $# -gt 0 ]]; do
|
|
28
|
+
case "$1" in
|
|
29
|
+
--focus) FOCUS="$2"; shift 2 ;;
|
|
30
|
+
--lang) LANG_OUT="$2"; shift 2 ;;
|
|
31
|
+
*) echo "kit-digest: unknown flag $1" >&2; exit 1 ;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
[[ -z "$INPUT" ]] && { echo "usage: kit-digest.sh <path|url> [--focus \"<topic>\"] [--lang es|en]" >&2; exit 1; }
|
|
36
|
+
|
|
37
|
+
if ! kit_local_alive; then
|
|
38
|
+
echo "kit-digest: local server down (mlx_lm.server :${KIT_LOCAL_PORT}) — read the original directly instead." >&2
|
|
39
|
+
echo " start it: mlx_lm.server --model ${KIT_LOCAL_MODEL} --port ${KIT_LOCAL_PORT}" >&2
|
|
40
|
+
exit 2
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Acquire text
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
WORK="$(mktemp -d /tmp/kit-digest.XXXXXX)" || exit 1
|
|
47
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
48
|
+
TEXT="$WORK/input.txt"
|
|
49
|
+
SOURCE_DESC="$INPUT"
|
|
50
|
+
|
|
51
|
+
vtt_to_text() { # stdin: VTT -> stdout: deduped plain text
|
|
52
|
+
python3 -c '
|
|
53
|
+
import re, sys
|
|
54
|
+
out, prev = [], ""
|
|
55
|
+
for l in sys.stdin:
|
|
56
|
+
l = l.strip()
|
|
57
|
+
if not l or "-->" in l or l.startswith(("WEBVTT", "Kind:", "Language:")) or re.match(r"^\d+$", l):
|
|
58
|
+
continue
|
|
59
|
+
l = re.sub(r"<[^>]+>", "", l)
|
|
60
|
+
if l != prev:
|
|
61
|
+
out.append(l); prev = l
|
|
62
|
+
print(re.sub(r"\s+", " ", " ".join(out)))'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if [[ "$INPUT" =~ ^https?://(www\.)?(youtube\.com|youtu\.be)/ ]]; then
|
|
66
|
+
command -v yt-dlp >/dev/null 2>&1 || { echo "kit-digest: yt-dlp required for YouTube URLs (brew install yt-dlp)" >&2; exit 1; }
|
|
67
|
+
TITLE="$(yt-dlp --skip-download --print '%(title)s' "$INPUT" 2>/dev/null | head -1 || true)"
|
|
68
|
+
SOURCE_DESC="${TITLE:-$INPUT} ($INPUT)"
|
|
69
|
+
yt-dlp --skip-download --write-auto-subs --write-subs --sub-langs "en.*,es.*" --sub-format vtt \
|
|
70
|
+
-o "$WORK/yt" "$INPUT" >/dev/null 2>&1 || true
|
|
71
|
+
VTT="$(ls "$WORK"/yt*.vtt 2>/dev/null | head -1 || true)"
|
|
72
|
+
[[ -z "$VTT" ]] && { echo "kit-digest: no subtitles found for $INPUT" >&2; exit 1; }
|
|
73
|
+
vtt_to_text < "$VTT" > "$TEXT"
|
|
74
|
+
elif [[ "$INPUT" =~ ^https?:// ]]; then
|
|
75
|
+
curl -sfL -m 30 "$INPUT" 2>/dev/null \
|
|
76
|
+
| perl -0pe 's/<script\b.*?<\/script>//gs; s/<style\b.*?<\/style>//gs; s/<[^>]+>/ /gs' \
|
|
77
|
+
| tr -s ' \t\n' ' ' > "$TEXT" || { echo "kit-digest: fetch failed: $INPUT" >&2; exit 1; }
|
|
78
|
+
else
|
|
79
|
+
[[ -f "$INPUT" ]] || { echo "kit-digest: file not found: $INPUT" >&2; exit 1; }
|
|
80
|
+
case "$INPUT" in
|
|
81
|
+
*.vtt) vtt_to_text < "$INPUT" > "$TEXT" ;;
|
|
82
|
+
*) cat "$INPUT" > "$TEXT" ;;
|
|
83
|
+
esac
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
WORDS=$(wc -w < "$TEXT" | tr -d ' ')
|
|
87
|
+
[[ "${WORDS:-0}" -eq 0 ]] && { echo "kit-digest: empty input" >&2; exit 1; }
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# Map (digest per ~2500-word chunk) -> reduce (merge)
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
CHUNK_WORDS=2500
|
|
93
|
+
if [[ "$LANG_OUT" == "en" ]]; then
|
|
94
|
+
SYS_MAP="You summarize for an engineering audience. Extract the key points, decisions, numbers and names from this fragment as tight bullets. No intro, no closing."
|
|
95
|
+
SYS_REDUCE="Merge these partial digests into ONE digest of at most 1500 tokens: tight bullets grouped by theme, keep concrete numbers/names/refs. No intro, no closing."
|
|
96
|
+
else
|
|
97
|
+
SYS_MAP="Resumes para una audiencia de ingenieria. Extrae los puntos clave, decisiones, numeros y nombres de este fragmento en bullets apretados. Sin intro ni cierre."
|
|
98
|
+
SYS_REDUCE="Fusiona estos digests parciales en UN digest de maximo 1500 tokens: bullets apretados agrupados por tema, conserva numeros/nombres/refs concretos. Sin intro ni cierre."
|
|
99
|
+
fi
|
|
100
|
+
[[ -n "$FOCUS" ]] && SYS_MAP="$SYS_MAP Prioriza todo lo relacionado con: $FOCUS."
|
|
101
|
+
|
|
102
|
+
# One chunk per line, CHUNK_WORDS words each (awk: xargs would choke on quotes).
|
|
103
|
+
awk -v n="$CHUNK_WORDS" '{for(i=1;i<=NF;i++){printf "%s%s",$i,(++c%n==0?"\n":" ")}}END{if(c%n!=0)print ""}' \
|
|
104
|
+
"$TEXT" > "$WORK/wrapped.txt"
|
|
105
|
+
split -l 1 "$WORK/wrapped.txt" "$WORK/chunk." 2>/dev/null
|
|
106
|
+
|
|
107
|
+
PARTS=()
|
|
108
|
+
for c in "$WORK"/chunk.*; do
|
|
109
|
+
[[ -s "$c" ]] || continue
|
|
110
|
+
part="$(kit_local_chat "$SYS_MAP" "$(cat "$c")" 700)" || { echo "kit-digest: local chat failed mid-run" >&2; exit 2; }
|
|
111
|
+
PARTS+=("$part")
|
|
112
|
+
done
|
|
113
|
+
|
|
114
|
+
if [[ ${#PARTS[@]} -eq 1 ]]; then
|
|
115
|
+
DIGEST="${PARTS[0]}"
|
|
116
|
+
else
|
|
117
|
+
ALL="$(printf '%s\n\n' "${PARTS[@]}")"
|
|
118
|
+
DIGEST="$(kit_local_chat "$SYS_REDUCE" "$ALL" 1500)" || DIGEST="$ALL"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
echo "# Digest (local: $(kit_local_model_tag)) — $SOURCE_DESC"
|
|
122
|
+
echo
|
|
123
|
+
printf '%s\n' "$DIGEST"
|
|
124
|
+
echo
|
|
125
|
+
echo "---"
|
|
126
|
+
echo "Original: $INPUT ($WORDS palabras, ${#PARTS[@]} chunk(s)) — para deep-dive selectivo, lee el original."
|