@agent-native/core 0.72.4 → 0.73.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 (91) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +28 -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 +25 -12
  7. package/corpus/core/src/cli/create.ts +1 -1
  8. package/corpus/core/src/cli/skills.ts +450 -19
  9. package/corpus/core/src/server/auth.ts +7 -0
  10. package/corpus/core/src/server/onboarding-html.ts +95 -9
  11. package/corpus/core/src/templates/default/AGENTS.md +4 -0
  12. package/corpus/core/src/templates/default/package.json +2 -1
  13. package/corpus/core/src/templates/headless/AGENTS.md +4 -0
  14. package/corpus/core/src/templates/headless/package.json +1 -0
  15. package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
  16. package/corpus/core/src/templates/workspace-root/README.md +4 -0
  17. package/corpus/core/src/templates/workspace-root/package.json +1 -0
  18. package/corpus/templates/analytics/actions/update-dashboard.ts +3 -4
  19. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +144 -40
  20. package/corpus/templates/analytics/app/global.css +15 -13
  21. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +9 -32
  22. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +0 -26
  23. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +0 -23
  24. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/pivot.ts +65 -2
  25. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +3 -4
  26. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-charts-now-fill-sparse-template-time-series-suppor.md +6 -0
  27. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-rows-now-auto-fit-panels-so-one-two-or-three-chart.md +6 -0
  28. package/corpus/templates/analytics/changelog/2026-06-24-dashboard-traffic-tables-now-show-top-urls-and-shared-clips-.md +6 -0
  29. package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +5 -0
  30. package/corpus/templates/analytics/changelog/2026-06-24-first-party-template-dashboards-now-include-repeat-users-ret.md +6 -0
  31. package/corpus/templates/analytics/changelog/2026-06-24-retention-charts-now-break-out-1-day-and-7-day-return-rates-.md +6 -0
  32. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +256 -44
  33. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +7 -2
  34. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +35 -8
  35. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +287 -42
  36. package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +1 -1
  37. package/corpus/templates/calendar/server/plugins/auth.ts +12 -0
  38. package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -7
  39. package/corpus/templates/clips/AGENTS.md +6 -2
  40. package/corpus/templates/clips/app/components/capture-install-options.tsx +20 -4
  41. package/corpus/templates/clips/app/components/player/player-controls.tsx +62 -32
  42. package/corpus/templates/clips/app/lib/capture-install-options.ts +39 -5
  43. package/corpus/templates/clips/app/routes/download.tsx +2 -2
  44. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
  45. package/corpus/templates/clips/changelog/2026-06-24-inline-slack-playback-controls-no-longer-shift-when-adjustin.md +6 -0
  46. package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-console-log-stream-all-le.md +6 -0
  47. package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-fetch-xhr-network-request.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-06-24-the-chrome-extension-option-now-only-appears-on-supported-hosts.md +5 -0
  49. package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +13 -8
  50. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  51. package/corpus/templates/clips/chrome-extension/scripts/package.ts +15 -2
  52. package/corpus/templates/clips/chrome-extension/src/overlay.css +4 -25
  53. package/corpus/templates/clips/chrome-extension/src/overlay.ts +1 -3
  54. package/corpus/templates/clips/server/lib/public-agent-context.ts +67 -24
  55. package/corpus/templates/mail/server/plugins/auth.ts +4 -1
  56. package/corpus/templates/plan/app/pages/PlansPage.tsx +271 -197
  57. package/corpus/templates/plan/server/plan-content.ts +45 -2
  58. package/dist/agent/durable-background.d.ts +9 -4
  59. package/dist/agent/durable-background.d.ts.map +1 -1
  60. package/dist/agent/durable-background.js +24 -11
  61. package/dist/agent/durable-background.js.map +1 -1
  62. package/dist/cli/create.js +1 -1
  63. package/dist/cli/create.js.map +1 -1
  64. package/dist/cli/skills.d.ts.map +1 -1
  65. package/dist/cli/skills.js +361 -19
  66. package/dist/cli/skills.js.map +1 -1
  67. package/dist/server/auth.d.ts +7 -0
  68. package/dist/server/auth.d.ts.map +1 -1
  69. package/dist/server/auth.js +1 -0
  70. package/dist/server/auth.js.map +1 -1
  71. package/dist/server/onboarding-html.d.ts +15 -0
  72. package/dist/server/onboarding-html.d.ts.map +1 -1
  73. package/dist/server/onboarding-html.js +73 -9
  74. package/dist/server/onboarding-html.js.map +1 -1
  75. package/dist/templates/default/AGENTS.md +4 -0
  76. package/dist/templates/default/package.json +2 -1
  77. package/dist/templates/headless/AGENTS.md +4 -0
  78. package/dist/templates/headless/package.json +1 -0
  79. package/dist/templates/workspace-root/AGENTS.md +4 -0
  80. package/dist/templates/workspace-root/README.md +4 -0
  81. package/dist/templates/workspace-root/package.json +1 -0
  82. package/docs/content/skills-guide.md +14 -0
  83. package/docs/content/template-clips.md +7 -1
  84. package/package.json +1 -1
  85. package/src/templates/default/AGENTS.md +4 -0
  86. package/src/templates/default/package.json +2 -1
  87. package/src/templates/headless/AGENTS.md +4 -0
  88. package/src/templates/headless/package.json +1 -0
  89. package/src/templates/workspace-root/AGENTS.md +4 -0
  90. package/src/templates/workspace-root/README.md +4 -0
  91. 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
 
@@ -262,6 +262,12 @@ export interface AuthOptions {
262
262
  continueLabel?: string;
263
263
  cancelLabel?: string;
264
264
  };
265
+ /**
266
+ * Optional email signup legal copy for the built-in login page.
267
+ * Leave unset to use Agent Native links only on `*.agent-native.com` hosts,
268
+ * pass false to suppress, or pass URLs for custom/self-hosted policies.
269
+ */
270
+ signupLegalNotice?: OnboardingHtmlOptions["signupLegalNotice"];
265
271
  /**
266
272
  * Google sign-in flow: `'popup'`, `'redirect'`, or `'auto'` (default).
267
273
  *
@@ -1013,6 +1019,7 @@ function getOnboardingHtmlOptions(
1013
1019
  googleOnly: options.googleOnly,
1014
1020
  marketing: options.marketing,
1015
1021
  googleSignInNotice: options.googleSignInNotice,
1022
+ signupLegalNotice: options.signupLegalNotice,
1016
1023
  googleAuthMode: options.googleAuthMode,
1017
1024
  requestHost: event ? getRequestHost(event) : undefined,
1018
1025
  requestPath: rawPath,