@stag-build/phonebook 0.1.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 +283 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/doctor.d.ts +33 -0
- package/dist/commands/doctor.js +690 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +27 -0
- package/dist/commands/init.js +415 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +1 -0
- package/dist/engines/android.d.ts +70 -0
- package/dist/engines/android.js +265 -0
- package/dist/engines/android.js.map +1 -0
- package/dist/engines/git.d.ts +4 -0
- package/dist/engines/git.js +13 -0
- package/dist/engines/git.js.map +1 -0
- package/dist/engines/ios.d.ts +55 -0
- package/dist/engines/ios.js +195 -0
- package/dist/engines/ios.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/gradle/catalog.d.ts +110 -0
- package/dist/gradle/catalog.js +413 -0
- package/dist/gradle/catalog.js.map +1 -0
- package/dist/ios/snapshotTestClass.d.ts +59 -0
- package/dist/ios/snapshotTestClass.js +195 -0
- package/dist/ios/snapshotTestClass.js.map +1 -0
- package/dist/manifest.d.ts +32 -0
- package/dist/manifest.js +23 -0
- package/dist/manifest.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +226 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/naming.d.ts +18 -0
- package/dist/naming.js +34 -0
- package/dist/naming.js.map +1 -0
- package/dist/scan/android.d.ts +7 -0
- package/dist/scan/android.js +222 -0
- package/dist/scan/android.js.map +1 -0
- package/dist/scan/hints.d.ts +25 -0
- package/dist/scan/hints.js +338 -0
- package/dist/scan/hints.js.map +1 -0
- package/dist/scan/ios.d.ts +7 -0
- package/dist/scan/ios.js +201 -0
- package/dist/scan/ios.js.map +1 -0
- package/dist/scan/types.d.ts +52 -0
- package/dist/scan/types.js +7 -0
- package/dist/scan/types.js.map +1 -0
- package/dist/site/build.d.ts +17 -0
- package/dist/site/build.js +0 -0
- package/dist/site/build.js.map +1 -0
- package/dist/versions.d.ts +71 -0
- package/dist/versions.js +334 -0
- package/dist/versions.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 stag.build
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @stag-build/phonebook
|
|
4
|
+
|
|
5
|
+
<p>A self-hosted, open-source alternative to Emerge Tools Snapshots: harvest your existing Compose <code>@Preview</code>s and SwiftUI <code>#Preview</code>s into a browsable, static HTML gallery — no SaaS account required.</p>
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
Phonebook turns screenshots your team already has into a Storybook-style component gallery. No new test code, no design tokens to maintain by hand — it renders what's already in your codebase into a static site designers can open without installing anything. Each repo runs Phonebook independently; v1 is single-platform, so one Android repo (or one iOS repo) produces one bundle and one site.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Zero new test code** — reuses `@Preview` / `#Preview` you've already written
|
|
16
|
+
- **No SaaS account** — self-hosted, runs entirely in your CI or locally
|
|
17
|
+
- **MCP-first** — a coding agent can check setup, analyze coverage, add missing previews, and build the gallery for you
|
|
18
|
+
- **Smart component grouping** — `component / state` cards inferred from preview names, no required annotation
|
|
19
|
+
- **Cross-platform** — Android (Roborazzi + ComposablePreviewScanner, runs on the JVM, no emulator) and iOS (SnapshotPreviews, runs on a simulator)
|
|
20
|
+
- **Version-aware setup** — `init`/`doctor` resolve library versions against your project's Kotlin version and catch Kotlin/Roborazzi metadata mismatches before they cause opaque compiler crashes
|
|
21
|
+
|
|
22
|
+
## Demo
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
*(replace with an actual screenshot of a generated gallery)*
|
|
27
|
+
|
|
28
|
+
## How it works
|
|
29
|
+
|
|
30
|
+
1. `phonebook generate` runs your platform's preview-rendering engine and harvests the output into a **bundle** (`manifest.json` + `images/`).
|
|
31
|
+
- Android: [Roborazzi](https://github.com/takahirom/roborazzi) + [ComposablePreviewScanner](https://github.com/sergio-sastre/ComposablePreviewScanner), run on the JVM via Robolectric. No emulator, works on Linux CI.
|
|
32
|
+
- iOS: [SnapshotPreviews](https://github.com/getsentry/SnapshotPreviews), run via `xcodebuild test` on a simulator. Requires macOS.
|
|
33
|
+
2. `phonebook build` turns that bundle into a static site — by default it writes `index.html` directly into the bundle directory (reusing the images already there, no copying), so the site lands at `<bundle>/index.html`. Pass `-o <dir>` to instead copy everything into a standalone site directory (for publishing elsewhere, or later merging multiple bundles). Plain HTML/CSS/JS, works from `file://` or any static host.
|
|
34
|
+
|
|
35
|
+
Install with `npm install -g @stag-build/phonebook`, or run without installing via `npx @stag-build/phonebook <cmd>`.
|
|
36
|
+
|
|
37
|
+
## Using it with a coding agent (recommended)
|
|
38
|
+
|
|
39
|
+
Most people won't run the CLI directly — Phonebook is built to be driven by a coding agent (Claude Code, Codex, etc.) through its MCP server. The agent adds previews, runs setup checks, and generates the gallery for you; the CLI underneath is the engine it calls.
|
|
40
|
+
|
|
41
|
+
The server runs via `npx @stag-build/phonebook mcp` — no install step needed. Pick your client below.
|
|
42
|
+
|
|
43
|
+
<details>
|
|
44
|
+
<summary><strong>Claude Code</strong></summary>
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
claude mcp add phonebook -- npx -y @stag-build/phonebook mcp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
</details>
|
|
51
|
+
|
|
52
|
+
<details>
|
|
53
|
+
<summary><strong>Codex CLI</strong></summary>
|
|
54
|
+
|
|
55
|
+
Add to `~/.codex/config.toml`:
|
|
56
|
+
|
|
57
|
+
```toml
|
|
58
|
+
[mcp_servers.phonebook]
|
|
59
|
+
command = "npx"
|
|
60
|
+
args = ["-y", "@stag-build/phonebook", "mcp"]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
</details>
|
|
64
|
+
|
|
65
|
+
<details>
|
|
66
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
67
|
+
|
|
68
|
+
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"phonebook": {
|
|
74
|
+
"command": "npx",
|
|
75
|
+
"args": ["-y", "@stag-build/phonebook", "mcp"]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
</details>
|
|
82
|
+
|
|
83
|
+
<details>
|
|
84
|
+
<summary><strong>Cursor</strong></summary>
|
|
85
|
+
|
|
86
|
+
Add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"phonebook": {
|
|
92
|
+
"command": "npx",
|
|
93
|
+
"args": ["-y", "@stag-build/phonebook", "mcp"]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
</details>
|
|
100
|
+
|
|
101
|
+
<details>
|
|
102
|
+
<summary><strong>Xcode (Codex Agent)</strong> — Xcode 26.3+</summary>
|
|
103
|
+
|
|
104
|
+
Add to `.codex/config.toml` at your project's workspace root. Xcode's agent runs with a minimal `PATH`, so the command wraps `npx` in a shell that adds the usual Homebrew/nvm locations first:
|
|
105
|
+
|
|
106
|
+
```toml
|
|
107
|
+
[mcp_servers.phonebook]
|
|
108
|
+
command = "/bin/zsh"
|
|
109
|
+
args = [
|
|
110
|
+
"-lc",
|
|
111
|
+
"PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin; npx -y @stag-build/phonebook mcp"
|
|
112
|
+
]
|
|
113
|
+
enabled = true
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
</details>
|
|
117
|
+
|
|
118
|
+
<details>
|
|
119
|
+
<summary><strong>Xcode (Claude Code Agent)</strong> — Xcode 26.3+</summary>
|
|
120
|
+
|
|
121
|
+
Add the `mcpServers` block to `~/Library/Developer/Xcode/CodingAssistant/ClaudeAgentConfig/.claude.json`:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"mcpServers": {
|
|
126
|
+
"phonebook": {
|
|
127
|
+
"command": "/bin/zsh",
|
|
128
|
+
"args": [
|
|
129
|
+
"-lc",
|
|
130
|
+
"PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin; npx -y @stag-build/phonebook mcp"
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
</details>
|
|
138
|
+
|
|
139
|
+
**Android Studio (Gemini Agent Mode):** not supported yet — its MCP integration only connects to remote `httpUrl` servers, not local stdio processes like Phonebook's. Use one of the terminal-based clients above (Claude Code, Codex CLI) from the Android repo instead.
|
|
140
|
+
|
|
141
|
+
Then, from a chat in your Android or iOS repo, just ask:
|
|
142
|
+
|
|
143
|
+
> "Use the phonebook MCP and create a catalog for my designer."
|
|
144
|
+
|
|
145
|
+
The agent figures out the rest — checking setup, filling in missing previews, generating, and building the site. For more targeted asks, it also exposes: `check_setup` (setup diagnosis, same as `phonebook doctor`), `analyze_coverage` (components missing previews or dark variants), `get_preview_guidance`, `run_generate`, and `run_build`.
|
|
146
|
+
|
|
147
|
+
## Quickstart: Android
|
|
148
|
+
|
|
149
|
+
Run `phonebook init` first — it detects your project's Kotlin version and prints these instructions with **library versions resolved to be compatible with it** (e.g. Kotlin 2.0 projects get Roborazzi 1.60.0; Kotlin 2.2+ gets the latest). The versions below are what a current-Kotlin project gets (see `samples/android/app/build.gradle.kts` for a full working example):
|
|
150
|
+
|
|
151
|
+
```kotlin
|
|
152
|
+
// app/build.gradle.kts
|
|
153
|
+
plugins {
|
|
154
|
+
id("io.github.takahirom.roborazzi") // root build.gradle.kts: version "1.72.0" apply false
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
roborazzi {
|
|
158
|
+
generateComposePreviewRobolectricTests {
|
|
159
|
+
enable = true
|
|
160
|
+
packages = listOf("dev.stag.phonebook.sample") // your app's package
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
dependencies {
|
|
165
|
+
testImplementation("org.robolectric:robolectric:4.14.1")
|
|
166
|
+
testImplementation("io.github.takahirom.roborazzi:roborazzi:1.72.0")
|
|
167
|
+
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose:1.72.0")
|
|
168
|
+
testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.3")
|
|
169
|
+
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose-preview-scanner-support:1.72.0")
|
|
170
|
+
testImplementation("androidx.compose.ui:ui-test-junit4") // version from your Compose BOM, or pin one
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Add a `phonebook.config.json` next to `settings.gradle.kts`:
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{
|
|
178
|
+
"appName": "My Android App",
|
|
179
|
+
"platform": "android",
|
|
180
|
+
"android": { "modules": [":app"], "variant": "debug" }
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Then, from the repo containing Phonebook:
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
npx @stag-build/phonebook generate -C /path/to/your/android/repo
|
|
188
|
+
npx @stag-build/phonebook build -C /path/to/your/android/repo
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Open `phonebook-out/index.html`.
|
|
192
|
+
|
|
193
|
+
## Quickstart: iOS
|
|
194
|
+
|
|
195
|
+
Add the [SnapshotPreviews](https://github.com/getsentry/SnapshotPreviews) SPM package to your project and a small XCTest target that subclasses `SnapshotTest` (see `samples/ios` for a full working example):
|
|
196
|
+
|
|
197
|
+
```swift
|
|
198
|
+
// PhonebookSnapshotTests.swift
|
|
199
|
+
import SnapshottingTests
|
|
200
|
+
|
|
201
|
+
final class PhonebookSnapshotTests: SnapshotTest {
|
|
202
|
+
override class func snapshotPreviews() -> [String]? {
|
|
203
|
+
return nil // record every #Preview
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Add `phonebook.config.json` next to your `.xcodeproj`:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"appName": "My iOS App",
|
|
213
|
+
"platform": "ios",
|
|
214
|
+
"ios": {
|
|
215
|
+
"project": "MyApp.xcodeproj",
|
|
216
|
+
"scheme": "MyApp",
|
|
217
|
+
"simulator": "iPhone 17 Pro"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Your scheme must build and test the snapshot test target (see `PhonebookSample.xcscheme` in the sample). Then:
|
|
223
|
+
|
|
224
|
+
```sh
|
|
225
|
+
npx @stag-build/phonebook generate -C /path/to/your/ios/repo
|
|
226
|
+
npx @stag-build/phonebook build -C /path/to/your/ios/repo
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Open `phonebook-out/index.html`.
|
|
230
|
+
|
|
231
|
+
## Naming convention
|
|
232
|
+
|
|
233
|
+
Phonebook groups screenshots into `component / state` cards from your existing preview names — no required annotation. See [docs/naming-convention.md](docs/naming-convention.md) for the full rules and examples.
|
|
234
|
+
|
|
235
|
+
## Configuration
|
|
236
|
+
|
|
237
|
+
`phonebook.config.json`:
|
|
238
|
+
|
|
239
|
+
| Key | Type | Default | Notes |
|
|
240
|
+
| --- | --- | --- | --- |
|
|
241
|
+
| `appName` | string | — | Required. Shown in the gallery header. |
|
|
242
|
+
| `platform` | `"android"` \| `"ios"` | — | Required. |
|
|
243
|
+
| `output` | string | `"phonebook-out"` | Bundle output directory, relative to the config file. |
|
|
244
|
+
| `android.modules` | string[] | `[":app"]` | Gradle modules to record. |
|
|
245
|
+
| `android.variant` | string | `"debug"` | Build variant; Phonebook runs `<module>:recordRoborazzi<Variant>`. |
|
|
246
|
+
| `ios.project` | string | — | Path to `.xcodeproj`, relative to the config file. One of `project`/`workspace` required. |
|
|
247
|
+
| `ios.workspace` | string | — | Path to `.xcworkspace`, relative to the config file. |
|
|
248
|
+
| `ios.scheme` | string | — | Required. Scheme that includes the SnapshotPreviews test target. |
|
|
249
|
+
| `ios.simulator` | string | `"iPhone 17 Pro"` | Simulator device name used for `-destination`. |
|
|
250
|
+
| `ios.onlyTesting` | string | auto-detected | `-only-testing:` filter so `generate` runs just the snapshot class, not the app's whole test suite. Auto-derived from the `SnapshotTest` subclass; set `""` to run everything. |
|
|
251
|
+
|
|
252
|
+
Both `generate` and `build` accept `-C <dir>` (project directory containing `phonebook.config.json`). `generate` takes `-o <dir>` to override the bundle output and `--allow-empty` to tolerate a run that records no previews. `build` takes an optional bundle path — with none, it uses the project's bundle directory — and `-o <dir>` for the site output; without `-o`, `build` writes `index.html` straight into the bundle directory and reuses its `images/` in place (no copying), which is what the quickstarts above do. Pass `-o <dir>` to instead copy the bundle's images into a separate, standalone site directory.
|
|
253
|
+
|
|
254
|
+
## `phonebook init` and `phonebook doctor`
|
|
255
|
+
|
|
256
|
+
`phonebook init` detects your platform and scaffolds `phonebook.config.json` plus the dependency/setup snippets — with library versions resolved against your project's Kotlin version and your app package filled in. It never edits your build files for you.
|
|
257
|
+
|
|
258
|
+
`phonebook doctor` checks that everything `generate` needs is wired up: plugin and test dependencies (resolved through Gradle version catalogs when you use them), the scanner's `packages` value, Kotlin/Roborazzi compatibility, and the toolchain (JDK/Xcode/simulator). Add `--deep` to also compile the test sources — slower, but authoritative when a static check and reality disagree. On iOS, if SnapshotPreviews is linked but no `SnapshotTest` subclass exists yet, `doctor` names the exact target and folder to add it to (parsed from the `.pbxproj`), so you're never just told to "add the class" with no location.
|
|
259
|
+
|
|
260
|
+
`phonebook init --write-snapshot-class` is the one exception to init's hands-off rule: when doctor's iOS check identifies the linking target *and* that target's source folder is one of Xcode's filesystem-synchronized groups, it writes `<folder>/PhonebookSnapshots.swift` directly — safe because a synchronized folder is picked up by Xcode automatically, so no `project.pbxproj` edit is made. It refuses (with the reason) in every other case: no SnapshotPreviews wiring yet, a non-synchronized-group project, or a subclass that already exists.
|
|
261
|
+
|
|
262
|
+
`phonebook mcp` runs the MCP server — see "Using it with a coding agent" above for setup and example prompts.
|
|
263
|
+
|
|
264
|
+
## Requirements
|
|
265
|
+
|
|
266
|
+
**Android**: JDK 17+. No emulator needed — Roborazzi renders on the JVM via Robolectric, so `generate` runs on Linux CI.
|
|
267
|
+
|
|
268
|
+
**iOS**: macOS with Xcode installed, plus a booted or bootable simulator (`generate` runs `xcodebuild test` against a named simulator destination). Requires a macOS runner in CI.
|
|
269
|
+
|
|
270
|
+
See [docs/ci.md](docs/ci.md) for CI recipes and [docs/naming-convention.md](docs/naming-convention.md) for the naming rules.
|
|
271
|
+
|
|
272
|
+
## Roadmap
|
|
273
|
+
|
|
274
|
+
Post-v1 (M5), not yet built:
|
|
275
|
+
|
|
276
|
+
- [ ] Search and filters in the generated gallery
|
|
277
|
+
- [ ] Multi-bundle merge with a side-by-side view (cross-platform sites)
|
|
278
|
+
- [ ] Version diffing between two runs (the manifest already carries commit + image hashes to enable this)
|
|
279
|
+
- [ ] Additional CI recipe docs
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT — see [LICENSE](LICENSE).
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { loadConfig } from './config.js';
|
|
6
|
+
import { runDoctor } from './commands/doctor.js';
|
|
7
|
+
import { runInit } from './commands/init.js';
|
|
8
|
+
import { generateAndroid } from './engines/android.js';
|
|
9
|
+
import { generateIos } from './engines/ios.js';
|
|
10
|
+
import { buildSite } from './site/build.js';
|
|
11
|
+
import { runMcpServer } from './mcp/server.js';
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name('phonebook')
|
|
15
|
+
.description('Static component gallery generated from native preview screenshots');
|
|
16
|
+
program
|
|
17
|
+
.command('generate')
|
|
18
|
+
.description('Render all previews and produce a bundle (manifest + images)')
|
|
19
|
+
.option('-C, --dir <dir>', 'project directory containing phonebook.config.json', '.')
|
|
20
|
+
.option('-o, --output <dir>', 'bundle output directory (default from config, else phonebook-out)')
|
|
21
|
+
.option('--allow-empty', 'write an empty manifest (with a warning) instead of failing when no previews are recorded', false)
|
|
22
|
+
.action(async (opts) => {
|
|
23
|
+
const { config, projectDir } = await loadConfig(opts.dir);
|
|
24
|
+
const outputDir = resolve(projectDir, opts.output ?? config.output ?? 'phonebook-out');
|
|
25
|
+
const generate = config.platform === 'android' ? generateAndroid : generateIos;
|
|
26
|
+
const manifest = await generate(config, projectDir, outputDir, { allowEmpty: opts.allowEmpty });
|
|
27
|
+
console.log(`Recorded ${manifest.entries.length} previews -> ${outputDir}`);
|
|
28
|
+
});
|
|
29
|
+
program
|
|
30
|
+
.command('build')
|
|
31
|
+
.description('Build the static gallery site from a bundle')
|
|
32
|
+
.argument('[bundle]', 'bundle directory produced by `phonebook generate` (default: the output dir from phonebook.config.json)')
|
|
33
|
+
.option('-C, --dir <dir>', 'project directory containing phonebook.config.json', '.')
|
|
34
|
+
.option('-o, --output <dir>', 'site output directory (default: write index.html into the bundle directory itself, reusing its images/ with no copying)')
|
|
35
|
+
.action(async (bundle, opts) => {
|
|
36
|
+
let bundleDir;
|
|
37
|
+
if (bundle) {
|
|
38
|
+
bundleDir = resolve(bundle);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const { config, projectDir } = await loadConfig(opts.dir);
|
|
42
|
+
bundleDir = resolve(projectDir, config.output ?? 'phonebook-out');
|
|
43
|
+
if (!existsSync(join(bundleDir, 'manifest.json'))) {
|
|
44
|
+
throw new Error(`No bundle found at ${bundleDir}. Run \`phonebook generate\` first, ` +
|
|
45
|
+
'or pass a bundle directory explicitly.');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const outDir = opts.output ? resolve(opts.output) : bundleDir;
|
|
49
|
+
const count = await buildSite(bundleDir, outDir);
|
|
50
|
+
console.log(`Built gallery with ${count} screenshots -> ${outDir}/index.html`);
|
|
51
|
+
});
|
|
52
|
+
program
|
|
53
|
+
.command('init')
|
|
54
|
+
.description('Detect the platform and scaffold phonebook.config.json')
|
|
55
|
+
.option('-C, --dir <dir>', 'project directory to initialize', '.')
|
|
56
|
+
.option('--write-snapshot-class', 'iOS only: write the missing SnapshotTest subclass, but only when the linking target uses Xcode ' +
|
|
57
|
+
"synchronized groups (so no project.pbxproj edit is needed) — the one exception to init's " +
|
|
58
|
+
'otherwise hands-off behavior', false)
|
|
59
|
+
.action(async (opts) => {
|
|
60
|
+
await runInit(opts.dir, { writeSnapshotClass: opts.writeSnapshotClass });
|
|
61
|
+
});
|
|
62
|
+
program
|
|
63
|
+
.command('doctor')
|
|
64
|
+
.description('Check that the project is correctly set up for `phonebook generate`')
|
|
65
|
+
.option('-C, --dir <dir>', 'project directory containing phonebook.config.json', '.')
|
|
66
|
+
.option('--deep', 'also compile the test sources (slower, catches build-time errors)', false)
|
|
67
|
+
.action(async (opts) => {
|
|
68
|
+
const ok = await runDoctor(opts.dir, { deep: opts.deep });
|
|
69
|
+
if (!ok)
|
|
70
|
+
process.exit(1);
|
|
71
|
+
});
|
|
72
|
+
program
|
|
73
|
+
.command('mcp')
|
|
74
|
+
.description('Run the Phonebook MCP server (stdio)')
|
|
75
|
+
.action(async () => {
|
|
76
|
+
await runMcpServer();
|
|
77
|
+
});
|
|
78
|
+
program.parseAsync().catch((err) => {
|
|
79
|
+
console.error(`error: ${err.message}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { findMissingTestHostNote, findSnapshotTestClassLocation, findSnapshotTestSubclass } from '../ios/snapshotTestClass.js';
|
|
2
|
+
export { findMissingTestHostNote, findSnapshotTestClassLocation, findSnapshotTestSubclass };
|
|
3
|
+
/** Parses the major version number out of `java -version`'s output (printed to stderr). */
|
|
4
|
+
export declare function parseJavaMajorVersion(versionOutput: string): number | undefined;
|
|
5
|
+
/** Parses the "Schemes:" section out of `xcodebuild -list` output. */
|
|
6
|
+
export declare function parseXcodeSchemes(listOutput: string): string[];
|
|
7
|
+
/** Parses device names out of `xcrun simctl list devices available` output. */
|
|
8
|
+
export declare function parseAvailableSimulatorNames(simctlOutput: string): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Runs the same checks as `phonebook doctor` and returns the printed lines plus
|
|
11
|
+
* overall pass/fail, without writing to stdout. Shared by the CLI command and
|
|
12
|
+
* the MCP `check_setup` tool.
|
|
13
|
+
*/
|
|
14
|
+
export declare function collectDoctorChecks(dir: string, options?: {
|
|
15
|
+
deep?: boolean;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
lines: string[];
|
|
18
|
+
ok: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function runDoctor(dir: string, options?: {
|
|
21
|
+
deep?: boolean;
|
|
22
|
+
}): Promise<boolean>;
|
|
23
|
+
/**
|
|
24
|
+
* Extracts the values configured in a `packages = listOf(...)` (Kotlin DSL)
|
|
25
|
+
* or `packages = ["a", "b"]` (Groovy) block. Returns undefined if no
|
|
26
|
+
* `packages = ...` assignment is present at all (as opposed to an empty list).
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractConfiguredPackages(gradleText: string): string[] | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Extracts compiler `e:` error lines from raw build output, dropping
|
|
31
|
+
* stack-frame continuation lines (indented, starting with "at ").
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractCompilerErrors(output: string): string[];
|