@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,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-export-project-test.sh — self-test for kit-export-project (#376). Runs under bash AND zsh.
|
|
3
|
+
# Run: bash scripts/kit-export-project-test.sh
|
|
4
|
+
PLUGIN_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
5
|
+
|
|
6
|
+
if [ -n "${KIT_EXPORT_TEST_INNER:-}" ]; then
|
|
7
|
+
set -u
|
|
8
|
+
fail=0
|
|
9
|
+
t() { if [ "$2" != "$3" ]; then echo "FAIL($KIT_EXPORT_TEST_INNER): $1 -> got '[$2]' want '[$3]'"; fail=1; else echo "ok($KIT_EXPORT_TEST_INNER): $1"; fi; }
|
|
10
|
+
|
|
11
|
+
proj="$(mktemp -d)"; trap 'rm -rf "$proj"' EXIT
|
|
12
|
+
cd "$proj"
|
|
13
|
+
. "$PLUGIN_DIR/scripts/kit-export-project.sh"
|
|
14
|
+
. "$PLUGIN_DIR/scripts/lib/kit-manifest.sh"
|
|
15
|
+
|
|
16
|
+
# A kit project: CLAUDE.md importing a tier-A rule + a tier-B hook; a knowledge corpus.
|
|
17
|
+
mkdir -p .claude/rules .claude/hooks .claude/agents knowledge
|
|
18
|
+
printf '# My Project\n\nRules:\n@.claude/rules/branch-naming.md\n\nHooks:\n@.claude/hooks/guard.sh\n' > CLAUDE.md
|
|
19
|
+
printf 'BRANCH RULE BODY\n' > .claude/rules/branch-naming.md
|
|
20
|
+
printf '#!/bin/sh\nguard\n' > .claude/hooks/guard.sh
|
|
21
|
+
printf 'AGENT BODY\n' > .claude/agents/pm.md
|
|
22
|
+
printf 'KNOWLEDGE DOC\n' > knowledge/doc.md
|
|
23
|
+
|
|
24
|
+
# Tier classification: rule = A, hook = B (heuristic, no manifest yet).
|
|
25
|
+
t "rule is tier A" "$(kit_export_tier .claude/rules/branch-naming.md)" "A"
|
|
26
|
+
t "hook is tier B" "$(kit_export_tier .claude/hooks/guard.sh)" "B"
|
|
27
|
+
|
|
28
|
+
# --verify: a tier-B import that CLAUDE.md requires is a portability defect -> rc 1.
|
|
29
|
+
kit_export_verify CLAUDE.md >/dev/null 2>&1
|
|
30
|
+
t "verify flags tier-B require (rc 1)" "$?" "1"
|
|
31
|
+
|
|
32
|
+
# Flattened instructions: inline the tier-A rule body, SKIP the tier-B hook (note it).
|
|
33
|
+
flat="$(kit_export_flatten_instructions CLAUDE.md)"
|
|
34
|
+
t "inlines tier-A rule body" "$(printf '%s' "$flat" | grep -c 'BRANCH RULE BODY')" "1"
|
|
35
|
+
t "marks tier-A inline" "$(printf '%s' "$flat" | grep -c 'tier A')" "1"
|
|
36
|
+
t "skips tier-B hook body" "$(printf '%s' "$flat" | grep -c 'guard$')" "0"
|
|
37
|
+
t "notes tier-B skip" "$(printf '%s' "$flat" | grep -c 'tier B')" "1"
|
|
38
|
+
t "keeps non-import prose" "$(printf '%s' "$flat" | grep -c 'My Project')" "1"
|
|
39
|
+
|
|
40
|
+
# dry-run writes nothing.
|
|
41
|
+
KIT_DRY_RUN=1 kit_export_all .kit-export knowledge >/dev/null 2>&1
|
|
42
|
+
t "dry-run writes no out dir" "$([ -d .kit-export ] && echo yes || echo no)" "no"
|
|
43
|
+
|
|
44
|
+
# real export: instructions + knowledge copy + matrix all present; knowledge content carried.
|
|
45
|
+
kit_export_all .kit-export knowledge >/dev/null 2>&1
|
|
46
|
+
t "instructions written" "$([ -f .kit-export/claude-instructions.md ] && echo yes || echo no)" "yes"
|
|
47
|
+
t "matrix written" "$([ -f .kit-export/SUPPORT-MATRIX.md ] && echo yes || echo no)" "yes"
|
|
48
|
+
t "knowledge copied" "$([ -f .kit-export/project-knowledge/knowledge/doc.md ] && echo yes || echo no)" "yes"
|
|
49
|
+
t "tier-A agents copied" "$([ -f .kit-export/project-knowledge/agents/pm.md ] && echo yes || echo no)" "yes"
|
|
50
|
+
t "tier-B hook NOT in instructions" "$(grep -c 'guard$' .kit-export/claude-instructions.md)" "0"
|
|
51
|
+
|
|
52
|
+
# matrix has the three surfaces.
|
|
53
|
+
m="$(kit_export_matrix)"
|
|
54
|
+
t "matrix names claude.ai" "$(printf '%s' "$m" | grep -qi 'claude.ai' && echo yes || echo no)" "yes"
|
|
55
|
+
t "matrix names Cowork" "$(printf '%s' "$m" | grep -qi 'Cowork' && echo yes || echo no)" "yes"
|
|
56
|
+
t "matrix names Terminal" "$(printf '%s' "$m" | grep -qi 'Terminal' && echo yes || echo no)" "yes"
|
|
57
|
+
|
|
58
|
+
# Now make the hook tier-A via the manifest: --verify should pass (portable).
|
|
59
|
+
kit_manifest_record .claude/rules/branch-naming.md A wire >/dev/null 2>&1
|
|
60
|
+
kit_manifest_record .claude/hooks/guard.sh A wire >/dev/null 2>&1 # force A to simulate a portable file
|
|
61
|
+
t "manifest tier wins over heuristic" "$(kit_export_tier .claude/hooks/guard.sh)" "A"
|
|
62
|
+
kit_export_verify CLAUDE.md >/dev/null 2>&1
|
|
63
|
+
t "verify passes when all imports tier-A (rc 0)" "$?" "0"
|
|
64
|
+
|
|
65
|
+
# A missing import is a defect on every surface.
|
|
66
|
+
printf '\n@.claude/rules/gone.md\n' >> CLAUDE.md
|
|
67
|
+
kit_export_verify CLAUDE.md >/dev/null 2>&1
|
|
68
|
+
t "verify flags a missing import (rc 1)" "$?" "1"
|
|
69
|
+
|
|
70
|
+
[ "$fail" -eq 0 ] && echo "ALL OK($KIT_EXPORT_TEST_INNER)"
|
|
71
|
+
exit "$fail"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
rc=0; ran=0
|
|
75
|
+
for sh in bash zsh; do
|
|
76
|
+
command -v "$sh" >/dev/null 2>&1 || continue
|
|
77
|
+
ran=$((ran+1)); echo "--- $sh ---"
|
|
78
|
+
rcflag=""; [ "$sh" = "zsh" ] && rcflag="--no-rcs"
|
|
79
|
+
KIT_EXPORT_TEST_INNER="$sh" PATH="$PATH" PLUGIN_DIR="$PLUGIN_DIR" "$sh" $rcflag "$0" || rc=1
|
|
80
|
+
done
|
|
81
|
+
[ "$ran" -eq 0 ] && { echo "no shell"; exit 1; }
|
|
82
|
+
exit "$rc"
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-export-project.sh — export a kit project to the two non-terminal Claude surfaces (#376).
|
|
3
|
+
#
|
|
4
|
+
# THE GOAL (the acceptance gate): the SAME project runs in three places with NO hand edits —
|
|
5
|
+
# 1. terminal — Claude Code reads CLAUDE.md + .claude/ natively (the home surface).
|
|
6
|
+
# 2. Cowork — Claude Code in the cloud; reads the same CLAUDE.md + .claude/ tree natively.
|
|
7
|
+
# 3. claude.ai — a Project has TWO slots (no .claude/ tree): "custom instructions" (one text box)
|
|
8
|
+
# + "project knowledge" (uploaded files). It cannot run scripts.
|
|
9
|
+
#
|
|
10
|
+
# Terminal + Cowork need NOTHING from this tool — they read the repo as-is. The only surface that
|
|
11
|
+
# needs a transform is claude.ai, because it has no filesystem: we FLATTEN CLAUDE.md (resolving its
|
|
12
|
+
# @-imports of .claude/rules/*) into ONE instructions file, and COPY the knowledge corpus into an
|
|
13
|
+
# upload-ready folder. We also VERIFY Cowork compat (every file the project leans on is tier-A
|
|
14
|
+
# portable, not a tier-B CLI-only shim) and print a support matrix.
|
|
15
|
+
#
|
|
16
|
+
# TIER MODEL (#373, recorded in .claude/kit.manifest.json):
|
|
17
|
+
# tier A = portable — skills/rules/agents/commands; meaningful in Cowork AND claude.ai.
|
|
18
|
+
# tier B = CLI-only — statusline.sh / settings.json / hooks/ / lib/ ; need a terminal+filesystem.
|
|
19
|
+
# The export carries ONLY tier-A semantics to claude.ai; a tier-B file the project REQUIRES to
|
|
20
|
+
# function is a portability defect we flag (it would silently no-op off-terminal).
|
|
21
|
+
#
|
|
22
|
+
# OUTPUT (default .kit-export/, override with --out DIR):
|
|
23
|
+
# claude-instructions.md the flattened CLAUDE.md + inlined @.claude/rules/* — paste into the
|
|
24
|
+
# claude.ai Project's "custom instructions" box.
|
|
25
|
+
# project-knowledge/ knowledge/** (+ agents/ + rules/ as reference) — upload as Project knowledge.
|
|
26
|
+
# SUPPORT-MATRIX.md terminal / Cowork / claude.ai feature support, generated from the manifest.
|
|
27
|
+
#
|
|
28
|
+
# Run:
|
|
29
|
+
# scripts/kit-export-project.sh # full export to .kit-export/
|
|
30
|
+
# scripts/kit-export-project.sh --out build/x # custom output dir
|
|
31
|
+
# scripts/kit-export-project.sh --verify # Cowork+claude.ai compat check only, write nothing (rc 1 on defect)
|
|
32
|
+
# scripts/kit-export-project.sh --matrix # print the support matrix to stdout, write nothing
|
|
33
|
+
# scripts/kit-export-project.sh --dry-run # report what it would write, write nothing
|
|
34
|
+
# Requires: jq + a sha256 tool (for tier lookups via the manifest).
|
|
35
|
+
|
|
36
|
+
set -uo pipefail
|
|
37
|
+
_export_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
38
|
+
# shellcheck source=/dev/null
|
|
39
|
+
. "$_export_dir/lib/kit-cli.sh" # kit_is_main
|
|
40
|
+
# shellcheck source=/dev/null
|
|
41
|
+
. "$_export_dir/lib/kit-manifest.sh" # tier lookups
|
|
42
|
+
|
|
43
|
+
_kex_say() { printf '%s\n' "$*" >&2; }
|
|
44
|
+
|
|
45
|
+
# Tier-classify a kit-shaped path the same way kit-wire/kit-adopt do — manifest first (authoritative,
|
|
46
|
+
# carries the tier the file was wired with), heuristic fallback when the file isn't manifest-tracked
|
|
47
|
+
# (e.g. a hand-imported project that never ran kit-adopt).
|
|
48
|
+
kit_export_tier() {
|
|
49
|
+
local p="$1" t
|
|
50
|
+
t="$(kit_manifest_get "$p" 2>/dev/null | jq -r '.tier // empty' 2>/dev/null || true)"
|
|
51
|
+
if [ -n "$t" ]; then printf '%s\n' "$t"; return 0; fi
|
|
52
|
+
case "$p" in
|
|
53
|
+
.claude/statusline.sh|.claude/settings.json|.claude/settings.local.json|.claude/hooks/*|.claude/lib/*) printf 'B\n' ;;
|
|
54
|
+
*) printf 'A\n' ;;
|
|
55
|
+
esac
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Resolve a Claude `@path` import to a relpath. Claude Code imports are written `@.claude/rules/x.md`
|
|
59
|
+
# or `@./KIT.md`; strip the leading @ and any ./ — return empty for a non-import line.
|
|
60
|
+
_kex_import_path() {
|
|
61
|
+
case "$1" in
|
|
62
|
+
@*) printf '%s\n' "${1#@}" | sed 's#^\./##' ;;
|
|
63
|
+
*) printf '\n' ;;
|
|
64
|
+
esac
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Flatten CLAUDE.md into a single instructions document for claude.ai. We INLINE every @-import
|
|
68
|
+
# (the .claude/rules/* and any @./KIT.md) so the one text box carries the whole instruction set —
|
|
69
|
+
# claude.ai can't follow an @ to a file it doesn't have. Imports are inlined ONCE (dedup) and only
|
|
70
|
+
# tier-A files are inlined (a tier-B shim has no meaning without a terminal). Non-import lines pass
|
|
71
|
+
# through verbatim. Recurses one level into imported rule files (they rarely chain, but we guard a
|
|
72
|
+
# seen-set so a cycle can't loop).
|
|
73
|
+
kit_export_flatten_instructions() {
|
|
74
|
+
local claude_md="${1:-CLAUDE.md}"
|
|
75
|
+
[ -f "$claude_md" ] || { _kex_say "kit-export: $claude_md not found"; return 1; }
|
|
76
|
+
|
|
77
|
+
# seen-set for dedup/cycle-guard (newline-delimited paths already inlined)
|
|
78
|
+
local seen=""
|
|
79
|
+
_kex_emit_file() { # <relpath> <depth>
|
|
80
|
+
local f="$1" depth="$2" line ip tier
|
|
81
|
+
case "$seen" in *"|$f|"*) return 0 ;; esac
|
|
82
|
+
seen="$seen|$f|"
|
|
83
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
84
|
+
ip="$(_kex_import_path "$line")"
|
|
85
|
+
if [ -n "$ip" ] && [ -f "$ip" ] && [ "$depth" -lt 4 ]; then
|
|
86
|
+
tier="$(kit_export_tier "$ip")"
|
|
87
|
+
if [ "$tier" = "A" ]; then
|
|
88
|
+
printf '\n<!-- inlined from %s (tier A) -->\n' "$ip"
|
|
89
|
+
_kex_emit_file "$ip" $((depth + 1))
|
|
90
|
+
else
|
|
91
|
+
printf '\n<!-- skipped %s (tier B — CLI-only, no meaning on claude.ai) -->\n' "$ip"
|
|
92
|
+
fi
|
|
93
|
+
else
|
|
94
|
+
printf '%s\n' "$line"
|
|
95
|
+
fi
|
|
96
|
+
done < "$f"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
printf '<!-- Generated by kit-export-project (#376) — paste into the claude.ai Project custom-instructions box. -->\n'
|
|
100
|
+
printf '<!-- Source: %s + inlined @.claude/rules/* (tier-A). Terminal/Cowork read the repo directly. -->\n\n' "$claude_md"
|
|
101
|
+
_kex_emit_file "$claude_md" 0
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Cowork + claude.ai compat verdict. Cowork runs Claude Code natively, so the question is: does the
|
|
105
|
+
# project FUNCTION when only the portable (tier-A) surface is honored? Any file CLAUDE.md imports
|
|
106
|
+
# that is tier-B is a portability defect (it silently no-ops off-terminal). rc 0 = portable, 1 = defect.
|
|
107
|
+
kit_export_verify() {
|
|
108
|
+
local claude_md="${1:-CLAUDE.md}" defects=0 line ip tier
|
|
109
|
+
if [ ! -f "$claude_md" ]; then _kex_say "kit-export --verify: $claude_md not found"; return 1; fi
|
|
110
|
+
_kex_say "kit-export --verify: checking that the portable surface is self-sufficient..."
|
|
111
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
112
|
+
ip="$(_kex_import_path "$line")"
|
|
113
|
+
[ -n "$ip" ] || continue
|
|
114
|
+
if [ ! -f "$ip" ]; then
|
|
115
|
+
_kex_say " ! CLAUDE.md imports $ip but it is MISSING — broken on every surface"
|
|
116
|
+
defects=$((defects + 1)); continue
|
|
117
|
+
fi
|
|
118
|
+
tier="$(kit_export_tier "$ip")"
|
|
119
|
+
if [ "$tier" = "B" ]; then
|
|
120
|
+
_kex_say " ! CLAUDE.md requires $ip (tier B / CLI-only) — silently no-ops in Cowork/claude.ai"
|
|
121
|
+
defects=$((defects + 1))
|
|
122
|
+
else
|
|
123
|
+
_kex_say " ok $ip (tier A — portable)"
|
|
124
|
+
fi
|
|
125
|
+
done < "$claude_md"
|
|
126
|
+
if [ "$defects" -eq 0 ]; then
|
|
127
|
+
_kex_say "kit-export --verify: PORTABLE — the same project runs in terminal / Cowork / claude.ai with no hand edits."
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
130
|
+
_kex_say "kit-export --verify: $defects portability defect(s) — fix before relying on the non-terminal surfaces."
|
|
131
|
+
return 1
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# The support matrix — what each surface can do, generated from the manifest tiers present.
|
|
135
|
+
kit_export_matrix() {
|
|
136
|
+
local a_count b_count
|
|
137
|
+
a_count="$(kit_manifest_list A 2>/dev/null | grep -c . || true)"
|
|
138
|
+
b_count="$(kit_manifest_list B 2>/dev/null | grep -c . || true)"
|
|
139
|
+
cat <<MATRIX
|
|
140
|
+
# claude-kit surface support matrix
|
|
141
|
+
|
|
142
|
+
Generated by kit-export-project (#376). The kit project runs on three Claude surfaces; this table
|
|
143
|
+
records what each supports and how the project gets there.
|
|
144
|
+
|
|
145
|
+
| Capability | Terminal (Claude Code) | Cowork (cloud Claude Code) | claude.ai Project |
|
|
146
|
+
| ----------------------------------- | :--------------------: | :------------------------: | :---------------: |
|
|
147
|
+
| Reads CLAUDE.md + .claude/ natively | yes | yes | no (no FS) |
|
|
148
|
+
| Tier-A skills / rules / agents | yes | yes | via export* |
|
|
149
|
+
| Tier-B statusline / hooks / settings| yes | partial** | no |
|
|
150
|
+
| Runs \`kit\` / \`scripts/*\` verbs | yes | yes | no (can't shell) |
|
|
151
|
+
| GitHub lifecycle (gh, worktrees) | yes | yes | no |
|
|
152
|
+
| Knowledge corpus available | in repo | in repo | via export* |
|
|
153
|
+
|
|
154
|
+
\* claude.ai has no filesystem: run \`kit-export-project\` → paste \`claude-instructions.md\` into the
|
|
155
|
+
Project custom-instructions box and upload \`project-knowledge/\` as Project knowledge.
|
|
156
|
+
\*\* Cowork runs Claude Code but a shim like the statusline / a guard hook may be inert in that
|
|
157
|
+
environment; the PROJECT still functions because tier-B files are conveniences, not requirements
|
|
158
|
+
(verified by \`kit-export-project --verify\`).
|
|
159
|
+
|
|
160
|
+
## This project's manifest tiering
|
|
161
|
+
- Tier A (portable): ${a_count} file(s)
|
|
162
|
+
- Tier B (CLI-only): ${b_count} file(s)
|
|
163
|
+
|
|
164
|
+
Tier A = portable to Cowork AND claude.ai. Tier B = needs a terminal + filesystem. The export
|
|
165
|
+
carries only tier-A semantics off-terminal; a tier-B file the project *requires* is a defect
|
|
166
|
+
\`--verify\` flags.
|
|
167
|
+
MATRIX
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Copy the knowledge corpus + portable reference files into an upload-ready folder for claude.ai.
|
|
171
|
+
# Honors KIT_DRY_RUN. Knowledge dir is config-resolved (defaults to knowledge/).
|
|
172
|
+
kit_export_knowledge() {
|
|
173
|
+
local out="$1" kdir="${2:-knowledge}" copied=0
|
|
174
|
+
local dest="$out/project-knowledge"
|
|
175
|
+
if [ -n "${KIT_DRY_RUN:-}" ]; then
|
|
176
|
+
[ -d "$kdir" ] && _kex_say " [dry-run] would copy $kdir/** -> $dest/"
|
|
177
|
+
_kex_say " [dry-run] would copy .claude/rules/ + .claude/agents/ (tier-A reference) -> $dest/"
|
|
178
|
+
return 0
|
|
179
|
+
fi
|
|
180
|
+
mkdir -p "$dest"
|
|
181
|
+
if [ -d "$kdir" ]; then
|
|
182
|
+
# -R preserves the knowledge/ tree under the dest; portable on bash + zsh, macOS + linux cp.
|
|
183
|
+
cp -R "$kdir" "$dest/" && copied=1
|
|
184
|
+
fi
|
|
185
|
+
# Carry tier-A reference material (rules + agents) so a claude.ai Project has the same context.
|
|
186
|
+
[ -d .claude/rules ] && cp -R .claude/rules "$dest/" 2>/dev/null && copied=1
|
|
187
|
+
[ -d .claude/agents ] && cp -R .claude/agents "$dest/" 2>/dev/null && copied=1
|
|
188
|
+
if [ "$copied" -eq 1 ]; then
|
|
189
|
+
_kex_say " knowledge + tier-A reference copied -> $dest/"
|
|
190
|
+
else
|
|
191
|
+
_kex_say " (no knowledge/ or tier-A reference found to copy)"
|
|
192
|
+
fi
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
# Full export: instructions + knowledge + matrix into OUT.
|
|
196
|
+
kit_export_all() {
|
|
197
|
+
local out="$1" kdir="${2:-knowledge}"
|
|
198
|
+
command -v jq >/dev/null 2>&1 || { _kex_say "kit-export: jq required"; return 1; }
|
|
199
|
+
[ -f CLAUDE.md ] || { _kex_say "kit-export: no CLAUDE.md here — run from the project root (or /kit-init first)."; return 1; }
|
|
200
|
+
|
|
201
|
+
_kex_say "kit-export: exporting to $out/ (terminal/Cowork read the repo directly; this is for claude.ai)"
|
|
202
|
+
|
|
203
|
+
if [ -n "${KIT_DRY_RUN:-}" ]; then
|
|
204
|
+
_kex_say " [dry-run] would write $out/claude-instructions.md (flattened CLAUDE.md + tier-A rules)"
|
|
205
|
+
kit_export_knowledge "$out" "$kdir"
|
|
206
|
+
_kex_say " [dry-run] would write $out/SUPPORT-MATRIX.md"
|
|
207
|
+
return 0
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
mkdir -p "$out"
|
|
211
|
+
kit_export_flatten_instructions CLAUDE.md > "$out/claude-instructions.md" \
|
|
212
|
+
&& _kex_say " wrote $out/claude-instructions.md"
|
|
213
|
+
kit_export_knowledge "$out" "$kdir"
|
|
214
|
+
kit_export_matrix > "$out/SUPPORT-MATRIX.md" && _kex_say " wrote $out/SUPPORT-MATRIX.md"
|
|
215
|
+
|
|
216
|
+
_kex_say "kit-export: done."
|
|
217
|
+
_kex_say " -> paste $out/claude-instructions.md into the claude.ai Project custom-instructions box"
|
|
218
|
+
_kex_say " -> upload $out/project-knowledge/ as the Project's knowledge"
|
|
219
|
+
# A portability nudge: report (don't fail) if the verify check finds defects.
|
|
220
|
+
kit_export_verify CLAUDE.md >/dev/null 2>&1 || _kex_say " note: run --verify — the portable surface has defect(s)."
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# CLI (direct execution only; sourcing exposes the functions for tests)
|
|
224
|
+
if kit_is_main; then
|
|
225
|
+
_mode=export _out=".kit-export" _kdir="knowledge"
|
|
226
|
+
# config-resolve the knowledge dir if available (kit-config.sh ships alongside in scripts/lib)
|
|
227
|
+
if [ -f "$_export_dir/lib/kit-config.sh" ]; then
|
|
228
|
+
# shellcheck source=/dev/null
|
|
229
|
+
. "$_export_dir/lib/kit-config.sh"
|
|
230
|
+
load_kit_config >/dev/null 2>&1 && _kdir="${KIT_KNOWLEDGE_DIR:-knowledge}" || true
|
|
231
|
+
fi
|
|
232
|
+
while [ $# -gt 0 ]; do case "$1" in
|
|
233
|
+
--out) _out="${2:?--out needs a dir}"; shift 2;;
|
|
234
|
+
--verify) _mode=verify; shift;;
|
|
235
|
+
--matrix) _mode=matrix; shift;;
|
|
236
|
+
--dry-run) export KIT_DRY_RUN=1; shift;;
|
|
237
|
+
-h|--help) sed -n '/^# Run:/,/^# Requires:/p' "$0" | sed 's/^# \{0,1\}//'; exit 0;;
|
|
238
|
+
*) echo "unknown arg: $1" >&2; exit 2;;
|
|
239
|
+
esac; done
|
|
240
|
+
case "$_mode" in
|
|
241
|
+
verify) kit_export_verify CLAUDE.md;;
|
|
242
|
+
matrix) kit_export_matrix;;
|
|
243
|
+
*) kit_export_all "$_out" "$_kdir";;
|
|
244
|
+
esac
|
|
245
|
+
fi
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-export-training-test.sh — self-test for kit-export-training (#881). Runs under bash AND zsh.
|
|
3
|
+
# Proves the dataset-builder path is config-driven (NOT hardcoded): --builder > env > sibling discovery.
|
|
4
|
+
# Run: bash scripts/kit-export-training-test.sh
|
|
5
|
+
PLUGIN_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
6
|
+
|
|
7
|
+
if [ -n "${KIT_XT_TEST_INNER:-}" ]; then
|
|
8
|
+
set -u
|
|
9
|
+
fail=0
|
|
10
|
+
t() { if [ "$2" != "$3" ]; then echo "FAIL($KIT_XT_TEST_INNER): $1 -> got '[$2]' want '[$3]'"; fail=1; else echo "ok($KIT_XT_TEST_INNER): $1"; fi; }
|
|
11
|
+
|
|
12
|
+
# Source the script: exposes functions, never runs main (verified by the source guard).
|
|
13
|
+
. "$PLUGIN_DIR/scripts/kit-export-training.sh"
|
|
14
|
+
t "resolver is defined" "$(command -v _kxt_resolve_builder >/dev/null && echo yes || echo no)" "yes"
|
|
15
|
+
|
|
16
|
+
# 1) explicit --builder value wins over everything.
|
|
17
|
+
KIT_DATASET_BUILDER="/env/build_dataset.py" \
|
|
18
|
+
t "explicit --builder wins" "$(_kxt_resolve_builder /explicit/build_dataset.py)" "/explicit/build_dataset.py"
|
|
19
|
+
|
|
20
|
+
# 2) KIT_DATASET_BUILDER env is used when no explicit value.
|
|
21
|
+
t "env var used when no flag" "$(KIT_DATASET_BUILDER=/env/build_dataset.py _kxt_resolve_builder '')" "/env/build_dataset.py"
|
|
22
|
+
|
|
23
|
+
# 3) sibling-repo discovery: a chat-datasets/build_dataset.py beside the git repo root is found,
|
|
24
|
+
# with NO hardcoded user path. Build a fake parent dir holding a repo + a sibling builder.
|
|
25
|
+
parent="$(mktemp -d)"; trap 'rm -rf "$parent"' EXIT
|
|
26
|
+
parent="$(cd "$parent" && pwd -P)" # canonicalize (macOS /var -> /private/var) to match git rev-parse
|
|
27
|
+
mkdir -p "$parent/myrepo" "$parent/chat-datasets"
|
|
28
|
+
( cd "$parent/myrepo" && git init -q )
|
|
29
|
+
printf '#!/usr/bin/env python3\n' > "$parent/chat-datasets/build_dataset.py"
|
|
30
|
+
got="$(cd "$parent/myrepo" && unset KIT_DATASET_BUILDER 2>/dev/null; _kxt_resolve_builder '')"
|
|
31
|
+
t "sibling discovery off repo root" "$got" "$parent/chat-datasets/build_dataset.py"
|
|
32
|
+
|
|
33
|
+
# 4) returns non-zero when nothing resolves (no flag, no env, no sibling).
|
|
34
|
+
empty="$(mktemp -d)"; mkdir -p "$empty/solo"; ( cd "$empty/solo" && git init -q )
|
|
35
|
+
( cd "$empty/solo" && unset KIT_DATASET_BUILDER 2>/dev/null; _kxt_resolve_builder '' >/dev/null 2>&1 )
|
|
36
|
+
t "fails closed when unresolved (rc 1)" "$?" "1"
|
|
37
|
+
rm -rf "$empty"
|
|
38
|
+
|
|
39
|
+
[ "$fail" -eq 0 ] && echo "ALL OK($KIT_XT_TEST_INNER)"
|
|
40
|
+
exit "$fail"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
rc=0; ran=0
|
|
44
|
+
for sh in bash zsh; do
|
|
45
|
+
command -v "$sh" >/dev/null 2>&1 || continue
|
|
46
|
+
ran=$((ran+1)); echo "--- $sh ---"
|
|
47
|
+
rcflag=""; [ "$sh" = "zsh" ] && rcflag="--no-rcs"
|
|
48
|
+
KIT_XT_TEST_INNER="$sh" PATH="$PATH" PLUGIN_DIR="$PLUGIN_DIR" "$sh" $rcflag "$0" || rc=1
|
|
49
|
+
done
|
|
50
|
+
[ "$ran" -eq 0 ] && { echo "no shell"; exit 1; }
|
|
51
|
+
exit "$rc"
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-export-training.sh — OPT-IN export of your Claude Code sessions to redacted training JSONL.
|
|
3
|
+
#
|
|
4
|
+
# Fine-tuning a local model on your own work only helps if the data leaves the machine clean. This
|
|
5
|
+
# handler is the consent gate in front of a dataset builder (chat-datasets/build_dataset.py): it
|
|
6
|
+
# lets a builder pick which sessions to export, ALWAYS runs the builder with redaction ON
|
|
7
|
+
# (secret/key/email masking is the builder's default — there is deliberately NO --no-redact
|
|
8
|
+
# passthrough here), and prints exactly where it wrote plus a reminder that redaction must be
|
|
9
|
+
# verified before sharing.
|
|
10
|
+
#
|
|
11
|
+
# Nothing here is automatic — it runs only when explicitly invoked, and it never uploads anything.
|
|
12
|
+
# The output is plain JSONL on disk for the training pipeline; what you do with it is your call.
|
|
13
|
+
#
|
|
14
|
+
# Run:
|
|
15
|
+
# scripts/kit-export-training.sh [--builder PATH] [--match KEY ...] [--dirs DIR ...]
|
|
16
|
+
# [--sessions FILE ...] [--out PATH] [--split FRAC]
|
|
17
|
+
# [--final-only] [--drop-narration]
|
|
18
|
+
# Selection (pick one; default = the current git project, matched by its slug):
|
|
19
|
+
# --match KEY ... project-dir name substrings under ~/.claude/projects (e.g. my-app acme)
|
|
20
|
+
# --dirs DIR ... explicit ~/.claude/projects/<dir> session directories
|
|
21
|
+
# --sessions FILE ... individual *.jsonl transcript files to export (symlinked into a temp dir)
|
|
22
|
+
# Options:
|
|
23
|
+
# --builder PATH path to build_dataset.py (overrides KIT_DATASET_BUILDER + auto-discovery)
|
|
24
|
+
# --out PATH output JSONL (default: ~/.claude/exports/<slug>-training.jsonl)
|
|
25
|
+
# --split FRAC also emit train.jsonl/valid.jsonl with FRAC held out (e.g. 0.1)
|
|
26
|
+
# --final-only keep only the last assistant message per turn
|
|
27
|
+
# --drop-narration drop short process-narration openers
|
|
28
|
+
# Env:
|
|
29
|
+
# KIT_DATASET_BUILDER path to build_dataset.py (used when --builder is not given)
|
|
30
|
+
# KIT_PROJECTS_ROOT sessions root (default: ~/.claude/projects)
|
|
31
|
+
# KIT_ASSUME_YES skip the final confirm (for batch/CI).
|
|
32
|
+
#
|
|
33
|
+
# Builder discovery (config-driven, NO hardcoded user path): --builder > KIT_DATASET_BUILDER >
|
|
34
|
+
# a sibling `chat-datasets/build_dataset.py` next to the git repo root (then $PWD). If none resolve,
|
|
35
|
+
# the script stops and tells you to pass --builder or set KIT_DATASET_BUILDER.
|
|
36
|
+
#
|
|
37
|
+
# Redaction is ALWAYS on here — the builder masks by default and this script never passes --no-redact.
|
|
38
|
+
set -uo pipefail
|
|
39
|
+
|
|
40
|
+
# ---- locate self + optional shared cli helpers ----------------------------
|
|
41
|
+
_kxt_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
|
42
|
+
# shellcheck source=/dev/null
|
|
43
|
+
[ -f "$_kxt_dir/lib/kit-cli.sh" ] && . "$_kxt_dir/lib/kit-cli.sh" # provides kit_is_main (optional)
|
|
44
|
+
|
|
45
|
+
PROJECTS_ROOT="${KIT_PROJECTS_ROOT:-$HOME/.claude/projects}"
|
|
46
|
+
|
|
47
|
+
# Staged-sessions temp dir, cleaned on exit. Script-global so the EXIT trap can read it
|
|
48
|
+
# after _kxt_main returns (a function-local would be out of scope under `set -u`).
|
|
49
|
+
_KXT_TMP_SEL=""
|
|
50
|
+
_kxt_cleanup() { [ -n "${_KXT_TMP_SEL:-}" ] && rm -rf "$_KXT_TMP_SEL"; return 0; }
|
|
51
|
+
|
|
52
|
+
# ---- minimal logging ------------------------------------------------------
|
|
53
|
+
if [ -t 2 ]; then _AZ=$'\033[38;2;201;122;44m'; _RD=$'\033[38;2;194;87;56m'; _GR=$'\033[38;2;120;160;90m'; _DM=$'\033[2m'; _RS=$'\033[0m'
|
|
54
|
+
else _AZ=''; _RD=''; _GR=''; _DM=''; _RS=''; fi
|
|
55
|
+
_kxt_say() { printf '%s->%s %s\n' "$_AZ" "$_RS" "$*" >&2; }
|
|
56
|
+
_kxt_warn() { printf '%s!%s %s\n' "$_RD" "$_RS" "$*" >&2; }
|
|
57
|
+
_kxt_err() { printf '%sx%s %s\n' "$_RD" "$_RS" "$*" >&2; }
|
|
58
|
+
_kxt_die() { _kxt_err "$*"; exit 1; }
|
|
59
|
+
|
|
60
|
+
# ---- builder resolution (config-driven, generalized off any one machine) ---
|
|
61
|
+
# $1 = explicit --builder value (may be empty). Priority: explicit > env > sibling discovery.
|
|
62
|
+
_kxt_resolve_builder() {
|
|
63
|
+
local explicit="${1:-}"
|
|
64
|
+
[ -n "$explicit" ] && { printf '%s\n' "$explicit"; return 0; }
|
|
65
|
+
[ -n "${KIT_DATASET_BUILDER:-}" ] && { printf '%s\n' "$KIT_DATASET_BUILDER"; return 0; }
|
|
66
|
+
local rel="chat-datasets/build_dataset.py" root="" rootp="" base="" cand=""
|
|
67
|
+
root="$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)"
|
|
68
|
+
[ -n "$root" ] && rootp="$(dirname "$root")"
|
|
69
|
+
for base in "$rootp" "$PWD" "$(dirname "$PWD")"; do
|
|
70
|
+
[ -n "$base" ] || continue
|
|
71
|
+
cand="$base/$rel"
|
|
72
|
+
[ -f "$cand" ] && { printf '%s\n' "$cand"; return 0; }
|
|
73
|
+
done
|
|
74
|
+
return 1
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# ---- main (direct execution only; sourcing exposes the functions for tests) ---
|
|
78
|
+
_kxt_main() {
|
|
79
|
+
local BUILDER_FLAG="" OUT="" SPLIT="" SLUG="export" BUILDER="" _root=""
|
|
80
|
+
local -a MATCH=() DIRS=() SESSIONS=() EXTRA=() SEL=() SPLIT_ARGS=()
|
|
81
|
+
while [ $# -gt 0 ]; do case "$1" in
|
|
82
|
+
--builder) BUILDER_FLAG="${2:-}"; shift 2 ;;
|
|
83
|
+
--match) shift; while [ $# -gt 0 ] && [ "${1#--}" = "$1" ]; do MATCH+=("$1"); shift; done ;;
|
|
84
|
+
--dirs) shift; while [ $# -gt 0 ] && [ "${1#--}" = "$1" ]; do DIRS+=("$1"); shift; done ;;
|
|
85
|
+
--sessions) shift; while [ $# -gt 0 ] && [ "${1#--}" = "$1" ]; do SESSIONS+=("$1"); shift; done ;;
|
|
86
|
+
--out) OUT="${2:-}"; shift 2 ;;
|
|
87
|
+
--split) SPLIT="${2:-}"; shift 2 ;;
|
|
88
|
+
--final-only) EXTRA+=(--final-only); shift ;;
|
|
89
|
+
--drop-narration) EXTRA+=(--drop-narration); shift ;;
|
|
90
|
+
-h|--help) sed -n '13,33p' "$0" | sed 's/^# \{0,1\}//'; return 0 ;;
|
|
91
|
+
*) _kxt_die "unknown flag: $1" ;;
|
|
92
|
+
esac; done
|
|
93
|
+
|
|
94
|
+
# ---- preflight ----------------------------------------------------------
|
|
95
|
+
command -v python3 >/dev/null 2>&1 || _kxt_die "python3 is required."
|
|
96
|
+
BUILDER="$(_kxt_resolve_builder "$BUILDER_FLAG")" || _kxt_die \
|
|
97
|
+
"dataset builder not found — pass --builder PATH or set KIT_DATASET_BUILDER (looked for chat-datasets/build_dataset.py beside the repo root)."
|
|
98
|
+
[ -f "$BUILDER" ] || _kxt_die "dataset builder not found: $BUILDER"
|
|
99
|
+
|
|
100
|
+
# ---- resolve a default selection from the current git project -----------
|
|
101
|
+
if [ ${#MATCH[@]} -eq 0 ] && [ ${#DIRS[@]} -eq 0 ] && [ ${#SESSIONS[@]} -eq 0 ]; then
|
|
102
|
+
_root="$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || true)"
|
|
103
|
+
[ -n "$_root" ] || _kxt_die "no selection given and not inside a git repo — pass --match/--dirs/--sessions."
|
|
104
|
+
SLUG="$(basename "$_root" | tr '[:upper:]' '[:lower:]')"
|
|
105
|
+
MATCH=("$SLUG")
|
|
106
|
+
_kxt_say "no selection given — defaulting to the current project (match: $SLUG)"
|
|
107
|
+
elif [ ${#MATCH[@]} -gt 0 ]; then
|
|
108
|
+
SLUG="$(printf '%s' "${MATCH[0]}" | tr '[:upper:]' '[:lower:]')"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# ---- default output path ------------------------------------------------
|
|
112
|
+
if [ -z "$OUT" ]; then
|
|
113
|
+
mkdir -p "$HOME/.claude/exports"
|
|
114
|
+
OUT="$HOME/.claude/exports/${SLUG}-training.jsonl"
|
|
115
|
+
fi
|
|
116
|
+
mkdir -p "$(dirname "$OUT")"
|
|
117
|
+
|
|
118
|
+
# ---- build the selection args for the builder ---------------------------
|
|
119
|
+
# --sessions: stage the chosen transcript files into a temp dir and pass via --dirs,
|
|
120
|
+
# since build_dataset.py selects at directory granularity.
|
|
121
|
+
local f=""
|
|
122
|
+
trap _kxt_cleanup EXIT
|
|
123
|
+
|
|
124
|
+
if [ ${#SESSIONS[@]} -gt 0 ]; then
|
|
125
|
+
_KXT_TMP_SEL="$(mktemp -d)"
|
|
126
|
+
for f in "${SESSIONS[@]}"; do
|
|
127
|
+
[ -f "$f" ] || _kxt_die "session file not found: $f"
|
|
128
|
+
ln -s "$(cd "$(dirname "$f")" && pwd)/$(basename "$f")" "$_KXT_TMP_SEL/$(basename "$f")"
|
|
129
|
+
done
|
|
130
|
+
SEL=(--dirs "$_KXT_TMP_SEL")
|
|
131
|
+
_kxt_say "exporting ${#SESSIONS[@]} selected session file(s)"
|
|
132
|
+
elif [ ${#DIRS[@]} -gt 0 ]; then
|
|
133
|
+
SEL=(--dirs "${DIRS[@]}")
|
|
134
|
+
_kxt_say "exporting from ${#DIRS[@]} session director(ies)"
|
|
135
|
+
else
|
|
136
|
+
SEL=(--match "${MATCH[@]}")
|
|
137
|
+
_kxt_say "exporting sessions matching: ${MATCH[*]}"
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
[ -d "$PROJECTS_ROOT" ] || _kxt_warn "sessions root $PROJECTS_ROOT not found — the export may be empty."
|
|
141
|
+
|
|
142
|
+
# ---- confirm (opt-in) ---------------------------------------------------
|
|
143
|
+
if [ -z "${KIT_ASSUME_YES:-}" ] && [ -t 0 ]; then
|
|
144
|
+
printf '%sExport redacted training JSONL to %s ? [y/N] %s' "$_DM" "$OUT" "$_RS" >&2
|
|
145
|
+
local _c=""; read -r _c < /dev/tty || true
|
|
146
|
+
case "$_c" in y|Y|yes|YES) ;; *) _kxt_die "aborted — nothing was written." ;; esac
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# ---- run the builder with redaction ON (always) -------------------------
|
|
150
|
+
[ -n "$SPLIT" ] && SPLIT_ARGS=(--split "$SPLIT")
|
|
151
|
+
_kxt_say "running build_dataset.py (redaction ON): $BUILDER"
|
|
152
|
+
python3 "$BUILDER" "${SEL[@]}" --out "$OUT" ${EXTRA[@]+"${EXTRA[@]}"} ${SPLIT_ARGS[@]+"${SPLIT_ARGS[@]}"} \
|
|
153
|
+
|| _kxt_die "build_dataset.py failed — nothing trustworthy was written."
|
|
154
|
+
|
|
155
|
+
# ---- report -------------------------------------------------------------
|
|
156
|
+
local STATS="${OUT%.jsonl}.stats.json"
|
|
157
|
+
printf '\n' >&2
|
|
158
|
+
_kxt_say "Wrote training data:"
|
|
159
|
+
printf ' %sJSONL:%s %s\n' "$_GR" "$_RS" "$OUT" >&2
|
|
160
|
+
[ -f "$STATS" ] && printf ' %sstats:%s %s\n' "$_GR" "$_RS" "$STATS" >&2
|
|
161
|
+
if [ -n "$SPLIT" ]; then
|
|
162
|
+
printf ' %ssplit:%s %s/train.jsonl + valid.jsonl\n' "$_GR" "$_RS" "$(dirname "$OUT")" >&2
|
|
163
|
+
fi
|
|
164
|
+
printf '\n' >&2
|
|
165
|
+
_kxt_warn "Redaction was applied automatically, but it is NOT a guarantee."
|
|
166
|
+
_kxt_warn "VERIFY the output for any leftover secrets, tokens, private names, or customer data"
|
|
167
|
+
_kxt_warn "BEFORE sharing it or feeding it into the training pipeline."
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# Run only on direct execution; `source`-ing the file just defines the functions (for tests).
|
|
171
|
+
if command -v kit_is_main >/dev/null 2>&1; then
|
|
172
|
+
kit_is_main && _kxt_main "$@"
|
|
173
|
+
else
|
|
174
|
+
[ "${BASH_SOURCE[0]:-$0}" = "${0}" ] && _kxt_main "$@"
|
|
175
|
+
fi
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kit-migrate-test.sh — self-test for kit-migrate (#373). Runs under bash AND zsh.
|
|
3
|
+
# Asserts the acceptance gate: migrate twice = no-op; old paths registered (survives /kit-update);
|
|
4
|
+
# manifest re-keyed; docs rewritten; .claude/ backed up.
|
|
5
|
+
# Run: bash scripts/kit-migrate-test.sh
|
|
6
|
+
PLUGIN_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
7
|
+
|
|
8
|
+
if [ -n "${KIT_MIGRATE_TEST_INNER:-}" ]; then
|
|
9
|
+
set -u
|
|
10
|
+
fail=0
|
|
11
|
+
t() { if [ "$2" != "$3" ]; then echo "FAIL($KIT_MIGRATE_TEST_INNER): $1 -> got '[$2]' want '[$3]'"; fail=1; else echo "ok($KIT_MIGRATE_TEST_INNER): $1"; fi; }
|
|
12
|
+
|
|
13
|
+
proj="$(mktemp -d)"; trap 'rm -rf "$proj"' EXIT
|
|
14
|
+
cd "$proj"
|
|
15
|
+
export KIT_ASSUME_YES=1
|
|
16
|
+
. "$PLUGIN_DIR/scripts/kit-migrate.sh"
|
|
17
|
+
. "$PLUGIN_DIR/scripts/lib/kit-manifest.sh"
|
|
18
|
+
|
|
19
|
+
# Old-layout project: task-* skills, a CLAUDE.md referencing /task-pr, a manifest tracking the old
|
|
20
|
+
# skill path, a kit.config.json with no upgrade map yet.
|
|
21
|
+
mkdir -p .claude/skills/task-pr .claude/skills/task-gc .claude/rules
|
|
22
|
+
printf 'pr skill body\n' > .claude/skills/task-pr/SKILL.md
|
|
23
|
+
printf 'gc skill body\n' > .claude/skills/task-gc/SKILL.md
|
|
24
|
+
printf 'Run /task-pr then /task-pr-merge; sweep with /task-gc.\n' > CLAUDE.md
|
|
25
|
+
printf '{ "schema":1 }\n' > .claude/kit.config.json
|
|
26
|
+
kit_manifest_record .claude/skills/task-pr/SKILL.md A wire 1 2026-01-01T00:00:00Z
|
|
27
|
+
|
|
28
|
+
# --- run 1: the real migration ---
|
|
29
|
+
kit_migrate_all >/dev/null 2>&1
|
|
30
|
+
|
|
31
|
+
t "old task-pr gone" "$([ -e .claude/skills/task-pr ] && echo yes || echo no)" "no"
|
|
32
|
+
t "new kit-task-pr present" "$([ -f .claude/skills/kit-task-pr/SKILL.md ] && echo yes || echo no)" "yes"
|
|
33
|
+
t "content preserved" "$(cat .claude/skills/kit-task-pr/SKILL.md)" "pr skill body"
|
|
34
|
+
t "task-gc -> kit-gc" "$([ -f .claude/skills/kit-gc/SKILL.md ] && echo yes || echo no)" "yes"
|
|
35
|
+
t "backup taken" "$(find . -maxdepth 1 -name ".claude.bak.*" 2>/dev/null | grep -c .)" "1"
|
|
36
|
+
|
|
37
|
+
# resurrection guard: old path registered in upgrade.renamed{} -> /kit-update skips it.
|
|
38
|
+
t "rename registered" "$(jq -r '.upgrade.renamed[".claude/skills/task-pr"] // "MISS"' .claude/kit.config.json)" ".claude/skills/kit-task-pr"
|
|
39
|
+
t "gc rename registered" "$(jq -r '.upgrade.renamed[".claude/skills/task-gc"] // "MISS"' .claude/kit.config.json)" ".claude/skills/kit-gc"
|
|
40
|
+
|
|
41
|
+
# manifest re-keyed to the v2 path.
|
|
42
|
+
t "manifest re-keyed" "$(kit_manifest_verify .claude/skills/kit-task-pr/SKILL.md)" "intact"
|
|
43
|
+
t "old manifest entry dropped" "$(kit_manifest_verify .claude/skills/task-pr/SKILL.md)" "untracked"
|
|
44
|
+
|
|
45
|
+
# CLAUDE.md surgery: /task-pr -> /kit-task-pr (no bare /task- left).
|
|
46
|
+
t "doc rewritten kit-task-pr" "$(grep -c '/kit-task-pr' CLAUDE.md)" "1"
|
|
47
|
+
t "doc rewritten kit-gc" "$(grep -c '/kit-gc' CLAUDE.md)" "1"
|
|
48
|
+
t "no bare /task- left" "$(grep -Ec '/task-(pr|gc|new)' CLAUDE.md)" "0"
|
|
49
|
+
|
|
50
|
+
# --- run 2: idempotency — migrate twice = no-op (nothing moves, no second backup) ---
|
|
51
|
+
out2="$(kit_migrate_all 2>&1)"
|
|
52
|
+
t "2nd run is no-op" "$(printf '%s' "$out2" | grep -c 'already on v2')" "1"
|
|
53
|
+
t "still ONE backup" "$(find . -maxdepth 1 -name ".claude.bak.*" 2>/dev/null | grep -c .)" "1"
|
|
54
|
+
t "kit-task-pr still there" "$([ -f .claude/skills/kit-task-pr/SKILL.md ] && echo yes || echo no)" "yes"
|
|
55
|
+
t "old still absent" "$([ -e .claude/skills/task-pr ] && echo yes || echo no)" "no"
|
|
56
|
+
|
|
57
|
+
# --- dry-run on a fresh old project moves nothing ---
|
|
58
|
+
proj2="$(mktemp -d)"; cd "$proj2"
|
|
59
|
+
mkdir -p .claude/skills/task-pr
|
|
60
|
+
printf 'x\n' > .claude/skills/task-pr/SKILL.md
|
|
61
|
+
printf '{ "schema":1 }\n' > .claude/kit.config.json
|
|
62
|
+
KIT_DRY_RUN=1 kit_migrate_all >/dev/null 2>&1
|
|
63
|
+
t "dry-run does not move" "$([ -e .claude/skills/task-pr ] && echo yes || echo no)" "yes"
|
|
64
|
+
t "dry-run no new dir" "$([ -e .claude/skills/kit-task-pr ] && echo yes || echo no)" "no"
|
|
65
|
+
t "dry-run no backup" "$(find . -maxdepth 1 -name ".claude.bak.*" 2>/dev/null | grep -c .)" "0"
|
|
66
|
+
t "dry-run no registry write" "$(jq -r '.upgrade // "NONE"' .claude/kit.config.json)" "NONE"
|
|
67
|
+
|
|
68
|
+
[ "$fail" -eq 0 ] && echo "ALL OK($KIT_MIGRATE_TEST_INNER)"
|
|
69
|
+
exit "$fail"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
rc=0; ran=0
|
|
73
|
+
for sh in bash zsh; do
|
|
74
|
+
command -v "$sh" >/dev/null 2>&1 || continue
|
|
75
|
+
ran=$((ran+1)); echo "--- $sh ---"
|
|
76
|
+
rcflag=""; [ "$sh" = "zsh" ] && rcflag="--no-rcs"
|
|
77
|
+
KIT_MIGRATE_TEST_INNER="$sh" PATH="$PATH" PLUGIN_DIR="$PLUGIN_DIR" "$sh" $rcflag "$0" || rc=1
|
|
78
|
+
done
|
|
79
|
+
[ "$ran" -eq 0 ] && { echo "no shell"; exit 1; }
|
|
80
|
+
exit "$rc"
|