@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,163 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+
4
+ CONFIG_PATH="${1:-}"
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ SCHEMA_PATH="${SCRIPT_DIR}/../schemas/publish.schema.json"
7
+ EXIT_CONFIG_INVALID=4
8
+
9
+ fail() {
10
+ printf 'publish config validation failed: %s\n' "$1" >&2
11
+ exit "$EXIT_CONFIG_INVALID"
12
+ }
13
+
14
+ [[ -n "$CONFIG_PATH" ]] || fail "missing config path argument"
15
+ [[ -f "$CONFIG_PATH" ]] || fail "config file not found at '$CONFIG_PATH'"
16
+ [[ -f "$SCHEMA_PATH" ]] || fail "schema file not found at '$SCHEMA_PATH'"
17
+ command -v jq >/dev/null 2>&1 || fail "jq is required"
18
+
19
+ jq -e . "$SCHEMA_PATH" >/dev/null 2>&1 || fail "schema file is not valid JSON"
20
+ jq -e . "$CONFIG_PATH" >/dev/null 2>&1 || fail "config file is not valid JSON"
21
+
22
+ # shellcheck disable=SC2154 # $t, $cfg, $schema are jq variables inside the single-quoted program below, not shell variables.
23
+ ERRORS="$(
24
+ jq -rn \
25
+ --slurpfile cfg "$CONFIG_PATH" \
26
+ --slurpfile schema "$SCHEMA_PATH" '
27
+ ($cfg[0]) as $cfg
28
+ | ($schema[0]) as $schema
29
+ | def env_ref: type == "string" and test("^\\$\\{?[A-Z][A-Z0-9_]*\\}?$");
30
+ def non_empty_string: type == "string" and length > 0;
31
+ def string_gate_name: type == "string" and test("^[a-z0-9][a-z0-9-]*$");
32
+ def gate_entry_valid:
33
+ (string_gate_name)
34
+ or (
35
+ type == "object"
36
+ and (
37
+ (.name? | string_gate_name)
38
+ or (.gate? | string_gate_name)
39
+ or (.type? | string_gate_name)
40
+ )
41
+ );
42
+ def gate_list_valid:
43
+ type == "array" and all(.[]?; gate_entry_valid);
44
+ def checks_valid:
45
+ type == "object"
46
+ and ((.pre? == null) or (.pre | gate_list_valid))
47
+ and ((.post? == null) or (.post | gate_list_valid));
48
+ def secrets_valid:
49
+ type == "object"
50
+ and (.app_store_connect_api_key_id? | env_ref)
51
+ and (.app_store_connect_issuer_id? | env_ref)
52
+ and (.app_store_connect_private_key_path? | env_ref)
53
+ and (.code_sign_identity? | env_ref)
54
+ and (.provisioning_profile_uuid? | env_ref);
55
+ def ios_valid:
56
+ type == "object"
57
+ and (.scheme? | non_empty_string)
58
+ and (.configuration? | non_empty_string)
59
+ and (.archive_path? | non_empty_string)
60
+ and (.export_path? | non_empty_string)
61
+ and (((.export_method? // "") == "ad-hoc") or ((.export_method? // "") == "app-store"))
62
+ and (.bundle_id? | non_empty_string)
63
+ and (.team_id? | type == "string" and test("^[A-Z0-9]{6,}$"))
64
+ and (.app_store_app_id? | type == "string" and test("^[0-9]+$"))
65
+ and (((.project_path? // empty) | non_empty_string) or ((.workspace_path? // empty) | non_empty_string));
66
+ def npm_secrets_valid:
67
+ type == "object"
68
+ and (
69
+ (((.NPM_TOKEN? // null) != null) and (.NPM_TOKEN | env_ref))
70
+ or (((.NODE_AUTH_TOKEN? // null) != null) and (.NODE_AUTH_TOKEN | env_ref))
71
+ );
72
+ def npm_valid:
73
+ type == "object"
74
+ and (.package_name? | non_empty_string)
75
+ and (.package_name | test("^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$"))
76
+ and ((.package_name | length) <= 214)
77
+ and (((.access? // "") == "public") or ((.access? // "") == "restricted"))
78
+ and (((.registry? // null) == null) or (.registry | non_empty_string))
79
+ and (((.dist_tag? // null) == null) or (((.dist_tag | type) == "string") and (.dist_tag | test("^[a-z0-9][a-z0-9._-]*$"))));
80
+ def npm_ci_valid:
81
+ type == "object"
82
+ and (.package_name? | non_empty_string)
83
+ and (.package_name | test("^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$"))
84
+ and ((.package_name | length) <= 214)
85
+ and (((.access? // "") == "public") or ((.access? // "") == "restricted"))
86
+ and (((.registry? // null) == null) or (.registry | non_empty_string))
87
+ and (((.dist_tag? // null) == null) or (((.dist_tag | type) == "string") and (.dist_tag | test("^[a-z0-9][a-z0-9._-]*$"))));
88
+ def ios_target_valid:
89
+ type == "object"
90
+ and (.name? | type == "string" and test("^[a-z0-9][a-z0-9-]*$"))
91
+ and (.type? == "mobile-ios")
92
+ and (.platform? == "ios-app-store")
93
+ and (.checks? | checks_valid)
94
+ and (.secrets? | secrets_valid)
95
+ and (.ios? | ios_valid);
96
+ def npm_target_valid:
97
+ type == "object"
98
+ and (.name? | type == "string" and test("^[a-z0-9][a-z0-9-]*$"))
99
+ and (.type? == "npm")
100
+ and (((.platform? // "") == "npm-registry") or ((.platform? // "") == "github-packages"))
101
+ and (.checks? | checks_valid)
102
+ and (.secrets? | npm_secrets_valid)
103
+ and (.npm? | npm_valid)
104
+ and ((.ios? // null) == null);
105
+ def npm_ci_target_valid:
106
+ type == "object"
107
+ and (.name? | type == "string" and test("^[a-z0-9][a-z0-9-]*$"))
108
+ and (.type? == "npm-ci")
109
+ and ((.github_repo? // "") | non_empty_string)
110
+ and (.npm? | npm_ci_valid)
111
+ and ((.ios? // null) == null);
112
+ def target_valid: ios_target_valid or npm_target_valid or npm_ci_target_valid;
113
+ def target_label: (.name? // "<unnamed>");
114
+ def npm_diagnostics:
115
+ . as $t
116
+ | [
117
+ (if (($t.npm? | type) != "object") then ("target \"" + ($t | target_label) + "\": npm block is required for npm targets") else empty end),
118
+ (if ((($t.npm?.package_name? // "") | non_empty_string) | not) then ("target \"" + ($t | target_label) + "\": npm.package_name is required and must be a non-empty string") else empty end),
119
+ (if ((($t.npm?.package_name? // "") | type) == "string" and (($t.npm.package_name | length) > 0) and ((($t.npm.package_name | test("^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$"))) | not)) then ("target \"" + ($t | target_label) + "\": npm.package_name must match npm naming rules") else empty end),
120
+ (if (((($t.npm?.access? // "") == "public") or (($t.npm?.access? // "") == "restricted")) | not) then ("target \"" + ($t | target_label) + "\": npm.access must be \"public\" or \"restricted\"") else empty end),
121
+ (if (($t.ios? // null) != null) then ("target \"" + ($t | target_label) + "\": ios block is not allowed on npm targets") else empty end),
122
+ (if (((($t.platform? // "") == "npm-registry") or (($t.platform? // "") == "github-packages")) | not) then ("target \"" + ($t | target_label) + "\": platform must be \"npm-registry\" or \"github-packages\" for npm targets") else empty end),
123
+ (if (($t.secrets? | npm_secrets_valid) | not) then ("target \"" + ($t | target_label) + "\": secrets must contain NPM_TOKEN or NODE_AUTH_TOKEN as an environment reference") else empty end)
124
+ ] | .[];
125
+ def npm_ci_diagnostics:
126
+ . as $t
127
+ | [
128
+ (if (($t.github_repo? // "") | non_empty_string) then empty else ("target \"" + ($t | target_label) + "\": github_repo is required and must be a non-empty string") end),
129
+ (if (($t.npm? | type) != "object") then ("target \"" + ($t | target_label) + "\": npm block is required for npm-ci targets") else empty end),
130
+ (if ((($t.npm?.package_name? // "") | non_empty_string) | not) then ("target \"" + ($t | target_label) + "\": npm.package_name is required and must be a non-empty string") else empty end),
131
+ (if ((($t.npm?.package_name? // "") | type) == "string" and (($t.npm.package_name | length) > 0) and ((($t.npm.package_name | test("^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$"))) | not)) then ("target \"" + ($t | target_label) + "\": npm.package_name must match npm naming rules") else empty end),
132
+ (if (((($t.npm?.access? // "") == "public") or (($t.npm?.access? // "") == "restricted")) | not) then ("target \"" + ($t | target_label) + "\": npm.access must be \"public\" or \"restricted\"") else empty end),
133
+ (if (($t.ios? // null) != null) then ("target \"" + ($t | target_label) + "\": ios block is not allowed on npm-ci targets") else empty end)
134
+ ] | .[];
135
+ def per_target_errors:
136
+ . as $t
137
+ | if target_valid then empty
138
+ elif ($t.type? == "npm") then ($t | npm_diagnostics)
139
+ elif ($t.type? == "npm-ci") then ($t | npm_ci_diagnostics)
140
+ elif ($t.type? == "mobile-ios") then ("invalid target: " + ($t | target_label))
141
+ else ("target \"" + ($t | target_label) + "\": type must be \"mobile-ios\", \"npm\", or \"npm-ci\"") end;
142
+ [
143
+ (if (($schema["$schema"] // "") | contains("draft-07")) then empty else "schema must declare draft-07" end),
144
+ (if (($cfg | type) == "object") then empty else "root config must be an object" end),
145
+ (if (($cfg.version? // null) == 1) then empty else "version must be 1" end),
146
+ (if (($cfg.defaults? | type) == "object") then empty else "defaults must be an object" end),
147
+ (if (($cfg.defaults.history_file? // "") | non_empty_string) then empty else "defaults.history_file must be a non-empty string" end),
148
+ (if (($cfg.defaults.mandatory_checks? | type) == "array" and (($cfg.defaults.mandatory_checks | length) > 0) and all($cfg.defaults.mandatory_checks[]; string_gate_name)) then empty else "defaults.mandatory_checks must be a non-empty array of gate names" end),
149
+ (if (($cfg.defaults.optional_checks? == null) or (($cfg.defaults.optional_checks | type) == "array" and all($cfg.defaults.optional_checks[]; string_gate_name))) then empty else "defaults.optional_checks must be an array of gate names when provided" end),
150
+ (if (($cfg.targets? | type) == "array" and (($cfg.targets | length) > 0)) then empty else "targets must be a non-empty array" end),
151
+ (if (($cfg.targets? | type) == "array") then ([$cfg.targets[] | per_target_errors] | .[]) else empty end)
152
+ ] | map(select(. != null and . != "")) | .[]
153
+ ' 2>/dev/null || true
154
+ )"
155
+
156
+ if [[ -n "$ERRORS" ]]; then
157
+ while IFS= read -r line; do
158
+ [[ -n "$line" ]] && printf 'publish config validation failed: %s\n' "$line" >&2
159
+ done <<< "$ERRORS"
160
+ exit "$EXIT_CONFIG_INVALID"
161
+ fi
162
+
163
+ exit 0
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+
4
+ EXIT_ENV_INVALID=4
5
+
6
+ # Usage: validate_droplet_env.sh <publish_json_path> <target_name>
7
+
8
+ CONFIG_PATH="${1:-}"
9
+ TARGET_NAME="${2:-}"
10
+
11
+ fail() {
12
+ printf 'droplet env validation failed: %s\n' "$1" >&2
13
+ exit "$EXIT_ENV_INVALID"
14
+ }
15
+
16
+ [[ -n "$CONFIG_PATH" ]] || fail "missing publish.json path argument"
17
+ [[ -f "$CONFIG_PATH" ]] || fail "publish config not found at '$CONFIG_PATH'"
18
+ command -v jq >/dev/null 2>&1 || fail "jq is required but not found on PATH"
19
+ [[ -n "$TARGET_NAME" ]] || fail "missing target name argument"
20
+
21
+ # Extract the droplet target block
22
+ TARGET_JSON="$(jq -c --arg t "$TARGET_NAME" \
23
+ '.targets[]? | select(.name == $t and .type == "droplet")' \
24
+ "$CONFIG_PATH")"
25
+ [[ -n "$TARGET_JSON" ]] || fail "target '$TARGET_NAME' was not found or is not type 'droplet' in '$CONFIG_PATH'"
26
+
27
+ check_field() {
28
+ local label="$1"
29
+ local value
30
+ value="$(printf '%s' "$TARGET_JSON" | jq -r "$2 // empty" 2>/dev/null || true)"
31
+ if [[ -z "$value" ]]; then
32
+ fail "missing required field '$label'"
33
+ fi
34
+ }
35
+
36
+ check_field "host" '.droplet.host'
37
+ check_field "ssh_user" '.droplet.ssh_user'
38
+ check_field "deploy_path" '.droplet.deploy_path'
39
+ check_field "github_repo" '.droplet.github_repo'
40
+ check_field "deploy_branch" '.droplet.deploy_branch'
41
+ check_field "start_cmd" '.droplet.start_cmd'
42
+ check_field "ssh_key_secret" '.secrets.ssh_key_secret'
43
+ check_field "known_hosts_secret" '.secrets.known_hosts_secret'
44
+
45
+ exit 0
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+
4
+ CONFIG_PATH="${1:-}"
5
+ TARGET_NAME="${2:-}"
6
+ EXIT_ENV_INVALID=4
7
+
8
+ fail() {
9
+ printf 'iOS env validation failed: %s\n' "$1" >&2
10
+ exit "$EXIT_ENV_INVALID"
11
+ }
12
+
13
+ [[ -n "$CONFIG_PATH" ]] || fail "missing publish.json path argument"
14
+ [[ -f "$CONFIG_PATH" ]] || fail "publish config not found at '$CONFIG_PATH'"
15
+ command -v jq >/dev/null 2>&1 || fail "jq is required"
16
+
17
+ if [[ -n "$TARGET_NAME" ]]; then
18
+ jq -e --arg target "$TARGET_NAME" '.targets[]? | select(.name == $target and .type == "mobile-ios")' "$CONFIG_PATH" >/dev/null 2>&1 || \
19
+ fail "target '$TARGET_NAME' was not found or is not mobile-ios"
20
+ fi
21
+
22
+ REQUIRED_VARS=()
23
+ while IFS= read -r var_name; do
24
+ [[ -n "$var_name" ]] && REQUIRED_VARS+=("$var_name")
25
+ done < <(
26
+ jq -r --arg target "$TARGET_NAME" '
27
+ [
28
+ .targets[]?
29
+ | select(.type == "mobile-ios")
30
+ | if $target == "" then . else select(.name == $target) end
31
+ | [
32
+ .secrets.app_store_connect_api_key_id?,
33
+ .secrets.app_store_connect_issuer_id?,
34
+ .secrets.app_store_connect_private_key_path?,
35
+ .secrets.code_sign_identity?,
36
+ .secrets.provisioning_profile_uuid?
37
+ ][]
38
+ | select(type == "string" and length > 0)
39
+ | sub("^\\$\\{"; "")
40
+ | sub("^\\$"; "")
41
+ | sub("\\}$"; "")
42
+ ]
43
+ | unique
44
+ | .[]
45
+ ' "$CONFIG_PATH"
46
+ )
47
+
48
+ if [[ ${#REQUIRED_VARS[@]} -eq 0 ]]; then
49
+ if [[ -n "$TARGET_NAME" ]]; then
50
+ fail "no mobile-ios env-var references were found for target '$TARGET_NAME'"
51
+ fi
52
+ fail "no mobile-ios env-var references were found in '$CONFIG_PATH'"
53
+ fi
54
+
55
+ MISSING_VARS=()
56
+ for var_name in "${REQUIRED_VARS[@]}"; do
57
+ value="$(printenv "$var_name" 2>/dev/null || true)"
58
+ if [[ -z "$value" ]]; then
59
+ MISSING_VARS+=("$var_name")
60
+ fi
61
+ done
62
+
63
+ if [[ ${#MISSING_VARS[@]} -gt 0 ]]; then
64
+ printf 'Missing required iOS environment variables: %s\n' "${MISSING_VARS[*]}" >&2
65
+ exit "$EXIT_ENV_INVALID"
66
+ fi
67
+
68
+ exit 0
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+
4
+ EXIT_ENV_INVALID=4
5
+
6
+ # Usage: validate_npm_ci_env.sh <publish_json_path> <target_name>
7
+
8
+ CONFIG_PATH="${1:-}"
9
+ TARGET_NAME="${2:-}"
10
+
11
+ fail() {
12
+ printf '[validate] ERROR: %s\n' "$1" >&2
13
+ exit "$EXIT_ENV_INVALID"
14
+ }
15
+
16
+ # ── runtime dependency checks ──────────────────────────────────────────────────
17
+
18
+ command -v gh >/dev/null 2>&1 || fail "gh CLI is required but not found on PATH. Install it from https://cli.github.com/ and re-run."
19
+
20
+ gh auth status >/dev/null 2>&1 || fail "gh CLI is not authenticated. Run 'gh auth login' and re-run."
21
+
22
+ command -v jq >/dev/null 2>&1 || fail "jq is required but not found on PATH. Install it from https://stedolan.github.io/jq/ and re-run."
23
+
24
+ # ── config argument checks ─────────────────────────────────────────────────────
25
+
26
+ [[ -n "$CONFIG_PATH" ]] || fail "missing publish.json path argument"
27
+ [[ -f "$CONFIG_PATH" ]] || fail "publish config not found at '$CONFIG_PATH'"
28
+ [[ -n "$TARGET_NAME" ]] || fail "missing target name argument"
29
+
30
+ # ── extract the npm-ci target block ───────────────────────────────────────────
31
+
32
+ TARGET_JSON="$(jq -c --arg t "$TARGET_NAME" \
33
+ '.targets[]? | select(.name == $t and .type == "npm-ci")' \
34
+ "$CONFIG_PATH")"
35
+ [[ -n "$TARGET_JSON" ]] || fail "target '$TARGET_NAME' was not found or is not type 'npm-ci' in '$CONFIG_PATH'"
36
+
37
+ # ── field validators ───────────────────────────────────────────────────────────
38
+
39
+ check_field() {
40
+ local label="$1"
41
+ local value
42
+ value="$(printf '%s' "$TARGET_JSON" | jq -r "$2 // empty" 2>/dev/null || true)"
43
+ if [[ -z "$value" ]]; then
44
+ fail "missing required field '$label'"
45
+ fi
46
+ }
47
+
48
+ # github_repo — required at the target level (not nested under .npm)
49
+ check_field "github_repo" '.github_repo'
50
+
51
+ # Verify github_repo contains exactly one '/'
52
+ GITHUB_REPO="$(printf '%s' "$TARGET_JSON" | jq -r '.github_repo // empty' 2>/dev/null || true)"
53
+ SLASH_COUNT="${GITHUB_REPO//[!\/]}"
54
+ if [[ ${#SLASH_COUNT} -ne 1 ]]; then
55
+ fail "field 'github_repo' must be in 'owner/name' format (contains exactly one '/'), got: '$GITHUB_REPO'"
56
+ fi
57
+
58
+ # npm.package_name — required
59
+ check_field "npm.package_name" '.npm.package_name'
60
+
61
+ # npm.access — required; must be 'public' or 'restricted'
62
+ check_field "npm.access" '.npm.access'
63
+ NPM_ACCESS="$(printf '%s' "$TARGET_JSON" | jq -r '.npm.access // empty' 2>/dev/null || true)"
64
+ if [[ "$NPM_ACCESS" != "public" && "$NPM_ACCESS" != "restricted" ]]; then
65
+ fail "field 'npm.access' must be 'public' or 'restricted', got: '$NPM_ACCESS'"
66
+ fi
67
+
68
+ # ── all checks passed ──────────────────────────────────────────────────────────
69
+
70
+ printf '[validate] environment OK\n'
71
+ exit 0
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env bash
2
+ set -u
3
+
4
+ EXIT_ENV_INVALID=4
5
+
6
+ fail() {
7
+ printf 'npm env validation failed: %s\n' "$1" >&2
8
+ exit "$EXIT_ENV_INVALID"
9
+ }
10
+
11
+ if ! command -v npm >/dev/null 2>&1; then
12
+ fail "'npm' was not found on PATH. Install Node.js (which provides npm) from https://nodejs.org/ or via your package manager, then re-run."
13
+ fi
14
+
15
+ NPM_TOKEN_VALUE="$(printenv NPM_TOKEN 2>/dev/null || true)"
16
+ NODE_AUTH_TOKEN_VALUE="$(printenv NODE_AUTH_TOKEN 2>/dev/null || true)"
17
+
18
+ if [[ -z "$NPM_TOKEN_VALUE" && -z "$NODE_AUTH_TOKEN_VALUE" ]]; then
19
+ fail "no npm auth token found. Set NPM_TOKEN (or NODE_AUTH_TOKEN) in the environment before publishing, e.g. 'export NPM_TOKEN=<your-npm-automation-token>'. Generate a token at https://www.npmjs.com/settings/<user>/tokens."
20
+ fi
21
+
22
+ exit 0
@@ -0,0 +1,126 @@
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: write_ledger_entry.sh <target> <adapter> <platform_state> <notes_path> [--yes] [--dry-run] [--version <vX.Y.Z>] [--config <path>]
10
+ USAGE
11
+ }
12
+
13
+ [[ $# -ge 4 ]] || { usage >&2; exit 1; }
14
+ TARGET_NAME="$1"
15
+ ADAPTER_NAME="$2"
16
+ PLATFORM_STATE="$3"
17
+ NOTES_PATH_RAW="$4"
18
+ shift 4
19
+
20
+ AUTO_ACCEPT=0
21
+ DRY_RUN=0
22
+ SELECTED_VERSION=''
23
+ CONFIG_PATH="${PUBLISH_CONFIG:-}"
24
+
25
+ while [[ $# -gt 0 ]]; do
26
+ case "$1" in
27
+ --yes)
28
+ AUTO_ACCEPT=1
29
+ shift
30
+ ;;
31
+ --dry-run)
32
+ DRY_RUN=1
33
+ shift
34
+ ;;
35
+ --version)
36
+ [[ $# -ge 2 ]] || { usage >&2; exit 1; }
37
+ SELECTED_VERSION="$2"
38
+ shift 2
39
+ ;;
40
+ --config)
41
+ [[ $# -ge 2 ]] || { usage >&2; exit 1; }
42
+ CONFIG_PATH="$2"
43
+ shift 2
44
+ ;;
45
+ --help|-h)
46
+ usage
47
+ exit 0
48
+ ;;
49
+ *)
50
+ usage >&2
51
+ exit 1
52
+ ;;
53
+ esac
54
+ done
55
+
56
+ [[ -n "$TARGET_NAME" && -n "$ADAPTER_NAME" && -n "$PLATFORM_STATE" ]] || { usage >&2; exit 1; }
57
+ command -v jq >/dev/null 2>&1 || { echo 'jq is required.' >&2; exit 1; }
58
+
59
+ HISTORY_FILE="$(publish_resolve_history_file "$CONFIG_PATH")"
60
+ publish_ensure_history_file "$HISTORY_FILE"
61
+
62
+ if [[ -n "$SELECTED_VERSION" ]]; then
63
+ VERSION="$SELECTED_VERSION"
64
+ else
65
+ SUGGEST_CMD=(bash "$SCRIPT_DIR/suggest_semver_bump.sh")
66
+ if (( AUTO_ACCEPT )); then
67
+ SUGGEST_CMD+=(--yes)
68
+ fi
69
+ if [[ -n "$CONFIG_PATH" ]]; then
70
+ SUGGEST_CMD+=("$CONFIG_PATH")
71
+ fi
72
+ VERSION_OUTPUT="$("${SUGGEST_CMD[@]}")"
73
+ printf '%s\n' "$VERSION_OUTPUT"
74
+ VERSION="$(printf '%s\n' "$VERSION_OUTPUT" | tail -n 1 | tr -d '\r')"
75
+ fi
76
+ VERSION="$(publish_normalize_version "$VERSION")" || {
77
+ echo "Unable to resolve a valid version from suggest_semver_bump.sh output." >&2
78
+ exit 1
79
+ }
80
+
81
+ GIT_TAG="$VERSION"
82
+ if (( DRY_RUN )); then
83
+ printf '[DRY RUN] Would create tag %s\n' "$GIT_TAG"
84
+ elif git rev-parse "$GIT_TAG" >/dev/null 2>&1; then
85
+ printf '⚠️ Git tag %s already exists — skipping tag creation\n' "$GIT_TAG" >&2
86
+ else
87
+ git tag -a "$GIT_TAG" -m "Release $GIT_TAG — deployed to $TARGET_NAME"
88
+ fi
89
+
90
+ TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
91
+ RELEASED_BY="$(publish_current_actor)"
92
+ COMMIT_SHA="$(git rev-parse HEAD)"
93
+ ENTRY_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
94
+
95
+ if [[ -z "$NOTES_PATH_RAW" || "$NOTES_PATH_RAW" == 'null' ]]; then
96
+ NOTES_JSON='null'
97
+ else
98
+ NOTES_JSON="$(jq -Rn --arg path "$NOTES_PATH_RAW" '$path')"
99
+ fi
100
+
101
+ ENTRY_JSON="$(jq -cn \
102
+ --arg id "$ENTRY_ID" \
103
+ --arg version "$VERSION" \
104
+ --arg target "$TARGET_NAME" \
105
+ --arg adapter "$ADAPTER_NAME" \
106
+ --arg timestamp "$TIMESTAMP" \
107
+ --arg released_by "$RELEASED_BY" \
108
+ --arg platform_state "$PLATFORM_STATE" \
109
+ --arg git_tag "$GIT_TAG" \
110
+ --arg commit_sha "$COMMIT_SHA" \
111
+ --argjson release_notes_path "$NOTES_JSON" \
112
+ '{
113
+ id: $id,
114
+ version: $version,
115
+ target: $target,
116
+ adapter: $adapter,
117
+ timestamp: $timestamp,
118
+ released_by: $released_by,
119
+ release_notes_path: $release_notes_path,
120
+ platform_state: $platform_state,
121
+ git_tag: $git_tag,
122
+ commit_sha: $commit_sha
123
+ }')"
124
+
125
+ publish_append_history_entry "$HISTORY_FILE" "$ENTRY_JSON"
126
+ printf '🧾 Publish ledger entry appended for %s\n' "$VERSION"