nativeapptemplate-agent 0.2.4 → 0.3.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/README.md +32 -2
- package/dist/agents/planner.d.ts +1 -0
- package/dist/agents/planner.d.ts.map +1 -1
- package/dist/agents/planner.js +18 -12
- package/dist/agents/planner.js.map +1 -1
- package/dist/agents/types.d.ts +1 -0
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/agents/workers/android.d.ts +2 -0
- package/dist/agents/workers/android.d.ts.map +1 -1
- package/dist/agents/workers/android.js +10 -6
- package/dist/agents/workers/android.js.map +1 -1
- package/dist/agents/workers/copy.d.ts +3 -0
- package/dist/agents/workers/copy.d.ts.map +1 -1
- package/dist/agents/workers/copy.js +46 -0
- package/dist/agents/workers/copy.js.map +1 -1
- package/dist/agents/workers/ios.d.ts +2 -0
- package/dist/agents/workers/ios.d.ts.map +1 -1
- package/dist/agents/workers/ios.js +22 -8
- package/dist/agents/workers/ios.js.map +1 -1
- package/dist/agents/workers/rails.d.ts +4 -1
- package/dist/agents/workers/rails.d.ts.map +1 -1
- package/dist/agents/workers/rails.js +18 -7
- package/dist/agents/workers/rails.js.map +1 -1
- package/dist/dispatch.d.ts +4 -0
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +21 -2
- package/dist/dispatch.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -9
- package/dist/index.js.map +1 -1
- package/dist/japanese-nouns.d.ts +5 -0
- package/dist/japanese-nouns.d.ts.map +1 -0
- package/dist/japanese-nouns.js +18 -0
- package/dist/japanese-nouns.js.map +1 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +7 -2
- package/dist/mcp.js.map +1 -1
- package/dist/rename-overrides.d.ts +12 -0
- package/dist/rename-overrides.d.ts.map +1 -1
- package/dist/rename-overrides.js +49 -1
- package/dist/rename-overrides.js.map +1 -1
- package/dist/reserved-words.d.ts +13 -0
- package/dist/reserved-words.d.ts.map +1 -0
- package/dist/reserved-words.js +72 -0
- package/dist/reserved-words.js.map +1 -0
- package/dist/validation/layer1.d.ts.map +1 -1
- package/dist/validation/layer1.js +13 -1
- package/dist/validation/layer1.js.map +1 -1
- package/package.json +1 -1
- package/scripts/ruby/rename.rb +30 -5
|
@@ -4,7 +4,7 @@ const TEXT_EXTS = new Set([
|
|
|
4
4
|
".rb", ".erb", ".yml", ".yaml", ".json", ".md", ".gemspec", ".rake", ".ru",
|
|
5
5
|
".txt", ".sample", ".example", ".conf", ".html", ".css", ".scss", ".js", ".mjs",
|
|
6
6
|
".tt", ".lock",
|
|
7
|
-
".swift", ".plist", ".strings", ".xcconfig", ".entitlements", ".pbxproj",
|
|
7
|
+
".swift", ".plist", ".strings", ".xcstrings", ".xcconfig", ".entitlements", ".pbxproj",
|
|
8
8
|
".xcworkspacedata", ".modulemap", ".xcscheme",
|
|
9
9
|
".kt", ".kts", ".xml", ".gradle", ".pro", ".toml", ".properties", ".cfg",
|
|
10
10
|
".proto",
|
|
@@ -12,6 +12,9 @@ const TEXT_EXTS = new Set([
|
|
|
12
12
|
const TEXT_BASENAMES = new Set([
|
|
13
13
|
"Gemfile", "Gemfile.lock", "Rakefile", "Procfile", "Procfile.dev",
|
|
14
14
|
"config.ru", "Dockerfile",
|
|
15
|
+
// Kamal's extensionless secrets files (.kamal/secrets, .kamal/secrets-common).
|
|
16
|
+
// config/deploy.yml names the secret keys defined there, so both must rename.
|
|
17
|
+
"secrets", "secrets-common",
|
|
15
18
|
"Podfile", "Podfile.lock", "Package.swift", "Cartfile", "Makefile",
|
|
16
19
|
"gradlew", "gradlew.bat", "gradle.properties", "local.properties",
|
|
17
20
|
]);
|
|
@@ -25,6 +28,12 @@ const SKIP_SEGMENTS = new Set([
|
|
|
25
28
|
// source by the bundler — e.g. Turbo's `visitCompleted` in application.js), so
|
|
26
29
|
// Layer 1 must not scan them either, or it flags tokens we intentionally left.
|
|
27
30
|
const SKIP_SUBPATHS = ["app/assets/builds", "public/assets"];
|
|
31
|
+
// Ordinary words that begin with a forbidden token but aren't the domain noun.
|
|
32
|
+
// rename.rb leaves them alone (the token is followed by a lowercase letter, not
|
|
33
|
+
// a word boundary), so they aren't leftovers, and "repairing" them would break
|
|
34
|
+
// the build. ShoppingCart / AddShoppingCart are Material icons in the paid
|
|
35
|
+
// Android template's subscription settings.
|
|
36
|
+
const NON_DOMAIN_WORDS = ["Shopping"];
|
|
28
37
|
export async function runLayer1(input) {
|
|
29
38
|
if (input.forbiddenTokens.length === 0) {
|
|
30
39
|
return { pass: true, findings: [] };
|
|
@@ -45,6 +54,9 @@ export async function runLayer1(input) {
|
|
|
45
54
|
regex.lastIndex = 0;
|
|
46
55
|
let m;
|
|
47
56
|
while ((m = regex.exec(line)) !== null) {
|
|
57
|
+
const at = m.index;
|
|
58
|
+
if (NON_DOMAIN_WORDS.some((w) => w.startsWith(m[0]) && line.startsWith(w, at)))
|
|
59
|
+
continue;
|
|
48
60
|
findings.push({
|
|
49
61
|
token: m[0],
|
|
50
62
|
file: relative(root, filePath),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layer1.js","sourceRoot":"","sources":["../../src/validation/layer1.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAmBnE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK;IAC1E,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;IAC/E,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"layer1.js","sourceRoot":"","sources":["../../src/validation/layer1.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nativeapptemplate-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A Claude Code agent that turns a natural-language SaaS spec into a working three-platform implementation (Rails API + SwiftUI iOS + Jetpack Compose Android).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
package/scripts/ruby/rename.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
#
|
|
3
3
|
# Apply a rename plan to a Rails project tree.
|
|
4
|
-
# Stdin: { "renamePlan": [{"from":"Shop","to":"Clinic"}, ...],
|
|
4
|
+
# Stdin: { "renamePlan": [{"from":"Shop","to":"Clinic"}, ...],
|
|
5
|
+
# "japaneseRenamePlan": [{"from":"店舗","to":"医院"}, ...], (optional)
|
|
6
|
+
# "root": "/abs/path" }
|
|
5
7
|
# Stdout: { "files_scanned": N, "files_changed": N, "substitutions": N, "files_renamed": N }
|
|
6
8
|
#
|
|
7
9
|
# Rewrites file content AND renames files/directories for every
|
|
@@ -16,6 +18,7 @@ require "json"
|
|
|
16
18
|
|
|
17
19
|
input = JSON.parse($stdin.read)
|
|
18
20
|
plan = input.fetch("renamePlan")
|
|
21
|
+
japanese_plan = input.fetch("japaneseRenamePlan", [])
|
|
19
22
|
root = input.fetch("root")
|
|
20
23
|
|
|
21
24
|
stats = { files_scanned: 0, files_changed: 0, substitutions: 0, files_renamed: 0 }
|
|
@@ -25,7 +28,7 @@ SKIP_SUBPATHS = %w[vendor/bundle app/assets/builds public/assets].freeze
|
|
|
25
28
|
TEXT_EXTS = %w[
|
|
26
29
|
.rb .erb .yml .yaml .json .md .gemspec .rake .ru .txt .sample .example .conf
|
|
27
30
|
.html .css .scss .js .mjs .tt .lock
|
|
28
|
-
.swift .plist .strings .xcconfig .entitlements .pbxproj .xcworkspacedata .modulemap .xcscheme
|
|
31
|
+
.swift .plist .strings .xcstrings .xcconfig .entitlements .pbxproj .xcworkspacedata .modulemap .xcscheme
|
|
29
32
|
.kt .kts .xml .gradle .pro .toml .properties .cfg
|
|
30
33
|
.proto
|
|
31
34
|
.svg .webmanifest
|
|
@@ -34,6 +37,7 @@ TEXT_BASENAMES = %w[
|
|
|
34
37
|
Gemfile Gemfile.lock Rakefile Procfile Procfile.dev
|
|
35
38
|
.gitignore .env.sample .ruby-version .node-version
|
|
36
39
|
config.ru Dockerfile
|
|
40
|
+
secrets secrets-common
|
|
37
41
|
Podfile Podfile.lock Package.swift Cartfile Makefile
|
|
38
42
|
gradlew gradlew.bat gradle.properties local.properties
|
|
39
43
|
brakeman.ignore .swiftformat
|
|
@@ -87,6 +91,17 @@ def build_patterns(from, to)
|
|
|
87
91
|
to_camel = camel_case(to)
|
|
88
92
|
from_camel_pl = pluralize(from_camel)
|
|
89
93
|
to_camel_pl = pluralize(to_camel)
|
|
94
|
+
# SCREAMING form: the upper case of whatever its lower-case spelling becomes.
|
|
95
|
+
# A one-word `from` has a single lower-case spelling (snake == flat) and the
|
|
96
|
+
# snake pattern above wins for it, so `nativeapptemplateapi` becomes
|
|
97
|
+
# `barber_queue_api`; NATIVEAPPTEMPLATEAPI must then become BARBER_QUEUE_API,
|
|
98
|
+
# not BARBERQUEUEAPI, or code that derives one from the other breaks (the
|
|
99
|
+
# API's deploy test checks `$#{SERVICE.upcase}_SENTRY_DSN` in .kamal/secrets).
|
|
100
|
+
# A multi-word `from` (NativeAppTemplate) keeps the flat form, matching its
|
|
101
|
+
# flat lower-case rename (NATIVEAPPTEMPLATE_API_DOMAIN -> CLINICQUEUE_API_DOMAIN).
|
|
102
|
+
one_word = from_snake == from_flat
|
|
103
|
+
to_upper = one_word ? to_snake.upcase : to.upcase
|
|
104
|
+
to_upper_pl = one_word ? to_snake_pl.upcase : to_pl.upcase
|
|
90
105
|
from_human_lo = humanize_lower(from)
|
|
91
106
|
to_human_lo = humanize_lower(to)
|
|
92
107
|
from_human_ti = humanize_title(from)
|
|
@@ -115,6 +130,9 @@ def build_patterns(from, to)
|
|
|
115
130
|
pascal_r = "(?:(?![A-Za-z])|(?=[A-Z]))"
|
|
116
131
|
snake_l = "(?<![A-Za-z])"
|
|
117
132
|
snake_r = "(?:(?![A-Za-z])|(?=[A-Z]))"
|
|
133
|
+
# SCREAMING tokens also follow Gradle's `-P` (`-PNATIVEAPPTEMPLATE_SENTRY_DSN=...`
|
|
134
|
+
# in the Android template's docs and build script comments).
|
|
135
|
+
upper_l = "(?:(?<![A-Za-z])|(?<=-P))"
|
|
118
136
|
|
|
119
137
|
patterns = [
|
|
120
138
|
[/#{pascal_l}#{Regexp.escape(from_pl)}#{pascal_r}/, to_pl],
|
|
@@ -132,8 +150,8 @@ def build_patterns(from, to)
|
|
|
132
150
|
# (from `ItemTag`) or `itemTagInfoFromNdefMessage`.
|
|
133
151
|
[/#{snake_l}#{Regexp.escape(from_camel_pl)}#{snake_r}/, to_camel_pl],
|
|
134
152
|
[/#{snake_l}#{Regexp.escape(from_camel)}#{snake_r}/, to_camel],
|
|
135
|
-
[/#{
|
|
136
|
-
[/#{
|
|
153
|
+
[/#{upper_l}#{Regexp.escape(from_pl.upcase)}(?![A-Za-z])/, to_upper_pl],
|
|
154
|
+
[/#{upper_l}#{Regexp.escape(from.upcase)}(?![A-Za-z])/, to_upper],
|
|
137
155
|
]
|
|
138
156
|
|
|
139
157
|
# Humanized display forms — three case shapes per number:
|
|
@@ -162,7 +180,14 @@ def build_patterns(from, to)
|
|
|
162
180
|
patterns
|
|
163
181
|
end
|
|
164
182
|
|
|
165
|
-
|
|
183
|
+
# Japanese has no word boundaries and its nouns don't inflect, so a plain
|
|
184
|
+
# substring covers compounds (店舗名) and every sentence the noun appears in.
|
|
185
|
+
# Longest first, so a noun inside another (タグ in アイテムタグ) can't win.
|
|
186
|
+
japanese_patterns = japanese_plan
|
|
187
|
+
.sort_by { |p| -p.fetch("from").length }
|
|
188
|
+
.map { |p| [/#{Regexp.escape(p.fetch("from"))}/, p.fetch("to")] }
|
|
189
|
+
|
|
190
|
+
all_patterns = japanese_patterns + plan.flat_map { |p| build_patterns(p.fetch("from"), p.fetch("to")) }
|
|
166
191
|
|
|
167
192
|
# One combined regex, applied in a single pass, so a replacement is never
|
|
168
193
|
# re-scanned by a later pair: with NativeAppTemplate→PetShop and Shop→Store,
|