@jenga-ai/agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +340 -0
- package/agents/ai_engineer.md +113 -0
- package/agents/developer.md +236 -0
- package/agents/scrum-master.md +349 -0
- package/agents/scrutiny-agent.md +137 -0
- package/agents/solution-assessor.md +185 -0
- package/agents/tester.md +339 -0
- package/bin/jenga.js +70 -0
- package/hooks/copilot_session_end.sh +29 -0
- package/hooks/on_session_end.sh +238 -0
- package/hooks/prompt_router.sh +11 -0
- package/hooks/prompt_router_helper.js +52 -0
- package/hooks/session_end_helper.js +29 -0
- package/hooks/session_end_watcher.sh +24 -0
- package/lib/commands/attach.js +47 -0
- package/lib/commands/init.js +207 -0
- package/lib/commands/start.js +16 -0
- package/lib/commands/status.js +53 -0
- package/lib/config-schema.js +72 -0
- package/lib/inject-settings.js +61 -0
- package/lib/mirror.js +244 -0
- package/lib/resolve-project-dir.sh +47 -0
- package/mcp/execute-ticket/index.js +10 -0
- package/mcp/execute-ticket/package.json +5 -0
- package/mcp/help/index.js +79 -0
- package/mcp/help/package.json +14 -0
- package/mcp/router/README.md +19 -0
- package/mcp/router/embedder.js +23 -0
- package/mcp/router/index.js +204 -0
- package/mcp/router/matcher.js +87 -0
- package/mcp/router/package-lock.json +1048 -0
- package/mcp/router/package.json +11 -0
- package/mcp/router/skill-index.js +104 -0
- package/package.json +47 -0
- package/scripts/board_resolver.sh +46 -0
- package/scripts/e25_s01_extract_board_graph.py +292 -0
- package/scripts/e25_s01_generate_synthetic_board.py +90 -0
- package/scripts/measurement-10x.json +50 -0
- package/scripts/measurement-10x.txt +4 -0
- package/scripts/measurement-real.json +50 -0
- package/scripts/measurement-real.txt +4 -0
- package/scripts/postinstall.js +165 -0
- package/scripts/todo_cleanup.sh +22 -0
- package/scripts/todo_manager.sh +86 -0
- package/scripts/validate-board.sh +190 -0
- package/scripts/validate-story-format.sh +53 -0
- package/skills/brainstorm/SKILL.md +47 -0
- package/skills/btw/SKILL.md +42 -0
- package/skills/commit/SKILL.md +29 -0
- package/skills/commit/assets/user_instructions_template.md +22 -0
- package/skills/continue/SKILL.md +29 -0
- package/skills/convert/SKILL.md +124 -0
- package/skills/convert/convert_cli.py +235 -0
- package/skills/convert/tests/sample.csv +4 -0
- package/skills/convert/tests/sample.json +5 -0
- package/skills/convert/tests/sample.jsonl +3 -0
- package/skills/convert/tests/sample.yaml +18 -0
- package/skills/convert/tests/sample_obj.csv +2 -0
- package/skills/convert/tests/sample_obj.json +9 -0
- package/skills/deep-dive/SKILL.md +167 -0
- package/skills/do/SKILL.md +88 -0
- package/skills/do/assets/sender_template.json +12 -0
- package/skills/doc/SKILL.md +314 -0
- package/skills/doc/assets/path-objectives.yaml +38 -0
- package/skills/doc-sync/SKILL.md +167 -0
- package/skills/doc-sync/assets/default_excludes.txt +21 -0
- package/skills/doc-sync/assets/doc_targets.md +14 -0
- package/skills/dooo/SKILL.md +60 -0
- package/skills/error/SKILL.md +29 -0
- package/skills/evaluate/SKILL.md +45 -0
- package/skills/evaluate/assets/evaluation_invokation_template.yml +3 -0
- package/skills/evaluate/assets/evaluation_rapport_template.md +24 -0
- package/skills/examplify/SKILL.md +42 -0
- package/skills/help/SKILL.md +36 -0
- package/skills/improve/SKILL.md +55 -0
- package/skills/index/scripts/board-index +4 -0
- package/skills/index/scripts/board_index.py +615 -0
- package/skills/index/scripts/smoke_test.sh +86 -0
- package/skills/init/SKILL.md +44 -0
- package/skills/init/assets/.gitignore_template +15 -0
- package/skills/init/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/init/assets/directory_structure.txt +13 -0
- package/skills/init/assets/test-config_template.json +4 -0
- package/skills/init/assets/workflow_template.json +30 -0
- package/skills/init/scripts/init.sh +48 -0
- package/skills/jbp/SKILL.md +25 -0
- package/skills/jenga/SKILL.md +68 -0
- package/skills/lgtm/SKILL.md +21 -0
- package/skills/mirror-public/SKILL.md +237 -0
- package/skills/mirror-public/assets/config.json +5 -0
- package/skills/mirror-public/scripts/mirror.sh +374 -0
- package/skills/pi-plan/SKILL.md +62 -0
- package/skills/pi-plan/assets/epic.json +7 -0
- package/skills/pi-plan/assets/story_template.md +18 -0
- package/skills/proceed/SKILL.md +29 -0
- package/skills/publish/SKILL.md +351 -0
- package/skills/publish/adapters/droplet.md +200 -0
- package/skills/publish/adapters/mobile-ios.md +114 -0
- package/skills/publish/adapters/npm-ci.md +223 -0
- package/skills/publish/adapters/npm.md +121 -0
- package/skills/publish/assets/ExportOptions.plist.template +19 -0
- package/skills/publish/assets/ci-contract.md +111 -0
- package/skills/publish/assets/ownership-matrix.md +17 -0
- package/skills/publish/assets/publish.example.json +85 -0
- package/skills/publish/assets/publish.example.npm-ci.json +40 -0
- package/skills/publish/assets/publish.example.npm.json +41 -0
- package/skills/publish/assets/secrets-guide.md +104 -0
- package/skills/publish/schemas/fixtures/npm-ci-minimal.json +17 -0
- package/skills/publish/schemas/fixtures/npm-ci-with-empty-secrets.json +18 -0
- package/skills/publish/schemas/fixtures/npm-ci-with-workflow-path.json +18 -0
- package/skills/publish/schemas/publish.schema.json +428 -0
- package/skills/publish/scripts/check_target_config.sh +96 -0
- package/skills/publish/scripts/droplet_pipeline.sh +208 -0
- package/skills/publish/scripts/generate_release_notes.sh +200 -0
- package/skills/publish/scripts/ios_pipeline.sh +486 -0
- package/skills/publish/scripts/npm_ci_pipeline.sh +225 -0
- package/skills/publish/scripts/npm_pipeline.sh +249 -0
- package/skills/publish/scripts/publish_common.sh +253 -0
- package/skills/publish/scripts/publish_deploy.sh +538 -0
- package/skills/publish/scripts/reconcile_tags.sh +135 -0
- package/skills/publish/scripts/run_gates.sh +616 -0
- package/skills/publish/scripts/setup_wizard.sh +394 -0
- package/skills/publish/scripts/show_history.sh +95 -0
- package/skills/publish/scripts/suggest_semver_bump.sh +105 -0
- package/skills/publish/scripts/validate_config.sh +163 -0
- package/skills/publish/scripts/validate_droplet_env.sh +45 -0
- package/skills/publish/scripts/validate_ios_env.sh +68 -0
- package/skills/publish/scripts/validate_npm_ci_env.sh +71 -0
- package/skills/publish/scripts/validate_npm_env.sh +22 -0
- package/skills/publish/scripts/write_ledger_entry.sh +126 -0
- package/skills/publish/wizards/droplet.md +275 -0
- package/skills/publish/wizards/mobile-ios.md +157 -0
- package/skills/publish/wizards/npm-ci.md +240 -0
- package/skills/publish/wizards/npm.md +224 -0
- package/skills/reconcile/SKILL.md +93 -0
- package/skills/reconcile/assets/report_format.md +44 -0
- package/skills/reconcile-origin/SKILL.md +75 -0
- package/skills/reconcile-origin/scripts/reconcile-origin.sh +372 -0
- package/skills/redo/SKILL.md +70 -0
- package/skills/route/SKILL.md +180 -0
- package/skills/self-sync/SKILL.md +73 -0
- package/skills/self-sync/scripts/run.js +136 -0
- package/skills/skillify/SKILL.md +68 -0
- package/skills/skillify/assets/init-new/SKILL.md +35 -0
- package/skills/skillify/assets/init-new/assets/.gitignore_template +15 -0
- package/skills/skillify/assets/init-new/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/skillify/assets/init-new/assets/directory_structure.txt +10 -0
- package/skills/skillify/assets/init-new/assets/test-config_template.json +4 -0
- package/skills/skillify/assets/init-new/assets/workflow_template.json +17 -0
- package/skills/skillify/assets/init-new/scripts/init.sh +48 -0
- package/skills/skillify/assets/init-old/SKILL.md +124 -0
- package/skills/spinoff/SKILL.md +48 -0
- package/skills/status/SKILL.md +33 -0
- package/skills/status/assets/output_format.md +41 -0
- package/skills/todo/SKILL.md +46 -0
- package/skills/todo/assets/todo_handoff_template.md +22 -0
- package/skills/todo/assets/todo_template.md +3 -0
- package/skills/train/SKILL.md +116 -0
- package/skills/train/assets/dashboard-templates/classifiers.html +106 -0
- package/skills/train/assets/dashboard-templates/nlp.html +102 -0
- package/skills/train/assets/dashboard-templates/transformers.html +98 -0
- package/skills/train/assets/results-parsers/__init__.py +9 -0
- package/skills/train/assets/results-parsers/classifiers.py +84 -0
- package/skills/train/assets/results-parsers/nlp.py +88 -0
- package/skills/train/assets/results-parsers/reporter.py +154 -0
- package/skills/train/assets/results-parsers/transformers.py +120 -0
- package/skills/train/train_cli.py +786 -0
- package/templates/EXECUTION_PLAN_TEMPLATE.md +43 -0
- package/templates/EXECUTION_SUMMARY_TEMPLATE.md +50 -0
- package/templates/JENGA_CONFIG_TEMPLATE.json +23 -0
- package/templates/PROBLEM_RAPPORT_TEMPLATE.md +88 -0
- package/templates/SCRUM_BOARD_SCHEMA.md +311 -0
- package/templates/SKILL.md +16 -0
- package/templates/SKILL_TEMPLATE.md +28 -0
- package/templates/USER_INSTRUCTIONS_TEMPLATE.md +22 -0
- package/templates/copilot-instructions.md.tpl +55 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
emit_error() {
|
|
5
|
+
local branch="${1:-}"
|
|
6
|
+
local code="$2"
|
|
7
|
+
local message="$3"
|
|
8
|
+
python3 - "$branch" "$code" "$message" <<'PY'
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
branch, code, message = sys.argv[1:4]
|
|
13
|
+
payload = {
|
|
14
|
+
"status": "error",
|
|
15
|
+
"code": code,
|
|
16
|
+
"message": message,
|
|
17
|
+
}
|
|
18
|
+
if branch:
|
|
19
|
+
payload["branch"] = branch
|
|
20
|
+
print(json.dumps(payload, ensure_ascii=False))
|
|
21
|
+
PY
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
emit_local_branch_missing() {
|
|
25
|
+
local branch="$1"
|
|
26
|
+
local remote_exists="$2"
|
|
27
|
+
python3 - "$branch" "$remote_exists" <<'PY'
|
|
28
|
+
import json
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
branch = sys.argv[1]
|
|
32
|
+
remote_exists = sys.argv[2].lower() == "true"
|
|
33
|
+
print(json.dumps({
|
|
34
|
+
"status": "local_branch_missing",
|
|
35
|
+
"branch": branch,
|
|
36
|
+
"remote_exists": remote_exists,
|
|
37
|
+
"message": "The requested branch does not exist locally."
|
|
38
|
+
}, ensure_ascii=False))
|
|
39
|
+
PY
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
emit_ok() {
|
|
43
|
+
local branch="$1"
|
|
44
|
+
local rebased_commits="$2"
|
|
45
|
+
python3 - "$branch" "$rebased_commits" <<'PY'
|
|
46
|
+
import json
|
|
47
|
+
import sys
|
|
48
|
+
|
|
49
|
+
branch, rebased_commits = sys.argv[1], int(sys.argv[2])
|
|
50
|
+
print(json.dumps({
|
|
51
|
+
"status": "ok",
|
|
52
|
+
"branch": branch,
|
|
53
|
+
"rebased_commits": rebased_commits,
|
|
54
|
+
}, ensure_ascii=False))
|
|
55
|
+
PY
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
emit_handled_later() {
|
|
59
|
+
local file="$1"
|
|
60
|
+
python3 - "$file" <<'PY'
|
|
61
|
+
import json
|
|
62
|
+
import sys
|
|
63
|
+
|
|
64
|
+
print(json.dumps({
|
|
65
|
+
"status": "handled_later",
|
|
66
|
+
"file": sys.argv[1],
|
|
67
|
+
}, ensure_ascii=False))
|
|
68
|
+
PY
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
rebase_in_progress() {
|
|
72
|
+
local git_dir
|
|
73
|
+
git_dir="$(git rev-parse --git-dir)"
|
|
74
|
+
[[ -d "$git_dir/rebase-merge" || -d "$git_dir/rebase-apply" ]]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
annotate_conflicts() {
|
|
78
|
+
local file="$1"
|
|
79
|
+
local branch="$2"
|
|
80
|
+
python3 - "$file" "$branch" <<'PY'
|
|
81
|
+
from pathlib import Path
|
|
82
|
+
import sys
|
|
83
|
+
|
|
84
|
+
path = Path(sys.argv[1])
|
|
85
|
+
branch = sys.argv[2]
|
|
86
|
+
lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
|
|
87
|
+
comment_block = [
|
|
88
|
+
"# RECONCILE-ORIGIN CONFLICT:\n",
|
|
89
|
+
f"# Local commits and origin/{branch} both changed the lines below in {path}.\n",
|
|
90
|
+
"# Resolve the conflict when you are ready, then remove this note if it is no longer needed.\n",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
result = []
|
|
94
|
+
i = 0
|
|
95
|
+
while i < len(lines):
|
|
96
|
+
line = lines[i]
|
|
97
|
+
if line.startswith("<<<<<<< "):
|
|
98
|
+
if len(result) >= len(comment_block) and result[-len(comment_block):] == comment_block:
|
|
99
|
+
pass
|
|
100
|
+
else:
|
|
101
|
+
result.extend(comment_block)
|
|
102
|
+
result.append(line)
|
|
103
|
+
i += 1
|
|
104
|
+
|
|
105
|
+
path.write_text("".join(result), encoding="utf-8")
|
|
106
|
+
PY
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
build_conflict_report() {
|
|
110
|
+
local branch="$1"
|
|
111
|
+
shift
|
|
112
|
+
python3 - "$branch" "$@" <<'PY'
|
|
113
|
+
import json
|
|
114
|
+
import sys
|
|
115
|
+
from pathlib import Path
|
|
116
|
+
|
|
117
|
+
branch = sys.argv[1]
|
|
118
|
+
files = sys.argv[2:]
|
|
119
|
+
conflicts = []
|
|
120
|
+
|
|
121
|
+
for file_path in files:
|
|
122
|
+
path = Path(file_path)
|
|
123
|
+
if not path.exists():
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
lines = path.read_text(encoding="utf-8").splitlines()
|
|
127
|
+
blocks = []
|
|
128
|
+
i = 0
|
|
129
|
+
while i < len(lines):
|
|
130
|
+
line = lines[i]
|
|
131
|
+
if line.startswith("<<<<<<< "):
|
|
132
|
+
i += 1
|
|
133
|
+
origin_lines = []
|
|
134
|
+
while i < len(lines) and lines[i] != "=======":
|
|
135
|
+
origin_lines.append(lines[i])
|
|
136
|
+
i += 1
|
|
137
|
+
if i < len(lines) and lines[i] == "=======":
|
|
138
|
+
i += 1
|
|
139
|
+
local_lines = []
|
|
140
|
+
while i < len(lines) and not lines[i].startswith(">>>>>>>"):
|
|
141
|
+
local_lines.append(lines[i])
|
|
142
|
+
i += 1
|
|
143
|
+
while i < len(lines) and not lines[i].startswith(">>>>>>>"):
|
|
144
|
+
i += 1
|
|
145
|
+
if i < len(lines) and lines[i].startswith(">>>>>>>"):
|
|
146
|
+
i += 1
|
|
147
|
+
blocks.append(("\n".join(local_lines).strip(), "\n".join(origin_lines).strip()))
|
|
148
|
+
else:
|
|
149
|
+
i += 1
|
|
150
|
+
|
|
151
|
+
if not blocks:
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
local_text = "\n\n---\n\n".join(block[0] for block in blocks)
|
|
155
|
+
origin_text = "\n\n---\n\n".join(block[1] for block in blocks)
|
|
156
|
+
conflicts.append({
|
|
157
|
+
"file": file_path,
|
|
158
|
+
"local_section": local_text,
|
|
159
|
+
"origin_section": origin_text,
|
|
160
|
+
"description": f"Overlapping edits between local commits and origin/{branch} across {len(blocks)} conflict block(s).",
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
print(json.dumps({
|
|
164
|
+
"status": "conflict",
|
|
165
|
+
"branch": branch,
|
|
166
|
+
"conflicts": conflicts,
|
|
167
|
+
}, ensure_ascii=False))
|
|
168
|
+
PY
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fetch_origin_branch() {
|
|
172
|
+
local branch="$1"
|
|
173
|
+
git fetch origin "refs/heads/$branch:refs/remotes/origin/$branch" >/dev/null 2>&1
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
ensure_clean_worktree() {
|
|
177
|
+
if [[ -n "$(git status --porcelain)" ]]; then
|
|
178
|
+
emit_error "$1" "uncommitted_changes" "Uncommitted changes detected. Stash or commit them before reconciling with origin."
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
ensure_branch_context() {
|
|
184
|
+
local branch="$1"
|
|
185
|
+
local create_tracking="$2"
|
|
186
|
+
local current_branch
|
|
187
|
+
current_branch="$(git branch --show-current)"
|
|
188
|
+
|
|
189
|
+
if ! git show-ref --verify --quiet "refs/heads/$branch"; then
|
|
190
|
+
if git ls-remote --exit-code --heads origin "$branch" >/dev/null 2>&1; then
|
|
191
|
+
if [[ "$create_tracking" == "true" ]]; then
|
|
192
|
+
fetch_origin_branch "$branch" || {
|
|
193
|
+
emit_error "$branch" "branch_not_found_on_origin" "Unable to fetch origin/$branch."
|
|
194
|
+
exit 1
|
|
195
|
+
}
|
|
196
|
+
git checkout -b "$branch" --track "origin/$branch" >/dev/null 2>&1 || {
|
|
197
|
+
emit_error "$branch" "checkout_failed" "Unable to create and check out local tracking branch $branch."
|
|
198
|
+
exit 1
|
|
199
|
+
}
|
|
200
|
+
else
|
|
201
|
+
emit_local_branch_missing "$branch" "true"
|
|
202
|
+
exit 0
|
|
203
|
+
fi
|
|
204
|
+
else
|
|
205
|
+
emit_local_branch_missing "$branch" "false"
|
|
206
|
+
exit 0
|
|
207
|
+
fi
|
|
208
|
+
elif [[ "$current_branch" != "$branch" ]]; then
|
|
209
|
+
git checkout "$branch" >/dev/null 2>&1 || {
|
|
210
|
+
emit_error "$branch" "checkout_failed" "Unable to check out local branch $branch."
|
|
211
|
+
exit 1
|
|
212
|
+
}
|
|
213
|
+
fi
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
run_standard_reconcile() {
|
|
217
|
+
local branch="$1"
|
|
218
|
+
fetch_origin_branch "$branch" || {
|
|
219
|
+
emit_error "$branch" "branch_not_found_on_origin" "Branch origin/$branch was not found or could not be fetched."
|
|
220
|
+
exit 1
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
local rebased_commits
|
|
224
|
+
rebased_commits="$(git rev-list --count "origin/$branch..HEAD" 2>/dev/null || echo 0)"
|
|
225
|
+
|
|
226
|
+
if git rebase "origin/$branch" >/dev/null 2>&1; then
|
|
227
|
+
emit_ok "$branch" "$rebased_commits"
|
|
228
|
+
return 0
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
local conflict_files
|
|
232
|
+
conflict_files="$(git diff --name-only --diff-filter=U || true)"
|
|
233
|
+
if [[ -n "$conflict_files" ]]; then
|
|
234
|
+
local -a conflict_array=()
|
|
235
|
+
while IFS= read -r line; do
|
|
236
|
+
[[ -n "$line" ]] && conflict_array+=("$line")
|
|
237
|
+
done <<<"$conflict_files"
|
|
238
|
+
local report
|
|
239
|
+
report="$(build_conflict_report "$branch" "${conflict_array[@]}")"
|
|
240
|
+
git rebase --abort >/dev/null 2>&1 || true
|
|
241
|
+
printf '%s\n' "$report"
|
|
242
|
+
return 0
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
if rebase_in_progress; then
|
|
246
|
+
git rebase --abort >/dev/null 2>&1 || true
|
|
247
|
+
fi
|
|
248
|
+
emit_error "$branch" "rebase_failed" "The rebase failed before conflict details could be collected."
|
|
249
|
+
exit 1
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
run_handle_later() {
|
|
253
|
+
local branch="$1"
|
|
254
|
+
local target_file="$2"
|
|
255
|
+
|
|
256
|
+
fetch_origin_branch "$branch" || {
|
|
257
|
+
emit_error "$branch" "branch_not_found_on_origin" "Branch origin/$branch was not found or could not be fetched."
|
|
258
|
+
exit 1
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
local rebased_commits
|
|
262
|
+
rebased_commits="$(git rev-list --count "origin/$branch..HEAD" 2>/dev/null || echo 0)"
|
|
263
|
+
|
|
264
|
+
if git rebase "origin/$branch" >/dev/null 2>&1; then
|
|
265
|
+
emit_ok "$branch" "$rebased_commits"
|
|
266
|
+
return 0
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
local conflict_files
|
|
270
|
+
conflict_files="$(git diff --name-only --diff-filter=U || true)"
|
|
271
|
+
if [[ -z "$conflict_files" ]]; then
|
|
272
|
+
if rebase_in_progress; then
|
|
273
|
+
git rebase --abort >/dev/null 2>&1 || true
|
|
274
|
+
fi
|
|
275
|
+
emit_error "$branch" "rebase_failed" "The rebase failed before the requested conflict could be handled."
|
|
276
|
+
exit 1
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
if ! printf '%s\n' "$conflict_files" | grep -Fxq "$target_file"; then
|
|
280
|
+
local -a conflict_array=()
|
|
281
|
+
while IFS= read -r line; do
|
|
282
|
+
[[ -n "$line" ]] && conflict_array+=("$line")
|
|
283
|
+
done <<<"$conflict_files"
|
|
284
|
+
local report
|
|
285
|
+
report="$(build_conflict_report "$branch" "${conflict_array[@]}")"
|
|
286
|
+
git rebase --abort >/dev/null 2>&1 || true
|
|
287
|
+
printf '%s\n' "$report"
|
|
288
|
+
exit 1
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
while true; do
|
|
292
|
+
annotate_conflicts "$target_file" "$branch"
|
|
293
|
+
git add "$target_file"
|
|
294
|
+
|
|
295
|
+
if GIT_EDITOR=true git rebase --continue >/dev/null 2>&1; then
|
|
296
|
+
emit_handled_later "$target_file"
|
|
297
|
+
return 0
|
|
298
|
+
fi
|
|
299
|
+
|
|
300
|
+
conflict_files="$(git diff --name-only --diff-filter=U || true)"
|
|
301
|
+
if [[ -z "$conflict_files" ]]; then
|
|
302
|
+
emit_error "$branch" "rebase_continue_failed" "Rebase continuation failed after annotating $target_file."
|
|
303
|
+
exit 1
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
if printf '%s\n' "$conflict_files" | grep -Fxq "$target_file"; then
|
|
307
|
+
continue
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
emit_handled_later "$target_file"
|
|
311
|
+
return 0
|
|
312
|
+
done
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
branch_arg=""
|
|
316
|
+
handle_later_file=""
|
|
317
|
+
create_tracking="false"
|
|
318
|
+
|
|
319
|
+
while [[ $# -gt 0 ]]; do
|
|
320
|
+
case "$1" in
|
|
321
|
+
--handle-later)
|
|
322
|
+
shift
|
|
323
|
+
if [[ $# -eq 0 ]]; then
|
|
324
|
+
emit_error "$branch_arg" "missing_handle_later_file" "The --handle-later flag requires a file path."
|
|
325
|
+
exit 1
|
|
326
|
+
fi
|
|
327
|
+
handle_later_file="$1"
|
|
328
|
+
;;
|
|
329
|
+
--create-tracking)
|
|
330
|
+
create_tracking="true"
|
|
331
|
+
;;
|
|
332
|
+
-h|--help)
|
|
333
|
+
cat <<'USAGE'
|
|
334
|
+
Usage: reconcile-origin.sh [<branch>] [--handle-later <file>] [--create-tracking]
|
|
335
|
+
USAGE
|
|
336
|
+
exit 0
|
|
337
|
+
;;
|
|
338
|
+
*)
|
|
339
|
+
if [[ -n "$branch_arg" ]]; then
|
|
340
|
+
emit_error "$branch_arg" "unexpected_argument" "Unexpected argument: $1"
|
|
341
|
+
exit 1
|
|
342
|
+
fi
|
|
343
|
+
branch_arg="$1"
|
|
344
|
+
;;
|
|
345
|
+
esac
|
|
346
|
+
shift
|
|
347
|
+
done
|
|
348
|
+
|
|
349
|
+
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
350
|
+
emit_error "$branch_arg" "not_a_git_repository" "This command must be run inside a git work tree."
|
|
351
|
+
exit 1
|
|
352
|
+
fi
|
|
353
|
+
|
|
354
|
+
if rebase_in_progress; then
|
|
355
|
+
emit_error "$branch_arg" "rebase_in_progress" "A rebase is already in progress. Resolve or abort it before running reconcile-origin."
|
|
356
|
+
exit 1
|
|
357
|
+
fi
|
|
358
|
+
|
|
359
|
+
branch="${branch_arg:-$(git branch --show-current)}"
|
|
360
|
+
if [[ -z "$branch" ]]; then
|
|
361
|
+
emit_error "$branch" "detached_head" "Unable to determine the current branch from a detached HEAD state."
|
|
362
|
+
exit 1
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
ensure_clean_worktree "$branch"
|
|
366
|
+
ensure_branch_context "$branch" "$create_tracking"
|
|
367
|
+
|
|
368
|
+
if [[ -n "$handle_later_file" ]]; then
|
|
369
|
+
run_handle_later "$branch" "$handle_later_file"
|
|
370
|
+
else
|
|
371
|
+
run_standard_reconcile "$branch"
|
|
372
|
+
fi
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: redo
|
|
3
|
+
description: Redo parts or entire previous implementations identified by a commit SHA or an Epic/Story number (e.g. E01, E02_S03). Expects an identifier and a description of what to redo and why. Use when the user wants to revisit, rework, or rewrite previously completed work, including updating all affected documentation.
|
|
4
|
+
keywords:
|
|
5
|
+
- redo
|
|
6
|
+
- rework
|
|
7
|
+
- rewrite
|
|
8
|
+
- revisit
|
|
9
|
+
- redo task
|
|
10
|
+
examples:
|
|
11
|
+
- "redo the login implementation"
|
|
12
|
+
- "rewrite story E02_S01"
|
|
13
|
+
metadata:
|
|
14
|
+
prefered_agent: scrum-master
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Redo — Rework a Previous Implementation
|
|
18
|
+
|
|
19
|
+
## Input Format
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
/redo <identifier> <description>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **identifier** — One of:
|
|
26
|
+
- A git commit SHA (full or short, e.g. `a1b2c3f`)
|
|
27
|
+
- An Epic number (e.g. `E01`)
|
|
28
|
+
- A Story number (e.g. `E02_S03`)
|
|
29
|
+
- **description** — What should be redone and, if relevant, why, how, or to what end.
|
|
30
|
+
|
|
31
|
+
If either part is missing, ask the user to provide it before proceeding.
|
|
32
|
+
|
|
33
|
+
## Instructions
|
|
34
|
+
|
|
35
|
+
### 1. Resolve the identifier
|
|
36
|
+
**Commit SHA:**
|
|
37
|
+
- Run `git --no-pager log --stat -1 <SHA>` to retrieve the commit message and changed files.
|
|
38
|
+
- Run `git --no-pager show <SHA>` to inspect the full diff.
|
|
39
|
+
|
|
40
|
+
**Epic / Story number:**
|
|
41
|
+
- Read the matching file under `$(bash scripts/board_resolver.sh)epics/` or `$(bash scripts/board_resolver.sh)stories/` to understand the scope.
|
|
42
|
+
- Use `git --no-pager log --all --oneline --grep="<epic or story title>"` to locate related commits and their diffs.
|
|
43
|
+
|
|
44
|
+
Collect the list of **files originally changed** and the **original intent** of the implementation.
|
|
45
|
+
|
|
46
|
+
### 2. Assess scope
|
|
47
|
+
Compare the user's redo description against the original implementation to determine:
|
|
48
|
+
- Which files need to change.
|
|
49
|
+
- Whether the redo is a partial revision (some files/logic) or a full rewrite.
|
|
50
|
+
- Which tests cover the affected code.
|
|
51
|
+
- Which documentation files reference the affected code or feature (check `project/`, `documentation/`, `project/documentation/`, `README.md`, `WARP.md`).
|
|
52
|
+
|
|
53
|
+
Summarise scope findings to the user in a brief list before continuing.
|
|
54
|
+
|
|
55
|
+
### 3. Plan the redo
|
|
56
|
+
Populate `skills/todo/assets/todo_handoff_template.md` with the following pre-collected context:
|
|
57
|
+
- **Mission title**: a short name for the redo work
|
|
58
|
+
- **Goal / objective**: the redo objective — what is changing and the desired outcome
|
|
59
|
+
- **Affected files or scope**: code, tests, and documentation files identified in step 2
|
|
60
|
+
- **Intended approach**: step-by-step changes
|
|
61
|
+
- **Epic/Story linkage**: the identifier resolved in step 1 (e.g. `E02_S03`), or `None` if a commit SHA
|
|
62
|
+
|
|
63
|
+
Then invoke the `/todo` skill with the populated template. Since the template fields are pre-filled, `/todo` should skip any questions already answered.
|
|
64
|
+
|
|
65
|
+
Wait for user approval before executing unless the user explicitly told you to go ahead.
|
|
66
|
+
|
|
67
|
+
## Examples
|
|
68
|
+
- `/redo a1b2c3f Rewrite the auth middleware to use JWT instead of session tokens`
|
|
69
|
+
- `/redo E02_S03 Replace the REST endpoints with GraphQL — the client team needs a flexible query interface`
|
|
70
|
+
- `/redo E01 Redo the entire onboarding epic using the new design-system components`
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: route
|
|
3
|
+
description: Intelligently routes a prompt to the best-matching skill in the project. Reads available skills, matches the input semantically and by keyword, enriches the prompt with board context and documentation references, then invokes the matched skill with the full context prepended.
|
|
4
|
+
keywords:
|
|
5
|
+
- "route"
|
|
6
|
+
- "dispatch"
|
|
7
|
+
- "auto skill"
|
|
8
|
+
- "smart prompt"
|
|
9
|
+
- "find skill"
|
|
10
|
+
examples:
|
|
11
|
+
- "route: I want to plan a new feature"
|
|
12
|
+
- "route: something is broken in the auth module"
|
|
13
|
+
- "route: let's review what's done so far"
|
|
14
|
+
- "route: analyze this approach in depth before we commit"
|
|
15
|
+
- "route: add a task to capture this idea"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Route — Intelligent Skill Dispatcher
|
|
19
|
+
|
|
20
|
+
## Purpose
|
|
21
|
+
|
|
22
|
+
`/route <prompt>` takes any natural-language input, finds the best-matching skill in the project, enriches it with live board context and documentation references, and invokes the matched skill — all in a single, fully-loaded message.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Instructions
|
|
27
|
+
|
|
28
|
+
### Step 1 — Discover Available Skills
|
|
29
|
+
|
|
30
|
+
Scan both skill registries (they may differ slightly — check both):
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
.agents/skills/<name>/SKILL.md
|
|
34
|
+
.claude/skills/<name>/SKILL.md (if the directory exists)
|
|
35
|
+
skills/<name>/SKILL.md (if the directory exists)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For each skill found, extract the following fields from its YAML front-matter:
|
|
39
|
+
|
|
40
|
+
| Field | Purpose |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `name` | Canonical identifier |
|
|
43
|
+
| `description` | Primary match signal |
|
|
44
|
+
| `keywords` | Exact/partial keyword match |
|
|
45
|
+
| `examples` | Semantic match reference |
|
|
46
|
+
| `metadata.prefered_agent` | Which agent to delegate to |
|
|
47
|
+
|
|
48
|
+
Build an in-memory skill registry from this data before proceeding.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### Step 2 — Match the Prompt to a Skill
|
|
53
|
+
|
|
54
|
+
Run matching in three passes (stop at first confident match):
|
|
55
|
+
|
|
56
|
+
#### Pass 1 — Keyword Match
|
|
57
|
+
Check whether any phrase from a skill's `keywords` list appears verbatim (case-insensitive) in the prompt.
|
|
58
|
+
|
|
59
|
+
#### Pass 2 — Example Similarity
|
|
60
|
+
Compare the prompt against each skill's `examples` list. Treat this as a semantic similarity check: pick the skill whose examples most closely reflect the intent of the prompt.
|
|
61
|
+
|
|
62
|
+
#### Pass 3 — Description Match
|
|
63
|
+
If no clear winner has emerged, compare the prompt against each skill's `description` field. Pick the skill whose description best captures what the user is trying to do.
|
|
64
|
+
|
|
65
|
+
**If two or more skills score equally**, present the top candidates and ask the user to choose:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
More than one skill matches your prompt. Which should I apply?
|
|
69
|
+
1. /<skill-a> — <one-line description>
|
|
70
|
+
2. /<skill-b> — <one-line description>
|
|
71
|
+
3. Neither — describe what you need
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**If no skill matches at all**, inform the user and offer:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
No matching skill found for: "<prompt>"
|
|
78
|
+
Would you like to:
|
|
79
|
+
1. Browse all available skills (/help)
|
|
80
|
+
2. Create a new skill for this use case (/btw)
|
|
81
|
+
3. Proceed without a skill (raw prompt)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### Step 3 — Check the Board for Related Context
|
|
87
|
+
|
|
88
|
+
Read `project/configs/workflow.json` for board paths (default: `project/board/`).
|
|
89
|
+
|
|
90
|
+
Scan the board for items that are topically related to the prompt:
|
|
91
|
+
|
|
92
|
+
1. **Epics** — `project/board/epics/` — check `title` and `description` fields
|
|
93
|
+
2. **Stories** — `project/board/stories/` — check `title` and `description` fields
|
|
94
|
+
3. **Tasks** — `project/board/tasks/` — check `title` and `description` fields
|
|
95
|
+
|
|
96
|
+
Extract any items whose title or description contains keywords from the prompt. Limit to the **top 5 most relevant** results. Do not include items whose status is `Archived` or `Cancelled`.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Step 4 — Check Documentation
|
|
101
|
+
|
|
102
|
+
Scan the following directories for relevant documentation files:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
project/documentation/plans/
|
|
106
|
+
project/documentation/summaries/
|
|
107
|
+
project/documentation/examples/
|
|
108
|
+
docs/
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Match filenames and headings against the prompt keywords. Collect the **top 3 most relevant** file paths.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
### Step 5 — Construct the Enriched Prompt
|
|
116
|
+
|
|
117
|
+
Assemble the following composite message in **exactly this order**:
|
|
118
|
+
|
|
119
|
+
#### Part A — Matched Skill (full content)
|
|
120
|
+
Inline the **full body** of the matched skill's `SKILL.md` (everything after the YAML front-matter). This is placed first so the agent reads the skill instructions before anything else.
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
<!-- SKILL: /<matched-skill-name> -->
|
|
124
|
+
<full skill instructions>
|
|
125
|
+
<!-- END SKILL -->
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Part B — Board & Documentation Context
|
|
129
|
+
Immediately after the skill, append a context block:
|
|
130
|
+
|
|
131
|
+
```markdown
|
|
132
|
+
---
|
|
133
|
+
## 📋 Relevant Board Context
|
|
134
|
+
|
|
135
|
+
<list each matched board item as:>
|
|
136
|
+
- [<status>] **<ID>** — <title> (`project/board/<type>/<filename>`)
|
|
137
|
+
|
|
138
|
+
## 📄 Relevant Documentation
|
|
139
|
+
|
|
140
|
+
<list each matched doc as:>
|
|
141
|
+
- `<file-path>` — <one-line summary of the doc's purpose>
|
|
142
|
+
---
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
If no board items or docs were found, omit the corresponding section rather than leaving it empty.
|
|
146
|
+
|
|
147
|
+
#### Part C — Original Prompt
|
|
148
|
+
Finally, append the user's original unmodified prompt:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
## 🗣 Original Prompt
|
|
152
|
+
|
|
153
|
+
> <original prompt verbatim>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### Step 6 — Invoke the Matched Skill
|
|
159
|
+
|
|
160
|
+
Deliver the enriched composite message to the appropriate agent:
|
|
161
|
+
|
|
162
|
+
- If the matched skill specifies `metadata.prefered_agent`, load that agent's definition from `agents/<prefered_agent>.md` and pass the full composite message to it.
|
|
163
|
+
- If no `prefered_agent` is specified, execute the skill instructions directly using the composite message as the working input.
|
|
164
|
+
|
|
165
|
+
Do **not** summarise or restate the composite message — deliver it as-is.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### Step 7 — Report Routing Decision
|
|
170
|
+
|
|
171
|
+
Before invoking the skill, briefly inform the user:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Routing to: /<matched-skill-name>
|
|
175
|
+
Reason: <one sentence explaining why this skill was chosen>
|
|
176
|
+
Board items found: <count>
|
|
177
|
+
Docs found: <count>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Then proceed immediately — do not wait for user confirmation unless the match was ambiguous (Step 2 tie-break already handled that).
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: self-sync
|
|
3
|
+
description: Mirror this repo's root-level framework directories into its own `.claude/` and `.agents/` sub-trees so edits to `/skills/*`, `/agents/*`, `/hooks/*`, `/scripts/*`, `/templates/*`, and `settings.json` take effect in the current Claude Code (and non-Claude) agent session — the in-repo replacement for the retired `/distribute` self-sync loop.
|
|
4
|
+
keywords:
|
|
5
|
+
- "self sync"
|
|
6
|
+
- "mirror skills"
|
|
7
|
+
- "sync .claude"
|
|
8
|
+
- "sync .agents"
|
|
9
|
+
- "in-repo distribute"
|
|
10
|
+
examples:
|
|
11
|
+
- "self-sync the mirrors"
|
|
12
|
+
- "/self-sync --dry-run"
|
|
13
|
+
- "refresh .claude/skills from root"
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Self-Sync
|
|
17
|
+
|
|
18
|
+
Copies the framework's root-level entries into this repo's own `.claude/` and `.agents/` mirrors so agent tooling (Claude Code, Copilot, custom) picks up local edits without reinstalling or running `/distribute`.
|
|
19
|
+
|
|
20
|
+
Delegates the actual filesystem work to `lib/mirror.js`, the same helper used by `scripts/postinstall.js` on consumer installs. Two-directory mirror + delete reconciliation are the only differences from that consumer path.
|
|
21
|
+
|
|
22
|
+
## Instructions
|
|
23
|
+
|
|
24
|
+
1. If the user passed `--dry-run` (or asked for a preview), invoke:
|
|
25
|
+
```
|
|
26
|
+
node skills/self-sync/scripts/run.js --dry-run
|
|
27
|
+
```
|
|
28
|
+
Report the printed plan (added / overwritten / deleted) verbatim.
|
|
29
|
+
|
|
30
|
+
2. Otherwise, invoke:
|
|
31
|
+
```
|
|
32
|
+
node skills/self-sync/scripts/run.js
|
|
33
|
+
```
|
|
34
|
+
Report the printed summary. Do not run any inline `cp`, `rsync`, or `fs` commands — all mirroring goes through the script.
|
|
35
|
+
|
|
36
|
+
3. If the summary shows `+0 ~0 -0` on a second consecutive real run, the mirrors are already in sync — surface that as confirmation of idempotency.
|
|
37
|
+
|
|
38
|
+
## Copy Set
|
|
39
|
+
|
|
40
|
+
The script mirrors exactly these root-level entries:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
bin/ lib/ scripts/ agents/ hooks/ mcp/ skills/ templates/ settings.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This is an **explicit constant** in `scripts/run.js`, deliberately not derived at runtime from `package.json` `files`. Two reasons:
|
|
47
|
+
|
|
48
|
+
- `package.json` `files` includes `README.md` and `LICENSE`, which should not appear in the agent mirrors.
|
|
49
|
+
- `settings.json` is required in the mirrors but is intentionally not shipped in the npm tarball, so it does not appear in `files`.
|
|
50
|
+
|
|
51
|
+
The list is kept aligned with `package.json` `files` by convention. If a new top-level framework directory is added, update both.
|
|
52
|
+
|
|
53
|
+
## Invocation Model
|
|
54
|
+
|
|
55
|
+
**Manual invocation only.** The user runs `/self-sync` (or `node skills/self-sync/scripts/run.js`) after making framework edits and before expecting them to be visible to the current agent session.
|
|
56
|
+
|
|
57
|
+
Rationale: an on-`/commit` or file-watch trigger would fire on every edit — including drafts that shouldn't leak into the mirrors — and would obscure the write footprint (delete reconciliation prunes orphans, so accidental deletes on the source side propagate). Making the sync explicit keeps the human in the loop for a destructive-by-default operation, which matches the framework's boundary-checked, dry-runnable posture.
|
|
58
|
+
|
|
59
|
+
## Dry-Run Behavior
|
|
60
|
+
|
|
61
|
+
`--dry-run` computes the full plan (added / overwritten / deleted / unchanged) via `lib/mirror.js` without touching the filesystem. Use it before every real run when the mirror state is uncertain, or when running in a repo where uncommitted mirror edits might exist.
|
|
62
|
+
|
|
63
|
+
## Safety Guarantees
|
|
64
|
+
|
|
65
|
+
- **Never writes outside the repo root.** `lib/mirror.js` boundary-checks every destination path with `assertInside(destRoot, target)`; the helper throws if a resolved child escapes its destination root.
|
|
66
|
+
- **Additive helper + explicit reconciliation.** The mirror helper is additive by default; delete reconciliation is enabled only because this skill passes `reconcileDeletes: true`.
|
|
67
|
+
- **No shell out.** Pure Node built-ins (`node:fs`, `node:path`) — no `cp`, `rsync`, or subprocess calls.
|
|
68
|
+
- **Idempotent.** A second consecutive run reports `+0 ~0 -0` because the byte-comparison in `filesEqual` classifies unchanged files as `skipped`.
|
|
69
|
+
|
|
70
|
+
## Guard Rails
|
|
71
|
+
|
|
72
|
+
- Do not inline mirroring logic in this skill body — all filesystem work goes through `scripts/run.js` → `lib/mirror.js`.
|
|
73
|
+
- Do not run this skill from a directory other than a checkout of the framework repo itself. `scripts/postinstall.js` handles consumer installs; this skill handles the framework's own repo.
|