@kafka0102/onespec 0.1.2 → 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.
Files changed (29) hide show
  1. package/README.md +45 -48
  2. package/assets/skills/onespec/SKILL.md +22 -14
  3. package/assets/skills/onespec/references/archive.md +214 -0
  4. package/assets/skills/{onespec-design/SKILL.md → onespec/references/design.md} +55 -51
  5. package/assets/skills/onespec/references/execute.md +291 -0
  6. package/assets/skills/onespec/references/fast.md +110 -0
  7. package/assets/skills/onespec/scripts/onespec-closeout.sh +238 -77
  8. package/assets/skills/onespec/scripts/onespec-commit.sh +191 -11
  9. package/assets/skills/onespec/scripts/onespec-handoff.sh +19 -6
  10. package/assets/skills/onespec/scripts/onespec-state.sh +157 -18
  11. package/assets/skills/onespec-fast/SKILL.md +22 -0
  12. package/assets/skills/onespec-fast/agents/openai.yaml +4 -0
  13. package/assets/skills-en/onespec/SKILL.md +22 -13
  14. package/assets/skills-en/onespec/references/archive.md +213 -0
  15. package/assets/skills-en/{onespec-design/SKILL.md → onespec/references/design.md} +58 -43
  16. package/assets/skills-en/onespec/references/execute.md +291 -0
  17. package/assets/skills-en/onespec/references/fast.md +110 -0
  18. package/assets/skills-en/onespec-fast/SKILL.md +22 -0
  19. package/package.json +10 -3
  20. package/scripts/postinstall.js +3 -3
  21. package/src/cli.js +120 -110
  22. package/src/doctor.js +46 -20
  23. package/src/init.js +24 -10
  24. package/src/platforms.js +88 -8
  25. package/src/setup.js +211 -0
  26. package/assets/skills/onespec-archive/SKILL.md +0 -202
  27. package/assets/skills/onespec-execute/SKILL.md +0 -219
  28. package/assets/skills-en/onespec-archive/SKILL.md +0 -199
  29. package/assets/skills-en/onespec-execute/SKILL.md +0 -219
@@ -26,7 +26,9 @@ change_dir() {
26
26
 
27
27
  state_file() {
28
28
  local change="$1"
29
- printf '%s/.onespec.yaml\n' "$(change_dir "$change")"
29
+ local script_root
30
+ script_root="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd -P)"
31
+ "${BASH:-bash}" "$script_root/onespec-state.sh" path "$change"
30
32
  }
31
33
 
32
34
  normalize_path() {
@@ -123,9 +125,12 @@ git_dirty_paths() {
123
125
 
124
126
  dirty_change_artifact_paths() {
125
127
  local change="$1"
126
- local dir
127
- dir="$(change_dir "$change")"
128
- git_dirty_paths | awk -v prefix="$dir/" 'index($0, prefix) == 1 { print }'
128
+ local active_prefix archive_prefix
129
+ active_prefix="openspec/changes/$change/"
130
+ archive_prefix="openspec/changes/archive/$change/"
131
+ git_dirty_paths | awk -v active="$active_prefix" -v archived="$archive_prefix" '
132
+ index($0, active) == 1 || index($0, archived) == 1 { print }
133
+ '
129
134
  }
130
135
 
131
136
  repo_layout() {
@@ -152,7 +157,20 @@ find_policy_doc() {
152
157
  local pattern='提交|commit message|commit messages|提交信息|提交规范|提交格式|conventional commit|conventional commits|git workflow|commitlint|commitizen'
153
158
  local file
154
159
 
155
- for file in AGENTS.md CONTRIBUTING.md CONTRIBUTING.zh-CN.md README.md README-zh.md README.en.md; do
160
+ for file in \
161
+ AGENTS.md \
162
+ agents.md \
163
+ .agents.md \
164
+ CLAUDE.md \
165
+ claude.md \
166
+ .claude.md \
167
+ CONTRIBUTING.md \
168
+ CONTRIBUTING.zh-CN.md \
169
+ README.md \
170
+ README-zh.md \
171
+ README.en.md \
172
+ README.zh-CN.md
173
+ do
156
174
  if [ -f "$file" ] && grep -Eiq "$pattern" "$file"; then
157
175
  printf '%s\n' "$file"
158
176
  return 0
@@ -222,13 +240,35 @@ detect_language_from_doc() {
222
240
  detect_format_from_file() {
223
241
  local file="$1"
224
242
 
225
- if grep -Eiq '<type>\(<scope>\):|conventional commit|conventional commits|commitlint|type\(scope\)' "$file"; then
243
+ if grep -Eiq '<type>\(<scope>\):|type\(scope\)' "$file"; then
244
+ echo "conventional-scope"
245
+ return 0
246
+ fi
247
+ if grep -Eiq '<type>:[[:space:]]*<|^```text$|^```$' "$file" && grep -Eiq '<type>:[[:space:]]*<' "$file"; then
248
+ echo "conventional"
249
+ return 0
250
+ fi
251
+ if grep -Eiq 'conventional commit|conventional commits|commitlint' "$file"; then
226
252
  echo "conventional"
227
253
  return 0
228
254
  fi
229
255
  echo "unknown"
230
256
  }
231
257
 
258
+ scope_mode_for_format() {
259
+ case "$1" in
260
+ conventional-scope)
261
+ echo "required"
262
+ ;;
263
+ conventional)
264
+ echo "optional"
265
+ ;;
266
+ *)
267
+ echo "optional"
268
+ ;;
269
+ esac
270
+ }
271
+
232
272
  infer_scope() {
233
273
  local change="${1:-}"
234
274
  local tracked
@@ -281,6 +321,85 @@ infer_scope() {
281
321
  rm -f "$tracked"
282
322
  }
283
323
 
324
+ valid_commit_context() {
325
+ case "$1" in
326
+ closeout|archive|preserve-state) ;;
327
+ *)
328
+ die "unsupported commit context: $1"
329
+ ;;
330
+ esac
331
+ }
332
+
333
+ policy_value() {
334
+ local policy="$1"
335
+ local key="$2"
336
+ printf '%s\n' "$policy" | awk -F ': ' -v key="$key" '$1 == key { print $2; exit }'
337
+ }
338
+
339
+ default_commit_summary() {
340
+ local change="$1"
341
+ local context="$2"
342
+ local language="$3"
343
+
344
+ case "$context" in
345
+ closeout)
346
+ if [ "$language" = "zh" ]; then
347
+ printf '提交 %s 收尾前改动\n' "$change"
348
+ else
349
+ printf 'record %s before closeout\n' "$change"
350
+ fi
351
+ ;;
352
+ archive)
353
+ if [ "$language" = "zh" ]; then
354
+ printf '归档 %s\n' "$change"
355
+ else
356
+ printf 'archive %s\n' "$change"
357
+ fi
358
+ ;;
359
+ preserve-state)
360
+ if [ "$language" = "zh" ]; then
361
+ printf '保存 %s 收尾状态\n' "$change"
362
+ else
363
+ printf 'preserve %s closeout state\n' "$change"
364
+ fi
365
+ ;;
366
+ esac
367
+ }
368
+
369
+ build_commit_message() {
370
+ local change="$1"
371
+ local context="$2"
372
+ local policy language scope summary format scope_mode type
373
+
374
+ valid_commit_context "$context"
375
+ policy="$(cmd_detect_policy "$change")"
376
+ language="$(policy_value "$policy" message_language)"
377
+ scope="$(policy_value "$policy" scope_hint)"
378
+ format="$(policy_value "$policy" commit_format)"
379
+ scope_mode="$(policy_value "$policy" scope_mode)"
380
+ [ -n "$language" ] || language="en"
381
+ [ -n "$scope" ] || scope="repo"
382
+ [ -n "$format" ] || format="conventional-scope"
383
+ [ -n "$scope_mode" ] || scope_mode="optional"
384
+ type="chore"
385
+ case "$context" in
386
+ archive|preserve-state)
387
+ scope="docs"
388
+ ;;
389
+ esac
390
+ summary="$(default_commit_summary "$change" "$context" "$language")"
391
+
392
+ if [ "$scope_mode" = "required" ]; then
393
+ printf '%s(%s): %s\n' "$type" "$scope" "$summary"
394
+ else
395
+ printf '%s: %s\n' "$type" "$summary"
396
+ fi
397
+ }
398
+
399
+ has_staged_changes() {
400
+ ! git diff --cached --quiet --exit-code
401
+ }
402
+
284
403
  cmd_track() {
285
404
  local change="$1"
286
405
  shift
@@ -363,22 +482,24 @@ cmd_stage_related() {
363
482
 
364
483
  cmd_detect_policy() {
365
484
  local change="${1:-}"
366
- local layout source origin format language confidence scope
485
+ local layout source origin format language confidence scope scope_mode template
367
486
 
368
487
  layout="$(repo_layout)"
369
488
  scope="$(infer_scope "$change")"
370
489
  confidence="default"
371
490
  origin="default"
372
491
  source="default"
373
- format="conventional"
492
+ format="conventional-scope"
374
493
  language="en"
494
+ scope_mode="optional"
495
+ template="<type>(<scope>): <summary>"
375
496
 
376
497
  if source="$(find_policy_doc)"; then
377
498
  origin="project-doc"
378
499
  confidence="explicit"
379
500
  format="$(detect_format_from_file "$source")"
380
501
  language="$(detect_language_from_doc "$source")"
381
- [ "$format" != "unknown" ] || format="conventional"
502
+ [ "$format" != "unknown" ] || format="conventional-scope"
382
503
  [ "$language" != "unknown" ] || language="en"
383
504
  else
384
505
  local config_source
@@ -386,20 +507,74 @@ cmd_detect_policy() {
386
507
  source="$config_source"
387
508
  origin="project-config"
388
509
  confidence="partial"
389
- format="conventional"
510
+ format="conventional-scope"
390
511
  language="en"
391
512
  fi
392
513
  fi
393
514
 
515
+ scope_mode="$(scope_mode_for_format "$format")"
516
+ case "$format" in
517
+ conventional-scope)
518
+ template="<type>(<scope>): <summary>"
519
+ ;;
520
+ conventional)
521
+ template="<type>: <summary>"
522
+ ;;
523
+ esac
524
+
394
525
  cat <<EOF
395
526
  policy_source: $source
396
527
  policy_origin: $origin
397
528
  policy_confidence: $confidence
398
529
  commit_format: $format
530
+ scope_mode: $scope_mode
399
531
  message_language: $language
400
532
  repo_layout: $layout
401
533
  scope_hint: $scope
402
- template: <type>(<scope>): <summary>
534
+ template: $template
535
+ EOF
536
+ }
537
+
538
+ cmd_commit_related() {
539
+ local change="$1"
540
+ local context="${2:-closeout}"
541
+ local related message sha
542
+
543
+ valid_change "$change"
544
+ valid_commit_context "$context"
545
+ ensure_git_repo
546
+
547
+ related="$(cmd_related_dirty "$change")"
548
+ if [ -z "$related" ]; then
549
+ cat <<EOF
550
+ commit_created: false
551
+ commit_context: $context
552
+ commit_sha: none
553
+ commit_message: none
554
+ EOF
555
+ return 0
556
+ fi
557
+
558
+ cmd_stage_related "$change" >/dev/null
559
+ if ! has_staged_changes; then
560
+ cat <<EOF
561
+ commit_created: false
562
+ commit_context: $context
563
+ commit_sha: none
564
+ commit_message: none
565
+ EOF
566
+ return 0
567
+ fi
568
+
569
+ message="$(build_commit_message "$change" "$context")"
570
+ git commit -m "$message" >/dev/null
571
+ sha="$(git rev-parse HEAD)"
572
+
573
+ cat <<EOF
574
+ commit_created: true
575
+ commit_context: $context
576
+ commit_sha: $sha
577
+ commit_message: $message
403
578
  EOF
404
579
  }
405
580
 
@@ -411,6 +586,7 @@ usage() {
411
586
  onespec-commit.sh related-dirty <change>
412
587
  onespec-commit.sh stage-related <change>
413
588
  onespec-commit.sh detect-policy [change]
589
+ onespec-commit.sh commit-related <change> [closeout|archive|preserve-state]
414
590
  EOF
415
591
  }
416
592
 
@@ -437,6 +613,10 @@ case "$cmd" in
437
613
  [ "$#" -le 2 ] || { usage; exit 2; }
438
614
  cmd_detect_policy "${2:-}"
439
615
  ;;
616
+ commit-related)
617
+ [ "$#" -ge 2 ] && [ "$#" -le 3 ] || { usage; exit 2; }
618
+ cmd_commit_related "$2" "${3:-closeout}"
619
+ ;;
440
620
  *)
441
621
  usage
442
622
  exit 2
@@ -30,6 +30,18 @@ json_escape() {
30
30
  sed 's/\\/\\\\/g; s/"/\\"/g' <<<"$1"
31
31
  }
32
32
 
33
+ display_path() {
34
+ local file="$1"
35
+ case "$file" in
36
+ */openspec/*)
37
+ printf 'openspec/%s\n' "${file##*/openspec/}"
38
+ ;;
39
+ *)
40
+ printf '%s\n' "$file"
41
+ ;;
42
+ esac
43
+ }
44
+
33
45
  valid_change() {
34
46
  local change="$1"
35
47
  [[ -n "$change" ]] || die "change name is required"
@@ -57,7 +69,7 @@ summary_text() {
57
69
  files="$(source_files)"
58
70
  count="$(printf '%s\n' "$files" | sed '/^$/d' | wc -l | tr -d ' ')"
59
71
  first="$(printf '%s\n' "$files" | sed -n '1p')"
60
- printf '%s handoff from %s file(s); primary artifact: %s' "$purpose" "$count" "$first"
72
+ printf '%s handoff from %s file(s); primary artifact: %s' "$purpose" "$count" "$(display_path "$first")"
61
73
  }
62
74
 
63
75
  write_excerpt() {
@@ -95,9 +107,11 @@ case "$purpose" in proposal|plan|review|archive) ;; *) die "purpose must be prop
95
107
  case "$full_flag" in "" ) mode="compact" ;; "--full" ) mode="full" ;; * ) die "unknown option: $full_flag" ;; esac
96
108
 
97
109
  change_dir="openspec/changes/$change"
98
- state="$change_dir/.onespec.yaml"
99
- [ -d "$change_dir" ] || die "change directory not found: $change_dir"
110
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd -P)"
111
+ state="$("${BASH:-bash}" "$script_dir/onespec-state.sh" path "$change")"
100
112
  [ -f "$state" ] || die "state file not found: $state"
113
+ change_dir="$(cd "$(dirname "$state")" && pwd -P)"
114
+ state_label="$(display_path "$state")"
101
115
 
102
116
  if [ "$(source_files | wc -l | tr -d ' ')" -eq 0 ]; then
103
117
  die "no OpenSpec artifacts found under $change_dir"
@@ -106,10 +120,9 @@ fi
106
120
  hash="$(context_hash)"
107
121
  summary="$(summary_text)"
108
122
 
109
- script_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd -P)"
110
- "${BASH:-bash}" "$script_dir/onespec-state.sh" set "$change" handoff_context "$state"
123
+ "${BASH:-bash}" "$script_dir/onespec-state.sh" set "$change" handoff_context "$state_label"
111
124
  "${BASH:-bash}" "$script_dir/onespec-state.sh" set "$change" handoff_purpose "$purpose"
112
125
  "${BASH:-bash}" "$script_dir/onespec-state.sh" set "$change" handoff_summary "$summary"
113
126
  "${BASH:-bash}" "$script_dir/onespec-state.sh" set "$change" handoff_hash "$hash"
114
127
 
115
- echo "$state"
128
+ echo "$state_label"
@@ -29,6 +29,41 @@ state_file() {
29
29
  printf '%s/.onespec.yaml\n' "$(change_dir "$change")"
30
30
  }
31
31
 
32
+ current_workspace_path() {
33
+ pwd -P
34
+ }
35
+
36
+ in_git_repo() {
37
+ git rev-parse --show-toplevel >/dev/null 2>&1
38
+ }
39
+
40
+ canonicalize_dir() {
41
+ local dir="$1"
42
+ if [ -z "$dir" ] || [ "$dir" = "unknown" ] || [ ! -d "$dir" ]; then
43
+ printf '%s\n' "$dir"
44
+ return 0
45
+ fi
46
+ (
47
+ cd "$dir"
48
+ pwd -P
49
+ )
50
+ }
51
+
52
+ state_file_in_workspace() {
53
+ local workspace="$1"
54
+ local change="$2"
55
+ local active archived
56
+ active="$workspace/openspec/changes/$change/.onespec.yaml"
57
+ archived="$workspace/openspec/changes/archive/$change/.onespec.yaml"
58
+ if [ -f "$active" ]; then
59
+ printf '%s\n' "$active"
60
+ elif [ -f "$archived" ]; then
61
+ printf '%s\n' "$archived"
62
+ else
63
+ printf '%s\n' "$active"
64
+ fi
65
+ }
66
+
32
67
  today() {
33
68
  date +%Y-%m-%d
34
69
  }
@@ -43,6 +78,96 @@ field_value() {
43
78
  awk -F ': *' -v key="$key" '$1 == key { sub(/^[^:]+: */, ""); print; found=1; exit } END { if (!found) exit 0 }' "$file" 2>/dev/null
44
79
  }
45
80
 
81
+ phase_rank() {
82
+ case "$1" in
83
+ intake) echo 0 ;;
84
+ proposal-ready) echo 1 ;;
85
+ approved) echo 2 ;;
86
+ plan-ready) echo 3 ;;
87
+ implementing) echo 4 ;;
88
+ review) echo 5 ;;
89
+ done) echo 6 ;;
90
+ archived) echo 7 ;;
91
+ *) echo -1 ;;
92
+ esac
93
+ }
94
+
95
+ preferred_state_file() {
96
+ local left="$1"
97
+ local right="$2"
98
+ local left_phase right_phase left_rank right_rank left_updated right_updated
99
+
100
+ if [ -z "$left" ] || [ ! -f "$left" ]; then
101
+ printf '%s\n' "$right"
102
+ return 0
103
+ fi
104
+ if [ -z "$right" ] || [ ! -f "$right" ]; then
105
+ printf '%s\n' "$left"
106
+ return 0
107
+ fi
108
+
109
+ left_phase="$(field_value "$left" phase)"
110
+ right_phase="$(field_value "$right" phase)"
111
+ left_rank="$(phase_rank "$left_phase")"
112
+ right_rank="$(phase_rank "$right_phase")"
113
+ if [ "$right_rank" -gt "$left_rank" ]; then
114
+ printf '%s\n' "$right"
115
+ return 0
116
+ fi
117
+ if [ "$left_rank" -gt "$right_rank" ]; then
118
+ printf '%s\n' "$left"
119
+ return 0
120
+ fi
121
+
122
+ left_updated="$(field_value "$left" updated_at)"
123
+ right_updated="$(field_value "$right" updated_at)"
124
+ if [ -n "$right_updated" ] && { [ -z "$left_updated" ] || [ "$right_updated" \> "$left_updated" ]; }; then
125
+ printf '%s\n' "$right"
126
+ return 0
127
+ fi
128
+ printf '%s\n' "$left"
129
+ }
130
+
131
+ resolve_state_file() {
132
+ local change="$1"
133
+ local current_workspace current_file best_file implementation_workspace implementation_file worktree_path candidate
134
+
135
+ current_workspace="$(current_workspace_path)"
136
+ current_file="$(state_file_in_workspace "$current_workspace" "$change")"
137
+ if [ ! -f "$current_file" ] && ! in_git_repo; then
138
+ printf '%s\n' "$current_file"
139
+ return 0
140
+ fi
141
+
142
+ best_file=""
143
+ if [ -f "$current_file" ]; then
144
+ best_file="$current_file"
145
+ implementation_workspace="$(canonicalize_dir "$(field_value "$current_file" implementation_workspace_path)")"
146
+ if [ -n "$implementation_workspace" ] && [ "$implementation_workspace" != "unknown" ]; then
147
+ implementation_file="$(state_file_in_workspace "$implementation_workspace" "$change")"
148
+ if [ -f "$implementation_file" ]; then
149
+ best_file="$(preferred_state_file "$best_file" "$implementation_file")"
150
+ fi
151
+ fi
152
+ fi
153
+
154
+ if in_git_repo; then
155
+ while IFS= read -r worktree_path; do
156
+ [ -n "$worktree_path" ] || continue
157
+ candidate="$(state_file_in_workspace "$worktree_path" "$change")"
158
+ if [ -f "$candidate" ]; then
159
+ best_file="$(preferred_state_file "$best_file" "$candidate")"
160
+ fi
161
+ done < <(git worktree list --porcelain 2>/dev/null | awk '/^worktree / { sub(/^worktree /, ""); print }')
162
+ fi
163
+
164
+ if [ -n "$best_file" ]; then
165
+ printf '%s\n' "$best_file"
166
+ else
167
+ printf '%s\n' "$current_file"
168
+ fi
169
+ }
170
+
46
171
  enum_contains() {
47
172
  local needle="$1"
48
173
  shift
@@ -170,6 +295,7 @@ workspace: undecided
170
295
  origin_branch: unknown
171
296
  origin_workspace_path: unknown
172
297
  origin_workspace_mode: unknown
298
+ implementation_workspace_path: unknown
173
299
  plan: null
174
300
  handoff_context: null
175
301
  handoff_purpose: null
@@ -189,7 +315,7 @@ cmd_get() {
189
315
  local key="$2"
190
316
  valid_change "$change"
191
317
  local file
192
- file="$(state_file "$change")"
318
+ file="$(resolve_state_file "$change")"
193
319
  [ -f "$file" ] || die "state not found: $file"
194
320
  field_value "$file" "$key"
195
321
  }
@@ -200,11 +326,11 @@ cmd_set() {
200
326
  local value="$3"
201
327
  valid_change "$change"
202
328
  local file
203
- file="$(state_file "$change")"
329
+ file="$(resolve_state_file "$change")"
204
330
  [ -f "$file" ] || die "state not found: $file"
205
331
 
206
332
  case "$key" in
207
- phase|ambiguity|complexity|implementation_path|execution_method|workspace|origin_branch|origin_workspace_path|origin_workspace_mode|plan|handoff_context|handoff_purpose|handoff_summary|handoff_hash|touched_files_b64|review_result|archive|updated_at)
333
+ phase|ambiguity|complexity|implementation_path|execution_method|workspace|origin_branch|origin_workspace_path|origin_workspace_mode|implementation_workspace_path|plan|handoff_context|handoff_purpose|handoff_summary|handoff_hash|touched_files_b64|review_result|archive|updated_at)
208
334
  ;;
209
335
  *)
210
336
  die "unsupported field: $key"
@@ -227,62 +353,63 @@ cmd_recover() {
227
353
  local change="$1"
228
354
  valid_change "$change"
229
355
  local file
230
- file="$(state_file "$change")"
356
+ file="$(resolve_state_file "$change")"
231
357
  [ -f "$file" ] || die "state not found: $file"
232
358
 
233
359
  echo "OneSpec 恢复状态"
234
360
  echo "change: $change"
235
- for key in phase ambiguity complexity implementation_path execution_method workspace origin_branch origin_workspace_path origin_workspace_mode plan handoff_context handoff_purpose handoff_summary handoff_hash review_result archive updated_at; do
361
+ for key in phase ambiguity complexity implementation_path execution_method workspace origin_branch origin_workspace_path origin_workspace_mode implementation_workspace_path plan handoff_context handoff_purpose handoff_summary handoff_hash review_result archive updated_at; do
236
362
  printf '%s: %s\n' "$key" "$(field_value "$file" "$key")"
237
363
  done
238
- local phase next_skill next_gate allowed_actions next_step
364
+ local phase next_skill next_reference next_gate allowed_actions next_step
239
365
  phase="$(field_value "$file" phase)"
366
+ next_skill="onespec"
240
367
 
241
368
  case "$phase" in
242
369
  intake)
243
- next_skill="onespec-design"
370
+ next_reference="references/design.md"
244
371
  next_gate="ambiguity-scan"
245
372
  allowed_actions="scan-ambiguity,draft-proposal"
246
373
  next_step="执行歧义扫描,然后创建或恢复 OpenSpec 提案"
247
374
  ;;
248
375
  proposal-ready)
249
- next_skill="onespec-design"
376
+ next_reference="references/design.md"
250
377
  next_gate="proposal-approval"
251
378
  allowed_actions="show-approval-menu,revise-artifacts,pause-design"
252
379
  next_step="汇总提案并展示编号批准选项,等待用户回复数字"
253
380
  ;;
254
381
  approved)
255
- next_skill="onespec-execute"
382
+ next_reference="references/execute.md"
256
383
  next_gate="implementation-route"
257
384
  allowed_actions="confirm-route,create-plan,choose-workspace"
258
- next_step="进入 \`onespec-execute\`,创建或校验实现计划"
385
+ next_step="读取 \`references/execute.md\`,创建或校验实现计划"
259
386
  ;;
260
387
  plan-ready)
261
- next_skill="onespec-execute"
388
+ next_reference="references/execute.md"
262
389
  next_gate="start-implementation"
263
390
  allowed_actions="record-phase-implementing,start-work,track-files"
264
- next_step="进入 \`onespec-execute\`,确认执行方式后开始实现,并先写入 \`phase implementing\`"
391
+ next_step="读取 \`references/execute.md\`,确认执行方式后开始实现,并先写入 \`phase implementing\`"
265
392
  ;;
266
393
  implementing)
267
- next_skill="onespec-execute"
394
+ next_reference="references/execute.md"
268
395
  next_gate="implementation-in-progress"
269
396
  allowed_actions="continue-implementation,update-tasks,run-tests"
270
397
  next_step="继续未完成任务,然后更新 tasks.md"
271
398
  ;;
272
399
  review)
273
- next_skill="onespec-archive"
400
+ next_reference="references/archive.md"
274
401
  next_gate="user-review-closeout"
275
- allowed_actions="request-changes,choose-archive-menu,direct-instruction"
276
- next_step="等待用户评审;若用户回复非编号内容则继续修改,若选择归档菜单则进入 \`onespec-archive\` 处理删除 worktree / 归档组合选项"
402
+ allowed_actions="request-changes,choose-closeout-action,direct-instruction"
403
+ next_step="等待用户评审;若用户回复非编号内容则继续修改,若用户明确选择收尾动作,则读取 \`references/archive.md\` 直接执行对应 closeout"
277
404
  ;;
278
405
  done|archived)
279
- next_skill="onespec-archive"
406
+ next_reference="references/archive.md"
280
407
  next_gate="no-implementation-work"
281
408
  allowed_actions="stop,archive-if-needed"
282
409
  next_step="没有剩余实现工作"
283
410
  ;;
284
411
  *)
285
- next_skill="onespec"
412
+ next_reference="references/design.md"
286
413
  next_gate="repair-state"
287
414
  allowed_actions="inspect-artifacts,repair-state"
288
415
  next_step="检查 OpenSpec 产物并修复状态"
@@ -290,11 +417,18 @@ cmd_recover() {
290
417
  esac
291
418
 
292
419
  printf 'next_skill: %s\n' "$next_skill"
420
+ printf 'next_reference: %s\n' "$next_reference"
293
421
  printf 'next_gate: %s\n' "$next_gate"
294
422
  printf 'allowed_actions: %s\n' "$allowed_actions"
295
423
  printf '下一步: %s\n' "$next_step"
296
424
  }
297
425
 
426
+ cmd_path() {
427
+ local change="$1"
428
+ valid_change "$change"
429
+ resolve_state_file "$change"
430
+ }
431
+
298
432
  cmd_list() {
299
433
  if [ ! -d openspec/changes ]; then
300
434
  return 0
@@ -306,6 +440,7 @@ usage() {
306
440
  cat <<'EOF'
307
441
  用法:
308
442
  onespec-state.sh init <change>
443
+ onespec-state.sh path <change>
309
444
  onespec-state.sh get <change> <field>
310
445
  onespec-state.sh set <change> <field> <value>
311
446
  onespec-state.sh recover <change>
@@ -319,6 +454,10 @@ case "$cmd" in
319
454
  [ "$#" -eq 2 ] || { usage; exit 2; }
320
455
  cmd_init "$2"
321
456
  ;;
457
+ path)
458
+ [ "$#" -eq 2 ] || { usage; exit 2; }
459
+ cmd_path "$2"
460
+ ;;
322
461
  get)
323
462
  [ "$#" -eq 3 ] || { usage; exit 2; }
324
463
  cmd_get "$2" "$3"
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: onespec-fast
3
+ description: 当用户明确要求使用 OneSpec 快速路径、onespec-fast、fast apply、OpenSpec 自动 proposal/开发/归档或自动贯通时使用。该 skill 会复用 `onespec/references/fast.md`,全程走原生 OpenSpec apply,不做复杂度检查,不生成 Superpowers plan。
4
+ ---
5
+
6
+ # OneSpec Fast
7
+
8
+ 这是 OneSpec 快速路径的独立入口。它不重复阶段规则;必须复用主 `onespec` skill 的 `references/fast.md`。
9
+
10
+ 开始时说明:
11
+
12
+ > 我正在使用 `onespec-fast` 快速路径。
13
+
14
+ ## 入口规则
15
+
16
+ - 只有用户明确要求 `onespec-fast`、快速路径、fast apply、OpenSpec 自动 proposal/开发/归档或自动贯通时使用。
17
+ - 先读取相邻安装的 `../onespec/SKILL.md`,遵守其中的恢复优先、共同约束和 reference 读取规则。
18
+ - 然后读取 `../onespec/references/fast.md` 并按其中步骤执行。
19
+ - 如果相邻路径不可用,先在当前项目、`$HOME/.codex`、`$HOME/.claude`、`$HOME/.cursor`、`$HOME/.gemini`、`$HOME/.copilot`、`$HOME/.agents`、`$HOME/.config` 下定位 `*/onespec/references/fast.md`;仍找不到时停止并要求重新运行 `onespec init --overwrite`。
20
+ - `references/fast.md` 可以复用 `design.md`、`execute.md` 和 `archive.md` 的过程段,但会覆盖普通 proposal approval、review pause 和 closeout menu gate。
21
+
22
+ 不要在 `onespec-fast/SKILL.md` 内重写快速路径步骤;完整规则只维护在 `onespec/references/fast.md`。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "OneSpec Fast"
3
+ short_description: "Run OneSpec changes end to end with OpenSpec apply"
4
+ default_prompt: "Use $onespec-fast to implement and archive an OpenSpec change with native OpenSpec apply."