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.
- package/LICENSE +21 -0
- package/README.md +202 -0
- package/core/agents/aaron.md +152 -0
- package/core/agents/bert.md +115 -0
- package/core/agents/isabelle.md +136 -0
- package/core/agents/jody.md +150 -0
- package/core/agents/leith.md +111 -0
- package/core/agents/marcelo.md +282 -0
- package/core/agents/melvin.md +101 -0
- package/core/agents/nyx.md +152 -0
- package/core/agents/otto.md +168 -0
- package/core/agents/patricia.md +283 -0
- package/core/commands/design-audit-local.md +155 -0
- package/core/commands/design-audit.md +235 -0
- package/core/commands/design-critique.md +96 -0
- package/core/commands/file-issue.md +22 -0
- package/core/commands/generate-project.md +45 -0
- package/core/commands/implement-issue.md +37 -0
- package/core/commands/implement-project.md +40 -0
- package/core/commands/naturalize.md +61 -0
- package/core/commands/pre-push.md +29 -0
- package/core/commands/prep-review-collect.md +130 -0
- package/core/commands/prep-review-finalize.md +121 -0
- package/core/commands/prep-review-publish.md +113 -0
- package/core/commands/prep-review.md +65 -0
- package/core/commands/project-closeout.md +25 -0
- package/core/skills/agentic-eval/SKILL.md +195 -0
- package/core/skills/chrome-devtools/SKILL.md +97 -0
- package/core/skills/code-review/SKILL.md +26 -0
- package/core/skills/gh-cli/SKILL.md +2202 -0
- package/core/skills/git-commit/SKILL.md +124 -0
- package/core/skills/git-workflow-agents/SKILL.md +462 -0
- package/core/skills/git-workflow-agents/reference.md +220 -0
- package/core/skills/github-actions/SKILL.md +190 -0
- package/core/skills/github-issues/SKILL.md +154 -0
- package/core/skills/llm-structured-outputs/SKILL.md +323 -0
- package/core/skills/llm-structured-outputs/references/provider-details.md +392 -0
- package/core/skills/pre-push/SKILL.md +115 -0
- package/core/skills/refactor/SKILL.md +645 -0
- package/core/skills/web-design-reviewer/SKILL.md +371 -0
- package/core/skills/webapp-testing/SKILL.md +127 -0
- package/core/skills/webapp-testing/test-helper.js +56 -0
- package/core/templates/CLAUDE.md.tmpl +98 -0
- package/core/templates/adr-template.md +67 -0
- package/core/templates/gh-issue-templates/bug.md +39 -0
- package/core/templates/gh-issue-templates/content.md +42 -0
- package/core/templates/gh-issue-templates/enhancement.md +36 -0
- package/core/templates/gh-issue-templates/feature.md +39 -0
- package/core/templates/gh-issue-templates/infrastructure.md +41 -0
- package/core/templates/post-edit-reminders.sh.tmpl +19 -0
- package/core/templates/settings.json.tmpl +90 -0
- package/core/templates/settings.local.json.tmpl +3 -0
- package/core/workflows/agent-implementation-workflow.md +346 -0
- package/core/workflows/generate-project.md +258 -0
- package/core/workflows/implement-project-workflow.md +190 -0
- package/core/workflows/issue-tracking.md +89 -0
- package/core/workflows/project-closeout-ceremony.md +77 -0
- package/core/workflows/review-workflow.md +266 -0
- package/engsys.config.example.yaml +46 -0
- package/install +202 -0
- package/lessons-library/README.md +80 -0
- package/lessons-library/async-callbacks-verify-liveness.md +15 -0
- package/lessons-library/change-isnt-done-until-every-surface-updated.md +15 -0
- package/lessons-library/claim-then-act-for-irreversible-ops.md +16 -0
- package/lessons-library/co-commit-entangled-work.md +15 -0
- package/lessons-library/dependabot-triage-playbook.md +17 -0
- package/lessons-library/deploy-by-digest-and-verify-the-running-revision.md +15 -0
- package/lessons-library/enforce-your-guarantee-at-your-boundary.md +16 -0
- package/lessons-library/gate-changes-on-measurement-not-vibes.md +15 -0
- package/lessons-library/iac-first-no-console-changes.md +15 -0
- package/lessons-library/independent-objective-review-gate.md +15 -0
- package/lessons-library/keep-an-immutable-source-of-truth.md +15 -0
- package/lessons-library/long-agent-runs-checkpoint-not-poll.md +15 -0
- package/lessons-library/model-identity-with-stable-ids-and-provenance.md +15 -0
- package/lessons-library/operator-choices-are-first-class.md +15 -0
- package/lessons-library/prefer-tool-enforced-structured-output.md +15 -0
- package/lessons-library/prove-causation-before-acting.md +15 -0
- package/lessons-library/re-read-state-before-acting.md +14 -0
- package/lessons-library/read-layer-tolerates-unbackfilled-rows.md +15 -0
- package/lessons-library/shell-safety-pipefail-and-validate-before-teardown.md +14 -0
- package/lessons-library/shift-correctness-left-and-distrust-false-greens.md +15 -0
- package/lessons-library/stray-control-bytes-hide-changes.md +14 -0
- package/lessons-library/tests-can-assert-the-bug.md +15 -0
- package/lessons-library/verify-ground-truth-not-reports.md +15 -0
- package/lessons-library/worktrees-need-bootstrap-from-origin-main.md +15 -0
- package/lib/commands.js +356 -0
- package/lib/generate-team-avatars.mjs +251 -0
- package/lib/manifest.js +155 -0
- package/lib/render.js +135 -0
- package/lib/selftest.js +90 -0
- package/lib/util.js +89 -0
- package/lib/yaml.js +156 -0
- package/optional-agents/gary.md +86 -0
- package/optional-agents/jos.md +136 -0
- package/optional-agents/sandy.md +101 -0
- package/optional-agents/steve.md +161 -0
- package/package.json +43 -0
- package/stacks/cloud/aws/claude.fragment.md +17 -0
- package/stacks/cloud/aws/settings.fragment.json +39 -0
- package/stacks/cloud/aws/skills/aws-deployment-preflight/SKILL.md +165 -0
- package/stacks/cloud/aws/skills/cloud-architecture-aws/SKILL.md +265 -0
- package/stacks/cloud/azure/claude.fragment.md +17 -0
- package/stacks/cloud/azure/settings.fragment.json +45 -0
- package/stacks/cloud/azure/skills/azure-deployment-preflight/SKILL.md +175 -0
- package/stacks/cloud/azure/skills/cloud-architecture-azure/SKILL.md +211 -0
- package/stacks/cloud/cloudflare/claude.fragment.md +21 -0
- package/stacks/cloud/cloudflare/settings.fragment.json +31 -0
- package/stacks/cloud/cloudflare/skills/cloud-architecture-cloudflare/SKILL.md +294 -0
- package/stacks/cloud/cloudflare/skills/cloudflare-deployment-preflight/SKILL.md +175 -0
- package/stacks/cloud/gcp/claude.fragment.md +17 -0
- package/stacks/cloud/gcp/settings.fragment.json +40 -0
- package/stacks/cloud/gcp/skills/cloud-architecture-gcp/SKILL.md +208 -0
- package/stacks/cloud/gcp/skills/gcp-deployment-preflight/SKILL.md +137 -0
- package/stacks/db/mongo/skills/mongo-conventions/SKILL.md +96 -0
- package/stacks/db/prisma/claude.fragment.md +49 -0
- package/stacks/db/prisma/skills/docker-database-package-copy/SKILL.md +44 -0
- package/stacks/db/prisma/skills/prisma-conventions/SKILL.md +37 -0
- package/stacks/domain/mobile-growth/skills/apple-ads/SKILL.md +184 -0
- package/stacks/domain/mobile-growth/skills/apple-ads/references/benchmark-notes.md +47 -0
- package/stacks/domain/mobile-growth/skills/apple-ads/references/official-links.md +53 -0
- package/stacks/domain/mobile-growth/skills/google-play-growth/SKILL.md +197 -0
- package/stacks/domain/mobile-growth/skills/google-play-growth/references/benchmark-notes.md +47 -0
- package/stacks/domain/mobile-growth/skills/google-play-growth/references/official-links.md +45 -0
- package/stacks/iac/bicep/claude.fragment.md +14 -0
- package/stacks/iac/bicep/settings.fragment.json +20 -0
- package/stacks/iac/bicep/skills/iac-bicep/SKILL.md +113 -0
- package/stacks/iac/cdk/claude.fragment.md +14 -0
- package/stacks/iac/cdk/settings.fragment.json +23 -0
- package/stacks/iac/cdk/skills/iac-cdk/SKILL.md +104 -0
- package/stacks/iac/terraform/claude.fragment.md +13 -0
- package/stacks/iac/terraform/settings.fragment.json +25 -0
- package/stacks/iac/terraform/skills/iac-terraform/SKILL.md +93 -0
- package/stacks/iac/terraform/skills/terraform-conventions/SKILL.md +87 -0
- package/stacks/lang/kotlin/skills/android-testing/SKILL.md +263 -0
- package/stacks/lang/kotlin/skills/jetpack-compose/SKILL.md +264 -0
- package/stacks/lang/kotlin/skills/kotlin-coroutines/SKILL.md +329 -0
- package/stacks/lang/python/skills/python-conventions/SKILL.md +61 -0
- package/stacks/lang/shell/skills/shell-scripting/SKILL.md +110 -0
- package/stacks/lang/swift/skills/swift-concurrency/SKILL.md +423 -0
- package/stacks/lang/swift/skills/swift-concurrency/references/approachable-concurrency.md +80 -0
- package/stacks/lang/swift/skills/swift-concurrency/references/concurrency-patterns.md +233 -0
- package/stacks/lang/swift/skills/swift-concurrency/references/swiftui-concurrency.md +187 -0
- package/stacks/lang/swift/skills/swift-concurrency/references/synchronization-primitives.md +341 -0
- package/stacks/lang/swift/skills/swift-testing/SKILL.md +497 -0
- package/stacks/lang/swift/skills/swift-testing/references/testing-advanced.md +106 -0
- package/stacks/lang/swift/skills/swift-testing/references/testing-patterns.md +504 -0
- package/stacks/lang/swift/skills/swiftdata/SKILL.md +334 -0
- package/stacks/lang/swift/skills/swiftdata/references/core-data-coexistence.md +504 -0
- package/stacks/lang/swift/skills/swiftdata/references/swiftdata-advanced.md +975 -0
- package/stacks/lang/swift/skills/swiftdata/references/swiftdata-queries.md +675 -0
- package/stacks/lang/swift/skills/swiftui-patterns/SKILL.md +371 -0
- package/stacks/lang/swift/skills/swiftui-patterns/references/architecture-patterns.md +486 -0
- package/stacks/lang/swift/skills/swiftui-patterns/references/deprecated-migration.md +1097 -0
- package/stacks/lang/swift/skills/swiftui-patterns/references/design-polish.md +780 -0
- package/stacks/lang/swift/skills/swiftui-patterns/references/platform-and-sharing.md +696 -0
- package/stacks/lang/typescript/skills/typescript-conventions/SKILL.md +91 -0
- package/stacks/platform/android/claude.fragment.md +40 -0
- package/stacks/platform/android/hooks/pre-push-gradle.sh +70 -0
- package/stacks/platform/android/settings.fragment.json +13 -0
- package/stacks/platform/android/skills/android-build-conventions/SKILL.md +247 -0
- package/stacks/platform/ios/claude.fragment.md +24 -0
- package/stacks/platform/ios/hooks/pre-push-xcodebuild.sh +82 -0
- package/stacks/platform/ios/settings.fragment.json +21 -0
- package/stacks/platform/ios/skills/xcodebuildmcp-simulator-logs/SKILL.md +76 -0
- package/stacks/platform/web/skills/frontend-testing/SKILL.md +246 -0
- package/stacks/platform/web/skills/react-conventions/SKILL.md +261 -0
- package/stacks/platform/web/skills/web-platform-conventions/SKILL.md +55 -0
- package/stacks/tooling/issue-tracker-github/claude.fragment.md +10 -0
- package/stacks/tooling/issue-tracker-github/settings.fragment.json +24 -0
- package/stacks/tooling/issue-tracker-github/skills/issue-tracker-github/SKILL.md +278 -0
- package/stacks/tooling/issue-tracker-linear/claude.fragment.md +17 -0
- package/stacks/tooling/issue-tracker-linear/settings.fragment.json +9 -0
- 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/`).
|