@jenga-ai/agent 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 (177) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +340 -0
  3. package/agents/ai_engineer.md +113 -0
  4. package/agents/developer.md +236 -0
  5. package/agents/scrum-master.md +349 -0
  6. package/agents/scrutiny-agent.md +137 -0
  7. package/agents/solution-assessor.md +185 -0
  8. package/agents/tester.md +339 -0
  9. package/bin/jenga.js +70 -0
  10. package/hooks/copilot_session_end.sh +29 -0
  11. package/hooks/on_session_end.sh +238 -0
  12. package/hooks/prompt_router.sh +11 -0
  13. package/hooks/prompt_router_helper.js +52 -0
  14. package/hooks/session_end_helper.js +29 -0
  15. package/hooks/session_end_watcher.sh +24 -0
  16. package/lib/commands/attach.js +47 -0
  17. package/lib/commands/init.js +207 -0
  18. package/lib/commands/start.js +16 -0
  19. package/lib/commands/status.js +53 -0
  20. package/lib/config-schema.js +72 -0
  21. package/lib/inject-settings.js +61 -0
  22. package/lib/mirror.js +244 -0
  23. package/lib/resolve-project-dir.sh +47 -0
  24. package/mcp/execute-ticket/index.js +10 -0
  25. package/mcp/execute-ticket/package.json +5 -0
  26. package/mcp/help/index.js +79 -0
  27. package/mcp/help/package.json +14 -0
  28. package/mcp/router/README.md +19 -0
  29. package/mcp/router/embedder.js +23 -0
  30. package/mcp/router/index.js +204 -0
  31. package/mcp/router/matcher.js +87 -0
  32. package/mcp/router/package-lock.json +1048 -0
  33. package/mcp/router/package.json +11 -0
  34. package/mcp/router/skill-index.js +104 -0
  35. package/package.json +47 -0
  36. package/scripts/board_resolver.sh +46 -0
  37. package/scripts/e25_s01_extract_board_graph.py +292 -0
  38. package/scripts/e25_s01_generate_synthetic_board.py +90 -0
  39. package/scripts/measurement-10x.json +50 -0
  40. package/scripts/measurement-10x.txt +4 -0
  41. package/scripts/measurement-real.json +50 -0
  42. package/scripts/measurement-real.txt +4 -0
  43. package/scripts/postinstall.js +165 -0
  44. package/scripts/todo_cleanup.sh +22 -0
  45. package/scripts/todo_manager.sh +86 -0
  46. package/scripts/validate-board.sh +190 -0
  47. package/scripts/validate-story-format.sh +53 -0
  48. package/skills/brainstorm/SKILL.md +47 -0
  49. package/skills/btw/SKILL.md +42 -0
  50. package/skills/commit/SKILL.md +29 -0
  51. package/skills/commit/assets/user_instructions_template.md +22 -0
  52. package/skills/continue/SKILL.md +29 -0
  53. package/skills/convert/SKILL.md +124 -0
  54. package/skills/convert/convert_cli.py +235 -0
  55. package/skills/convert/tests/sample.csv +4 -0
  56. package/skills/convert/tests/sample.json +5 -0
  57. package/skills/convert/tests/sample.jsonl +3 -0
  58. package/skills/convert/tests/sample.yaml +18 -0
  59. package/skills/convert/tests/sample_obj.csv +2 -0
  60. package/skills/convert/tests/sample_obj.json +9 -0
  61. package/skills/deep-dive/SKILL.md +167 -0
  62. package/skills/do/SKILL.md +88 -0
  63. package/skills/do/assets/sender_template.json +12 -0
  64. package/skills/doc/SKILL.md +314 -0
  65. package/skills/doc/assets/path-objectives.yaml +38 -0
  66. package/skills/doc-sync/SKILL.md +167 -0
  67. package/skills/doc-sync/assets/default_excludes.txt +21 -0
  68. package/skills/doc-sync/assets/doc_targets.md +14 -0
  69. package/skills/dooo/SKILL.md +60 -0
  70. package/skills/error/SKILL.md +29 -0
  71. package/skills/evaluate/SKILL.md +45 -0
  72. package/skills/evaluate/assets/evaluation_invokation_template.yml +3 -0
  73. package/skills/evaluate/assets/evaluation_rapport_template.md +24 -0
  74. package/skills/examplify/SKILL.md +42 -0
  75. package/skills/help/SKILL.md +36 -0
  76. package/skills/improve/SKILL.md +55 -0
  77. package/skills/index/scripts/board-index +4 -0
  78. package/skills/index/scripts/board_index.py +615 -0
  79. package/skills/index/scripts/smoke_test.sh +86 -0
  80. package/skills/init/SKILL.md +44 -0
  81. package/skills/init/assets/.gitignore_template +15 -0
  82. package/skills/init/assets/PROJECT_SUMMARY_template.md +13 -0
  83. package/skills/init/assets/directory_structure.txt +13 -0
  84. package/skills/init/assets/test-config_template.json +4 -0
  85. package/skills/init/assets/workflow_template.json +30 -0
  86. package/skills/init/scripts/init.sh +48 -0
  87. package/skills/jbp/SKILL.md +25 -0
  88. package/skills/jenga/SKILL.md +68 -0
  89. package/skills/lgtm/SKILL.md +21 -0
  90. package/skills/mirror-public/SKILL.md +237 -0
  91. package/skills/mirror-public/assets/config.json +5 -0
  92. package/skills/mirror-public/scripts/mirror.sh +374 -0
  93. package/skills/pi-plan/SKILL.md +62 -0
  94. package/skills/pi-plan/assets/epic.json +7 -0
  95. package/skills/pi-plan/assets/story_template.md +18 -0
  96. package/skills/proceed/SKILL.md +29 -0
  97. package/skills/publish/SKILL.md +351 -0
  98. package/skills/publish/adapters/droplet.md +200 -0
  99. package/skills/publish/adapters/mobile-ios.md +114 -0
  100. package/skills/publish/adapters/npm-ci.md +223 -0
  101. package/skills/publish/adapters/npm.md +121 -0
  102. package/skills/publish/assets/ExportOptions.plist.template +19 -0
  103. package/skills/publish/assets/ci-contract.md +111 -0
  104. package/skills/publish/assets/ownership-matrix.md +17 -0
  105. package/skills/publish/assets/publish.example.json +85 -0
  106. package/skills/publish/assets/publish.example.npm-ci.json +40 -0
  107. package/skills/publish/assets/publish.example.npm.json +41 -0
  108. package/skills/publish/assets/secrets-guide.md +104 -0
  109. package/skills/publish/schemas/fixtures/npm-ci-minimal.json +17 -0
  110. package/skills/publish/schemas/fixtures/npm-ci-with-empty-secrets.json +18 -0
  111. package/skills/publish/schemas/fixtures/npm-ci-with-workflow-path.json +18 -0
  112. package/skills/publish/schemas/publish.schema.json +428 -0
  113. package/skills/publish/scripts/check_target_config.sh +96 -0
  114. package/skills/publish/scripts/droplet_pipeline.sh +208 -0
  115. package/skills/publish/scripts/generate_release_notes.sh +200 -0
  116. package/skills/publish/scripts/ios_pipeline.sh +486 -0
  117. package/skills/publish/scripts/npm_ci_pipeline.sh +225 -0
  118. package/skills/publish/scripts/npm_pipeline.sh +249 -0
  119. package/skills/publish/scripts/publish_common.sh +253 -0
  120. package/skills/publish/scripts/publish_deploy.sh +538 -0
  121. package/skills/publish/scripts/reconcile_tags.sh +135 -0
  122. package/skills/publish/scripts/run_gates.sh +616 -0
  123. package/skills/publish/scripts/setup_wizard.sh +394 -0
  124. package/skills/publish/scripts/show_history.sh +95 -0
  125. package/skills/publish/scripts/suggest_semver_bump.sh +105 -0
  126. package/skills/publish/scripts/validate_config.sh +163 -0
  127. package/skills/publish/scripts/validate_droplet_env.sh +45 -0
  128. package/skills/publish/scripts/validate_ios_env.sh +68 -0
  129. package/skills/publish/scripts/validate_npm_ci_env.sh +71 -0
  130. package/skills/publish/scripts/validate_npm_env.sh +22 -0
  131. package/skills/publish/scripts/write_ledger_entry.sh +126 -0
  132. package/skills/publish/wizards/droplet.md +275 -0
  133. package/skills/publish/wizards/mobile-ios.md +157 -0
  134. package/skills/publish/wizards/npm-ci.md +240 -0
  135. package/skills/publish/wizards/npm.md +224 -0
  136. package/skills/reconcile/SKILL.md +93 -0
  137. package/skills/reconcile/assets/report_format.md +44 -0
  138. package/skills/reconcile-origin/SKILL.md +75 -0
  139. package/skills/reconcile-origin/scripts/reconcile-origin.sh +372 -0
  140. package/skills/redo/SKILL.md +70 -0
  141. package/skills/route/SKILL.md +180 -0
  142. package/skills/self-sync/SKILL.md +73 -0
  143. package/skills/self-sync/scripts/run.js +136 -0
  144. package/skills/skillify/SKILL.md +68 -0
  145. package/skills/skillify/assets/init-new/SKILL.md +35 -0
  146. package/skills/skillify/assets/init-new/assets/.gitignore_template +15 -0
  147. package/skills/skillify/assets/init-new/assets/PROJECT_SUMMARY_template.md +13 -0
  148. package/skills/skillify/assets/init-new/assets/directory_structure.txt +10 -0
  149. package/skills/skillify/assets/init-new/assets/test-config_template.json +4 -0
  150. package/skills/skillify/assets/init-new/assets/workflow_template.json +17 -0
  151. package/skills/skillify/assets/init-new/scripts/init.sh +48 -0
  152. package/skills/skillify/assets/init-old/SKILL.md +124 -0
  153. package/skills/spinoff/SKILL.md +48 -0
  154. package/skills/status/SKILL.md +33 -0
  155. package/skills/status/assets/output_format.md +41 -0
  156. package/skills/todo/SKILL.md +46 -0
  157. package/skills/todo/assets/todo_handoff_template.md +22 -0
  158. package/skills/todo/assets/todo_template.md +3 -0
  159. package/skills/train/SKILL.md +116 -0
  160. package/skills/train/assets/dashboard-templates/classifiers.html +106 -0
  161. package/skills/train/assets/dashboard-templates/nlp.html +102 -0
  162. package/skills/train/assets/dashboard-templates/transformers.html +98 -0
  163. package/skills/train/assets/results-parsers/__init__.py +9 -0
  164. package/skills/train/assets/results-parsers/classifiers.py +84 -0
  165. package/skills/train/assets/results-parsers/nlp.py +88 -0
  166. package/skills/train/assets/results-parsers/reporter.py +154 -0
  167. package/skills/train/assets/results-parsers/transformers.py +120 -0
  168. package/skills/train/train_cli.py +786 -0
  169. package/templates/EXECUTION_PLAN_TEMPLATE.md +43 -0
  170. package/templates/EXECUTION_SUMMARY_TEMPLATE.md +50 -0
  171. package/templates/JENGA_CONFIG_TEMPLATE.json +23 -0
  172. package/templates/PROBLEM_RAPPORT_TEMPLATE.md +88 -0
  173. package/templates/SCRUM_BOARD_SCHEMA.md +311 -0
  174. package/templates/SKILL.md +16 -0
  175. package/templates/SKILL_TEMPLATE.md +28 -0
  176. package/templates/USER_INSTRUCTIONS_TEMPLATE.md +22 -0
  177. package/templates/copilot-instructions.md.tpl +55 -0
@@ -0,0 +1,394 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ SKILL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+ REPO_ROOT="$(cd "${SKILL_DIR}/../.." && pwd)"
7
+ SECRETS_GUIDE_PATH="${SKILL_DIR}/assets/secrets-guide.md"
8
+ VALIDATE_SCRIPT="${SCRIPT_DIR}/validate_config.sh"
9
+ DEFAULT_CONFIG_PATH="${REPO_ROOT}/publish.json"
10
+ EXIT_ABORTED=1
11
+ EXIT_CONFIG_INVALID=4
12
+
13
+ TARGET_NAME=""
14
+ DEPLOY_TYPE=""
15
+ CONFIG_PATH="$DEFAULT_CONFIG_PATH"
16
+ PROMPT_RESULT=""
17
+
18
+ usage() {
19
+ cat <<'EOF_USAGE'
20
+ Usage: setup_wizard.sh [<target_name>] [--type <deployment_type>] [--config <path>]
21
+ EOF_USAGE
22
+ }
23
+
24
+ fail() {
25
+ printf 'setup wizard error: %s\n' "$1" >&2
26
+ exit "$EXIT_CONFIG_INVALID"
27
+ }
28
+
29
+ prompt_line() {
30
+ local prompt="$1"
31
+ local value=""
32
+ printf '%s' "$prompt"
33
+ if ! IFS= read -r value; then
34
+ printf '\nSetup cancelled before input was completed.\n' >&2
35
+ exit "$EXIT_ABORTED"
36
+ fi
37
+ PROMPT_RESULT="$value"
38
+ }
39
+
40
+ prompt_required() {
41
+ local prompt="$1"
42
+ while :; do
43
+ prompt_line "$prompt"
44
+ if [[ -n "$PROMPT_RESULT" ]]; then
45
+ return 0
46
+ fi
47
+ printf 'Value is required.\n' >&2
48
+ done
49
+ }
50
+
51
+ prompt_target_name() {
52
+ while :; do
53
+ prompt_required "Target name: "
54
+ if printf '%s' "$PROMPT_RESULT" | grep -Eq '^[a-z0-9][a-z0-9-]*$'; then
55
+ TARGET_NAME="$PROMPT_RESULT"
56
+ return 0
57
+ fi
58
+ printf 'Target name must be lowercase kebab-case.\n' >&2
59
+ done
60
+ }
61
+
62
+ prompt_deploy_type() {
63
+ printf 'Available deployment types:\n'
64
+ printf '1. mobile-ios\n'
65
+ printf '2. npm\n'
66
+ printf '3. npm-ci\n'
67
+ prompt_line 'Choose type [1]: '
68
+ case "${PROMPT_RESULT:-1}" in
69
+ 1|mobile-ios|"") DEPLOY_TYPE="mobile-ios" ;;
70
+ 2|npm) DEPLOY_TYPE="npm" ;;
71
+ 3|npm-ci) DEPLOY_TYPE="npm-ci" ;;
72
+ *) printf 'Unsupported type. Choose mobile-ios, npm, or npm-ci.\n' >&2; prompt_deploy_type ;;
73
+ esac
74
+ }
75
+
76
+ question_section() {
77
+ local field="$1"
78
+ awk -v field="$field" '
79
+ $0 == "## Question: " field {capture=1}
80
+ capture {
81
+ if ($0 ~ /^## Question: / && $0 != "## Question: " field) {
82
+ exit
83
+ }
84
+ print
85
+ }
86
+ ' "$TEMPLATE_PATH"
87
+ }
88
+
89
+ validate_env_var_name() {
90
+ printf '%s' "$1" | grep -Eq '^[A-Z][A-Z0-9_]*$'
91
+ }
92
+
93
+ warn_if_missing_env() {
94
+ local var_name="$1"
95
+ if [[ -z "${!var_name:-}" ]]; then
96
+ printf "⚠️ Env var '%s' is not set. Set it before deploying.\n" "$var_name" >&2
97
+ fi
98
+ }
99
+
100
+ capture_question_answer() {
101
+ local field="$1"
102
+ printf '\n%s\n' "$(question_section "$field")"
103
+ while :; do
104
+ case "$field" in
105
+ configuration)
106
+ prompt_line '> [Release] '
107
+ PROMPT_RESULT="${PROMPT_RESULT:-Release}"
108
+ ;;
109
+ provider_short_name)
110
+ prompt_line '> '
111
+ return 0
112
+ ;;
113
+ *)
114
+ prompt_required '> '
115
+ ;;
116
+ esac
117
+
118
+ case "$field" in
119
+ export_method)
120
+ if [[ "$PROMPT_RESULT" == "ad-hoc" || "$PROMPT_RESULT" == "app-store" ]]; then
121
+ return 0
122
+ fi
123
+ printf 'Enter ad-hoc or app-store.\n' >&2
124
+ ;;
125
+ team_id)
126
+ if printf '%s' "$PROMPT_RESULT" | grep -Eq '^[A-Z0-9]{6,}$'; then
127
+ return 0
128
+ fi
129
+ printf 'Enter a valid Apple team identifier such as ABCDE12345.\n' >&2
130
+ ;;
131
+ app_store_app_id)
132
+ if printf '%s' "$PROMPT_RESULT" | grep -Eq '^[0-9]+$'; then
133
+ return 0
134
+ fi
135
+ printf 'Enter a numeric App Store app identifier.\n' >&2
136
+ ;;
137
+ project_or_workspace_path)
138
+ if printf '%s' "$PROMPT_RESULT" | grep -Eq '\.(xcodeproj|xcworkspace)$'; then
139
+ return 0
140
+ fi
141
+ printf 'Enter a path ending in .xcodeproj or .xcworkspace.\n' >&2
142
+ ;;
143
+ api_key_id_env_var|issuer_id_env_var|private_key_path_env_var|code_sign_identity_env_var|provisioning_profile_uuid_env_var)
144
+ if validate_env_var_name "$PROMPT_RESULT"; then
145
+ warn_if_missing_env "$PROMPT_RESULT"
146
+ return 0
147
+ fi
148
+ printf 'Enter an uppercase environment-variable name.\n' >&2
149
+ ;;
150
+ *)
151
+ return 0
152
+ ;;
153
+ esac
154
+ done
155
+ }
156
+
157
+ build_target_json() {
158
+ local project_path=""
159
+ local workspace_path=""
160
+ if [[ "$PROJECT_OR_WORKSPACE_PATH" == *.xcworkspace ]]; then
161
+ workspace_path="$PROJECT_OR_WORKSPACE_PATH"
162
+ else
163
+ project_path="$PROJECT_OR_WORKSPACE_PATH"
164
+ fi
165
+
166
+ jq -n \
167
+ --arg name "$TARGET_NAME" \
168
+ --arg type "$DEPLOY_TYPE" \
169
+ --arg project_path "$project_path" \
170
+ --arg workspace_path "$workspace_path" \
171
+ --arg scheme "$SCHEME" \
172
+ --arg configuration "$CONFIGURATION" \
173
+ --arg archive_path "$ARCHIVE_PATH" \
174
+ --arg export_path "$EXPORT_PATH" \
175
+ --arg export_method "$EXPORT_METHOD" \
176
+ --arg bundle_id "$BUNDLE_ID" \
177
+ --arg team_id "$TEAM_ID" \
178
+ --arg app_store_app_id "$APP_STORE_APP_ID" \
179
+ --arg provider_short_name "$PROVIDER_SHORT_NAME" \
180
+ --arg api_key_id_ref "\$$API_KEY_ID_ENV_VAR" \
181
+ --arg issuer_id_ref "\$$ISSUER_ID_ENV_VAR" \
182
+ --arg private_key_ref "\$$PRIVATE_KEY_PATH_ENV_VAR" \
183
+ --arg code_sign_identity_ref "\$$CODE_SIGN_IDENTITY_ENV_VAR" \
184
+ --arg provisioning_profile_ref "\$$PROVISIONING_PROFILE_UUID_ENV_VAR" \
185
+ '{
186
+ name: $name,
187
+ type: $type,
188
+ platform: "ios-app-store",
189
+ checks: {
190
+ pre: [],
191
+ post: []
192
+ },
193
+ secrets: {
194
+ app_store_connect_api_key_id: $api_key_id_ref,
195
+ app_store_connect_issuer_id: $issuer_id_ref,
196
+ app_store_connect_private_key_path: $private_key_ref,
197
+ code_sign_identity: $code_sign_identity_ref,
198
+ provisioning_profile_uuid: $provisioning_profile_ref
199
+ },
200
+ ios: {
201
+ scheme: $scheme,
202
+ configuration: $configuration,
203
+ archive_path: $archive_path,
204
+ export_path: $export_path,
205
+ export_method: $export_method,
206
+ bundle_id: $bundle_id,
207
+ team_id: $team_id,
208
+ app_store_app_id: $app_store_app_id
209
+ }
210
+ }
211
+ | if $project_path != "" then .ios.project_path = $project_path else . end
212
+ | if $workspace_path != "" then .ios.workspace_path = $workspace_path else . end
213
+ | if $provider_short_name != "" then .ios.provider_short_name = $provider_short_name else . end'
214
+ }
215
+
216
+ save_config() {
217
+ local target_json="$1"
218
+ local backup_path="${CONFIG_PATH}.setup-wizard.bak"
219
+ local merged_json=""
220
+ local defaults_json='{"history_file":"project/logs/publish-history.json","mandatory_checks":["build","test"],"optional_checks":["lint","type-check","custom-script","smoke-test","ping"]}'
221
+
222
+ mkdir -p "$(dirname "$CONFIG_PATH")"
223
+
224
+ if [[ -f "$CONFIG_PATH" ]]; then
225
+ jq -e . "$CONFIG_PATH" >/dev/null 2>&1 || fail "existing publish.json is not valid JSON"
226
+ cp "$CONFIG_PATH" "$backup_path"
227
+ merged_json="$(jq \
228
+ --argjson new_target "$target_json" \
229
+ --argjson defaults "$defaults_json" '
230
+ .version = (.version // 1)
231
+ | .defaults = (.defaults // $defaults)
232
+ | .targets = (
233
+ (.targets // [])
234
+ | if any(.name == $new_target.name) then
235
+ map(if .name == $new_target.name then $new_target else . end)
236
+ else
237
+ . + [$new_target]
238
+ end
239
+ )
240
+ ' "$CONFIG_PATH")" || {
241
+ rm -f "$backup_path"
242
+ fail "could not merge target into existing publish.json"
243
+ }
244
+ else
245
+ merged_json="$(jq -n \
246
+ --argjson new_target "$target_json" \
247
+ --argjson defaults "$defaults_json" \
248
+ '{version: 1, defaults: $defaults, targets: [$new_target]}')" || fail "could not create publish.json payload"
249
+ fi
250
+
251
+ printf '%s\n' "$merged_json" > "$CONFIG_PATH"
252
+
253
+ if ! bash "$VALIDATE_SCRIPT" "$CONFIG_PATH"; then
254
+ printf 'publish.json failed validation after save. Rolling back.\n' >&2
255
+ if [[ -f "$backup_path" ]]; then
256
+ mv "$backup_path" "$CONFIG_PATH"
257
+ else
258
+ rm -f "$CONFIG_PATH"
259
+ fi
260
+ exit "$EXIT_CONFIG_INVALID"
261
+ fi
262
+
263
+ rm -f "$backup_path"
264
+ printf 'Saved config to %s\n' "$CONFIG_PATH"
265
+ }
266
+
267
+ while [[ $# -gt 0 ]]; do
268
+ case "$1" in
269
+ --type)
270
+ [[ $# -ge 2 ]] || { usage >&2; exit "$EXIT_CONFIG_INVALID"; }
271
+ DEPLOY_TYPE="$2"
272
+ shift 2
273
+ ;;
274
+ --config)
275
+ [[ $# -ge 2 ]] || { usage >&2; exit "$EXIT_CONFIG_INVALID"; }
276
+ CONFIG_PATH="$2"
277
+ shift 2
278
+ ;;
279
+ --help|-h)
280
+ usage
281
+ exit 0
282
+ ;;
283
+ --*)
284
+ usage >&2
285
+ exit "$EXIT_CONFIG_INVALID"
286
+ ;;
287
+ *)
288
+ if [[ -z "$TARGET_NAME" ]]; then
289
+ TARGET_NAME="$1"
290
+ else
291
+ usage >&2
292
+ exit "$EXIT_CONFIG_INVALID"
293
+ fi
294
+ shift
295
+ ;;
296
+ esac
297
+ done
298
+
299
+ [[ -f "$SECRETS_GUIDE_PATH" ]] || fail "secrets guide not found at '$SECRETS_GUIDE_PATH'"
300
+ [[ -f "$VALIDATE_SCRIPT" ]] || fail "validate_config.sh not found at '$VALIDATE_SCRIPT'"
301
+
302
+ if [[ "$CONFIG_PATH" != /* ]]; then
303
+ CONFIG_PATH="$REPO_ROOT/${CONFIG_PATH#./}"
304
+ fi
305
+
306
+ if [[ -z "$TARGET_NAME" ]]; then
307
+ prompt_target_name
308
+ elif ! printf '%s' "$TARGET_NAME" | grep -Eq '^[a-z0-9][a-z0-9-]*$'; then
309
+ fail "target name must be lowercase kebab-case"
310
+ fi
311
+
312
+ if [[ -z "$DEPLOY_TYPE" ]]; then
313
+ prompt_deploy_type
314
+ elif [[ "$DEPLOY_TYPE" != "mobile-ios" && "$DEPLOY_TYPE" != "npm" && "$DEPLOY_TYPE" != "npm-ci" ]]; then
315
+ fail "unsupported deployment type '$DEPLOY_TYPE'"
316
+ fi
317
+
318
+ TEMPLATE_PATH="${SKILL_DIR}/wizards/${DEPLOY_TYPE}.md"
319
+ [[ -f "$TEMPLATE_PATH" ]] || fail "wizard template not found at '$TEMPLATE_PATH'"
320
+
321
+ printf '📦 /publish setup — %s (%s)\n' "$TARGET_NAME" "$DEPLOY_TYPE"
322
+ printf 'Secrets guide: %s\n\n' "$SECRETS_GUIDE_PATH"
323
+ awk '
324
+ /^## Never do this/ {capture=1; print; next}
325
+ capture && /^## / {exit}
326
+ capture {print}
327
+ ' "$SECRETS_GUIDE_PATH"
328
+ printf '\nUsing wizard template: %s\n' "$TEMPLATE_PATH"
329
+
330
+ # The npm and npm-ci wizards (wizards/npm.md, wizards/npm-ci.md) are
331
+ # agent-driven templates: they specify question prompts, post-collection
332
+ # actions, and JSON assembly rules that the /publish skill agent executes
333
+ # directly. Unlike the iOS wizard, their questions do not map onto a
334
+ # shell-side JSON build loop. Hand off by printing the resolved template
335
+ # path and exiting; the caller reads the template and drives the flow.
336
+ if [[ "$DEPLOY_TYPE" == "npm" ]]; then
337
+ printf '\nnpm setup: follow the prompts in %s to collect answers,\n' "$TEMPLATE_PATH"
338
+ printf 'then perform the "Post-collection actions" section (write target block\n'
339
+ printf 'to publish.json and generate the NPM_TOKEN instructions file).\n'
340
+ exit 0
341
+ fi
342
+
343
+ if [[ "$DEPLOY_TYPE" == "npm-ci" ]]; then
344
+ printf '\nnpm-ci setup: follow the prompts in %s to collect answers,\n' "$TEMPLATE_PATH"
345
+ printf 'then perform the "Post-collection actions" section (write target block\n'
346
+ printf 'to publish.json). No NPM_TOKEN is required — publishing uses OIDC.\n'
347
+ exit 0
348
+ fi
349
+
350
+ QUESTION_FIELDS="$(awk '/^## Question: /{sub(/^## Question: /, ""); print}' "$TEMPLATE_PATH")"
351
+ [[ -n "$QUESTION_FIELDS" ]] || fail "no questions found in template"
352
+
353
+ OLD_IFS="$IFS"
354
+ IFS='
355
+ '
356
+ set -f
357
+ for field in $QUESTION_FIELDS; do
358
+ capture_question_answer "$field"
359
+ case "$field" in
360
+ bundle_id) BUNDLE_ID="$PROMPT_RESULT" ;;
361
+ team_id) TEAM_ID="$PROMPT_RESULT" ;;
362
+ scheme) SCHEME="$PROMPT_RESULT" ;;
363
+ configuration) CONFIGURATION="$PROMPT_RESULT" ;;
364
+ archive_path) ARCHIVE_PATH="$PROMPT_RESULT" ;;
365
+ export_path) EXPORT_PATH="$PROMPT_RESULT" ;;
366
+ export_method) EXPORT_METHOD="$PROMPT_RESULT" ;;
367
+ app_store_app_id) APP_STORE_APP_ID="$PROMPT_RESULT" ;;
368
+ project_or_workspace_path) PROJECT_OR_WORKSPACE_PATH="$PROMPT_RESULT" ;;
369
+ provider_short_name) PROVIDER_SHORT_NAME="$PROMPT_RESULT" ;;
370
+ api_key_id_env_var) API_KEY_ID_ENV_VAR="$PROMPT_RESULT" ;;
371
+ issuer_id_env_var) ISSUER_ID_ENV_VAR="$PROMPT_RESULT" ;;
372
+ private_key_path_env_var) PRIVATE_KEY_PATH_ENV_VAR="$PROMPT_RESULT" ;;
373
+ code_sign_identity_env_var) CODE_SIGN_IDENTITY_ENV_VAR="$PROMPT_RESULT" ;;
374
+ provisioning_profile_uuid_env_var) PROVISIONING_PROFILE_UUID_ENV_VAR="$PROMPT_RESULT" ;;
375
+ *) fail "unsupported question field '$field'" ;;
376
+ esac
377
+ done
378
+ set +f
379
+ IFS="$OLD_IFS"
380
+
381
+ TARGET_JSON="$(build_target_json)" || fail "could not assemble target config"
382
+
383
+ printf '\nConfig preview:\n'
384
+ printf '%s\n' "$TARGET_JSON" | jq .
385
+ prompt_line 'Save this config to publish.json? [y/N] '
386
+ case "${PROMPT_RESULT:-N}" in
387
+ y|Y|yes|YES)
388
+ save_config "$TARGET_JSON"
389
+ ;;
390
+ *)
391
+ printf 'Config not saved. Re-run /publish setup any time.\n'
392
+ exit "$EXIT_ABORTED"
393
+ ;;
394
+ esac
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ source "$SCRIPT_DIR/publish_common.sh"
6
+
7
+ usage() {
8
+ cat <<'USAGE'
9
+ Usage: show_history.sh [--limit <n>] [--target <name>] [--json] [--config <path>]
10
+ USAGE
11
+ }
12
+
13
+ LIMIT_VALUE=''
14
+ TARGET_NAME=''
15
+ JSON_OUTPUT=0
16
+ CONFIG_PATH=''
17
+
18
+ while [[ $# -gt 0 ]]; do
19
+ case "$1" in
20
+ --limit)
21
+ [[ $# -ge 2 ]] || { usage >&2; exit 1; }
22
+ LIMIT_VALUE="$2"
23
+ shift 2
24
+ ;;
25
+ --target)
26
+ [[ $# -ge 2 ]] || { usage >&2; exit 1; }
27
+ TARGET_NAME="$2"
28
+ shift 2
29
+ ;;
30
+ --json)
31
+ JSON_OUTPUT=1
32
+ shift
33
+ ;;
34
+ --config)
35
+ [[ $# -ge 2 ]] || { usage >&2; exit 1; }
36
+ CONFIG_PATH="$2"
37
+ shift 2
38
+ ;;
39
+ --help|-h)
40
+ usage
41
+ exit 0
42
+ ;;
43
+ *)
44
+ usage >&2
45
+ exit 1
46
+ ;;
47
+ esac
48
+ done
49
+
50
+ if [[ -n "$LIMIT_VALUE" && ! "$LIMIT_VALUE" =~ ^[0-9]+$ ]]; then
51
+ echo '--limit must be a non-negative integer.' >&2
52
+ exit 1
53
+ fi
54
+
55
+ HISTORY_FILE="$(publish_resolve_history_file "$CONFIG_PATH")"
56
+ if [[ ! -f "$HISTORY_FILE" ]]; then
57
+ if (( JSON_OUTPUT )); then
58
+ printf '[]\n'
59
+ else
60
+ printf 'No publish history found.\n'
61
+ fi
62
+ exit 0
63
+ fi
64
+
65
+ FILTERED_JSON="$(jq -c \
66
+ --arg target "$TARGET_NAME" \
67
+ --arg limit "$LIMIT_VALUE" '
68
+ [ .[]?
69
+ | select(type == "object")
70
+ | if $target == "" then . else select((.target // "") == $target) end
71
+ ]
72
+ | reverse
73
+ | if $limit == "" then . else .[:($limit | tonumber)] end
74
+ ' "$HISTORY_FILE")"
75
+
76
+ if [[ "$FILTERED_JSON" == '[]' ]]; then
77
+ if (( JSON_OUTPUT )); then
78
+ printf '[]\n'
79
+ else
80
+ printf 'No publish history found.\n'
81
+ fi
82
+ exit 0
83
+ fi
84
+
85
+ if (( JSON_OUTPUT )); then
86
+ printf '%s\n' "$FILTERED_JSON" | jq .
87
+ exit 0
88
+ fi
89
+
90
+ printf '%-10s %-20s %-20s %-10s %s\n' 'Version' 'Target' 'Date' 'State' 'Tag'
91
+ printf '%-10s %-20s %-20s %-10s %s\n' '-------' '------' '----' '-----' '---'
92
+ printf '%s\n' "$FILTERED_JSON" | jq -r '.[] | [(.version // "-"), (.target // "-"), (.timestamp // .completed_at // .started_at // "-"), (.platform_state // .state // "-"), (.git_tag // .version // "-")] | @tsv' | \
93
+ while IFS=$'\t' read -r version target date state tag; do
94
+ printf '%-10s %-20s %-20s %-10s %s\n' "$version" "$target" "$date" "$state" "$tag"
95
+ done
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ source "$SCRIPT_DIR/publish_common.sh"
6
+
7
+ usage() {
8
+ cat <<'USAGE'
9
+ Usage: suggest_semver_bump.sh [--current-version <vX.Y.Z>] [--yes] [<publish_json_path>]
10
+ USAGE
11
+ }
12
+
13
+ CURRENT_VERSION=""
14
+ AUTO_ACCEPT=0
15
+ CONFIG_PATH=""
16
+ POSITIONAL_COUNT=0
17
+
18
+ while [[ $# -gt 0 ]]; do
19
+ case "$1" in
20
+ --current-version)
21
+ [[ $# -ge 2 ]] || { usage >&2; exit 1; }
22
+ CURRENT_VERSION="$2"
23
+ shift 2
24
+ ;;
25
+ --yes)
26
+ AUTO_ACCEPT=1
27
+ shift
28
+ ;;
29
+ --help|-h)
30
+ usage
31
+ exit 0
32
+ ;;
33
+ --*)
34
+ usage >&2
35
+ exit 1
36
+ ;;
37
+ *)
38
+ POSITIONAL_COUNT=$((POSITIONAL_COUNT + 1))
39
+ if (( POSITIONAL_COUNT > 1 )); then
40
+ usage >&2
41
+ exit 1
42
+ fi
43
+ CONFIG_PATH="$1"
44
+ shift
45
+ ;;
46
+ esac
47
+ done
48
+
49
+ HISTORY_FILE="$(publish_resolve_history_file "$CONFIG_PATH")"
50
+ if [[ -z "$CURRENT_VERSION" ]]; then
51
+ CURRENT_VERSION="$(publish_latest_ledger_version "$HISTORY_FILE" 2>/dev/null || true)"
52
+ fi
53
+ [[ -n "$CURRENT_VERSION" ]] || CURRENT_VERSION='v0.0.0'
54
+ CURRENT_VERSION="$(publish_normalize_version "$CURRENT_VERSION")" || {
55
+ echo "Invalid current version: $CURRENT_VERSION" >&2
56
+ exit 1
57
+ }
58
+
59
+ LAST_TAG="$(publish_resolve_last_publish_tag "$HISTORY_FILE" HEAD 2>/dev/null || true)"
60
+ if [[ -n "$LAST_TAG" ]]; then
61
+ RANGE=("$LAST_TAG..HEAD")
62
+ else
63
+ RANGE=(HEAD)
64
+ fi
65
+
66
+ LOG_CONTENT="$(git log "${RANGE[@]}" --format='%s%n%b<<__COMMIT_BOUNDARY__>>' 2>/dev/null || true)"
67
+ BUMP_LEVEL='patch'
68
+ if printf '%s\n' "$LOG_CONTENT" | grep -Eq 'BREAKING CHANGE|^[^[:space:]]+(\([^)]+\))?!:'; then
69
+ BUMP_LEVEL='major'
70
+ elif printf '%s\n' "$LOG_CONTENT" | grep -Eq '^feat(\([^)]+\))?(!)?: '; then
71
+ BUMP_LEVEL='minor'
72
+ fi
73
+
74
+ NEW_VERSION="$(publish_bump_version "$CURRENT_VERSION" "$BUMP_LEVEL")"
75
+ printf 'Suggested version bump: %s → %s\n' "$BUMP_LEVEL" "$NEW_VERSION"
76
+
77
+ SELECTED_VERSION="$NEW_VERSION"
78
+ if (( AUTO_ACCEPT )); then
79
+ :
80
+ elif [[ -t 0 ]]; then
81
+ read -r -p "Use this version? [Y/n]: " response
82
+ response="${response:-Y}"
83
+ case "$response" in
84
+ Y|y|yes|YES)
85
+ :
86
+ ;;
87
+ N|n|no|NO)
88
+ read -r -p "Enter version (vX.Y.Z), or press Enter to abort: " manual_version
89
+ [[ -n "$manual_version" ]] || exit 1
90
+ SELECTED_VERSION="$(publish_normalize_version "$manual_version")" || {
91
+ echo "Invalid version: $manual_version" >&2
92
+ exit 1
93
+ }
94
+ ;;
95
+ *)
96
+ echo 'Unrecognized response.' >&2
97
+ exit 1
98
+ ;;
99
+ esac
100
+ else
101
+ echo 'Non-interactive use requires --yes.' >&2
102
+ exit 1
103
+ fi
104
+
105
+ printf '%s\n' "$SELECTED_VERSION"