@rpamis/comet 0.2.1 → 0.2.2

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,258 @@
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: Update archived: true in .comet.yaml ---
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
- sed -i 's/^archived:.*/archived: true/' "$ARCHIVE_YAML"
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
+ 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: Update archived: true in .comet.yaml ---
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
+ sed -i 's/^archived:.*/archived: true/' "$ARCHIVE_YAML"
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