gsd-pi 2.35.0-dev.4bbf377 → 2.35.0-dev.6179610
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/dist/resources/extensions/gsd/auto-loop.js +2 -7
- package/dist/resources/extensions/gsd/auto-model-selection.js +3 -15
- package/dist/resources/extensions/gsd/commands.js +1 -37
- package/dist/resources/extensions/gsd/guided-flow.js +1 -7
- package/dist/resources/skills/core-web-vitals/SKILL.md +1 -1
- package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +1 -1
- package/dist/resources/skills/github-workflows/SKILL.md +2 -0
- package/dist/resources/skills/swiftui/SKILL.md +208 -0
- package/dist/resources/skills/swiftui/references/animations.md +921 -0
- package/dist/resources/skills/swiftui/references/architecture.md +1561 -0
- package/dist/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/dist/resources/skills/swiftui/references/navigation.md +1492 -0
- package/dist/resources/skills/swiftui/references/networking-async.md +214 -0
- package/dist/resources/skills/swiftui/references/performance.md +1706 -0
- package/dist/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/dist/resources/skills/swiftui/references/state-management.md +1443 -0
- package/dist/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/dist/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/dist/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/dist/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/dist/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/dist/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/dist/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/dist/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/dist/resources/skills/swiftui/workflows/write-tests.md +235 -0
- package/dist/resources/skills/web-quality-audit/SKILL.md +2 -0
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +2 -10
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +8 -19
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/src/agent.ts +10 -31
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +4 -20
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +12 -36
- package/src/resources/extensions/gsd/auto-loop.ts +1 -11
- package/src/resources/extensions/gsd/auto-model-selection.ts +2 -23
- package/src/resources/extensions/gsd/commands.ts +1 -36
- package/src/resources/extensions/gsd/guided-flow.ts +1 -7
- package/src/resources/skills/core-web-vitals/SKILL.md +1 -1
- package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +1 -1
- package/src/resources/skills/github-workflows/SKILL.md +2 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
- package/src/resources/skills/web-quality-audit/SKILL.md +2 -0
|
@@ -1171,14 +1171,11 @@ export class AgentSession {
|
|
|
1171
1171
|
if (images) {
|
|
1172
1172
|
content.push(...images);
|
|
1173
1173
|
}
|
|
1174
|
-
this.agent.steer(
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
},
|
|
1180
|
-
"user",
|
|
1181
|
-
);
|
|
1174
|
+
this.agent.steer({
|
|
1175
|
+
role: "user",
|
|
1176
|
+
content,
|
|
1177
|
+
timestamp: Date.now(),
|
|
1178
|
+
});
|
|
1182
1179
|
}
|
|
1183
1180
|
|
|
1184
1181
|
/**
|
|
@@ -1190,14 +1187,11 @@ export class AgentSession {
|
|
|
1190
1187
|
if (images) {
|
|
1191
1188
|
content.push(...images);
|
|
1192
1189
|
}
|
|
1193
|
-
this.agent.followUp(
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
},
|
|
1199
|
-
"user",
|
|
1200
|
-
);
|
|
1190
|
+
this.agent.followUp({
|
|
1191
|
+
role: "user",
|
|
1192
|
+
content,
|
|
1193
|
+
timestamp: Date.now(),
|
|
1194
|
+
});
|
|
1201
1195
|
}
|
|
1202
1196
|
|
|
1203
1197
|
/**
|
|
@@ -1310,28 +1304,10 @@ export class AgentSession {
|
|
|
1310
1304
|
* @returns Object with steering and followUp arrays
|
|
1311
1305
|
*/
|
|
1312
1306
|
clearQueue(): { steering: string[]; followUp: string[] } {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
// while system-generated messages (extension notifications, etc.) are discarded.
|
|
1316
|
-
const userMessages = this.agent.drainUserMessages();
|
|
1317
|
-
|
|
1318
|
-
// Extract text content from preserved user messages
|
|
1319
|
-
const extractText = (m: AgentMessage): string => {
|
|
1320
|
-
if (!("content" in m) || !Array.isArray(m.content)) return "";
|
|
1321
|
-
const textPart = m.content.find((c: { type: string }) => c.type === "text");
|
|
1322
|
-
return textPart && "text" in textPart ? (textPart as { text: string }).text : "";
|
|
1323
|
-
};
|
|
1324
|
-
const preservedSteering = userMessages.steering.map(extractText).filter((t) => t.length > 0);
|
|
1325
|
-
const preservedFollowUp = userMessages.followUp.map(extractText).filter((t) => t.length > 0);
|
|
1326
|
-
|
|
1327
|
-
// Session-level string arrays track what was queued for display purposes.
|
|
1328
|
-
// Return the full set (session-tracked + any agent-only user messages).
|
|
1329
|
-
const steering = [...this._steeringMessages, ...preservedSteering];
|
|
1330
|
-
const followUp = [...this._followUpMessages, ...preservedFollowUp];
|
|
1307
|
+
const steering = [...this._steeringMessages];
|
|
1308
|
+
const followUp = [...this._followUpMessages];
|
|
1331
1309
|
this._steeringMessages = [];
|
|
1332
1310
|
this._followUpMessages = [];
|
|
1333
|
-
|
|
1334
|
-
// Clear remaining system messages from agent queues
|
|
1335
1311
|
this.agent.clearAllQueues();
|
|
1336
1312
|
return { steering, followUp };
|
|
1337
1313
|
}
|
|
@@ -456,7 +456,6 @@ export interface LoopDeps {
|
|
|
456
456
|
prefs: GSDPreferences | undefined,
|
|
457
457
|
verbose: boolean,
|
|
458
458
|
startModel: { provider: string; id: string } | null,
|
|
459
|
-
retryContext?: { isRetry: boolean; previousTier?: string },
|
|
460
459
|
) => Promise<{ routing: { tier: string; modelDowngraded: boolean } | null }>;
|
|
461
460
|
startUnitSupervision: (sctx: {
|
|
462
461
|
s: AutoSession;
|
|
@@ -1183,14 +1182,6 @@ export async function autoLoop(
|
|
|
1183
1182
|
unitId,
|
|
1184
1183
|
});
|
|
1185
1184
|
|
|
1186
|
-
// Detect retry and capture previous tier for escalation
|
|
1187
|
-
const isRetry = !!(
|
|
1188
|
-
s.currentUnit &&
|
|
1189
|
-
s.currentUnit.type === unitType &&
|
|
1190
|
-
s.currentUnit.id === unitId
|
|
1191
|
-
);
|
|
1192
|
-
const previousTier = s.currentUnitRouting?.tier;
|
|
1193
|
-
|
|
1194
1185
|
// Closeout previous unit
|
|
1195
1186
|
if (s.currentUnit) {
|
|
1196
1187
|
await deps.closeoutUnit(
|
|
@@ -1344,7 +1335,7 @@ export async function autoLoop(
|
|
|
1344
1335
|
);
|
|
1345
1336
|
}
|
|
1346
1337
|
|
|
1347
|
-
// Select and apply model
|
|
1338
|
+
// Select and apply model
|
|
1348
1339
|
const modelResult = await deps.selectAndApplyModel(
|
|
1349
1340
|
ctx,
|
|
1350
1341
|
pi,
|
|
@@ -1354,7 +1345,6 @@ export async function autoLoop(
|
|
|
1354
1345
|
prefs,
|
|
1355
1346
|
s.verbose,
|
|
1356
1347
|
s.autoModeStartModel,
|
|
1357
|
-
{ isRetry, previousTier },
|
|
1358
1348
|
);
|
|
1359
1349
|
s.currentUnitRouting =
|
|
1360
1350
|
modelResult.routing as AutoSession["currentUnitRouting"];
|
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
8
8
|
import type { GSDPreferences } from "./preferences.js";
|
|
9
9
|
import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
|
|
10
|
-
import type { ComplexityTier } from "./complexity-classifier.js";
|
|
11
10
|
import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
|
|
12
|
-
import { resolveModelForComplexity
|
|
11
|
+
import { resolveModelForComplexity } from "./model-router.js";
|
|
13
12
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
14
13
|
import { unitPhaseLabel } from "./auto-dashboard.js";
|
|
15
14
|
|
|
@@ -34,7 +33,6 @@ export async function selectAndApplyModel(
|
|
|
34
33
|
prefs: GSDPreferences | undefined,
|
|
35
34
|
verbose: boolean,
|
|
36
35
|
autoModeStartModel: { provider: string; id: string } | null,
|
|
37
|
-
retryContext?: { isRetry: boolean; previousTier?: string },
|
|
38
36
|
): Promise<ModelSelectionResult> {
|
|
39
37
|
const modelConfig = resolveModelWithFallbacksForUnit(unitType);
|
|
40
38
|
let routing: { tier: string; modelDowngraded: boolean } | null = null;
|
|
@@ -62,27 +60,8 @@ export async function selectAndApplyModel(
|
|
|
62
60
|
const shouldClassify = !isHook || routingConfig.hooks !== false;
|
|
63
61
|
|
|
64
62
|
if (shouldClassify) {
|
|
65
|
-
|
|
63
|
+
const classification = classifyUnitComplexity(unitType, unitId, basePath, budgetPct);
|
|
66
64
|
const availableModelIds = availableModels.map(m => m.id);
|
|
67
|
-
|
|
68
|
-
// Escalate tier on retry when escalate_on_failure is enabled (default: true)
|
|
69
|
-
if (
|
|
70
|
-
retryContext?.isRetry &&
|
|
71
|
-
retryContext.previousTier &&
|
|
72
|
-
routingConfig.escalate_on_failure !== false
|
|
73
|
-
) {
|
|
74
|
-
const escalated = escalateTier(retryContext.previousTier as ComplexityTier);
|
|
75
|
-
if (escalated) {
|
|
76
|
-
classification = { ...classification, tier: escalated, reason: "escalated after failure" };
|
|
77
|
-
if (verbose) {
|
|
78
|
-
ctx.ui.notify(
|
|
79
|
-
`Tier escalation: ${retryContext.previousTier} → ${escalated} (retry after failure)`,
|
|
80
|
-
"info",
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
65
|
const routingResult = resolveModelForComplexity(classification, modelConfig, routingConfig, availableModelIds);
|
|
87
66
|
|
|
88
67
|
if (routingResult.wasDowngraded) {
|
|
@@ -48,7 +48,6 @@ import { computeProgressScore, formatProgressLine } from "./progress-score.js";
|
|
|
48
48
|
import { runEnvironmentChecks } from "./doctor-environment.js";
|
|
49
49
|
import { handleLogs } from "./commands-logs.js";
|
|
50
50
|
import { handleStart, handleTemplates, getTemplateCompletions } from "./commands-workflow-templates.js";
|
|
51
|
-
import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.js";
|
|
52
51
|
|
|
53
52
|
|
|
54
53
|
/** Resolve the effective project root, accounting for worktree paths. */
|
|
@@ -70,39 +69,6 @@ export function projectRoot(): string {
|
|
|
70
69
|
return root;
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
/**
|
|
74
|
-
* Check if another process holds the auto-mode session lock.
|
|
75
|
-
* Returns the lock data if a remote session is alive, null otherwise.
|
|
76
|
-
*/
|
|
77
|
-
function getRemoteAutoSession(basePath: string): { pid: number } | null {
|
|
78
|
-
const lockData = readSessionLockData(basePath);
|
|
79
|
-
if (!lockData) return null;
|
|
80
|
-
if (lockData.pid === process.pid) return null;
|
|
81
|
-
if (!isSessionLockProcessAlive(lockData)) return null;
|
|
82
|
-
return { pid: lockData.pid };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Show a steering menu when auto-mode is running in another process.
|
|
87
|
-
* Returns true if a remote session was detected (caller should return early).
|
|
88
|
-
*/
|
|
89
|
-
function notifyRemoteAutoActive(ctx: ExtensionCommandContext, basePath: string): boolean {
|
|
90
|
-
const remote = getRemoteAutoSession(basePath);
|
|
91
|
-
if (!remote) return false;
|
|
92
|
-
ctx.ui.notify(
|
|
93
|
-
`Auto-mode is running in another process (PID ${remote.pid}).\n` +
|
|
94
|
-
`Use these commands to interact with it:\n` +
|
|
95
|
-
` /gsd status — check progress\n` +
|
|
96
|
-
` /gsd discuss — discuss architecture decisions\n` +
|
|
97
|
-
` /gsd queue — queue the next milestone\n` +
|
|
98
|
-
` /gsd steer — apply an override to active work\n` +
|
|
99
|
-
` /gsd capture — fire-and-forget thought\n` +
|
|
100
|
-
` /gsd stop — stop auto-mode`,
|
|
101
|
-
"warning",
|
|
102
|
-
);
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
72
|
export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
107
73
|
pi.registerCommand("gsd", {
|
|
108
74
|
description: "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|update",
|
|
@@ -546,7 +512,6 @@ export async function handleGSDCommand(
|
|
|
546
512
|
await handleDryRun(ctx, projectRoot());
|
|
547
513
|
return;
|
|
548
514
|
}
|
|
549
|
-
if (notifyRemoteAutoActive(ctx, projectRoot())) return;
|
|
550
515
|
const verboseMode = trimmed.includes("--verbose");
|
|
551
516
|
const debugMode = trimmed.includes("--debug");
|
|
552
517
|
if (debugMode) enableDebug(projectRoot());
|
|
@@ -941,7 +906,7 @@ Examples:
|
|
|
941
906
|
}
|
|
942
907
|
|
|
943
908
|
if (trimmed === "") {
|
|
944
|
-
|
|
909
|
+
// Bare /gsd defaults to step mode
|
|
945
910
|
await startAuto(ctx, pi, projectRoot(), false, { step: true });
|
|
946
911
|
return;
|
|
947
912
|
}
|
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
} from "./paths.js";
|
|
24
24
|
import { join } from "node:path";
|
|
25
25
|
import { readFileSync, existsSync, mkdirSync, readdirSync, rmSync, unlinkSync } from "node:fs";
|
|
26
|
-
import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.js";
|
|
27
26
|
import { nativeIsRepo, nativeInit } from "./native-git-bridge.js";
|
|
28
27
|
import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
|
|
29
28
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
@@ -517,13 +516,8 @@ export async function showDiscuss(
|
|
|
517
516
|
// If all pending slices are discussed, notify and exit instead of looping
|
|
518
517
|
const allDiscussed = pendingSlices.every(s => discussedMap.get(s.id));
|
|
519
518
|
if (allDiscussed) {
|
|
520
|
-
const lockData = readSessionLockData(basePath);
|
|
521
|
-
const remoteAutoRunning = lockData && lockData.pid !== process.pid && isSessionLockProcessAlive(lockData);
|
|
522
|
-
const nextStep = remoteAutoRunning
|
|
523
|
-
? "Auto-mode is already running — use /gsd status to check progress."
|
|
524
|
-
: "Run /gsd to start planning.";
|
|
525
519
|
ctx.ui.notify(
|
|
526
|
-
`All ${pendingSlices.length} slices discussed.
|
|
520
|
+
`All ${pendingSlices.length} slices discussed. Run /gsd to start planning.`,
|
|
527
521
|
"info",
|
|
528
522
|
);
|
|
529
523
|
return;
|
|
@@ -438,4 +438,4 @@ startTransition(() => setExpensiveState(newValue));
|
|
|
438
438
|
- [web.dev LCP](https://web.dev/articles/lcp)
|
|
439
439
|
- [web.dev INP](https://web.dev/articles/inp)
|
|
440
440
|
- [web.dev CLS](https://web.dev/articles/cls)
|
|
441
|
-
- [
|
|
441
|
+
- [Performance skill](../performance/SKILL.md)
|
|
@@ -42,7 +42,7 @@ The file must `export default function(pi: ExtensionAPI) { ... }`.
|
|
|
42
42
|
|
|
43
43
|
## Step 4: Check for Common Mistakes
|
|
44
44
|
|
|
45
|
-
Read
|
|
45
|
+
Read `references/key-rules-gotchas.md` and verify each rule against the extension code.
|
|
46
46
|
|
|
47
47
|
## Step 5: Add Debugging
|
|
48
48
|
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swiftui
|
|
3
|
+
description: SwiftUI apps from scratch through App Store. Full lifecycle - create, debug, test, optimize, ship.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<essential_principles>
|
|
7
|
+
## How We Work
|
|
8
|
+
|
|
9
|
+
**The user is the product owner. Claude is the developer.**
|
|
10
|
+
|
|
11
|
+
The user does not write code. The user does not read code. The user describes what they want and judges whether the result is acceptable. Claude implements, verifies, and reports outcomes.
|
|
12
|
+
|
|
13
|
+
### 1. Prove, Don't Promise
|
|
14
|
+
|
|
15
|
+
Never say "this should work." Prove it:
|
|
16
|
+
```bash
|
|
17
|
+
xcodebuild build 2>&1 | xcsift # Build passes
|
|
18
|
+
xcodebuild test # Tests pass
|
|
19
|
+
open .../App.app # App launches
|
|
20
|
+
```
|
|
21
|
+
If you didn't run it, you don't know it works.
|
|
22
|
+
|
|
23
|
+
### 2. Tests for Correctness, Eyes for Quality
|
|
24
|
+
|
|
25
|
+
| Question | How to Answer |
|
|
26
|
+
|----------|---------------|
|
|
27
|
+
| Does the logic work? | Write test, see it pass |
|
|
28
|
+
| Does it look right? | Launch app, user looks at it |
|
|
29
|
+
| Does it feel right? | User uses it |
|
|
30
|
+
| Does it crash? | Test + launch |
|
|
31
|
+
| Is it fast enough? | Profiler |
|
|
32
|
+
|
|
33
|
+
Tests verify *correctness*. The user verifies *desirability*.
|
|
34
|
+
|
|
35
|
+
### 3. Report Outcomes, Not Code
|
|
36
|
+
|
|
37
|
+
**Bad:** "I refactored the view model to use @Observable with environment injection"
|
|
38
|
+
**Good:** "Fixed the state bug. App now updates correctly when you add items. Ready for you to verify."
|
|
39
|
+
|
|
40
|
+
The user doesn't care what you changed. The user cares what's different.
|
|
41
|
+
|
|
42
|
+
### 4. Small Steps, Always Verified
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Change → Verify → Report → Next change
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Never batch up work. Never say "I made several changes." Each change is verified before the next. If something breaks, you know exactly what caused it.
|
|
49
|
+
|
|
50
|
+
### 5. Ask Before, Not After
|
|
51
|
+
|
|
52
|
+
Unclear requirement? Ask now.
|
|
53
|
+
Multiple valid approaches? Ask which.
|
|
54
|
+
Scope creep? Ask if wanted.
|
|
55
|
+
Big refactor needed? Ask permission.
|
|
56
|
+
|
|
57
|
+
Wrong: Build for 30 minutes, then "is this what you wanted?"
|
|
58
|
+
Right: "Before I start, does X mean Y or Z?"
|
|
59
|
+
|
|
60
|
+
### 6. Always Leave It Working
|
|
61
|
+
|
|
62
|
+
Every stopping point = working state. Tests pass, app launches, changes committed. The user can walk away anytime and come back to something that works.
|
|
63
|
+
</essential_principles>
|
|
64
|
+
|
|
65
|
+
<swiftui_principles>
|
|
66
|
+
## SwiftUI Framework Principles
|
|
67
|
+
|
|
68
|
+
### Declarative Mindset
|
|
69
|
+
Describe what the UI should look like for a given state, not how to mutate it. Let SwiftUI manage the rendering. Never force updates - change the state and let the framework react.
|
|
70
|
+
|
|
71
|
+
### Single Source of Truth
|
|
72
|
+
Every piece of data has one authoritative location. Use the right property wrapper: @State for view-local, @Observable for shared objects, @Environment for app-wide. Derived data should be computed, not stored.
|
|
73
|
+
|
|
74
|
+
### Composition Over Inheritance
|
|
75
|
+
Build complex UIs by composing small, focused views. Extract reusable components when patterns emerge. Prefer many small views over few large ones.
|
|
76
|
+
|
|
77
|
+
### Platform-Adaptive Design
|
|
78
|
+
Write once but respect platform idioms. Use native navigation patterns, respect safe areas, adapt to screen sizes. Test on all target platforms.
|
|
79
|
+
</swiftui_principles>
|
|
80
|
+
|
|
81
|
+
<intake>
|
|
82
|
+
**What would you like to do?**
|
|
83
|
+
|
|
84
|
+
1. Build a new SwiftUI app
|
|
85
|
+
2. Debug an existing SwiftUI app
|
|
86
|
+
3. Add a feature to an existing app
|
|
87
|
+
4. Write/run tests
|
|
88
|
+
5. Optimize performance
|
|
89
|
+
6. Ship/release to App Store
|
|
90
|
+
7. Something else
|
|
91
|
+
|
|
92
|
+
**Then read the matching workflow from `workflows/` and follow it.**
|
|
93
|
+
</intake>
|
|
94
|
+
|
|
95
|
+
<routing>
|
|
96
|
+
| Response | Workflow |
|
|
97
|
+
|----------|----------|
|
|
98
|
+
| 1, "new", "create", "build", "start" | `workflows/build-new-app.md` |
|
|
99
|
+
| 2, "broken", "fix", "debug", "crash", "bug" | `workflows/debug-swiftui.md` |
|
|
100
|
+
| 3, "add", "feature", "implement", "change" | `workflows/add-feature.md` |
|
|
101
|
+
| 4, "test", "tests", "TDD", "coverage" | `workflows/write-tests.md` |
|
|
102
|
+
| 5, "slow", "optimize", "performance", "fast" | `workflows/optimize-performance.md` |
|
|
103
|
+
| 6, "ship", "release", "deploy", "publish", "app store" | `workflows/ship-app.md` |
|
|
104
|
+
| 7, other | Clarify, then select workflow or references |
|
|
105
|
+
</routing>
|
|
106
|
+
|
|
107
|
+
<verification_loop>
|
|
108
|
+
## After Every Change
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# 1. Does it build?
|
|
112
|
+
xcodebuild -scheme AppName build 2>&1 | xcsift
|
|
113
|
+
|
|
114
|
+
# 2. Do tests pass? (use Core scheme for SwiftUI apps to avoid @main hang)
|
|
115
|
+
xcodebuild -scheme AppNameCore test
|
|
116
|
+
|
|
117
|
+
# 3. Does it launch?
|
|
118
|
+
# macOS:
|
|
119
|
+
open ./build/Build/Products/Debug/AppName.app
|
|
120
|
+
|
|
121
|
+
# iOS Simulator:
|
|
122
|
+
xcrun simctl boot "iPhone 15 Pro" 2>/dev/null || true
|
|
123
|
+
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/AppName.app
|
|
124
|
+
xcrun simctl launch booted com.yourcompany.appname
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Note: If tests hang, the test target likely depends on the app target which has `@main`. Extract testable code to a framework target. See `../macos-apps/references/testing-tdd.md` for the pattern.
|
|
128
|
+
|
|
129
|
+
Report to the user:
|
|
130
|
+
- "Build: ✓"
|
|
131
|
+
- "Tests: 12 pass, 0 fail"
|
|
132
|
+
- "App launches, ready for you to check [specific thing]"
|
|
133
|
+
</verification_loop>
|
|
134
|
+
|
|
135
|
+
<cli_infrastructure>
|
|
136
|
+
## CLI Workflow References
|
|
137
|
+
|
|
138
|
+
For building, debugging, testing, and shipping from CLI without opening Xcode, read these from `../macos-apps/references/`:
|
|
139
|
+
|
|
140
|
+
| Reference | Use For |
|
|
141
|
+
|-----------|---------|
|
|
142
|
+
| `cli-workflow.md` | Build, run, test commands; xcodebuild usage; code signing |
|
|
143
|
+
| `cli-observability.md` | Log streaming, crash analysis, memory debugging, LLDB |
|
|
144
|
+
| `project-scaffolding.md` | XcodeGen project.yml templates, file structure, entitlements |
|
|
145
|
+
| `testing-tdd.md` | Test patterns that work from CLI, avoiding @main hangs |
|
|
146
|
+
|
|
147
|
+
These docs are platform-agnostic. For iOS, change destinations:
|
|
148
|
+
```bash
|
|
149
|
+
# iOS Simulator
|
|
150
|
+
xcodebuild -scheme AppName -destination 'platform=iOS Simulator,name=iPhone 15 Pro' build
|
|
151
|
+
|
|
152
|
+
# macOS
|
|
153
|
+
xcodebuild -scheme AppName build
|
|
154
|
+
```
|
|
155
|
+
</cli_infrastructure>
|
|
156
|
+
|
|
157
|
+
<reference_index>
|
|
158
|
+
## Domain Knowledge
|
|
159
|
+
|
|
160
|
+
All in `references/`:
|
|
161
|
+
|
|
162
|
+
**Core:**
|
|
163
|
+
- architecture.md - MVVM patterns, project structure, dependency injection
|
|
164
|
+
- state-management.md - Property wrappers, @Observable, data flow
|
|
165
|
+
- layout-system.md - Stacks, grids, GeometryReader, custom layouts
|
|
166
|
+
|
|
167
|
+
**Navigation & Animation:**
|
|
168
|
+
- navigation.md - NavigationStack, sheets, tabs, deep linking
|
|
169
|
+
- animations.md - Built-in animations, transitions, matchedGeometryEffect
|
|
170
|
+
|
|
171
|
+
**Data & Platform:**
|
|
172
|
+
- swiftdata.md - Persistence, @Model, @Query, CloudKit sync
|
|
173
|
+
- platform-integration.md - iOS/macOS/watchOS/visionOS specifics
|
|
174
|
+
- uikit-appkit-interop.md - UIViewRepresentable, hosting controllers
|
|
175
|
+
|
|
176
|
+
**Support:**
|
|
177
|
+
- networking-async.md - async/await, .task modifier, API clients
|
|
178
|
+
- testing-debugging.md - Previews, unit tests, UI tests, debugging
|
|
179
|
+
- performance.md - Profiling, lazy loading, view identity
|
|
180
|
+
</reference_index>
|
|
181
|
+
|
|
182
|
+
<workflows_index>
|
|
183
|
+
## Workflows
|
|
184
|
+
|
|
185
|
+
All in `workflows/`:
|
|
186
|
+
|
|
187
|
+
| Workflow | Purpose |
|
|
188
|
+
|----------|---------|
|
|
189
|
+
| build-new-app.md | Create new SwiftUI app from scratch |
|
|
190
|
+
| debug-swiftui.md | Find and fix SwiftUI bugs |
|
|
191
|
+
| add-feature.md | Add functionality to existing app |
|
|
192
|
+
| write-tests.md | Write UI and unit tests |
|
|
193
|
+
| optimize-performance.md | Profile and improve performance |
|
|
194
|
+
| ship-app.md | App Store submission, TestFlight, distribution |
|
|
195
|
+
</workflows_index>
|
|
196
|
+
|
|
197
|
+
<canonical_terminology>
|
|
198
|
+
## Terminology
|
|
199
|
+
|
|
200
|
+
Use these terms consistently:
|
|
201
|
+
- **view** (not: widget, component, element)
|
|
202
|
+
- **@Observable** (not: ObservableObject, @Published for new iOS 17+ code)
|
|
203
|
+
- **NavigationStack** (not: NavigationView - deprecated)
|
|
204
|
+
- **SwiftData** (not: Core Data for new projects)
|
|
205
|
+
- **@Environment** (not: @EnvironmentObject for new code)
|
|
206
|
+
- **modifier** (not: method/function when describing view modifiers)
|
|
207
|
+
- **body** (not: render/build when describing view body)
|
|
208
|
+
</canonical_terminology>
|