spec-and-loop 1.0.7 → 1.0.8

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-and-loop",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "OpenSpec + Ralph Loop integration for iterative development with opencode",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -13,6 +13,16 @@ detect_os() {
13
13
 
14
14
  detect_os
15
15
 
16
+ # Cross-platform file modification time
17
+ get_file_mtime() {
18
+ local file="$1"
19
+ if [[ "$OS" == "macOS" ]]; then
20
+ stat -f %m "$file" 2>/dev/null || echo 0
21
+ else
22
+ stat -c %Y "$file" 2>/dev/null || echo 0
23
+ fi
24
+ }
25
+
16
26
  # Cross-platform file modification time display
17
27
  get_file_mtime_display() {
18
28
  local file="$1"
@@ -23,20 +33,57 @@ get_file_mtime_display() {
23
33
  fi
24
34
  }
25
35
 
26
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
36
+ resolve_project_root() {
37
+ local git_root=""
38
+ git_root=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
39
+
40
+ if [[ -n "$git_root" ]]; then
41
+ printf "%s" "$git_root"
42
+ else
43
+ pwd
44
+ fi
45
+ }
46
+
47
+ auto_detect_change() {
48
+ local project_root="$1"
49
+ local changes_dir="$project_root/openspec/changes"
50
+
51
+ if [[ ! -d "$changes_dir" ]]; then
52
+ echo ""
53
+ return 1
54
+ fi
55
+
56
+ local latest_change=""
57
+ local latest_time=0
58
+
59
+ for change_dir in "$changes_dir"/*; do
60
+ if [[ -d "$change_dir" && -f "$change_dir/tasks.md" ]]; then
61
+ local mod_time
62
+ mod_time=$(get_file_mtime "$change_dir/tasks.md")
63
+
64
+ if [[ "$mod_time" -gt "$latest_time" ]]; then
65
+ latest_time="$mod_time"
66
+ latest_change=$(basename "$change_dir")
67
+ fi
68
+ fi
69
+ done
70
+
71
+ printf "%s" "$latest_change"
72
+ }
73
+
74
+ PROJECT_ROOT="$(resolve_project_root)"
27
75
  CHANGE_NAME="${1:-auto-detect}"
28
76
 
29
77
  if [[ "$CHANGE_NAME" == "auto-detect" ]]; then
30
- # Auto-detect most recent change
31
- CHANGE_NAME=$(ls -t openspec/changes/ 2>/dev/null | head -1)
78
+ CHANGE_NAME=$(auto_detect_change "$PROJECT_ROOT")
32
79
  if [[ -z "$CHANGE_NAME" ]]; then
33
- echo "No changes found in openspec/changes/"
80
+ echo "No changes found in $PROJECT_ROOT/openspec/changes/"
34
81
  exit 1
35
82
  fi
36
83
  fi
37
84
 
38
- TASKS_FILE="$SCRIPT_DIR/../openspec/changes/$CHANGE_NAME/tasks.md"
39
- RALPH_STATE="$SCRIPT_DIR/../.ralph/ralph-loop.state.json"
85
+ TASKS_FILE="$PROJECT_ROOT/openspec/changes/$CHANGE_NAME/tasks.md"
86
+ RALPH_STATE="$PROJECT_ROOT/.ralph/ralph-loop.state.json"
40
87
 
41
88
  if [[ ! -f "$TASKS_FILE" ]]; then
42
89
  echo "Tasks file not found: $TASKS_FILE"
@@ -61,6 +61,64 @@ get_realpath() {
61
61
  fi
62
62
  }
63
63
 
64
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
65
+ LOCAL_NODE_BIN="$SCRIPT_DIR/../node_modules/.bin"
66
+ BUNDLED_RALPH_JS="$SCRIPT_DIR/../node_modules/@th0rgal/ralph-wiggum/bin/ralph.js"
67
+ RALPH_CMD=()
68
+
69
+ if [[ -d "$LOCAL_NODE_BIN" ]]; then
70
+ case ":$PATH:" in
71
+ *":$LOCAL_NODE_BIN:"*) ;;
72
+ *) export PATH="$LOCAL_NODE_BIN:$PATH" ;;
73
+ esac
74
+ fi
75
+
76
+ get_temp_root() {
77
+ local temp_root="${TMPDIR:-/tmp}"
78
+ temp_root="${temp_root%/}"
79
+
80
+ if [[ -z "$temp_root" ]]; then
81
+ temp_root="/tmp"
82
+ fi
83
+
84
+ printf "%s" "$temp_root"
85
+ }
86
+
87
+ make_temp_dir() {
88
+ local prefix="${1:-ralph-run}"
89
+ local temp_root
90
+ temp_root=$(get_temp_root)
91
+
92
+ local temp_dir=""
93
+ temp_dir=$(mktemp -d "${temp_root}/${prefix}-XXXXXX" 2>/dev/null) || \
94
+ temp_dir=$(mktemp -d -t "$prefix" 2>/dev/null) || \
95
+ temp_dir=""
96
+
97
+ if [[ -n "$temp_dir" ]]; then
98
+ printf "%s" "$temp_dir"
99
+ return 0
100
+ fi
101
+
102
+ local fallback_dir="${temp_root}/${prefix}-$(date +"%Y%m%d_%H%M%S")-$$"
103
+ mkdir -p "$fallback_dir"
104
+ printf "%s" "$fallback_dir"
105
+ }
106
+
107
+ resolve_ralph_command() {
108
+ if command -v ralph >/dev/null 2>&1; then
109
+ RALPH_CMD=("ralph")
110
+ return 0
111
+ fi
112
+
113
+ if [[ -f "$BUNDLED_RALPH_JS" ]] && command -v node >/dev/null 2>&1; then
114
+ RALPH_CMD=("node" "$BUNDLED_RALPH_JS")
115
+ return 0
116
+ fi
117
+
118
+ RALPH_CMD=()
119
+ return 1
120
+ }
121
+
64
122
  CHANGE_NAME=""
65
123
  MAX_ITERATIONS=""
66
124
  ERROR_OCCURRED=false
@@ -126,7 +184,7 @@ PREREQUISITES:
126
184
  - Git repository (git init)
127
185
  - OpenSpec artifacts created (openspec init, opsx-new, opsx-ff)
128
186
  - ralph CLI installed (npm install -g @th0rgal/ralph-wiggum)
129
- - opencode CLI installed (npm install -g opencode)
187
+ - opencode CLI installed (npm install -g opencode-ai)
130
188
 
131
189
  EOF
132
190
  }
@@ -193,21 +251,29 @@ validate_git_repository() {
193
251
  validate_dependencies() {
194
252
  log_verbose "Validating dependencies..."
195
253
 
196
- # Check for ralph
197
- if ! command -v ralph &> /dev/null; then
254
+ if ! resolve_ralph_command; then
198
255
  log_error "ralph CLI not found."
199
256
  log_error "Please install open-ralph-wiggum: npm install -g @th0rgal/ralph-wiggum"
257
+ log_error "If spec-and-loop is globally installed, reinstalling it should also restore the bundled Ralph dependency."
200
258
  exit 1
201
259
  fi
202
- log_verbose "Found: ralph"
260
+ log_verbose "Found Ralph command: ${RALPH_CMD[*]}"
203
261
 
204
262
  # Check for opencode
205
263
  if ! command -v opencode &> /dev/null; then
206
264
  log_error "opencode CLI not found."
207
- log_error "Please install opencode: npm install -g opencode"
265
+ log_error "Please install opencode: npm install -g opencode-ai"
208
266
  exit 1
209
267
  fi
210
268
  log_verbose "Found: opencode"
269
+
270
+ # Check for jq
271
+ if ! command -v jq &> /dev/null; then
272
+ log_error "jq CLI not found."
273
+ log_error "Please install jq: brew install jq / apt-get install jq"
274
+ exit 1
275
+ fi
276
+ log_verbose "Found: jq"
211
277
 
212
278
  log_verbose "All dependencies validated"
213
279
  }
@@ -854,11 +920,9 @@ setup_output_capture() {
854
920
 
855
921
  log_verbose "Setting up output capture..."
856
922
 
857
- # Create timestamped output directory in /tmp
858
- local timestamp=$(date +"%Y%m%d_%H%M%S")
859
- local output_dir="/tmp/ralph-run-$timestamp"
860
-
861
- mkdir -p "$output_dir"
923
+ # Use the system temp directory so macOS and Linux both work naturally.
924
+ local output_dir
925
+ output_dir=$(make_temp_dir "ralph-run")
862
926
  log_info "Output directory: $output_dir"
863
927
 
864
928
  # Store output directory path in Ralph directory for reference
@@ -870,8 +934,11 @@ setup_output_capture() {
870
934
  cleanup_old_output() {
871
935
  log_verbose "Cleaning up old Ralph output directories..."
872
936
 
937
+ local temp_root
938
+ temp_root=$(get_temp_root)
939
+
873
940
  # Keep last 3 Ralph output directories, delete older ones
874
- find /tmp -type d -name "ralph-run-*" -mtime +7d 2>/dev/null | while read old_dir; do
941
+ find "$temp_root" -type d -name "ralph-run*" -mtime +7 2>/dev/null | while IFS= read -r old_dir; do
875
942
  log_verbose "Removing old output directory: $old_dir"
876
943
  rm -rf "$old_dir"
877
944
  done
@@ -886,7 +953,7 @@ execute_ralph_loop() {
886
953
  log_info "Max iterations: $max_iterations"
887
954
  log_info "Change directory: $change_dir"
888
955
 
889
- if ! command -v ralph &> /dev/null; then
956
+ if ! resolve_ralph_command; then
890
957
  log_error "ralph CLI not found."
891
958
  log_error "Please install open-ralph-wiggum: npm install -g @th0rgal/ralph-wiggum"
892
959
  return 1
@@ -932,7 +999,7 @@ execute_ralph_loop() {
932
999
 
933
1000
  # Run Ralph and capture output to both console and files
934
1001
  {
935
- ralph --prompt-file "$ralph_dir/PRD.md" \
1002
+ "${RALPH_CMD[@]}" --prompt-file "$ralph_dir/PRD.md" \
936
1003
  --agent opencode \
937
1004
  --tasks \
938
1005
  --max-iterations "$max_iterations" \
package/scripts/setup.js CHANGED
@@ -38,6 +38,6 @@ console.log(' ralph-run # Auto-detect change');
38
38
  console.log('');
39
39
  console.log('Prerequisites:');
40
40
  console.log(' - openspec CLI: npm install -g openspec');
41
- console.log(' - opencode CLI: npm install -g opencode');
41
+ console.log(' - opencode CLI: npm install -g opencode-ai');
42
42
  console.log(' - jq CLI: apt install jq / brew install jq');
43
43
  console.log(' - git: git init');