geeknative 0.0.0-stage → 0.1.1

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 (159) hide show
  1. package/LICENSE +202 -0
  2. package/bin/geeknative.js +12 -0
  3. package/package.json +42 -4
  4. package/src/cli.js +61 -0
  5. package/src/codegen/app.js +105 -0
  6. package/src/codegen/icons.js +87 -0
  7. package/src/codegen/index.js +44 -0
  8. package/src/codegen/strings.js +308 -0
  9. package/src/codegen/tokens.js +449 -0
  10. package/src/codegen/util.js +122 -0
  11. package/src/commands/check.js +148 -0
  12. package/src/commands/create.js +82 -0
  13. package/src/commands/doctor.js +108 -0
  14. package/src/commands/gen.js +44 -0
  15. package/src/commands/parity.js +134 -0
  16. package/src/commands/run.js +91 -0
  17. package/src/lib/exec.js +77 -0
  18. package/src/lib/fsutil.js +92 -0
  19. package/src/lib/log.js +37 -0
  20. package/src/lib/managed.js +80 -0
  21. package/src/lib/project.js +52 -0
  22. package/src/lib/toolchains.js +126 -0
  23. package/template/.claude/agents/android-engineer.md +15 -0
  24. package/template/.claude/agents/ios-engineer.md +15 -0
  25. package/template/.claude/agents/web-engineer.md +15 -0
  26. package/template/.claude/settings.json +65 -0
  27. package/template/.geeknative/framework.mk +41 -0
  28. package/template/.geeknative/hooks/after-edit.mjs +28 -0
  29. package/template/.geeknative/hooks/parity-guard.mjs +49 -0
  30. package/template/.geeknative/hooks/subagent-context.mjs +24 -0
  31. package/template/.geeknative/playbooks/bug.md +7 -0
  32. package/template/.geeknative/playbooks/design.md +10 -0
  33. package/template/.geeknative/playbooks/engineering.md +9 -0
  34. package/template/.geeknative/playbooks/feature.md +11 -0
  35. package/template/.geeknative/playbooks/platform-specific.md +8 -0
  36. package/template/.geeknative/playbooks/product.md +12 -0
  37. package/template/.geeknative/templates/feature.md +49 -0
  38. package/template/.gemini/settings.json +5 -0
  39. package/template/.github/dependabot.yml +18 -0
  40. package/template/AGENTS.md +97 -0
  41. package/template/CLAUDE.md +9 -0
  42. package/template/DESIGN.md +65 -0
  43. package/template/Makefile +6 -0
  44. package/template/PRODUCT.md +40 -0
  45. package/template/README.md +28 -0
  46. package/template/_gitignore +33 -0
  47. package/template/apps/android/AGENTS.md +66 -0
  48. package/template/apps/android/CLAUDE.md +1 -0
  49. package/template/apps/android/app/build.gradle.kts +68 -0
  50. package/template/apps/android/app/identity.properties +4 -0
  51. package/template/apps/android/app/src/main/AndroidManifest.xml +24 -0
  52. package/template/apps/android/app/src/main/kotlin/app/App.kt +15 -0
  53. package/template/apps/android/app/src/main/kotlin/app/MainActivity.kt +49 -0
  54. package/template/apps/android/app/src/main/kotlin/app/core/AppContainer.kt +27 -0
  55. package/template/apps/android/app/src/main/kotlin/app/core/designsystem/AppCard.kt +30 -0
  56. package/template/apps/android/app/src/main/kotlin/app/core/designsystem/AppTheme.kt +35 -0
  57. package/template/apps/android/app/src/main/kotlin/app/core/navigation/AppNavigation.kt +40 -0
  58. package/template/apps/android/app/src/main/kotlin/app/core/navigation/Route.kt +13 -0
  59. package/template/apps/android/app/src/main/kotlin/app/features/home/HomeScreen.kt +67 -0
  60. package/template/apps/android/app/src/main/kotlin/app/features/settings/Appearance.kt +22 -0
  61. package/template/apps/android/app/src/main/kotlin/app/features/settings/SettingsRepository.kt +40 -0
  62. package/template/apps/android/app/src/main/kotlin/app/features/settings/SettingsScreen.kt +113 -0
  63. package/template/apps/android/app/src/main/kotlin/app/features/settings/SettingsViewModel.kt +28 -0
  64. package/template/apps/android/app/src/main/kotlin/app/generated/AppIdentity.kt +10 -0
  65. package/template/apps/android/app/src/main/kotlin/app/generated/Icons.kt +15 -0
  66. package/template/apps/android/app/src/main/kotlin/app/generated/Tokens.kt +144 -0
  67. package/template/apps/android/app/src/main/res/drawable/ic_launcher_foreground.xml +17 -0
  68. package/template/apps/android/app/src/main/res/mipmap/ic_launcher.xml +10 -0
  69. package/template/apps/android/app/src/main/res/mipmap/ic_launcher_round.xml +10 -0
  70. package/template/apps/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  71. package/template/apps/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  72. package/template/apps/android/app/src/main/res/values/generated_colors.xml +15 -0
  73. package/template/apps/android/app/src/main/res/values/generated_strings.xml +16 -0
  74. package/template/apps/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
  75. package/template/apps/android/app/src/main/res/values/themes.xml +7 -0
  76. package/template/apps/android/app/src/main/res/values-night/generated_colors.xml +15 -0
  77. package/template/apps/android/app/src/main/res/values-night/themes.xml +6 -0
  78. package/template/apps/android/app/src/test/kotlin/app/features/settings/SettingsTest.kt +90 -0
  79. package/template/apps/android/build.gradle.kts +5 -0
  80. package/template/apps/android/gradle/gradle-daemon-jvm.properties +12 -0
  81. package/template/apps/android/gradle/libs.versions.toml +37 -0
  82. package/template/apps/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  83. package/template/apps/android/gradle/wrapper/gradle-wrapper.properties +9 -0
  84. package/template/apps/android/gradle.properties +7 -0
  85. package/template/apps/android/gradlew +248 -0
  86. package/template/apps/android/gradlew.bat +82 -0
  87. package/template/apps/android/settings.gradle.kts +29 -0
  88. package/template/apps/ios/AGENTS.md +70 -0
  89. package/template/apps/ios/App/AppMain.swift +13 -0
  90. package/template/apps/ios/App/Core/AppContainer.swift +22 -0
  91. package/template/apps/ios/App/Core/DesignSystem/AppCard.swift +15 -0
  92. package/template/apps/ios/App/Core/Navigation/Route.swift +4 -0
  93. package/template/apps/ios/App/Core/RootView.swift +32 -0
  94. package/template/apps/ios/App/Features/Home/HomeScreen.swift +35 -0
  95. package/template/apps/ios/App/Features/Settings/Appearance.swift +27 -0
  96. package/template/apps/ios/App/Features/Settings/SettingsRepository.swift +40 -0
  97. package/template/apps/ios/App/Features/Settings/SettingsScreen.swift +40 -0
  98. package/template/apps/ios/App/Features/Settings/SettingsViewModel.swift +23 -0
  99. package/template/apps/ios/App/Generated/AppIdentity.swift +9 -0
  100. package/template/apps/ios/App/Generated/Colors.xcassets/AccentColor.colorset/Contents.json +38 -0
  101. package/template/apps/ios/App/Generated/Colors.xcassets/Contents.json +6 -0
  102. package/template/apps/ios/App/Generated/Icons.swift +8 -0
  103. package/template/apps/ios/App/Generated/Localizable.xcstrings +127 -0
  104. package/template/apps/ios/App/Generated/Strings.swift +40 -0
  105. package/template/apps/ios/App/Generated/Tokens.swift +65 -0
  106. package/template/apps/ios/App/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
  107. package/template/apps/ios/App/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +14 -0
  108. package/template/apps/ios/App/Resources/Assets.xcassets/Contents.json +6 -0
  109. package/template/apps/ios/App.xcodeproj/project.pbxproj +298 -0
  110. package/template/apps/ios/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  111. package/template/apps/ios/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme +89 -0
  112. package/template/apps/ios/AppTests/SettingsTests.swift +54 -0
  113. package/template/apps/ios/CLAUDE.md +1 -0
  114. package/template/apps/ios/Config/App.xcconfig +23 -0
  115. package/template/apps/ios/Config/AppTests.xcconfig +10 -0
  116. package/template/apps/ios/Config/Base.xcconfig +25 -0
  117. package/template/apps/ios/Config/Debug.xcconfig +9 -0
  118. package/template/apps/ios/Config/Identity.xcconfig +5 -0
  119. package/template/apps/ios/Config/Release.xcconfig +6 -0
  120. package/template/apps/web/AGENTS.md +63 -0
  121. package/template/apps/web/CLAUDE.md +1 -0
  122. package/template/apps/web/package.json +38 -0
  123. package/template/apps/web/public/favicon.svg +6 -0
  124. package/template/apps/web/src/core/AppContainer.ts +18 -0
  125. package/template/apps/web/src/core/AppearanceEffect.tsx +31 -0
  126. package/template/apps/web/src/core/designsystem/AppCard.tsx +6 -0
  127. package/template/apps/web/src/features/home/HomeScreen.tsx +15 -0
  128. package/template/apps/web/src/features/home/home.test.tsx +22 -0
  129. package/template/apps/web/src/features/settings/Appearance.ts +29 -0
  130. package/template/apps/web/src/features/settings/LocalStorageSettingsRepository.ts +21 -0
  131. package/template/apps/web/src/features/settings/SettingsRepository.ts +7 -0
  132. package/template/apps/web/src/features/settings/SettingsScreen.tsx +44 -0
  133. package/template/apps/web/src/features/settings/settings.test.ts +45 -0
  134. package/template/apps/web/src/features/settings/useSettingsViewModel.ts +42 -0
  135. package/template/apps/web/src/generated/app.ts +9 -0
  136. package/template/apps/web/src/generated/icons.ts +9 -0
  137. package/template/apps/web/src/generated/strings.ts +88 -0
  138. package/template/apps/web/src/generated/tokens.css +86 -0
  139. package/template/apps/web/src/routeTree.gen.ts +86 -0
  140. package/template/apps/web/src/router.tsx +23 -0
  141. package/template/apps/web/src/routes/__root.tsx +72 -0
  142. package/template/apps/web/src/routes/index.tsx +6 -0
  143. package/template/apps/web/src/routes/settings.tsx +9 -0
  144. package/template/apps/web/src/styles.css +9 -0
  145. package/template/apps/web/tsconfig.json +26 -0
  146. package/template/apps/web/tsr.config.json +3 -0
  147. package/template/apps/web/vite.config.ts +9 -0
  148. package/template/apps/web/vitest.config.ts +9 -0
  149. package/template/contracts/app.json +7 -0
  150. package/template/contracts/icons.json +5 -0
  151. package/template/contracts/strings/en.json +13 -0
  152. package/template/contracts/tokens.json +40 -0
  153. package/template/package.json +17 -0
  154. package/template/spec/decisions.md +10 -0
  155. package/template/spec/features/home.md +39 -0
  156. package/template/spec/features/settings.md +50 -0
  157. package/template/spec/navigation.md +16 -0
  158. package/template/spec/parity.md +29 -0
  159. package/README.md +0 -3
@@ -0,0 +1,49 @@
1
+ // Claude Code Stop hook (managed by geeknative).
2
+ // If the uncommitted changes touch some apps but not the others, remind the agent once,
3
+ // since requests apply to all three apps unless they're platform-specific.
4
+ import { spawnSync } from 'node:child_process'
5
+ import { createHash } from 'node:crypto'
6
+ import { readFileSync, writeFileSync } from 'node:fs'
7
+ import path from 'node:path'
8
+
9
+ let input = ''
10
+ for await (const chunk of process.stdin) input += chunk
11
+ if (JSON.parse(input || '{}').stop_hook_active) process.exit(0)
12
+
13
+ const root = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
14
+ /** Runs git in the project folder; paths come back relative to it. @param {string[]} args */
15
+ const git = (...args) => spawnSync('git', args, { cwd: root, encoding: 'utf8' })
16
+
17
+ const tracked = git('diff', '--name-only', '--relative', 'HEAD', '--', 'apps')
18
+ const untracked = git('ls-files', '--others', '--exclude-standard', '--', 'apps')
19
+ if (tracked.status !== 0 || untracked.status !== 0) process.exit(0) // not a git repo, or no commits yet
20
+
21
+ // Generated code and agent docs don't count as implementing a change.
22
+ const ignored = /(\/Generated\/|\/generated\/|\/generated_[^/]*\.xml$|Identity\.xcconfig$|identity\.properties$|AGENTS\.md$|CLAUDE\.md$)/
23
+ const changed = [...tracked.stdout.split('\n'), ...untracked.stdout.split('\n')]
24
+ .filter((file) => file.startsWith('apps/') && !ignored.test(file))
25
+ .sort()
26
+
27
+ const names = { ios: 'iOS', android: 'Android', web: 'web' }
28
+ const touched = Object.keys(names).filter((p) => changed.some((file) => file.startsWith(`apps/${p}/`)))
29
+ if (touched.length === 0 || touched.length === 3) process.exit(0)
30
+
31
+ // Remind only once per set of changes.
32
+ const fingerprint = createHash('sha1').update(changed.join('\n')).digest('hex')
33
+ const stateFile = path.resolve(root, git('rev-parse', '--git-dir').stdout.trim(), 'geeknative-parity-guard')
34
+ let last = ''
35
+ try {
36
+ last = readFileSync(stateFile, 'utf8')
37
+ } catch {}
38
+ if (last === fingerprint) process.exit(0)
39
+ writeFileSync(stateFile, fingerprint)
40
+
41
+ const missing = Object.keys(names).filter((p) => !touched.includes(p))
42
+ const list = (/** @type {string[]} */ platforms) =>
43
+ platforms.map((p) => names[/** @type {keyof typeof names} */ (p)]).join(' and ')
44
+ console.error(
45
+ `Parity check: the uncommitted changes touch ${list(touched)} but not ${list(missing)}. ` +
46
+ 'If this request applies to every app, implement it there too (AGENTS.md section 1). ' +
47
+ "If it's intentionally platform-specific, say so in your report and note it in the spec's Platform notes. Then finish.",
48
+ )
49
+ process.exit(2)
@@ -0,0 +1,24 @@
1
+ // Claude Code SubagentStart hook (managed by geeknative).
2
+ // Gives a platform subagent its platform's AGENTS.md as soon as it starts.
3
+ // Usage: node subagent-context.mjs <ios|android|web>
4
+ import { readFileSync } from 'node:fs'
5
+ import path from 'node:path'
6
+
7
+ const platform = process.argv[2]
8
+ const root = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
9
+
10
+ let guide
11
+ try {
12
+ guide = readFileSync(path.join(root, 'apps', platform, 'AGENTS.md'), 'utf8')
13
+ } catch {
14
+ process.exit(0)
15
+ }
16
+
17
+ console.log(
18
+ JSON.stringify({
19
+ hookSpecificOutput: {
20
+ hookEventName: 'SubagentStart',
21
+ additionalContext: `Conventions for this app, from apps/${platform}/AGENTS.md:\n\n${guide}`,
22
+ },
23
+ }),
24
+ )
@@ -0,0 +1,7 @@
1
+ # Playbook: bug
2
+
3
+ 1. **Locate.** Find which app has the bug, and whether that app breaks the spec or the spec never covered this case.
4
+ 2. **If the spec was silent or wrong,** fix the spec first (add the missing state or rule, with an acceptance criterion), then fix every app that doesn't match it. A bug in one app often exists in the others.
5
+ 3. **If the spec was right,** fix the app that breaks it, then check the other two apps for the same mistake.
6
+ 4. **Add a regression test** named after the acceptance criterion it protects, in each app you fixed.
7
+ 5. **Verify** with `make check` for every app you touched, and report which apps had the bug.
@@ -0,0 +1,10 @@
1
+ # Playbook: design change
2
+
3
+ Use for look and feel: colors, type, spacing, corner radii, dark mode, density, or "make it feel more ...".
4
+
5
+ 1. **Direction first.** For a broad request ("warmer", "more premium"), update the Direction section of DESIGN.md in a sentence or two, so later changes stay consistent with it.
6
+ 2. **Tokens before code.** Change values in contracts/tokens.json. Keep light and dark values in step and text readable (WCAG AA: 4.5:1 for body text). Run `make gen`. Most visual changes need nothing else, because every app reads the tokens.
7
+ 3. **New tokens are roles.** Name new colors by purpose (`success`, `warning`), not by hue (`green`). Every app gets them on the next `make gen`.
8
+ 4. **Components.** If a change affects a pattern (for example, every card gets a border), update the Components table in DESIGN.md, then change that pattern in all three apps.
9
+ 5. **Stay native.** Change brand elements (color, type, spacing, imagery) everywhere, but keep platform conventions (navigation bars, system controls, gestures) native unless the user explicitly asks otherwise.
10
+ 6. **Verify** with `make check`. If you can, launch the apps (`make run-ios`, `make run-android`, `make run-web`) and look at the result.
@@ -0,0 +1,9 @@
1
+ # Playbook: engineering change
2
+
3
+ Use for tooling, dependencies, build settings, refactoring and performance work that doesn't change what users see.
4
+
5
+ 1. **No spec changes** unless behavior changes. If it does, treat it as a feature or a bug instead.
6
+ 2. **Keep the apps mirrored.** If you change how state, navigation, data or dependencies work on one platform, make the matching change on the others, or record why not in spec/decisions.md.
7
+ 3. **Dependencies.** Prefer platform and first-party libraries, and ask before adding a large dependency. The iOS app has no third-party packages; adding one changes the Xcode project, so ask first.
8
+ 4. **Generated code.** Never edit generated files. Change the contract, or for a bug in the generator, report it upstream: codegen lives in the geeknative package.
9
+ 5. **Verify** with `make check` for every platform you touched.
@@ -0,0 +1,11 @@
1
+ # Playbook: feature or behavior change
2
+
3
+ 1. **Spec.** For a new feature, copy .geeknative/templates/feature.md to `spec/features/<id>.md` (`id` in kebab-case). For a change, edit the existing spec. Write acceptance criteria that are testable and platform-neutral, numbered `AC-<id>-1`, `AC-<id>-2`, ...
4
+ 2. **States.** Cover loading, empty, error and success for every screen that loads or saves data. Missing states are the most common way apps drift apart.
5
+ 3. **Data.** Describe what's stored and in what format (keys, values, types), so every app stores it the same way. New data goes through a repository interface with a local implementation; there's no backend yet.
6
+ 4. **Navigation.** If the feature adds a screen, add it to spec/navigation.md.
7
+ 5. **Contracts.** Add copy to `contracts/strings/<locale>.json` (every locale), icons to contracts/icons.json and any new design tokens to contracts/tokens.json. Run `make gen`.
8
+ 6. **Implement** on iOS, Android and web, each following its `apps/<platform>/AGENTS.md` and using the same names everywhere: `<Name>Screen`, `<Name>ViewModel`, `<Name>Repository`.
9
+ 7. **Test** each acceptance criterion on each platform where it can be tested at the unit level, with the AC ID in the test name. Criteria that need UI tests can wait, but list them in your report.
10
+ 8. **Verify** with `make check`, and fix until it passes.
11
+ 9. **Record.** Set each platform's status in the spec's front matter (`done`, `in-progress`, `planned`, or `n/a` for a feature that's not on that platform). Run `make parity`. Add new features to PRODUCT.md's feature list.
@@ -0,0 +1,8 @@
1
+ # Playbook: platform-specific change
2
+
3
+ Use when the user names a platform ("on Android, ...", "iOS only").
4
+
5
+ 1. **Check it's really platform-only.** Platform conventions (haptics, share sheets, widgets, keyboard shortcuts on web) belong on one platform. A product change that merely happens to be described for one platform probably belongs on all three. If unsure, ask once.
6
+ 2. **Record it** in the feature's "Platform notes" if users can see it, so nobody "fixes" the difference later. For a feature that exists on only some platforms, set the others to `n/a` in its front matter.
7
+ 3. **Implement and verify** on that platform only, with `make check-<platform>`.
8
+ 4. **Report** that the change is intentionally limited to that platform.
@@ -0,0 +1,12 @@
1
+ # Playbook: product
2
+
3
+ Use when the user describes an app (often the first prompt in a new project), or changes what it is, who it's for, or its scope.
4
+
5
+ 1. **Understand.** Restate the product in one sentence. Ask only about things that would change the data model or the navigation structure (AGENTS.md section 6), and assume the rest.
6
+ 2. **PRODUCT.md.** Fill in the one-sentence description, the audience, the feature list, the glossary and what's out of scope. Glossary terms become the names used in code on every platform.
7
+ 3. **Design.** If the user described a look, write the direction in DESIGN.md and update contracts/tokens.json (colors in light and dark, type, spacing, radii). Run `make gen`.
8
+ 4. **Navigation.** Update spec/navigation.md with the screens and how users move between them (tabs, stacks, modals). Keep the starter Settings screen unless the user says otherwise.
9
+ 5. **Feature specs.** Write one spec per feature in spec/features/, starting from .geeknative/templates/feature.md, with every platform's status set to `planned`. Keep version one small: the core loop first.
10
+ 6. **Build in vertical slices.** Take the first feature through the loop in AGENTS.md section 4 on all three apps, verify it, record its status, then start the next. Never build every screen on one platform first.
11
+ 7. **Replace the starter content.** Once real features exist, update or remove Home's welcome card, and update spec/features/home.md to match.
12
+ 8. **Report** what you built on each platform, what's planned next, and the assumptions you made.
@@ -0,0 +1,49 @@
1
+ ---
2
+ id: feature-id # kebab-case; used in file names and acceptance criteria IDs
3
+ title: Feature title
4
+ status: # planned | in-progress | done | n/a (not on this platform)
5
+ ios: planned
6
+ android: planned
7
+ web: planned
8
+ ---
9
+
10
+ # Feature title
11
+
12
+ ## Summary
13
+
14
+ One or two sentences: what the user can do, and why.
15
+
16
+ ## User stories
17
+
18
+ - As a <who>, I want <what> so that <why>.
19
+
20
+ ## Screens and states
21
+
22
+ For each screen: what it's for, what it shows, and its loading, empty, error and success states.
23
+
24
+ ## Behavior and rules
25
+
26
+ Business rules, validation, limits and edge cases. For stored data: the keys, values and types, identical on every platform.
27
+
28
+ ## Acceptance criteria
29
+
30
+ Testable and platform-neutral. Each platform's tests put these IDs in their names.
31
+
32
+ - AC-feature-id-1: ...
33
+ - AC-feature-id-2: ...
34
+
35
+ ## Copy
36
+
37
+ New or changed keys in contracts/strings.
38
+
39
+ ## Platform notes
40
+
41
+ Where the platforms intentionally differ (navigation chrome, controls, gestures), and anything platform-only.
42
+
43
+ ## Test IDs
44
+
45
+ - `feature.element`: ...
46
+
47
+ ## Assumptions and open questions
48
+
49
+ - Assumption: ...
@@ -0,0 +1,5 @@
1
+ {
2
+ "context": {
3
+ "fileName": ["AGENTS.md"]
4
+ }
5
+ }
@@ -0,0 +1,18 @@
1
+ # Keeps each app's dependencies current. geeknative itself is an npm dev dependency,
2
+ # so framework releases arrive here too (then run `make gen` and `make check`).
3
+ version: 2
4
+ updates:
5
+ - package-ecosystem: npm
6
+ directory: /
7
+ schedule:
8
+ interval: weekly
9
+ groups:
10
+ tanstack:
11
+ patterns: ["@tanstack/*"]
12
+ - package-ecosystem: gradle
13
+ directory: /apps/android
14
+ schedule:
15
+ interval: weekly
16
+ groups:
17
+ androidx:
18
+ patterns: ["androidx.*"]
@@ -0,0 +1,97 @@
1
+ <!-- geeknative:begin (managed by geeknative: replaced on upgrade. Add your own rules under "Project rules" at the end.) -->
2
+ # How to work in this repo
3
+
4
+ This repo is **one product, built three times, natively**: iOS (SwiftUI), Android (Kotlin + Jetpack Compose) and web (React + TanStack Start). The apps share no code. They behave the same because every change goes through shared docs and contracts first, and you keep all three in step.
5
+
6
+ ## 1. Every request is for all three apps
7
+
8
+ Unless the user names a platform ("on Android, ...") or the spec marks something as platform-only, a request is done only when iOS, Android **and** web all do it.
9
+
10
+ ## 2. Triage first, and say it in one line
11
+
12
+ Before changing anything, decide what kind of request it is. State it as the first line of your reply, so the user can correct you early:
13
+
14
+ > **Feature:** notes list → spec/features/notes.md, strings · iOS, Android, Web
15
+
16
+ | The request... | Type | Update first | Playbook |
17
+ |-----------------------------------------------------|-------------------|----------------------------------------------------------|---------------------------------------------|
18
+ | describes an app, or changes what it is or who it's for | Product | PRODUCT.md, DESIGN.md, spec/navigation.md, feature specs | .geeknative/playbooks/product.md |
19
+ | adds a screen, flow or capability | Feature | a new `spec/features/<id>.md` | .geeknative/playbooks/feature.md |
20
+ | changes how something existing behaves | Behavior change | that feature's spec | .geeknative/playbooks/feature.md |
21
+ | changes the look and feel | Design | DESIGN.md and/or contracts/tokens.json | .geeknative/playbooks/design.md |
22
+ | changes wording only | Copy | contracts/strings/*.json, then `make gen` | none |
23
+ | reports something broken | Bug | the spec, only if it was silent or wrong | .geeknative/playbooks/bug.md |
24
+ | names one platform | Platform-specific | the feature's "Platform notes", if users can see it | .geeknative/playbooks/platform-specific.md |
25
+ | is about tooling, dependencies, builds or refactoring | Engineering | nothing | .geeknative/playbooks/engineering.md |
26
+ | asks a question | Question | nothing: answer it, don't edit files | none |
27
+
28
+ - A request can be several types at once (a feature usually adds copy too).
29
+ - Keep it proportional: small tweaks don't need new spec files. Update a doc only when what it says changes.
30
+ - If the user says "plan", "spec only" or "don't build yet", stop after updating the docs.
31
+
32
+ ## 3. Where things live
33
+
34
+ | What | Source of truth |
35
+ |---|---|
36
+ | What the product is, who it's for, glossary | PRODUCT.md |
37
+ | Visual language, components, what adapts per platform | DESIGN.md |
38
+ | How each feature behaves, with acceptance criteria | `spec/features/<id>.md` (start from .geeknative/templates/feature.md) |
39
+ | Screens and routes | spec/navigation.md |
40
+ | Decisions and assumptions, dated | spec/decisions.md |
41
+ | App name, ID and version | contracts/app.json |
42
+ | Colors, spacing, radii and type | contracts/tokens.json |
43
+ | All user-facing text | `contracts/strings/<locale>.json` |
44
+ | Icons (SF Symbols, Material, Lucide) | contracts/icons.json |
45
+ | Which platform has what | feature front matter → `make parity` → spec/parity.md |
46
+
47
+ `make gen` compiles contracts/ into each app (`Generated/`, `generated/`, `generated_*.xml`, `Identity.xcconfig`, `identity.properties`). Never edit generated files: change the contract and regenerate.
48
+
49
+ ## 4. The loop
50
+
51
+ 1. **Update the source of truth** (section 3).
52
+ 2. **Regenerate** with `make gen` if contracts changed.
53
+ 3. **Implement on each platform.** Before editing under `apps/<platform>/`, read `apps/<platform>/AGENTS.md`.
54
+ - If you can run sub-agents in parallel, give each platform its own, with the spec path, the acceptance criteria IDs and what changed in contracts/. Only you, the lead, edit `spec/`, `contracts/`, PRODUCT.md and DESIGN.md; platform agents report gaps back to you.
55
+ - Otherwise, do the platforms one after another.
56
+ 4. **Verify** with `make check-ios`, `make check-android` and `make check-web` (or `make check` for everything). Fix until they pass.
57
+ 5. **Record** each platform's status in the feature's front matter, then run `make parity`.
58
+ 6. **Report** one line per platform (done, partial or skipped, and why), the checks you ran, and any assumptions you made.
59
+
60
+ ## 5. Keeping the apps aligned
61
+
62
+ - **Build from the spec, not by porting.** Don't translate another app's code. If you had to read another app's code to know how something should behave, the spec has a gap: fix the spec first.
63
+ - **Same names everywhere.** A feature `notes` is `NotesScreen`, `NotesViewModel` and `NotesRepository` in every app, in each app's notes feature folder. One search finds all three.
64
+ - **Same IDs everywhere.** Test IDs (`notes.addButton`), storage keys and stored values match across apps.
65
+ - **Tests name acceptance criteria.** Put the ID (for example `AC-notes-2`) in each test's name, so `make parity` can show coverage.
66
+ - **Native, not identical.** Features, flows, data, copy and brand match. Navigation chrome, controls, gestures and system conventions follow each platform. DESIGN.md says which is which.
67
+ - **One feature at a time.** Finish a feature on every platform before starting the next.
68
+ - **Spec changes ripple.** If building one platform changes the spec, recheck the platforms you already did.
69
+ - **No backend yet.** Features read and write through a repository interface with local implementations. When a backend arrives, only the repositories change.
70
+
71
+ ## 6. Ask or assume
72
+
73
+ Ask only when a wrong guess is expensive to undo: the data model, the navigation structure, deleting features or data, or anything irreversible. Otherwise pick the conventional option, note it under "Assumptions" in the spec (and in spec/decisions.md if it affects more than one feature), and mention it in your report.
74
+
75
+ ## 7. Done means
76
+
77
+ - The docs and contracts describe the new behavior.
78
+ - `make gen` leaves no changes.
79
+ - Checks pass for every platform you touched.
80
+ - Each platform's status is recorded and spec/parity.md is current.
81
+ - Anything left undone is in your report, with the reason.
82
+
83
+ ## Commands
84
+
85
+ | Command | What it does |
86
+ |---|---|
87
+ | `make doctor` | Checks Xcode, the Android SDK, Java and Node (`make doctor FIX=1` installs what it can) |
88
+ | `make gen` | Regenerates app code from contracts/ |
89
+ | `make check` | Validates contracts, then builds and tests all three apps |
90
+ | `make check-ios`, `make check-android`, `make check-web` | One app; full logs go to .geeknative/logs/ |
91
+ | `make run-ios`, `make run-android`, `make run-web` | Builds and launches one app |
92
+ | `make parity` | Updates spec/parity.md |
93
+ <!-- geeknative:end -->
94
+
95
+ ## Project rules
96
+
97
+ <!-- Your own rules go here. They override the defaults above, and geeknative upgrades never touch them. -->
@@ -0,0 +1,9 @@
1
+ @AGENTS.md
2
+
3
+ <!-- geeknative:begin (managed by geeknative: replaced on upgrade) -->
4
+ ## Claude Code
5
+
6
+ - When a change touches more than one app, hand the platform work to the `ios-engineer`, `android-engineer` and `web-engineer` subagents in parallel (all three in one message). Give each the spec path, the acceptance criteria IDs and what changed in contracts/. You stay the lead and own spec/, contracts/, PRODUCT.md and DESIGN.md.
7
+ - A hook runs `make gen` whenever you edit a file in contracts/, and reports errors in the contract right away.
8
+ - A stop hook reminds you once if a turn changed some apps but not the others.
9
+ <!-- geeknative:end -->
@@ -0,0 +1,65 @@
1
+ # Design
2
+
3
+ <!--
4
+ How the app looks and feels on every platform. Your coding agent updates this when you ask for
5
+ design changes. Token values live in contracts/tokens.json; this file explains how they're used.
6
+ -->
7
+
8
+ ## Direction
9
+
10
+ _Not described yet. Describe the look you want (for example "calm and minimal" or "bold and playful") and your coding agent will set colors, type and spacing to match._
11
+
12
+ ## Same everywhere, native where it counts
13
+
14
+ | Same on every platform | Native to each platform |
15
+ |---|---|
16
+ | Features, flows and data | Navigation chrome (navigation bar, top app bar, site header) |
17
+ | Copy (contracts/strings) | Controls: pickers, switches, radio lists, segmented controls |
18
+ | Colors, spacing, radii and type scale (contracts/tokens.json) | Gestures, haptics and transitions |
19
+ | Icons by meaning (contracts/icons.json) | Icon artwork: SF Symbols, Material icons, Lucide |
20
+ | Screen structure and content order | Sheets, dialogs and menus |
21
+ | Accessibility labels and test IDs | System font (SF Pro, Roboto, the browser's system font) |
22
+
23
+ ## Tokens
24
+
25
+ Defined in contracts/tokens.json and compiled into each app by `make gen`.
26
+
27
+ | Token | iOS | Android | Web (Tailwind) |
28
+ |---|---|---|---|
29
+ | Color `primary` | `AppColors.primary` | `AppTheme.colors.primary` | `bg-primary`, `text-primary` |
30
+ | Color `onPrimary` | `AppColors.onPrimary` | `AppTheme.colors.onPrimary` | `text-on-primary` |
31
+ | Spacing `md` | `AppSpacing.md` | `AppSpacing.md` | `p-md`, `gap-md` |
32
+ | Radius `lg` | `AppRadius.lg` | `AppRadius.lg` | `rounded-lg` |
33
+ | Type `title` | `AppTypography.title` | `AppTypography.title` | `text-title` |
34
+
35
+ **Color roles.** Every color has a light and a dark value. Add new colors by role (what they're for), not by hue.
36
+
37
+ - `background` / `onBackground`: screens.
38
+ - `surface` / `onSurface`: cards and grouped content on a screen.
39
+ - `surfaceVariant` / `onSurfaceVariant`: subtle fills and secondary text.
40
+ - `primary` / `onPrimary`: the brand color, for primary actions and selection.
41
+ - `outline`: borders and dividers.
42
+ - `error` / `onError`: errors and destructive actions.
43
+
44
+ **Type roles.** `display`, `headline`, `title`, `body`, `label`, `caption`. On iOS each maps to a Dynamic Type style; on Android and web, sizes scale with the user's font size setting.
45
+
46
+ **Fonts.** The apps use each platform's system font. Codegen doesn't support custom fonts yet.
47
+
48
+ ## Components
49
+
50
+ How each platform builds the shared patterns. Add a row whenever a new pattern appears.
51
+
52
+ | Pattern | iOS | Android | Web |
53
+ |---|---|---|---|
54
+ | Screen | Screen in a `NavigationStack`, with a navigation title | `Scaffold` with a `TopAppBar` | Page in the app shell (header and main) |
55
+ | Settings or form | `Form` with sections | Column with section headers | Sections in cards |
56
+ | Single choice | Inline `Picker` (checkmarks) | Radio buttons | Radio group |
57
+ | Card | `AppColors.surface` fill, `AppRadius.lg` corners | `Surface` in `AppTheme.colors.surface`, `AppRadius.lg` corners | `bg-surface rounded-lg` |
58
+ | Primary action | `.borderedProminent` button | `Button` | `button` with `bg-primary text-on-primary` |
59
+
60
+ ## Accessibility
61
+
62
+ - Support Dynamic Type (iOS), font scaling (Android) and browser zoom (web).
63
+ - Every icon-only button has a label from contracts/strings.
64
+ - Touch targets are at least 44 pt on iOS and 48 dp on Android.
65
+ - Text meets WCAG AA contrast (4.5:1 for body text) in light and dark mode.
@@ -0,0 +1,6 @@
1
+ # Framework commands (doctor, gen, check, run-*, parity) come from geeknative.
2
+ include .geeknative/framework.mk
3
+
4
+ .DEFAULT_GOAL := help
5
+
6
+ # Add your own targets below.
@@ -0,0 +1,40 @@
1
+ # Product
2
+
3
+ <!--
4
+ What the app is and who it's for. Your coding agent fills this in when you describe your app,
5
+ and keeps it current as the product changes. Keep it short: details belong in spec/features/.
6
+ -->
7
+
8
+ ## In one sentence
9
+
10
+ _Not described yet. Tell your coding agent what you want to build._
11
+
12
+ ## Who it's for
13
+
14
+ _Not described yet._
15
+
16
+ ## Features
17
+
18
+ | Feature | Spec | Summary |
19
+ |---|---|---|
20
+ | Home | [spec/features/home.md](spec/features/home.md) | Starter screen with a welcome card |
21
+ | Settings | [spec/features/settings.md](spec/features/settings.md) | Appearance (System, Light, Dark) and app version |
22
+
23
+ ## Platforms
24
+
25
+ | Platform | Built with | Minimum |
26
+ |---|---|---|
27
+ | iOS | SwiftUI | iOS 17 |
28
+ | Android | Kotlin, Jetpack Compose | Android 7.0 (API 24) |
29
+ | Web | React, TanStack Start | Safari 16.4, Chrome 111, Firefox 128 |
30
+
31
+ ## Glossary
32
+
33
+ Domain terms, defined once. Code uses these exact names on every platform.
34
+
35
+ | Term | Meaning |
36
+ |---|---|
37
+
38
+ ## Out of scope
39
+
40
+ _Nothing yet._
@@ -0,0 +1,28 @@
1
+ # {{APP_NAME}}
2
+
3
+ Native iOS, Android and web apps built from one spec by your coding agent, using [geeknative](https://www.npmjs.com/package/geeknative).
4
+
5
+ ## Get started
6
+
7
+ 1. `make doctor` checks Xcode, the Android SDK, Java and Node. `make doctor FIX=1` installs what it can.
8
+ 2. `make check` builds and tests all three apps.
9
+ 3. Open this folder in your coding agent (Claude Code, Codex, Cursor, Gemini CLI, ...) and describe what you want to build.
10
+
11
+ ## Run the apps
12
+
13
+ | Command | Opens |
14
+ |---|---|
15
+ | `make run-ios` | The iOS app in a simulator |
16
+ | `make run-android` | The Android app on an emulator or connected device |
17
+ | `make run-web` | The web app at http://localhost:3000 |
18
+
19
+ ## What's where
20
+
21
+ | Path | Contents |
22
+ |---|---|
23
+ | AGENTS.md | Instructions every coding agent follows |
24
+ | PRODUCT.md, DESIGN.md, spec/ | What the app does and how it looks |
25
+ | contracts/ | App identity, design tokens, copy and icons, compiled into each app by `make gen` |
26
+ | apps/ios, apps/android, apps/web | The three native apps |
27
+
28
+ Run `make` to see every command.
@@ -0,0 +1,33 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # geeknative logs
5
+ .geeknative/logs/
6
+
7
+ # iOS
8
+ apps/ios/.build/
9
+ xcuserdata/
10
+ *.xcuserstate
11
+
12
+ # Android
13
+ apps/android/.gradle/
14
+ apps/android/.kotlin/
15
+ apps/android/build/
16
+ apps/android/app/build/
17
+ apps/android/local.properties
18
+ apps/android/.idea/
19
+ *.iml
20
+ .cxx/
21
+
22
+ # Web
23
+ apps/web/dist/
24
+ apps/web/.output/
25
+ apps/web/.tanstack/
26
+ apps/web/.nitro/
27
+ apps/web/.vinxi/
28
+
29
+ # OS and editors
30
+ .DS_Store
31
+ .env
32
+ .env.*
33
+ !.env.example
@@ -0,0 +1,66 @@
1
+ <!-- geeknative:begin (managed by geeknative: replaced on upgrade. Add your own rules under "Project rules" at the end.) -->
2
+ # Android app (Kotlin + Jetpack Compose)
3
+
4
+ Read the root AGENTS.md first. This file covers how the Android app is built.
5
+
6
+ ## Stack
7
+
8
+ - Kotlin and Jetpack Compose with Material 3, Android 7.0 (API 24) and later (Navigation 3's minimum). Compiles against API 37.
9
+ - Navigation 3, ViewModel with StateFlow, Kotlin coroutines, DataStore.
10
+ - Dependencies are wired by hand in `AppContainer`: no DI framework.
11
+ - AGP 9 with built-in Kotlin. Gradle downloads JDK 21 for the build (gradle/gradle-daemon-jvm.properties).
12
+ - JUnit and kotlinx-coroutines-test for unit tests.
13
+
14
+ ## Layout
15
+
16
+ ```
17
+ apps/android/
18
+ gradle/libs.versions.toml All dependency versions
19
+ app/build.gradle.kts Reads app/identity.properties (generated) for the app ID and version
20
+ app/src/main/kotlin/app/
21
+ App.kt, MainActivity.kt Creates AppContainer; applies the theme and appearance
22
+ core/ AppContainer, navigation/ (Route, AppNavigation), designsystem/ (AppTheme, AppCard)
23
+ features/<name>/ <Name>Screen.kt, <Name>ViewModel.kt, <Name>Repository.kt, models
24
+ generated/ From contracts/ by `make gen`. Never edit.
25
+ app/src/main/res/ values/generated_*.xml are generated; everything else is yours
26
+ app/src/test/kotlin/app/ Unit tests, named after acceptance criteria
27
+ ```
28
+
29
+ The Kotlin package is always `app`. The app's identity is its application ID (from contracts/app.json), so renaming the app never moves code.
30
+
31
+ ## Patterns
32
+
33
+ Same names and responsibilities as the iOS and web apps.
34
+
35
+ | Concept | Android |
36
+ |---|---|
37
+ | Screen | `@Composable fun NotesScreen(...)` in features/notes/, plus a stateless `NotesContent` for previews |
38
+ | State and actions | `NotesViewModel : ViewModel()` exposing `uiState: StateFlow<NotesUiState>` and action functions |
39
+ | Data | `interface NotesRepository`, plus implementations such as `DataStoreNotesRepository` and `InMemoryNotesRepository` |
40
+ | Dependencies | `AppContainer` from `LocalAppContainer.current`; ViewModels are created with `viewModel { NotesViewModel(container.notesRepository) }` |
41
+ | Navigation | Add a `Route` object and an `entry<Route.Notes>` in AppNavigation; push with `backStack.add(Route.Notes)` |
42
+ | Copy | `stringResource(R.string.notes_empty_title)` (generated from contracts/strings) |
43
+ | Colors, spacing, radii, type | `AppTheme.colors.primary`, `AppSpacing.md`, `AppRadius.lg`, `AppTypography.title` |
44
+ | Icons | `Icon(AppIcons.settings, contentDescription = ...)` (generated) |
45
+ | Test IDs | `Modifier.testTag("notes.addButton")` |
46
+
47
+ Follow features/settings as the reference implementation.
48
+
49
+ ## Rules
50
+
51
+ - Never hardcode user-facing text, colors or spacing. Add them to contracts/ and run `make gen`.
52
+ - Prefer native Material 3 components and Android conventions: top app bars, the system back gesture, snackbars, bottom sheets. DESIGN.md says what must match the other platforms.
53
+ - Collect flows with `collectAsStateWithLifecycle()`. Keep screens stateless below the ViewModel so they can be previewed.
54
+ - Support font scaling, dark mode and TalkBack: icon-only buttons need a content description from strings.
55
+ - Name tests after acceptance criteria: ``fun `AC-notes-2 ...`()``.
56
+
57
+ ## Commands
58
+
59
+ - `make check-android`: builds the app and runs the unit tests. Full log: .geeknative/logs/android.log.
60
+ - `make run-android`: installs and launches the app on a running emulator or connected device.
61
+ - Open apps/android in Android Studio to use previews and the layout inspector.
62
+ <!-- geeknative:end -->
63
+
64
+ ## Project rules
65
+
66
+ <!-- Your Android-specific rules. They override the defaults above, and geeknative upgrades never touch them. -->
@@ -0,0 +1 @@
1
+ @AGENTS.md
@@ -0,0 +1,68 @@
1
+ import java.util.Properties
2
+
3
+ plugins {
4
+ alias(libs.plugins.android.application)
5
+ alias(libs.plugins.kotlin.compose)
6
+ alias(libs.plugins.kotlin.serialization)
7
+ }
8
+
9
+ // App name, ID and version come from contracts/app.json (generated by `make gen`).
10
+ val identity = Properties().apply {
11
+ load(providers.fileContents(layout.projectDirectory.file("identity.properties")).asText.get().reader())
12
+ }
13
+
14
+ android {
15
+ // The Kotlin package stays "app" in every project; the app's identity is applicationId.
16
+ namespace = "app"
17
+ compileSdk {
18
+ version = release(37)
19
+ }
20
+
21
+ defaultConfig {
22
+ applicationId = identity.getProperty("applicationId")
23
+ minSdk = 24
24
+ targetSdk = 37
25
+ versionCode = identity.getProperty("versionCode").toInt()
26
+ versionName = identity.getProperty("versionName")
27
+ }
28
+
29
+ buildTypes {
30
+ release {
31
+ isMinifyEnabled = false
32
+ }
33
+ }
34
+
35
+ compileOptions {
36
+ sourceCompatibility = JavaVersion.VERSION_17
37
+ targetCompatibility = JavaVersion.VERSION_17
38
+ }
39
+
40
+ buildFeatures {
41
+ compose = true
42
+ }
43
+
44
+ testOptions {
45
+ unitTests.isReturnDefaultValues = true
46
+ }
47
+ }
48
+
49
+ dependencies {
50
+ implementation(libs.androidx.core.ktx)
51
+ implementation(libs.androidx.activity.compose)
52
+ implementation(platform(libs.androidx.compose.bom))
53
+ implementation(libs.androidx.compose.ui)
54
+ implementation(libs.androidx.compose.ui.tooling.preview)
55
+ implementation(libs.androidx.compose.material3)
56
+ implementation(libs.androidx.compose.material.icons.core)
57
+ implementation(libs.androidx.lifecycle.runtime.compose)
58
+ implementation(libs.androidx.lifecycle.viewmodel.compose)
59
+ implementation(libs.androidx.lifecycle.viewmodel.navigation3)
60
+ implementation(libs.androidx.navigation3.runtime)
61
+ implementation(libs.androidx.navigation3.ui)
62
+ implementation(libs.androidx.datastore.preferences)
63
+ implementation(libs.kotlinx.serialization.core)
64
+ debugImplementation(libs.androidx.compose.ui.tooling)
65
+
66
+ testImplementation(libs.junit)
67
+ testImplementation(libs.kotlinx.coroutines.test)
68
+ }
@@ -0,0 +1,4 @@
1
+ # GENERATED by geeknative from contracts/app.json. Do not edit: change the contract, then run `make gen`.
2
+ applicationId=com.example.geeknative
3
+ versionName=1.0.0
4
+ versionCode=1