prizmkit 1.0.100 → 1.0.102
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/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/launch-daemon.sh +29 -4
- package/bundled/dev-pipeline/run.sh +43 -6
- package/bundled/dev-pipeline/scripts/update-feature-status.py +70 -8
- package/bundled/skills/_metadata.json +1 -1
- package/bundled/skills/app-planner/SKILL.md +43 -0
- package/bundled/skills/dev-pipeline-launcher/SKILL.md +39 -1
- package/package.json +1 -1
- package/src/gitignore-template.js +0 -3
- package/src/scaffold.js +2 -12
package/bundled/VERSION.json
CHANGED
|
@@ -93,6 +93,7 @@ cmd_start() {
|
|
|
93
93
|
local feature_list=""
|
|
94
94
|
local env_overrides=""
|
|
95
95
|
local mode_override=""
|
|
96
|
+
local features_filter=""
|
|
96
97
|
|
|
97
98
|
# Parse arguments
|
|
98
99
|
while [[ $# -gt 0 ]]; do
|
|
@@ -123,6 +124,15 @@ cmd_start() {
|
|
|
123
124
|
esac
|
|
124
125
|
shift
|
|
125
126
|
;;
|
|
127
|
+
--features)
|
|
128
|
+
shift
|
|
129
|
+
if [[ $# -eq 0 ]]; then
|
|
130
|
+
log_error "--features requires a value (e.g. --features F-001,F-003 or --features F-001:F-010)"
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
features_filter="$1"
|
|
134
|
+
shift
|
|
135
|
+
;;
|
|
126
136
|
*)
|
|
127
137
|
feature_list="$1"
|
|
128
138
|
shift
|
|
@@ -199,6 +209,9 @@ cmd_start() {
|
|
|
199
209
|
log_info "Launching pipeline..."
|
|
200
210
|
log_info "Feature list: $feature_list"
|
|
201
211
|
log_info "Log file: $LOG_FILE"
|
|
212
|
+
if [[ -n "$features_filter" ]]; then
|
|
213
|
+
log_info "Features filter: $features_filter"
|
|
214
|
+
fi
|
|
202
215
|
if [[ -n "$mode_override" ]]; then
|
|
203
216
|
log_info "Mode: $mode_override"
|
|
204
217
|
fi
|
|
@@ -212,6 +225,9 @@ cmd_start() {
|
|
|
212
225
|
echo "================================================================"
|
|
213
226
|
echo " Pipeline Daemon Started: $start_time"
|
|
214
227
|
echo " Feature list: $feature_list"
|
|
228
|
+
if [[ -n "$features_filter" ]]; then
|
|
229
|
+
echo " Features filter: $features_filter"
|
|
230
|
+
fi
|
|
215
231
|
if [[ -n "$mode_override" ]]; then
|
|
216
232
|
echo " Mode: $mode_override"
|
|
217
233
|
fi
|
|
@@ -227,11 +243,17 @@ cmd_start() {
|
|
|
227
243
|
# is inherited and blocks child claude processes with "nested sessions" error.
|
|
228
244
|
unset CLAUDECODE 2>/dev/null || true
|
|
229
245
|
|
|
246
|
+
# Build features flag
|
|
247
|
+
local features_flag=""
|
|
248
|
+
if [[ -n "$features_filter" ]]; then
|
|
249
|
+
features_flag="--features $features_filter"
|
|
250
|
+
fi
|
|
251
|
+
|
|
230
252
|
# Launch with nohup + disown for full detachment
|
|
231
253
|
if [[ -n "$env_cmd" ]]; then
|
|
232
|
-
nohup $env_cmd "$RUN_SCRIPT" run "$feature_list" >> "$LOG_FILE" 2>&1 &
|
|
254
|
+
nohup $env_cmd "$RUN_SCRIPT" run "$feature_list" $features_flag >> "$LOG_FILE" 2>&1 &
|
|
233
255
|
else
|
|
234
|
-
nohup "$RUN_SCRIPT" run "$feature_list" >> "$LOG_FILE" 2>&1 &
|
|
256
|
+
nohup "$RUN_SCRIPT" run "$feature_list" $features_flag >> "$LOG_FILE" 2>&1 &
|
|
235
257
|
fi
|
|
236
258
|
local pipeline_pid=$!
|
|
237
259
|
disown "$pipeline_pid" 2>/dev/null || true
|
|
@@ -548,20 +570,23 @@ show_help() {
|
|
|
548
570
|
Usage: launch-daemon.sh <command> [options]
|
|
549
571
|
|
|
550
572
|
Commands:
|
|
551
|
-
start [feature-list.json] [--mode <mode>] [--env "K=V ..."] Start pipeline in background
|
|
573
|
+
start [feature-list.json] [--mode <mode>] [--features <filter>] [--env "K=V ..."] Start pipeline in background
|
|
552
574
|
stop Gracefully stop pipeline
|
|
553
575
|
status Check if pipeline is running
|
|
554
576
|
logs [--lines N] [--follow] View pipeline logs
|
|
555
|
-
restart [feature-list.json] [--mode <mode>] [--env "K=V ..."] Stop + start pipeline
|
|
577
|
+
restart [feature-list.json] [--mode <mode>] [--features <filter>] [--env "K=V ..."] Stop + start pipeline
|
|
556
578
|
help Show this help
|
|
557
579
|
|
|
558
580
|
Options:
|
|
559
581
|
--mode <lite|standard|full|self-evolve> Override pipeline mode for all features
|
|
582
|
+
--features <filter> Run only specified features (e.g. F-001,F-003 or F-001:F-010)
|
|
560
583
|
--env "KEY=VAL ..." Set environment variables
|
|
561
584
|
|
|
562
585
|
Examples:
|
|
563
586
|
./launch-daemon.sh start # Start with default feature-list.json
|
|
564
587
|
./launch-daemon.sh start my-features.json # Start with custom feature list
|
|
588
|
+
./launch-daemon.sh start --features F-001:F-005 # Run only features F-001 through F-005
|
|
589
|
+
./launch-daemon.sh start --features F-001,F-003,F-007 # Run specific features
|
|
565
590
|
./launch-daemon.sh start --mode self-evolve # Self-evolve mode (framework development)
|
|
566
591
|
./launch-daemon.sh start --env "MAX_RETRIES=5 SESSION_TIMEOUT=7200"
|
|
567
592
|
./launch-daemon.sh start feature-list.json --mode self-evolve --env "VERBOSE=1"
|
|
@@ -783,6 +783,7 @@ print(data.get('reload_needed', False))
|
|
|
783
783
|
|
|
784
784
|
main() {
|
|
785
785
|
local feature_list="${1:-feature-list.json}"
|
|
786
|
+
local features_filter="${2:-}"
|
|
786
787
|
|
|
787
788
|
# Resolve to absolute path
|
|
788
789
|
if [[ ! "$feature_list" = /* ]]; then
|
|
@@ -842,6 +843,9 @@ main() {
|
|
|
842
843
|
echo -e "${BOLD} Dev-Pipeline Runner Started${NC}"
|
|
843
844
|
echo -e "${BOLD}════════════════════════════════════════════════════${NC}"
|
|
844
845
|
log_info "Feature list: $feature_list"
|
|
846
|
+
if [[ -n "$features_filter" ]]; then
|
|
847
|
+
log_info "Features filter: $features_filter"
|
|
848
|
+
fi
|
|
845
849
|
log_info "Max retries per feature: $MAX_RETRIES"
|
|
846
850
|
if [[ $SESSION_TIMEOUT -gt 0 ]]; then
|
|
847
851
|
log_info "Session timeout: ${SESSION_TIMEOUT}s"
|
|
@@ -889,11 +893,17 @@ for f in data.get('stuck_features', []):
|
|
|
889
893
|
|
|
890
894
|
# Find next feature to process
|
|
891
895
|
local next_feature
|
|
896
|
+
local _get_next_args=(
|
|
897
|
+
--feature-list "$feature_list"
|
|
898
|
+
--state-dir "$STATE_DIR"
|
|
899
|
+
--max-retries "$MAX_RETRIES"
|
|
900
|
+
--action get_next
|
|
901
|
+
)
|
|
902
|
+
if [[ -n "$features_filter" ]]; then
|
|
903
|
+
_get_next_args+=(--features "$features_filter")
|
|
904
|
+
fi
|
|
892
905
|
next_feature=$(python3 "$SCRIPTS_DIR/update-feature-status.py" \
|
|
893
|
-
|
|
894
|
-
--state-dir "$STATE_DIR" \
|
|
895
|
-
--max-retries "$MAX_RETRIES" \
|
|
896
|
-
--action get_next 2>/dev/null) || true
|
|
906
|
+
"${_get_next_args[@]}" 2>/dev/null) || true
|
|
897
907
|
|
|
898
908
|
if [[ "$next_feature" == "PIPELINE_COMPLETE" ]]; then
|
|
899
909
|
echo ""
|
|
@@ -1060,13 +1070,18 @@ show_help() {
|
|
|
1060
1070
|
echo "Usage: $0 <command> [options]"
|
|
1061
1071
|
echo ""
|
|
1062
1072
|
echo "Commands:"
|
|
1063
|
-
echo " run [feature-list.json]
|
|
1073
|
+
echo " run [feature-list.json] [--features <filter>] Run features (all or filtered subset)"
|
|
1064
1074
|
echo " run <feature-id> [options] Run a single feature"
|
|
1065
1075
|
echo " status [feature-list.json] Show pipeline status"
|
|
1066
1076
|
echo " test-cli Test AI CLI: show detected CLI, version, and model"
|
|
1067
1077
|
echo " reset Clear all state and start fresh"
|
|
1068
1078
|
echo " help Show this help message"
|
|
1069
1079
|
echo ""
|
|
1080
|
+
echo "Feature Filter (--features):"
|
|
1081
|
+
echo " --features F-001,F-003,F-005 Run only specified features"
|
|
1082
|
+
echo " --features F-001:F-010 Run a range of features (F-001 through F-010)"
|
|
1083
|
+
echo " --features F-001,F-005:F-010 Mixed: individual IDs + ranges"
|
|
1084
|
+
echo ""
|
|
1070
1085
|
echo "Single Feature Options (run <feature-id>):"
|
|
1071
1086
|
echo " --dry-run Generate bootstrap prompt only, don't spawn session"
|
|
1072
1087
|
echo " --resume-phase N Override resume phase (default: auto-detect)"
|
|
@@ -1089,6 +1104,8 @@ show_help() {
|
|
|
1089
1104
|
echo ""
|
|
1090
1105
|
echo "Examples:"
|
|
1091
1106
|
echo " ./run.sh run # Run all features"
|
|
1107
|
+
echo " ./run.sh run --features F-001,F-003,F-005 # Run specific features"
|
|
1108
|
+
echo " ./run.sh run --features F-001:F-010 # Run features F-001 through F-010"
|
|
1092
1109
|
echo " ./run.sh run F-007 --dry-run # Inspect generated prompt"
|
|
1093
1110
|
echo " ./run.sh run F-007 --dry-run --mode lite # Test lite mode"
|
|
1094
1111
|
echo " ./run.sh run F-007 --resume-phase 6 # Skip to implementation"
|
|
@@ -1107,7 +1124,27 @@ case "${1:-run}" in
|
|
|
1107
1124
|
if [[ "${1:-}" =~ ^[Ff]-[0-9]+ ]]; then
|
|
1108
1125
|
run_one "$@"
|
|
1109
1126
|
else
|
|
1110
|
-
|
|
1127
|
+
# Parse positional and --features flag
|
|
1128
|
+
local _run_feature_list="feature-list.json"
|
|
1129
|
+
local _run_features_filter=""
|
|
1130
|
+
while [[ $# -gt 0 ]]; do
|
|
1131
|
+
case "$1" in
|
|
1132
|
+
--features)
|
|
1133
|
+
shift
|
|
1134
|
+
if [[ $# -eq 0 ]]; then
|
|
1135
|
+
log_error "--features requires a value (e.g. --features F-001,F-003 or --features F-001:F-010)"
|
|
1136
|
+
exit 1
|
|
1137
|
+
fi
|
|
1138
|
+
_run_features_filter="$1"
|
|
1139
|
+
shift
|
|
1140
|
+
;;
|
|
1141
|
+
*)
|
|
1142
|
+
_run_feature_list="$1"
|
|
1143
|
+
shift
|
|
1144
|
+
;;
|
|
1145
|
+
esac
|
|
1146
|
+
done
|
|
1147
|
+
main "$_run_feature_list" "$_run_features_filter"
|
|
1111
1148
|
fi
|
|
1112
1149
|
;;
|
|
1113
1150
|
status)
|
|
@@ -16,7 +16,8 @@ Usage:
|
|
|
16
16
|
--feature-list <path> --state-dir <path> \
|
|
17
17
|
--action <get_next|start|update|status|pause|reset|clean|complete> \
|
|
18
18
|
[--feature-id <id>] [--session-status <status>] \
|
|
19
|
-
[--session-id <id>] [--max-retries <n>]
|
|
19
|
+
[--session-id <id>] [--max-retries <n>] \
|
|
20
|
+
[--features <filter>]
|
|
20
21
|
"""
|
|
21
22
|
|
|
22
23
|
import argparse
|
|
@@ -102,9 +103,49 @@ def parse_args():
|
|
|
102
103
|
default=None,
|
|
103
104
|
help="Project root directory. Required for 'clean' action.",
|
|
104
105
|
)
|
|
106
|
+
parser.add_argument(
|
|
107
|
+
"--features",
|
|
108
|
+
default=None,
|
|
109
|
+
help="Feature filter: comma-separated IDs (F-001,F-003) or range (F-001:F-010), or mixed.",
|
|
110
|
+
)
|
|
105
111
|
return parser.parse_args()
|
|
106
112
|
|
|
107
113
|
|
|
114
|
+
def parse_feature_filter(features_str):
|
|
115
|
+
"""Parse --features argument into a set of feature IDs.
|
|
116
|
+
|
|
117
|
+
Supported formats:
|
|
118
|
+
F-001,F-003,F-005 -> {"F-001", "F-003", "F-005"}
|
|
119
|
+
F-001:F-010 -> {"F-001", "F-002", ..., "F-010"}
|
|
120
|
+
F-001,F-005:F-010 -> mixed, union of both
|
|
121
|
+
|
|
122
|
+
Returns None if features_str is None/empty (meaning no filter).
|
|
123
|
+
"""
|
|
124
|
+
if not features_str:
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
result = set()
|
|
128
|
+
for part in features_str.split(","):
|
|
129
|
+
part = part.strip()
|
|
130
|
+
if not part:
|
|
131
|
+
continue
|
|
132
|
+
if ":" in part:
|
|
133
|
+
tokens = part.split(":", 1)
|
|
134
|
+
m_start = re.search(r"\d+", tokens[0])
|
|
135
|
+
m_end = re.search(r"\d+", tokens[1])
|
|
136
|
+
if not m_start or not m_end:
|
|
137
|
+
error_out("Invalid range format: {}".format(part))
|
|
138
|
+
start_num = int(m_start.group())
|
|
139
|
+
end_num = int(m_end.group())
|
|
140
|
+
if start_num > end_num:
|
|
141
|
+
start_num, end_num = end_num, start_num
|
|
142
|
+
for i in range(start_num, end_num + 1):
|
|
143
|
+
result.add("F-{:03d}".format(i))
|
|
144
|
+
else:
|
|
145
|
+
result.add(part.upper())
|
|
146
|
+
return result if result else None
|
|
147
|
+
|
|
148
|
+
|
|
108
149
|
def now_iso():
|
|
109
150
|
"""Return the current UTC time in ISO8601 format."""
|
|
110
151
|
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -285,20 +326,31 @@ def load_session_status(state_dir, feature_id, session_id):
|
|
|
285
326
|
# Action: get_next
|
|
286
327
|
# ---------------------------------------------------------------------------
|
|
287
328
|
|
|
288
|
-
def action_get_next(feature_list_data, state_dir):
|
|
329
|
+
def action_get_next(feature_list_data, state_dir, feature_filter=None):
|
|
289
330
|
"""Find the next feature to process.
|
|
290
331
|
|
|
291
332
|
Priority logic:
|
|
292
333
|
1. Skip terminal statuses (completed, failed, skipped)
|
|
293
|
-
2.
|
|
294
|
-
3.
|
|
295
|
-
4.
|
|
334
|
+
2. If feature_filter is set, skip features not in the filter
|
|
335
|
+
3. Check that all dependencies are completed
|
|
336
|
+
4. Prefer in_progress features over pending ones (interrupted session resume)
|
|
337
|
+
5. Among eligible features, pick lowest priority number (highest priority)
|
|
296
338
|
"""
|
|
297
339
|
features = feature_list_data.get("features", [])
|
|
298
340
|
if not features:
|
|
299
341
|
print("PIPELINE_COMPLETE")
|
|
300
342
|
return
|
|
301
343
|
|
|
344
|
+
# Apply feature filter: only consider features in the whitelist
|
|
345
|
+
if feature_filter is not None:
|
|
346
|
+
features = [
|
|
347
|
+
f for f in features
|
|
348
|
+
if isinstance(f, dict) and f.get("id") in feature_filter
|
|
349
|
+
]
|
|
350
|
+
if not features:
|
|
351
|
+
print("PIPELINE_COMPLETE")
|
|
352
|
+
return
|
|
353
|
+
|
|
302
354
|
# Build a map of feature statuses from state dir
|
|
303
355
|
status_map = {} # feature_id -> status string
|
|
304
356
|
status_data_map = {} # feature_id -> full status data
|
|
@@ -678,7 +730,7 @@ def _estimate_remaining_time(features, state_dir, counts, feature_list_data=None
|
|
|
678
730
|
return remaining_seconds, confidence
|
|
679
731
|
|
|
680
732
|
|
|
681
|
-
def action_status(feature_list_data, state_dir):
|
|
733
|
+
def action_status(feature_list_data, state_dir, feature_filter=None):
|
|
682
734
|
"""Print a formatted overview of all features and their status.
|
|
683
735
|
|
|
684
736
|
Status is read exclusively from feature-list.json (the single source of
|
|
@@ -688,6 +740,13 @@ def action_status(feature_list_data, state_dir):
|
|
|
688
740
|
features = feature_list_data.get("features", [])
|
|
689
741
|
app_name = feature_list_data.get("app_name", "Unknown")
|
|
690
742
|
|
|
743
|
+
# Apply feature filter
|
|
744
|
+
if feature_filter is not None:
|
|
745
|
+
features = [
|
|
746
|
+
f for f in features
|
|
747
|
+
if isinstance(f, dict) and f.get("id") in feature_filter
|
|
748
|
+
]
|
|
749
|
+
|
|
691
750
|
# Gather status info
|
|
692
751
|
counts = {
|
|
693
752
|
"completed": 0,
|
|
@@ -1080,15 +1139,18 @@ def main():
|
|
|
1080
1139
|
if err:
|
|
1081
1140
|
error_out("Cannot load feature list: {}".format(err))
|
|
1082
1141
|
|
|
1142
|
+
# Parse feature filter (used by get_next and status)
|
|
1143
|
+
feature_filter = parse_feature_filter(args.features)
|
|
1144
|
+
|
|
1083
1145
|
# Dispatch action
|
|
1084
1146
|
if args.action == "get_next":
|
|
1085
|
-
action_get_next(feature_list_data, args.state_dir)
|
|
1147
|
+
action_get_next(feature_list_data, args.state_dir, feature_filter)
|
|
1086
1148
|
elif args.action == "start":
|
|
1087
1149
|
action_start(args, args.feature_list, args.state_dir)
|
|
1088
1150
|
elif args.action == "update":
|
|
1089
1151
|
action_update(args, args.feature_list, args.state_dir)
|
|
1090
1152
|
elif args.action == "status":
|
|
1091
|
-
action_status(feature_list_data, args.state_dir)
|
|
1153
|
+
action_status(feature_list_data, args.state_dir, feature_filter)
|
|
1092
1154
|
elif args.action == "reset":
|
|
1093
1155
|
action_reset(args, args.feature_list, args.state_dir)
|
|
1094
1156
|
elif args.action == "clean":
|
|
@@ -75,12 +75,31 @@ Actions:
|
|
|
75
75
|
3. Append features with next sequential `F-NNN` IDs
|
|
76
76
|
4. Preserve style/language/detail consistency with existing plan
|
|
77
77
|
|
|
78
|
+
## Intent Confirmation (Mandatory First Step)
|
|
79
|
+
|
|
80
|
+
After scenario routing, immediately confirm the user's deliverable intent:
|
|
81
|
+
|
|
82
|
+
1. **Ask explicitly**:
|
|
83
|
+
- "本次规划的目标是生成可执行的 `feature-list.json` 吗?还是只想先讨论想法?"
|
|
84
|
+
- (English: "Is the goal to produce a `feature-list.json` for pipeline execution, or just explore ideas?")
|
|
85
|
+
|
|
86
|
+
2. **Route by answer**:
|
|
87
|
+
- **"Produce feature-list.json"** → Continue to Core Workflow. Set session goal = `produce`.
|
|
88
|
+
- **"Just explore ideas"** → Enter **Exploration Mode**:
|
|
89
|
+
- Run Phases 1-5 normally (vision, constraints, feature proposals, refinement, DAG)
|
|
90
|
+
- At Phase 5 completion, re-ask: "Ideas are taking shape. Ready to generate `feature-list.json` now?"
|
|
91
|
+
- If yes → continue to Phase 6
|
|
92
|
+
- If no → summarize discussion, suggest saving notes, end session
|
|
93
|
+
|
|
94
|
+
3. **Session goal tracking**: Track the intent (`produce` or `explore`) throughout the session. If `explore`, always re-prompt before ending.
|
|
95
|
+
|
|
78
96
|
## Core Workflow
|
|
79
97
|
|
|
80
98
|
Execute the selected scenario workflow in conversation mode with mandatory checkpoints:
|
|
81
99
|
|
|
82
100
|
### Interactive Phases
|
|
83
101
|
1. clarify business goal and scope
|
|
102
|
+
1.1 confirm deliverable intent (→ Intent Confirmation)
|
|
84
103
|
2. confirm constraints and tech assumptions
|
|
85
104
|
3. propose feature set with dependencies
|
|
86
105
|
4. refine descriptions and acceptance criteria
|
|
@@ -95,6 +114,7 @@ Checkpoints catch cascading errors early — skipping one means the next phase b
|
|
|
95
114
|
|
|
96
115
|
| Checkpoint | Artifact/State | Criteria | Phase |
|
|
97
116
|
|-----------|----------------|----------|-------|
|
|
117
|
+
| **CP-AP-0** | Intent Confirmed | User confirmed session goal (produce / explore) | 1 |
|
|
98
118
|
| **CP-AP-1** | Vision Summary | Goal/users/differentiators confirmed by user | 1-2 |
|
|
99
119
|
| **CP-AP-2** | Feature Proposals | Feature set with titles+deps identified (pre-validation) | 3-5 |
|
|
100
120
|
| **CP-AP-3** | DAG Validity | No cycles, dependencies resolved (validation dry-run) | 6 |
|
|
@@ -331,6 +351,29 @@ The pipeline reads `feature-list.json` from the project root by default. If the
|
|
|
331
351
|
|
|
332
352
|
Maintainer note: evaluation workflow moved to `assets/evaluation-guide.md`.
|
|
333
353
|
|
|
354
|
+
## Session Exit Gate
|
|
355
|
+
|
|
356
|
+
Prevent accidental session exit without deliverable completion.
|
|
357
|
+
|
|
358
|
+
### Trigger Conditions
|
|
359
|
+
|
|
360
|
+
Activate exit gate when ALL of these are true:
|
|
361
|
+
- Session goal = `produce` (user confirmed they want feature-list.json via CP-AP-0)
|
|
362
|
+
- Current phase < Phase 7 (final validation not yet passed)
|
|
363
|
+
- No valid `feature-list.json` has been written in this session
|
|
364
|
+
|
|
365
|
+
### Gate Behavior
|
|
366
|
+
|
|
367
|
+
When the session appears to be ending (user says "thanks", "that's all", "bye", or conversation goes idle after Phase 3+):
|
|
368
|
+
|
|
369
|
+
1. **Remind**: "You set out to produce `feature-list.json` but we haven't completed it yet."
|
|
370
|
+
2. **Offer 3 options**:
|
|
371
|
+
- **(a) Continue to completion** — resume from current phase
|
|
372
|
+
- **(b) Abandon** — confirm user explicitly wants to exit without output
|
|
373
|
+
3. **If (b) Save draft**: Write the draft file and remind user: "This is a draft, not validated. Run `/app-planner` again to resume and finalize."
|
|
374
|
+
4. **If (c) Abandon**: Accept without further prompting.
|
|
375
|
+
|
|
376
|
+
|
|
334
377
|
## Handoff Message Template
|
|
335
378
|
|
|
336
379
|
After successful validation, report:
|
|
@@ -28,6 +28,7 @@ Use daemon mode (`launch-daemon.sh`) only when:
|
|
|
28
28
|
- "launch pipeline", "start implementing", "run the pipeline", "start auto-development"
|
|
29
29
|
- "implement next steps", "execute feature list", "start building"
|
|
30
30
|
- After app-planner completes: "build it", "start developing from the feature list"
|
|
31
|
+
- "run only F-001 to F-005", "run features F-001,F-003", "only build these features"
|
|
31
32
|
|
|
32
33
|
**Check status** -- User says:
|
|
33
34
|
- "pipeline status", "check pipeline", "how's it going", "progress"
|
|
@@ -117,11 +118,20 @@ Detect user intent from their message, then follow the corresponding workflow:
|
|
|
117
118
|
```bash
|
|
118
119
|
dev-pipeline/run.sh run feature-list.json
|
|
119
120
|
```
|
|
121
|
+
If user wants to run only specific features:
|
|
122
|
+
```bash
|
|
123
|
+
dev-pipeline/run.sh run feature-list.json --features F-001:F-005
|
|
124
|
+
dev-pipeline/run.sh run feature-list.json --features F-001,F-003,F-007
|
|
125
|
+
```
|
|
120
126
|
|
|
121
127
|
**If option 2 (background)**:
|
|
122
128
|
```bash
|
|
123
129
|
dev-pipeline/launch-daemon.sh start feature-list.json
|
|
124
130
|
```
|
|
131
|
+
With feature filter:
|
|
132
|
+
```bash
|
|
133
|
+
dev-pipeline/launch-daemon.sh start feature-list.json --features F-001:F-005
|
|
134
|
+
```
|
|
125
135
|
Note: Pipeline runs fully detached. Survives session closure.
|
|
126
136
|
|
|
127
137
|
**If option 3 (manual)**: Print commands and stop. Do not execute anything.
|
|
@@ -251,7 +261,35 @@ dev-pipeline/launch-daemon.sh start feature-list.json --env "SESSION_TIMEOUT=720
|
|
|
251
261
|
|
|
252
262
|
---
|
|
253
263
|
|
|
254
|
-
#### Intent F:
|
|
264
|
+
#### Intent F: Run Subset of Features
|
|
265
|
+
|
|
266
|
+
When user says "run only F-001 to F-005", "only build features 1 through 5":
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
dev-pipeline/run.sh run feature-list.json --features F-001:F-005
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
When user says "run F-001, F-003, and F-007", "only build these three features":
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
dev-pipeline/run.sh run feature-list.json --features F-001,F-003,F-007
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Mixed format (IDs + ranges):
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
dev-pipeline/run.sh run feature-list.json --features F-001,F-005:F-010
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Background daemon with feature filter:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
dev-pipeline/launch-daemon.sh start feature-list.json --features F-001:F-005
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
#### Intent G: Retry Single Feature Node
|
|
255
293
|
|
|
256
294
|
When user says "retry F-003":
|
|
257
295
|
|
package/package.json
CHANGED
package/src/scaffold.js
CHANGED
|
@@ -655,18 +655,8 @@ export async function installGitignore(projectRoot, options, dryRun) {
|
|
|
655
655
|
}
|
|
656
656
|
|
|
657
657
|
if (await fs.pathExists(targetPath)) {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
// If .prizmkit/ is already ignored, nothing to do
|
|
661
|
-
if (existing.includes('.prizmkit/')) {
|
|
662
|
-
console.log(chalk.yellow(` ⚠ .gitignore 已包含 .prizmkit/ 条目,跳过`));
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// No PrizmKit entries at all — append full .prizmkit/ block
|
|
667
|
-
const prizmkitSection = '\n\n# PrizmKit\n.prizmkit/\n';
|
|
668
|
-
await fs.appendFile(targetPath, prizmkitSection);
|
|
669
|
-
console.log(chalk.green(` ✓ .gitignore (追加 PrizmKit 条目)`));
|
|
658
|
+
console.log(chalk.yellow(` ⚠ .gitignore 已存在,跳过`));
|
|
659
|
+
return;
|
|
670
660
|
} else {
|
|
671
661
|
const content = generateGitignore({ pipeline: options.pipeline });
|
|
672
662
|
await fs.writeFile(targetPath, content);
|