@agent-native/core 0.72.4 → 0.74.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.
Files changed (129) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +67 -0
  3. package/corpus/core/docs/content/skills-guide.md +14 -0
  4. package/corpus/core/docs/content/template-clips.md +7 -1
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/agent/durable-background.ts +35 -12
  7. package/corpus/core/src/agent/production-agent.ts +55 -8
  8. package/corpus/core/src/cli/create.ts +1 -1
  9. package/corpus/core/src/cli/skills.ts +450 -19
  10. package/corpus/core/src/client/ConnectBuilderCard.tsx +7 -7
  11. package/corpus/core/src/client/NewWorkspaceAppFlow.tsx +1 -1
  12. package/corpus/core/src/client/blocks/library/AnnotatedCodeBlock.tsx +33 -18
  13. package/corpus/core/src/client/blocks/library/annotation-rail.tsx +3 -4
  14. package/corpus/core/src/client/components/CodeRequiredDialog.tsx +5 -8
  15. package/corpus/core/src/server/agent-chat-plugin.ts +7 -7
  16. package/corpus/core/src/server/auth.ts +7 -0
  17. package/corpus/core/src/server/onboarding-html.ts +95 -9
  18. package/corpus/core/src/server/self-dispatch.ts +8 -1
  19. package/corpus/core/src/templates/default/AGENTS.md +4 -0
  20. package/corpus/core/src/templates/default/package.json +2 -1
  21. package/corpus/core/src/templates/headless/AGENTS.md +4 -0
  22. package/corpus/core/src/templates/headless/package.json +1 -0
  23. package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
  24. package/corpus/core/src/templates/workspace-root/README.md +4 -0
  25. package/corpus/core/src/templates/workspace-root/package.json +1 -0
  26. package/corpus/templates/analytics/actions/compose-dashboard.ts +34 -0
  27. package/corpus/templates/analytics/actions/install-dashboard-template.ts +51 -3
  28. package/corpus/templates/analytics/actions/update-dashboard.ts +3 -4
  29. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +175 -48
  30. package/corpus/templates/analytics/app/global.css +15 -13
  31. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +9 -32
  32. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +0 -26
  33. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +0 -23
  34. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/pivot.ts +65 -2
  35. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +3 -4
  36. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-charts-now-fill-sparse-template-time-series-suppor.md +6 -0
  37. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-filters-now-apply-consistently-to-first-party-traf.md +6 -0
  38. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-rows-now-auto-fit-panels-so-one-two-or-three-chart.md +6 -0
  39. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-traffic-tables-now-show-top-urls-and-shared-clips-.md +6 -0
  40. package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +5 -0
  41. package/corpus/templates/analytics/changelog/2026-06-24-first-party-template-dashboards-now-include-repeat-users-ret.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-06-24-retention-charts-now-break-out-1-day-and-7-day-return-rates-.md +6 -0
  43. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +330 -49
  44. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +7 -2
  45. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +35 -8
  46. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +337 -45
  47. package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +1 -1
  48. package/corpus/templates/calendar/server/plugins/auth.ts +12 -0
  49. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -7
  50. package/corpus/templates/clips/AGENTS.md +6 -2
  51. package/corpus/templates/clips/app/components/capture-install-options.tsx +20 -4
  52. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -0
  53. package/corpus/templates/clips/app/components/player/player-controls.tsx +62 -32
  54. package/corpus/templates/clips/app/lib/capture-install-options.ts +39 -5
  55. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +786 -323
  56. package/corpus/templates/clips/app/routes/download.tsx +2 -2
  57. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
  58. package/corpus/templates/clips/changelog/2026-06-24-inline-slack-playback-controls-no-longer-shift-when-adjustin.md +6 -0
  59. package/corpus/templates/clips/changelog/2026-06-24-settings-now-puts-what-s-new-in-a-compact-sidebar-prioritize.md +6 -0
  60. package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-console-log-stream-all-le.md +6 -0
  61. package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-fetch-xhr-network-request.md +6 -0
  62. package/corpus/templates/clips/changelog/2026-06-24-the-chrome-extension-option-now-only-appears-on-supported-hosts.md +5 -0
  63. package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +13 -8
  64. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  65. package/corpus/templates/clips/chrome-extension/scripts/package.ts +15 -2
  66. package/corpus/templates/clips/chrome-extension/src/overlay.css +4 -25
  67. package/corpus/templates/clips/chrome-extension/src/overlay.ts +1 -3
  68. package/corpus/templates/clips/server/lib/public-agent-context.ts +67 -24
  69. package/corpus/templates/mail/server/plugins/auth.ts +4 -1
  70. package/corpus/templates/plan/app/pages/PlansPage.tsx +271 -197
  71. package/corpus/templates/plan/changelog/2026-06-24-annotated-code-callouts-now-use-numbered-gutter-markers-that.md +6 -0
  72. package/corpus/templates/plan/server/plan-content.ts +45 -2
  73. package/dist/agent/durable-background.d.ts +9 -4
  74. package/dist/agent/durable-background.d.ts.map +1 -1
  75. package/dist/agent/durable-background.js +34 -11
  76. package/dist/agent/durable-background.js.map +1 -1
  77. package/dist/agent/production-agent.d.ts.map +1 -1
  78. package/dist/agent/production-agent.js +52 -8
  79. package/dist/agent/production-agent.js.map +1 -1
  80. package/dist/cli/create.js +1 -1
  81. package/dist/cli/create.js.map +1 -1
  82. package/dist/cli/skills.d.ts.map +1 -1
  83. package/dist/cli/skills.js +361 -19
  84. package/dist/cli/skills.js.map +1 -1
  85. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  86. package/dist/client/ConnectBuilderCard.js +6 -6
  87. package/dist/client/ConnectBuilderCard.js.map +1 -1
  88. package/dist/client/NewWorkspaceAppFlow.js +1 -1
  89. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  90. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  91. package/dist/client/blocks/library/AnnotatedCodeBlock.js +14 -10
  92. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  93. package/dist/client/blocks/library/annotation-rail.d.ts +3 -4
  94. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
  95. package/dist/client/blocks/library/annotation-rail.js +3 -4
  96. package/dist/client/blocks/library/annotation-rail.js.map +1 -1
  97. package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
  98. package/dist/client/components/CodeRequiredDialog.js +2 -1
  99. package/dist/client/components/CodeRequiredDialog.js.map +1 -1
  100. package/dist/server/agent-chat-plugin.js +7 -7
  101. package/dist/server/agent-chat-plugin.js.map +1 -1
  102. package/dist/server/auth.d.ts +7 -0
  103. package/dist/server/auth.d.ts.map +1 -1
  104. package/dist/server/auth.js +1 -0
  105. package/dist/server/auth.js.map +1 -1
  106. package/dist/server/onboarding-html.d.ts +15 -0
  107. package/dist/server/onboarding-html.d.ts.map +1 -1
  108. package/dist/server/onboarding-html.js +73 -9
  109. package/dist/server/onboarding-html.js.map +1 -1
  110. package/dist/server/self-dispatch.d.ts.map +1 -1
  111. package/dist/server/self-dispatch.js +5 -1
  112. package/dist/server/self-dispatch.js.map +1 -1
  113. package/dist/templates/default/AGENTS.md +4 -0
  114. package/dist/templates/default/package.json +2 -1
  115. package/dist/templates/headless/AGENTS.md +4 -0
  116. package/dist/templates/headless/package.json +1 -0
  117. package/dist/templates/workspace-root/AGENTS.md +4 -0
  118. package/dist/templates/workspace-root/README.md +4 -0
  119. package/dist/templates/workspace-root/package.json +1 -0
  120. package/docs/content/skills-guide.md +14 -0
  121. package/docs/content/template-clips.md +7 -1
  122. package/package.json +1 -1
  123. package/src/templates/default/AGENTS.md +4 -0
  124. package/src/templates/default/package.json +2 -1
  125. package/src/templates/headless/AGENTS.md +4 -0
  126. package/src/templates/headless/package.json +1 -0
  127. package/src/templates/workspace-root/AGENTS.md +4 -0
  128. package/src/templates/workspace-root/README.md +4 -0
  129. package/src/templates/workspace-root/package.json +1 -0
@@ -31,13 +31,14 @@ import {
31
31
  } from "./context-xray-local.js";
32
32
  import { CLIENTS, type ClientId } from "./mcp-config-writers.js";
33
33
  import { PR_VISUAL_RECAP_SETUP, writePrVisualRecapWorkflow } from "./recap.js";
34
+ import { setupAgentSymlinks } from "./setup-agents.js";
34
35
 
35
36
  const HELP = `npx @agent-native/core@latest skills
36
37
 
37
38
  Usage:
38
39
  npx @agent-native/core@latest skills list
39
- npx @agent-native/core@latest skills status [assets|content|design-exploration|visual-plan|visual-recap|context-xray] [--client codex|claude-code|pi|all] [--scope user|project] [--json]
40
- npx @agent-native/core@latest skills update [assets|content|design-exploration|visual-plan|visual-recap|context-xray] [--client codex|claude-code|pi|all] [--scope user|project] [--dry-run] [--json]
40
+ npx @agent-native/core@latest skills status [assets|content|design-exploration|visual-plan|visual-recap|context-xray|scaffold] [--client codex|claude-code|pi|all] [--scope user|project] [--json]
41
+ npx @agent-native/core@latest skills update [assets|content|design-exploration|visual-plan|visual-recap|context-xray|scaffold] [--client codex|claude-code|pi|all] [--scope user|project] [--dry-run] [--json]
41
42
  npx @agent-native/core@latest skills add assets|content|design-exploration|visual-plan|visual-recap|context-xray [--client codex|claude-code|cowork|cursor|opencode|github-copilot|all] [--scope user|project] [--mode hosted|local-files|self-hosted] [--mcp-url <url>] [--no-connect] [--with-github-action] [--yes] [--dry-run] [--json]
42
43
  npx @agent-native/core@latest skills add <manifest-or-app-dir|skill-repo> [--skill <name>] [--client ...] [--yes]
43
44
 
@@ -52,6 +53,7 @@ Examples:
52
53
  npx @agent-native/core@latest skills add visual-plan --mode self-hosted --mcp-url https://my-plan-app.example.com
53
54
  npx @agent-native/core@latest skills status visual-plan
54
55
  npx @agent-native/core@latest skills update visual-plan
56
+ npx @agent-native/core@latest skills update scaffold --project
55
57
  npx @agent-native/core@latest skills add visual-plan --no-connect
56
58
  npx @agent-native/core@latest skills add context-xray --client all
57
59
  npx @agent-native/core@latest skills add assets --client claude-code
@@ -101,7 +103,10 @@ run "npx @agent-native/core@latest recap setup" / "npx @agent-native/core@latest
101
103
  verify GitHub Actions. Docs: https://www.agent-native.com/docs/pr-visual-recap.
102
104
 
103
105
  The status/update commands inspect copied Agent Native skill folders and refresh
104
- their instruction files from the current @agent-native/core package.`;
106
+ their instruction files from the current @agent-native/core package. In generated
107
+ apps/workspaces, "skills update scaffold --project" refreshes the framework
108
+ skills copied into the scaffold and repairs AGENTS.md / CLAUDE.md and
109
+ .agents/skills / .claude/skills compatibility links.`;
105
110
 
106
111
  const ASSETS_SKILL_MD = `---
107
112
  name: assets
@@ -2844,6 +2849,19 @@ interface SkillInstallState {
2844
2849
  managed: boolean;
2845
2850
  }
2846
2851
 
2852
+ interface ScaffoldGuidanceState {
2853
+ kind: "workspace-core" | "standalone";
2854
+ displayName: string;
2855
+ templateName: "workspace-core" | "headless" | "default";
2856
+ path: string;
2857
+ sourcePath: string;
2858
+ projectRoot: string;
2859
+ workspaceRoot?: string;
2860
+ sharedPackageDir?: string;
2861
+ current: boolean;
2862
+ skillCount: number;
2863
+ }
2864
+
2847
2865
  interface SkillInstallTarget {
2848
2866
  id: string;
2849
2867
  displayName: string;
@@ -3591,7 +3609,368 @@ function skillSearchRoots(input: {
3591
3609
  });
3592
3610
  }
3593
3611
 
3612
+ const SCAFFOLD_GUIDANCE_TARGETS = new Set([
3613
+ "scaffold",
3614
+ "generated",
3615
+ "generated-app",
3616
+ "generated-workspace",
3617
+ "workspace",
3618
+ "workspace-core",
3619
+ "framework-guidance",
3620
+ ]);
3621
+
3622
+ function isScaffoldGuidanceTarget(value: string | undefined): boolean {
3623
+ if (!value) return false;
3624
+ return SCAFFOLD_GUIDANCE_TARGETS.has(value.trim().toLowerCase());
3625
+ }
3626
+
3627
+ function corePackageRootDir(): string {
3628
+ const here = path.dirname(fileURLToPath(import.meta.url));
3629
+ return path.resolve(here, "../..");
3630
+ }
3631
+
3632
+ function bundledScaffoldSkillsDir(
3633
+ templateName: ScaffoldGuidanceState["templateName"],
3634
+ ): string {
3635
+ return path.join(
3636
+ corePackageRootDir(),
3637
+ "src",
3638
+ "templates",
3639
+ templateName,
3640
+ ".agents",
3641
+ "skills",
3642
+ );
3643
+ }
3644
+
3645
+ function readJsonRecord(file: string): Record<string, unknown> | undefined {
3646
+ try {
3647
+ const parsed = JSON.parse(fs.readFileSync(file, "utf-8"));
3648
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
3649
+ return parsed as Record<string, unknown>;
3650
+ }
3651
+ } catch {}
3652
+ return undefined;
3653
+ }
3654
+
3655
+ function readPackageJson(dir: string): Record<string, unknown> | undefined {
3656
+ return readJsonRecord(path.join(dir, "package.json"));
3657
+ }
3658
+
3659
+ function packageName(pkg: Record<string, unknown> | undefined): string | null {
3660
+ return typeof pkg?.name === "string" ? pkg.name : null;
3661
+ }
3662
+
3663
+ function workspaceCorePackageName(
3664
+ pkg: Record<string, unknown> | undefined,
3665
+ ): string | null {
3666
+ const agentNative = pkg?.["agent-native"];
3667
+ if (
3668
+ agentNative &&
3669
+ typeof agentNative === "object" &&
3670
+ !Array.isArray(agentNative) &&
3671
+ typeof (agentNative as Record<string, unknown>).workspaceCore === "string"
3672
+ ) {
3673
+ return (agentNative as Record<string, string>).workspaceCore;
3674
+ }
3675
+ return null;
3676
+ }
3677
+
3678
+ function hasAgentNativeCoreDependency(
3679
+ pkg: Record<string, unknown> | undefined,
3680
+ ): boolean {
3681
+ for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
3682
+ const deps = pkg?.[field];
3683
+ if (
3684
+ deps &&
3685
+ typeof deps === "object" &&
3686
+ !Array.isArray(deps) &&
3687
+ "@agent-native/core" in deps
3688
+ ) {
3689
+ return true;
3690
+ }
3691
+ }
3692
+ return false;
3693
+ }
3694
+
3695
+ function findWorkspaceCorePackageDir(
3696
+ workspaceRoot: string,
3697
+ workspaceCoreName: string,
3698
+ ): string | undefined {
3699
+ const packagesDir = path.join(workspaceRoot, "packages");
3700
+ if (!fs.existsSync(packagesDir)) return undefined;
3701
+ for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) {
3702
+ if (!entry.isDirectory()) continue;
3703
+ const candidate = path.join(packagesDir, entry.name);
3704
+ if (packageName(readPackageJson(candidate)) === workspaceCoreName) {
3705
+ return candidate;
3706
+ }
3707
+ }
3708
+ const fallback = path.join(workspaceRoot, "packages", "shared");
3709
+ return fs.existsSync(path.join(fallback, "package.json"))
3710
+ ? fallback
3711
+ : undefined;
3712
+ }
3713
+
3714
+ function findGeneratedWorkspace(startDir: string):
3715
+ | {
3716
+ workspaceRoot: string;
3717
+ sharedPackageDir: string;
3718
+ }
3719
+ | undefined {
3720
+ let current = path.resolve(startDir);
3721
+ while (true) {
3722
+ const pkg = readPackageJson(current);
3723
+ const workspaceCoreName = workspaceCorePackageName(pkg);
3724
+ if (workspaceCoreName && fs.existsSync(path.join(current, "apps"))) {
3725
+ const sharedPackageDir = findWorkspaceCorePackageDir(
3726
+ current,
3727
+ workspaceCoreName,
3728
+ );
3729
+ if (sharedPackageDir) {
3730
+ return { workspaceRoot: current, sharedPackageDir };
3731
+ }
3732
+ }
3733
+ const parent = path.dirname(current);
3734
+ if (parent === current) break;
3735
+ current = parent;
3736
+ }
3737
+ return undefined;
3738
+ }
3739
+
3740
+ function detectStandaloneScaffoldTemplate(
3741
+ projectRoot: string,
3742
+ ): "headless" | "default" | undefined {
3743
+ const pkg = readPackageJson(projectRoot);
3744
+ if (!hasAgentNativeCoreDependency(pkg)) return undefined;
3745
+ if (!fs.existsSync(path.join(projectRoot, ".agents", "skills"))) {
3746
+ return undefined;
3747
+ }
3748
+
3749
+ const hasAppDir = fs.existsSync(path.join(projectRoot, "app"));
3750
+ const hasHeadlessHello = fs.existsSync(
3751
+ path.join(projectRoot, "actions", "hello.ts"),
3752
+ );
3753
+ if (!hasAppDir && hasHeadlessHello) return "headless";
3754
+
3755
+ const looksLikeDefaultTemplate =
3756
+ fs.existsSync(path.join(projectRoot, "app", "routes", "database.tsx")) &&
3757
+ fs.existsSync(path.join(projectRoot, "app", "routes", "_index.tsx")) &&
3758
+ fs.existsSync(path.join(projectRoot, "actions", "view-screen.ts"));
3759
+ return looksLikeDefaultTemplate ? "default" : undefined;
3760
+ }
3761
+
3762
+ function listImmediateSkillDirs(dir: string): string[] {
3763
+ if (!fs.existsSync(dir)) return [];
3764
+ return fs
3765
+ .readdirSync(dir, { withFileTypes: true })
3766
+ .filter((entry) => entry.isDirectory())
3767
+ .map((entry) => entry.name)
3768
+ .sort();
3769
+ }
3770
+
3771
+ function skillDirContentsMatch(sourceDir: string, targetDir: string): boolean {
3772
+ const expected = listSkillFolderFiles(sourceDir);
3773
+ const actual = listSkillFolderFiles(targetDir);
3774
+ const expectedFiles = Object.keys(expected).sort();
3775
+ const actualFiles = Object.keys(actual).sort();
3776
+ if (expectedFiles.length !== actualFiles.length) return false;
3777
+ for (let i = 0; i < expectedFiles.length; i += 1) {
3778
+ if (expectedFiles[i] !== actualFiles[i]) return false;
3779
+ }
3780
+ return expectedFiles.every((file) => expected[file] === actual[file]);
3781
+ }
3782
+
3783
+ function scaffoldGuidanceCurrent(
3784
+ sourceRoot: string,
3785
+ targetRoot: string,
3786
+ ): boolean {
3787
+ const skills = listImmediateSkillDirs(sourceRoot);
3788
+ if (skills.length === 0) return false;
3789
+ return skills.every((skill) =>
3790
+ skillDirContentsMatch(
3791
+ path.join(sourceRoot, skill),
3792
+ path.join(targetRoot, skill),
3793
+ ),
3794
+ );
3795
+ }
3796
+
3797
+ function collectScaffoldGuidanceStates(
3798
+ parsed: ParsedSkillsArgs,
3799
+ options: RunSkillsOptions,
3800
+ ): ScaffoldGuidanceState[] {
3801
+ if (parsed.target && !isScaffoldGuidanceTarget(parsed.target)) return [];
3802
+ if (parsed.scopeExplicit && parsed.scope !== "project") return [];
3803
+
3804
+ const baseDir = path.resolve(options.baseDir ?? process.cwd());
3805
+ const workspace = findGeneratedWorkspace(baseDir);
3806
+ if (workspace) {
3807
+ const sourcePath = bundledScaffoldSkillsDir("workspace-core");
3808
+ const targetPath = path.join(
3809
+ workspace.sharedPackageDir,
3810
+ ".agents",
3811
+ "skills",
3812
+ );
3813
+ if (!fs.existsSync(sourcePath)) return [];
3814
+ return [
3815
+ {
3816
+ kind: "workspace-core",
3817
+ displayName: "Generated workspace framework skills",
3818
+ templateName: "workspace-core",
3819
+ path: targetPath,
3820
+ sourcePath,
3821
+ projectRoot: workspace.workspaceRoot,
3822
+ workspaceRoot: workspace.workspaceRoot,
3823
+ sharedPackageDir: workspace.sharedPackageDir,
3824
+ current: scaffoldGuidanceCurrent(sourcePath, targetPath),
3825
+ skillCount: listImmediateSkillDirs(sourcePath).length,
3826
+ },
3827
+ ];
3828
+ }
3829
+
3830
+ const templateName = detectStandaloneScaffoldTemplate(baseDir);
3831
+ if (!templateName) return [];
3832
+ const sourcePath = bundledScaffoldSkillsDir(templateName);
3833
+ const targetPath = path.join(baseDir, ".agents", "skills");
3834
+ if (!fs.existsSync(sourcePath)) return [];
3835
+ return [
3836
+ {
3837
+ kind: "standalone",
3838
+ displayName: `Generated ${templateName} app framework skills`,
3839
+ templateName,
3840
+ path: targetPath,
3841
+ sourcePath,
3842
+ projectRoot: baseDir,
3843
+ current: scaffoldGuidanceCurrent(sourcePath, targetPath),
3844
+ skillCount: listImmediateSkillDirs(sourcePath).length,
3845
+ },
3846
+ ];
3847
+ }
3848
+
3849
+ function copyScaffoldGuidanceSkills(
3850
+ sourceRoot: string,
3851
+ targetRoot: string,
3852
+ ): void {
3853
+ fs.mkdirSync(targetRoot, { recursive: true });
3854
+ for (const skill of listImmediateSkillDirs(sourceRoot)) {
3855
+ const targetSkillDir = path.join(targetRoot, skill);
3856
+ if (
3857
+ fs.existsSync(targetSkillDir) &&
3858
+ fs.lstatSync(targetSkillDir).isSymbolicLink()
3859
+ ) {
3860
+ continue;
3861
+ }
3862
+ fs.rmSync(targetSkillDir, { recursive: true, force: true });
3863
+ fs.cpSync(path.join(sourceRoot, skill), targetSkillDir, {
3864
+ recursive: true,
3865
+ });
3866
+ }
3867
+ }
3868
+
3869
+ function updateScaffoldGuidanceStates(
3870
+ states: ScaffoldGuidanceState[],
3871
+ dryRun: boolean,
3872
+ ): ScaffoldGuidanceState[] {
3873
+ const updated: ScaffoldGuidanceState[] = [];
3874
+ for (const state of states) {
3875
+ if (state.current) continue;
3876
+ if (!dryRun) {
3877
+ copyScaffoldGuidanceSkills(state.sourcePath, state.path);
3878
+ }
3879
+ updated.push({
3880
+ ...state,
3881
+ current: !dryRun,
3882
+ });
3883
+ }
3884
+ return updated;
3885
+ }
3886
+
3887
+ function ensureWorkspaceRootSkillsLink(
3888
+ workspaceRoot: string,
3889
+ sharedPackageDir: string,
3890
+ ): void {
3891
+ const sharedSkillsDir = path.join(sharedPackageDir, ".agents", "skills");
3892
+ if (!fs.existsSync(sharedSkillsDir)) return;
3893
+
3894
+ const agentsDir = path.join(workspaceRoot, ".agents");
3895
+ const linkPath = path.join(agentsDir, "skills");
3896
+ const target = path.relative(agentsDir, sharedSkillsDir);
3897
+
3898
+ fs.mkdirSync(agentsDir, { recursive: true });
3899
+ try {
3900
+ const stat = fs.lstatSync(linkPath);
3901
+ if (stat.isSymbolicLink()) {
3902
+ if (fs.readlinkSync(linkPath) === target) return;
3903
+ fs.unlinkSync(linkPath);
3904
+ } else {
3905
+ copyScaffoldGuidanceSkills(sharedSkillsDir, linkPath);
3906
+ return;
3907
+ }
3908
+ } catch {}
3909
+
3910
+ try {
3911
+ fs.symlinkSync(
3912
+ target,
3913
+ linkPath,
3914
+ process.platform === "win32" ? "junction" : "dir",
3915
+ );
3916
+ } catch {
3917
+ try {
3918
+ fs.cpSync(sharedSkillsDir, linkPath, { recursive: true });
3919
+ } catch {}
3920
+ }
3921
+ }
3922
+
3923
+ function refreshCopiedClaudeSkills(projectRoot: string): void {
3924
+ const agentsSkillsDir = path.join(projectRoot, ".agents", "skills");
3925
+ const claudeSkillsDir = path.join(projectRoot, ".claude", "skills");
3926
+ if (!fs.existsSync(agentsSkillsDir) || !fs.existsSync(claudeSkillsDir)) {
3927
+ return;
3928
+ }
3929
+ try {
3930
+ if (fs.lstatSync(claudeSkillsDir).isSymbolicLink()) return;
3931
+ copyScaffoldGuidanceSkills(agentsSkillsDir, claudeSkillsDir);
3932
+ } catch {}
3933
+ }
3934
+
3935
+ function repairScaffoldAgentLinks(states: ScaffoldGuidanceState[]): void {
3936
+ const seen = new Set<string>();
3937
+ for (const state of states) {
3938
+ if (state.workspaceRoot && state.sharedPackageDir) {
3939
+ const key = `workspace:${state.workspaceRoot}`;
3940
+ if (seen.has(key)) continue;
3941
+ seen.add(key);
3942
+ ensureWorkspaceRootSkillsLink(
3943
+ state.workspaceRoot,
3944
+ state.sharedPackageDir,
3945
+ );
3946
+ setupAgentSymlinks(state.workspaceRoot);
3947
+ refreshCopiedClaudeSkills(state.workspaceRoot);
3948
+ setupAgentSymlinks(state.sharedPackageDir);
3949
+ refreshCopiedClaudeSkills(state.sharedPackageDir);
3950
+ const appsDir = path.join(state.workspaceRoot, "apps");
3951
+ if (fs.existsSync(appsDir)) {
3952
+ for (const entry of fs.readdirSync(appsDir, { withFileTypes: true })) {
3953
+ if (!entry.isDirectory()) continue;
3954
+ const appDir = path.join(appsDir, entry.name);
3955
+ if (fs.existsSync(path.join(appDir, "package.json"))) {
3956
+ setupAgentSymlinks(appDir);
3957
+ refreshCopiedClaudeSkills(appDir);
3958
+ }
3959
+ }
3960
+ }
3961
+ continue;
3962
+ }
3963
+
3964
+ const key = `standalone:${state.projectRoot}`;
3965
+ if (seen.has(key)) continue;
3966
+ seen.add(key);
3967
+ setupAgentSymlinks(state.projectRoot);
3968
+ refreshCopiedClaudeSkills(state.projectRoot);
3969
+ }
3970
+ }
3971
+
3594
3972
  function targetIdsForStatus(parsed: ParsedSkillsArgs): BuiltInAppSkillId[] {
3973
+ if (isScaffoldGuidanceTarget(parsed.target)) return [];
3595
3974
  if (!parsed.target) {
3596
3975
  return (Object.keys(BUILT_IN_APP_SKILLS) as BuiltInAppSkillId[]).filter(
3597
3976
  (id) => !isLocalOnlyBuiltInSkill(BUILT_IN_APP_SKILLS[id]),
@@ -5423,6 +5802,27 @@ function formatSkillState(state: SkillInstallState): string {
5423
5802
  return `${state.skillName.padEnd(22)} ${status.padEnd(7)} ${state.scope}/${state.client} ${managed}${hashes}\n ${state.path}`;
5424
5803
  }
5425
5804
 
5805
+ function scaffoldStateJson(state: ScaffoldGuidanceState) {
5806
+ return {
5807
+ kind: state.kind,
5808
+ displayName: state.displayName,
5809
+ templateName: state.templateName,
5810
+ path: state.path,
5811
+ sourcePath: state.sourcePath,
5812
+ projectRoot: state.projectRoot,
5813
+ workspaceRoot: state.workspaceRoot,
5814
+ sharedPackageDir: state.sharedPackageDir,
5815
+ skillCount: state.skillCount,
5816
+ status: state.current ? "current" : "stale",
5817
+ managed: true,
5818
+ };
5819
+ }
5820
+
5821
+ function formatScaffoldState(state: ScaffoldGuidanceState): string {
5822
+ const status = state.current ? "current" : "stale";
5823
+ return `${"scaffold".padEnd(22)} ${status.padEnd(7)} project/${state.kind} managed (${state.skillCount} skills)\n ${state.path}`;
5824
+ }
5825
+
5426
5826
  function planModeSummary(mode: PlanInstallMode): string {
5427
5827
  if (mode === "local-files")
5428
5828
  return "Local files - no hosted writes by default";
@@ -5503,25 +5903,46 @@ function runSkillsStatusOrUpdate(
5503
5903
  options: RunSkillsOptions,
5504
5904
  update: boolean,
5505
5905
  ): void {
5506
- const before = collectSkillInstallStates(parsed, options);
5507
- const changed = update ? updateSkillInstallStates(before, parsed.dryRun) : [];
5508
- const after =
5906
+ const skillBefore = collectSkillInstallStates(parsed, options);
5907
+ const scaffoldBefore = collectScaffoldGuidanceStates(parsed, options);
5908
+ const skillChanged = update
5909
+ ? updateSkillInstallStates(skillBefore, parsed.dryRun)
5910
+ : [];
5911
+ const scaffoldChanged = update
5912
+ ? updateScaffoldGuidanceStates(scaffoldBefore, parsed.dryRun)
5913
+ : [];
5914
+ if (update && !parsed.dryRun && scaffoldChanged.length > 0) {
5915
+ repairScaffoldAgentLinks(scaffoldChanged);
5916
+ }
5917
+ const skillAfter =
5509
5918
  update && !parsed.dryRun
5510
5919
  ? collectSkillInstallStates(parsed, options)
5511
- : before;
5920
+ : skillBefore;
5921
+ const scaffoldAfter =
5922
+ update && !parsed.dryRun
5923
+ ? collectScaffoldGuidanceStates(parsed, options)
5924
+ : scaffoldBefore;
5925
+ const beforeCount = skillBefore.length + scaffoldBefore.length;
5926
+ const changedCount = skillChanged.length + scaffoldChanged.length;
5512
5927
 
5513
5928
  if (parsed.printJson) {
5514
- const outputStates = update && !parsed.dryRun ? after : before;
5929
+ const outputSkillStates =
5930
+ update && !parsed.dryRun ? skillAfter : skillBefore;
5931
+ const outputScaffoldStates =
5932
+ update && !parsed.dryRun ? scaffoldAfter : scaffoldBefore;
5515
5933
  process.stdout.write(
5516
5934
  `${JSON.stringify(
5517
5935
  {
5518
5936
  ok: true,
5519
5937
  command: parsed.command,
5520
5938
  dryRun: parsed.dryRun,
5521
- found: before.length,
5522
- stale: outputStates.filter((state) => !state.current).length,
5523
- updated: changed.length,
5524
- skills: outputStates.map(skillStateJson),
5939
+ found: beforeCount,
5940
+ stale:
5941
+ outputSkillStates.filter((state) => !state.current).length +
5942
+ outputScaffoldStates.filter((state) => !state.current).length,
5943
+ updated: changedCount,
5944
+ skills: outputSkillStates.map(skillStateJson),
5945
+ scaffold: outputScaffoldStates.map(scaffoldStateJson),
5525
5946
  },
5526
5947
  null,
5527
5948
  2,
@@ -5530,10 +5951,13 @@ function runSkillsStatusOrUpdate(
5530
5951
  return;
5531
5952
  }
5532
5953
 
5533
- if (before.length === 0) {
5954
+ if (beforeCount === 0) {
5534
5955
  const target = parsed.target ? ` for ${parsed.target}` : "";
5956
+ const hint = isScaffoldGuidanceTarget(parsed.target)
5957
+ ? `Run this from a generated Agent Native app or workspace root.\n`
5958
+ : `Run "npx @agent-native/core@latest skills add ${parsed.target ?? "visual-plan"}" to install one.\n`;
5535
5959
  process.stdout.write(
5536
- `No installed Agent Native skill copies found${target}.\nRun "npx @agent-native/core@latest skills add ${parsed.target ?? "visual-plan"}" to install one.\n`,
5960
+ `No installed Agent Native skill copies found${target}.\n${hint}`,
5537
5961
  );
5538
5962
  return;
5539
5963
  }
@@ -5541,20 +5965,27 @@ function runSkillsStatusOrUpdate(
5541
5965
  if (update) {
5542
5966
  if (parsed.dryRun) {
5543
5967
  process.stdout.write(
5544
- changed.length
5545
- ? `Would update ${changed.length} skill folder${changed.length === 1 ? "" : "s"}:\n`
5968
+ changedCount
5969
+ ? `Would update ${changedCount} skill folder${changedCount === 1 ? "" : "s"}:\n`
5546
5970
  : "All discovered skill folders are already current.\n",
5547
5971
  );
5548
5972
  } else {
5549
5973
  process.stdout.write(
5550
- changed.length
5551
- ? `Updated ${changed.length} skill folder${changed.length === 1 ? "" : "s"}.\n`
5974
+ changedCount
5975
+ ? `Updated ${changedCount} skill folder${changedCount === 1 ? "" : "s"}.\n`
5552
5976
  : "All discovered skill folders are already current.\n",
5553
5977
  );
5554
5978
  }
5555
5979
  }
5556
5980
 
5557
- const rows = (update && parsed.dryRun ? before : after).map(formatSkillState);
5981
+ const rows = [
5982
+ ...(update && parsed.dryRun ? skillBefore : skillAfter).map(
5983
+ formatSkillState,
5984
+ ),
5985
+ ...(update && parsed.dryRun ? scaffoldBefore : scaffoldAfter).map(
5986
+ formatScaffoldState,
5987
+ ),
5988
+ ];
5558
5989
  process.stdout.write(`${rows.join("\n")}\n`);
5559
5990
  }
5560
5991
 
@@ -7,6 +7,9 @@ import { cn } from "./utils.js";
7
7
  import { agentNativePath } from "./api-path.js";
8
8
 
9
9
  const DESKTOP_DOWNLOAD_URL = "https://www.agent-native.com/download";
10
+ const CODE_CHANGE_FALLBACK_DETAIL =
11
+ "Edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way you like.";
12
+ const CODE_CHANGE_FALLBACK_TEXT = `This requires a code change. ${CODE_CHANGE_FALLBACK_DETAIL}`;
10
13
 
11
14
  function isLocalBrowserOutsideDesktop() {
12
15
  if (typeof window === "undefined" || typeof navigator === "undefined") {
@@ -200,7 +203,7 @@ export function ConnectBuilderCard({
200
203
  // so the render tree below stays flat.
201
204
  const connectedCapabilityText = builderEnabled
202
205
  ? "AI credits and cloud code changes are ready to use."
203
- : "AI credits are ready to use. Builder Cloud Agents for code changes are not available for this workspace yet.";
206
+ : `AI credits are ready to use. ${CODE_CHANGE_FALLBACK_TEXT}`;
204
207
  let title: string;
205
208
  let subtitle: React.ReactNode;
206
209
  if (runResult) {
@@ -215,20 +218,17 @@ export function ConnectBuilderCard({
215
218
  </>
216
219
  );
217
220
  } else if (showWaitlist) {
218
- title = waitlistJoined
219
- ? "You're on the waitlist"
220
- : "Builder Cloud Agents coming soon";
221
+ title = "This requires a code change";
221
222
  subtitle = waitlistJoined ? (
222
223
  <>
223
- We'll let you know when Builder Cloud Agents are available for this
224
- workspace.{" "}
224
+ You're on the waitlist. {CODE_CHANGE_FALLBACK_DETAIL}{" "}
225
225
  {localBrowser
226
226
  ? "Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone."
227
227
  : "You can still clone the project locally and use the desktop app for code changes."}
228
228
  </>
229
229
  ) : (
230
230
  <>
231
- You don't have access to Builder Cloud Agents for this workspace yet.{" "}
231
+ {CODE_CHANGE_FALLBACK_DETAIL}{" "}
232
232
  {localBrowser
233
233
  ? "Since this project is already running locally, open it in the desktop app for local coding tools or keep editing from your clone."
234
234
  : "You can still clone the project locally and use the desktop app for code changes."}
@@ -294,7 +294,7 @@ export function NewWorkspaceAppFlow({
294
294
  } else {
295
295
  setStatusMessage(
296
296
  result?.message ||
297
- "Builder app creation is coming soon here. Open this workspace in Builder to create an app from this prompt.",
297
+ "This requires a code change. Edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way you like.",
298
298
  );
299
299
  }
300
300
  }