@cogito.ai/cli 0.3.4 → 0.4.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 (70) hide show
  1. package/dist/index.js +77 -6
  2. package/dist/templates/web-nextjs/apps/docs/.source/browser.ts +1 -1
  3. package/dist/templates/web-nextjs/apps/docs/.source/server.ts +3 -2
  4. package/dist/templates/web-nextjs/apps/docs/content/docs/decisions/meta.json +1 -1
  5. package/dist/templates/web-nextjs/apps/docs/content/docs/decisions/turbo-package-manager.mdx +70 -0
  6. package/dist/templates/web-nextjs/apps/docs/package.json +1 -1
  7. package/dist/templates/web-nextjs/apps/web/middleware.ts +6 -13
  8. package/dist/templates/web-nextjs/apps/web/package.json +26 -0
  9. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/login/page.tsx +87 -93
  10. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/signup/page.tsx +116 -98
  11. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/dashboard/page.tsx +38 -29
  12. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/layout.tsx +2 -5
  13. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/layout.tsx +11 -8
  14. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/privacy/page.tsx +23 -0
  15. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/terms/page.tsx +23 -0
  16. package/dist/templates/web-nextjs/apps/web/src/app/route.ts +13 -0
  17. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/app-sidebar.tsx +188 -0
  18. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/chart-area-interactive.tsx +291 -0
  19. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/data-table.tsx +807 -0
  20. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/data.json +614 -0
  21. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-documents.tsx +92 -0
  22. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-main.tsx +58 -0
  23. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-secondary.tsx +42 -0
  24. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-user.tsx +118 -0
  25. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/page.tsx +40 -0
  26. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/section-cards.tsx +102 -0
  27. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/site-header.tsx +30 -0
  28. package/dist/templates/web-nextjs/apps/web/src/components/ui/alert-dialog.tsx +196 -0
  29. package/dist/templates/web-nextjs/apps/web/src/components/ui/avatar.tsx +109 -0
  30. package/dist/templates/web-nextjs/apps/web/src/components/ui/badge.tsx +48 -0
  31. package/dist/templates/web-nextjs/apps/web/src/components/ui/breadcrumb.tsx +109 -0
  32. package/dist/templates/web-nextjs/apps/web/src/components/ui/button.tsx +14 -2
  33. package/dist/templates/web-nextjs/apps/web/src/components/ui/card.tsx +92 -0
  34. package/dist/templates/web-nextjs/apps/web/src/components/ui/chart.tsx +374 -0
  35. package/dist/templates/web-nextjs/apps/web/src/components/ui/checkbox.tsx +32 -0
  36. package/dist/templates/web-nextjs/apps/web/src/components/ui/collapsible.tsx +33 -0
  37. package/dist/templates/web-nextjs/apps/web/src/components/ui/command.tsx +184 -0
  38. package/dist/templates/web-nextjs/apps/web/src/components/ui/dialog.tsx +158 -0
  39. package/dist/templates/web-nextjs/apps/web/src/components/ui/drawer.tsx +135 -0
  40. package/dist/templates/web-nextjs/apps/web/src/components/ui/dropdown-menu.tsx +257 -0
  41. package/dist/templates/web-nextjs/apps/web/src/components/ui/field.tsx +248 -0
  42. package/dist/templates/web-nextjs/apps/web/src/components/ui/input-otp.tsx +77 -0
  43. package/dist/templates/web-nextjs/apps/web/src/components/ui/pagination.tsx +127 -0
  44. package/dist/templates/web-nextjs/apps/web/src/components/ui/popover.tsx +89 -0
  45. package/dist/templates/web-nextjs/apps/web/src/components/ui/progress.tsx +31 -0
  46. package/dist/templates/web-nextjs/apps/web/src/components/ui/radio-group.tsx +45 -0
  47. package/dist/templates/web-nextjs/apps/web/src/components/ui/scroll-area.tsx +58 -0
  48. package/dist/templates/web-nextjs/apps/web/src/components/ui/select.tsx +190 -0
  49. package/dist/templates/web-nextjs/apps/web/src/components/ui/separator.tsx +29 -0
  50. package/dist/templates/web-nextjs/apps/web/src/components/ui/sheet.tsx +143 -0
  51. package/dist/templates/web-nextjs/apps/web/src/components/ui/sidebar.tsx +726 -0
  52. package/dist/templates/web-nextjs/apps/web/src/components/ui/skeleton.tsx +13 -0
  53. package/dist/templates/web-nextjs/apps/web/src/components/ui/slider.tsx +63 -0
  54. package/dist/templates/web-nextjs/apps/web/src/components/ui/sonner.tsx +11 -25
  55. package/dist/templates/web-nextjs/apps/web/src/components/ui/switch.tsx +35 -0
  56. package/dist/templates/web-nextjs/apps/web/src/components/ui/table.tsx +116 -0
  57. package/dist/templates/web-nextjs/apps/web/src/components/ui/tabs.tsx +91 -0
  58. package/dist/templates/web-nextjs/apps/web/src/components/ui/textarea.tsx +18 -0
  59. package/dist/templates/web-nextjs/apps/web/src/components/ui/toggle-group.tsx +83 -0
  60. package/dist/templates/web-nextjs/apps/web/src/components/ui/toggle.tsx +47 -0
  61. package/dist/templates/web-nextjs/apps/web/src/components/ui/tooltip.tsx +57 -0
  62. package/dist/templates/web-nextjs/apps/web/src/features/auth/server.ts +15 -0
  63. package/dist/templates/web-nextjs/apps/web/src/hooks/use-mobile.ts +21 -0
  64. package/dist/templates/web-nextjs/package.json +10 -1
  65. package/dist/templates/web-nextjs/packages/openspec-docs-sync/package.json +1 -3
  66. package/dist/templates/web-nextjs/pnpm-lock.yaml +1562 -100
  67. package/dist/templates/web-nextjs/pnpm-workspace.yaml +15 -0
  68. package/dist/templates/web-nextjs/turbo.json +1 -0
  69. package/package.json +2 -2
  70. /package/dist/templates/web-nextjs/{.env.example → apps/web/.env.example} +0 -0
package/dist/index.js CHANGED
@@ -863,6 +863,55 @@ function checkVersion(cliVersion, minCliVersion, templateId) {
863
863
  }
864
864
  }
865
865
 
866
+ // package.json
867
+ var package_default;
868
+ var init_package = __esm(() => {
869
+ package_default = {
870
+ name: "@cogito.ai/cli",
871
+ version: "0.4.0",
872
+ type: "module",
873
+ description: "AgentDock CLI – scaffold projects for humans and AI agents",
874
+ publishConfig: {
875
+ access: "public"
876
+ },
877
+ bin: {
878
+ agentdock: "./dist/index.js"
879
+ },
880
+ main: "./dist/index.js",
881
+ files: [
882
+ "dist/"
883
+ ],
884
+ engines: {
885
+ node: ">=18"
886
+ },
887
+ scripts: {
888
+ build: "pnpm run generate-registry && bun build bin/agentdock.ts --outfile dist/index.js --target node && cp src/registry.json dist/registry.json && rm -rf dist/templates && mkdir -p dist/templates && rsync -a --exclude='node_modules/' --exclude='.next/' --exclude='.turbo/' ../../templates/ dist/templates/",
889
+ "generate-registry": "tsx ../../scripts/generate-registry/index.ts",
890
+ "check-types": "tsc --noEmit",
891
+ test: "vitest run"
892
+ },
893
+ dependencies: {
894
+ citty: "^0.1.6",
895
+ "@clack/prompts": "^0.9.1",
896
+ "@modelcontextprotocol/sdk": "^1.0.0",
897
+ giget: "^1.2.4"
898
+ },
899
+ devDependencies: {
900
+ "@cogito.ai/tsconfig": "workspace:*",
901
+ "@types/node": "^20.0.0",
902
+ bun: "latest",
903
+ vitest: "^4.1.8"
904
+ }
905
+ };
906
+ });
907
+
908
+ // src/version.ts
909
+ var VERSION;
910
+ var init_version = __esm(() => {
911
+ init_package();
912
+ VERSION = package_default.version;
913
+ });
914
+
866
915
  // src/core/scaffold.ts
867
916
  import {
868
917
  cpSync,
@@ -874,6 +923,7 @@ import {
874
923
  } from "fs";
875
924
  import { join as join2, dirname as dirname2 } from "path";
876
925
  import { fileURLToPath as fileURLToPath2 } from "url";
926
+ import { execSync } from "child_process";
877
927
  function getTemplateSourceDir(templateSource) {
878
928
  const runtimeDir = dirname2(fileURLToPath2(import.meta.url));
879
929
  const candidates = [
@@ -894,6 +944,7 @@ function rewritePackageJson(pkgJsonPath, name, resolvedDependencies) {
894
944
  pkg["version"] = "0.1.0";
895
945
  delete pkg["private"];
896
946
  delete pkg["agentdock"];
947
+ delete pkg["packageManager"];
897
948
  for (const key of ["dependencies", "devDependencies", "peerDependencies"]) {
898
949
  const deps = pkg[key];
899
950
  if (!deps)
@@ -907,10 +958,27 @@ function rewritePackageJson(pkgJsonPath, name, resolvedDependencies) {
907
958
  writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + `
908
959
  `, "utf-8");
909
960
  }
961
+ function injectPackageManager(pkgJsonPath) {
962
+ if (!existsSync2(pkgJsonPath))
963
+ return;
964
+ try {
965
+ const pnpmVersion = execSync("pnpm --version", {
966
+ encoding: "utf-8",
967
+ stdio: ["ignore", "pipe", "ignore"]
968
+ }).trim();
969
+ if (!pnpmVersion)
970
+ return;
971
+ const raw = readFileSync2(pkgJsonPath, "utf-8");
972
+ const pkg = JSON.parse(raw);
973
+ pkg["packageManager"] = `pnpm@${pnpmVersion}`;
974
+ writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + `
975
+ `, "utf-8");
976
+ } catch {}
977
+ }
910
978
  function scaffoldProject(options) {
911
979
  const { targetDir, name, template, packageManager: _pm } = options;
912
980
  try {
913
- checkVersion(CLI_VERSION, template.minCliVersion, template.id);
981
+ checkVersion(VERSION, template.minCliVersion, template.id);
914
982
  } catch (err) {
915
983
  return {
916
984
  ok: false,
@@ -933,6 +1001,7 @@ function scaffoldProject(options) {
933
1001
  if (existsSync2(pkgJsonPath)) {
934
1002
  rewritePackageJson(pkgJsonPath, name, template.resolvedDependencies);
935
1003
  }
1004
+ injectPackageManager(pkgJsonPath);
936
1005
  return {
937
1006
  ok: true,
938
1007
  targetDir,
@@ -947,8 +1016,9 @@ function scaffoldProject(options) {
947
1016
  };
948
1017
  }
949
1018
  }
950
- var CLI_VERSION = "0.1.0";
951
- var init_scaffold = () => {};
1019
+ var init_scaffold = __esm(() => {
1020
+ init_version();
1021
+ });
952
1022
 
953
1023
  // src/adapters/agent.ts
954
1024
  var exports_agent = {};
@@ -16316,7 +16386,7 @@ __export(exports_server, {
16316
16386
  startMcpServer: () => startMcpServer
16317
16387
  });
16318
16388
  async function startMcpServer() {
16319
- const server = new Server({ name: "agentdock", version: PKG_VERSION }, {
16389
+ const server = new Server({ name: "agentdock", version: VERSION }, {
16320
16390
  capabilities: {
16321
16391
  tools: {}
16322
16392
  }
@@ -16380,12 +16450,12 @@ async function startMcpServer() {
16380
16450
  process.exit(0);
16381
16451
  });
16382
16452
  }
16383
- var PKG_VERSION = "0.1.0";
16384
16453
  var init_server3 = __esm(() => {
16385
16454
  init_server2();
16386
16455
  init_stdio2();
16387
16456
  init_types();
16388
16457
  init_tools();
16458
+ init_version();
16389
16459
  });
16390
16460
 
16391
16461
  // ../../node_modules/.pnpm/consola@3.4.2/node_modules/consola/dist/core.mjs
@@ -17854,11 +17924,12 @@ var mcpCommand = defineCommand({
17854
17924
  });
17855
17925
 
17856
17926
  // src/main.ts
17927
+ init_version();
17857
17928
  var main = defineCommand({
17858
17929
  meta: {
17859
17930
  name: "agentdock",
17860
17931
  description: "AgentDock CLI – scaffold projects for humans and AI agents",
17861
- version: "0.1.0"
17932
+ version: VERSION
17862
17933
  },
17863
17934
  subCommands: {
17864
17935
  init: initCommand,
@@ -7,6 +7,6 @@ const create = browser<typeof Config, import("fumadocs-mdx/runtime/types").Inter
7
7
  }
8
8
  }>();
9
9
  const browserCollections = {
10
- docs: create.doc("docs", {"index.mdx": () => import("../content/docs/index.mdx?collection=docs"), "changelog/index.mdx": () => import("../content/docs/changelog/index.mdx?collection=docs"), "roadmap/index.mdx": () => import("../content/docs/roadmap/index.mdx?collection=docs"), "features/auth.mdx": () => import("../content/docs/features/auth.mdx?collection=docs"), "features/hello.mdx": () => import("../content/docs/features/hello.mdx?collection=docs"), }),
10
+ docs: create.doc("docs", {"index.mdx": () => import("../content/docs/index.mdx?collection=docs"), "changelog/index.mdx": () => import("../content/docs/changelog/index.mdx?collection=docs"), "decisions/turbo-package-manager.mdx": () => import("../content/docs/decisions/turbo-package-manager.mdx?collection=docs"), "features/auth.mdx": () => import("../content/docs/features/auth.mdx?collection=docs"), "features/hello.mdx": () => import("../content/docs/features/hello.mdx?collection=docs"), "roadmap/index.mdx": () => import("../content/docs/roadmap/index.mdx?collection=docs"), }),
11
11
  };
12
12
  export default browserCollections;
@@ -1,7 +1,8 @@
1
1
  // @ts-nocheck
2
+ import * as __fd_glob_10 from "../content/docs/roadmap/index.mdx?collection=docs"
2
3
  import * as __fd_glob_9 from "../content/docs/features/hello.mdx?collection=docs"
3
4
  import * as __fd_glob_8 from "../content/docs/features/auth.mdx?collection=docs"
4
- import * as __fd_glob_7 from "../content/docs/roadmap/index.mdx?collection=docs"
5
+ import * as __fd_glob_7 from "../content/docs/decisions/turbo-package-manager.mdx?collection=docs"
5
6
  import * as __fd_glob_6 from "../content/docs/changelog/index.mdx?collection=docs"
6
7
  import * as __fd_glob_5 from "../content/docs/index.mdx?collection=docs"
7
8
  import { default as __fd_glob_4 } from "../content/docs/roadmap/meta.json?collection=docs"
@@ -17,4 +18,4 @@ const create = server<typeof Config, import("fumadocs-mdx/runtime/types").Intern
17
18
  }
18
19
  }>({"doc":{"passthroughs":["extractedReferences"]}});
19
20
 
20
- export const docs = await create.docs("docs", "content/docs", {"meta.json": __fd_glob_0, "changelog/meta.json": __fd_glob_1, "decisions/meta.json": __fd_glob_2, "features/meta.json": __fd_glob_3, "roadmap/meta.json": __fd_glob_4, }, {"index.mdx": __fd_glob_5, "changelog/index.mdx": __fd_glob_6, "roadmap/index.mdx": __fd_glob_7, "features/auth.mdx": __fd_glob_8, "features/hello.mdx": __fd_glob_9, });
21
+ export const docs = await create.docs("docs", "content/docs", {"meta.json": __fd_glob_0, "changelog/meta.json": __fd_glob_1, "decisions/meta.json": __fd_glob_2, "features/meta.json": __fd_glob_3, "roadmap/meta.json": __fd_glob_4, }, {"index.mdx": __fd_glob_5, "changelog/index.mdx": __fd_glob_6, "decisions/turbo-package-manager.mdx": __fd_glob_7, "features/auth.mdx": __fd_glob_8, "features/hello.mdx": __fd_glob_9, "roadmap/index.mdx": __fd_glob_10, });
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "title": "Decisions",
3
- "pages": []
3
+ "pages": ["turbo-package-manager"]
4
4
  }
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: "Turbo & Package Manager"
3
+ description: "Why dangerouslyDisablePackageManagerCheck exists, its tradeoffs, and how to properly configure packageManager for your team."
4
+ ---
5
+
6
+ # Turborepo & Package Manager Configuration
7
+
8
+ ## Why `dangerouslyDisablePackageManagerCheck` is enabled
9
+
10
+ This template ships with `dangerouslyDisablePackageManagerCheck: true` in `turbo.json` to avoid an out-of-the-box failure:
11
+
12
+ ```json
13
+ // turbo.json
14
+ {
15
+ "dangerouslyDisablePackageManagerCheck": true
16
+ }
17
+ ```
18
+
19
+ **Root cause:** Turborepo 2.0+ requires the `packageManager` field in `package.json` to identify your package manager and version. Without it, Turbo refuses to run:
20
+
21
+ ```
22
+ × Could not resolve workspaces.
23
+ ╰─▶ Missing `packageManager` field in package.json
24
+ ```
25
+
26
+ Since users may have different pnpm versions installed, the template cannot hard-code a specific version without risking the same `Corepack PATH` error that plagues a pinned `packageManager` field. `dangerouslyDisablePackageManagerCheck` lets Turbo skip this validation so the template works on first `pnpm dev`.
27
+
28
+ ## What `dangerouslyDisablePackageManagerCheck` actually disables
29
+
30
+ | What Turbo normally does | What is skipped |
31
+ | --- | --- |
32
+ | Reads `packageManager` to identify which lockfile to hash | Cache hashing falls back to a less-precise strategy |
33
+ | Validates that the running package manager matches the declared one | No mismatch detection (could build with wrong PM) |
34
+ | Optimizes workspace discovery per package manager | Slightly slower workspace graph resolution |
35
+
36
+ **In practice:** for most single-developer or small team projects, the only noticeable difference is that Turbo cache keys are slightly less precise — you might get a cache miss that would otherwise be a hit. This is a minor inconvenience, not a correctness issue.
37
+
38
+ ## How to remove the flag (recommended for team projects)
39
+
40
+ **Step 1 — Find your installed pnpm version:**
41
+
42
+ ```bash
43
+ pnpm --version
44
+ # e.g. 11.5.1
45
+ ```
46
+
47
+ **Step 2 — Add `packageManager` to your root `package.json`:**
48
+
49
+ ```json
50
+ {
51
+ "packageManager": "pnpm@11.5.1"
52
+ }
53
+ ```
54
+
55
+ **Step 3 — Remove `dangerouslyDisablePackageManagerCheck` from `turbo.json`:**
56
+
57
+ ```json
58
+ {
59
+ "$schema": "https://turbo.build/schema.json",
60
+ "tasks": {
61
+ ...
62
+ }
63
+ }
64
+ ```
65
+
66
+ **Step 4 — Commit both files.** All developers cloning the repo will now use the same pnpm version enforced by Corepack, and Turbo's cache hashing is fully accurate.
67
+
68
+ ## Automatic injection (CLI 0.3.6+)
69
+
70
+ Starting with `@cogito.ai/cli@0.3.6`, the `agentdock init` command automatically detects your installed pnpm version and injects `packageManager: "pnpm@X.Y.Z"` into the scaffolded project's root `package.json`. This means new projects are created with the correct field pre-filled, and you can immediately remove `dangerouslyDisablePackageManagerCheck` from `turbo.json` after scaffolding.
@@ -8,7 +8,7 @@
8
8
  "start": "next start",
9
9
  "lint": "eslint .",
10
10
  "check-types": "tsc --noEmit",
11
- "docs:sync": "openspec-docs-sync",
11
+ "docs:sync": "pnpm --filter @cogito.ai/openspec-docs-sync run build && node ../../packages/openspec-docs-sync/dist/index.js",
12
12
  "postinstall": "fumadocs-mdx"
13
13
  },
14
14
  "dependencies": {
@@ -20,20 +20,13 @@ function copyCookies(from: NextResponse, to: NextResponse) {
20
20
  export default async function middleware(request: NextRequest) {
21
21
  const pathname = request.nextUrl.pathname
22
22
 
23
- if (pathname === '/') {
24
- const url = request.nextUrl.clone()
25
- url.pathname = `/${defaultLocale}`
26
- return NextResponse.redirect(url)
27
- }
28
-
29
- // Skip Supabase session refresh for the auth callback route
30
- // (it's handled by the route handler itself)
23
+ // Auth callback is handled by its own route handler — skip all middleware processing
31
24
  if (pathname.startsWith('/auth/')) {
32
25
  return NextResponse.next()
33
26
  }
34
27
 
35
- // 1. Let next-intl build the final response first.
36
- // Supabase cookie refresh should write into this response to avoid cookie loss.
28
+ // 1. Let next-intl handle locale routing.
29
+ // With localePrefix: 'always' (default), this also redirects / /${defaultLocale}.
37
30
  const response = handleI18nRouting(request)
38
31
 
39
32
  // 2. Refresh the Supabase session (keeps token alive, writes updated cookie)
@@ -54,7 +47,7 @@ export default async function middleware(request: NextRequest) {
54
47
  return redirectResponse
55
48
  }
56
49
 
57
- // 4. Normalize unknown locale-like prefixes: /fr/hello -> /en/hello
50
+ // 4. Normalize unknown locale-like prefixes: /fr/hello /zh/hello
58
51
  const segments = pathname.split('/').filter(Boolean)
59
52
  const firstSegment = segments[0]
60
53
 
@@ -74,6 +67,6 @@ export default async function middleware(request: NextRequest) {
74
67
 
75
68
  export const config = {
76
69
  // Match all pathnames except Next.js internals, static files, and auth callback.
77
- // The explicit '/' entry ensures the root redirect to the default locale always fires.
78
- matcher: ['/', '/((?!_next|_vercel|auth/callback|.*\\..*).*)'],
70
+ // The regex also matches '/' so next-intl handles the root /${defaultLocale} redirect.
71
+ matcher: ['/((?!_next|_vercel|auth/callback|.*\\..*).*)'],
79
72
  }
@@ -12,21 +12,47 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@base-ui/react": "^1.5.0",
15
+ "@dnd-kit/core": "^6.3.1",
16
+ "@dnd-kit/modifiers": "^9.0.0",
17
+ "@dnd-kit/sortable": "^10.0.0",
18
+ "@dnd-kit/utilities": "^3.2.2",
15
19
  "@hookform/resolvers": "^5.4.0",
20
+ "@radix-ui/react-avatar": "^1.1.11",
21
+ "@radix-ui/react-checkbox": "^1.3.3",
22
+ "@radix-ui/react-collapsible": "^1.1.12",
23
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
24
+ "@radix-ui/react-progress": "^1.1.8",
25
+ "@radix-ui/react-radio-group": "^1.3.8",
26
+ "@radix-ui/react-scroll-area": "^1.2.10",
27
+ "@radix-ui/react-select": "^2.2.6",
28
+ "@radix-ui/react-separator": "^1.1.8",
29
+ "@radix-ui/react-slider": "^1.3.6",
30
+ "@radix-ui/react-slot": "^1.2.4",
31
+ "@radix-ui/react-switch": "^1.2.6",
32
+ "@radix-ui/react-tabs": "^1.1.13",
33
+ "@radix-ui/react-toggle": "^1.1.10",
34
+ "@radix-ui/react-toggle-group": "^1.1.11",
16
35
  "@supabase/ssr": "^0.10.3",
36
+ "@tabler/icons-react": "^3.44.0",
37
+ "@tanstack/react-table": "^8.21.3",
17
38
  "class-variance-authority": "^0.7.1",
18
39
  "clsx": "^2.1.1",
40
+ "cmdk": "^1.1.1",
41
+ "input-otp": "^1.4.2",
19
42
  "lucide-react": "^1.17.0",
20
43
  "next": "16",
21
44
  "next-intl": "^4.13.0",
22
45
  "next-themes": "^0.4.6",
46
+ "radix-ui": "^1.4.3",
23
47
  "react": "19",
24
48
  "react-dom": "19",
25
49
  "react-hook-form": "^7.77.0",
50
+ "recharts": "^3.8.1",
26
51
  "shadcn": "^4.10.0",
27
52
  "sonner": "^2.0.7",
28
53
  "tailwind-merge": "^3.6.0",
29
54
  "tw-animate-css": "^1.4.0",
55
+ "vaul": "^1.1.2",
30
56
  "zod": "^4.4.3"
31
57
  },
32
58
  "devDependencies": {
@@ -6,18 +6,18 @@ import Link from 'next/link'
6
6
  import { useTranslations } from 'next-intl'
7
7
  import { useForm } from 'react-hook-form'
8
8
  import { zodResolver } from '@hookform/resolvers/zod'
9
- import { GitBranch } from 'lucide-react'
9
+ import { GalleryVerticalEnd } from 'lucide-react'
10
10
  import { toast } from 'sonner'
11
11
  import { Button } from '@/components/ui/button'
12
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
12
13
  import { Input } from '@/components/ui/input'
13
14
  import {
14
- Form,
15
- FormControl,
16
- FormField,
17
- FormItem,
18
- FormLabel,
19
- FormMessage,
20
- } from '@/components/ui/form'
15
+ Field,
16
+ FieldDescription,
17
+ FieldGroup,
18
+ FieldLabel,
19
+ FieldSeparator,
20
+ } from '@/components/ui/field'
21
21
  import { signIn, signInWithGithubForLocale } from '@/features/auth'
22
22
  import { signInSchema, type SignInInput } from '@/lib/validations/auth'
23
23
  import type { ActionResult } from '@/core/types/auth'
@@ -28,114 +28,108 @@ export default function LoginPage() {
28
28
  const routeParams = useParams<{ locale: string }>()
29
29
  const locale = routeParams.locale ?? 'en'
30
30
 
31
- const form = useForm<SignInInput>({
31
+ const { register, formState: { errors } } = useForm<SignInInput>({
32
32
  resolver: zodResolver(signInSchema),
33
33
  defaultValues: { email: '', password: '' },
34
34
  })
35
35
 
36
- const [state, formAction, isPending] = useActionState<ActionResult | null, FormData>(
37
- signIn,
38
- null,
39
- )
36
+ const [state, formAction, isPending] = useActionState<ActionResult | null, FormData>(signIn, null)
40
37
 
41
38
  useEffect(() => {
42
- if (state?.error) {
43
- toast.error(state.error)
44
- }
39
+ if (state?.error) toast.error(state.error)
45
40
  }, [state])
46
41
 
47
42
  async function handleGithub() {
48
43
  const result = await signInWithGithubForLocale(locale)
49
- if (result.error) {
50
- toast.error(result.error)
51
- return
52
- }
53
- if (result.data?.url) {
54
- router.push(result.data.url)
55
- }
44
+ if (result.error) { toast.error(result.error); return }
45
+ if (result.data?.url) router.push(result.data.url)
56
46
  }
57
47
 
58
48
  return (
59
- <div className="flex min-h-screen items-center justify-center px-4 py-12">
60
- <div className="w-full max-w-sm space-y-6">
61
- <div className="space-y-2 text-center">
62
- <h1 className="text-3xl font-bold">{t('loginTitle')}</h1>
63
- <p className="text-muted-foreground">{t('loginSubtitle')}</p>
64
- </div>
65
-
66
- <Button
67
- type="button"
68
- variant="outline"
69
- className="w-full"
70
- onClick={handleGithub}
71
- >
72
- <GitBranch className="mr-2 h-4 w-4" />
73
- {t('githubButton')}
74
- </Button>
75
-
76
- <div className="relative">
77
- <div className="absolute inset-0 flex items-center">
78
- <span className="w-full border-t" />
79
- </div>
80
- <div className="relative flex justify-center text-xs uppercase">
81
- <span className="bg-background px-2 text-muted-foreground">{t('orContinueWith')}</span>
49
+ <div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-muted p-6 md:p-10">
50
+ <div className="flex w-full max-w-sm flex-col gap-6">
51
+ <Link href={`/${locale}`} className="flex items-center gap-2 self-center font-medium">
52
+ <div className="flex size-6 items-center justify-center rounded-md bg-primary text-primary-foreground">
53
+ <GalleryVerticalEnd className="size-4" />
82
54
  </div>
83
- </div>
55
+ AgentDock
56
+ </Link>
84
57
 
85
- <Form {...form}>
86
- <form action={formAction} className="space-y-4">
87
- <input type="hidden" name="locale" value={locale} />
88
- <FormField
89
- control={form.control}
90
- name="email"
91
- render={({ field }) => (
92
- <FormItem>
93
- <FormLabel>{t('emailLabel')}</FormLabel>
94
- <FormControl>
58
+ <div className="flex flex-col gap-6">
59
+ <Card>
60
+ <CardHeader className="text-center">
61
+ <CardTitle className="text-xl">{t('loginTitle')}</CardTitle>
62
+ <CardDescription>{t('loginSubtitle')}</CardDescription>
63
+ </CardHeader>
64
+ <CardContent>
65
+ <form action={formAction}>
66
+ <input type="hidden" name="locale" value={locale} />
67
+ <FieldGroup>
68
+ <Field>
69
+ <Button
70
+ type="button"
71
+ variant="outline"
72
+ className="w-full"
73
+ onClick={handleGithub}
74
+ >
75
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" className="size-4">
76
+ <path
77
+ d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
78
+ fill="currentColor"
79
+ />
80
+ </svg>
81
+ {t('githubButton')}
82
+ </Button>
83
+ </Field>
84
+ <FieldSeparator>{t('orContinueWith')}</FieldSeparator>
85
+ <Field>
86
+ <FieldLabel htmlFor="email">{t('emailLabel')}</FieldLabel>
95
87
  <Input
88
+ id="email"
96
89
  type="email"
97
90
  placeholder={t('emailPlaceholder')}
98
- {...field}
91
+ autoComplete="email"
92
+ {...register('email')}
99
93
  />
100
- </FormControl>
101
- <FormMessage />
102
- </FormItem>
103
- )}
104
- />
105
-
106
- <FormField
107
- control={form.control}
108
- name="password"
109
- render={({ field }) => (
110
- <FormItem>
111
- <FormLabel>{t('passwordLabel')}</FormLabel>
112
- <FormControl>
94
+ {errors.email && (
95
+ <FieldDescription className="text-destructive">
96
+ {errors.email.message}
97
+ </FieldDescription>
98
+ )}
99
+ </Field>
100
+ <Field>
101
+ <div className="flex items-center">
102
+ <FieldLabel htmlFor="password">{t('passwordLabel')}</FieldLabel>
103
+ </div>
113
104
  <Input
105
+ id="password"
114
106
  type="password"
115
107
  placeholder={t('passwordPlaceholder')}
116
- {...field}
108
+ autoComplete="current-password"
109
+ {...register('password')}
117
110
  />
118
- </FormControl>
119
- <FormMessage />
120
- </FormItem>
121
- )}
122
- />
123
-
124
- <Button type="submit" className="w-full" disabled={isPending}>
125
- {isPending ? '...' : t('signInButton')}
126
- </Button>
127
- </form>
128
- </Form>
129
-
130
- <p className="text-center text-sm text-muted-foreground">
131
- {t('noAccountText')}{' '}
132
- <Link
133
- href={`/${locale}/signup`}
134
- className="underline underline-offset-4 hover:text-primary"
135
- >
136
- {t('signUpLink')}
137
- </Link>
138
- </p>
111
+ {errors.password && (
112
+ <FieldDescription className="text-destructive">
113
+ {errors.password.message}
114
+ </FieldDescription>
115
+ )}
116
+ </Field>
117
+ <Field>
118
+ <Button type="submit" className="w-full" disabled={isPending}>
119
+ {isPending ? '\u2026' : t('signInButton')}
120
+ </Button>
121
+ <FieldDescription className="text-center">
122
+ {t('noAccountText')}{' '}
123
+ <Link href={`/${locale}/signup`} className="underline underline-offset-4">
124
+ {t('signUpLink')}
125
+ </Link>
126
+ </FieldDescription>
127
+ </Field>
128
+ </FieldGroup>
129
+ </form>
130
+ </CardContent>
131
+ </Card>
132
+ </div>
139
133
  </div>
140
134
  </div>
141
135
  )