nativeapptemplate-agent 0.1.2 → 0.2.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 (108) hide show
  1. package/CLAUDE.md +10 -8
  2. package/README.md +65 -21
  3. package/dist/agents/judge.d.ts +8 -0
  4. package/dist/agents/judge.d.ts.map +1 -1
  5. package/dist/agents/judge.js +167 -29
  6. package/dist/agents/judge.js.map +1 -1
  7. package/dist/agents/repair.d.ts +14 -0
  8. package/dist/agents/repair.d.ts.map +1 -0
  9. package/dist/agents/repair.js +103 -0
  10. package/dist/agents/repair.js.map +1 -0
  11. package/dist/agents/types.d.ts +34 -1
  12. package/dist/agents/types.d.ts.map +1 -1
  13. package/dist/dispatch.d.ts +21 -2
  14. package/dist/dispatch.d.ts.map +1 -1
  15. package/dist/dispatch.js +250 -14
  16. package/dist/dispatch.js.map +1 -1
  17. package/dist/env-bridge.d.ts +17 -0
  18. package/dist/env-bridge.d.ts.map +1 -0
  19. package/dist/env-bridge.js +211 -0
  20. package/dist/env-bridge.js.map +1 -0
  21. package/dist/index.d.ts +11 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +88 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp.d.ts +5 -0
  26. package/dist/mcp.d.ts.map +1 -0
  27. package/dist/mcp.js +83 -0
  28. package/dist/mcp.js.map +1 -0
  29. package/dist/mobile.d.ts +31 -0
  30. package/dist/mobile.d.ts.map +1 -0
  31. package/dist/mobile.js +179 -0
  32. package/dist/mobile.js.map +1 -0
  33. package/dist/rails-lifecycle.d.ts +16 -0
  34. package/dist/rails-lifecycle.d.ts.map +1 -0
  35. package/dist/rails-lifecycle.js +184 -0
  36. package/dist/rails-lifecycle.js.map +1 -0
  37. package/dist/rename-overrides.d.ts +23 -0
  38. package/dist/rename-overrides.d.ts.map +1 -0
  39. package/dist/rename-overrides.js +58 -0
  40. package/dist/rename-overrides.js.map +1 -0
  41. package/dist/repair-loop.d.ts +25 -0
  42. package/dist/repair-loop.d.ts.map +1 -0
  43. package/dist/repair-loop.js +67 -0
  44. package/dist/repair-loop.js.map +1 -0
  45. package/dist/report/collect.d.ts +28 -0
  46. package/dist/report/collect.d.ts.map +1 -0
  47. package/dist/report/collect.js +117 -0
  48. package/dist/report/collect.js.map +1 -0
  49. package/dist/report/model.d.ts +49 -0
  50. package/dist/report/model.d.ts.map +1 -0
  51. package/dist/report/model.js +2 -0
  52. package/dist/report/model.js.map +1 -0
  53. package/dist/report/render.d.ts +3 -0
  54. package/dist/report/render.d.ts.map +1 -0
  55. package/dist/report/render.js +242 -0
  56. package/dist/report/render.js.map +1 -0
  57. package/dist/report/theme.d.ts +2 -0
  58. package/dist/report/theme.d.ts.map +1 -0
  59. package/dist/report/theme.js +115 -0
  60. package/dist/report/theme.js.map +1 -0
  61. package/dist/slug.d.ts +3 -0
  62. package/dist/slug.d.ts.map +1 -1
  63. package/dist/slug.js +30 -0
  64. package/dist/slug.js.map +1 -1
  65. package/dist/validation/capture.d.ts.map +1 -1
  66. package/dist/validation/capture.js +19 -2
  67. package/dist/validation/capture.js.map +1 -1
  68. package/dist/validation/discover.js +1 -1
  69. package/dist/validation/discover.js.map +1 -1
  70. package/dist/validation/index.d.ts +8 -2
  71. package/dist/validation/index.d.ts.map +1 -1
  72. package/dist/validation/index.js +4 -1
  73. package/dist/validation/index.js.map +1 -1
  74. package/dist/validation/launch.d.ts +10 -0
  75. package/dist/validation/launch.d.ts.map +1 -1
  76. package/dist/validation/launch.js +127 -12
  77. package/dist/validation/launch.js.map +1 -1
  78. package/dist/validation/layer1.d.ts.map +1 -1
  79. package/dist/validation/layer1.js +12 -4
  80. package/dist/validation/layer1.js.map +1 -1
  81. package/dist/validation/layer2.js +1 -1
  82. package/dist/validation/layer2.js.map +1 -1
  83. package/dist/validation/scenarios/queue.d.ts +12 -0
  84. package/dist/validation/scenarios/queue.d.ts.map +1 -0
  85. package/dist/validation/scenarios/queue.js +216 -0
  86. package/dist/validation/scenarios/queue.js.map +1 -0
  87. package/dist/validation/stage1.d.ts +2 -0
  88. package/dist/validation/stage1.d.ts.map +1 -1
  89. package/dist/validation/stage1.js +2 -0
  90. package/dist/validation/stage1.js.map +1 -1
  91. package/dist/validation/stage2-judge.d.ts +23 -0
  92. package/dist/validation/stage2-judge.d.ts.map +1 -0
  93. package/dist/validation/stage2-judge.js +225 -0
  94. package/dist/validation/stage2-judge.js.map +1 -0
  95. package/dist/validation/stage2.d.ts +74 -0
  96. package/dist/validation/stage2.d.ts.map +1 -0
  97. package/dist/validation/stage2.js +328 -0
  98. package/dist/validation/stage2.js.map +1 -0
  99. package/dist/validation/visual-judge.d.ts +37 -0
  100. package/dist/validation/visual-judge.d.ts.map +1 -1
  101. package/dist/validation/visual-judge.js +100 -16
  102. package/dist/validation/visual-judge.js.map +1 -1
  103. package/dist/version.d.ts +2 -0
  104. package/dist/version.d.ts.map +1 -0
  105. package/dist/version.js +17 -0
  106. package/dist/version.js.map +1 -0
  107. package/package.json +6 -3
  108. package/scripts/ruby/rename.rb +7 -3
@@ -0,0 +1,211 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { slugToPascal } from "./slug.js";
5
+ // Bridges substrate-named API config (NATIVEAPPTEMPLATE_API_*) to the
6
+ // renamed-product equivalents (<PRODUCT>_API_*) so the agent's auto-
7
+ // validation runs see the right values without forcing the user to
8
+ // hand-set N env vars per generated app.
9
+ //
10
+ // Three sinks the agent has to feed:
11
+ // 1. ~/.gradle/gradle.properties (so Android Studio + manual ./gradlew
12
+ // see the value when the user opens the generated project later)
13
+ // 2. ORG_GRADLE_PROJECT_<KEY>=<value> in the child env when the agent
14
+ // itself spawns ./gradlew (Layer 2 build mode, defensive — works
15
+ // even if gradle.properties is mid-write or the user hasn't saved)
16
+ // 3. SIMCTL_CHILD_<KEY>=<value> in the child env when the agent spawns
17
+ // `xcrun simctl launch` (so the env var reaches the running iOS
18
+ // app via simctl's child-env passthrough convention)
19
+ //
20
+ // Source of truth, by suffix:
21
+ // API_DOMAIN ← $NATIVEAPPTEMPLATE_API/.env HOST
22
+ // API_PORT ← $NATIVEAPPTEMPLATE_API/.env PORT
23
+ // API_SCHEME ← shell env NATIVEAPPTEMPLATE_API_SCHEME, fall back to
24
+ // ~/.gradle/gradle.properties (since the substrate's
25
+ // Rails .env doesn't carry SCHEME)
26
+ //
27
+ // Single source of truth: the substrate's own Rails .env (where Rails
28
+ // actually binds). The mobile clients connect to exactly that host:port,
29
+ // so reading from .env eliminates the drift risk of a parallel shell
30
+ // export. User maintains one file, .env, for their dev-network setup.
31
+ // The three keys we mirror. Adding a new one here automatically threads
32
+ // it through all three sinks. Listed in the same order they appear in
33
+ // the substrate's gradle.properties / .env.sample so diffs read
34
+ // naturally.
35
+ const MIRRORED_SUFFIXES = ["API_DOMAIN", "API_PORT", "API_SCHEME"];
36
+ const SENTINEL_BEGIN = "# BEGIN nativeapptemplate-agent (mirrored API config — do not edit by hand)";
37
+ const SENTINEL_END = "# END nativeapptemplate-agent";
38
+ export function productTokenFor(domain) {
39
+ return slugToPascal(domain.slug).toUpperCase();
40
+ }
41
+ // Read the substrate side of the bridge. Returns only the keys whose
42
+ // source-of-truth carries a non-empty value — silently omits anything
43
+ // missing. Caller decides what to do with empty results.
44
+ export async function readSubstrateApiVars() {
45
+ const result = {};
46
+ // DOMAIN + PORT come from the substrate's Rails .env (HOST + PORT —
47
+ // where Rails actually binds). Mobile clients must connect to that
48
+ // exact host:port, so this is the single source of truth.
49
+ const substratePath = process.env['NATIVEAPPTEMPLATE_API'];
50
+ if (substratePath) {
51
+ const env = await readDotenv(join(substratePath, ".env"));
52
+ if (env['HOST'])
53
+ result['API_DOMAIN'] = env['HOST'];
54
+ if (env['PORT'])
55
+ result['API_PORT'] = env['PORT'];
56
+ }
57
+ // SCHEME isn't in the Rails .env — pull it from the user's shell
58
+ // env, fall back to ~/.gradle/gradle.properties, fall back to nothing
59
+ // (caller treats missing as "use the app's compiled-in default").
60
+ const shellScheme = process.env['NATIVEAPPTEMPLATE_API_SCHEME'];
61
+ if (shellScheme) {
62
+ result['API_SCHEME'] = shellScheme;
63
+ }
64
+ else {
65
+ const gradleScheme = (await readGradleProperties())['NATIVEAPPTEMPLATE_API_SCHEME'];
66
+ if (gradleScheme)
67
+ result['API_SCHEME'] = gradleScheme;
68
+ }
69
+ return result;
70
+ }
71
+ async function readDotenv(path) {
72
+ const raw = await readFileOrEmpty(path);
73
+ const out = {};
74
+ for (const line of raw.split("\n")) {
75
+ const trimmed = line.trim();
76
+ if (!trimmed || trimmed.startsWith("#"))
77
+ continue;
78
+ const eq = trimmed.indexOf("=");
79
+ if (eq < 1)
80
+ continue;
81
+ const key = trimmed.slice(0, eq).trim();
82
+ const valueRaw = trimmed.slice(eq + 1).trim();
83
+ const value = valueRaw.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
84
+ if (key && value)
85
+ out[key] = value;
86
+ }
87
+ return out;
88
+ }
89
+ export async function buildBridgeValues(domain) {
90
+ const product = productTokenFor(domain);
91
+ const substrate = await readSubstrateApiVars();
92
+ const values = {};
93
+ for (const [suffix, value] of Object.entries(substrate)) {
94
+ values[`${product}_${suffix}`] = value;
95
+ }
96
+ return { values };
97
+ }
98
+ // Set ORG_GRADLE_PROJECT_<KEY> and SIMCTL_CHILD_<KEY> entries in the
99
+ // agent's own process.env so any subsequent child spawn that inherits
100
+ // process.env (via env.ts's scrubbedEnv) sees them. Idempotent — calling
101
+ // twice with the same domain is a no-op.
102
+ export function applyBridgeToProcessEnv(bridge) {
103
+ for (const [key, value] of Object.entries(bridge.values)) {
104
+ process.env[`ORG_GRADLE_PROJECT_${key}`] = value;
105
+ process.env[`SIMCTL_CHILD_${key}`] = value;
106
+ }
107
+ }
108
+ // Write a sentinel block to ~/.gradle/gradle.properties containing the
109
+ // mirrored keys. Replaces an existing block from a prior run; preserves
110
+ // every line outside the block. If gradle.properties doesn't exist,
111
+ // creates it. If the bridge has no values (substrate vars unset),
112
+ // removes the existing block (so stale values from a prior run don't
113
+ // linger) and writes nothing else.
114
+ //
115
+ // Safety knobs (env vars, both default to off):
116
+ // NATIVEAPPTEMPLATE_BRIDGE=off — skip the file write entirely;
117
+ // process.env injection still
118
+ // runs for the agent's own
119
+ // child-spawn paths.
120
+ // NATIVEAPPTEMPLATE_BRIDGE_DRY_RUN=1 — log what would be written
121
+ // instead of writing. Useful for
122
+ // users who want to inspect the
123
+ // bridge's behavior before
124
+ // giving it write access to
125
+ // their user-global gradle.
126
+ export async function syncGradleProperties(bridge) {
127
+ const path = join(homedir(), ".gradle", "gradle.properties");
128
+ if (process.env['NATIVEAPPTEMPLATE_BRIDGE'] === "off") {
129
+ return { path, mode: "skipped", removedStale: false };
130
+ }
131
+ const existing = await readFileOrEmpty(path);
132
+ const stripped = removeSentinelBlock(existing);
133
+ const hasValues = Object.keys(bridge.values).length > 0;
134
+ const removedStale = stripped !== existing && !hasValues;
135
+ let next;
136
+ if (!hasValues) {
137
+ next = stripped;
138
+ }
139
+ else {
140
+ const block = formatSentinelBlock(bridge.values);
141
+ next = ensureTrailingNewline(stripped) + block + "\n";
142
+ }
143
+ const dryRun = process.env['NATIVEAPPTEMPLATE_BRIDGE_DRY_RUN'] === "1";
144
+ if (next === existing) {
145
+ return { path, mode: "noop", removedStale };
146
+ }
147
+ if (dryRun) {
148
+ return { path, mode: "dry-run", removedStale, preview: next };
149
+ }
150
+ await writeFile(path, next);
151
+ return { path, mode: hasValues ? "wrote" : "noop", removedStale };
152
+ }
153
+ async function readGradleProperties() {
154
+ const path = join(homedir(), ".gradle", "gradle.properties");
155
+ const raw = await readFileOrEmpty(path);
156
+ const out = {};
157
+ for (const line of raw.split("\n")) {
158
+ const trimmed = line.trim();
159
+ if (!trimmed || trimmed.startsWith("#"))
160
+ continue;
161
+ const eq = trimmed.indexOf("=");
162
+ if (eq < 1)
163
+ continue;
164
+ const key = trimmed.slice(0, eq).trim();
165
+ const value = trimmed.slice(eq + 1).trim();
166
+ if (key && value)
167
+ out[key] = value;
168
+ }
169
+ return out;
170
+ }
171
+ async function readFileOrEmpty(path) {
172
+ try {
173
+ return await readFile(path, "utf8");
174
+ }
175
+ catch {
176
+ return "";
177
+ }
178
+ }
179
+ function removeSentinelBlock(content) {
180
+ const beginIdx = content.indexOf(SENTINEL_BEGIN);
181
+ if (beginIdx === -1)
182
+ return content;
183
+ const endIdx = content.indexOf(SENTINEL_END, beginIdx);
184
+ if (endIdx === -1)
185
+ return content;
186
+ const after = endIdx + SENTINEL_END.length;
187
+ // Also consume the newline after END if present, so removing leaves
188
+ // no orphan blank line.
189
+ const trailing = content[after] === "\n" ? after + 1 : after;
190
+ // And consume one preceding blank-separator newline so we don't
191
+ // accumulate blanks across cycles.
192
+ let before = beginIdx;
193
+ if (before > 0 && content[before - 1] === "\n" && content[before - 2] === "\n") {
194
+ before -= 1;
195
+ }
196
+ return content.slice(0, before) + content.slice(trailing);
197
+ }
198
+ function formatSentinelBlock(values) {
199
+ const lines = [SENTINEL_BEGIN];
200
+ for (const key of Object.keys(values).sort()) {
201
+ lines.push(`${key}=${values[key]}`);
202
+ }
203
+ lines.push(SENTINEL_END);
204
+ return lines.join("\n");
205
+ }
206
+ function ensureTrailingNewline(s) {
207
+ if (s === "")
208
+ return "";
209
+ return s.endsWith("\n") ? s : s + "\n";
210
+ }
211
+ //# sourceMappingURL=env-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-bridge.js","sourceRoot":"","sources":["../src/env-bridge.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ import { type DispatchReportOptions } from "./dispatch.js";
3
+ import type { RenamePair } from "./agents/types.js";
4
+ export type ParsedArgs = {
5
+ spec: string;
6
+ report: DispatchReportOptions;
7
+ open: boolean;
8
+ exitZero: boolean;
9
+ renameOverrides: RenamePair[];
10
+ projectName?: string;
11
+ };
12
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs;
2
13
  export declare function main(spec?: string): Promise<void>;
3
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAQA,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAY,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAIrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAmC7D;AAED,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoDvD"}
package/dist/index.js CHANGED
@@ -1,23 +1,108 @@
1
1
  #!/usr/bin/env node
2
2
  import { realpathSync } from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
+ import { spawn } from "node:child_process";
4
5
  import { dispatch } from "./dispatch.js";
5
6
  import { loadDotenvIfPresent } from "./env.js";
7
+ import { parseRenamePair } from "./rename-overrides.js";
8
+ import { projectNameToSlug, slugToPascal, isValidSlug } from "./slug.js";
6
9
  loadDotenvIfPresent();
10
+ export function parseArgs(argv) {
11
+ const specParts = [];
12
+ const report = {};
13
+ const renameOverrides = [];
14
+ let open = false;
15
+ let exitZero = false;
16
+ let projectName;
17
+ for (let i = 0; i < argv.length; i++) {
18
+ const arg = argv[i];
19
+ if (arg === undefined)
20
+ continue;
21
+ if (arg === "--no-report")
22
+ report.enabled = false;
23
+ else if (arg === "--report-open")
24
+ open = true;
25
+ else if (arg === "--exit-zero")
26
+ exitZero = true;
27
+ else if (arg.startsWith("--report-format=")) {
28
+ const value = arg.slice("--report-format=".length);
29
+ if (value === "html" || value === "json" || value === "both")
30
+ report.format = value;
31
+ }
32
+ else if (arg.startsWith("--report-embed=")) {
33
+ report.embed = arg.slice("--report-embed=".length) !== "false";
34
+ }
35
+ else if (arg === "--rename" || arg.startsWith("--rename=")) {
36
+ // Accept both `--rename From=To` (per ROADMAP) and `--rename=From=To`.
37
+ const raw = arg === "--rename" ? argv[++i] : arg.slice("--rename=".length);
38
+ const pair = parseRenamePair(raw);
39
+ if (pair)
40
+ renameOverrides.push(pair);
41
+ else
42
+ console.error(`warning: ignoring malformed --rename "${raw ?? ""}" (expected From=To, e.g. --rename Shop=Clinic)`);
43
+ }
44
+ else if (arg === "--project-name" || arg.startsWith("--project-name=")) {
45
+ const raw = (arg === "--project-name" ? argv[++i] : arg.slice("--project-name=".length))?.trim();
46
+ // Accept any of "Vet Clinic" / "VetClinic" / "vet-clinic" — the slug is
47
+ // derived in dispatch. Reject only if it yields no valid slug at all.
48
+ if (raw && isValidSlug(projectNameToSlug(raw)))
49
+ projectName = raw;
50
+ else
51
+ console.error(`warning: ignoring invalid --project-name "${raw ?? ""}" (e.g. --project-name="Vet Clinic")`);
52
+ }
53
+ else {
54
+ specParts.push(arg);
55
+ }
56
+ }
57
+ return { spec: specParts.join(" ").trim(), report, open, exitZero, renameOverrides, ...(projectName !== undefined ? { projectName } : {}) };
58
+ }
7
59
  export async function main(spec) {
8
- const input = spec ?? process.argv.slice(2).join(" ").trim();
60
+ const parsed = parseArgs(process.argv.slice(2));
61
+ const input = (spec ?? parsed.spec).trim();
9
62
  if (!input) {
10
- console.error('Usage: nativeapptemplate-agent "your spec here"');
63
+ console.error('Usage: nativeapptemplate-agent "your spec here" [--project-name="Vet Clinic"] [--rename From=To]... [--no-report] [--report-format=html|json|both] [--report-embed=true|false] [--report-open] [--exit-zero]');
11
64
  process.exitCode = 1;
12
65
  return;
13
66
  }
14
67
  console.log(`nativeapptemplate-agent: received spec: ${input}`);
15
68
  console.log('(tail tmp/trace/*.log in a tiled view via scripts/demo-tmux.sh)');
16
- const result = await dispatch(input);
69
+ const result = await dispatch(input, {
70
+ report: parsed.report,
71
+ renameOverrides: parsed.renameOverrides,
72
+ ...(parsed.projectName !== undefined ? { projectName: parsed.projectName } : {}),
73
+ });
74
+ if (parsed.projectName !== undefined) {
75
+ const finalSlug = result.report.meta.slug;
76
+ console.log(`project: ${slugToPascal(finalSlug)} (output out/${finalSlug}/)`);
77
+ }
78
+ for (const o of result.renameOverrideOutcomes) {
79
+ if (o.kind === "changed")
80
+ console.log(`override: ${o.from} → ${o.to} (overrode planner's "${o.was}")`);
81
+ else if (o.kind === "noop")
82
+ console.log(`override: ${o.from} → ${o.to} (already the planner's pick)`);
83
+ else {
84
+ const sources = result.report.domain.renamePlan.map((p) => p.from).join(", ");
85
+ console.error(`warning: --rename ${o.from}=${o.to} matched no planned rename — skipped (renamable: ${sources || "none"})`);
86
+ }
87
+ }
17
88
  console.log('');
18
89
  console.log('=== run complete ===');
19
90
  console.log(`result: ${result.summary}`);
20
91
  console.log(`overall: ${result.overallPass ? 'PASS' : 'FAIL'}`);
92
+ if (result.reportPaths.htmlPath) {
93
+ console.log(`report: file://${result.reportPaths.htmlPath}`);
94
+ if (parsed.open && process.platform === 'darwin') {
95
+ spawn('open', [result.reportPaths.htmlPath], { stdio: 'ignore', detached: true }).unref();
96
+ }
97
+ }
98
+ else if (result.reportPaths.jsonPath) {
99
+ console.log(`report: ${result.reportPaths.jsonPath}`);
100
+ }
101
+ // Non-zero exit on validation failure so CI / shell `&&` chains catch
102
+ // it. --exit-zero opts out (e.g. when you only want the report).
103
+ if (!result.overallPass && !parsed.exitZero) {
104
+ process.exitCode = 1;
105
+ }
21
106
  }
22
107
  // Entry guard: run main() when this file is the program entry point. Resolve
23
108
  // both sides to canonical (symlink-followed) paths so npm bin symlinks (e.g.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,mBAAmB,EAAE,CAAC;AAEtB,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAa;IACtC,MAAM,KAAK,GAAG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACjE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;IAE/E,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC;IAErC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,0EAA0E;AAC1E,+BAA+B;AAC/B,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACtC,OAAO,SAAS,KAAK,UAAU,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function createMcpServer(): McpServer;
4
+ export declare function main(): Promise<void>;
5
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAcpE,wBAAgB,eAAe,IAAI,SAAS,CAsD3C;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAK1C"}
package/dist/mcp.js ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ import { realpathSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ import { dispatch } from "./dispatch.js";
8
+ import { loadDotenvIfPresent } from "./env.js";
9
+ import { readPackageVersion } from "./version.js";
10
+ // MCP surface (MONETIZATION.md §"MCP as a distribution surface"):
11
+ // thin wrapper around dispatch() so any MCP-compatible AI assistant
12
+ // (Claude Code, Cursor, Cline, Continue, Goose, ...) can invoke the
13
+ // agent as a tool. Same backend as the CLI; different wire format.
14
+ //
15
+ // Run via: `npx -y nativeapptemplate-agent-mcp` from an MCP client config.
16
+ export function createMcpServer() {
17
+ const server = new McpServer({
18
+ name: "nativeapptemplate-agent",
19
+ version: readPackageVersion(),
20
+ });
21
+ server.registerTool("generate_app", {
22
+ title: "Generate three-platform SaaS app",
23
+ description: "Generate a working three-platform SaaS app from a natural-language spec — Rails 8.1 API + SwiftUI iOS + Jetpack Compose Android. Validated end-to-end (rename completeness, build, vision judge). Output lands in ./out/<spec-slug>/{rails,ios,android}/. Each invocation runs ~3-5 minutes and consumes Anthropic API usage on the configured key, separate from the calling assistant's session.",
24
+ inputSchema: {
25
+ spec: z
26
+ .string()
27
+ .min(1)
28
+ .describe('Natural-language SaaS spec, e.g. "a walk-in queue for a barbershop"'),
29
+ renameOverrides: z
30
+ .array(z.object({ from: z.string().min(1), to: z.string().min(1) }))
31
+ .optional()
32
+ .describe('Optional manual rename overrides keyed on the substrate token (from = "Shop" | "Shopkeeper" | "ItemTag"), each replacing the planner\'s chosen target. Overrides that match no planned rename are ignored.'),
33
+ projectName: z
34
+ .string()
35
+ .optional()
36
+ .describe('Optional project name (e.g. "Vet Clinic" or "VetClinic"). Overrides the planner\'s name; the slug — which sets the output directory, DB prefix, and the Pascal project name across all three platforms — and the display name are derived from it. Ignored if it yields no valid slug.'),
37
+ },
38
+ }, async ({ spec, renameOverrides, projectName }) => {
39
+ const result = await dispatch(spec, {
40
+ ...(renameOverrides ? { renameOverrides } : {}),
41
+ ...(projectName !== undefined ? { projectName } : {}),
42
+ });
43
+ return {
44
+ content: [{ type: "text", text: result.summary }],
45
+ structuredContent: {
46
+ overallPass: result.overallPass,
47
+ summary: result.summary,
48
+ ...(result.visual ? { visual: result.visual } : {}),
49
+ report: result.report,
50
+ ...(result.reportPaths.htmlPath ? { reportHtmlPath: result.reportPaths.htmlPath } : {}),
51
+ ...(result.reportPaths.jsonPath ? { reportJsonPath: result.reportPaths.jsonPath } : {}),
52
+ },
53
+ isError: !result.overallPass,
54
+ };
55
+ });
56
+ return server;
57
+ }
58
+ export async function main() {
59
+ loadDotenvIfPresent();
60
+ const server = createMcpServer();
61
+ const transport = new StdioServerTransport();
62
+ await server.connect(transport);
63
+ }
64
+ if (isEntryPoint()) {
65
+ main().catch((err) => {
66
+ console.error(err);
67
+ process.exitCode = 1;
68
+ });
69
+ }
70
+ function isEntryPoint() {
71
+ const argv1 = process.argv[1];
72
+ if (!argv1)
73
+ return false;
74
+ try {
75
+ const modulePath = fileURLToPath(import.meta.url);
76
+ const argv1Real = realpathSync(argv1);
77
+ return argv1Real === modulePath;
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ }
83
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,kEAAkE;AAClE,oEAAoE;AACpE,oEAAoE;AACpE,mEAAmE;AACnE,EAAE;AACF,2EAA2E;AAE3E,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,yBAAyB;QAC/B,OAAO,EAAE,kBAAkB,EAAE;KAC9B,CAAC,CAAC;IAEH,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,kCAAkC;QACzC,WAAW,EACT,oYAAoY;QACtY,WAAW,EAAE;YACX,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CACP,qEAAqE,CACtE;YACH,eAAe,EAAE,CAAC;iBACf,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;iBACnE,QAAQ,EAAE;iBACV,QAAQ,CACP,4MAA4M,CAC7M;YACH,WAAW,EAAE,CAAC;iBACX,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACP,wRAAwR,CACzR;SACJ;KACF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,EAAE,EAAE;QAC/C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE;YAClC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtD,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YACjD,iBAAiB,EAAE;gBACjB,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnD,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvF,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxF;YACD,OAAO,EAAE,CAAC,MAAM,CAAC,WAAW;SAC7B,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,mBAAmB,EAAE,CAAC;IACtB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACtC,OAAO,SAAS,KAAK,UAAU,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ export type MobilePlatform = "ios" | "android";
4
+ export type ScreenElement = {
5
+ readonly [key: string]: unknown;
6
+ };
7
+ export type Screenshot = {
8
+ readonly data: Buffer;
9
+ readonly mimeType: string;
10
+ };
11
+ export type MobileClient = {
12
+ callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult>;
13
+ useDevice(name: string | undefined): void;
14
+ listDevices(): Promise<readonly ScreenElement[]>;
15
+ listElements(): Promise<readonly ScreenElement[]>;
16
+ click(x: number, y: number): Promise<void>;
17
+ typeKeys(text: string, submit?: boolean): Promise<void>;
18
+ pressButton(button: string): Promise<void>;
19
+ launchApp(packageName: string): Promise<void>;
20
+ takeScreenshot(): Promise<Screenshot>;
21
+ saveScreenshot(absolutePath: string): Promise<void>;
22
+ close(): Promise<void>;
23
+ };
24
+ export type MobileClientOptions = {
25
+ command?: string;
26
+ args?: readonly string[];
27
+ };
28
+ export declare function createMobileClient(opts?: MobileClientOptions): Promise<MobileClient>;
29
+ export declare function attachMobileClient(client: Client): MobileClient;
30
+ export declare function isStubMobile(): boolean;
31
+ //# sourceMappingURL=mobile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile.d.ts","sourceRoot":"","sources":["../src/mobile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAgBzE,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG;IAI1B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAEvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAKhF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC1C,WAAW,IAAI,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IACjD,YAAY,IAAI,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IAClD,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAK3C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAI3C,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAGhC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B,CAAC;AAmBF,wBAAsB,kBAAkB,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAY1F;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAE/D;AAED,wBAAgB,YAAY,IAAI,OAAO,CAKtC"}