@kennethsolomon/shipkit 3.10.0 → 3.10.1
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/package.json +1 -1
- package/skills/.claude/settings.local.json +20 -1
- package/skills/sk:brainstorming/SKILL.md +6 -6
- package/skills/sk:debug/__pycache__/debug_conductor.cpython-314.pyc +0 -0
- package/skills/sk:debug/debug_conductor.py +0 -8
- package/skills/sk:debug/lib/__pycache__/findings_writer.cpython-314.pyc +0 -0
- package/skills/sk:debug/lib/__pycache__/lessons_writer.cpython-314.pyc +0 -0
- package/skills/sk:debug/lib/__pycache__/step_runner.cpython-314.pyc +0 -0
- package/skills/sk:debug/lib/findings_writer.py +2 -2
- package/skills/sk:debug/lib/lessons_writer.py +2 -2
- package/skills/sk:debug/lib/step_runner.py +8 -10
- package/skills/sk:lint/SKILL.md +2 -2
- package/skills/sk:setup-claude/scripts/__pycache__/apply_setup_claude.cpython-314.pyc +0 -0
- package/skills/sk:setup-claude/scripts/apply_setup_claude.py +12 -3
- package/skills/sk:setup-optimizer/lib/__pycache__/discover.cpython-314.pyc +0 -0
- package/skills/sk:setup-optimizer/lib/discover.py +9 -10
- package/skills/sk:skill-creator/scripts/__pycache__/generate_report.cpython-314.pyc +0 -0
- package/skills/sk:skill-creator/scripts/__pycache__/improve_description.cpython-314.pyc +0 -0
- package/skills/sk:skill-creator/scripts/__pycache__/package_skill.cpython-314.pyc +0 -0
- package/skills/sk:skill-creator/scripts/__pycache__/run_eval.cpython-314.pyc +0 -0
- package/skills/sk:skill-creator/scripts/__pycache__/run_loop.cpython-314.pyc +0 -0
- package/skills/sk:skill-creator/scripts/__pycache__/utils.cpython-314.pyc +0 -0
- package/skills/sk:skill-creator/scripts/generate_report.py +2 -0
- package/skills/sk:skill-creator/scripts/improve_description.py +2 -0
- package/skills/sk:skill-creator/scripts/package_skill.py +4 -0
- package/skills/sk:skill-creator/scripts/run_eval.py +3 -1
- package/skills/sk:skill-creator/scripts/run_loop.py +2 -0
- package/skills/sk:skill-creator/scripts/utils.py +2 -0
- package/skills/sk:test/SKILL.md +2 -2
package/package.json
CHANGED
|
@@ -12,7 +12,26 @@
|
|
|
12
12
|
"Bash(done)",
|
|
13
13
|
"Bash(tasks/security-findings.md:*)",
|
|
14
14
|
"Bash(git push:*)",
|
|
15
|
-
"Bash(git tag:*)"
|
|
15
|
+
"Bash(git tag:*)",
|
|
16
|
+
"Bash(find /Users/kennethsolomon/Herd/shipit -type f -name *.md -path */node_modules/*commands/sk/*)",
|
|
17
|
+
"Bash(grep -r \"sk:\" /Users/kennethsolomon/Herd/shipit/commands/sk/*.md)",
|
|
18
|
+
"Bash(find /Users/kennethsolomon/Herd/shipit/.claude/docs -name *.md)",
|
|
19
|
+
"Bash(grep -v \"^Command$\")",
|
|
20
|
+
"Bash(find /Users/kennethsolomon/Herd/shipit -path */test* -o -path */__tests__/* -o -path */spec/*)",
|
|
21
|
+
"Bash(find /Users/kennethsolomon/Herd/shipit/commands/sk -type f -name *.md)",
|
|
22
|
+
"Bash(find /Users/kennethsolomon/Herd/shipit/skills -maxdepth 1 -type d -name sk:*)",
|
|
23
|
+
"Bash(python3 -m py_compile skills/sk:debug/lib/step_runner.py)",
|
|
24
|
+
"Bash(python3 -m py_compile skills/sk:debug/debug_conductor.py)",
|
|
25
|
+
"Bash(python3 -m py_compile skills/sk:setup-optimizer/lib/discover.py)",
|
|
26
|
+
"Bash(python3 -m py_compile skills/sk:debug/lib/findings_writer.py)",
|
|
27
|
+
"Bash(python3 -m py_compile skills/sk:debug/lib/lessons_writer.py)",
|
|
28
|
+
"Bash(python3 -m py_compile skills/sk:skill-creator/scripts/run_eval.py)",
|
|
29
|
+
"Bash(python3 -m py_compile skills/sk:skill-creator/scripts/run_loop.py)",
|
|
30
|
+
"Bash(python3 -m py_compile skills/sk:skill-creator/scripts/improve_description.py)",
|
|
31
|
+
"Bash(python3 -m py_compile skills/sk:skill-creator/scripts/generate_report.py)",
|
|
32
|
+
"Bash(python3 -m py_compile skills/sk:skill-creator/scripts/utils.py)",
|
|
33
|
+
"Bash(git:*)",
|
|
34
|
+
"Bash(npm publish:*)"
|
|
16
35
|
]
|
|
17
36
|
}
|
|
18
37
|
}
|
|
@@ -34,7 +34,7 @@ You MUST create a task for each of these items and complete them in order:
|
|
|
34
34
|
- Open questions (if any remain)
|
|
35
35
|
Additionally, **append** an ADR entry to `docs/decisions.md` (see "Decisions Log" section below).
|
|
36
36
|
(Optionally also write a full design doc to docs/plans/YYYY-MM-DD-<topic>-design.md)
|
|
37
|
-
6. **Transition to implementation** — invoke
|
|
37
|
+
6. **Transition to implementation** — invoke `/sk:write-plan` skill to create implementation plan
|
|
38
38
|
|
|
39
39
|
## Process Flow
|
|
40
40
|
|
|
@@ -46,7 +46,7 @@ digraph brainstorming {
|
|
|
46
46
|
"Present design sections" [shape=box];
|
|
47
47
|
"User approves design?" [shape=diamond];
|
|
48
48
|
"Write design doc" [shape=box];
|
|
49
|
-
"Invoke
|
|
49
|
+
"Invoke `/sk:write-plan` skill" [shape=doublecircle];
|
|
50
50
|
|
|
51
51
|
"Explore project context" -> "Ask clarifying questions";
|
|
52
52
|
"Ask clarifying questions" -> "Propose 2-3 approaches";
|
|
@@ -54,11 +54,11 @@ digraph brainstorming {
|
|
|
54
54
|
"Present design sections" -> "User approves design?";
|
|
55
55
|
"User approves design?" -> "Present design sections" [label="no, revise"];
|
|
56
56
|
"User approves design?" -> "Write design doc" [label="yes"];
|
|
57
|
-
"Write design doc" -> "Invoke
|
|
57
|
+
"Write design doc" -> "Invoke `/sk:write-plan` skill";
|
|
58
58
|
}
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
**The terminal state is invoking
|
|
61
|
+
**The terminal state is invoking `/sk:write-plan`.** Do NOT invoke frontend-design, mcp-builder, or any other implementation skill. The ONLY skill you invoke after brainstorming is `/sk:write-plan`.
|
|
62
62
|
|
|
63
63
|
## The Process
|
|
64
64
|
|
|
@@ -95,8 +95,8 @@ digraph brainstorming {
|
|
|
95
95
|
- Commit the findings, decisions log entry, and any design document to git
|
|
96
96
|
|
|
97
97
|
**Implementation:**
|
|
98
|
-
- Invoke the
|
|
99
|
-
- Do NOT invoke any other skill.
|
|
98
|
+
- Invoke the `/sk:write-plan` skill to create a detailed implementation plan
|
|
99
|
+
- Do NOT invoke any other skill. `/sk:write-plan` is the next step.
|
|
100
100
|
|
|
101
101
|
## Decisions Log
|
|
102
102
|
|
|
Binary file
|
|
@@ -75,7 +75,6 @@ class DebugConductor:
|
|
|
75
75
|
if not self._can_proceed(step):
|
|
76
76
|
print(f"\n⛔ BLOCKED: {self._gate_reason(step)}")
|
|
77
77
|
print("\nRun /debug again to continue from this step.")
|
|
78
|
-
self._save_progress()
|
|
79
78
|
return
|
|
80
79
|
|
|
81
80
|
# Run step and update state
|
|
@@ -94,7 +93,6 @@ class DebugConductor:
|
|
|
94
93
|
proceed = input(f"\n✅ Step {step_num} complete. Continue to step {step_num + 1}? (y/n): ").strip().lower()
|
|
95
94
|
if proceed not in ['y', 'yes', '']:
|
|
96
95
|
print(f"Pausing after step {step_num}. Run /debug again to continue.")
|
|
97
|
-
self._save_progress()
|
|
98
96
|
return
|
|
99
97
|
|
|
100
98
|
print("\n" + "="*70)
|
|
@@ -161,12 +159,6 @@ class DebugConductor:
|
|
|
161
159
|
return "No confirmed hypothesis yet—cannot propose fix."
|
|
162
160
|
return "Precondition not met for this step."
|
|
163
161
|
|
|
164
|
-
def _save_progress(self):
|
|
165
|
-
"""Save current state for resumption (optional)."""
|
|
166
|
-
# Could implement checkpoint system here
|
|
167
|
-
pass
|
|
168
|
-
|
|
169
|
-
|
|
170
162
|
def main():
|
|
171
163
|
"""Entry point for /debug skill."""
|
|
172
164
|
conductor = DebugConductor()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -44,8 +44,8 @@ class FindingsWriter:
|
|
|
44
44
|
self._append_entry(entry)
|
|
45
45
|
print(f"\n✅ Findings written to {self.findings_path}")
|
|
46
46
|
return True
|
|
47
|
-
except
|
|
48
|
-
print(f"\n❌ Error writing findings: {e}")
|
|
47
|
+
except OSError as e:
|
|
48
|
+
print(f"\n❌ Error writing findings to {self.findings_path}: {e}")
|
|
49
49
|
return False
|
|
50
50
|
|
|
51
51
|
def _ensure_file_exists(self):
|
|
@@ -66,8 +66,8 @@ class LessonsWriter:
|
|
|
66
66
|
self._append_lesson(entry)
|
|
67
67
|
print(f"\n✅ New lesson added to {self.lessons_path}")
|
|
68
68
|
return True
|
|
69
|
-
except
|
|
70
|
-
print(f"\n❌ Error writing lesson: {e}")
|
|
69
|
+
except OSError as e:
|
|
70
|
+
print(f"\n❌ Error writing lesson to {self.lessons_path}: {e}")
|
|
71
71
|
return False
|
|
72
72
|
|
|
73
73
|
def _ensure_file_exists(self):
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Execute individual debug steps (3-11)."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shlex
|
|
3
6
|
import subprocess
|
|
4
|
-
from typing import Dict
|
|
5
7
|
from pathlib import Path
|
|
6
|
-
import
|
|
8
|
+
from typing import Dict
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class StepRunner:
|
|
@@ -42,8 +44,7 @@ class StepRunner:
|
|
|
42
44
|
# Recent commits
|
|
43
45
|
print("📋 Recent commits:")
|
|
44
46
|
result = subprocess.run(
|
|
45
|
-
"git log --oneline -10",
|
|
46
|
-
shell=True,
|
|
47
|
+
["git", "log", "--oneline", "-10"],
|
|
47
48
|
capture_output=True,
|
|
48
49
|
text=True
|
|
49
50
|
)
|
|
@@ -52,8 +53,7 @@ class StepRunner:
|
|
|
52
53
|
# Recent diffs
|
|
53
54
|
print("\n📋 Recent file changes:")
|
|
54
55
|
result = subprocess.run(
|
|
55
|
-
"git diff HEAD~3 --stat",
|
|
56
|
-
shell=True,
|
|
56
|
+
["git", "diff", "HEAD~3", "--stat"],
|
|
57
57
|
capture_output=True,
|
|
58
58
|
text=True
|
|
59
59
|
)
|
|
@@ -107,8 +107,7 @@ class StepRunner:
|
|
|
107
107
|
|
|
108
108
|
try:
|
|
109
109
|
result = subprocess.run(
|
|
110
|
-
command,
|
|
111
|
-
shell=True,
|
|
110
|
+
shlex.split(command),
|
|
112
111
|
capture_output=True,
|
|
113
112
|
text=True,
|
|
114
113
|
timeout=30
|
|
@@ -277,8 +276,7 @@ class StepRunner:
|
|
|
277
276
|
if command:
|
|
278
277
|
try:
|
|
279
278
|
result = subprocess.run(
|
|
280
|
-
command,
|
|
281
|
-
shell=True,
|
|
279
|
+
shlex.split(command),
|
|
282
280
|
capture_output=True,
|
|
283
281
|
text=True,
|
|
284
282
|
timeout=30
|
package/skills/sk:lint/SKILL.md
CHANGED
|
@@ -171,8 +171,8 @@ Read `.shipkit/config.json` from the project root if it exists.
|
|
|
171
171
|
|
|
172
172
|
| Profile | Model |
|
|
173
173
|
|---------|-------|
|
|
174
|
-
| `full-sail` |
|
|
175
|
-
| `quality` |
|
|
174
|
+
| `full-sail` | opus (inherit) |
|
|
175
|
+
| `quality` | opus (inherit) |
|
|
176
176
|
| `balanced` | haiku |
|
|
177
177
|
| `budget` | haiku |
|
|
178
178
|
|
|
Binary file
|
|
@@ -271,7 +271,16 @@ def _write_file_if_missing(dest: Path, content: str) -> Tuple[str, Path]:
|
|
|
271
271
|
if dest.exists():
|
|
272
272
|
return ("skipped", dest)
|
|
273
273
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
274
|
-
|
|
274
|
+
# Atomic write: write to temp file then rename to avoid TOCTOU race
|
|
275
|
+
import tempfile
|
|
276
|
+
fd, tmp_path = tempfile.mkstemp(dir=dest.parent, suffix=".tmp")
|
|
277
|
+
try:
|
|
278
|
+
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
|
279
|
+
f.write(content)
|
|
280
|
+
Path(tmp_path).replace(dest)
|
|
281
|
+
except BaseException:
|
|
282
|
+
Path(tmp_path).unlink(missing_ok=True)
|
|
283
|
+
raise
|
|
275
284
|
return ("created", dest)
|
|
276
285
|
|
|
277
286
|
|
|
@@ -282,7 +291,7 @@ def _write_file_if_generated(dest: Path, content: str, template_path: Optional[P
|
|
|
282
291
|
return ("created", dest)
|
|
283
292
|
try:
|
|
284
293
|
existing = dest.read_text(encoding="utf-8")
|
|
285
|
-
except
|
|
294
|
+
except (OSError, UnicodeDecodeError):
|
|
286
295
|
return ("skipped", dest)
|
|
287
296
|
if GENERATED_MARKER in existing:
|
|
288
297
|
# Check if content is identical (fast path)
|
|
@@ -311,7 +320,7 @@ def _plan_file_if_generated(dest: Path, content: str) -> str:
|
|
|
311
320
|
return "created"
|
|
312
321
|
try:
|
|
313
322
|
existing = dest.read_text(encoding="utf-8")
|
|
314
|
-
except
|
|
323
|
+
except (OSError, UnicodeDecodeError):
|
|
315
324
|
return "skipped"
|
|
316
325
|
if GENERATED_MARKER not in existing:
|
|
317
326
|
return "skipped"
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Intelligent project structure, documentation, and workflow discovery."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
from typing import Dict, List
|
|
5
|
+
from typing import Dict, List
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
# Directories to exclude from discovery
|
|
@@ -174,16 +175,14 @@ def _extract_makefile_targets(root_path: Path) -> List[str]:
|
|
|
174
175
|
target = line.split(':')[0].strip()
|
|
175
176
|
if target and not target.startswith('.'):
|
|
176
177
|
targets.append(target)
|
|
177
|
-
except
|
|
178
|
-
|
|
178
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
179
|
+
print(f"Warning: Could not parse Makefile: {e}")
|
|
179
180
|
|
|
180
181
|
return list(set(targets))[:10] # Limit to 10 targets
|
|
181
182
|
|
|
182
183
|
|
|
183
184
|
def _extract_npm_scripts(root_path: Path) -> List[str]:
|
|
184
185
|
"""Extract scripts from package.json."""
|
|
185
|
-
import json
|
|
186
|
-
|
|
187
186
|
package_json = root_path / 'package.json'
|
|
188
187
|
if not package_json.exists():
|
|
189
188
|
return []
|
|
@@ -191,14 +190,14 @@ def _extract_npm_scripts(root_path: Path) -> List[str]:
|
|
|
191
190
|
scripts = []
|
|
192
191
|
try:
|
|
193
192
|
content = json.loads(package_json.read_text())
|
|
194
|
-
if 'scripts' in content:
|
|
193
|
+
if isinstance(content, dict) and 'scripts' in content:
|
|
195
194
|
# Include common scripts, exclude default ones
|
|
196
195
|
exclude = {'test', 'start', 'dev', 'build'}
|
|
197
196
|
for script_name in content['scripts'].keys():
|
|
198
197
|
if script_name not in exclude:
|
|
199
198
|
scripts.append(script_name)
|
|
200
|
-
except
|
|
201
|
-
|
|
199
|
+
except (OSError, json.JSONDecodeError, UnicodeDecodeError) as e:
|
|
200
|
+
print(f"Warning: Could not parse package.json scripts: {e}")
|
|
202
201
|
|
|
203
202
|
return scripts[:8] # Limit to 8 scripts
|
|
204
203
|
|
|
@@ -215,7 +214,7 @@ def _find_github_workflows(root_path: Path) -> List[str]:
|
|
|
215
214
|
workflows.append(workflow_file.stem)
|
|
216
215
|
for workflow_file in workflows_dir.glob('*.yaml'):
|
|
217
216
|
workflows.append(workflow_file.stem)
|
|
218
|
-
except
|
|
219
|
-
|
|
217
|
+
except OSError as e:
|
|
218
|
+
print(f"Warning: Could not read GitHub workflows: {e}")
|
|
220
219
|
|
|
221
220
|
return workflows[:5] # Limit to 5 workflows
|
|
Binary file
|
|
Binary file
|
|
@@ -93,6 +93,10 @@ def package_skill(skill_path, output_dir=None):
|
|
|
93
93
|
for file_path in skill_path.rglob('*'):
|
|
94
94
|
if not file_path.is_file():
|
|
95
95
|
continue
|
|
96
|
+
# Skip symlinks to prevent path traversal
|
|
97
|
+
if file_path.is_symlink():
|
|
98
|
+
print(f" Skipped (symlink): {file_path}")
|
|
99
|
+
continue
|
|
96
100
|
arcname = file_path.relative_to(skill_path.parent)
|
|
97
101
|
if should_exclude(arcname):
|
|
98
102
|
print(f" Skipped: {arcname}")
|
|
@@ -5,6 +5,8 @@ Tests whether a skill's description causes Claude to trigger (read the skill)
|
|
|
5
5
|
for a set of queries. Outputs results as JSON.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
8
10
|
import argparse
|
|
9
11
|
import json
|
|
10
12
|
import os
|
|
@@ -221,7 +223,7 @@ def run_eval(
|
|
|
221
223
|
try:
|
|
222
224
|
query_triggers[query].append(future.result())
|
|
223
225
|
except Exception as e:
|
|
224
|
-
print(f"Warning: query failed: {e}", file=sys.stderr)
|
|
226
|
+
print(f"Warning: query failed for '{query[:60]}': {type(e).__name__}: {e}", file=sys.stderr)
|
|
225
227
|
query_triggers[query].append(False)
|
|
226
228
|
|
|
227
229
|
for query, triggers in query_triggers.items():
|
package/skills/sk:test/SKILL.md
CHANGED
|
@@ -184,8 +184,8 @@ Read `.shipkit/config.json` from the project root if it exists.
|
|
|
184
184
|
|
|
185
185
|
| Profile | Model |
|
|
186
186
|
|---------|-------|
|
|
187
|
-
| `full-sail` |
|
|
188
|
-
| `quality` |
|
|
187
|
+
| `full-sail` | opus (inherit) |
|
|
188
|
+
| `quality` | opus (inherit) |
|
|
189
189
|
| `balanced` | haiku |
|
|
190
190
|
| `budget` | haiku |
|
|
191
191
|
|