@rpamis/comet 0.2.1 → 0.2.3
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/LICENSE +21 -21
- package/README.md +313 -270
- package/assets/manifest.json +21 -21
- package/assets/skills/comet/scripts/comet-archive.sh +258 -258
- package/assets/skills/comet/scripts/comet-guard.sh +255 -255
- package/assets/skills/comet/scripts/comet-state.sh +497 -497
- package/assets/skills/comet/scripts/comet-yaml-validate.sh +132 -132
- package/assets/skills/comet-archive/SKILL.md +63 -63
- package/assets/skills/comet-build/SKILL.md +153 -153
- package/assets/skills/comet-design/SKILL.md +91 -91
- package/assets/skills/comet-open/SKILL.md +70 -70
- package/assets/skills/comet-verify/SKILL.md +114 -114
- package/assets/skills-zh/comet-archive/SKILL.md +63 -63
- package/assets/skills-zh/comet-build/SKILL.md +153 -153
- package/assets/skills-zh/comet-design/SKILL.md +91 -91
- package/assets/skills-zh/comet-open/SKILL.md +70 -70
- package/assets/skills-zh/comet-verify/SKILL.md +114 -114
- package/bin/comet.js +3 -3
- package/dist/cli/index.js +10 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/detect.d.ts +6 -1
- package/dist/core/detect.d.ts.map +1 -1
- package/dist/core/detect.js +29 -1
- package/dist/core/detect.js.map +1 -1
- package/package.json +62 -62
- package/scripts/postinstall.js +44 -44
|
@@ -1,497 +1,497 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Comet State — unified interface for .comet.yaml state management
|
|
3
|
-
# Usage: comet-state.sh <subcommand> <change-name> [args...]
|
|
4
|
-
#
|
|
5
|
-
# Subcommands:
|
|
6
|
-
# init <change-name> <workflow> — Initialize .comet.yaml with workflow defaults
|
|
7
|
-
# get <change-name> <field> — Read a field value from .comet.yaml
|
|
8
|
-
# set <change-name> <field> <val> — Update a field value
|
|
9
|
-
# check <phase> <change-name> — Verify entry requirements for a phase
|
|
10
|
-
# scale <change-name> — Assess and set verification mode based on metrics
|
|
11
|
-
#
|
|
12
|
-
# Workflows: full, hotfix, tweak
|
|
13
|
-
# Phases for check: open, design, build, verify, archive
|
|
14
|
-
|
|
15
|
-
set -euo pipefail
|
|
16
|
-
|
|
17
|
-
# --- Color output helpers ---
|
|
18
|
-
|
|
19
|
-
red() { echo -e "\033[31m$1\033[0m" >&2; }
|
|
20
|
-
green() { echo -e "\033[32m$1\033[0m" >&2; }
|
|
21
|
-
yellow() { echo -e "\033[33m$1\033[0m" >&2; }
|
|
22
|
-
|
|
23
|
-
# --- Script location ---
|
|
24
|
-
|
|
25
|
-
# shellcheck disable=SC2034
|
|
26
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
27
|
-
|
|
28
|
-
# --- Input validation ---
|
|
29
|
-
|
|
30
|
-
validate_change_name() {
|
|
31
|
-
local name="$1"
|
|
32
|
-
# Reject empty names
|
|
33
|
-
if [ -z "$name" ]; then
|
|
34
|
-
red "ERROR: Change name cannot be empty" >&2
|
|
35
|
-
exit 1
|
|
36
|
-
fi
|
|
37
|
-
# Only allow alphanumeric, hyphens, and underscores
|
|
38
|
-
if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
39
|
-
red "ERROR: Invalid change name: '$name'" >&2
|
|
40
|
-
red "Valid characters: a-z, A-Z, 0-9, -, _" >&2
|
|
41
|
-
exit 1
|
|
42
|
-
fi
|
|
43
|
-
# Reject path traversal attempts
|
|
44
|
-
if [[ "$name" =~ \.\. ]]; then
|
|
45
|
-
red "ERROR: Change name cannot contain '..' (path traversal not allowed)" >&2
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
validate_enum() {
|
|
51
|
-
local value="$1"
|
|
52
|
-
shift
|
|
53
|
-
local valid_values=("$@")
|
|
54
|
-
|
|
55
|
-
for valid in "${valid_values[@]}"; do
|
|
56
|
-
if [ "$value" = "$valid" ]; then
|
|
57
|
-
return 0
|
|
58
|
-
fi
|
|
59
|
-
done
|
|
60
|
-
|
|
61
|
-
red "ERROR: Invalid value: '$value'" >&2
|
|
62
|
-
red "Valid values: ${valid_values[*]}" >&2
|
|
63
|
-
exit 1
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
# --- Helper functions ---
|
|
67
|
-
|
|
68
|
-
yaml_field() {
|
|
69
|
-
local field="$1"
|
|
70
|
-
local yaml_file="$2"
|
|
71
|
-
if [ -f "$yaml_file" ]; then
|
|
72
|
-
grep "^${field}:" "$yaml_file" | sed "s/^${field}: *//" | tr -d '"' | tr -d "'"
|
|
73
|
-
fi
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
file_nonempty() {
|
|
77
|
-
[ -f "$1" ] && [ -s "$1" ]
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
# --- Subcommands ---
|
|
81
|
-
|
|
82
|
-
cmd_init() {
|
|
83
|
-
local change_name="$1"
|
|
84
|
-
local workflow="$2"
|
|
85
|
-
|
|
86
|
-
validate_change_name "$change_name"
|
|
87
|
-
validate_enum "$workflow" "full" "hotfix" "tweak"
|
|
88
|
-
|
|
89
|
-
local change_dir="openspec/changes/$change_name"
|
|
90
|
-
local yaml_file="$change_dir/.comet.yaml"
|
|
91
|
-
|
|
92
|
-
# Check if .comet.yaml already exists
|
|
93
|
-
if [ -f "$yaml_file" ]; then
|
|
94
|
-
red "ERROR: .comet.yaml already exists at $yaml_file"
|
|
95
|
-
exit 1
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
# Create change directory if it doesn't exist
|
|
99
|
-
mkdir -p "$change_dir"
|
|
100
|
-
|
|
101
|
-
# Set workflow-appropriate defaults
|
|
102
|
-
local phase build_mode isolation verify_mode
|
|
103
|
-
|
|
104
|
-
case "$workflow" in
|
|
105
|
-
full)
|
|
106
|
-
phase="design"
|
|
107
|
-
build_mode="null"
|
|
108
|
-
isolation="null"
|
|
109
|
-
verify_mode="null"
|
|
110
|
-
;;
|
|
111
|
-
hotfix|tweak)
|
|
112
|
-
phase="build"
|
|
113
|
-
build_mode="direct"
|
|
114
|
-
isolation="branch"
|
|
115
|
-
verify_mode="light"
|
|
116
|
-
;;
|
|
117
|
-
esac
|
|
118
|
-
|
|
119
|
-
# Write .comet.yaml
|
|
120
|
-
cat > "$yaml_file" <<EOF
|
|
121
|
-
workflow: $workflow
|
|
122
|
-
phase: $phase
|
|
123
|
-
build_mode: $build_mode
|
|
124
|
-
isolation: $isolation
|
|
125
|
-
verify_mode: $verify_mode
|
|
126
|
-
design_doc: null
|
|
127
|
-
plan: null
|
|
128
|
-
verify_result: pending
|
|
129
|
-
verified_at: null
|
|
130
|
-
archived: false
|
|
131
|
-
EOF
|
|
132
|
-
|
|
133
|
-
green "Initialized: $yaml_file (workflow=$workflow)"
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
cmd_get() {
|
|
137
|
-
local change_name="$1"
|
|
138
|
-
local field="$2"
|
|
139
|
-
|
|
140
|
-
validate_change_name "$change_name"
|
|
141
|
-
|
|
142
|
-
local change_dir="openspec/changes/$change_name"
|
|
143
|
-
local yaml_file="$change_dir/.comet.yaml"
|
|
144
|
-
|
|
145
|
-
# Check if .comet.yaml exists
|
|
146
|
-
if [ ! -f "$yaml_file" ]; then
|
|
147
|
-
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
148
|
-
exit 1
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
# Read and output the field value
|
|
152
|
-
local value
|
|
153
|
-
value=$(yaml_field "$field" "$yaml_file")
|
|
154
|
-
echo "${value:-}"
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
cmd_set() {
|
|
158
|
-
local change_name="$1"
|
|
159
|
-
local field="$2"
|
|
160
|
-
local value="$3"
|
|
161
|
-
|
|
162
|
-
validate_change_name "$change_name"
|
|
163
|
-
|
|
164
|
-
local change_dir="openspec/changes/$change_name"
|
|
165
|
-
local yaml_file="$change_dir/.comet.yaml"
|
|
166
|
-
|
|
167
|
-
# Check if .comet.yaml exists
|
|
168
|
-
if [ ! -f "$yaml_file" ]; then
|
|
169
|
-
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
170
|
-
exit 1
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
# Validate field name
|
|
174
|
-
case "$field" in
|
|
175
|
-
workflow|phase|build_mode|isolation|verify_mode|verify_result|archived|design_doc|plan|verified_at)
|
|
176
|
-
# Valid field
|
|
177
|
-
;;
|
|
178
|
-
*)
|
|
179
|
-
red "ERROR: Unknown field: '$field'" >&2
|
|
180
|
-
red "Valid fields: workflow, phase, design_doc, plan, build_mode, isolation, verify_mode, verify_result, verified_at, archived" >&2
|
|
181
|
-
exit 1
|
|
182
|
-
;;
|
|
183
|
-
esac
|
|
184
|
-
|
|
185
|
-
# Validate enum values
|
|
186
|
-
case "$field" in
|
|
187
|
-
workflow)
|
|
188
|
-
validate_enum "$value" "full" "hotfix" "tweak"
|
|
189
|
-
;;
|
|
190
|
-
phase)
|
|
191
|
-
validate_enum "$value" "design" "build" "verify" "archive"
|
|
192
|
-
;;
|
|
193
|
-
build_mode)
|
|
194
|
-
validate_enum "$value" "subagent-driven-development" "executing-plans" "direct"
|
|
195
|
-
;;
|
|
196
|
-
isolation)
|
|
197
|
-
validate_enum "$value" "branch" "worktree"
|
|
198
|
-
;;
|
|
199
|
-
verify_mode)
|
|
200
|
-
validate_enum "$value" "light" "full"
|
|
201
|
-
;;
|
|
202
|
-
verify_result)
|
|
203
|
-
validate_enum "$value" "pending" "pass" "fail"
|
|
204
|
-
;;
|
|
205
|
-
archived)
|
|
206
|
-
validate_enum "$value" "true" "false"
|
|
207
|
-
;;
|
|
208
|
-
design_doc|plan|verified_at)
|
|
209
|
-
# No validation for path fields and date fields
|
|
210
|
-
;;
|
|
211
|
-
esac
|
|
212
|
-
|
|
213
|
-
# Write or update the field
|
|
214
|
-
if grep -q "^${field}:" "$yaml_file"; then
|
|
215
|
-
# Field exists, replace it (use | delimiter to avoid / conflicts in paths)
|
|
216
|
-
sed -i "s|^${field}:.*|${field}: ${value}|" "$yaml_file"
|
|
217
|
-
else
|
|
218
|
-
# Field doesn't exist, append it
|
|
219
|
-
echo "${field}: ${value}" >> "$yaml_file"
|
|
220
|
-
fi
|
|
221
|
-
|
|
222
|
-
green "[SET] ${field}=${value}"
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
# --- Check helpers for entry verification ---
|
|
226
|
-
|
|
227
|
-
CHECK_BLOCK=0
|
|
228
|
-
|
|
229
|
-
check_pass() {
|
|
230
|
-
local msg="$1"
|
|
231
|
-
echo " $(green "[PASS]") $msg"
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
check_fail() {
|
|
235
|
-
local msg="$1"
|
|
236
|
-
echo " $(red "[FAIL]") $msg"
|
|
237
|
-
CHECK_BLOCK=1
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
check_nonempty() {
|
|
241
|
-
local desc="$1"
|
|
242
|
-
local path="$2"
|
|
243
|
-
if file_nonempty "$path"; then
|
|
244
|
-
check_pass "$desc non-empty"
|
|
245
|
-
else
|
|
246
|
-
check_fail "$desc missing or empty"
|
|
247
|
-
fi
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
check_yaml_is() {
|
|
251
|
-
local field="$1"
|
|
252
|
-
local expected="$2"
|
|
253
|
-
local change_name="$3"
|
|
254
|
-
local actual
|
|
255
|
-
actual=$(cmd_get "$change_name" "$field")
|
|
256
|
-
if [ "$actual" = "$expected" ]; then
|
|
257
|
-
check_pass "${field}=${actual} (expected: ${expected})"
|
|
258
|
-
else
|
|
259
|
-
check_fail "${field}=${actual} (expected: ${expected})"
|
|
260
|
-
fi
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
check_yaml_empty() {
|
|
264
|
-
local field="$1"
|
|
265
|
-
local change_name="$2"
|
|
266
|
-
local value
|
|
267
|
-
value=$(cmd_get "$change_name" "$field")
|
|
268
|
-
if [ -z "$value" ] || [ "$value" = "null" ]; then
|
|
269
|
-
check_pass "${field} is empty/null"
|
|
270
|
-
else
|
|
271
|
-
check_fail "${field}=${value} (expected: empty/null)"
|
|
272
|
-
fi
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
check_file_not_exists() {
|
|
276
|
-
local desc="$1"
|
|
277
|
-
local path="$2"
|
|
278
|
-
if [ ! -f "$path" ]; then
|
|
279
|
-
check_pass "$desc does not exist"
|
|
280
|
-
else
|
|
281
|
-
check_fail "$desc exists (should not exist)"
|
|
282
|
-
fi
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
cmd_check() {
|
|
286
|
-
local phase="$1"
|
|
287
|
-
local change_name="$2"
|
|
288
|
-
|
|
289
|
-
validate_change_name "$change_name"
|
|
290
|
-
validate_enum "$phase" "open" "design" "build" "verify" "archive"
|
|
291
|
-
|
|
292
|
-
local change_dir="openspec/changes/$change_name"
|
|
293
|
-
local yaml_file="$change_dir/.comet.yaml"
|
|
294
|
-
local proposal_file="$change_dir/proposal.md"
|
|
295
|
-
local design_file="$change_dir/design.md"
|
|
296
|
-
local tasks_file="$change_dir/tasks.md"
|
|
297
|
-
|
|
298
|
-
echo "=== Entry Check: comet-${phase} ==="
|
|
299
|
-
|
|
300
|
-
# For non-open phases, .comet.yaml must exist
|
|
301
|
-
if [ "$phase" != "open" ]; then
|
|
302
|
-
if [ ! -f "$yaml_file" ]; then
|
|
303
|
-
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
304
|
-
exit 1
|
|
305
|
-
fi
|
|
306
|
-
fi
|
|
307
|
-
|
|
308
|
-
# Phase-specific checks
|
|
309
|
-
case "$phase" in
|
|
310
|
-
open)
|
|
311
|
-
check_file_not_exists ".comet.yaml" "$yaml_file"
|
|
312
|
-
check_nonempty "proposal.md" "$proposal_file"
|
|
313
|
-
check_nonempty "design.md" "$design_file"
|
|
314
|
-
check_nonempty "tasks.md" "$tasks_file"
|
|
315
|
-
;;
|
|
316
|
-
design)
|
|
317
|
-
check_pass ".comet.yaml exists"
|
|
318
|
-
check_yaml_is "phase" "design" "$change_name"
|
|
319
|
-
check_yaml_is "workflow" "full" "$change_name"
|
|
320
|
-
check_yaml_empty "design_doc" "$change_name"
|
|
321
|
-
check_nonempty "proposal.md" "$proposal_file"
|
|
322
|
-
check_nonempty "design.md" "$design_file"
|
|
323
|
-
check_nonempty "tasks.md" "$tasks_file"
|
|
324
|
-
;;
|
|
325
|
-
build)
|
|
326
|
-
check_pass ".comet.yaml exists"
|
|
327
|
-
check_yaml_is "phase" "build" "$change_name"
|
|
328
|
-
# Check design_doc is non-null and file exists
|
|
329
|
-
local design_doc
|
|
330
|
-
design_doc=$(cmd_get "$change_name" "design_doc")
|
|
331
|
-
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ] && [ -f "$change_dir/$design_doc" ]; then
|
|
332
|
-
check_pass "design_doc=${design_doc} (file exists)"
|
|
333
|
-
else
|
|
334
|
-
check_fail "design_doc=${design_doc} (expected: non-null and file exists)"
|
|
335
|
-
fi
|
|
336
|
-
check_nonempty "proposal.md" "$proposal_file"
|
|
337
|
-
check_nonempty "tasks.md" "$tasks_file"
|
|
338
|
-
;;
|
|
339
|
-
verify)
|
|
340
|
-
check_pass ".comet.yaml exists"
|
|
341
|
-
check_yaml_is "phase" "verify" "$change_name"
|
|
342
|
-
# Check verify_result is pending or null
|
|
343
|
-
local verify_result
|
|
344
|
-
verify_result=$(cmd_get "$change_name" "verify_result")
|
|
345
|
-
if [ "$verify_result" = "pending" ] || [ -z "$verify_result" ] || [ "$verify_result" = "null" ]; then
|
|
346
|
-
check_pass "verify_result=${verify_result} (expected: pending or null)"
|
|
347
|
-
else
|
|
348
|
-
check_fail "verify_result=${verify_result} (expected: pending or null)"
|
|
349
|
-
fi
|
|
350
|
-
;;
|
|
351
|
-
archive)
|
|
352
|
-
check_pass ".comet.yaml exists"
|
|
353
|
-
check_yaml_is "phase" "archive" "$change_name"
|
|
354
|
-
check_yaml_is "verify_result" "pass" "$change_name"
|
|
355
|
-
# Check archived is NOT true
|
|
356
|
-
local archived
|
|
357
|
-
archived=$(cmd_get "$change_name" "archived")
|
|
358
|
-
if [ "$archived" != "true" ]; then
|
|
359
|
-
check_pass "archived=${archived} (expected: not true)"
|
|
360
|
-
else
|
|
361
|
-
check_fail "archived=${archived} (expected: not true)"
|
|
362
|
-
fi
|
|
363
|
-
;;
|
|
364
|
-
*)
|
|
365
|
-
red "ERROR: Unknown phase for check: $phase"
|
|
366
|
-
exit 1
|
|
367
|
-
;;
|
|
368
|
-
esac
|
|
369
|
-
|
|
370
|
-
echo ""
|
|
371
|
-
if [ "$CHECK_BLOCK" -eq 1 ]; then
|
|
372
|
-
red "BLOCKED — fix failing checks before proceeding"
|
|
373
|
-
exit 1
|
|
374
|
-
else
|
|
375
|
-
green "ALL CHECKS PASSED — ready to proceed"
|
|
376
|
-
exit 0
|
|
377
|
-
fi
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
cmd_scale() {
|
|
381
|
-
local change_name="$1"
|
|
382
|
-
|
|
383
|
-
validate_change_name "$change_name"
|
|
384
|
-
|
|
385
|
-
local change_dir="openspec/changes/$change_name"
|
|
386
|
-
local yaml_file="$change_dir/.comet.yaml"
|
|
387
|
-
|
|
388
|
-
# Verify .comet.yaml exists
|
|
389
|
-
if [ ! -f "$yaml_file" ]; then
|
|
390
|
-
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
391
|
-
exit 1
|
|
392
|
-
fi
|
|
393
|
-
|
|
394
|
-
# Read metrics
|
|
395
|
-
# 1. Task count: count lines matching `- [` in tasks.md
|
|
396
|
-
local tasks_file="$change_dir/tasks.md"
|
|
397
|
-
local task_count=0
|
|
398
|
-
if [ -f "$tasks_file" ]; then
|
|
399
|
-
task_count=$(grep -c '^\- \[' "$tasks_file" 2>/dev/null || echo "0")
|
|
400
|
-
fi
|
|
401
|
-
|
|
402
|
-
# 2. Delta spec count: count files named spec.md under specs/*/spec.md
|
|
403
|
-
local delta_spec_count=0
|
|
404
|
-
if [ -d "$change_dir/specs" ]; then
|
|
405
|
-
delta_spec_count=$(find "$change_dir/specs" -name "spec.md" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
406
|
-
fi
|
|
407
|
-
|
|
408
|
-
# 3. Changed files: from git diff --stat HEAD
|
|
409
|
-
local changed_files=0
|
|
410
|
-
if git rev-parse --git-dir > /dev/null 2>&1; then
|
|
411
|
-
# Extract the number before "file" in the last line
|
|
412
|
-
local stat_output
|
|
413
|
-
stat_output=$(git diff --stat HEAD 2>/dev/null | tail -1)
|
|
414
|
-
if [[ "$stat_output" =~ ([0-9]+)\ file ]]; then
|
|
415
|
-
changed_files="${BASH_REMATCH[1]}"
|
|
416
|
-
fi
|
|
417
|
-
fi
|
|
418
|
-
|
|
419
|
-
# Decision rules
|
|
420
|
-
local result="light"
|
|
421
|
-
if [ "$task_count" -gt 3 ] || [ "$delta_spec_count" -gt 1 ] || [ "$changed_files" -gt 5 ]; then
|
|
422
|
-
result="full"
|
|
423
|
-
fi
|
|
424
|
-
|
|
425
|
-
# Output assessment to stderr
|
|
426
|
-
echo "=== Scale Assessment: $change_name ===" >&2
|
|
427
|
-
echo " Tasks: $task_count (threshold: 3)" >&2
|
|
428
|
-
echo " Delta specs: $delta_spec_count capabilities (threshold: 1)" >&2
|
|
429
|
-
echo " Changed files: $changed_files (threshold: 5)" >&2
|
|
430
|
-
echo " → Result: $result" >&2
|
|
431
|
-
|
|
432
|
-
# Update verify_mode in .comet.yaml
|
|
433
|
-
sed -i "s/^verify_mode:.*/verify_mode: $result/" "$yaml_file"
|
|
434
|
-
|
|
435
|
-
green "[SCALE] verify_mode=$result"
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
# --- Main ---
|
|
439
|
-
|
|
440
|
-
SUBCOMMAND="${1:-}"
|
|
441
|
-
shift || true
|
|
442
|
-
|
|
443
|
-
case "$SUBCOMMAND" in
|
|
444
|
-
init)
|
|
445
|
-
if [ $# -lt 2 ]; then
|
|
446
|
-
red "Usage: comet-state.sh init <change-name> <workflow>" >&2
|
|
447
|
-
red "Workflows: full, hotfix, tweak" >&2
|
|
448
|
-
exit 1
|
|
449
|
-
fi
|
|
450
|
-
cmd_init "$@"
|
|
451
|
-
;;
|
|
452
|
-
get)
|
|
453
|
-
if [ $# -lt 2 ]; then
|
|
454
|
-
red "Usage: comet-state.sh get <change-name> <field>" >&2
|
|
455
|
-
exit 1
|
|
456
|
-
fi
|
|
457
|
-
cmd_get "$@"
|
|
458
|
-
;;
|
|
459
|
-
set)
|
|
460
|
-
if [ $# -lt 3 ]; then
|
|
461
|
-
red "Usage: comet-state.sh set <change-name> <field> <value>" >&2
|
|
462
|
-
exit 1
|
|
463
|
-
fi
|
|
464
|
-
cmd_set "$@"
|
|
465
|
-
;;
|
|
466
|
-
check)
|
|
467
|
-
if [ $# -lt 2 ]; then
|
|
468
|
-
red "Usage: comet-state.sh check <phase> <change-name>" >&2
|
|
469
|
-
red "Phases: open, design, build, verify, archive" >&2
|
|
470
|
-
exit 1
|
|
471
|
-
fi
|
|
472
|
-
cmd_check "$@"
|
|
473
|
-
;;
|
|
474
|
-
scale)
|
|
475
|
-
if [ $# -lt 1 ]; then
|
|
476
|
-
red "Usage: comet-state.sh scale <change-name>" >&2
|
|
477
|
-
exit 1
|
|
478
|
-
fi
|
|
479
|
-
cmd_scale "$@"
|
|
480
|
-
;;
|
|
481
|
-
*)
|
|
482
|
-
red "Unknown subcommand: $SUBCOMMAND" >&2
|
|
483
|
-
echo "" >&2
|
|
484
|
-
echo "Usage: comet-state.sh <subcommand> <change-name> [args...]" >&2
|
|
485
|
-
echo "" >&2
|
|
486
|
-
echo "Subcommands:" >&2
|
|
487
|
-
echo " init <change-name> <workflow> — Initialize .comet.yaml with workflow defaults" >&2
|
|
488
|
-
echo " get <change-name> <field> — Read a field value from .comet.yaml" >&2
|
|
489
|
-
echo " set <change-name> <field> <val> — Update a field value in .comet.yaml" >&2
|
|
490
|
-
echo " check <phase> <change-name> — Verify entry requirements for a phase" >&2
|
|
491
|
-
echo " scale <change-name> — Assess and set verification mode based on metrics" >&2
|
|
492
|
-
echo "" >&2
|
|
493
|
-
echo "Workflows: full, hotfix, tweak" >&2
|
|
494
|
-
echo "Phases for check: open, design, build, verify, archive" >&2
|
|
495
|
-
exit 1
|
|
496
|
-
;;
|
|
497
|
-
esac
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Comet State — unified interface for .comet.yaml state management
|
|
3
|
+
# Usage: comet-state.sh <subcommand> <change-name> [args...]
|
|
4
|
+
#
|
|
5
|
+
# Subcommands:
|
|
6
|
+
# init <change-name> <workflow> — Initialize .comet.yaml with workflow defaults
|
|
7
|
+
# get <change-name> <field> — Read a field value from .comet.yaml
|
|
8
|
+
# set <change-name> <field> <val> — Update a field value
|
|
9
|
+
# check <phase> <change-name> — Verify entry requirements for a phase
|
|
10
|
+
# scale <change-name> — Assess and set verification mode based on metrics
|
|
11
|
+
#
|
|
12
|
+
# Workflows: full, hotfix, tweak
|
|
13
|
+
# Phases for check: open, design, build, verify, archive
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
# --- Color output helpers ---
|
|
18
|
+
|
|
19
|
+
red() { echo -e "\033[31m$1\033[0m" >&2; }
|
|
20
|
+
green() { echo -e "\033[32m$1\033[0m" >&2; }
|
|
21
|
+
yellow() { echo -e "\033[33m$1\033[0m" >&2; }
|
|
22
|
+
|
|
23
|
+
# --- Script location ---
|
|
24
|
+
|
|
25
|
+
# shellcheck disable=SC2034
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
27
|
+
|
|
28
|
+
# --- Input validation ---
|
|
29
|
+
|
|
30
|
+
validate_change_name() {
|
|
31
|
+
local name="$1"
|
|
32
|
+
# Reject empty names
|
|
33
|
+
if [ -z "$name" ]; then
|
|
34
|
+
red "ERROR: Change name cannot be empty" >&2
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
# Only allow alphanumeric, hyphens, and underscores
|
|
38
|
+
if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
|
|
39
|
+
red "ERROR: Invalid change name: '$name'" >&2
|
|
40
|
+
red "Valid characters: a-z, A-Z, 0-9, -, _" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
# Reject path traversal attempts
|
|
44
|
+
if [[ "$name" =~ \.\. ]]; then
|
|
45
|
+
red "ERROR: Change name cannot contain '..' (path traversal not allowed)" >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
validate_enum() {
|
|
51
|
+
local value="$1"
|
|
52
|
+
shift
|
|
53
|
+
local valid_values=("$@")
|
|
54
|
+
|
|
55
|
+
for valid in "${valid_values[@]}"; do
|
|
56
|
+
if [ "$value" = "$valid" ]; then
|
|
57
|
+
return 0
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
red "ERROR: Invalid value: '$value'" >&2
|
|
62
|
+
red "Valid values: ${valid_values[*]}" >&2
|
|
63
|
+
exit 1
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# --- Helper functions ---
|
|
67
|
+
|
|
68
|
+
yaml_field() {
|
|
69
|
+
local field="$1"
|
|
70
|
+
local yaml_file="$2"
|
|
71
|
+
if [ -f "$yaml_file" ]; then
|
|
72
|
+
grep "^${field}:" "$yaml_file" | sed "s/^${field}: *//" | tr -d '"' | tr -d "'"
|
|
73
|
+
fi
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
file_nonempty() {
|
|
77
|
+
[ -f "$1" ] && [ -s "$1" ]
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# --- Subcommands ---
|
|
81
|
+
|
|
82
|
+
cmd_init() {
|
|
83
|
+
local change_name="$1"
|
|
84
|
+
local workflow="$2"
|
|
85
|
+
|
|
86
|
+
validate_change_name "$change_name"
|
|
87
|
+
validate_enum "$workflow" "full" "hotfix" "tweak"
|
|
88
|
+
|
|
89
|
+
local change_dir="openspec/changes/$change_name"
|
|
90
|
+
local yaml_file="$change_dir/.comet.yaml"
|
|
91
|
+
|
|
92
|
+
# Check if .comet.yaml already exists
|
|
93
|
+
if [ -f "$yaml_file" ]; then
|
|
94
|
+
red "ERROR: .comet.yaml already exists at $yaml_file"
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Create change directory if it doesn't exist
|
|
99
|
+
mkdir -p "$change_dir"
|
|
100
|
+
|
|
101
|
+
# Set workflow-appropriate defaults
|
|
102
|
+
local phase build_mode isolation verify_mode
|
|
103
|
+
|
|
104
|
+
case "$workflow" in
|
|
105
|
+
full)
|
|
106
|
+
phase="design"
|
|
107
|
+
build_mode="null"
|
|
108
|
+
isolation="null"
|
|
109
|
+
verify_mode="null"
|
|
110
|
+
;;
|
|
111
|
+
hotfix|tweak)
|
|
112
|
+
phase="build"
|
|
113
|
+
build_mode="direct"
|
|
114
|
+
isolation="branch"
|
|
115
|
+
verify_mode="light"
|
|
116
|
+
;;
|
|
117
|
+
esac
|
|
118
|
+
|
|
119
|
+
# Write .comet.yaml
|
|
120
|
+
cat > "$yaml_file" <<EOF
|
|
121
|
+
workflow: $workflow
|
|
122
|
+
phase: $phase
|
|
123
|
+
build_mode: $build_mode
|
|
124
|
+
isolation: $isolation
|
|
125
|
+
verify_mode: $verify_mode
|
|
126
|
+
design_doc: null
|
|
127
|
+
plan: null
|
|
128
|
+
verify_result: pending
|
|
129
|
+
verified_at: null
|
|
130
|
+
archived: false
|
|
131
|
+
EOF
|
|
132
|
+
|
|
133
|
+
green "Initialized: $yaml_file (workflow=$workflow)"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
cmd_get() {
|
|
137
|
+
local change_name="$1"
|
|
138
|
+
local field="$2"
|
|
139
|
+
|
|
140
|
+
validate_change_name "$change_name"
|
|
141
|
+
|
|
142
|
+
local change_dir="openspec/changes/$change_name"
|
|
143
|
+
local yaml_file="$change_dir/.comet.yaml"
|
|
144
|
+
|
|
145
|
+
# Check if .comet.yaml exists
|
|
146
|
+
if [ ! -f "$yaml_file" ]; then
|
|
147
|
+
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
148
|
+
exit 1
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# Read and output the field value
|
|
152
|
+
local value
|
|
153
|
+
value=$(yaml_field "$field" "$yaml_file")
|
|
154
|
+
echo "${value:-}"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
cmd_set() {
|
|
158
|
+
local change_name="$1"
|
|
159
|
+
local field="$2"
|
|
160
|
+
local value="$3"
|
|
161
|
+
|
|
162
|
+
validate_change_name "$change_name"
|
|
163
|
+
|
|
164
|
+
local change_dir="openspec/changes/$change_name"
|
|
165
|
+
local yaml_file="$change_dir/.comet.yaml"
|
|
166
|
+
|
|
167
|
+
# Check if .comet.yaml exists
|
|
168
|
+
if [ ! -f "$yaml_file" ]; then
|
|
169
|
+
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
170
|
+
exit 1
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# Validate field name
|
|
174
|
+
case "$field" in
|
|
175
|
+
workflow|phase|build_mode|isolation|verify_mode|verify_result|archived|design_doc|plan|verified_at)
|
|
176
|
+
# Valid field
|
|
177
|
+
;;
|
|
178
|
+
*)
|
|
179
|
+
red "ERROR: Unknown field: '$field'" >&2
|
|
180
|
+
red "Valid fields: workflow, phase, design_doc, plan, build_mode, isolation, verify_mode, verify_result, verified_at, archived" >&2
|
|
181
|
+
exit 1
|
|
182
|
+
;;
|
|
183
|
+
esac
|
|
184
|
+
|
|
185
|
+
# Validate enum values
|
|
186
|
+
case "$field" in
|
|
187
|
+
workflow)
|
|
188
|
+
validate_enum "$value" "full" "hotfix" "tweak"
|
|
189
|
+
;;
|
|
190
|
+
phase)
|
|
191
|
+
validate_enum "$value" "design" "build" "verify" "archive"
|
|
192
|
+
;;
|
|
193
|
+
build_mode)
|
|
194
|
+
validate_enum "$value" "subagent-driven-development" "executing-plans" "direct"
|
|
195
|
+
;;
|
|
196
|
+
isolation)
|
|
197
|
+
validate_enum "$value" "branch" "worktree"
|
|
198
|
+
;;
|
|
199
|
+
verify_mode)
|
|
200
|
+
validate_enum "$value" "light" "full"
|
|
201
|
+
;;
|
|
202
|
+
verify_result)
|
|
203
|
+
validate_enum "$value" "pending" "pass" "fail"
|
|
204
|
+
;;
|
|
205
|
+
archived)
|
|
206
|
+
validate_enum "$value" "true" "false"
|
|
207
|
+
;;
|
|
208
|
+
design_doc|plan|verified_at)
|
|
209
|
+
# No validation for path fields and date fields
|
|
210
|
+
;;
|
|
211
|
+
esac
|
|
212
|
+
|
|
213
|
+
# Write or update the field
|
|
214
|
+
if grep -q "^${field}:" "$yaml_file"; then
|
|
215
|
+
# Field exists, replace it (use | delimiter to avoid / conflicts in paths)
|
|
216
|
+
sed -i "s|^${field}:.*|${field}: ${value}|" "$yaml_file"
|
|
217
|
+
else
|
|
218
|
+
# Field doesn't exist, append it
|
|
219
|
+
echo "${field}: ${value}" >> "$yaml_file"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
green "[SET] ${field}=${value}"
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
# --- Check helpers for entry verification ---
|
|
226
|
+
|
|
227
|
+
CHECK_BLOCK=0
|
|
228
|
+
|
|
229
|
+
check_pass() {
|
|
230
|
+
local msg="$1"
|
|
231
|
+
echo " $(green "[PASS]") $msg"
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
check_fail() {
|
|
235
|
+
local msg="$1"
|
|
236
|
+
echo " $(red "[FAIL]") $msg"
|
|
237
|
+
CHECK_BLOCK=1
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
check_nonempty() {
|
|
241
|
+
local desc="$1"
|
|
242
|
+
local path="$2"
|
|
243
|
+
if file_nonempty "$path"; then
|
|
244
|
+
check_pass "$desc non-empty"
|
|
245
|
+
else
|
|
246
|
+
check_fail "$desc missing or empty"
|
|
247
|
+
fi
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
check_yaml_is() {
|
|
251
|
+
local field="$1"
|
|
252
|
+
local expected="$2"
|
|
253
|
+
local change_name="$3"
|
|
254
|
+
local actual
|
|
255
|
+
actual=$(cmd_get "$change_name" "$field")
|
|
256
|
+
if [ "$actual" = "$expected" ]; then
|
|
257
|
+
check_pass "${field}=${actual} (expected: ${expected})"
|
|
258
|
+
else
|
|
259
|
+
check_fail "${field}=${actual} (expected: ${expected})"
|
|
260
|
+
fi
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
check_yaml_empty() {
|
|
264
|
+
local field="$1"
|
|
265
|
+
local change_name="$2"
|
|
266
|
+
local value
|
|
267
|
+
value=$(cmd_get "$change_name" "$field")
|
|
268
|
+
if [ -z "$value" ] || [ "$value" = "null" ]; then
|
|
269
|
+
check_pass "${field} is empty/null"
|
|
270
|
+
else
|
|
271
|
+
check_fail "${field}=${value} (expected: empty/null)"
|
|
272
|
+
fi
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
check_file_not_exists() {
|
|
276
|
+
local desc="$1"
|
|
277
|
+
local path="$2"
|
|
278
|
+
if [ ! -f "$path" ]; then
|
|
279
|
+
check_pass "$desc does not exist"
|
|
280
|
+
else
|
|
281
|
+
check_fail "$desc exists (should not exist)"
|
|
282
|
+
fi
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
cmd_check() {
|
|
286
|
+
local phase="$1"
|
|
287
|
+
local change_name="$2"
|
|
288
|
+
|
|
289
|
+
validate_change_name "$change_name"
|
|
290
|
+
validate_enum "$phase" "open" "design" "build" "verify" "archive"
|
|
291
|
+
|
|
292
|
+
local change_dir="openspec/changes/$change_name"
|
|
293
|
+
local yaml_file="$change_dir/.comet.yaml"
|
|
294
|
+
local proposal_file="$change_dir/proposal.md"
|
|
295
|
+
local design_file="$change_dir/design.md"
|
|
296
|
+
local tasks_file="$change_dir/tasks.md"
|
|
297
|
+
|
|
298
|
+
echo "=== Entry Check: comet-${phase} ==="
|
|
299
|
+
|
|
300
|
+
# For non-open phases, .comet.yaml must exist
|
|
301
|
+
if [ "$phase" != "open" ]; then
|
|
302
|
+
if [ ! -f "$yaml_file" ]; then
|
|
303
|
+
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
304
|
+
exit 1
|
|
305
|
+
fi
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
# Phase-specific checks
|
|
309
|
+
case "$phase" in
|
|
310
|
+
open)
|
|
311
|
+
check_file_not_exists ".comet.yaml" "$yaml_file"
|
|
312
|
+
check_nonempty "proposal.md" "$proposal_file"
|
|
313
|
+
check_nonempty "design.md" "$design_file"
|
|
314
|
+
check_nonempty "tasks.md" "$tasks_file"
|
|
315
|
+
;;
|
|
316
|
+
design)
|
|
317
|
+
check_pass ".comet.yaml exists"
|
|
318
|
+
check_yaml_is "phase" "design" "$change_name"
|
|
319
|
+
check_yaml_is "workflow" "full" "$change_name"
|
|
320
|
+
check_yaml_empty "design_doc" "$change_name"
|
|
321
|
+
check_nonempty "proposal.md" "$proposal_file"
|
|
322
|
+
check_nonempty "design.md" "$design_file"
|
|
323
|
+
check_nonempty "tasks.md" "$tasks_file"
|
|
324
|
+
;;
|
|
325
|
+
build)
|
|
326
|
+
check_pass ".comet.yaml exists"
|
|
327
|
+
check_yaml_is "phase" "build" "$change_name"
|
|
328
|
+
# Check design_doc is non-null and file exists
|
|
329
|
+
local design_doc
|
|
330
|
+
design_doc=$(cmd_get "$change_name" "design_doc")
|
|
331
|
+
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ] && [ -f "$change_dir/$design_doc" ]; then
|
|
332
|
+
check_pass "design_doc=${design_doc} (file exists)"
|
|
333
|
+
else
|
|
334
|
+
check_fail "design_doc=${design_doc} (expected: non-null and file exists)"
|
|
335
|
+
fi
|
|
336
|
+
check_nonempty "proposal.md" "$proposal_file"
|
|
337
|
+
check_nonempty "tasks.md" "$tasks_file"
|
|
338
|
+
;;
|
|
339
|
+
verify)
|
|
340
|
+
check_pass ".comet.yaml exists"
|
|
341
|
+
check_yaml_is "phase" "verify" "$change_name"
|
|
342
|
+
# Check verify_result is pending or null
|
|
343
|
+
local verify_result
|
|
344
|
+
verify_result=$(cmd_get "$change_name" "verify_result")
|
|
345
|
+
if [ "$verify_result" = "pending" ] || [ -z "$verify_result" ] || [ "$verify_result" = "null" ]; then
|
|
346
|
+
check_pass "verify_result=${verify_result} (expected: pending or null)"
|
|
347
|
+
else
|
|
348
|
+
check_fail "verify_result=${verify_result} (expected: pending or null)"
|
|
349
|
+
fi
|
|
350
|
+
;;
|
|
351
|
+
archive)
|
|
352
|
+
check_pass ".comet.yaml exists"
|
|
353
|
+
check_yaml_is "phase" "archive" "$change_name"
|
|
354
|
+
check_yaml_is "verify_result" "pass" "$change_name"
|
|
355
|
+
# Check archived is NOT true
|
|
356
|
+
local archived
|
|
357
|
+
archived=$(cmd_get "$change_name" "archived")
|
|
358
|
+
if [ "$archived" != "true" ]; then
|
|
359
|
+
check_pass "archived=${archived} (expected: not true)"
|
|
360
|
+
else
|
|
361
|
+
check_fail "archived=${archived} (expected: not true)"
|
|
362
|
+
fi
|
|
363
|
+
;;
|
|
364
|
+
*)
|
|
365
|
+
red "ERROR: Unknown phase for check: $phase"
|
|
366
|
+
exit 1
|
|
367
|
+
;;
|
|
368
|
+
esac
|
|
369
|
+
|
|
370
|
+
echo ""
|
|
371
|
+
if [ "$CHECK_BLOCK" -eq 1 ]; then
|
|
372
|
+
red "BLOCKED — fix failing checks before proceeding"
|
|
373
|
+
exit 1
|
|
374
|
+
else
|
|
375
|
+
green "ALL CHECKS PASSED — ready to proceed"
|
|
376
|
+
exit 0
|
|
377
|
+
fi
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
cmd_scale() {
|
|
381
|
+
local change_name="$1"
|
|
382
|
+
|
|
383
|
+
validate_change_name "$change_name"
|
|
384
|
+
|
|
385
|
+
local change_dir="openspec/changes/$change_name"
|
|
386
|
+
local yaml_file="$change_dir/.comet.yaml"
|
|
387
|
+
|
|
388
|
+
# Verify .comet.yaml exists
|
|
389
|
+
if [ ! -f "$yaml_file" ]; then
|
|
390
|
+
red "ERROR: .comet.yaml not found at $yaml_file"
|
|
391
|
+
exit 1
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
# Read metrics
|
|
395
|
+
# 1. Task count: count lines matching `- [` in tasks.md
|
|
396
|
+
local tasks_file="$change_dir/tasks.md"
|
|
397
|
+
local task_count=0
|
|
398
|
+
if [ -f "$tasks_file" ]; then
|
|
399
|
+
task_count=$(grep -c '^\- \[' "$tasks_file" 2>/dev/null || echo "0")
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
# 2. Delta spec count: count files named spec.md under specs/*/spec.md
|
|
403
|
+
local delta_spec_count=0
|
|
404
|
+
if [ -d "$change_dir/specs" ]; then
|
|
405
|
+
delta_spec_count=$(find "$change_dir/specs" -name "spec.md" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
# 3. Changed files: from git diff --stat HEAD
|
|
409
|
+
local changed_files=0
|
|
410
|
+
if git rev-parse --git-dir > /dev/null 2>&1; then
|
|
411
|
+
# Extract the number before "file" in the last line
|
|
412
|
+
local stat_output
|
|
413
|
+
stat_output=$(git diff --stat HEAD 2>/dev/null | tail -1)
|
|
414
|
+
if [[ "$stat_output" =~ ([0-9]+)\ file ]]; then
|
|
415
|
+
changed_files="${BASH_REMATCH[1]}"
|
|
416
|
+
fi
|
|
417
|
+
fi
|
|
418
|
+
|
|
419
|
+
# Decision rules
|
|
420
|
+
local result="light"
|
|
421
|
+
if [ "$task_count" -gt 3 ] || [ "$delta_spec_count" -gt 1 ] || [ "$changed_files" -gt 5 ]; then
|
|
422
|
+
result="full"
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
# Output assessment to stderr
|
|
426
|
+
echo "=== Scale Assessment: $change_name ===" >&2
|
|
427
|
+
echo " Tasks: $task_count (threshold: 3)" >&2
|
|
428
|
+
echo " Delta specs: $delta_spec_count capabilities (threshold: 1)" >&2
|
|
429
|
+
echo " Changed files: $changed_files (threshold: 5)" >&2
|
|
430
|
+
echo " → Result: $result" >&2
|
|
431
|
+
|
|
432
|
+
# Update verify_mode in .comet.yaml
|
|
433
|
+
sed -i "s/^verify_mode:.*/verify_mode: $result/" "$yaml_file"
|
|
434
|
+
|
|
435
|
+
green "[SCALE] verify_mode=$result"
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
# --- Main ---
|
|
439
|
+
|
|
440
|
+
SUBCOMMAND="${1:-}"
|
|
441
|
+
shift || true
|
|
442
|
+
|
|
443
|
+
case "$SUBCOMMAND" in
|
|
444
|
+
init)
|
|
445
|
+
if [ $# -lt 2 ]; then
|
|
446
|
+
red "Usage: comet-state.sh init <change-name> <workflow>" >&2
|
|
447
|
+
red "Workflows: full, hotfix, tweak" >&2
|
|
448
|
+
exit 1
|
|
449
|
+
fi
|
|
450
|
+
cmd_init "$@"
|
|
451
|
+
;;
|
|
452
|
+
get)
|
|
453
|
+
if [ $# -lt 2 ]; then
|
|
454
|
+
red "Usage: comet-state.sh get <change-name> <field>" >&2
|
|
455
|
+
exit 1
|
|
456
|
+
fi
|
|
457
|
+
cmd_get "$@"
|
|
458
|
+
;;
|
|
459
|
+
set)
|
|
460
|
+
if [ $# -lt 3 ]; then
|
|
461
|
+
red "Usage: comet-state.sh set <change-name> <field> <value>" >&2
|
|
462
|
+
exit 1
|
|
463
|
+
fi
|
|
464
|
+
cmd_set "$@"
|
|
465
|
+
;;
|
|
466
|
+
check)
|
|
467
|
+
if [ $# -lt 2 ]; then
|
|
468
|
+
red "Usage: comet-state.sh check <phase> <change-name>" >&2
|
|
469
|
+
red "Phases: open, design, build, verify, archive" >&2
|
|
470
|
+
exit 1
|
|
471
|
+
fi
|
|
472
|
+
cmd_check "$@"
|
|
473
|
+
;;
|
|
474
|
+
scale)
|
|
475
|
+
if [ $# -lt 1 ]; then
|
|
476
|
+
red "Usage: comet-state.sh scale <change-name>" >&2
|
|
477
|
+
exit 1
|
|
478
|
+
fi
|
|
479
|
+
cmd_scale "$@"
|
|
480
|
+
;;
|
|
481
|
+
*)
|
|
482
|
+
red "Unknown subcommand: $SUBCOMMAND" >&2
|
|
483
|
+
echo "" >&2
|
|
484
|
+
echo "Usage: comet-state.sh <subcommand> <change-name> [args...]" >&2
|
|
485
|
+
echo "" >&2
|
|
486
|
+
echo "Subcommands:" >&2
|
|
487
|
+
echo " init <change-name> <workflow> — Initialize .comet.yaml with workflow defaults" >&2
|
|
488
|
+
echo " get <change-name> <field> — Read a field value from .comet.yaml" >&2
|
|
489
|
+
echo " set <change-name> <field> <val> — Update a field value in .comet.yaml" >&2
|
|
490
|
+
echo " check <phase> <change-name> — Verify entry requirements for a phase" >&2
|
|
491
|
+
echo " scale <change-name> — Assess and set verification mode based on metrics" >&2
|
|
492
|
+
echo "" >&2
|
|
493
|
+
echo "Workflows: full, hotfix, tweak" >&2
|
|
494
|
+
echo "Phases for check: open, design, build, verify, archive" >&2
|
|
495
|
+
exit 1
|
|
496
|
+
;;
|
|
497
|
+
esac
|