minutework 0.1.32 → 0.1.33

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/assets/claude-local/skills/README.md +2 -0
  2. package/assets/claude-local/skills/app-pack-authoring/SKILL.md +14 -1
  3. package/assets/claude-local/skills/contract-first-public-intake/SKILL.md +11 -3
  4. package/assets/claude-local/skills/generated-workspace-architecture/SKILL.md +10 -3
  5. package/assets/claude-local/skills/published-web-and-mw-core-site/SKILL.md +9 -6
  6. package/assets/claude-local/skills/standalone-mobile-client/SKILL.md +5 -4
  7. package/assets/templates/next-tenant-app/README.md +26 -138
  8. package/assets/templates/next-tenant-app/package.json +1 -0
  9. package/assets/templates/next-tenant-app/src/app/app/demo/page.tsx +15 -0
  10. package/assets/templates/next-tenant-app/src/app/app/layout.tsx +1 -4
  11. package/assets/templates/next-tenant-app/src/app/app/page.tsx +2 -17
  12. package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.tsx +9 -67
  13. package/assets/templates/next-tenant-app/src/app/blog/page.tsx +10 -46
  14. package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.tsx +9 -65
  15. package/assets/templates/next-tenant-app/src/app/docs/page.tsx +10 -46
  16. package/assets/templates/next-tenant-app/src/app/layout.tsx +8 -10
  17. package/assets/templates/next-tenant-app/src/app/login/page.tsx +3 -23
  18. package/assets/templates/next-tenant-app/src/app/page.tsx +11 -44
  19. package/assets/templates/next-tenant-app/src/app/pricing/page.tsx +10 -44
  20. package/assets/templates/next-tenant-app/src/app/providers.tsx +2 -1
  21. package/assets/templates/next-tenant-app/src/app/robots.ts +7 -18
  22. package/assets/templates/next-tenant-app/src/app/sitemap.ts +4 -39
  23. package/assets/templates/next-tenant-app/src/features/auth/components/login-screen.tsx +97 -98
  24. package/assets/templates/next-tenant-app/src/features/dashboard/components/tenant-dashboard.tsx +43 -78
  25. package/assets/templates/next-tenant-app/src/features/demo/components/manifest-demo.tsx +89 -0
  26. package/assets/templates/next-tenant-app/src/features/public-shell/components/static-public-page.tsx +58 -0
  27. package/assets/templates/next-tenant-app/src/features/shell/components/private-app-shell.tsx +48 -552
  28. package/assets/templates/next-tenant-app/src/lib/app-routes.ts +2 -2
  29. package/assets/templates/next-tenant-app/src/lib/public-site.ts +5 -30
  30. package/assets/templates/next-tenant-app/src/mw/client.ts +18 -0
  31. package/assets/templates/next-tenant-app/src/mw/mock.test.ts +21 -0
  32. package/assets/templates/next-tenant-app/src/mw/mock.ts +35 -0
  33. package/assets/templates/next-tenant-app/src/mw/provider.tsx +17 -0
  34. package/assets/templates/next-tenant-app/template.json +3 -3
  35. package/assets/templates/next-tenant-app/template.schema.json +1 -0
  36. package/assets/templates/next-tenant-app/tools/template/validate-route-contract.mjs +4 -5
  37. package/package.json +2 -2
  38. package/vendor/workspace-mcp/types.d.ts +4 -0
  39. package/assets/templates/next-tenant-app/src/app/(cms)/[...path]/page.tsx +0 -89
  40. package/assets/templates/next-tenant-app/src/app/api/auth/context/route.test.ts +0 -90
  41. package/assets/templates/next-tenant-app/src/app/api/auth/context/route.ts +0 -78
  42. package/assets/templates/next-tenant-app/src/app/api/auth/login/route.ts +0 -31
  43. package/assets/templates/next-tenant-app/src/app/api/auth/logout/route.ts +0 -16
  44. package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.test.ts +0 -79
  45. package/assets/templates/next-tenant-app/src/app/api/auth/password-change/route.ts +0 -40
  46. package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.test.ts +0 -42
  47. package/assets/templates/next-tenant-app/src/app/api/auth/password-status/route.ts +0 -29
  48. package/assets/templates/next-tenant-app/src/app/api/auth/session/route.ts +0 -26
  49. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.test.ts +0 -40
  50. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/[runId]/route.ts +0 -47
  51. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.test.ts +0 -43
  52. package/assets/templates/next-tenant-app/src/app/api/gateway/commands/route.ts +0 -45
  53. package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.test.ts +0 -83
  54. package/assets/templates/next-tenant-app/src/app/app/examples/runtime-commands/page.tsx +0 -30
  55. package/assets/templates/next-tenant-app/src/app/app/page.test.ts +0 -62
  56. package/assets/templates/next-tenant-app/src/app/app/private-content-source.test.ts +0 -88
  57. package/assets/templates/next-tenant-app/src/app/blog/[slug]/page.test.ts +0 -70
  58. package/assets/templates/next-tenant-app/src/app/blog/page.test.ts +0 -46
  59. package/assets/templates/next-tenant-app/src/app/docs/[...slug]/page.test.ts +0 -70
  60. package/assets/templates/next-tenant-app/src/app/docs/page.test.ts +0 -46
  61. package/assets/templates/next-tenant-app/src/app/login/page.test.ts +0 -55
  62. package/assets/templates/next-tenant-app/src/app/page.test.ts +0 -90
  63. package/assets/templates/next-tenant-app/src/app/pricing/page.test.ts +0 -59
  64. package/assets/templates/next-tenant-app/src/app/robots.test.ts +0 -40
  65. package/assets/templates/next-tenant-app/src/app/sitemap.test.ts +0 -63
  66. package/assets/templates/next-tenant-app/src/features/examples/runtime-command-demo/components/runtime-command-demo.tsx +0 -342
  67. package/assets/templates/next-tenant-app/src/features/public-shell/components/content-article.tsx +0 -66
  68. package/assets/templates/next-tenant-app/src/features/public-shell/components/content-collection.tsx +0 -108
  69. package/assets/templates/next-tenant-app/src/features/public-shell/components/marketing-page-canvas.tsx +0 -111
  70. package/assets/templates/next-tenant-app/src/features/public-shell/components/public-site-shell.tsx +0 -111
  71. package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.test.ts +0 -38
  72. package/assets/templates/next-tenant-app/src/features/public-shell/components/section-renderer.tsx +0 -145
  73. package/assets/templates/next-tenant-app/src/lib/content/__fixtures__/public-site-snapshot.ts +0 -189
  74. package/assets/templates/next-tenant-app/src/lib/content/adapter.server.test.ts +0 -444
  75. package/assets/templates/next-tenant-app/src/lib/content/adapter.server.ts +0 -383
  76. package/assets/templates/next-tenant-app/src/lib/content/contracts.test.ts +0 -138
  77. package/assets/templates/next-tenant-app/src/lib/content/contracts.ts +0 -399
  78. package/assets/templates/next-tenant-app/src/lib/content/custom-adapter.ts +0 -5
  79. package/assets/templates/next-tenant-app/src/lib/content/empty-state.ts +0 -96
  80. package/assets/templates/next-tenant-app/src/lib/content/release-manifest.test.ts +0 -93
  81. package/assets/templates/next-tenant-app/src/lib/content/release-manifest.ts +0 -123
  82. package/assets/templates/next-tenant-app/src/lib/platform/auth.server.test.ts +0 -75
  83. package/assets/templates/next-tenant-app/src/lib/platform/auth.server.ts +0 -25
  84. package/assets/templates/next-tenant-app/src/lib/platform/client.server.test.ts +0 -170
  85. package/assets/templates/next-tenant-app/src/lib/platform/client.server.ts +0 -661
  86. package/assets/templates/next-tenant-app/src/lib/platform/contracts.ts +0 -131
  87. package/assets/templates/next-tenant-app/src/lib/platform/endpoints.server.ts +0 -34
  88. package/assets/templates/next-tenant-app/src/lib/platform/env.server.test.ts +0 -211
  89. package/assets/templates/next-tenant-app/src/lib/platform/env.server.ts +0 -151
  90. package/assets/templates/next-tenant-app/src/lib/platform/route-response.ts +0 -33
  91. package/assets/templates/next-tenant-app/src/lib/platform/session.server.ts +0 -108
@@ -1,13 +1,13 @@
1
1
  "use client";
2
2
 
3
3
  import Link from "next/link";
4
- import { Blocks, Building2, FolderKanban, ShieldCheck } from "lucide-react";
4
+ import { ArrowRight, Database, ShieldCheck, UserRound } from "lucide-react";
5
+ import type { TenantCustomerSession } from "@minutework/web-auth";
5
6
 
6
7
  import { PanelFrame } from "@/design-system/patterns/panel-frame";
7
8
  import { StatusBadge } from "@/design-system/patterns/status-badge";
8
9
  import { Button } from "@/design-system/primitives/button";
9
10
  import { appRoutes } from "@/lib/app-routes";
10
- import type { AuthenticatedPlatformSession } from "@/lib/platform/contracts";
11
11
 
12
12
  function DashboardMetric({
13
13
  label,
@@ -28,40 +28,32 @@ function DashboardMetric({
28
28
 
29
29
  export function TenantDashboard({
30
30
  appName,
31
- runtimeCommandExampleEnabled,
32
31
  session,
33
32
  }: {
34
33
  appName: string;
35
- runtimeCommandExampleEnabled: boolean;
36
- session: AuthenticatedPlatformSession;
34
+ session: TenantCustomerSession;
37
35
  }) {
38
36
  return (
39
37
  <div className="grid gap-6">
40
38
  <PanelFrame tone="floating" radius="xl" padding="lg" className="space-y-6">
41
39
  <div className="space-y-2">
42
- <p className="text-sm font-semibold uppercase tracking-widest text-muted-foreground">
43
- Authenticated Workspace
44
- </p>
45
40
  <h2 className="text-3xl font-semibold tracking-tight">
46
- {appName} ships with a reusable authenticated shell behind `/app`.
41
+ {appName} workspace
47
42
  </h2>
48
43
  <p className="max-w-3xl text-sm leading-7 text-muted-foreground">
49
- This template is designed for Builder materialization into a writable
50
- sandbox. Public storytelling lives at the root, while the
51
- authenticated shell, tenant context controls, password settings,
52
- route gating, and BFF auth boundary remain real under `/app`.
44
+ Customer session state is loaded through the MinuteWork web auth SDK.
53
45
  </p>
54
46
  </div>
55
47
 
56
48
  <div className="grid gap-3 md:grid-cols-3">
57
- <DashboardMetric label="Signed-in user" value={session.user.username} />
58
49
  <DashboardMetric
59
- label="Active tenant"
60
- value={session.active_tenant.tenant_name}
50
+ label="Customer"
51
+ value={session.user?.display_name || session.user?.email || "Customer"}
61
52
  />
53
+ <DashboardMetric label="Tenant" value={session.tenant.name} />
62
54
  <DashboardMetric
63
- label="Membership count"
64
- value={`${session.memberships.length}`}
55
+ label="Role"
56
+ value={session.customer_membership?.roles.join(", ") || "customer"}
65
57
  />
66
58
  </div>
67
59
  </PanelFrame>
@@ -71,85 +63,58 @@ export function TenantDashboard({
71
63
  <div className="flex items-center gap-2">
72
64
  <ShieldCheck className="size-5 text-primary" />
73
65
  <h3 className="text-xl font-semibold tracking-tight">
74
- Auth profile
66
+ Tenant web auth
75
67
  </h3>
76
68
  </div>
77
69
  <div className="flex flex-wrap gap-3">
78
- <StatusBadge tone="primary">platform_session_bff</StatusBadge>
79
- <StatusBadge tone="info">Server-owned cookies</StatusBadge>
80
- <StatusBadge tone="success">CSRF forwarding</StatusBadge>
70
+ <StatusBadge tone="primary">tenant_web_auth_sdk</StatusBadge>
71
+ <StatusBadge tone="info">HttpOnly cookies</StatusBadge>
72
+ <StatusBadge tone="success">CSRF protected</StatusBadge>
81
73
  </div>
82
74
  <p className="text-sm leading-7 text-muted-foreground">
83
- The browser talks only to this Next.js app. Upstream platform session
84
- cookies and CSRF tokens remain server-owned inside route handlers.
85
- Request-scoped tenant overrides stay scoped to feature actions unless
86
- the explicit session-context APIs are used.
75
+ Customer sessions stay same-origin through /_mw.
87
76
  </p>
88
77
  </PanelFrame>
89
78
 
90
79
  <PanelFrame tone="raised" radius="xl" padding="lg" className="space-y-4">
91
80
  <div className="flex items-center gap-2">
92
- <Building2 className="size-5 text-primary" />
81
+ <Database className="size-5 text-primary" />
93
82
  <h3 className="text-xl font-semibold tracking-tight">
94
- Builder materialization
83
+ Manifest API
95
84
  </h3>
96
85
  </div>
97
86
  <p className="text-sm leading-7 text-muted-foreground">
98
- The canonical template bundle is meant to be copied into
99
- a writable workspace copy before an agent edits it. In runtime
100
- Builder flows that writable root lives at
101
- `BuilderWorkspace.sandbox_root/app/`; in CLI scaffolds the same
102
- starter lands in `tenant-app/`. This source bundle stays governed
103
- and read-only in normal authoring flows.
87
+ Query and action calls go through the SDK and are authorized as
88
+ `tenant_customer`.
104
89
  </p>
90
+ <Button asChild className="w-fit">
91
+ <Link href={appRoutes.demo}>
92
+ Open demo
93
+ <ArrowRight className="size-4" />
94
+ </Link>
95
+ </Button>
105
96
  </PanelFrame>
106
97
  </section>
107
98
 
108
- <section className="grid gap-6 xl:grid-cols-[1fr,1fr]">
109
- <PanelFrame tone="raised" radius="xl" padding="lg" className="space-y-4">
110
- <div className="flex items-center gap-2">
111
- <Blocks className="size-5 text-primary" />
112
- <h3 className="text-xl font-semibold tracking-tight">
113
- Scaffold contents
114
- </h3>
115
- </div>
116
- <ul className="space-y-3 text-sm leading-6 text-muted-foreground">
117
- <li>Design-system primitives, patterns, docs, and governance checks.</li>
118
- <li>Public marketing, docs, and blog routes with a swappable content adapter.</li>
119
- <li>Typed platform session BFF routes for login, context, and password flows.</li>
120
- <li>Tenant-aware shell components ready for Builder-specific feature work.</li>
121
- </ul>
122
- </PanelFrame>
123
-
124
- <PanelFrame tone="raised" radius="xl" padding="lg" className="space-y-4">
125
- <div className="flex items-center gap-2">
126
- <FolderKanban className="size-5 text-primary" />
127
- <h3 className="text-xl font-semibold tracking-tight">
128
- Optional example surface
129
- </h3>
130
- </div>
131
- <p className="text-sm leading-7 text-muted-foreground">
132
- The runtime-command example is bundled as optional sample code rather
133
- than the template&apos;s default product identity.
134
- </p>
135
- {runtimeCommandExampleEnabled ? (
136
- <Button asChild className="w-fit">
137
- <Link href={appRoutes.runtimeCommandExample}>
138
- Open runtime command example
139
- </Link>
140
- </Button>
141
- ) : (
142
- <div className="space-y-2">
143
- <StatusBadge tone="default">Disabled by default</StatusBadge>
144
- <p className="text-sm leading-6 text-muted-foreground">
145
- Set `MW_ENABLE_RUNTIME_COMMAND_EXAMPLE=true` in the materialized
146
- workspace to enable the example route and its matching gateway
147
- proxy handlers.
148
- </p>
149
- </div>
99
+ <PanelFrame tone="raised" radius="xl" padding="lg" className="space-y-4">
100
+ <div className="flex items-center gap-2">
101
+ <UserRound className="size-5 text-primary" />
102
+ <h3 className="text-xl font-semibold tracking-tight">
103
+ Session payload
104
+ </h3>
105
+ </div>
106
+ <pre className="overflow-auto rounded-md bg-muted p-4 text-xs text-muted-foreground">
107
+ {JSON.stringify(
108
+ {
109
+ principal_kind: session.principal_kind,
110
+ tenant_id: session.tenant.id,
111
+ customer_membership_id: session.customer_membership?.id,
112
+ },
113
+ null,
114
+ 2,
150
115
  )}
151
- </PanelFrame>
152
- </section>
116
+ </pre>
117
+ </PanelFrame>
153
118
  </div>
154
119
  );
155
120
  }
@@ -0,0 +1,89 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Database, Play, RefreshCcw } from "lucide-react";
5
+
6
+ import { PanelFrame } from "@/design-system/patterns/panel-frame";
7
+ import { Button } from "@/design-system/primitives/button";
8
+ import { Input } from "@/design-system/primitives/input";
9
+ import { createIdempotencyKey, mw } from "@/mw/client";
10
+
11
+ type DemoResult = {
12
+ data?: unknown;
13
+ count?: number;
14
+ created?: boolean;
15
+ idempotency_key?: string;
16
+ };
17
+
18
+ export function ManifestDemo() {
19
+ const [title, setTitle] = useState("Demo record");
20
+ const [result, setResult] = useState<DemoResult | null>(null);
21
+ const [error, setError] = useState("");
22
+ const [pending, setPending] = useState(false);
23
+
24
+ async function runQuery() {
25
+ setPending(true);
26
+ setError("");
27
+ try {
28
+ setResult(await mw.query<DemoResult>("demo.list", { limit: 10 }));
29
+ } catch (caught) {
30
+ setError(caught instanceof Error ? caught.message : "Query failed.");
31
+ } finally {
32
+ setPending(false);
33
+ }
34
+ }
35
+
36
+ async function runAction() {
37
+ setPending(true);
38
+ setError("");
39
+ try {
40
+ setResult(
41
+ await mw.action<DemoResult>(
42
+ "demo.create",
43
+ { title, status: "new" },
44
+ { idempotencyKey: createIdempotencyKey("demo-create") },
45
+ ),
46
+ );
47
+ } catch (caught) {
48
+ setError(caught instanceof Error ? caught.message : "Action failed.");
49
+ } finally {
50
+ setPending(false);
51
+ }
52
+ }
53
+
54
+ return (
55
+ <div className="grid gap-6">
56
+ <PanelFrame tone="floating" radius="xl" padding="lg" className="space-y-5">
57
+ <div className="flex items-center gap-2">
58
+ <Database className="size-5 text-primary" />
59
+ <h2 className="text-2xl font-semibold tracking-tight">
60
+ Manifest query and action
61
+ </h2>
62
+ </div>
63
+ <div className="grid gap-3 md:grid-cols-[1fr,auto,auto]">
64
+ <Input
65
+ value={title}
66
+ onChange={(event) => setTitle(event.target.value)}
67
+ className="h-11"
68
+ aria-label="Demo title"
69
+ />
70
+ <Button type="button" variant="outline" onClick={runQuery} disabled={pending}>
71
+ <RefreshCcw className="size-4" />
72
+ Query
73
+ </Button>
74
+ <Button type="button" onClick={runAction} disabled={pending}>
75
+ <Play className="size-4" />
76
+ Action
77
+ </Button>
78
+ </div>
79
+ {error ? <p className="text-sm text-destructive">{error}</p> : null}
80
+ </PanelFrame>
81
+
82
+ <PanelFrame tone="raised" radius="xl" padding="lg">
83
+ <pre className="min-h-40 overflow-auto rounded-md bg-muted p-4 text-xs text-muted-foreground">
84
+ {result ? JSON.stringify(result, null, 2) : "No result yet"}
85
+ </pre>
86
+ </PanelFrame>
87
+ </div>
88
+ );
89
+ }
@@ -0,0 +1,58 @@
1
+ import Link from "next/link";
2
+ import { ArrowRight } from "lucide-react";
3
+
4
+ import { Button } from "@/design-system/primitives/button";
5
+ import { appRoutes } from "@/lib/app-routes";
6
+
7
+ export function StaticPublicPage({
8
+ eyebrow,
9
+ title,
10
+ body,
11
+ }: {
12
+ eyebrow: string;
13
+ title: string;
14
+ body: string;
15
+ }) {
16
+ return (
17
+ <main className="min-h-screen bg-background text-foreground">
18
+ <div className="mx-auto flex min-h-screen max-w-6xl flex-col px-6 py-6">
19
+ <nav className="flex items-center justify-between">
20
+ <Link className="text-sm font-semibold text-foreground" href={appRoutes.publicHome}>
21
+ Tenant App
22
+ </Link>
23
+ <div className="flex items-center gap-2">
24
+ <Button asChild variant="ghost">
25
+ <Link href={appRoutes.pricing}>Pricing</Link>
26
+ </Button>
27
+ <Button asChild variant="ghost">
28
+ <Link href={appRoutes.docsIndex}>Docs</Link>
29
+ </Button>
30
+ <Button asChild>
31
+ <Link href={appRoutes.login}>Log in</Link>
32
+ </Button>
33
+ </div>
34
+ </nav>
35
+
36
+ <section className="grid flex-1 content-center gap-8 py-16">
37
+ <div className="max-w-3xl space-y-5">
38
+ <p className="text-sm font-semibold uppercase tracking-widest text-muted-foreground">
39
+ {eyebrow}
40
+ </p>
41
+ <h1 className="text-5xl font-semibold tracking-tight text-balance sm:text-6xl">
42
+ {title}
43
+ </h1>
44
+ <p className="max-w-2xl text-lg leading-8 text-muted-foreground">
45
+ {body}
46
+ </p>
47
+ <Button asChild className="w-fit">
48
+ <Link href={appRoutes.login}>
49
+ Continue
50
+ <ArrowRight className="size-4" />
51
+ </Link>
52
+ </Button>
53
+ </div>
54
+ </section>
55
+ </div>
56
+ </main>
57
+ );
58
+ }