@rpamis/comet 0.2.5 → 0.2.7

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.
@@ -1,258 +1,255 @@
1
- #!/bin/bash
2
- # Comet Archive — automates the archive phase in one command
3
- # Usage: comet-archive.sh <change-name> [--dry-run]
4
- # Exit 0 = archive complete, exit 1 = fatal error
5
-
6
- set -euo pipefail
7
-
8
- red() { echo -e "\033[31m$1\033[0m" >&2; }
9
- green() { echo -e "\033[32m$1\033[0m" >&2; }
10
- yellow() { echo -e "\033[33m$1\033[0m" >&2; }
11
-
12
- DRY_RUN=0
13
- if [[ "${2:-}" == "--dry-run" ]]; then
14
- DRY_RUN=1
15
- fi
16
-
17
- # Input validation
18
- validate_change_name() {
19
- local name="$1"
20
- if [ -z "$name" ]; then
21
- red "FATAL: Change name cannot be empty"
22
- exit 1
23
- fi
24
- if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
25
- red "FATAL: Invalid change name: '$name'"
26
- red "Valid characters: a-z, A-Z, 0-9, -, _"
27
- exit 1
28
- fi
29
- if [[ "$name" =~ \.\. ]]; then
30
- red "FATAL: Change name cannot contain '..'"
31
- exit 1
32
- fi
33
- }
34
-
35
- CHANGE="$1"
36
- validate_change_name "$CHANGE"
37
-
38
- CHANGE_DIR="openspec/changes/$CHANGE"
39
- YAML="$CHANGE_DIR/.comet.yaml"
40
- SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")" 2>/dev/null || dirname "$0")" && pwd)"
41
- STATE_SH="$SCRIPT_DIR/comet-state.sh"
42
- TODAY=$(date +%Y-%m-%d)
43
- ARCHIVE_NAME="${TODAY}-${CHANGE}"
44
- ARCHIVE_DIR="openspec/changes/archive/${ARCHIVE_NAME}"
45
-
46
- STEPS_OK=0
47
- STEPS_TOTAL=0
48
-
49
- step_ok() {
50
- green " [OK] $1"
51
- STEPS_OK=$((STEPS_OK + 1))
52
- STEPS_TOTAL=$((STEPS_TOTAL + 1))
53
- }
54
-
55
- step_fail() {
56
- red " [FAIL] $1"
57
- STEPS_TOTAL=$((STEPS_TOTAL + 1))
58
- }
59
-
60
- echo "=== Comet Archive: $CHANGE ===" >&2
61
-
62
- # --- Step 1: Read .comet.yaml, extract paths ---
63
-
64
- yaml_field() {
65
- local field="$1"
66
- if [ -f "$STATE_SH" ]; then
67
- bash "$STATE_SH" get "$CHANGE" "$field" 2>/dev/null
68
- else
69
- if [ -f "$YAML" ]; then
70
- grep "^${field}:" "$YAML" | sed "s/^${field}: *//" | tr -d '"' | tr -d "'"
71
- fi
72
- fi
73
- }
74
-
75
- if [ ! -f "$YAML" ]; then
76
- red "FATAL: .comet.yaml not found in $CHANGE_DIR/"
77
- exit 1
78
- fi
79
-
80
- DESIGN_DOC=$(yaml_field "design_doc")
81
- PLAN_PATH=$(yaml_field "plan")
82
-
83
- # --- Step 2: Validate entry state ---
84
-
85
- PHASE_VAL=$(yaml_field "phase")
86
- VERIFY_VAL=$(yaml_field "verify_result")
87
- ARCHIVED_VAL=$(yaml_field "archived")
88
-
89
- if [ "$PHASE_VAL" != "archive" ]; then
90
- red "FATAL: phase is '$PHASE_VAL', expected 'archive'"
91
- exit 1
92
- fi
93
-
94
- if [ "$VERIFY_VAL" != "pass" ]; then
95
- red "FATAL: verify_result is '$VERIFY_VAL', expected 'pass'. Run comet-verify first."
96
- exit 1
97
- fi
98
-
99
- if [ "$ARCHIVED_VAL" = "true" ]; then
100
- red "FATAL: change already archived"
101
- exit 1
102
- fi
103
-
104
- step_ok "Entry state verified"
105
-
106
- # --- Step 3: Check archive target ---
107
-
108
- if [ -d "$ARCHIVE_DIR" ]; then
109
- red "FATAL: archive target already exists: $ARCHIVE_DIR"
110
- exit 1
111
- fi
112
-
113
- step_ok "Archive target available"
114
-
115
- # --- Step 4: Sync delta specs main specs ---
116
-
117
- sync_delta_specs() {
118
- local delta_root="$CHANGE_DIR/specs"
119
- if [ ! -d "$delta_root" ]; then
120
- return 0
121
- fi
122
-
123
- for delta_spec_dir in "$delta_root"/*/; do
124
- [ -d "$delta_spec_dir" ] || continue
125
- local capability
126
- capability=$(basename "$delta_spec_dir")
127
- local delta_spec="$delta_spec_dir/spec.md"
128
- local main_spec="openspec/specs/$capability/spec.md"
129
-
130
- if [ ! -f "$delta_spec" ]; then
131
- continue
132
- fi
133
-
134
- STEPS_TOTAL=$((STEPS_TOTAL + 1))
135
-
136
- if [ "$DRY_RUN" -eq 1 ]; then
137
- yellow " [DRY-RUN] Would sync: $capability → $main_spec"
138
- STEPS_OK=$((STEPS_OK + 1))
139
- continue
140
- fi
141
-
142
- if [ ! -f "$main_spec" ]; then
143
- mkdir -p "openspec/specs/$capability"
144
- fi
145
- cp "$delta_spec" "$main_spec"
146
-
147
- step_ok "Delta spec synced: $capability openspec/specs/$capability/spec.md"
148
- done
149
- }
150
-
151
- sync_delta_specs
152
-
153
- # --- Step 5: Annotate design doc frontmatter ---
154
-
155
- annotate_frontmatter() {
156
- local file="$1"
157
- local extra_fields="$2"
158
-
159
- if [ ! -f "$file" ]; then
160
- return 0
161
- fi
162
-
163
- STEPS_TOTAL=$((STEPS_TOTAL + 1))
164
-
165
- if [ "$DRY_RUN" -eq 1 ]; then
166
- yellow " [DRY-RUN] Would annotate: $file"
167
- STEPS_OK=$((STEPS_OK + 1))
168
- return 0
169
- fi
170
-
171
- if head -1 "$file" | grep -q '^---'; then
172
- local tmp_file
173
- tmp_file=$(mktemp)
174
- awk -v archive="$ARCHIVE_NAME" -v extra="$extra_fields" '
175
- /^archived-with:/ { next }
176
- NR==1 && /^---/ { print; next }
177
- /^---/ && NR>1 {
178
- print "archived-with: " archive
179
- if (extra != "") print extra
180
- print; next
181
- }
182
- { print }
183
- ' "$file" > "$tmp_file"
184
- mv "$tmp_file" "$file"
185
- else
186
- local tmp_file
187
- tmp_file=$(mktemp)
188
- {
189
- echo "---"
190
- echo "archived-with: $ARCHIVE_NAME"
191
- if [ -n "$extra_fields" ]; then
192
- echo "$extra_fields"
193
- fi
194
- echo "status: final"
195
- echo "---"
196
- cat "$file"
197
- } > "$tmp_file"
198
- mv "$tmp_file" "$file"
199
- fi
200
-
201
- step_ok "Annotated: $file"
202
- }
203
-
204
- if [ -n "$DESIGN_DOC" ] && [ "$DESIGN_DOC" != "null" ]; then
205
- annotate_frontmatter "$DESIGN_DOC" "status: final"
206
- fi
207
-
208
- # --- Step 6: Annotate plan frontmatter ---
209
-
210
- if [ -n "$PLAN_PATH" ] && [ "$PLAN_PATH" != "null" ]; then
211
- annotate_frontmatter "$PLAN_PATH" ""
212
- fi
213
-
214
- # --- Step 7: Move change to archive ---
215
-
216
- STEPS_TOTAL=$((STEPS_TOTAL + 1))
217
-
218
- if [ "$DRY_RUN" -eq 1 ]; then
219
- yellow " [DRY-RUN] Would move: $CHANGE_DIR $ARCHIVE_DIR"
220
- STEPS_OK=$((STEPS_OK + 1))
221
- else
222
- mkdir -p "openspec/changes/archive"
223
- mv "$CHANGE_DIR" "$ARCHIVE_DIR"
224
- step_ok "Moved to: $ARCHIVE_DIR"
225
- fi
226
-
227
- # --- Step 8: Mark archived via comet-state transition ---
228
-
229
- STEPS_TOTAL=$((STEPS_TOTAL + 1))
230
-
231
- ARCHIVE_YAML="$ARCHIVE_DIR/.comet.yaml"
232
-
233
- if [ "$DRY_RUN" -eq 1 ]; then
234
- yellow " [DRY-RUN] Would set archived: true in $ARCHIVE_YAML"
235
- STEPS_OK=$((STEPS_OK + 1))
236
- else
237
- if [ -f "$ARCHIVE_YAML" ]; then
238
- bash "$STATE_SH" transition "$ARCHIVE_NAME" archived >/dev/null
239
- step_ok "archived: true"
240
- else
241
- step_fail "archived: true (.comet.yaml not found after move)"
242
- fi
243
- fi
244
-
245
- # --- Step 9: Print summary ---
246
-
247
- echo "" >&2
248
- if [ "$DRY_RUN" -eq 1 ]; then
249
- yellow "Dry run complete. $STEPS_OK/$STEPS_TOTAL steps would succeed."
250
- else
251
- green "Archive complete. $STEPS_OK/$STEPS_TOTAL steps succeeded."
252
- fi
253
-
254
- if [ "$STEPS_OK" -lt "$STEPS_TOTAL" ]; then
255
- exit 1
256
- fi
257
-
258
- exit 0
1
+ #!/bin/bash
2
+ # Comet Archive — automates the archive phase in one command
3
+ # Usage: comet-archive.sh <change-name> [--dry-run]
4
+ # Exit 0 = archive complete, exit 1 = fatal error
5
+
6
+ set -euo pipefail
7
+
8
+ red() { echo -e "\033[31m$1\033[0m" >&2; }
9
+ green() { echo -e "\033[32m$1\033[0m" >&2; }
10
+ yellow() { echo -e "\033[33m$1\033[0m" >&2; }
11
+
12
+ DRY_RUN=0
13
+ if [[ "${2:-}" == "--dry-run" ]]; then
14
+ DRY_RUN=1
15
+ fi
16
+
17
+ # Input validation
18
+ validate_change_name() {
19
+ local name="$1"
20
+ if [ -z "$name" ]; then
21
+ red "FATAL: Change name cannot be empty"
22
+ exit 1
23
+ fi
24
+ if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
25
+ red "FATAL: Invalid change name: '$name'"
26
+ red "Valid characters: a-z, A-Z, 0-9, -, _"
27
+ exit 1
28
+ fi
29
+ if [[ "$name" =~ \.\. ]]; then
30
+ red "FATAL: Change name cannot contain '..'"
31
+ exit 1
32
+ fi
33
+ }
34
+
35
+ CHANGE="$1"
36
+ validate_change_name "$CHANGE"
37
+
38
+ CHANGE_DIR="openspec/changes/$CHANGE"
39
+ YAML="$CHANGE_DIR/.comet.yaml"
40
+ SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")" 2>/dev/null || dirname "$0")" && pwd)"
41
+ STATE_SH="$SCRIPT_DIR/comet-state.sh"
42
+ TODAY=$(date +%Y-%m-%d)
43
+ ARCHIVE_NAME="${TODAY}-${CHANGE}"
44
+ ARCHIVE_DIR="openspec/changes/archive/${ARCHIVE_NAME}"
45
+
46
+ STEPS_OK=0
47
+ STEPS_TOTAL=0
48
+
49
+ step_ok() {
50
+ green " [OK] $1"
51
+ STEPS_OK=$((STEPS_OK + 1))
52
+ STEPS_TOTAL=$((STEPS_TOTAL + 1))
53
+ }
54
+
55
+ step_fail() {
56
+ red " [FAIL] $1"
57
+ STEPS_TOTAL=$((STEPS_TOTAL + 1))
58
+ }
59
+
60
+ step_dry_run() {
61
+ yellow " [DRY-RUN] $1"
62
+ STEPS_OK=$((STEPS_OK + 1))
63
+ STEPS_TOTAL=$((STEPS_TOTAL + 1))
64
+ }
65
+
66
+ echo "=== Comet Archive: $CHANGE ===" >&2
67
+
68
+ # --- Step 1: Read .comet.yaml, extract paths ---
69
+
70
+ yaml_field() {
71
+ local field="$1"
72
+ if [ -f "$STATE_SH" ]; then
73
+ bash "$STATE_SH" get "$CHANGE" "$field" 2>/dev/null
74
+ else
75
+ if [ -f "$YAML" ]; then
76
+ grep "^${field}:" "$YAML" | sed "s/^${field}: *//" | tr -d '"' | tr -d "'"
77
+ fi
78
+ fi
79
+ }
80
+
81
+ if [ ! -f "$YAML" ]; then
82
+ red "FATAL: .comet.yaml not found in $CHANGE_DIR/"
83
+ exit 1
84
+ fi
85
+
86
+ DESIGN_DOC=$(yaml_field "design_doc")
87
+ PLAN_PATH=$(yaml_field "plan")
88
+
89
+ # --- Step 2: Validate entry state ---
90
+
91
+ PHASE_VAL=$(yaml_field "phase")
92
+ VERIFY_VAL=$(yaml_field "verify_result")
93
+ ARCHIVED_VAL=$(yaml_field "archived")
94
+
95
+ if [ "$PHASE_VAL" != "archive" ]; then
96
+ red "FATAL: phase is '$PHASE_VAL', expected 'archive'"
97
+ exit 1
98
+ fi
99
+
100
+ if [ "$VERIFY_VAL" != "pass" ]; then
101
+ red "FATAL: verify_result is '$VERIFY_VAL', expected 'pass'. Run comet-verify first."
102
+ exit 1
103
+ fi
104
+
105
+ if [ "$ARCHIVED_VAL" = "true" ]; then
106
+ red "FATAL: change already archived"
107
+ exit 1
108
+ fi
109
+
110
+ step_ok "Entry state verified"
111
+
112
+ # --- Step 3: Check archive target ---
113
+
114
+ if [ -d "$ARCHIVE_DIR" ]; then
115
+ red "FATAL: archive target already exists: $ARCHIVE_DIR"
116
+ exit 1
117
+ fi
118
+
119
+ step_ok "Archive target available"
120
+
121
+ # --- Step 4: Sync delta specs → main specs ---
122
+
123
+ sync_delta_specs() {
124
+ local delta_root="$CHANGE_DIR/specs"
125
+ if [ ! -d "$delta_root" ]; then
126
+ return 0
127
+ fi
128
+
129
+ for delta_spec_dir in "$delta_root"/*/; do
130
+ [ -d "$delta_spec_dir" ] || continue
131
+ local capability
132
+ capability=$(basename "$delta_spec_dir")
133
+ local delta_spec="$delta_spec_dir/spec.md"
134
+ local main_spec="openspec/specs/$capability/spec.md"
135
+
136
+ if [ ! -f "$delta_spec" ]; then
137
+ continue
138
+ fi
139
+
140
+ if [ "$DRY_RUN" -eq 1 ]; then
141
+ step_dry_run "Would sync: $capability → $main_spec"
142
+ continue
143
+ fi
144
+
145
+ if [ ! -f "$main_spec" ]; then
146
+ mkdir -p "openspec/specs/$capability"
147
+ elif ! cmp -s "$main_spec" "$delta_spec"; then
148
+ yellow " [DIFF] Delta spec differs from main spec before sync: $capability"
149
+ diff -u "$main_spec" "$delta_spec" >&2 || true
150
+ fi
151
+ cp "$delta_spec" "$main_spec"
152
+
153
+ step_ok "Delta spec synced: $capability openspec/specs/$capability/spec.md"
154
+ done
155
+ }
156
+
157
+ sync_delta_specs
158
+
159
+ # --- Step 5: Annotate design doc frontmatter ---
160
+
161
+ annotate_frontmatter() {
162
+ local file="$1"
163
+ local extra_fields="$2"
164
+
165
+ if [ ! -f "$file" ]; then
166
+ return 0
167
+ fi
168
+
169
+ if [ "$DRY_RUN" -eq 1 ]; then
170
+ step_dry_run "Would annotate: $file"
171
+ return 0
172
+ fi
173
+
174
+ if head -1 "$file" | grep -q '^---'; then
175
+ local tmp_file
176
+ tmp_file=$(mktemp)
177
+ awk -v archive="$ARCHIVE_NAME" -v extra="$extra_fields" '
178
+ /^archived-with:/ { next }
179
+ NR==1 && /^---/ { print; next }
180
+ /^---/ && NR>1 {
181
+ print "archived-with: " archive
182
+ if (extra != "") print extra
183
+ print; next
184
+ }
185
+ { print }
186
+ ' "$file" > "$tmp_file"
187
+ mv "$tmp_file" "$file"
188
+ else
189
+ local tmp_file
190
+ tmp_file=$(mktemp)
191
+ {
192
+ echo "---"
193
+ echo "archived-with: $ARCHIVE_NAME"
194
+ if [ -n "$extra_fields" ]; then
195
+ echo "$extra_fields"
196
+ fi
197
+ echo "status: final"
198
+ echo "---"
199
+ cat "$file"
200
+ } > "$tmp_file"
201
+ mv "$tmp_file" "$file"
202
+ fi
203
+
204
+ step_ok "Annotated: $file"
205
+ }
206
+
207
+ if [ -n "$DESIGN_DOC" ] && [ "$DESIGN_DOC" != "null" ]; then
208
+ annotate_frontmatter "$DESIGN_DOC" "status: final"
209
+ fi
210
+
211
+ # --- Step 6: Annotate plan frontmatter ---
212
+
213
+ if [ -n "$PLAN_PATH" ] && [ "$PLAN_PATH" != "null" ]; then
214
+ annotate_frontmatter "$PLAN_PATH" ""
215
+ fi
216
+
217
+ # --- Step 7: Move change to archive ---
218
+
219
+ if [ "$DRY_RUN" -eq 1 ]; then
220
+ step_dry_run "Would move: $CHANGE_DIR → $ARCHIVE_DIR"
221
+ else
222
+ mkdir -p "openspec/changes/archive"
223
+ mv "$CHANGE_DIR" "$ARCHIVE_DIR"
224
+ step_ok "Moved to: $ARCHIVE_DIR"
225
+ fi
226
+
227
+ # --- Step 8: Mark archived via comet-state transition ---
228
+
229
+ ARCHIVE_YAML="$ARCHIVE_DIR/.comet.yaml"
230
+
231
+ if [ "$DRY_RUN" -eq 1 ]; then
232
+ step_dry_run "Would set archived: true in $ARCHIVE_YAML"
233
+ else
234
+ if [ -f "$ARCHIVE_YAML" ]; then
235
+ bash "$STATE_SH" transition "$ARCHIVE_NAME" archived >/dev/null
236
+ step_ok "archived: true"
237
+ else
238
+ step_fail "archived: true (.comet.yaml not found after move)"
239
+ fi
240
+ fi
241
+
242
+ # --- Step 9: Print summary ---
243
+
244
+ echo "" >&2
245
+ if [ "$DRY_RUN" -eq 1 ]; then
246
+ yellow "Dry run complete. $STEPS_OK/$STEPS_TOTAL steps would succeed."
247
+ else
248
+ green "Archive complete. $STEPS_OK/$STEPS_TOTAL steps succeeded."
249
+ fi
250
+
251
+ if [ "$STEPS_OK" -lt "$STEPS_TOTAL" ]; then
252
+ exit 1
253
+ fi
254
+
255
+ exit 0