feat(scripts): add atomic file operations and safe temp files (skills-7bu)
This commit is contained in:
parent
e164285c6c
commit
6200abc32f
|
|
@ -30,7 +30,7 @@
|
||||||
{"id":"skills-8d4","title":"Compare CLI_REFERENCE.md with upstream","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-03T20:15:53.268324087-08:00","updated_at":"2025-12-03T20:17:26.552616779-08:00","closed_at":"2025-12-03T20:17:26.552616779-08:00","dependencies":[{"issue_id":"skills-8d4","depends_on_id":"skills-ebh","type":"discovered-from","created_at":"2025-12-03T20:15:53.27265681-08:00","created_by":"daemon","metadata":"{}"}]}
|
{"id":"skills-8d4","title":"Compare CLI_REFERENCE.md with upstream","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-03T20:15:53.268324087-08:00","updated_at":"2025-12-03T20:17:26.552616779-08:00","closed_at":"2025-12-03T20:17:26.552616779-08:00","dependencies":[{"issue_id":"skills-8d4","depends_on_id":"skills-ebh","type":"discovered-from","created_at":"2025-12-03T20:15:53.27265681-08:00","created_by":"daemon","metadata":"{}"}]}
|
||||||
{"id":"skills-8d9","title":"Add conversational patterns to orch skill","description":"## Context\nThe orch skill currently documents consensus and single-shot chat, but doesn't\nteach agents how to use orch for multi-turn conversations with external AIs.\n\n## Goal\nAdd documentation and patterns for agent-driven conversations where the calling\nagent (Claude Code) orchestrates multi-turn dialogues using orch primitives.\n\n## Patterns to document\n\n### Session-based multi-turn\n```bash\n# Initial query\nRESPONSE=$(orch chat \"Analyze this\" --model claude --format json)\nSESSION=$(echo \"$RESPONSE\" | jq -r .session_id)\n\n# Continue conversation\norch chat \"Elaborate on X\" --model claude --session $SESSION\n\n# Inspect state\norch sessions info $SESSION\norch sessions show $SESSION --last 2 --format text\n```\n\n### Cross-model dialogue\n```bash\n# Get one model's take\nCLAUDE=$(orch chat \"Review this\" --model claude --format json)\nCLAUDE_SAYS=$(echo \"$CLAUDE\" | jq -r '.responses[0].content')\n\n# Ask another model to respond\norch chat \"Claude said: $CLAUDE_SAYS\n\nWhat's your perspective?\" --model gemini\n```\n\n### When to use conversations vs consensus\n- Consensus: quick parallel opinions on a decision\n- Conversation: deeper exploration, follow-up questions, iterative refinement\n\n## Files\n- skills/orch/SKILL.md\n\n## Related\n- orch-c3r: Design: Session introspection for agent-driven conversations (in orch repo)","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-18T19:57:28.201494288-08:00","updated_at":"2025-12-29T15:34:16.254181578-05:00","closed_at":"2025-12-29T15:34:16.254181578-05:00","close_reason":"Added conversational patterns section to orch SKILL.md: sessions, cross-model dialogue, iterative refinement, consensus vs chat guidance."}
|
{"id":"skills-8d9","title":"Add conversational patterns to orch skill","description":"## Context\nThe orch skill currently documents consensus and single-shot chat, but doesn't\nteach agents how to use orch for multi-turn conversations with external AIs.\n\n## Goal\nAdd documentation and patterns for agent-driven conversations where the calling\nagent (Claude Code) orchestrates multi-turn dialogues using orch primitives.\n\n## Patterns to document\n\n### Session-based multi-turn\n```bash\n# Initial query\nRESPONSE=$(orch chat \"Analyze this\" --model claude --format json)\nSESSION=$(echo \"$RESPONSE\" | jq -r .session_id)\n\n# Continue conversation\norch chat \"Elaborate on X\" --model claude --session $SESSION\n\n# Inspect state\norch sessions info $SESSION\norch sessions show $SESSION --last 2 --format text\n```\n\n### Cross-model dialogue\n```bash\n# Get one model's take\nCLAUDE=$(orch chat \"Review this\" --model claude --format json)\nCLAUDE_SAYS=$(echo \"$CLAUDE\" | jq -r '.responses[0].content')\n\n# Ask another model to respond\norch chat \"Claude said: $CLAUDE_SAYS\n\nWhat's your perspective?\" --model gemini\n```\n\n### When to use conversations vs consensus\n- Consensus: quick parallel opinions on a decision\n- Conversation: deeper exploration, follow-up questions, iterative refinement\n\n## Files\n- skills/orch/SKILL.md\n\n## Related\n- orch-c3r: Design: Session introspection for agent-driven conversations (in orch repo)","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-12-18T19:57:28.201494288-08:00","updated_at":"2025-12-29T15:34:16.254181578-05:00","closed_at":"2025-12-29T15:34:16.254181578-05:00","close_reason":"Added conversational patterns section to orch SKILL.md: sessions, cross-model dialogue, iterative refinement, consensus vs chat guidance."}
|
||||||
{"id":"skills-8ma","title":"worklog skill: remove org-mode references, use markdown instead","description":"The worklog skill currently references org-mode format (.org files) in the template and instructions. Update to use markdown (.md) instead:\n\n1. Update ~/.claude/skills/worklog/templates/worklog-template.org → worklog-template.md\n2. Convert org-mode syntax to markdown (#+TITLE → # Title, * → ##, etc.)\n3. Update skill instructions to reference .md files\n4. Update suggest-filename.sh to output .md extension\n\nContext: org-mode is less widely supported than markdown in tooling and editors.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T08:43:55.761429693-05:00","created_by":"dan","updated_at":"2026-01-02T00:13:05.338810905-05:00","closed_at":"2026-01-02T00:13:05.338810905-05:00","close_reason":"Migrated worklog skill from org-mode to markdown. Template, scripts, and SKILL.md updated. Backward compatible with existing .org files."}
|
{"id":"skills-8ma","title":"worklog skill: remove org-mode references, use markdown instead","description":"The worklog skill currently references org-mode format (.org files) in the template and instructions. Update to use markdown (.md) instead:\n\n1. Update ~/.claude/skills/worklog/templates/worklog-template.org → worklog-template.md\n2. Convert org-mode syntax to markdown (#+TITLE → # Title, * → ##, etc.)\n3. Update skill instructions to reference .md files\n4. Update suggest-filename.sh to output .md extension\n\nContext: org-mode is less widely supported than markdown in tooling and editors.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-31T08:43:55.761429693-05:00","created_by":"dan","updated_at":"2026-01-02T00:13:05.338810905-05:00","closed_at":"2026-01-02T00:13:05.338810905-05:00","close_reason":"Migrated worklog skill from org-mode to markdown. Template, scripts, and SKILL.md updated. Backward compatible with existing .org files."}
|
||||||
{"id":"skills-8v0","title":"Consolidate skill list definitions (flake.nix + ai-skills.nix)","description":"Skill list duplicated in:\n- flake.nix (lines 15-27)\n- modules/ai-skills.nix (lines 8-18)\n\nIssues:\n- Manual sync required when adding skills\n- No validation that referenced skills exist\n\nFix:\n- Single source of truth for skill list\n- Consider generating one from the other\n\nSeverity: MEDIUM","status":"open","priority":3,"issue_type":"task","created_at":"2025-12-24T02:51:14.432158871-05:00","updated_at":"2025-12-24T02:51:14.432158871-05:00"}
|
{"id":"skills-8v0","title":"Consolidate skill list definitions (flake.nix + ai-skills.nix)","description":"Skill list duplicated in:\n- flake.nix (lines 15-27)\n- modules/ai-skills.nix (lines 8-18)\n\nIssues:\n- Manual sync required when adding skills\n- No validation that referenced skills exist\n\nFix:\n- Single source of truth for skill list\n- Consider generating one from the other\n\nSeverity: MEDIUM","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-24T02:51:14.432158871-05:00","updated_at":"2026-01-03T12:06:23.731969973-08:00","closed_at":"2026-01-03T12:06:23.731969973-08:00","close_reason":"Created skills.nix as single source of truth for skill names and descriptions. Updated flake.nix and Home Manager module to use it."}
|
||||||
{"id":"skills-8y6","title":"Define skill versioning strategy","description":"Git SHA alone is insufficient. Need tuple approach:\n\n- skill_source_rev: git SHA (if available)\n- skill_content_hash: hash of SKILL.md + scripts\n- runtime_ref: flake.lock hash or Nix store path\n\nQuestions to resolve:\n- Do Protos pin to versions (stable but maintenance) or float on latest (risky)?\n- How to handle breaking changes in skills?\n- Record in wisp trace vs proto definition?\n\nFrom consensus: both models flagged versioning instability as high severity.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T19:49:30.839064445-05:00","updated_at":"2025-12-23T20:55:04.439779336-05:00","closed_at":"2025-12-23T20:55:04.439779336-05:00","close_reason":"ADRs revised with orch consensus feedback"}
|
{"id":"skills-8y6","title":"Define skill versioning strategy","description":"Git SHA alone is insufficient. Need tuple approach:\n\n- skill_source_rev: git SHA (if available)\n- skill_content_hash: hash of SKILL.md + scripts\n- runtime_ref: flake.lock hash or Nix store path\n\nQuestions to resolve:\n- Do Protos pin to versions (stable but maintenance) or float on latest (risky)?\n- How to handle breaking changes in skills?\n- Record in wisp trace vs proto definition?\n\nFrom consensus: both models flagged versioning instability as high severity.","status":"closed","priority":2,"issue_type":"task","created_at":"2025-12-23T19:49:30.839064445-05:00","updated_at":"2025-12-23T20:55:04.439779336-05:00","closed_at":"2025-12-23T20:55:04.439779336-05:00","close_reason":"ADRs revised with orch consensus feedback"}
|
||||||
{"id":"skills-9af","title":"spec-review: Add spike/research task handling","description":"Tasks like 'Investigate X' can linger without clear outcomes.\n\nAdd to REVIEW_TASKS:\n- Flag research/spike tasks\n- Require timebox and concrete outputs (decision record, prototype, risks)\n- Pattern for handling unknowns","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-15T00:23:26.887719136-08:00","updated_at":"2025-12-15T14:08:13.441095034-08:00","closed_at":"2025-12-15T14:08:13.441095034-08:00"}
|
{"id":"skills-9af","title":"spec-review: Add spike/research task handling","description":"Tasks like 'Investigate X' can linger without clear outcomes.\n\nAdd to REVIEW_TASKS:\n- Flag research/spike tasks\n- Require timebox and concrete outputs (decision record, prototype, risks)\n- Pattern for handling unknowns","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-15T00:23:26.887719136-08:00","updated_at":"2025-12-15T14:08:13.441095034-08:00","closed_at":"2025-12-15T14:08:13.441095034-08:00"}
|
||||||
{"id":"skills-9bc","title":"Investigate pre-compression hook for worklogs","description":"## Revised Understanding\n\nClaude Code already persists full conversation history in `~/.claude/projects/\u003cproject\u003e/\u003csession-id\u003e.jsonl`. Pre-compact hooks aren't needed for data capture.\n\n## Question\nWhat's the ideal workflow for generating worklogs from session data?\n\n## Options\n\n### 1. Post-session script\n- Run after exiting Claude Code\n- Reads most recent session JSONL\n- Generates worklog from conversation content\n- Pro: Async, doesn't interrupt flow\n- Con: May forget to run it\n\n### 2. On-demand slash command\n- `/worklog-from-session` or similar\n- Reads current session's JSONL file\n- Generates worklog with full context\n- Pro: Explicit control\n- Con: Still need to remember\n\n### 3. Pre-compact reminder\n- Hook prints reminder: \"Consider running /worklog\"\n- Doesn't automate, just nudges\n- Pro: Simple, non-intrusive\n- Con: Easy to dismiss\n\n### 4. Async batch processing\n- Process old sessions whenever\n- All data persists in JSONL files\n- Pro: No urgency, can do later\n- Con: Context may be stale\n\n## Data Format\nSession files contain:\n- User messages with timestamp\n- Assistant responses with model info\n- Tool calls and results\n- Git branch, cwd, version info\n\n## Next Steps\n- Decide preferred workflow\n- Build script to parse session JSONL → worklog format","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-17T14:32:32.568430817-08:00","updated_at":"2025-12-17T15:56:38.864916015-08:00","closed_at":"2025-12-17T15:56:38.864916015-08:00","close_reason":"Pivoted: worklogs may be redundant given full conversation persistence. New approach: make conversations searchable directly."}
|
{"id":"skills-9bc","title":"Investigate pre-compression hook for worklogs","description":"## Revised Understanding\n\nClaude Code already persists full conversation history in `~/.claude/projects/\u003cproject\u003e/\u003csession-id\u003e.jsonl`. Pre-compact hooks aren't needed for data capture.\n\n## Question\nWhat's the ideal workflow for generating worklogs from session data?\n\n## Options\n\n### 1. Post-session script\n- Run after exiting Claude Code\n- Reads most recent session JSONL\n- Generates worklog from conversation content\n- Pro: Async, doesn't interrupt flow\n- Con: May forget to run it\n\n### 2. On-demand slash command\n- `/worklog-from-session` or similar\n- Reads current session's JSONL file\n- Generates worklog with full context\n- Pro: Explicit control\n- Con: Still need to remember\n\n### 3. Pre-compact reminder\n- Hook prints reminder: \"Consider running /worklog\"\n- Doesn't automate, just nudges\n- Pro: Simple, non-intrusive\n- Con: Easy to dismiss\n\n### 4. Async batch processing\n- Process old sessions whenever\n- All data persists in JSONL files\n- Pro: No urgency, can do later\n- Con: Context may be stale\n\n## Data Format\nSession files contain:\n- User messages with timestamp\n- Assistant responses with model info\n- Tool calls and results\n- Git branch, cwd, version info\n\n## Next Steps\n- Decide preferred workflow\n- Build script to parse session JSONL → worklog format","status":"closed","priority":3,"issue_type":"task","created_at":"2025-12-17T14:32:32.568430817-08:00","updated_at":"2025-12-17T15:56:38.864916015-08:00","closed_at":"2025-12-17T15:56:38.864916015-08:00","close_reason":"Pivoted: worklogs may be redundant given full conversation persistence. New approach: make conversations searchable directly."}
|
||||||
|
|
|
||||||
|
|
@ -327,29 +327,37 @@ create_new_agent_file() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local substitutions=(
|
local substitutions=(
|
||||||
"s|\[PROJECT NAME\]|$project_name|"
|
"-e" "s|\[PROJECT NAME\]|$project_name|"
|
||||||
"s|\[DATE\]|$current_date|"
|
"-e" "s|\[DATE\]|$current_date|"
|
||||||
"s|\[EXTRACTED FROM ALL PLAN.MD FILES\]|$tech_stack|"
|
"-e" "s|\[EXTRACTED FROM ALL PLAN.MD FILES\]|$tech_stack|"
|
||||||
"s|\[ACTUAL STRUCTURE FROM PLANS\]|$project_structure|g"
|
"-e" "s|\[ACTUAL STRUCTURE FROM PLANS\]|$project_structure|g"
|
||||||
"s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$commands|"
|
"-e" "s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$commands|"
|
||||||
"s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$language_conventions|"
|
"-e" "s|\[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE\]|$language_conventions|"
|
||||||
"s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|$recent_change|"
|
"-e" "s|\[LAST 3 FEATURES AND WHAT THEY ADDED\]|$recent_change|"
|
||||||
)
|
)
|
||||||
|
|
||||||
for substitution in "${substitutions[@]}"; do
|
# Perform all substitutions in one pass to a second temp file
|
||||||
if ! sed -i.bak -e "$substitution" "$temp_file"; then
|
local temp_file_final
|
||||||
log_error "Failed to perform substitution: $substitution"
|
temp_file_final="${temp_file}.final"
|
||||||
rm -f "$temp_file" "$temp_file.bak"
|
|
||||||
return 1
|
if ! sed "${substitutions[@]}" "$temp_file" > "$temp_file_final"; then
|
||||||
fi
|
log_error "Failed to perform substitutions"
|
||||||
done
|
rm -f "$temp_file_final"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Replace the working temp file
|
||||||
|
mv "$temp_file_final" "$temp_file"
|
||||||
|
|
||||||
# Convert \n sequences to actual newlines
|
# Convert \n sequences to actual newlines
|
||||||
newline=$(printf '\n')
|
newline=$(printf '\n')
|
||||||
sed -i.bak2 "s/\\\\n/${newline}/g" "$temp_file"
|
temp_file_nl="${temp_file}.nl"
|
||||||
|
if ! sed "s/\\\\n/${newline}/g" "$temp_file" > "$temp_file_nl"; then
|
||||||
# Clean up backup files
|
log_error "Failed to convert newlines"
|
||||||
rm -f "$temp_file.bak" "$temp_file.bak2"
|
rm -f "$temp_file_nl"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
mv "$temp_file_nl" "$temp_file"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,15 +123,45 @@ inject_nix_config() {
|
||||||
echo "ℹ️ Config already present in $(basename "$target_file")"
|
echo "ℹ️ Config already present in $(basename "$target_file")"
|
||||||
else
|
else
|
||||||
echo "Injecting config into $(basename "$target_file")..."
|
echo "Injecting config into $(basename "$target_file")..."
|
||||||
# Create backup
|
|
||||||
cp "$target_file" "${target_file}.bak"
|
# Create a secure temporary file
|
||||||
|
local temp_file
|
||||||
|
temp_file=$(mktemp "${target_file}.XXXXXX")
|
||||||
|
# Ensure cleanup on exit or error
|
||||||
|
trap 'rm -f "$temp_file"' EXIT
|
||||||
|
|
||||||
# Insert before the last line (assuming it is '}')
|
# Insert before the last line (assuming it is '}')
|
||||||
# We use a temporary file to construct the new content
|
if ! head -n -1 "$target_file" > "$temp_file"; then
|
||||||
head -n -1 "$target_file" > "${target_file}.tmp"
|
echo "Error: failed to read $target_file" >&2
|
||||||
echo "$config_block" >> "${target_file}.tmp"
|
return 1
|
||||||
tail -n 1 "$target_file" >> "${target_file}.tmp"
|
fi
|
||||||
mv "${target_file}.tmp" "$target_file"
|
|
||||||
|
echo "$config_block" >> "$temp_file"
|
||||||
|
|
||||||
|
if ! tail -n 1 "$target_file" >> "$temp_file"; then
|
||||||
|
echo "Error: failed to append to $temp_file" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate: temp file should be larger than original (since we're adding)
|
||||||
|
local orig_size
|
||||||
|
orig_size=$(stat -c%s "$target_file")
|
||||||
|
local new_size
|
||||||
|
new_size=$(stat -c%s "$temp_file")
|
||||||
|
|
||||||
|
if [[ $new_size -le $orig_size ]]; then
|
||||||
|
echo "Error: Validation failed, new file is not larger than original" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Atomic move
|
||||||
|
if ! mv "$temp_file" "$target_file"; then
|
||||||
|
echo "Error: Failed to replace $target_file" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clear trap after successful move
|
||||||
|
trap - EXIT
|
||||||
echo "✓ Updated $(basename "$target_file")"
|
echo "✓ Updated $(basename "$target_file")"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,13 +113,34 @@ if [[ "$DRY_RUN" == true ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Perform atomic update using sed
|
# Perform atomic update
|
||||||
sed -i \
|
TEMP_FILE=$(mktemp "${NIX_FILE}.XXXXXX")
|
||||||
|
trap 'rm -f "$TEMP_FILE"' EXIT
|
||||||
|
|
||||||
|
if ! sed \
|
||||||
-e "s/version = \"[^\"]*\"/version = \"$VERSION\"/" \
|
-e "s/version = \"[^\"]*\"/version = \"$VERSION\"/" \
|
||||||
-e "s/sha256 = \"[^\"]*\"/sha256 = \"$SHA256\"/" \
|
-e "s/sha256 = \"[^\"]*\"/sha256 = \"$SHA256\"/" \
|
||||||
"$NIX_FILE"
|
"$NIX_FILE" > "$TEMP_FILE"; then
|
||||||
|
echo "Error: failed to generate updated Nix file" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Verify update succeeded
|
# Basic validation: check if it's not empty and contains expected values
|
||||||
|
if [[ ! -s "$TEMP_FILE" ]]; then
|
||||||
|
echo "Error: Generated file is empty" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -q "version = \"$VERSION\"" "$TEMP_FILE" || ! grep -q "sha256 = \"$SHA256\"" "$TEMP_FILE"; then
|
||||||
|
echo "Error: Generated file validation failed (expected patterns not found)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Atomic move
|
||||||
|
mv "$TEMP_FILE" "$NIX_FILE"
|
||||||
|
trap - EXIT
|
||||||
|
|
||||||
|
# Verify update succeeded (extra safety check)
|
||||||
UPDATED_VERSION=$(grep -oP 'version\s*=\s*"\K[^"]+' "$NIX_FILE")
|
UPDATED_VERSION=$(grep -oP 'version\s*=\s*"\K[^"]+' "$NIX_FILE")
|
||||||
UPDATED_SHA256=$(grep -oP 'sha256\s*=\s*"\K[^"]+' "$NIX_FILE")
|
UPDATED_SHA256=$(grep -oP 'sha256\s*=\s*"\K[^"]+' "$NIX_FILE")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue