engsys 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 (173) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/core/agents/aaron.md +152 -0
  4. package/core/agents/bert.md +115 -0
  5. package/core/agents/isabelle.md +136 -0
  6. package/core/agents/jody.md +150 -0
  7. package/core/agents/leith.md +111 -0
  8. package/core/agents/marcelo.md +282 -0
  9. package/core/agents/melvin.md +101 -0
  10. package/core/agents/nyx.md +152 -0
  11. package/core/agents/otto.md +168 -0
  12. package/core/agents/patricia.md +283 -0
  13. package/core/commands/design-audit-local.md +155 -0
  14. package/core/commands/design-audit.md +235 -0
  15. package/core/commands/design-critique.md +96 -0
  16. package/core/commands/file-issue.md +22 -0
  17. package/core/commands/generate-project.md +45 -0
  18. package/core/commands/implement-issue.md +37 -0
  19. package/core/commands/implement-project.md +40 -0
  20. package/core/commands/naturalize.md +61 -0
  21. package/core/commands/pre-push.md +29 -0
  22. package/core/commands/prep-review-collect.md +130 -0
  23. package/core/commands/prep-review-finalize.md +121 -0
  24. package/core/commands/prep-review-publish.md +113 -0
  25. package/core/commands/prep-review.md +65 -0
  26. package/core/commands/project-closeout.md +25 -0
  27. package/core/skills/agentic-eval/SKILL.md +195 -0
  28. package/core/skills/chrome-devtools/SKILL.md +97 -0
  29. package/core/skills/code-review/SKILL.md +26 -0
  30. package/core/skills/gh-cli/SKILL.md +2202 -0
  31. package/core/skills/git-commit/SKILL.md +124 -0
  32. package/core/skills/git-workflow-agents/SKILL.md +462 -0
  33. package/core/skills/git-workflow-agents/reference.md +220 -0
  34. package/core/skills/github-actions/SKILL.md +190 -0
  35. package/core/skills/github-issues/SKILL.md +154 -0
  36. package/core/skills/llm-structured-outputs/SKILL.md +323 -0
  37. package/core/skills/llm-structured-outputs/references/provider-details.md +392 -0
  38. package/core/skills/pre-push/SKILL.md +115 -0
  39. package/core/skills/refactor/SKILL.md +645 -0
  40. package/core/skills/web-design-reviewer/SKILL.md +371 -0
  41. package/core/skills/webapp-testing/SKILL.md +127 -0
  42. package/core/skills/webapp-testing/test-helper.js +56 -0
  43. package/core/templates/CLAUDE.md.tmpl +98 -0
  44. package/core/templates/adr-template.md +67 -0
  45. package/core/templates/gh-issue-templates/bug.md +39 -0
  46. package/core/templates/gh-issue-templates/content.md +42 -0
  47. package/core/templates/gh-issue-templates/enhancement.md +36 -0
  48. package/core/templates/gh-issue-templates/feature.md +39 -0
  49. package/core/templates/gh-issue-templates/infrastructure.md +41 -0
  50. package/core/templates/post-edit-reminders.sh.tmpl +19 -0
  51. package/core/templates/settings.json.tmpl +90 -0
  52. package/core/templates/settings.local.json.tmpl +3 -0
  53. package/core/workflows/agent-implementation-workflow.md +346 -0
  54. package/core/workflows/generate-project.md +258 -0
  55. package/core/workflows/implement-project-workflow.md +190 -0
  56. package/core/workflows/issue-tracking.md +89 -0
  57. package/core/workflows/project-closeout-ceremony.md +77 -0
  58. package/core/workflows/review-workflow.md +266 -0
  59. package/engsys.config.example.yaml +46 -0
  60. package/install +202 -0
  61. package/lessons-library/README.md +80 -0
  62. package/lessons-library/async-callbacks-verify-liveness.md +15 -0
  63. package/lessons-library/change-isnt-done-until-every-surface-updated.md +15 -0
  64. package/lessons-library/claim-then-act-for-irreversible-ops.md +16 -0
  65. package/lessons-library/co-commit-entangled-work.md +15 -0
  66. package/lessons-library/dependabot-triage-playbook.md +17 -0
  67. package/lessons-library/deploy-by-digest-and-verify-the-running-revision.md +15 -0
  68. package/lessons-library/enforce-your-guarantee-at-your-boundary.md +16 -0
  69. package/lessons-library/gate-changes-on-measurement-not-vibes.md +15 -0
  70. package/lessons-library/iac-first-no-console-changes.md +15 -0
  71. package/lessons-library/independent-objective-review-gate.md +15 -0
  72. package/lessons-library/keep-an-immutable-source-of-truth.md +15 -0
  73. package/lessons-library/long-agent-runs-checkpoint-not-poll.md +15 -0
  74. package/lessons-library/model-identity-with-stable-ids-and-provenance.md +15 -0
  75. package/lessons-library/operator-choices-are-first-class.md +15 -0
  76. package/lessons-library/prefer-tool-enforced-structured-output.md +15 -0
  77. package/lessons-library/prove-causation-before-acting.md +15 -0
  78. package/lessons-library/re-read-state-before-acting.md +14 -0
  79. package/lessons-library/read-layer-tolerates-unbackfilled-rows.md +15 -0
  80. package/lessons-library/shell-safety-pipefail-and-validate-before-teardown.md +14 -0
  81. package/lessons-library/shift-correctness-left-and-distrust-false-greens.md +15 -0
  82. package/lessons-library/stray-control-bytes-hide-changes.md +14 -0
  83. package/lessons-library/tests-can-assert-the-bug.md +15 -0
  84. package/lessons-library/verify-ground-truth-not-reports.md +15 -0
  85. package/lessons-library/worktrees-need-bootstrap-from-origin-main.md +15 -0
  86. package/lib/commands.js +356 -0
  87. package/lib/generate-team-avatars.mjs +251 -0
  88. package/lib/manifest.js +155 -0
  89. package/lib/render.js +135 -0
  90. package/lib/selftest.js +90 -0
  91. package/lib/util.js +89 -0
  92. package/lib/yaml.js +156 -0
  93. package/optional-agents/gary.md +86 -0
  94. package/optional-agents/jos.md +136 -0
  95. package/optional-agents/sandy.md +101 -0
  96. package/optional-agents/steve.md +161 -0
  97. package/package.json +43 -0
  98. package/stacks/cloud/aws/claude.fragment.md +17 -0
  99. package/stacks/cloud/aws/settings.fragment.json +39 -0
  100. package/stacks/cloud/aws/skills/aws-deployment-preflight/SKILL.md +165 -0
  101. package/stacks/cloud/aws/skills/cloud-architecture-aws/SKILL.md +265 -0
  102. package/stacks/cloud/azure/claude.fragment.md +17 -0
  103. package/stacks/cloud/azure/settings.fragment.json +45 -0
  104. package/stacks/cloud/azure/skills/azure-deployment-preflight/SKILL.md +175 -0
  105. package/stacks/cloud/azure/skills/cloud-architecture-azure/SKILL.md +211 -0
  106. package/stacks/cloud/cloudflare/claude.fragment.md +21 -0
  107. package/stacks/cloud/cloudflare/settings.fragment.json +31 -0
  108. package/stacks/cloud/cloudflare/skills/cloud-architecture-cloudflare/SKILL.md +294 -0
  109. package/stacks/cloud/cloudflare/skills/cloudflare-deployment-preflight/SKILL.md +175 -0
  110. package/stacks/cloud/gcp/claude.fragment.md +17 -0
  111. package/stacks/cloud/gcp/settings.fragment.json +40 -0
  112. package/stacks/cloud/gcp/skills/cloud-architecture-gcp/SKILL.md +208 -0
  113. package/stacks/cloud/gcp/skills/gcp-deployment-preflight/SKILL.md +137 -0
  114. package/stacks/db/mongo/skills/mongo-conventions/SKILL.md +96 -0
  115. package/stacks/db/prisma/claude.fragment.md +49 -0
  116. package/stacks/db/prisma/skills/docker-database-package-copy/SKILL.md +44 -0
  117. package/stacks/db/prisma/skills/prisma-conventions/SKILL.md +37 -0
  118. package/stacks/domain/mobile-growth/skills/apple-ads/SKILL.md +184 -0
  119. package/stacks/domain/mobile-growth/skills/apple-ads/references/benchmark-notes.md +47 -0
  120. package/stacks/domain/mobile-growth/skills/apple-ads/references/official-links.md +53 -0
  121. package/stacks/domain/mobile-growth/skills/google-play-growth/SKILL.md +197 -0
  122. package/stacks/domain/mobile-growth/skills/google-play-growth/references/benchmark-notes.md +47 -0
  123. package/stacks/domain/mobile-growth/skills/google-play-growth/references/official-links.md +45 -0
  124. package/stacks/iac/bicep/claude.fragment.md +14 -0
  125. package/stacks/iac/bicep/settings.fragment.json +20 -0
  126. package/stacks/iac/bicep/skills/iac-bicep/SKILL.md +113 -0
  127. package/stacks/iac/cdk/claude.fragment.md +14 -0
  128. package/stacks/iac/cdk/settings.fragment.json +23 -0
  129. package/stacks/iac/cdk/skills/iac-cdk/SKILL.md +104 -0
  130. package/stacks/iac/terraform/claude.fragment.md +13 -0
  131. package/stacks/iac/terraform/settings.fragment.json +25 -0
  132. package/stacks/iac/terraform/skills/iac-terraform/SKILL.md +93 -0
  133. package/stacks/iac/terraform/skills/terraform-conventions/SKILL.md +87 -0
  134. package/stacks/lang/kotlin/skills/android-testing/SKILL.md +263 -0
  135. package/stacks/lang/kotlin/skills/jetpack-compose/SKILL.md +264 -0
  136. package/stacks/lang/kotlin/skills/kotlin-coroutines/SKILL.md +329 -0
  137. package/stacks/lang/python/skills/python-conventions/SKILL.md +61 -0
  138. package/stacks/lang/shell/skills/shell-scripting/SKILL.md +110 -0
  139. package/stacks/lang/swift/skills/swift-concurrency/SKILL.md +423 -0
  140. package/stacks/lang/swift/skills/swift-concurrency/references/approachable-concurrency.md +80 -0
  141. package/stacks/lang/swift/skills/swift-concurrency/references/concurrency-patterns.md +233 -0
  142. package/stacks/lang/swift/skills/swift-concurrency/references/swiftui-concurrency.md +187 -0
  143. package/stacks/lang/swift/skills/swift-concurrency/references/synchronization-primitives.md +341 -0
  144. package/stacks/lang/swift/skills/swift-testing/SKILL.md +497 -0
  145. package/stacks/lang/swift/skills/swift-testing/references/testing-advanced.md +106 -0
  146. package/stacks/lang/swift/skills/swift-testing/references/testing-patterns.md +504 -0
  147. package/stacks/lang/swift/skills/swiftdata/SKILL.md +334 -0
  148. package/stacks/lang/swift/skills/swiftdata/references/core-data-coexistence.md +504 -0
  149. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-advanced.md +975 -0
  150. package/stacks/lang/swift/skills/swiftdata/references/swiftdata-queries.md +675 -0
  151. package/stacks/lang/swift/skills/swiftui-patterns/SKILL.md +371 -0
  152. package/stacks/lang/swift/skills/swiftui-patterns/references/architecture-patterns.md +486 -0
  153. package/stacks/lang/swift/skills/swiftui-patterns/references/deprecated-migration.md +1097 -0
  154. package/stacks/lang/swift/skills/swiftui-patterns/references/design-polish.md +780 -0
  155. package/stacks/lang/swift/skills/swiftui-patterns/references/platform-and-sharing.md +696 -0
  156. package/stacks/lang/typescript/skills/typescript-conventions/SKILL.md +91 -0
  157. package/stacks/platform/android/claude.fragment.md +40 -0
  158. package/stacks/platform/android/hooks/pre-push-gradle.sh +70 -0
  159. package/stacks/platform/android/settings.fragment.json +13 -0
  160. package/stacks/platform/android/skills/android-build-conventions/SKILL.md +247 -0
  161. package/stacks/platform/ios/claude.fragment.md +24 -0
  162. package/stacks/platform/ios/hooks/pre-push-xcodebuild.sh +82 -0
  163. package/stacks/platform/ios/settings.fragment.json +21 -0
  164. package/stacks/platform/ios/skills/xcodebuildmcp-simulator-logs/SKILL.md +76 -0
  165. package/stacks/platform/web/skills/frontend-testing/SKILL.md +246 -0
  166. package/stacks/platform/web/skills/react-conventions/SKILL.md +261 -0
  167. package/stacks/platform/web/skills/web-platform-conventions/SKILL.md +55 -0
  168. package/stacks/tooling/issue-tracker-github/claude.fragment.md +10 -0
  169. package/stacks/tooling/issue-tracker-github/settings.fragment.json +24 -0
  170. package/stacks/tooling/issue-tracker-github/skills/issue-tracker-github/SKILL.md +278 -0
  171. package/stacks/tooling/issue-tracker-linear/claude.fragment.md +17 -0
  172. package/stacks/tooling/issue-tracker-linear/settings.fragment.json +9 -0
  173. package/stacks/tooling/issue-tracker-linear/skills/issue-tracker-linear/SKILL.md +183 -0
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: typescript-conventions
3
+ description: TypeScript conventions for this repo (TS 5.x targeting ES2022, Node 22, pnpm workspaces). Use when writing or reviewing any *.ts file. Covers naming, type-system expectations, async/error handling, the pnpm-monorepo @smithy hoisting gotcha, and lib-DOM-for-fetch.
4
+ ---
5
+
6
+ # TypeScript Conventions
7
+
8
+ Applies to: `*.ts` across the repo's TypeScript packages. Target: TS 5.x → ES2022. Runtime: Node 22. Build: esbuild (or the project's bundler). Test: Vitest.
9
+
10
+ > Naturalize: confirm the exact package paths, bundler, and test runner in `CLAUDE.md`. The defaults below assume a pnpm workspace shipping to a Node 22 runtime.
11
+
12
+ ## Core Intent
13
+
14
+ - Respect existing architecture; extend abstractions before inventing new ones.
15
+ - Readable, explicit solutions over clever ones.
16
+ - Pure ES modules — never `require` / `module.exports` / CJS helpers.
17
+
18
+ ## Naming & Style
19
+
20
+ - kebab-case filenames (`user-session.ts`, `data-service.ts`).
21
+ - PascalCase for classes, interfaces, enums, type aliases. camelCase for everything else.
22
+ - No `I`-prefix on interfaces.
23
+ - Name for behavior/domain meaning, not implementation.
24
+
25
+ ## Type System
26
+
27
+ - Avoid `any` (implicit or explicit). Prefer `unknown` + narrowing.
28
+ - Discriminated unions for state machines and event payloads.
29
+ - Centralize shared contracts in a `shared`/`common` package rather than duplicating across packages.
30
+ - Use TS utility types (`Readonly`, `Partial`, `Record`, etc.) to express intent.
31
+
32
+ ## Async, Events, Errors
33
+
34
+ - `async/await` with try/catch and structured errors.
35
+ - Guard edge cases early to avoid deep nesting.
36
+ - Route errors through the project's logging/telemetry utilities.
37
+
38
+ ## Architecture
39
+
40
+ - Single-purpose modules.
41
+ - Keep transport / domain / presentation layers decoupled.
42
+ - Instantiate clients (cloud SDKs, DB clients, etc.) outside hot paths; inject for testability.
43
+
44
+ ## pnpm monorepo: known gotchas
45
+
46
+ If the repo uses pnpm workspaces, two patterns repeatedly bite:
47
+
48
+ ### `@smithy/*` hoisting for the AWS SDK v3
49
+
50
+ pnpm's virtual store (`.pnpm/`) is **not** traversed by TypeScript's module resolution. `@smithy/smithy-client` is the base class that provides `Client.send()` on every AWS SDK v3 client. Without hoisting you get:
51
+
52
+ ```
53
+ Property 'send' does not exist on type 'SQSClient'
54
+ Property 'send' does not exist on type 'SecretsManagerClient'
55
+ ```
56
+
57
+ **Fix:** root `.npmrc` must contain:
58
+
59
+ ```
60
+ public-hoist-pattern[]=@smithy*
61
+ ```
62
+
63
+ Then `pnpm install`. (The same class of fix applies to any SDK that ships a base client via a separate hoisted package.)
64
+
65
+ ### `DOM` lib for fetch / Response / Headers
66
+
67
+ Any handler that uses `fetch()`, `Response`, or `Headers` needs `"DOM"` in `tsconfig` `lib`:
68
+
69
+ ```json
70
+ { "compilerOptions": { "lib": ["ES2022", "DOM"] } }
71
+ ```
72
+
73
+ Without it: `Property 'ok' does not exist on type 'Response'`.
74
+
75
+ ## Testing
76
+
77
+ - Vitest. Add or update unit tests alongside changes.
78
+ - Avoid brittle timing assertions; prefer fake timers or injected clocks.
79
+
80
+ ## Build / verification
81
+
82
+ - `pnpm typecheck` at the root runs `tsc --noEmit` across packages.
83
+ - `pnpm lint`, `pnpm test`, `pnpm build` work per-package via `pnpm -r`.
84
+ - `esbuild` bundles for the deploy target; uses the `default` export condition.
85
+
86
+ ## Security
87
+
88
+ - Validate external input with schema validators or type guards (prefer a `zod`-style schema layer in the shared package).
89
+ - Parameterized queries for any persistence; never string-concatenate untrusted input into queries.
90
+ - Secrets via the cloud secret store (Secrets Manager / Key Vault / Secret Manager), never env-var-in-code-comment.
91
+ - Patch dependencies promptly; pin vulnerable transitives via `pnpm overrides` in root `package.json`.
@@ -0,0 +1,40 @@
1
+ <!-- pack: platform/android -->
2
+ ## Mobile platform — Android
3
+
4
+ This project ships an Android app (Kotlin / Jetpack Compose). The Kotlin/Android
5
+ framework conventions live in the `lang/kotlin` skills (`kotlin-coroutines`,
6
+ `jetpack-compose`, `android-testing`); Gary is the mobile experience architect.
7
+
8
+ ### Local Gradle gate (no CI for Android)
9
+
10
+ Android has **no cloud CI for the app build** — the gate is a local `./gradlew`
11
+ run. Run it before reporting any Android change complete:
12
+
13
+ ```
14
+ ./gradlew <!-- naturalize: :app: -->assembleDebug
15
+ ./gradlew <!-- naturalize: :app: -->testDebugUnitTest
16
+ ./gradlew <!-- naturalize: :app: -->lintDebug
17
+ ```
18
+
19
+ Always use the project's `./gradlew` wrapper, never a global `gradle`.
20
+ Instrumented tests (`connectedDebugAndroidTest`) need an emulator and are not part
21
+ of the fast gate.
22
+
23
+ The `pre-push-gradle.sh` hook (this pack) runs assemble + unit tests + lint
24
+ automatically when Android files change on push. Install once per clone with
25
+ `git config core.hooksPath .githooks`. **Do not bypass with `--no-verify`.** The
26
+ hook reads `ANDROID_DIR`, `ANDROID_PATH_PREFIX`, and `GRADLE_MODULE` knobs.
27
+
28
+ ### MCP servers
29
+
30
+ None required. `settings.fragment.json` allows read-only Gradle inspection
31
+ (`./gradlew tasks`, `projects`, `help`, `dependencies`) and `adb devices` without
32
+ prompting; it adds no Android MCP server.
33
+
34
+ <!-- naturalize: record these Android project facts in the Project facts section:
35
+ - Gradle project root / ANDROID_DIR (dir containing ./gradlew)
36
+ - app module name (GRADLE_MODULE, e.g. :app)
37
+ - applicationId (e.g. com.example.app)
38
+ - minSdk / targetSdk / compileSdk
39
+ - product flavors / build variants, if any
40
+ -->
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+ # pre-push hook — runs a fast Gradle gate when Android files are being pushed.
3
+ # Prevents broken Android builds from reaching the default branch.
4
+ #
5
+ # Gate: assembleDebug + unit tests + lint (no emulator required).
6
+ #
7
+ # Install once per clone:
8
+ # git config core.hooksPath .githooks
9
+ #
10
+ # Naturalize the project facts below to match this repo:
11
+ # ANDROID_PATH_PREFIX — repo-relative path that, when touched, should trigger the gate
12
+ # ANDROID_DIR — directory containing ./gradlew (Gradle project root)
13
+ # GRADLE_MODULE — the app module (e.g. ":app"); leave empty for a single-module root
14
+
15
+ set -euo pipefail
16
+
17
+ # --- Project facts (naturalize) ---------------------------------------------
18
+ ANDROID_PATH_PREFIX="${ANDROID_PATH_PREFIX:-packages/android/}"
19
+ ANDROID_DIR="${ANDROID_DIR:-packages/android}"
20
+ GRADLE_MODULE="${GRADLE_MODULE:-:app}"
21
+ # ----------------------------------------------------------------------------
22
+
23
+ REMOTE="${1:-}"
24
+ REMOTE_URL="${2:-}"
25
+
26
+ ANDROID_CHANGED=0
27
+
28
+ while read -r local_ref local_sha remote_ref remote_sha; do
29
+ # New branch — compare against empty tree
30
+ if [[ "$remote_sha" == "0000000000000000000000000000000000000000" ]]; then
31
+ base=$(git hash-object -t tree /dev/null)
32
+ else
33
+ base="$remote_sha"
34
+ fi
35
+
36
+ if git diff --name-only "$base" "$local_sha" 2>/dev/null | grep -q "^${ANDROID_PATH_PREFIX}"; then
37
+ ANDROID_CHANGED=1
38
+ break
39
+ fi
40
+ done
41
+
42
+ if [[ "$ANDROID_CHANGED" -eq 0 ]]; then
43
+ exit 0
44
+ fi
45
+
46
+ # Resolve task names. With a module prefix, tasks are e.g. ":app:assembleDebug".
47
+ ASSEMBLE_TASK="${GRADLE_MODULE:+$GRADLE_MODULE:}assembleDebug"
48
+ TEST_TASK="${GRADLE_MODULE:+$GRADLE_MODULE:}testDebugUnitTest"
49
+ LINT_TASK="${GRADLE_MODULE:+$GRADLE_MODULE:}lintDebug"
50
+
51
+ GRADLEW="./gradlew"
52
+ if [[ ! -x "${ANDROID_DIR}/${GRADLEW}" && ! -f "${ANDROID_DIR}/gradlew" ]]; then
53
+ echo "⚠️ No gradlew found in ${ANDROID_DIR} — skipping Android gate."
54
+ echo " Naturalize ANDROID_DIR to the Gradle project root to enable it."
55
+ exit 0
56
+ fi
57
+
58
+ echo "🤖 Android files changed — running the Gradle gate (assemble + tests + lint)..."
59
+ echo ""
60
+
61
+ if ! (cd "$ANDROID_DIR" && ./gradlew "$ASSEMBLE_TASK" "$TEST_TASK" "$LINT_TASK" --console=plain) 2>&1; then
62
+ echo ""
63
+ echo "❌ Gradle gate failed. Fix the build/tests/lint before pushing."
64
+ echo " Reproduce: (cd ${ANDROID_DIR} && ./gradlew ${ASSEMBLE_TASK} ${TEST_TASK} ${LINT_TASK})"
65
+ exit 1
66
+ fi
67
+
68
+ echo ""
69
+ echo "✅ Android gate passed (assembleDebug + unit tests + lint)."
70
+ exit 0
@@ -0,0 +1,13 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(./gradlew tasks:*)",
5
+ "Bash(./gradlew projects:*)",
6
+ "Bash(./gradlew help:*)",
7
+ "Bash(./gradlew dependencies:*)",
8
+ "Bash(./gradlew :*:dependencies:*)",
9
+ "Bash(adb devices:*)"
10
+ ]
11
+ },
12
+ "mcpServers": {}
13
+ }
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: android-build-conventions
3
+ description: "Configure and verify Android Gradle builds: Kotlin DSL (build.gradle.kts), version catalogs (libs.versions.toml), build types and product flavors, R8/ProGuard shrinking and keep rules, signing configs, and the local build/verify gate (./gradlew assembleDebug, testDebugUnitTest, lintDebug). Use when editing Gradle build files, adding dependencies, setting up flavors/variants, configuring minification or signing, or running the Android build/test/lint gate before reporting a change complete."
4
+ ---
5
+
6
+ # Android Build Conventions
7
+
8
+ Configure, maintain, and verify Android builds with Gradle's Kotlin DSL targeting
9
+ AGP 8.x+ and Gradle 8.x+. Use the version catalog as the single source of truth
10
+ for dependencies, keep build logic declarative, and **always run the local gate**
11
+ before reporting an Android change complete — Android typically has no cloud CI
12
+ for the app build, so the local `./gradlew` run is the gate.
13
+
14
+ ## Contents
15
+
16
+ - [Local Build / Verify Gate](#local-build--verify-gate)
17
+ - [Kotlin DSL Conventions](#kotlin-dsl-conventions)
18
+ - [Version Catalogs](#version-catalogs)
19
+ - [Build Types and Product Flavors](#build-types-and-product-flavors)
20
+ - [R8 / ProGuard](#r8--proguard)
21
+ - [Signing](#signing)
22
+ - [Convention Plugins](#convention-plugins)
23
+ - [Common Mistakes](#common-mistakes)
24
+ - [Review Checklist](#review-checklist)
25
+
26
+ ## Local Build / Verify Gate
27
+
28
+ Run these before reporting any Android change complete. They are fast, local, and
29
+ the de-facto gate (there is no cloud CI for the app build):
30
+
31
+ ```
32
+ ./gradlew assembleDebug # compile + package the debug APK
33
+ ./gradlew testDebugUnitTest # run JVM unit tests (src/test)
34
+ ./gradlew lintDebug # Android Lint static analysis
35
+ ```
36
+
37
+ Naturalize the module if the app isn't the root project (e.g.
38
+ `./gradlew :app:assembleDebug`). The `pre-push-gradle.sh` hook in this pack runs
39
+ this gate automatically when Android files change on push — **do not bypass it
40
+ with `--no-verify`.**
41
+
42
+ Notes:
43
+ - Use the project's `./gradlew` wrapper, never a globally installed `gradle`.
44
+ - Instrumented tests (`connectedDebugAndroidTest`) need an emulator/device and are
45
+ **not** part of the fast gate — run them on demand.
46
+ - For a deeper check before release, add `./gradlew assembleRelease` (exercises
47
+ R8/minification).
48
+
49
+ ## Kotlin DSL Conventions
50
+
51
+ Use `build.gradle.kts` (Kotlin DSL), not Groovy, for new build files.
52
+
53
+ ```kotlin
54
+ plugins {
55
+ alias(libs.plugins.android.application)
56
+ alias(libs.plugins.kotlin.android)
57
+ alias(libs.plugins.kotlin.compose) // Compose compiler plugin (Kotlin 2.0+)
58
+ }
59
+
60
+ android {
61
+ namespace = "com.example.app"
62
+ compileSdk = 35
63
+
64
+ defaultConfig {
65
+ applicationId = "com.example.app"
66
+ minSdk = 26
67
+ targetSdk = 35
68
+ versionCode = 1
69
+ versionName = "1.0.0"
70
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
71
+ }
72
+
73
+ buildFeatures { compose = true }
74
+
75
+ kotlin { jvmToolchain(17) } // pin the JDK toolchain
76
+ }
77
+
78
+ dependencies {
79
+ implementation(platform(libs.androidx.compose.bom))
80
+ implementation(libs.androidx.compose.material3)
81
+ implementation(libs.androidx.activity.compose)
82
+ testImplementation(libs.junit)
83
+ testImplementation(libs.turbine)
84
+ androidTestImplementation(libs.androidx.compose.ui.test.junit4)
85
+ }
86
+ ```
87
+
88
+ - Declare the Compose BOM with `platform(...)` so all Compose artifacts share one
89
+ version. Apply the Compose compiler plugin (`org.jetbrains.kotlin.plugin.compose`)
90
+ on Kotlin 2.0+.
91
+ - Pin the JDK with `jvmToolchain(17)` (or the project's standard) for reproducible
92
+ builds across machines.
93
+ - Prefer `implementation` over `api` unless the dependency is part of a module's
94
+ public API — it keeps the build graph fast.
95
+
96
+ ## Version Catalogs
97
+
98
+ Keep all versions, libraries, and plugins in `gradle/libs.versions.toml`. This is
99
+ the single source of truth; never hardcode versions in `build.gradle.kts`.
100
+
101
+ ```toml
102
+ [versions]
103
+ agp = "8.7.0"
104
+ kotlin = "2.1.0"
105
+ composeBom = "2025.01.00"
106
+ coroutines = "1.9.0"
107
+ turbine = "1.2.0"
108
+
109
+ [libraries]
110
+ androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
111
+ androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
112
+ androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.9.3" }
113
+ kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
114
+ turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" }
115
+ junit = { group = "junit", name = "junit", version = "4.13.2" }
116
+
117
+ [plugins]
118
+ android-application = { id = "com.android.application", version.ref = "agp" }
119
+ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
120
+ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
121
+ ```
122
+
123
+ - Reference libraries as `libs.androidx.compose.material3` and plugins as
124
+ `alias(libs.plugins.android.application)`.
125
+ - Use `[bundles]` to group related dependencies that are always added together.
126
+ - BOM-managed artifacts (Compose) omit a version — the BOM resolves it.
127
+
128
+ ## Build Types and Product Flavors
129
+
130
+ `buildTypes` vary how the **same** code is built (debug vs release). `flavors`
131
+ vary **what** is built (free vs paid, dev vs prod backend). Their product is a
132
+ build *variant* (e.g. `freeDebug`).
133
+
134
+ ```kotlin
135
+ android {
136
+ buildTypes {
137
+ debug {
138
+ applicationIdSuffix = ".debug" // install alongside release
139
+ isDebuggable = true
140
+ }
141
+ release {
142
+ isMinifyEnabled = true
143
+ isShrinkResources = true
144
+ proguardFiles(
145
+ getDefaultProguardFile("proguard-android-optimize.txt"),
146
+ "proguard-rules.pro",
147
+ )
148
+ signingConfig = signingConfigs.getByName("release")
149
+ }
150
+ }
151
+
152
+ flavorDimensions += "environment"
153
+ productFlavors {
154
+ create("dev") { dimension = "environment"; applicationIdSuffix = ".dev" }
155
+ create("prod") { dimension = "environment" }
156
+ }
157
+ }
158
+ ```
159
+
160
+ - Use `applicationIdSuffix` on debug/dev so multiple variants coexist on a device.
161
+ - Keep flavor-specific code/resources in `src/<flavor>/` source sets.
162
+ - Put environment config (base URLs, keys) in `buildConfigField` or resource
163
+ overlays, not in code branches.
164
+
165
+ ## R8 / ProGuard
166
+
167
+ R8 is the default shrinker/optimizer/obfuscator for release builds (replaces
168
+ ProGuard but reads the same `proguard-rules.pro` keep rules).
169
+
170
+ - Enable for release: `isMinifyEnabled = true` and `isShrinkResources = true`.
171
+ - Add **keep rules** for anything accessed reflectively: serialization models,
172
+ JNI, code referenced only by manifest/XML, libraries that need them.
173
+
174
+ ```proguard
175
+ # Keep models used by kotlinx.serialization / Gson reflection.
176
+ -keep,allowobfuscation class com.example.app.model.** { *; }
177
+ # Keep enums used reflectively.
178
+ -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
179
+ ```
180
+
181
+ - Most modern libraries ship **consumer keep rules**, so you usually need few
182
+ manual rules — add them only when release builds crash where debug doesn't.
183
+ - Always test a **release** build before shipping; minification bugs don't appear
184
+ in `assembleDebug`.
185
+ - Enable obfuscation mapping retention so crash reports can be de-obfuscated.
186
+
187
+ ## Signing
188
+
189
+ - **Never commit keystores or passwords.** Inject signing credentials from
190
+ `local.properties`, environment variables, or a secrets manager.
191
+
192
+ ```kotlin
193
+ val keystoreProps = Properties().apply {
194
+ val f = rootProject.file("keystore.properties")
195
+ if (f.exists()) load(f.inputStream())
196
+ }
197
+
198
+ android {
199
+ signingConfigs {
200
+ create("release") {
201
+ storeFile = keystoreProps["storeFile"]?.let { file(it as String) }
202
+ storePassword = keystoreProps["storePassword"] as String?
203
+ keyAlias = keystoreProps["keyAlias"] as String?
204
+ keyPassword = keystoreProps["keyPassword"] as String?
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ - Add `keystore.properties` and `*.keystore`/`*.jks` to `.gitignore`.
211
+ - Debug builds use the auto-generated debug keystore — fine for local work.
212
+
213
+ ## Convention Plugins
214
+
215
+ For multi-module projects, extract shared build logic into convention plugins in
216
+ `build-logic/` (composite build) rather than copy-pasting config across modules.
217
+ This keeps module `build.gradle.kts` files small and consistent. Avoid the legacy
218
+ top-level `allprojects {}`/`subprojects {}` blocks.
219
+
220
+ ## Common Mistakes
221
+
222
+ 1. **Hardcoded versions** in `build.gradle.kts` — use the version catalog.
223
+ 2. **Groovy DSL for new files** — use Kotlin DSL (`.kts`).
224
+ 3. **Not running the local gate** before reporting done — always
225
+ `assembleDebug` + `testDebugUnitTest` + `lintDebug`.
226
+ 4. **Using a system `gradle`** instead of `./gradlew`.
227
+ 5. **Committing keystores/passwords** — inject them; gitignore secrets.
228
+ 6. **Shipping without testing a release build** — R8 issues only surface there.
229
+ 7. **Missing keep rules** for reflective code — release crashes, debug fine.
230
+ 8. **`api` everywhere** — leaks the dependency graph and slows builds; default to
231
+ `implementation`.
232
+ 9. **Mismatched Compose compiler / Kotlin versions** — apply the Compose compiler
233
+ plugin matched to the Kotlin version (Kotlin 2.0+).
234
+ 10. **No JDK toolchain pin** — builds differ across machines; set `jvmToolchain`.
235
+
236
+ ## Review Checklist
237
+
238
+ - [ ] Build files use Kotlin DSL (`.kts`)
239
+ - [ ] All versions/libs/plugins live in `gradle/libs.versions.toml`
240
+ - [ ] Compose BOM declared via `platform(...)`; compiler plugin matched to Kotlin
241
+ - [ ] `release` build enables R8 (`isMinifyEnabled`, `isShrinkResources`)
242
+ - [ ] Keep rules present for reflective/serialized types
243
+ - [ ] Signing credentials injected, not committed; keystores gitignored
244
+ - [ ] JDK toolchain pinned (`jvmToolchain`)
245
+ - [ ] Local gate run: `assembleDebug` + `testDebugUnitTest` + `lintDebug`
246
+ - [ ] A release build was assembled before shipping
247
+ - [ ] Variants coexist on device (`applicationIdSuffix` on debug/dev)
@@ -0,0 +1,24 @@
1
+ <!-- pack: platform/ios -->
2
+ ## iOS platform
3
+
4
+ This project ships an iOS app (Swift / SwiftUI). The Swift framework conventions live in the `lang/swift` skills (`swift-concurrency`, `swiftui-patterns`, `swift-testing`, `swiftdata`); Gary is the iOS experience architect.
5
+
6
+ ### Local xcodebuild gate (no CI for iOS)
7
+
8
+ iOS has **no CI workflow** — the only gate is a local `xcodebuild`. Run it before reporting any iOS change complete:
9
+
10
+ ```
11
+ xcodebuild -project <!-- naturalize: path/to/App.xcodeproj --> \
12
+ -scheme <!-- naturalize: scheme --> -configuration Debug \
13
+ -destination 'generic/platform=iOS Simulator' \
14
+ CODE_SIGNING_ALLOWED=NO build
15
+ ```
16
+
17
+ The `pre-push-xcodebuild.sh` hook (this pack) runs SwiftLint + `xcodebuild build-for-testing` automatically when iOS files change. Install once per clone with `git config core.hooksPath .githooks`. **Do not bypass with `--no-verify`.**
18
+
19
+ ### MCP servers
20
+
21
+ - **xcodebuildmcp** — drives Simulator builds/runs and log capture without manual copy/paste (see the `xcodebuildmcp-simulator-logs` skill). Configure `XCODEBUILDMCP_PROJECT_PATH`, `_SCHEME`, `_SIMULATOR_ID`, and `_BUNDLE_ID` in `settings.local.json`.
22
+ - **sentry** — runtime crash/error triage for the shipped app.
23
+
24
+ <!-- naturalize: record the iOS project path, scheme, bundle id, and simulator UDID in the Project facts section. -->
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env bash
2
+ # pre-push hook — runs SwiftLint + xcodebuild when iOS files are being pushed.
3
+ # Prevents broken iOS builds from reaching CI / the default branch.
4
+ #
5
+ # Install once per clone:
6
+ # git config core.hooksPath .githooks
7
+ #
8
+ # Naturalize the three project facts below to match this repo:
9
+ # IOS_PATH_PREFIX — repo-relative path that, when touched, should trigger a build
10
+ # PROJECT — path to the .xcodeproj (or use -workspace + WORKSPACE)
11
+ # SCHEME — the build scheme
12
+ # LINT_DIR — directory to run swiftlint from (so .swiftlint.yml resolves)
13
+
14
+ set -euo pipefail
15
+
16
+ # --- Project facts (naturalize) ---------------------------------------------
17
+ IOS_PATH_PREFIX="packages/ios/"
18
+ PROJECT="packages/ios/App/App.xcodeproj"
19
+ SCHEME="App"
20
+ LINT_DIR="packages/ios/App"
21
+ # ----------------------------------------------------------------------------
22
+
23
+ REMOTE="${1:-}"
24
+ REMOTE_URL="${2:-}"
25
+
26
+ IOS_CHANGED=0
27
+
28
+ while read -r local_ref local_sha remote_ref remote_sha; do
29
+ # New branch — compare against empty tree
30
+ if [[ "$remote_sha" == "0000000000000000000000000000000000000000" ]]; then
31
+ base=$(git hash-object -t tree /dev/null)
32
+ else
33
+ base="$remote_sha"
34
+ fi
35
+
36
+ if git diff --name-only "$base" "$local_sha" 2>/dev/null | grep -q "^${IOS_PATH_PREFIX}"; then
37
+ IOS_CHANGED=1
38
+ break
39
+ fi
40
+ done
41
+
42
+ if [[ "$IOS_CHANGED" -eq 0 ]]; then
43
+ exit 0
44
+ fi
45
+
46
+ echo "📱 iOS files changed — running SwiftLint before push..."
47
+ echo ""
48
+
49
+ if command -v swiftlint &>/dev/null; then
50
+ # Run from the iOS project dir so .swiftlint.yml is resolved correctly
51
+ # and its included/excluded paths are respected.
52
+ if ! (cd "$LINT_DIR" && swiftlint lint --quiet --reporter emoji) 2>&1; then
53
+ echo ""
54
+ echo "❌ SwiftLint failed. Fix the violations before pushing."
55
+ exit 1
56
+ fi
57
+ echo "✅ SwiftLint passed."
58
+ else
59
+ echo "⚠️ swiftlint not found — skipping lint (brew install swiftlint to enable)."
60
+ fi
61
+
62
+ echo ""
63
+ echo "🔨 Running xcodebuild build-for-testing..."
64
+ echo ""
65
+
66
+ if ! xcodebuild build-for-testing \
67
+ -project "$PROJECT" \
68
+ -scheme "$SCHEME" \
69
+ -destination "generic/platform=iOS Simulator" \
70
+ -configuration Debug \
71
+ CODE_SIGNING_ALLOWED=NO \
72
+ ASSETCATALOG_COMPILER_SKIP_APP_STORE_DEPLOYMENT=YES \
73
+ -quiet 2>&1; then
74
+ echo ""
75
+ echo "❌ xcodebuild build-for-testing failed. Fix the build before pushing."
76
+ echo " Run with more detail: xcodebuild build-for-testing -project $PROJECT -scheme $SCHEME -destination 'generic/platform=iOS Simulator' -configuration Debug CODE_SIGNING_ALLOWED=NO"
77
+ exit 1
78
+ fi
79
+
80
+ echo ""
81
+ echo "✅ iOS build (including tests) passed."
82
+ exit 0
@@ -0,0 +1,21 @@
1
+ {
2
+ "mcpServers": {
3
+ "xcodebuildmcp": {
4
+ "type": "stdio",
5
+ "command": "npx",
6
+ "args": ["-y", "xcodebuildmcp@latest", "mcp"],
7
+ "env": {
8
+ "XCODEBUILDMCP_ENABLED_WORKFLOWS": "simulator,logging",
9
+ "XCODEBUILDMCP_PROJECT_PATH": "<naturalize: path/to/App.xcodeproj>",
10
+ "XCODEBUILDMCP_SCHEME": "<naturalize: scheme>",
11
+ "XCODEBUILDMCP_PLATFORM": "iOS Simulator",
12
+ "XCODEBUILDMCP_SIMULATOR_ID": "<naturalize: simulator UDID>",
13
+ "XCODEBUILDMCP_BUNDLE_ID": "<naturalize: bundle id>"
14
+ }
15
+ },
16
+ "sentry": {
17
+ "type": "http",
18
+ "url": "https://mcp.sentry.dev/mcp"
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: xcodebuildmcp-simulator-logs
3
+ description: Use XcodeBuildMCP to run iOS simulator workflows and capture logs without manual copy/paste. Use when asked to build or run the iOS app in Simulator, inspect session defaults, start or stop simulator log capture, collect app logs for debugging, or troubleshoot StoreKit and runtime issues from simulator output.
4
+ ---
5
+
6
+ # XcodeBuildMCP Simulator Logs
7
+
8
+ Manage simulator runs and capture logs directly through the XcodeBuildMCP server. The user does not need to paste console output by hand.
9
+
10
+ > Requires the `xcodebuildmcp` MCP server (configured in `.mcp.json`). It will prompt for approval on first use this session.
11
+
12
+ ## When to Use This Skill
13
+
14
+ - The user wants simulator logs captured or analyzed.
15
+ - The user asks to run or verify the iOS app in Simulator.
16
+ - The user wants ongoing debugging without copy/pasting log output.
17
+ - The user is troubleshooting StoreKit, auth, crash, or runtime behavior in Simulator.
18
+
19
+ ## Core Rules
20
+
21
+ 1. Before the first simulator build, run, or test call in a session, call `session_show_defaults`.
22
+ 2. If defaults are missing or wrong, discover projects and schemes first, then proceed with simulator workflows.
23
+ 3. Do **not** call `boot_sim` or `open_sim` as prerequisites for `build_run_sim` — `build_run_sim` handles boot and opening automatically.
24
+ 4. For ongoing logs, call `start_sim_log_cap` and keep the returned `logSessionId` for later retrieval.
25
+ 5. To read captured output, call `stop_sim_log_cap` with the `logSessionId`, then immediately start a new capture if continuous monitoring is needed.
26
+
27
+ ## Workflow: Connect to Simulator Log Stream
28
+
29
+ 1. `session_show_defaults` — verify active project/workspace, scheme, simulator.
30
+ 2. `start_sim_log_cap` with `subsystemFilter: app` unless broader logs are needed.
31
+ 3. Ask the user to reproduce the issue.
32
+ 4. `stop_sim_log_cap` to retrieve logs.
33
+ 5. Parse and summarize findings.
34
+ 6. If debugging continues, start a new capture immediately and keep monitoring.
35
+
36
+ ## Workflow: Build, Run, and Capture
37
+
38
+ 1. `session_show_defaults`.
39
+ 2. If defaults are valid, `build_run_sim`.
40
+ 3. Start simulator log capture.
41
+ 4. Reproduce the target scenario.
42
+ 5. Stop capture and analyze logs.
43
+
44
+ ## Suggested Filters
45
+
46
+ - `app` — primary app and relevant runtime logs.
47
+ - `all` — broad capture for deep investigations.
48
+ - `swiftui` — UI lifecycle or rendering issues.
49
+
50
+ ## Troubleshooting
51
+
52
+ | Symptom | Likely Cause | Action |
53
+ |---|---|---|
54
+ | No logs captured | Capture not started or wrong filter | Start capture again with `subsystemFilter: app` or `all` |
55
+ | `build_run_sim` fails immediately | Missing or wrong session defaults | Run `session_show_defaults`, then set/correct defaults |
56
+ | Logs too noisy | Filter too broad | Use `subsystemFilter: app` |
57
+ | Need continuous monitoring | Capture ended after stop | Restart capture immediately after each stop |
58
+
59
+ ## Notes
60
+
61
+ - Session defaults reduce repeated arguments and keep simulator workflows deterministic.
62
+ - Capture/stop cycles are the practical way to maintain ongoing visibility during iterative debugging.
63
+
64
+ ## Repository Scripts (if the project ships them)
65
+
66
+ Some projects wrap the capture/stop cycle in helper scripts so the loop is one command. If present, check `CLAUDE.md` / `scripts/` for names like:
67
+
68
+ - a `sim-log-capture` script — start, stop, pull, status, clear.
69
+ - a topic-focused pull-and-restart script (e.g. StoreKit logs).
70
+
71
+ ### Recommended Usage
72
+
73
+ 1. Start continuous capture (`start_sim_log_cap`, or the project's capture script).
74
+ 2. Reproduce the issue in Simulator.
75
+ 3. Pull + restart capture.
76
+ 4. Inspect extracted logs (projects commonly write them under `tmp/xcodebuildmcp/`).