@nlaprell/shipit 1.0.0

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 (160) hide show
  1. package/.cursor/commands/create_intent_from_issue.md +28 -0
  2. package/.cursor/commands/create_pr.md +28 -0
  3. package/.cursor/commands/dashboard.md +39 -0
  4. package/.cursor/commands/deploy.md +152 -0
  5. package/.cursor/commands/drift_check.md +36 -0
  6. package/.cursor/commands/fix.md +39 -0
  7. package/.cursor/commands/generate_release_plan.md +31 -0
  8. package/.cursor/commands/generate_roadmap.md +38 -0
  9. package/.cursor/commands/help.md +37 -0
  10. package/.cursor/commands/init_project.md +26 -0
  11. package/.cursor/commands/kill.md +72 -0
  12. package/.cursor/commands/new_intent.md +68 -0
  13. package/.cursor/commands/pr.md +77 -0
  14. package/.cursor/commands/revert-plan.md +58 -0
  15. package/.cursor/commands/risk.md +64 -0
  16. package/.cursor/commands/rollback.md +43 -0
  17. package/.cursor/commands/scope_project.md +53 -0
  18. package/.cursor/commands/ship.md +345 -0
  19. package/.cursor/commands/status.md +71 -0
  20. package/.cursor/commands/suggest.md +44 -0
  21. package/.cursor/commands/test_shipit.md +197 -0
  22. package/.cursor/commands/verify.md +50 -0
  23. package/.cursor/rules/architect.mdc +84 -0
  24. package/.cursor/rules/assumption-extractor.mdc +95 -0
  25. package/.cursor/rules/docs.mdc +66 -0
  26. package/.cursor/rules/implementer.mdc +112 -0
  27. package/.cursor/rules/pm.mdc +136 -0
  28. package/.cursor/rules/qa.mdc +97 -0
  29. package/.cursor/rules/security.mdc +90 -0
  30. package/.cursor/rules/steward.mdc +99 -0
  31. package/.cursor/rules/test-runner.mdc +196 -0
  32. package/AGENTS.md +121 -0
  33. package/README.md +264 -0
  34. package/_system/architecture/CANON.md +159 -0
  35. package/_system/architecture/invariants.yml +87 -0
  36. package/_system/architecture/project-schema.json +98 -0
  37. package/_system/architecture/workflow-state-layout.md +68 -0
  38. package/_system/artifacts/SYSTEM_STATE.md +43 -0
  39. package/_system/artifacts/confidence-calibration.json +16 -0
  40. package/_system/artifacts/dependencies.md +46 -0
  41. package/_system/artifacts/framework-files-manifest.json +179 -0
  42. package/_system/artifacts/usage.json +1 -0
  43. package/_system/behaviors/DO_RELEASE.md +371 -0
  44. package/_system/behaviors/DO_RELEASE_AI.md +329 -0
  45. package/_system/behaviors/PREPARE_RELEASE.md +373 -0
  46. package/_system/behaviors/PREPARE_RELEASE_AI.md +234 -0
  47. package/_system/behaviors/WORK_ROOT_PLATFORM_ISSUES.md +140 -0
  48. package/_system/behaviors/WORK_TEST_PLAN_ISSUES.md +380 -0
  49. package/_system/do-not-repeat/abandoned-designs.md +18 -0
  50. package/_system/do-not-repeat/bad-patterns.md +19 -0
  51. package/_system/do-not-repeat/failed-experiments.md +18 -0
  52. package/_system/do-not-repeat/rejected-libraries.md +19 -0
  53. package/_system/drift/baselines.md +49 -0
  54. package/_system/drift/metrics.md +33 -0
  55. package/_system/golden-data/.gitkeep +0 -0
  56. package/_system/golden-data/README.md +47 -0
  57. package/_system/reports/mutation/mutation.html +492 -0
  58. package/_system/security/audit-allowlist.json +4 -0
  59. package/bin/create-shipit-app +29 -0
  60. package/bin/shipit +183 -0
  61. package/cli/src/commands/check.js +82 -0
  62. package/cli/src/commands/create.js +195 -0
  63. package/cli/src/commands/init.js +267 -0
  64. package/cli/src/commands/upgrade.js +196 -0
  65. package/cli/src/utils/config.js +27 -0
  66. package/cli/src/utils/file-copy.js +144 -0
  67. package/cli/src/utils/gitignore-merge.js +44 -0
  68. package/cli/src/utils/manifest.js +105 -0
  69. package/cli/src/utils/package-json-merge.js +163 -0
  70. package/cli/src/utils/project-json-merge.js +57 -0
  71. package/cli/src/utils/prompts.js +30 -0
  72. package/cli/src/utils/stack-detection.js +56 -0
  73. package/cli/src/utils/stack-files.js +364 -0
  74. package/cli/src/utils/upgrade-backup.js +159 -0
  75. package/cli/src/utils/version.js +64 -0
  76. package/dashboard-app/README.md +73 -0
  77. package/dashboard-app/eslint.config.js +23 -0
  78. package/dashboard-app/index.html +13 -0
  79. package/dashboard-app/package.json +30 -0
  80. package/dashboard-app/pnpm-lock.yaml +2721 -0
  81. package/dashboard-app/public/dashboard.json +66 -0
  82. package/dashboard-app/public/vite.svg +1 -0
  83. package/dashboard-app/src/App.css +141 -0
  84. package/dashboard-app/src/App.tsx +155 -0
  85. package/dashboard-app/src/assets/react.svg +1 -0
  86. package/dashboard-app/src/index.css +68 -0
  87. package/dashboard-app/src/main.tsx +10 -0
  88. package/dashboard-app/tsconfig.app.json +28 -0
  89. package/dashboard-app/tsconfig.json +4 -0
  90. package/dashboard-app/tsconfig.node.json +26 -0
  91. package/dashboard-app/vite.config.ts +7 -0
  92. package/package.json +116 -0
  93. package/scripts/README.md +70 -0
  94. package/scripts/audit-check.sh +125 -0
  95. package/scripts/calibration-report.sh +198 -0
  96. package/scripts/check-readiness.sh +155 -0
  97. package/scripts/collect-metrics.sh +116 -0
  98. package/scripts/command-manifest.yml +131 -0
  99. package/scripts/create-test-plan-issue.sh +110 -0
  100. package/scripts/dashboard-start.sh +16 -0
  101. package/scripts/deploy.sh +170 -0
  102. package/scripts/drift-check.sh +93 -0
  103. package/scripts/execute-rollback.sh +177 -0
  104. package/scripts/export-dashboard-json.js +208 -0
  105. package/scripts/fix-intents.sh +239 -0
  106. package/scripts/generate-dashboard.sh +136 -0
  107. package/scripts/generate-docs.sh +279 -0
  108. package/scripts/generate-project-context.sh +142 -0
  109. package/scripts/generate-release-plan.sh +443 -0
  110. package/scripts/generate-roadmap.sh +189 -0
  111. package/scripts/generate-system-state.sh +95 -0
  112. package/scripts/gh/create-intent-from-issue.sh +82 -0
  113. package/scripts/gh/create-issue-from-intent.sh +59 -0
  114. package/scripts/gh/create-pr.sh +41 -0
  115. package/scripts/gh/link-issue.sh +44 -0
  116. package/scripts/gh/on-ship-update-issue.sh +42 -0
  117. package/scripts/headless/README.md +8 -0
  118. package/scripts/headless/call-llm.js +109 -0
  119. package/scripts/headless/run-phase.sh +99 -0
  120. package/scripts/help.sh +271 -0
  121. package/scripts/init-project.sh +976 -0
  122. package/scripts/kill-intent.sh +125 -0
  123. package/scripts/lib/common.sh +29 -0
  124. package/scripts/lib/intent.sh +61 -0
  125. package/scripts/lib/progress.sh +57 -0
  126. package/scripts/lib/suggest-next.sh +131 -0
  127. package/scripts/lib/validate-intents.sh +240 -0
  128. package/scripts/lib/verify-outputs.sh +55 -0
  129. package/scripts/lib/workflow_state.sh +201 -0
  130. package/scripts/new-intent.sh +271 -0
  131. package/scripts/publish-npm.sh +28 -0
  132. package/scripts/scope-project.sh +380 -0
  133. package/scripts/setup-worktrees.sh +125 -0
  134. package/scripts/status.sh +278 -0
  135. package/scripts/suggest.sh +173 -0
  136. package/scripts/test-headless.sh +47 -0
  137. package/scripts/test-shipit.sh +52 -0
  138. package/scripts/test-workflow-state.sh +49 -0
  139. package/scripts/usage-report.sh +47 -0
  140. package/scripts/usage.sh +58 -0
  141. package/scripts/validate-cursor.sh +151 -0
  142. package/scripts/validate-project.sh +71 -0
  143. package/scripts/validate-vscode.sh +146 -0
  144. package/scripts/verify.sh +153 -0
  145. package/scripts/workflow-orchestrator.sh +97 -0
  146. package/scripts/workflow-templates/01_analysis.md.tpl +25 -0
  147. package/scripts/workflow-templates/02_plan.md.tpl +30 -0
  148. package/scripts/workflow-templates/03_implementation.md.tpl +25 -0
  149. package/scripts/workflow-templates/04_verification.md.tpl +29 -0
  150. package/scripts/workflow-templates/05_release_notes.md.tpl +16 -0
  151. package/scripts/workflow-templates/05_verification_legacy.md.tpl +6 -0
  152. package/scripts/workflow-templates/active.md.tpl +18 -0
  153. package/scripts/workflow-templates/phases.yml +39 -0
  154. package/stryker.conf.json +8 -0
  155. package/work/intent/templates/api-endpoint.md +124 -0
  156. package/work/intent/templates/bugfix.md +116 -0
  157. package/work/intent/templates/frontend-feature.md +115 -0
  158. package/work/intent/templates/generic.md +122 -0
  159. package/work/intent/templates/infra-change.md +121 -0
  160. package/work/intent/templates/refactor.md +116 -0
@@ -0,0 +1,278 @@
1
+ #!/bin/bash
2
+
3
+ # Show current ShipIt project status
4
+ # Displays active intent(s), workflow phase(s), intent counts. Supports flat and per-intent layout.
5
+
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ # shellcheck source=lib/workflow_state.sh
10
+ [ -f "$SCRIPT_DIR/lib/workflow_state.sh" ] && source "$SCRIPT_DIR/lib/workflow_state.sh"
11
+
12
+ # Colors
13
+ GREEN='\033[0;32m'
14
+ YELLOW='\033[1;33m'
15
+ BLUE='\033[0;34m'
16
+ CYAN='\033[0;36m'
17
+ RED='\033[0;31m'
18
+ NC='\033[0m'
19
+
20
+ INTENT_DIR="work/intent"
21
+ WS_BASE="work/workflow-state"
22
+
23
+ echo -e "${BLUE}════════════════════════════════════════${NC}"
24
+ echo -e "${BLUE}ShipIt Project Status${NC}"
25
+ echo -e "${BLUE}════════════════════════════════════════${NC}"
26
+ echo ""
27
+
28
+ # Check if project.json exists
29
+ if [ ! -f "project.json" ]; then
30
+ echo -e "${RED}⚠ Not a ShipIt project (project.json missing)${NC}"
31
+ echo "Run /init-project to initialize a project."
32
+ exit 1
33
+ fi
34
+
35
+ PROJECT_NAME=$(jq -r '.name' project.json 2>/dev/null || echo "unknown")
36
+ echo -e "${CYAN}Project:${NC} $PROJECT_NAME"
37
+ echo ""
38
+
39
+ # Active intent(s) — from flat active.md or per-intent dirs
40
+ ACTIVE_IDS=($(list_active_intent_ids))
41
+ if [ "${#ACTIVE_IDS[@]}" -gt 0 ]; then
42
+ echo -e "${CYAN}Active Intent(s):${NC}"
43
+ for aid in "${ACTIVE_IDS[@]}"; do
44
+ dir="$(get_workflow_state_dir "$aid")"
45
+ phase=""
46
+ if [ -n "$dir" ] && [ -f "$dir/02_plan.md" ]; then
47
+ phase="Planning"
48
+ elif [ -n "$dir" ] && [ -f "$dir/03_implementation.md" ]; then
49
+ phase="Implementation"
50
+ elif [ -n "$dir" ] && [ -f "$dir/04_verification.md" ]; then
51
+ phase="Verification"
52
+ elif [ -n "$dir" ] && [ -f "$dir/01_analysis.md" ]; then
53
+ phase="Analysis"
54
+ else
55
+ phase="(see workflow state)"
56
+ fi
57
+ echo " $aid — $phase"
58
+ done
59
+ echo ""
60
+ fi
61
+ if [ -f "$WS_BASE/active.md" ]; then
62
+ ACTIVE_STATUS=$(grep -E "^\\*\\*Status:\\*\\*" "$WS_BASE/active.md" 2>/dev/null | sed 's/.*\*\*Status:\*\* //' | tr -d ' ' || echo "")
63
+ if [ "$ACTIVE_STATUS" = "idle" ] && [ "${#ACTIVE_IDS[@]}" -eq 0 ]; then
64
+ echo -e "${CYAN}Active Intent:${NC} none (idle)"
65
+ echo ""
66
+ fi
67
+ fi
68
+
69
+ # Intent counts (initialize to 0)
70
+ PLANNED=0
71
+ ACTIVE=0
72
+ BLOCKED=0
73
+ VALIDATING=0
74
+ SHIPPED=0
75
+ KILLED=0
76
+ TOTAL=0
77
+
78
+ if [ -d "$INTENT_DIR" ]; then
79
+ intent_files=()
80
+ while IFS= read -r file; do
81
+ intent_files+=("$file")
82
+ done < <(find "$INTENT_DIR" -type f -name "*.md" ! -name "_TEMPLATE.md" 2>/dev/null)
83
+
84
+ for file in "${intent_files[@]}"; do
85
+ [ -f "$file" ] || continue
86
+
87
+ STATUS=$(awk '
88
+ $0 ~ /^## Status/ {found=1; next}
89
+ found && $0 ~ /^## / {exit}
90
+ found && $0 ~ /[^[:space:]]/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0); print tolower($0); exit}
91
+ ' "$file" 2>/dev/null || echo "planned")
92
+
93
+ case "$STATUS" in
94
+ planned) PLANNED=$((PLANNED + 1)) ;;
95
+ active) ACTIVE=$((ACTIVE + 1)) ;;
96
+ blocked) BLOCKED=$((BLOCKED + 1)) ;;
97
+ validating) VALIDATING=$((VALIDATING + 1)) ;;
98
+ shipped) SHIPPED=$((SHIPPED + 1)) ;;
99
+ killed) KILLED=$((KILLED + 1)) ;;
100
+ esac
101
+ done
102
+
103
+ TOTAL=$((PLANNED + ACTIVE + BLOCKED + VALIDATING + SHIPPED + KILLED))
104
+ fi
105
+
106
+ if [ "$TOTAL" -gt 0 ]; then
107
+ echo -e "${CYAN}Intent Status:${NC}"
108
+ echo " Planned: $PLANNED"
109
+ echo " Active: $ACTIVE"
110
+ echo " Blocked: $BLOCKED"
111
+ echo " Validating: $VALIDATING"
112
+ echo " Shipped: $SHIPPED"
113
+ echo " Killed: $KILLED"
114
+ echo " Total: $TOTAL"
115
+ echo ""
116
+ fi
117
+
118
+ # Workflow state files (flat or per-intent)
119
+ if [ -d "$WS_BASE" ]; then
120
+ echo -e "${CYAN}Workflow State:${NC}"
121
+ if [ "${#ACTIVE_IDS[@]}" -eq 0 ]; then
122
+ WORKFLOW_DIR="$WS_BASE"
123
+ for phase in 01_analysis 02_plan 03_implementation 04_verification 05_release_notes 06_shipped; do
124
+ if [ -f "$WORKFLOW_DIR/${phase}.md" ]; then
125
+ echo -e " ${GREEN}✓${NC} $phase"
126
+ else
127
+ echo -e " ${YELLOW}○${NC} $phase (not started)"
128
+ fi
129
+ done
130
+ else
131
+ for aid in "${ACTIVE_IDS[@]}"; do
132
+ dir="$(get_workflow_state_dir "$aid")"
133
+ [ -z "$dir" ] && continue
134
+ echo " [$aid]"
135
+ for phase in 01_analysis 02_plan 03_implementation 04_verification 05_release_notes 06_shipped; do
136
+ if [ -f "$dir/${phase}.md" ]; then
137
+ echo -e " ${GREEN}✓${NC} $phase"
138
+ else
139
+ echo -e " ${YELLOW}○${NC} $phase (not started)"
140
+ fi
141
+ done
142
+ done
143
+ fi
144
+ echo ""
145
+ fi
146
+
147
+ # Workflow state sanity check (top-level required files)
148
+ required_top_files=( "active.md" "blocked.md" "validating.md" "shipped.md" "disagreements.md" )
149
+ required_phase_files=( "01_analysis.md" "02_plan.md" "03_implementation.md" "04_verification.md" "05_release_notes.md" )
150
+ if [ -d "$WS_BASE" ]; then
151
+ missing_top=()
152
+ for file in "${required_top_files[@]}"; do
153
+ [ ! -f "$WS_BASE/$file" ] && missing_top+=("$file")
154
+ done
155
+ for aid in "${ACTIVE_IDS[@]}"; do
156
+ dir="$(get_workflow_state_dir "$aid")"
157
+ [ -z "$dir" ] && continue
158
+ for file in "${required_phase_files[@]}"; do
159
+ [ ! -f "$dir/$file" ] && missing_top+=("$aid/$file")
160
+ done
161
+ done
162
+ if [ "${#ACTIVE_IDS[@]}" -eq 0 ]; then
163
+ for file in "${required_phase_files[@]}"; do
164
+ [ ! -f "$WS_BASE/$file" ] && missing_top+=("$file")
165
+ done
166
+ fi
167
+ if [ "${#missing_top[@]}" -gt 0 ]; then
168
+ echo -e "${YELLOW}⚠ Workflow State Check:${NC}"
169
+ echo " Missing: ${missing_top[*]}"
170
+ echo " 💡 Run /ship <id> or re-run /init-project to seed files"
171
+ echo ""
172
+ fi
173
+ fi
174
+
175
+ # Recent intents
176
+ if [ -d "$INTENT_DIR" ] && [ "${TOTAL:-0}" -gt 0 ]; then
177
+ echo -e "${CYAN}Recent Intents:${NC}"
178
+ if [ "${#intent_files[@]}" -gt 0 ]; then
179
+ ls -t "${intent_files[@]}" 2>/dev/null | head -5 | while read -r file; do
180
+ ID=$(basename "$file" .md)
181
+ TITLE=$(grep "^# " "$file" 2>/dev/null | head -1 | sed 's/^# [^:]*: //' || echo "$ID")
182
+ STATUS=$(awk '
183
+ $0 ~ /^## Status/ {found=1; next}
184
+ found && $0 ~ /^## / {exit}
185
+ found && $0 ~ /[^[:space:]]/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0); print tolower($0); exit}
186
+ ' "$file" 2>/dev/null || echo "unknown")
187
+ echo " $ID: $TITLE ($STATUS)"
188
+ done
189
+ fi
190
+ echo ""
191
+ fi
192
+
193
+ # Pending approvals (check flat or each active's plan)
194
+ check_pending_approval() {
195
+ local f="$1"
196
+ [ ! -f "$f" ] && return 0
197
+ if grep -q "\[ \].*approval\|\[ \].*Approval" "$f" 2>/dev/null && ! grep -q "\[x\].*approval\|\[x\].*Approval\|Approved\|APPROVE" "$f" 2>/dev/null; then
198
+ echo -e "${YELLOW}⚠ Pending Approval:${NC}"
199
+ echo " Plan approval required in $f"
200
+ echo ""
201
+ return 1
202
+ fi
203
+ return 0
204
+ }
205
+ if [ "${#ACTIVE_IDS[@]}" -eq 0 ] && [ -f "$WS_BASE/02_plan.md" ]; then
206
+ check_pending_approval "$WS_BASE/02_plan.md" || true
207
+ else
208
+ for aid in "${ACTIVE_IDS[@]}"; do
209
+ dir="$(get_workflow_state_dir "$aid")"
210
+ [ -n "$dir" ] && [ -f "$dir/02_plan.md" ] && check_pending_approval "$dir/02_plan.md" || true
211
+ done
212
+ fi
213
+
214
+ # Test results summary
215
+ if command -v pnpm >/dev/null 2>&1 && [ -f "package.json" ]; then
216
+ echo -e "${CYAN}Test Status:${NC}"
217
+ if [ -f "package.json" ] && grep -q '"test"' package.json; then
218
+ # Try to get test results (non-blocking, timeout to avoid hanging)
219
+ test_output=$(timeout 10 pnpm test 2>&1 | tail -5 || echo "")
220
+ if [ -n "$test_output" ] && echo "$test_output" | grep -q "PASS\|pass\|✓\|Test Files.*passed"; then
221
+ test_count=$(echo "$test_output" | grep -oE "[0-9]+ passing\|[0-9]+ passed\|Test Files.*[0-9]+" | head -1 || echo "")
222
+ echo -e " ${GREEN}✓${NC} Tests passing${test_count:+ ($test_count)}"
223
+ elif [ -n "$test_output" ] && echo "$test_output" | grep -q "FAIL\|fail\|✗\|failed"; then
224
+ echo -e " ${RED}✗${NC} Tests failing (run 'pnpm test' for details)"
225
+ else
226
+ echo " ${YELLOW}○${NC} Run 'pnpm test' to check test status"
227
+ fi
228
+ else
229
+ echo " ${YELLOW}○${NC} No test script configured"
230
+ fi
231
+ echo ""
232
+ fi
233
+
234
+ # Token/usage summary (if usage.json exists)
235
+ USAGE_FILE="_system/artifacts/usage.json"
236
+ if [ -f "$USAGE_FILE" ] && command -v jq >/dev/null 2>&1; then
237
+ ENTRY_COUNT=$(jq '.entries | length' "$USAGE_FILE" 2>/dev/null || echo "0")
238
+ if [ "${ENTRY_COUNT:-0}" -gt 0 ]; then
239
+ echo -e "${CYAN}Usage (tokens/cost):${NC}"
240
+ LAST=$(jq -r '.entries[-1] | "\(.intent_id) \(.phase): \(.tokens_in + .tokens_out) tokens" + (if .estimated_cost_usd != null then " (~$" + (.estimated_cost_usd|tostring) + ")" else " (estimate)" end)' "$USAGE_FILE" 2>/dev/null || true)
241
+ if [ -n "$LAST" ]; then
242
+ echo " Last: $LAST"
243
+ fi
244
+ echo " Run \`pnpm usage-report\` for full table"
245
+ echo ""
246
+ fi
247
+ fi
248
+
249
+ # Recent changes (git)
250
+ if command -v git >/dev/null 2>&1 && git rev-parse --git-dir >/dev/null 2>&1; then
251
+ echo -e "${CYAN}Recent Changes:${NC}"
252
+ recent_commits=$(git log --oneline -5 2>/dev/null | head -5 || true)
253
+ if [ -n "$recent_commits" ]; then
254
+ echo "$recent_commits" | while IFS= read -r line; do
255
+ [ -n "$line" ] && echo " $line"
256
+ done
257
+ else
258
+ echo " (No commits yet)"
259
+ fi
260
+ echo ""
261
+ fi
262
+
263
+ # Suggestions
264
+ echo -e "${CYAN}Next Steps:${NC}"
265
+ if [ "$TOTAL" -eq 0 ]; then
266
+ echo " 💡 Run /scope-project to break down your project"
267
+ echo " 💡 Run /new_intent to create your first intent"
268
+ elif [ "$ACTIVE" -eq 0 ] && [ "$PLANNED" -gt 0 ]; then
269
+ echo " 💡 Run /ship <id> to start working on a planned intent"
270
+ echo " 💡 Run /new_intent to create another intent"
271
+ elif [ "$ACTIVE" -gt 0 ]; then
272
+ echo " 💡 Continue workflow for active intent"
273
+ echo " 💡 Run /status to see current phase"
274
+ else
275
+ echo " 💡 Run /generate-release-plan to update release plan"
276
+ echo " 💡 Run /generate-roadmap to update roadmap"
277
+ fi
278
+ echo ""
@@ -0,0 +1,173 @@
1
+ #!/bin/bash
2
+
3
+ # Suggest next actions based on current project state
4
+ # Supports flat and per-intent workflow state; considers all active intents.
5
+
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ # shellcheck source=lib/workflow_state.sh
10
+ [ -f "$SCRIPT_DIR/lib/workflow_state.sh" ] && source "$SCRIPT_DIR/lib/workflow_state.sh"
11
+
12
+ # Colors
13
+ GREEN='\033[0;32m'
14
+ YELLOW='\033[1;33m'
15
+ BLUE='\033[0;34m'
16
+ CYAN='\033[0;36m'
17
+ RED='\033[0;31m'
18
+ NC='\033[0m'
19
+
20
+ INTENT_DIR="work/intent"
21
+ WS_BASE="work/workflow-state"
22
+
23
+ echo -e "${BLUE}ShipIt Suggestions${NC}"
24
+ echo ""
25
+
26
+ # Check if project exists
27
+ if [ ! -f "project.json" ]; then
28
+ echo -e "${YELLOW}→ Initialize a project:${NC} /init-project \"My Project\""
29
+ exit 0
30
+ fi
31
+
32
+ # Count intents by status (initialize to 0)
33
+ PLANNED=0
34
+ ACTIVE=0
35
+ SHIPPED=0
36
+ KILLED=0
37
+ TOTAL=0
38
+
39
+ if [ -d "$INTENT_DIR" ]; then
40
+ intent_files=()
41
+ while IFS= read -r file; do
42
+ intent_files+=("$file")
43
+ done < <(find "$INTENT_DIR" -type f -name "*.md" ! -name "_TEMPLATE.md" 2>/dev/null)
44
+
45
+ for file in "${intent_files[@]}"; do
46
+ [ -f "$file" ] || continue
47
+
48
+ STATUS=$(awk '
49
+ $0 ~ /^## Status/ {found=1; next}
50
+ found && $0 ~ /^## / {exit}
51
+ found && $0 ~ /[^[:space:]]/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0); print tolower($0); exit}
52
+ ' "$file" 2>/dev/null || echo "planned")
53
+
54
+ case "$STATUS" in
55
+ planned) PLANNED=$((PLANNED + 1)) ;;
56
+ active) ACTIVE=$((ACTIVE + 1)) ;;
57
+ shipped) SHIPPED=$((SHIPPED + 1)) ;;
58
+ killed) KILLED=$((KILLED + 1)) ;;
59
+ esac
60
+ done
61
+ fi
62
+
63
+ TOTAL=$((PLANNED + ACTIVE + SHIPPED + KILLED))
64
+
65
+ # Active intent(s) — use first for phase-based suggestions; show all if multiple
66
+ ACTIVE_IDS=($(list_active_intent_ids))
67
+ ACTIVE_ID=""
68
+ ACTIVE_PHASE=""
69
+ WORKFLOW_DIR=""
70
+ if [ "${#ACTIVE_IDS[@]}" -gt 0 ]; then
71
+ ACTIVE_ID="${ACTIVE_IDS[0]}"
72
+ WORKFLOW_DIR="$(get_workflow_state_dir "$ACTIVE_ID")"
73
+ [ -z "$WORKFLOW_DIR" ] && WORKFLOW_DIR="$WS_BASE"
74
+ if [ -n "$WORKFLOW_DIR" ]; then
75
+ if [ -f "$WORKFLOW_DIR/02_plan.md" ]; then ACTIVE_PHASE="Plan"; fi
76
+ if [ -f "$WORKFLOW_DIR/03_implementation.md" ]; then ACTIVE_PHASE="Implementation"; fi
77
+ if [ -f "$WORKFLOW_DIR/04_verification.md" ]; then ACTIVE_PHASE="Verification"; fi
78
+ if [ -f "$WORKFLOW_DIR/01_analysis.md" ] && [ -z "$ACTIVE_PHASE" ]; then ACTIVE_PHASE="Analysis"; fi
79
+ fi
80
+ fi
81
+
82
+ # Generate suggestions
83
+ SUGGESTIONS=()
84
+
85
+ # No intents at all
86
+ if [ "$TOTAL" -eq 0 ]; then
87
+ SUGGESTIONS+=("${CYAN}1. Scope your project:${NC} /scope-project \"project description\"")
88
+ SUGGESTIONS+=("${CYAN}2. Create your first intent:${NC} /new_intent")
89
+ fi
90
+
91
+ # Has planned intents but no active
92
+ if [ "$PLANNED" -gt 0 ] && [ -z "$ACTIVE_ID" ]; then
93
+ # Find first planned intent (actually check status)
94
+ FIRST_PLANNED=""
95
+ for file in "${intent_files[@]}"; do
96
+ [ -f "$file" ] || continue
97
+ STATUS=$(awk '
98
+ $0 ~ /^## Status/ {found=1; next}
99
+ found && $0 ~ /^## / {exit}
100
+ found && $0 ~ /[^[:space:]]/ {gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0); print tolower($0); exit}
101
+ ' "$file" 2>/dev/null || echo "")
102
+ if [ "$STATUS" = "planned" ]; then
103
+ FIRST_PLANNED=$(basename "$file" .md)
104
+ break
105
+ fi
106
+ done
107
+ if [ -n "$FIRST_PLANNED" ]; then
108
+ SUGGESTIONS+=("${CYAN}1. Start working on an intent:${NC} /ship $FIRST_PLANNED")
109
+ fi
110
+ fi
111
+
112
+ # Has active intent(s)
113
+ if [ -n "$ACTIVE_ID" ]; then
114
+ if [ "${#ACTIVE_IDS[@]}" -gt 1 ]; then
115
+ SUGGESTIONS+=("${CYAN}Multiple active intents:${NC} ${ACTIVE_IDS[*]} — use /ship <id> or /verify <id>")
116
+ fi
117
+ case "$ACTIVE_PHASE" in
118
+ *analysis*|*Analysis*)
119
+ SUGGESTIONS+=("${CYAN}1. Continue analysis:${NC} Fill in $WORKFLOW_DIR/01_analysis.md")
120
+ SUGGESTIONS+=("${CYAN}2. Move to planning:${NC} Run /ship $ACTIVE_ID (will proceed to Phase 2)")
121
+ ;;
122
+ *plan*|*Plan*)
123
+ if [ -n "$WORKFLOW_DIR" ] && [ -f "$WORKFLOW_DIR/02_plan.md" ] && grep -q "\[ \].*approval\|\[ \].*Approval" "$WORKFLOW_DIR/02_plan.md" 2>/dev/null && ! grep -q "\[x\].*approval\|\[x\].*Approval\|Approved\|APPROVE" "$WORKFLOW_DIR/02_plan.md" 2>/dev/null; then
124
+ SUGGESTIONS+=("${YELLOW}1. ⚠ Approval required:${NC} Review and approve $WORKFLOW_DIR/02_plan.md")
125
+ else
126
+ SUGGESTIONS+=("${CYAN}1. Continue to implementation:${NC} Run /ship $ACTIVE_ID")
127
+ fi
128
+ ;;
129
+ *implementation*|*Implementation*)
130
+ SUGGESTIONS+=("${CYAN}1. Continue implementation:${NC} Fill in $WORKFLOW_DIR/03_implementation.md")
131
+ SUGGESTIONS+=("${CYAN}2. Move to verification:${NC} Run /verify $ACTIVE_ID")
132
+ ;;
133
+ *verification*|*Verification*)
134
+ SUGGESTIONS+=("${CYAN}1. Complete verification:${NC} Fill in $WORKFLOW_DIR/04_verification.md")
135
+ SUGGESTIONS+=("${CYAN}2. Move to release:${NC} Run /ship $ACTIVE_ID")
136
+ ;;
137
+ *)
138
+ SUGGESTIONS+=("${CYAN}1. Continue workflow:${NC} Run /ship $ACTIVE_ID")
139
+ ;;
140
+ esac
141
+ fi
142
+
143
+ # Release plan needs update
144
+ if [ "$PLANNED" -gt 0 ] || [ "$ACTIVE" -gt 0 ]; then
145
+ if [ ! -f "work/release/plan.md" ] || [ -n "$(find "$INTENT_DIR" -type f -name "*.md" ! -name "_TEMPLATE.md" -newer "work/release/plan.md" -print -quit 2>/dev/null)" ]; then
146
+ SUGGESTIONS+=("${CYAN}Update release plan:${NC} /generate-release-plan")
147
+ fi
148
+ fi
149
+
150
+ # Roadmap needs update
151
+ if [ "$PLANNED" -gt 0 ] || [ "$ACTIVE" -gt 0 ]; then
152
+ if [ ! -f "work/roadmap/now.md" ] || [ -n "$(find "$INTENT_DIR" -type f -name "*.md" ! -name "_TEMPLATE.md" -newer "work/roadmap/now.md" -print -quit 2>/dev/null)" ]; then
153
+ SUGGESTIONS+=("${CYAN}Update roadmap:${NC} /generate-roadmap")
154
+ fi
155
+ fi
156
+
157
+ # Show suggestions
158
+ if [ ${#SUGGESTIONS[@]} -eq 0 ]; then
159
+ echo -e "${GREEN}✓ Everything looks good!${NC}"
160
+ echo ""
161
+ echo "You might want to:"
162
+ echo " → Create a new intent: /new_intent"
163
+ echo " → Check project status: /status"
164
+ echo " → Review release plan: /generate-release-plan"
165
+ else
166
+ for suggestion in "${SUGGESTIONS[@]}"; do
167
+ echo -e "$suggestion"
168
+ done
169
+ fi
170
+
171
+ echo ""
172
+ echo -e "${CYAN}More help:${NC} /help [command]"
173
+ echo -e "${CYAN}Project status:${NC} /status"
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bash
2
+ # Smoke test for headless mode (issue #65). Ensures run-phase.sh runs and fails cleanly without API keys.
3
+ # With OPENAI_API_KEY or ANTHROPIC_API_KEY set, you can run phase 1 and assert output file exists.
4
+
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
9
+ cd "$REPO_ROOT"
10
+
11
+ RED='\033[0;31m'
12
+ GREEN='\033[0;32m'
13
+ NC='\033[0m'
14
+ fail() { echo -e "${RED}FAIL: $*${NC}" >&2; exit 1; }
15
+ ok() { echo -e "${GREEN}OK: $*${NC}"; }
16
+
17
+ # Need an intent that exists and has workflow state (or we'll get "run workflow-orchestrator first")
18
+ INTENT_ID="${1:-F-DUMMY}"
19
+ if [ ! -f "work/intent/features/$INTENT_ID.md" ] && [ ! -f "work/intent/$INTENT_ID.md" ]; then
20
+ # Try to find any intent
21
+ INTENT_ID=""
22
+ for f in work/intent/features/*.md work/intent/*.md; do
23
+ [ -f "$f" ] && INTENT_ID="$(basename "$f" .md)" && break
24
+ done 2>/dev/null || true
25
+ fi
26
+ [ -n "$INTENT_ID" ] || { echo "Skip: no intent file found (create one or pass intent-id)"; exit 0; }
27
+
28
+ # 1) Without API keys, run-phase.sh must exit 1 with clear message
29
+ unset OPENAI_API_KEY ANTHROPIC_API_KEY
30
+ out="$(bash scripts/headless/run-phase.sh "$INTENT_ID" 1 2>&1)" || true
31
+ if [ "${#out}" -eq 0 ]; then
32
+ fail "run-phase.sh should print message when API keys unset"
33
+ fi
34
+ if ! echo "$out" | grep -q "OPENAI_API_KEY\|ANTHROPIC_API_KEY"; then
35
+ fail "run-phase.sh should mention API key env vars when unset"
36
+ fi
37
+ ok "run-phase.sh exits with clear message when API keys unset"
38
+
39
+ # 2) Invalid phase number must exit 1
40
+ out2="$(bash scripts/headless/run-phase.sh "$INTENT_ID" 99 2>&1)" || true
41
+ if ! echo "$out2" | grep -q "Phase must be 1-5\|error\|Error"; then
42
+ fail "run-phase.sh should reject invalid phase number"
43
+ fi
44
+ ok "run-phase.sh rejects invalid phase"
45
+
46
+ echo ""
47
+ echo -e "${GREEN}Headless smoke test passed.${NC}"
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env bash
2
+ # Automated ShipIt framework E2E test: create test project and run basic assertions.
3
+ # Headless (no editor). For full workflow use .cursor/commands/test_shipit.md.
4
+ # Usage: ./scripts/test-shipit.sh [--clean]
5
+
6
+ set -euo pipefail
7
+
8
+ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
9
+ cd "$ROOT_DIR"
10
+
11
+ # Optional: remove existing test project first
12
+ if [[ "${1:-}" == "--clean" ]]; then
13
+ rm -rf tests/test-project
14
+ fi
15
+
16
+ # Source common for error_exit
17
+ # shellcheck source=scripts/lib/common.sh
18
+ source "$ROOT_DIR/scripts/lib/common.sh"
19
+
20
+ echo "ShipIt framework E2E test (headless)"
21
+ echo " Root: $ROOT_DIR"
22
+ echo ""
23
+
24
+ # 1) Create test project (non-interactive; reads tests/fixtures.json)
25
+ echo "Step 1: Create test project..."
26
+ ./scripts/init-project.sh shipit-test || error_exit "init-project.sh failed" 1
27
+
28
+ # 2) Assert key files and dirs exist
29
+ TP="$ROOT_DIR/tests/test-project"
30
+ assert() {
31
+ local path="$1"
32
+ local desc="${2:-$path}"
33
+ if [[ -e "$path" ]]; then
34
+ echo " ok: $desc"
35
+ else
36
+ error_exit "Missing: $desc ($path)" 1
37
+ fi
38
+ }
39
+
40
+ echo "Step 2: Assert project structure..."
41
+ assert "$TP/project.json" "project.json"
42
+ assert "$TP/AGENTS.md" "AGENTS.md"
43
+ assert "$TP/scripts" "scripts/"
44
+ assert "$TP/scripts/verify.sh" "scripts/verify.sh"
45
+ assert "$TP/_system/architecture/CANON.md" "_system/architecture/CANON.md"
46
+ assert "$TP/work/intent" "work/intent/"
47
+ # .override/ is created by CLI init; init-project.sh may or may not create it
48
+ [[ -d "$TP/.override" ]] && echo " ok: .override/" || true
49
+
50
+ echo ""
51
+ echo "PASS: ShipIt framework E2E (test project creation and structure)"
52
+ exit 0
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # Smoke test for workflow-state flat vs per-intent layout (issue #60).
3
+ # Run from repo root: bash scripts/test-workflow-state.sh
4
+ # Requires: project.json, work/intent (with at least one intent).
5
+
6
+ set -euo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
10
+ cd "$REPO_ROOT"
11
+
12
+ RED='\033[0;31m'
13
+ GREEN='\033[0;32m'
14
+ NC='\033[0m'
15
+
16
+ fail() { echo -e "${RED}FAIL: $*${NC}" >&2; exit 1; }
17
+ ok() { echo -e "${GREEN}OK: $*${NC}"; }
18
+
19
+ # Source the lib (use ORCHESTRATOR_DIR pattern so SCRIPT_DIR in lib is lib dir)
20
+ source "$SCRIPT_DIR/lib/workflow_state.sh"
21
+
22
+ # 1) list_active_intent_ids: should not crash; may be empty or list ids
23
+ ids=($(list_active_intent_ids))
24
+ ok "list_active_intent_ids returned ${#ids[@]} active(s)"
25
+
26
+ # 2) get_workflow_state_dir with no arg: when flat in use, returns WS_BASE
27
+ dir="$(get_workflow_state_dir)"
28
+ if flat_in_use; then
29
+ [ "$dir" = "$WS_BASE" ] || fail "get_workflow_state_dir (no arg) should be WS_BASE when flat in use, got: $dir"
30
+ ok "get_workflow_state_dir () returns flat when flat in use"
31
+ fi
32
+
33
+ # 3) get_workflow_state_dir with intent id: returns path (flat or per-intent)
34
+ if [ ${#ids[@]} -gt 0 ]; then
35
+ first="${ids[0]}"
36
+ dir="$(get_workflow_state_dir "$first")"
37
+ [ -n "$dir" ] || fail "get_workflow_state_dir $first returned empty"
38
+ [[ "$dir" == *"workflow-state"* ]] || fail "get_workflow_state_dir $first should contain workflow-state, got: $dir"
39
+ ok "get_workflow_state_dir $first = $dir"
40
+ fi
41
+
42
+ # 4) ensure_workflow_state_dir with new id: creates per-intent when flat already has one active
43
+ # Skip if we don't have two intents - just ensure ensure_workflow_state_dir runs
44
+ d="$(ensure_workflow_state_dir "F-DUMMY")"
45
+ [ -n "$d" ] || fail "ensure_workflow_state_dir F-DUMMY returned empty"
46
+ ok "ensure_workflow_state_dir F-DUMMY = $d"
47
+
48
+ echo ""
49
+ echo -e "${GREEN}Workflow state layout smoke test passed.${NC}"
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+
3
+ # Print token/cost usage report from _system/artifacts/usage.json.
4
+ # Usage: usage-report.sh [--last N]
5
+ # Default: show all entries. --last N: show last N entries only.
6
+
7
+ set -euo pipefail
8
+
9
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11
+ CD="${CD:-$REPO_ROOT}"
12
+ cd "$CD"
13
+
14
+ # shellcheck source=scripts/lib/common.sh
15
+ . "$SCRIPT_DIR/lib/common.sh"
16
+
17
+ require_cmd jq
18
+
19
+ USAGE_FILE="_system/artifacts/usage.json"
20
+ LAST_N=""
21
+
22
+ if [ "${1:-}" = "--last" ] && [ -n "${2:-}" ]; then
23
+ LAST_N="$2"
24
+ fi
25
+
26
+ if [ ! -f "$USAGE_FILE" ]; then
27
+ echo "No usage data (missing $USAGE_FILE). Record with: pnpm usage-record <intent_id> <phase> <tokens_in> <tokens_out>" >&2
28
+ exit 0
29
+ fi
30
+
31
+ ENTRIES=$(jq -r '.entries // []' "$USAGE_FILE")
32
+ if [ "$ENTRIES" = "[]" ] || [ -z "$ENTRIES" ]; then
33
+ echo "No usage entries yet."
34
+ exit 0
35
+ fi
36
+
37
+ if [ -n "$LAST_N" ]; then
38
+ DATA=$(jq --argjson n "$LAST_N" '.entries | if length > $n then .[length-$n:] else . end' "$USAGE_FILE")
39
+ else
40
+ DATA=$(jq '.entries' "$USAGE_FILE")
41
+ fi
42
+
43
+ # Table: intent_id, phase, tokens_in, tokens_out, total, estimated_cost_usd, date, source
44
+ printf "%-12s %-14s %10s %10s %10s %12s %-25s %s\n" "intent_id" "phase" "tokens_in" "tokens_out" "total" "cost_usd" "timestamp_iso" "source"
45
+ echo "$DATA" | jq -r '.[] | [.intent_id, .phase, (.tokens_in|tostring), (.tokens_out|tostring), ((.tokens_in + .tokens_out)|tostring), (if .estimated_cost_usd == null then "-" else (.estimated_cost_usd | tostring) end), .timestamp_iso, .source] | @tsv' 2>/dev/null | while IFS=$'\t' read -r intent_id phase tokens_in tokens_out total cost ts source; do
46
+ printf "%-12s %-14s %10s %10s %10s %12s %-25s %s\n" "$intent_id" "$phase" "$tokens_in" "$tokens_out" "$total" "$cost" "$ts" "$source"
47
+ done