frontier-os-app-builder 1.1.0 → 1.2.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 +25 -0
- package/agents/fos-executor.md +22 -65
- package/agents/fos-plan-checker.md +13 -12
- package/agents/fos-planner.md +20 -67
- package/agents/fos-researcher.md +14 -10
- package/agents/fos-verifier.md +11 -5
- package/bin/fos-tools.cjs +48 -11
- package/bin/install.js +8 -5
- package/commands/fos/add-feature.md +1 -2
- package/commands/fos/discuss.md +0 -1
- package/commands/fos/new-app.md +1 -3
- package/commands/fos/new-milestone.md +1 -1
- package/commands/fos/plan.md +0 -2
- package/package.json +7 -1
- package/references/app-patterns.md +46 -28
- package/references/deployment.md +40 -74
- package/references/module-index.md +32 -0
- package/references/sdk/chain.md +92 -0
- package/references/sdk/communities.md +159 -0
- package/references/sdk/events.md +212 -0
- package/references/sdk/init.md +126 -0
- package/references/sdk/navigation.md +49 -0
- package/references/sdk/offices.md +76 -0
- package/references/sdk/partnerships.md +111 -0
- package/references/sdk/storage.md +44 -0
- package/references/sdk/thirdparty.md +240 -0
- package/references/sdk/token-amount.md +99 -0
- package/references/sdk/types.md +27 -0
- package/references/sdk/ui-utils.md +39 -0
- package/references/sdk/user.md +208 -0
- package/references/sdk/wallet.md +334 -0
- package/references/verification-rules.md +18 -18
- package/templates/app/frontier-services.tsx +75 -18
- package/templates/app/layout.tsx +19 -9
- package/templates/app/package.json +2 -1
- package/templates/app/public/favicon.svg +3 -0
- package/templates/app/sdk-context.tsx +7 -9
- package/templates/app/sdk-services.tsx +92 -117
- package/templates/app/vercel.json +8 -47
- package/templates/state/plan.md +32 -14
- package/templates/state/roadmap.md +2 -2
- package/templates/state/summary.md +26 -29
- package/workflows/add-feature.md +6 -1
- package/workflows/discuss.md +9 -3
- package/workflows/execute-plan.md +3 -3
- package/workflows/execute.md +17 -6
- package/workflows/new-app.md +54 -18
- package/workflows/new-milestone.md +9 -2
- package/workflows/plan.md +14 -5
- package/workflows/ship.md +26 -10
- package/workflows/status.md +0 -1
- package/references/module-inference.md +0 -348
- package/references/sdk-surface.md +0 -1600
- package/templates/app/main-simple-standalone.tsx +0 -19
- package/templates/app/main-simple.tsx +0 -19
- package/templates/state/manifest.json +0 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BerlinhouseLabs
|
|
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
CHANGED
|
@@ -17,6 +17,8 @@ npx frontier-os-app-builder
|
|
|
17
17
|
|
|
18
18
|
That's it. This installs `/fos:*` commands into your Claude Code environment.
|
|
19
19
|
|
|
20
|
+
> Running `npx frontier-os-app-builder` executes the installer automatically. If you install globally instead (`npm i -g frontier-os-app-builder`), run `frontier-os-app-builder` once afterward to populate `~/.claude`.
|
|
21
|
+
|
|
20
22
|
To uninstall:
|
|
21
23
|
|
|
22
24
|
```bash
|
|
@@ -72,6 +74,12 @@ npx frontier-os-app-builder --uninstall
|
|
|
72
74
|
| `/fos:next` | Auto-route to the next step in the workflow |
|
|
73
75
|
| `/fos:status` | Show current project state |
|
|
74
76
|
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
| Environment variable | Used by | Effect |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `FOS_GITHUB_ORG` | `/fos:ship` | Creates the deployed app's GitHub repo under this organization. When unset (the default), the repo is created under your authenticated `gh` user account. |
|
|
82
|
+
|
|
75
83
|
## How it works
|
|
76
84
|
|
|
77
85
|
The framework is a multi-layered meta-prompting system:
|
|
@@ -92,6 +100,23 @@ Each command reads state from `.frontier-app/` on disk, does its work, writes up
|
|
|
92
100
|
- **Frontier-specific verification** — checks CORS origins, iframe detection, standalone fallback, SDK permissions, dark theme
|
|
93
101
|
- **Milestones** — iterative development with `/fos:new-milestone` for v2, v3, and beyond
|
|
94
102
|
|
|
103
|
+
## Frontier Studio
|
|
104
|
+
|
|
105
|
+
Frontier Studio is a live companion dashboard that runs alongside Claude Code. It watches your `.frontier-app/` state files and shows a visual dashboard with a live app preview in your browser.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
cd your-app-directory
|
|
109
|
+
npx frontier-studio
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Opens `http://localhost:4983` with:
|
|
113
|
+
|
|
114
|
+
- **Project dashboard** — phases, status, SDK modules, progress, next action
|
|
115
|
+
- **Live preview** — your app rendered via Vite HMR in an iframe (desktop/tablet/mobile)
|
|
116
|
+
- **Activity stream** — file changes, git commits, phase transitions in real time
|
|
117
|
+
|
|
118
|
+
See [studio/README.md](./studio/README.md) for development details.
|
|
119
|
+
|
|
95
120
|
## SDK Coverage
|
|
96
121
|
|
|
97
122
|
The framework has built-in knowledge of all 10 Frontier SDK modules:
|
package/agents/fos-executor.md
CHANGED
|
@@ -128,11 +128,12 @@ node $HOME/.claude/frontier-os-app-builder/bin/fos-tools.cjs scaffold <template>
|
|
|
128
128
|
| `vite.config.ts` | `./vite.config.ts` |
|
|
129
129
|
| `frontier-services.tsx` | `./src/lib/frontier-services.tsx` |
|
|
130
130
|
| `layout-standalone.tsx` | `./src/views/Layout.tsx` |
|
|
131
|
-
| `main-router.tsx`
|
|
131
|
+
| `main-router.tsx` | `./src/main.tsx` |
|
|
132
132
|
| `router.tsx` | `./src/router.tsx` |
|
|
133
133
|
| `index.css` | `./src/styles/index.css` |
|
|
134
134
|
| `test-setup.ts` | `./src/test/setup.ts` |
|
|
135
135
|
| `gitignore` | `./.gitignore` |
|
|
136
|
+
| `public/` | `./public/` |
|
|
136
137
|
|
|
137
138
|
**Critical scaffold requirements:**
|
|
138
139
|
- `src/lib/frontier-services.tsx` — Exports `useServices()` with mock backend. Modified only during SDK Integration phase.
|
|
@@ -183,19 +184,23 @@ const wallet = services.wallet;
|
|
|
183
184
|
**Create hooks with loading/error states:**
|
|
184
185
|
```typescript
|
|
185
186
|
import { useState, useEffect } from 'react';
|
|
186
|
-
import { useServices } from '../lib/frontier-services';
|
|
187
|
-
|
|
187
|
+
import { useServices, type FrontierServices } from '../lib/frontier-services';
|
|
188
|
+
|
|
189
|
+
// Derive feature data types from the FrontierServices seam — NEVER import the
|
|
190
|
+
// SDK in feature code (it isn't installed in scaffold/feature phases and fails
|
|
191
|
+
// verification rule M-03). `ReturnType` here is the TypeScript built-in.
|
|
192
|
+
type Balance = Awaited<ReturnType<FrontierServices['wallet']['getBalance']>>;
|
|
188
193
|
|
|
189
|
-
export function
|
|
194
|
+
export function useBalance() {
|
|
190
195
|
const services = useServices();
|
|
191
|
-
const [data, setData] = useState<
|
|
196
|
+
const [data, setData] = useState<Balance | null>(null);
|
|
192
197
|
const [loading, setLoading] = useState(true);
|
|
193
198
|
const [error, setError] = useState<string | null>(null);
|
|
194
199
|
|
|
195
200
|
useEffect(() => {
|
|
196
201
|
const fetchData = async () => {
|
|
197
202
|
try {
|
|
198
|
-
const result = await services.
|
|
203
|
+
const result = await services.wallet.getBalance();
|
|
199
204
|
setData(result);
|
|
200
205
|
} catch (err) {
|
|
201
206
|
setError(err instanceof Error ? err.message : 'Failed to load data');
|
|
@@ -241,39 +246,18 @@ export default function FeatureView() {
|
|
|
241
246
|
|
|
242
247
|
### Tailwind Dark Theme Classes
|
|
243
248
|
|
|
244
|
-
|
|
245
|
-
- **Backgrounds:** `bg-background`, `bg-card`, `bg-muted-background`
|
|
246
|
-
- **Text:** `text-foreground`, `text-card-foreground`, `text-muted-foreground`
|
|
247
|
-
- **Borders:** `border-border`
|
|
248
|
-
- **Interactive:** `bg-primary text-primary-foreground`, `bg-accent text-accent-foreground`
|
|
249
|
-
- **Status:** `text-success`, `text-danger`, `text-alert`
|
|
250
|
-
- **Inputs:** `bg-input`, `ring-ring`, `outline-outline`
|
|
251
|
-
|
|
252
|
-
**NEVER use hardcoded colors** (no `bg-white`, `text-black`, `bg-gray-900`). Always use the semantic CSS variable classes.
|
|
249
|
+
Use semantic Tailwind classes from the app's index.css @theme block (bg-background, text-foreground, etc.). See app-patterns.md "Dark Theme CSS Variables" section for the full token list. **NEVER use hardcoded colors** (no `bg-white`, `text-black`, `bg-gray-900`).
|
|
253
250
|
|
|
254
251
|
### Type Safety
|
|
255
252
|
|
|
256
253
|
- Always use the SDK types from `@frontiertower/frontier-sdk`
|
|
257
|
-
- Never invent types — use `WalletBalance`, `
|
|
254
|
+
- Never invent types — use `WalletBalance`, `UserOperationReceipt`, `SmartAccount`, etc.
|
|
258
255
|
- If a type is not exported by the SDK, define a local interface that matches the SDK's return shape
|
|
259
256
|
- Always use `strict: true` TypeScript
|
|
260
257
|
|
|
261
|
-
### Module Access
|
|
258
|
+
### Module Access
|
|
262
259
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
| Service Property | Description |
|
|
266
|
-
|-----------------|-------------|
|
|
267
|
-
| `services.wallet` | Wallet operations (balance, transactions, send) |
|
|
268
|
-
| `services.storage` | Storage operations (get, set, delete) |
|
|
269
|
-
| `services.chain` | Chain/blockchain operations |
|
|
270
|
-
| `services.user` | User profile and authentication |
|
|
271
|
-
| `services.partnerships` | Partnerships module |
|
|
272
|
-
| `services.thirdParty` | Third-party integrations |
|
|
273
|
-
| `services.communities` | Communities module |
|
|
274
|
-
| `services.events` | Events module |
|
|
275
|
-
| `services.offices` | Offices module |
|
|
276
|
-
| `services.navigation` | Navigation module |
|
|
260
|
+
Access modules via `services.<module>` from `useServices()`. Property names match SDK module names in lowercase (`services.wallet`, `services.events`, etc.).
|
|
277
261
|
|
|
278
262
|
</feature_execution>
|
|
279
263
|
|
|
@@ -293,9 +277,9 @@ When accessing modules via `useServices()`, use these property names:
|
|
|
293
277
|
**TIER 2 — SDK INTEGRATION PHASE ONLY:**
|
|
294
278
|
7. **SDK access:** `useSdk()` hook from `src/lib/sdk-context.tsx`, used only inside `sdk-services.tsx` and `Layout.tsx`.
|
|
295
279
|
8. **Iframe detection:** `isInFrontierApp()` check in Layout.tsx. Standalone mode shows fallback banner.
|
|
296
|
-
9. **
|
|
280
|
+
9. **Provider wrapping:** In-frame, Layout wraps the app in `SdkProvider` AND bridges the SDK into `FrontierServicesProvider` (so `useServices()` resolves). SDK created once in an effect and exposed via `useState`, destroyed on unmount.
|
|
297
281
|
10. **Permissions:** Every SDK method used must have permission declared in manifest.json.
|
|
298
|
-
11. **CORS:** vercel.json
|
|
282
|
+
11. **CORS:** vercel.json sets CORS for the production origin plus a CSP `frame-ancestors` listing the 3 Frontier OS origins (os.frontiertower.io, sandbox.os.frontiertower.io, localhost:5173) and security headers. Copy templates/app/vercel.json verbatim.
|
|
299
283
|
12. **SDK imports:** Use `@frontiertower/frontier-sdk` for SDK classes. Exact import paths, not barrel imports.
|
|
300
284
|
</frontier_os_rules>
|
|
301
285
|
|
|
@@ -303,35 +287,7 @@ When accessing modules via `useServices()`, use these property names:
|
|
|
303
287
|
|
|
304
288
|
### SDK Integration Phase Execution
|
|
305
289
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
**Step 1: Add SDK dependency**
|
|
309
|
-
```bash
|
|
310
|
-
npm install @frontiertower/frontier-sdk
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
**Step 2: Create sdk-context.tsx**
|
|
314
|
-
Render from `templates/app/sdk-context.tsx` to `src/lib/sdk-context.tsx`.
|
|
315
|
-
This file is IDENTICAL across all apps — never customize it.
|
|
316
|
-
|
|
317
|
-
**Step 3: Create sdk-services.tsx**
|
|
318
|
-
Render from `templates/app/sdk-services.tsx` to `src/lib/sdk-services.tsx`.
|
|
319
|
-
This adapter maps each `FrontierServices` method to the corresponding `sdk.getModule().method()` call.
|
|
320
|
-
|
|
321
|
-
**Step 4: Upgrade frontier-services.tsx**
|
|
322
|
-
Modify `src/lib/frontier-services.tsx` to add environment detection:
|
|
323
|
-
- Import `isInFrontierApp` from `@frontiertower/frontier-sdk/ui-utils`
|
|
324
|
-
- If in iframe: import and use `createSdkServices` from `./sdk-services`
|
|
325
|
-
- If standalone: use existing `createMockServices()` (no change to mock code)
|
|
326
|
-
|
|
327
|
-
**Step 5: Upgrade Layout.tsx**
|
|
328
|
-
Follow standard Layout pattern from `templates/app/layout.tsx`:
|
|
329
|
-
- Add `isInFrontierApp()` detection
|
|
330
|
-
- Add `createStandaloneHTML()` fallback for standalone mode
|
|
331
|
-
- Wrap children in `SdkProvider` when in iframe
|
|
332
|
-
|
|
333
|
-
**Step 6: Add CORS origins to vercel.json**
|
|
334
|
-
Replace vercel.json with full version from `templates/app/vercel.json` (has all 3 CORS origin blocks plus SPA rewrite).
|
|
290
|
+
Follow the SDK Integration pattern from app-patterns.md "SDK Integration Pattern" section. The steps are: add SDK dependency; create sdk-context.tsx; create sdk-services.tsx (wire the modules your app uses); swap in templates/app/layout.tsx (it bridges the SDK into FrontierServicesProvider so useServices() works in-frame); swap in the full vercel.json. Leave frontier-services.tsx unchanged — it stays the SDK-free mock seam; do NOT add SDK imports or detection to it. Use templates from templates/app/ for each file.
|
|
335
291
|
|
|
336
292
|
</sdk_integration_execution>
|
|
337
293
|
|
|
@@ -497,7 +453,7 @@ tasks_completed: N/N
|
|
|
497
453
|
|
|
498
454
|
# Phase [X] Plan [Y]: [Name] Summary
|
|
499
455
|
|
|
500
|
-
[One-liner must be substantive: "Balance display with useBalance hook calling
|
|
456
|
+
[One-liner must be substantive: "Balance display with useBalance hook calling getBalance() and formatting via formatAmount(), card-based UI with loading/error states" NOT "Balance feature implemented"]
|
|
501
457
|
|
|
502
458
|
## Tasks Completed
|
|
503
459
|
|
|
@@ -513,7 +469,7 @@ tasks_completed: N/N
|
|
|
513
469
|
|
|
514
470
|
| Method | Module | Permission | Files |
|
|
515
471
|
|--------|--------|------------|-------|
|
|
516
|
-
| wallet.
|
|
472
|
+
| wallet.getBalance() | Wallet | wallet:getBalance | src/hooks/useBalance.ts |
|
|
517
473
|
|
|
518
474
|
## Deviations from Plan
|
|
519
475
|
|
|
@@ -561,7 +517,8 @@ Do NOT skip. Do NOT proceed to state updates if self-check fails.
|
|
|
561
517
|
</self_check>
|
|
562
518
|
|
|
563
519
|
<sdk_reference>
|
|
564
|
-
|
|
520
|
+
Focused SDK reference is provided via <files_to_read> in the spawn prompt.
|
|
521
|
+
Contains only modules relevant to this app (from references/sdk/*.md).
|
|
565
522
|
</sdk_reference>
|
|
566
523
|
|
|
567
524
|
<app_patterns_reference>
|
|
@@ -48,7 +48,7 @@ Before verifying, load project context:
|
|
|
48
48
|
<core_principle>
|
|
49
49
|
**Plan completeness =/= Goal achievement**
|
|
50
50
|
|
|
51
|
-
A task "create balance display" can be in the plan while the SDK method `
|
|
51
|
+
A task "create balance display" can be in the plan while the SDK method `getBalance()` is called with wrong arguments. The task exists but the goal "show user's balance" won't be achieved.
|
|
52
52
|
|
|
53
53
|
Goal-backward verification works backwards from outcome:
|
|
54
54
|
|
|
@@ -74,10 +74,10 @@ Then verify each level against the actual plan files.
|
|
|
74
74
|
3. Check method signatures match (parameter types, return types)
|
|
75
75
|
4. Verify the correct module accessor is used (e.g., `getWallet()` not `wallet()`)
|
|
76
76
|
|
|
77
|
-
Feature phases access methods via `services.module.method()` (not `sdk.getModule().method()`). The method NAMES are the same — validate against sdk
|
|
77
|
+
Feature phases access methods via `services.module.method()` (not `sdk.getModule().method()`). The method NAMES are the same — validate against the per-module SDK reference files (references/sdk/*.md) for correctness. The import path should be `../lib/frontier-services`, not `../lib/sdk-context`.
|
|
78
78
|
|
|
79
79
|
**Validation rules:**
|
|
80
|
-
- Method must exist in
|
|
80
|
+
- Method must exist in the focused SDK reference (provided via <files_to_read>)
|
|
81
81
|
- Module accessor must use the correct getter: `sdk.getWallet()`, `sdk.getStorage()`, etc.
|
|
82
82
|
- Parameter types must match SDK definitions (e.g., `bigint` for amounts, `string` for addresses)
|
|
83
83
|
- Return types referenced in task must match SDK definitions
|
|
@@ -86,17 +86,17 @@ Feature phases access methods via `services.module.method()` (not `sdk.getModule
|
|
|
86
86
|
- Method name doesn't exist in SDK (e.g., `getTokenBalance()` instead of `getBalance()`)
|
|
87
87
|
- Wrong module (e.g., `sdk.getUser().getBalance()` instead of `sdk.getWallet().getBalance()`)
|
|
88
88
|
- Wrong parameter types (e.g., `number` instead of `bigint` for token amounts)
|
|
89
|
-
- Invented types not in SDK (e.g., `BalanceInfo` instead of `
|
|
89
|
+
- Invented types not in SDK (e.g., `BalanceInfo` instead of `WalletBalance`)
|
|
90
90
|
|
|
91
91
|
**Example issue:**
|
|
92
92
|
```yaml
|
|
93
93
|
issue:
|
|
94
94
|
dimension: sdk_method_correctness
|
|
95
95
|
severity: blocker
|
|
96
|
-
description: "Task 2 references sdk.getWallet().getTokenBalance() — method does not exist. Use getBalance()
|
|
96
|
+
description: "Task 2 references sdk.getWallet().getTokenBalance() — method does not exist. Use getBalance()"
|
|
97
97
|
plan: "02-01"
|
|
98
98
|
task: 2
|
|
99
|
-
fix_hint: "Replace getTokenBalance() with
|
|
99
|
+
fix_hint: "Replace getTokenBalance() with getBalance() — returns WalletBalance with .total, .fnd, .internalFnd bigint fields; format each with formatAmount() from '@frontiertower/frontier-sdk'"
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
## Dimension 2: Permission Alignment
|
|
@@ -128,10 +128,10 @@ For feature phases: permission mismatches are severity **warning** (permissions
|
|
|
128
128
|
issue:
|
|
129
129
|
dimension: permission_alignment
|
|
130
130
|
severity: blocker
|
|
131
|
-
description: "Task 1 calls sdk.getWallet().
|
|
131
|
+
description: "Task 1 calls sdk.getWallet().getBalance() but manifest.json does not declare wallet:getBalance permission"
|
|
132
132
|
plan: "02-01"
|
|
133
133
|
task: 1
|
|
134
|
-
fix_hint: "Add 'wallet:
|
|
134
|
+
fix_hint: "Add 'wallet:getBalance' to permissions array in manifest.json, or add a task to update manifest"
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
## Dimension 3: File Structure Compliance
|
|
@@ -205,7 +205,7 @@ issue:
|
|
|
205
205
|
- ❌ `layout.tsx` template (without `-standalone`) — use `layout-standalone.tsx`
|
|
206
206
|
- ❌ `package.json` template (without `-standalone`) — use `package-standalone.json`
|
|
207
207
|
- ❌ `vercel.json` template (without `-standalone`) — use `vercel-standalone.json`
|
|
208
|
-
- ❌ `main-
|
|
208
|
+
- ❌ single-component entries — use `main-router.tsx` instead (all apps use the router)
|
|
209
209
|
|
|
210
210
|
If the researcher recommended SDK patterns from production apps, the planner should NOT have included them in Phase 1. Flag as blocker with fix hint: "Phase 1 is standalone-first. Remove SDK artifacts and use standalone templates."
|
|
211
211
|
|
|
@@ -357,8 +357,8 @@ issue:
|
|
|
357
357
|
1. Plan includes task to add `@frontiertower/frontier-sdk` dependency (`npm install`)
|
|
358
358
|
2. Plan includes task to create `src/lib/sdk-context.tsx` from template
|
|
359
359
|
3. Plan includes task to create `src/lib/sdk-services.tsx` adapter
|
|
360
|
-
4. Plan
|
|
361
|
-
5. Plan includes task to
|
|
360
|
+
4. Plan keeps `src/lib/frontier-services.tsx` as the SDK-free mock seam (NO task to add SDK imports/detection there)
|
|
361
|
+
5. Plan includes task to swap in `src/views/Layout.tsx` from the template (iframe detection + `SdkProvider` + `FrontierServicesProvider` bridge so `useServices()` resolves)
|
|
362
362
|
6. Plan includes task to add CORS origins to `vercel.json`
|
|
363
363
|
7. Plan includes verification task (build, typecheck, full validation)
|
|
364
364
|
|
|
@@ -414,7 +414,8 @@ Return structured PASS/FAIL with issues list:
|
|
|
414
414
|
</output_format>
|
|
415
415
|
|
|
416
416
|
<sdk_reference>
|
|
417
|
-
|
|
417
|
+
Focused SDK reference is provided via <files_to_read> in the spawn prompt.
|
|
418
|
+
Contains only modules relevant to this app (from references/sdk/*.md).
|
|
418
419
|
</sdk_reference>
|
|
419
420
|
|
|
420
421
|
<verification_rules_reference>
|
package/agents/fos-planner.md
CHANGED
|
@@ -121,14 +121,14 @@ Phase 1 ALWAYS produces a single plan that scaffolds the entire app. This plan u
|
|
|
121
121
|
- `vite.config.ts` — Vite + Vitest config (parameterized: APP_NAME)
|
|
122
122
|
- `frontier-services.tsx` — Service layer with mock implementations (parameterized: modules used)
|
|
123
123
|
- `layout-standalone.tsx` — Shell layout for standalone mode (parameterized: APP_NAME)
|
|
124
|
-
- `main-router.tsx`
|
|
124
|
+
- `main-router.tsx` — React root (router entry; all apps use the router)
|
|
125
125
|
- `router.tsx` — Route definitions (parameterized: routes)
|
|
126
126
|
- `index.css` — Tailwind + dark theme variables (parameterized: app colors)
|
|
127
127
|
- `test-setup.ts` — Vitest setup (identical across apps)
|
|
128
128
|
- `gitignore` — Git ignore patterns
|
|
129
129
|
|
|
130
130
|
**Scaffold plan specifics:**
|
|
131
|
-
-
|
|
131
|
+
- All standalone template files listed above must be created (excluding SDK-only and non-standalone variants from the BLOCKLIST below)
|
|
132
132
|
- `frontier-services.tsx` goes to `src/lib/frontier-services.tsx`
|
|
133
133
|
- `layout-standalone.tsx` goes to `src/views/Layout.tsx`
|
|
134
134
|
- `index.css` goes to `src/styles/index.css`
|
|
@@ -142,7 +142,7 @@ Phase 1 ALWAYS produces a single plan that scaffolds the entire app. This plan u
|
|
|
142
142
|
**Phase 1 BLOCKLIST — NEVER include these in a scaffold plan:**
|
|
143
143
|
- ❌ `sdk-context.tsx` — this file is created during SDK Integration phase, NOT Phase 1
|
|
144
144
|
- ❌ `layout.tsx` template — use `layout-standalone.tsx` instead (no iframe detection, no SdkProvider)
|
|
145
|
-
- ❌
|
|
145
|
+
- ❌ single-component entries — use `main-router.tsx` instead (all apps use the router; no single-component entry)
|
|
146
146
|
- ❌ `package.json` template — use `package-standalone.json` instead (no SDK dependency)
|
|
147
147
|
- ❌ `vercel.json` template — use `vercel-standalone.json` instead (no CORS headers)
|
|
148
148
|
- ❌ `@frontiertower/frontier-sdk` in dependencies — SDK is added during SDK Integration phase
|
|
@@ -161,8 +161,8 @@ Feature phases create plans with tasks for:
|
|
|
161
161
|
- **Tests** — Vitest tests for hooks and components
|
|
162
162
|
|
|
163
163
|
**Task specificity requirements:**
|
|
164
|
-
- Name exact service methods: `services.wallet.
|
|
165
|
-
- Name exact types: `
|
|
164
|
+
- Name exact service methods: `services.wallet.getBalance()` not "get balance"
|
|
165
|
+
- Name exact types: `WalletBalance` not "balance type"
|
|
166
166
|
- Name exact file paths: `src/hooks/useBalance.ts` not "a balance hook"
|
|
167
167
|
- Name exact Tailwind classes: `bg-card text-card-foreground rounded-lg p-4` not "styled card"
|
|
168
168
|
- Name exact imports: `import { useServices } from '../lib/frontier-services'` not "import services"
|
|
@@ -177,17 +177,17 @@ The SDK Integration phase is ALWAYS the last phase. It is mechanical — no feat
|
|
|
177
177
|
1. **Add SDK dependency** — `npm install @frontiertower/frontier-sdk@{{SDK_VERSION}}`. Add to package.json dependencies.
|
|
178
178
|
2. **Create sdk-context.tsx** — Render from `templates/app/sdk-context.tsx` to `src/lib/sdk-context.tsx`. Standard SdkProvider + useSdk() hook, identical across all apps. NEVER modify after creation.
|
|
179
179
|
3. **Create sdk-services.tsx** — Render from `templates/app/sdk-services.tsx` to `src/lib/sdk-services.tsx`. Adapter mapping FrontierServices interface to real SDK calls via useSdk().
|
|
180
|
-
4. **
|
|
181
|
-
5. **
|
|
182
|
-
6. **
|
|
180
|
+
4. **Leave frontier-services.tsx unchanged** — it stays the SDK-free mock seam. The iframe/standalone switch lives in Layout (step 5); do NOT add SDK imports or detection to `src/lib/frontier-services.tsx`.
|
|
181
|
+
5. **Swap in Layout.tsx** — Copy `templates/app/layout.tsx`: `isInFrontierApp()` detection, `createStandaloneHTML()` fallback, and in-frame wrap `<Outlet />` in `SdkProvider` + bridge the SDK into `FrontierServicesProvider` (`createSdkServices(sdk)`) so `useServices()` resolves.
|
|
182
|
+
6. **Swap in the full vercel.json** — Replace with `templates/app/vercel.json` (CORS + CSP `frame-ancestors` for the 3 origins + security headers).
|
|
183
183
|
7. **Verify** — Build passes (`npm run build`), typecheck passes (`npx tsc --noEmit`), all verification rules pass including Tier 2.
|
|
184
184
|
|
|
185
185
|
**Success criteria (fixed, not user-determined):**
|
|
186
186
|
1. `src/lib/sdk-context.tsx` exists and exports `useSdk` + `SdkProvider`
|
|
187
187
|
2. `src/lib/sdk-services.tsx` exists and maps all used service methods to real SDK calls
|
|
188
|
-
3. `src/
|
|
189
|
-
4. `src/views/Layout.tsx` has `isInFrontierApp()` detection
|
|
190
|
-
5. `vercel.json` has
|
|
188
|
+
3. `src/views/Layout.tsx` bridges the SDK into `FrontierServicesProvider` (via `createSdkServices`) so `useServices()` resolves in-frame
|
|
189
|
+
4. `src/views/Layout.tsx` has `isInFrontierApp()` detection, `SdkProvider`, AND `FrontierServicesProvider`
|
|
190
|
+
5. `vercel.json` has CORS + CSP `frame-ancestors` (3 origins) + security headers
|
|
191
191
|
6. `npm run build` succeeds
|
|
192
192
|
7. `npx tsc --noEmit` passes
|
|
193
193
|
|
|
@@ -209,22 +209,22 @@ Every task has six required fields:
|
|
|
209
209
|
- Rule: At minimum, include the files this task's code will import from or integrate with.
|
|
210
210
|
|
|
211
211
|
**<action>:** Specific implementation instructions with CONCRETE SDK values.
|
|
212
|
-
- Good: "Create `useBalance` hook that calls `services.wallet.
|
|
212
|
+
- Good: "Create `useBalance` hook that calls `services.wallet.getBalance()` returning `WalletBalance` (bigint fields `total`/`fnd`/`internalFnd`), formatting for display via `formatAmount` imported from `@frontiertower/frontier-sdk`. Handle loading/error states with useState. The hook returns `{ balance: WalletBalance | null, loading: boolean, error: string | null }`. Import `useServices` from `../lib/frontier-services`. Wrap the service call in try/catch. Call in a useEffect with `[services]` dependency."
|
|
213
213
|
- Bad: "Create a hook that fetches the balance"
|
|
214
214
|
|
|
215
215
|
**<verify>:** How to prove the task is complete.
|
|
216
|
-
- Good: `grep -q "
|
|
216
|
+
- Good: `grep -q "getBalance" src/hooks/useBalance.ts && npx tsc --noEmit`
|
|
217
217
|
- Bad: "It works"
|
|
218
218
|
|
|
219
219
|
**<acceptance_criteria>:** Grep-verifiable conditions the executor checks programmatically. Every task MUST include this field.
|
|
220
220
|
- Good:
|
|
221
|
-
- `grep -q "
|
|
221
|
+
- `grep -q "getBalance" src/hooks/useBalance.ts`
|
|
222
222
|
- `grep -q "loading" src/hooks/useBalance.ts`
|
|
223
223
|
- `npx tsc --noEmit` exits 0
|
|
224
224
|
- Bad: "It compiles", or omitting acceptance_criteria entirely
|
|
225
225
|
|
|
226
226
|
**<done>:** Acceptance criteria — measurable state of completion.
|
|
227
|
-
- Good: "`useBalance.ts` exports a hook that returns `{ balance, loading, error }`. TypeScript compiles without errors. The hook calls `
|
|
227
|
+
- Good: "`useBalance.ts` exports a hook that returns `{ balance, loading, error }`. TypeScript compiles without errors. The hook calls `getBalance()` and formats with `formatAmount()`, with proper error handling."
|
|
228
228
|
- Bad: "Balance hook is complete"
|
|
229
229
|
|
|
230
230
|
## Task Types
|
|
@@ -299,7 +299,7 @@ SDK Modules: [Which SDK modules are used, if any]
|
|
|
299
299
|
@.frontier-app/STATE.md
|
|
300
300
|
|
|
301
301
|
# SDK reference for modules used:
|
|
302
|
-
|
|
302
|
+
# Provided via <files_to_read> in spawn prompt (focused per-module refs from references/sdk/)
|
|
303
303
|
|
|
304
304
|
# Prior plan summaries if needed:
|
|
305
305
|
@.frontier-app/phases/XX-name/NN-NN-SUMMARY.md
|
|
@@ -376,57 +376,9 @@ Plans should complete within ~50% context. Each plan: 2-3 tasks maximum.
|
|
|
376
376
|
|
|
377
377
|
<frontier_os_specifics>
|
|
378
378
|
|
|
379
|
-
## Service Method Reference
|
|
379
|
+
## Service Method Reference
|
|
380
380
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
**Initialization (always via useServices):**
|
|
384
|
-
```typescript
|
|
385
|
-
import { useServices } from '../lib/frontier-services';
|
|
386
|
-
const services = useServices();
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
**Module access:**
|
|
390
|
-
```typescript
|
|
391
|
-
const wallet = services.wallet;
|
|
392
|
-
const storage = services.storage;
|
|
393
|
-
const chain = services.chain;
|
|
394
|
-
const user = services.user;
|
|
395
|
-
const partnerships = services.partnerships;
|
|
396
|
-
const thirdParty = services.thirdParty;
|
|
397
|
-
const communities = services.communities;
|
|
398
|
-
const events = services.events;
|
|
399
|
-
const offices = services.offices;
|
|
400
|
-
const navigation = services.navigation;
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
**Standard hook pattern:**
|
|
404
|
-
```typescript
|
|
405
|
-
export function useFeature() {
|
|
406
|
-
const services = useServices();
|
|
407
|
-
const [data, setData] = useState<FeatureType | null>(null);
|
|
408
|
-
const [loading, setLoading] = useState(true);
|
|
409
|
-
const [error, setError] = useState<string | null>(null);
|
|
410
|
-
|
|
411
|
-
useEffect(() => {
|
|
412
|
-
const fetch = async () => {
|
|
413
|
-
try {
|
|
414
|
-
const result = await services.module.method();
|
|
415
|
-
setData(result);
|
|
416
|
-
} catch (err) {
|
|
417
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
418
|
-
} finally {
|
|
419
|
-
setLoading(false);
|
|
420
|
-
}
|
|
421
|
-
};
|
|
422
|
-
fetch();
|
|
423
|
-
}, [services]);
|
|
424
|
-
|
|
425
|
-
return { data, loading, error };
|
|
426
|
-
}
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
**Permission mapping:** `services.module.method()` requires permission `module:method` in manifest.json.
|
|
381
|
+
Access modules via `services.<module>` from `useServices()` (imported from `../lib/frontier-services`). Property names match SDK module names in lowercase. Method signatures and types are in the focused SDK reference provided via `<files_to_read>`. Permission mapping: `services.module.method()` requires permission `module:method` in manifest.json.
|
|
430
382
|
|
|
431
383
|
## Required Patterns for All Plans
|
|
432
384
|
|
|
@@ -463,7 +415,8 @@ When spawned in revision mode with checker issues:
|
|
|
463
415
|
</revision_mode>
|
|
464
416
|
|
|
465
417
|
<sdk_reference>
|
|
466
|
-
|
|
418
|
+
Focused SDK reference is provided via <files_to_read> in the spawn prompt.
|
|
419
|
+
Contains only modules relevant to this app (from references/sdk/*.md).
|
|
467
420
|
</sdk_reference>
|
|
468
421
|
|
|
469
422
|
<app_patterns_reference>
|
package/agents/fos-researcher.md
CHANGED
|
@@ -48,16 +48,17 @@ If CONTEXT.md exists, it constrains your research scope. Don't explore alternati
|
|
|
48
48
|
All production apps live at `~/frontieros/frontier-os-app-*`. Use this mapping to identify which apps to study based on the current phase's feature needs.
|
|
49
49
|
|
|
50
50
|
### Wallet / Payments / Transactions
|
|
51
|
-
**
|
|
51
|
+
**Preferred apps (v0.23+ bigint amounts — study these first):** `frontier-os-app-fiat-rails` (bigint `transferFrontierDollar`/transfers + on/off-ramp), `frontier-os-app-ifnd-converter` (gold v0.24 migration — `getBalance()` + `formatAmount()`/`parseAmount()` patterns)
|
|
52
|
+
**Legacy apps (PRE-bigint — useful for UI/flow patterns only, NOT for amount handling):** `frontier-os-app-pos`, `frontier-os-app-pos-payment`, `frontier-os-app-subscriptions`. WARNING: these predate v0.23, use `amount: string`, and `pos`/`pos-payment` still call the REMOVED `getBalanceFormatted()`. Do NOT copy their amount/balance code — use the preferred apps for that.
|
|
52
53
|
**What to study:**
|
|
53
|
-
- `transferFrontierDollar()` and `transferOverallFrontierDollar()` call patterns
|
|
54
|
+
- `transferFrontierDollar()` and `transferOverallFrontierDollar()` call patterns — amounts are `bigint` base units (v0.23+); build with `parseAmount('10.5')` from `@frontiertower/frontier-sdk`
|
|
54
55
|
- Payment confirmation flows and receipt UI
|
|
55
|
-
- Balance display with `
|
|
56
|
+
- Balance display: `getBalance()` returns `WalletBalance { total, fnd, internalFnd }` (all `bigint`); format each field with `formatAmount()` from `@frontiertower/frontier-sdk`
|
|
56
57
|
- Transaction error handling and retry patterns
|
|
57
58
|
- Loading states during biometric auth prompts
|
|
58
59
|
|
|
59
60
|
### Events / Bookings / Reservations
|
|
60
|
-
**Apps:** `frontier-os-app-superhero-hotel`
|
|
61
|
+
**Apps:** `frontier-os-app-superhero-hotel` (PRE-bigint, v0.15.x — good for event/booking UI + flow patterns; if the phase touches FND amounts or the new event security-deposit methods, take amount/balance patterns from the v0.23+ Wallet apps above instead)
|
|
61
62
|
**What to study:**
|
|
62
63
|
- Event listing and detail views
|
|
63
64
|
- Booking flow (date selection, confirmation, payment)
|
|
@@ -79,7 +80,7 @@ All production apps live at `~/frontieros/frontier-os-app-*`. Use this mapping t
|
|
|
79
80
|
- Reward tracking with wallet integration
|
|
80
81
|
|
|
81
82
|
### Maintenance / AI / Tools
|
|
82
|
-
**Apps:** `frontier-os-app-maintenance`
|
|
83
|
+
**Apps:** `frontier-os-app-maintenance` (PRE-bigint, v0.15.x — good for ThirdParty/AI integration + tool-layout patterns; do not copy any FND amount/balance handling from it)
|
|
83
84
|
**What to study:**
|
|
84
85
|
- ThirdParty module usage
|
|
85
86
|
- AI/ML integration patterns
|
|
@@ -201,20 +202,21 @@ For every SDK module relevant to the phase, document:
|
|
|
201
202
|
```typescript
|
|
202
203
|
// Import
|
|
203
204
|
import { useSdk } from '../lib/sdk-context';
|
|
205
|
+
import { formatAmount } from '@frontiertower/frontier-sdk'; // bigint -> display string (package root, NOT /ui-utils)
|
|
204
206
|
|
|
205
207
|
// In component
|
|
206
208
|
const sdk = useSdk();
|
|
207
209
|
const wallet = sdk.getWallet();
|
|
208
210
|
|
|
209
|
-
// Call pattern
|
|
210
|
-
const [balance, setBalance] = useState<
|
|
211
|
+
// Call pattern — getBalance() returns WalletBalance { total, fnd, internalFnd } (all bigint base units)
|
|
212
|
+
const [balance, setBalance] = useState<WalletBalance | null>(null);
|
|
211
213
|
const [loading, setLoading] = useState(true);
|
|
212
214
|
const [error, setError] = useState<string | null>(null);
|
|
213
215
|
|
|
214
216
|
useEffect(() => {
|
|
215
217
|
const fetchBalance = async () => {
|
|
216
218
|
try {
|
|
217
|
-
const result = await wallet.getBalanceFormatted()
|
|
219
|
+
const result = await wallet.getBalance(); // display via formatAmount(result.fnd) — getBalanceFormatted() was REMOVED in v0.23
|
|
218
220
|
setBalance(result);
|
|
219
221
|
} catch (err) {
|
|
220
222
|
setError(err instanceof Error ? err.message : 'Failed to load balance');
|
|
@@ -225,7 +227,8 @@ useEffect(() => {
|
|
|
225
227
|
fetchBalance();
|
|
226
228
|
}, [wallet]);
|
|
227
229
|
|
|
228
|
-
//
|
|
230
|
+
// Display: formatAmount(balance.total) — symbol-free decimal string; prepend '$' yourself if wanted
|
|
231
|
+
// Permission: wallet:getBalance (import { formatAmount } from '@frontiertower/frontier-sdk')
|
|
229
232
|
```
|
|
230
233
|
|
|
231
234
|
### 2. Component Structure
|
|
@@ -357,7 +360,8 @@ Do NOT continue reading indefinitely. Research without output is a stuck signal.
|
|
|
357
360
|
</analysis_paralysis_guard>
|
|
358
361
|
|
|
359
362
|
<sdk_reference>
|
|
360
|
-
|
|
363
|
+
Focused SDK reference is provided via <files_to_read> in the spawn prompt.
|
|
364
|
+
Contains only modules relevant to this app (from references/sdk/*.md).
|
|
361
365
|
</sdk_reference>
|
|
362
366
|
|
|
363
367
|
<app_patterns_reference>
|
package/agents/fos-verifier.md
CHANGED
|
@@ -146,13 +146,14 @@ grep -q "createStandaloneHTML" src/views/Layout.tsx && echo "PASS: I-02" || echo
|
|
|
146
146
|
|
|
147
147
|
Verify the pattern: when `isInFrontierApp()` returns false, `createStandaloneHTML()` is called and rendered via `dangerouslySetInnerHTML`.
|
|
148
148
|
|
|
149
|
-
### I-03: SdkProvider wrapping children
|
|
149
|
+
### I-03: SdkProvider + FrontierServicesProvider wrapping children
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
|
-
grep -q "SdkProvider" src/views/Layout.tsx && echo "PASS: I-03" || echo "FAIL: I-03"
|
|
152
|
+
grep -q "SdkProvider" src/views/Layout.tsx && echo "PASS: I-03 SdkProvider" || echo "FAIL: I-03 SdkProvider"
|
|
153
|
+
grep -q "FrontierServicesProvider" src/views/Layout.tsx && echo "PASS: I-03 FrontierServicesProvider" || echo "FAIL: I-03 FrontierServicesProvider (useServices() will crash at runtime)"
|
|
153
154
|
```
|
|
154
155
|
|
|
155
|
-
Verify
|
|
156
|
+
Verify the "in Frontier" path wraps children in `<SdkProvider>` AND bridges the SDK into `<FrontierServicesProvider>` (feature code uses `useServices()`, not `useSdk()`).
|
|
156
157
|
|
|
157
158
|
### I-04: useSdk() hook available and used
|
|
158
159
|
|
|
@@ -174,11 +175,15 @@ If any file outside `sdk-context.tsx` instantiates `new FrontierSDK()` directly,
|
|
|
174
175
|
> **Tier 2 — SDK Integration phase only.** CORS origins are added during SDK Integration.
|
|
175
176
|
|
|
176
177
|
```bash
|
|
177
|
-
# Check all 3 origins present
|
|
178
|
+
# Check all 3 origins present (in the CSP frame-ancestors directive)
|
|
178
179
|
for origin in "os.frontiertower.io" "sandbox.os.frontiertower.io" "localhost:5173"; do
|
|
179
180
|
grep -q "$origin" vercel.json && echo "PASS: $origin" || echo "FAIL: $origin missing from vercel.json"
|
|
180
181
|
done
|
|
181
182
|
|
|
183
|
+
# Check CSP frame-ancestors + security headers
|
|
184
|
+
grep -q "frame-ancestors" vercel.json && echo "PASS: CSP frame-ancestors" || echo "FAIL: CSP frame-ancestors missing"
|
|
185
|
+
grep -q "X-Content-Type-Options" vercel.json && echo "PASS: security headers" || echo "FAIL: security headers missing"
|
|
186
|
+
|
|
182
187
|
# Check SPA rewrite
|
|
183
188
|
grep -q '"/(.*)"' vercel.json && echo "PASS: SPA rewrite" || echo "FAIL: SPA rewrite missing"
|
|
184
189
|
```
|
|
@@ -546,7 +551,8 @@ gaps: [list of failed check IDs, empty if passed]
|
|
|
546
551
|
</verification_rules_reference>
|
|
547
552
|
|
|
548
553
|
<sdk_reference>
|
|
549
|
-
|
|
554
|
+
Focused SDK reference is provided via <files_to_read> in the spawn prompt.
|
|
555
|
+
Contains only modules relevant to this app (from references/sdk/*.md).
|
|
550
556
|
</sdk_reference>
|
|
551
557
|
|
|
552
558
|
<app_patterns_reference>
|