showpane 0.4.25 → 0.4.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/meta/scaffold-manifest.json +6 -5
- package/bundle/scaffold/prisma/schema.local.prisma +1 -0
- package/bundle/scaffold/src/__tests__/branding.test.ts +43 -0
- package/bundle/scaffold/src/app/(portal)/client/page.tsx +42 -15
- package/bundle/scaffold/src/lib/branding.ts +47 -0
- package/bundle/scaffold/src/lib/portal-contracts.ts +1 -0
- package/bundle/toolchain/CLI_VERSION +1 -1
- package/bundle/toolchain/bin/create-portal.ts +50 -11
- package/bundle/toolchain/bin/export-runtime-state.ts +1 -0
- package/bundle/toolchain/skills/portal-analytics/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-create/SKILL.md +17 -11
- package/bundle/toolchain/skills/portal-create/SKILL.md.tmpl +15 -9
- package/bundle/toolchain/skills/portal-credentials/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-delete/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +6 -3
- package/bundle/toolchain/skills/portal-deploy/SKILL.md.tmpl +4 -1
- package/bundle/toolchain/skills/portal-dev/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-list/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +4 -2
- package/bundle/toolchain/skills/portal-onboard/SKILL.md.tmpl +2 -0
- package/bundle/toolchain/skills/portal-preview/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-setup/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-share/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-status/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-update/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md +2 -2
- package/bundle/toolchain/skills/portal-verify/SKILL.md +2 -2
- package/dist/index.js +6 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-04-13T22:
|
|
3
|
+
"generatedAt": "2026-04-13T22:42:22.971Z",
|
|
4
4
|
"scaffoldVersion": "0.2.7",
|
|
5
5
|
"files": {
|
|
6
6
|
".env.example": "ed105f2bdcd1888a98181d55e3c9f7d6eff3ae9c3e2366c2e777a12e3caddfa7",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"package.json": "b095e17e7fc181c630e87fe9f473c5a4ef969afcd4b110f9f9c6d6a6d93f1c0b",
|
|
12
12
|
"postcss.config.js": "fa650b380adfabb151a0b352f7135e107e6352345f899060f1c5c231228f94bf",
|
|
13
13
|
"prisma.config.ts": "36f56fd74eae70632e484443e38d08665158d72d5c978dc456651d8d5e1a636e",
|
|
14
|
-
"prisma/schema.local.prisma": "
|
|
14
|
+
"prisma/schema.local.prisma": "90b9bc60e31a968137a574f156ad83ed5e46a45a8d6080b40126118806b9cd34",
|
|
15
15
|
"prisma/seed.ts": "01b4295296676ab415ab2b30b4e39e13c3fdd30c3bab2b22a7cca672d1c2ea90",
|
|
16
16
|
"public/example-avatar.svg": "0edeb0d3fbefa89cc27ffe6564d20e3ee0fd073cb6d9f2a025248ef3b3f277fd",
|
|
17
17
|
"public/example-logo.svg": "bc5cd933aff2a17698dee66a7b4ea940ad12238e9d813474d643b459b1e8d6da",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"scripts/prisma-db-push.mjs": "76ac85fe65b5dc3d9cc7432e44618fcc84b7443574c8d88198d01f13ac23c040",
|
|
20
20
|
"scripts/prisma-generate.mjs": "d371e63388fa39f963b7c3c7cb8f87e0d9cd43cbf69d254b999108e29b8738c8",
|
|
21
21
|
"scripts/prisma-schema.mjs": "0a86cc1b5f84120948aed8f97a84f2d5b173f91a43ea34ad6767441894121d83",
|
|
22
|
+
"src/__tests__/branding.test.ts": "dffbb3744705bc378ad91b9d7b8868004ffeaa45623f4748e1fed951096b9e3f",
|
|
22
23
|
"src/__tests__/client-portals.test.ts": "9c3236bf0f7190b7d5ba9082287dcb29bc00d28dd63782a89505125ead06c624",
|
|
23
24
|
"src/__tests__/deploy-bundle.test.ts": "abd3216170f306c09df6abb0d2afad966a5741e8859f25a310a0a09693d37609",
|
|
24
25
|
"src/__tests__/portal-contracts.test.ts": "a3ecf29c460a14f22ef48449fc9de5b945ce5f03423f12380d05e01159d1e3b4",
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
"src/app/(portal)/client/example/example-client.tsx": "ed32b111acea861f448d865338f8841d47c6ca7c2f87ed30d85bb0804940d4ec",
|
|
28
29
|
"src/app/(portal)/client/example/page.tsx": "f330864f63c9feea76c8a62c3eba3ce57578627e0d4abd929fd7fefdfc7af058",
|
|
29
30
|
"src/app/(portal)/client/layout.tsx": "4f43871510408a81da229d48ae316ec1d1c1beda93121922246300a2c8fd0999",
|
|
30
|
-
"src/app/(portal)/client/page.tsx": "
|
|
31
|
+
"src/app/(portal)/client/page.tsx": "cf0d1b0517aadf1bfbfc91552d0c13201a140c31d2df9e08e1c62a5d48e59189",
|
|
31
32
|
"src/app/api/client-auth/route.ts": "ce1858559b1e944d5b1dc719d1f03bebf66286671700b1b5397382109f0f1e0d",
|
|
32
33
|
"src/app/api/client-auth/share/route.ts": "ed82414212dcd26af8c6c0f2bd44d9d79a727ed35cfedbac8c4077a6220aad14",
|
|
33
34
|
"src/app/api/client-events/route.ts": "13d545537b7e8ce421e6169d25c105adf2a2de3d978ae0a2c6751ff5f7d2eb33",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"src/components/portal-login.tsx": "8b0d91bb28674e1102fd2e5b5ddcc3a93755dd806fbd3d1b2dbea2646cffca5e",
|
|
44
45
|
"src/components/portal-shell.tsx": "f46a0f753a4a0318f06c8b4e46295febb84b03ea082c95057a6da50c737b4e21",
|
|
45
46
|
"src/lib/abuse-controls.ts": "d79d58d93267aca48ad0b7b9b91f753c9a3c27263e4e98daf768a950c44a6fc6",
|
|
46
|
-
"src/lib/branding.ts": "
|
|
47
|
+
"src/lib/branding.ts": "f0fa242c285610105c27fcaf76828f1892c8051fc2db5c36bdbe92ea916aa4bd",
|
|
47
48
|
"src/lib/client-auth.ts": "b9bdfe77dbe5d6ec6c6a930627fc43d3253f0d76fd8fc4093af5a75742bebe42",
|
|
48
49
|
"src/lib/client-portals.ts": "9b531f9a9ea459b4ab85257b9dd282874fa1422838fe89d511940e417114216a",
|
|
49
50
|
"src/lib/control-plane.ts": "e0cf39f28ec7de715fd5cfbb5f4240773fcd3d775cd1677588dd749fff740a0e",
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
"src/lib/deploy-bundle.ts": "e9675cccb2c802e408639481986c6b629737541853e1c93f322c08a5b9dfc5f9",
|
|
52
53
|
"src/lib/files.ts": "24fd8d1d53c180d62441019395fb140ba3baa28311918ac488284adcdda8eb9a",
|
|
53
54
|
"src/lib/load-app-env.ts": "78b80e17d896885f0d72315ee9a6cf7a0a8c6c08171f26e3d599bb9b2e8afeee",
|
|
54
|
-
"src/lib/portal-contracts.ts": "
|
|
55
|
+
"src/lib/portal-contracts.ts": "927695c64d4f7b56be9e7c0bc37fac105d37558404df8eac09c6f0d1a8f27669",
|
|
55
56
|
"src/lib/prisma-client.ts": "28cd100129a0178a6c8fdfe49e6997b19983fcc427b9fa7caee3ac26226e5eb3",
|
|
56
57
|
"src/lib/runtime-state.ts": "3d30de7dfeaaa48d8b6fd5d29976ecd001408172100c95b063d5d804fdce0a2e",
|
|
57
58
|
"src/lib/storage.ts": "ae3b85fc6cccd39d4174a391dcbe6e91fb9460eb407ec9dbfedd63594a441d08",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getBrandLogoUrl,
|
|
5
|
+
getDomainFromWebsite,
|
|
6
|
+
getLogoUrl,
|
|
7
|
+
normalizeWebsiteUrl,
|
|
8
|
+
} from "@/lib/branding";
|
|
9
|
+
|
|
10
|
+
describe("branding helpers", () => {
|
|
11
|
+
it("normalizes bare domains into https URLs", () => {
|
|
12
|
+
expect(normalizeWebsiteUrl("bidgen.io")).toBe("https://bidgen.io/");
|
|
13
|
+
expect(normalizeWebsiteUrl("https://bidgen.io")).toBe("https://bidgen.io/");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("extracts hostnames from bare domains and full URLs", () => {
|
|
17
|
+
expect(getDomainFromWebsite("bidgen.io")).toBe("bidgen.io");
|
|
18
|
+
expect(getDomainFromWebsite("https://www.bidgen.io/path")).toBe("www.bidgen.io");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("prefers a stored logo URL, then website-derived logos, then initials", () => {
|
|
22
|
+
expect(
|
|
23
|
+
getBrandLogoUrl({
|
|
24
|
+
logoUrl: "https://cdn.example/logo.png",
|
|
25
|
+
websiteUrl: "bidgen.io",
|
|
26
|
+
fallbackName: "Bidgen",
|
|
27
|
+
}),
|
|
28
|
+
).toBe("https://cdn.example/logo.png");
|
|
29
|
+
|
|
30
|
+
expect(
|
|
31
|
+
getBrandLogoUrl({
|
|
32
|
+
websiteUrl: "bidgen.io",
|
|
33
|
+
fallbackName: "Bidgen",
|
|
34
|
+
}),
|
|
35
|
+
).toBe(getLogoUrl("bidgen.io", "Bidgen"));
|
|
36
|
+
|
|
37
|
+
expect(
|
|
38
|
+
getBrandLogoUrl({
|
|
39
|
+
fallbackName: "Bidgen",
|
|
40
|
+
}),
|
|
41
|
+
).toBe(getLogoUrl("", "Bidgen"));
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PortalLogin } from "@/components/portal-login";
|
|
2
|
-
import {
|
|
2
|
+
import { getBrandLogoUrl } from "@/lib/branding";
|
|
3
3
|
import { prisma } from "@/lib/db";
|
|
4
4
|
import { resolveDefaultOrganizationId } from "@/lib/client-portals";
|
|
5
5
|
import { getRuntimePortalBySlug, getRuntimeState, isRuntimeSnapshotMode } from "@/lib/runtime-state";
|
|
@@ -12,12 +12,13 @@ export default async function ClientLogin({
|
|
|
12
12
|
const params = (await searchParams) ?? {};
|
|
13
13
|
const portalSlug = params.portal?.trim() || null;
|
|
14
14
|
|
|
15
|
-
let companyName = "
|
|
15
|
+
let companyName = "Showpane";
|
|
16
16
|
let companyUrl = "https://showpane.com";
|
|
17
17
|
let supportEmail = "support@showpane.com";
|
|
18
18
|
let portalLabel = "Client Portal";
|
|
19
19
|
let description = "Private portal access. Sign in with the credentials you were sent.";
|
|
20
|
-
let
|
|
20
|
+
let companyLogoSrc: string | null = null;
|
|
21
|
+
let companyLogoAlt = companyName;
|
|
21
22
|
|
|
22
23
|
if (portalSlug) {
|
|
23
24
|
try {
|
|
@@ -25,10 +26,16 @@ export default async function ClientLogin({
|
|
|
25
26
|
const state = await getRuntimeState();
|
|
26
27
|
const portal = await getRuntimePortalBySlug(portalSlug);
|
|
27
28
|
if (portal) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const orgName = state?.organization?.name || companyName;
|
|
30
|
+
companyName = orgName;
|
|
31
|
+
companyLogoAlt = orgName;
|
|
32
|
+
portalLabel = state?.organization?.portalLabel || `${orgName} Portal`;
|
|
33
|
+
description = `Private portal created by ${orgName} for ${portal.companyName}. Sign in with the credentials you were sent.`;
|
|
34
|
+
companyLogoSrc = getBrandLogoUrl({
|
|
35
|
+
logoUrl: state?.organization?.logoUrl,
|
|
36
|
+
websiteUrl: state?.organization?.websiteUrl,
|
|
37
|
+
fallbackName: orgName,
|
|
38
|
+
});
|
|
32
39
|
if (state?.organization?.websiteUrl) {
|
|
33
40
|
companyUrl = state.organization.websiteUrl;
|
|
34
41
|
}
|
|
@@ -45,11 +52,15 @@ export default async function ClientLogin({
|
|
|
45
52
|
slug: portalSlug,
|
|
46
53
|
isActive: true,
|
|
47
54
|
},
|
|
48
|
-
select: {
|
|
55
|
+
select: {
|
|
56
|
+
companyName: true,
|
|
57
|
+
},
|
|
49
58
|
});
|
|
50
59
|
const organization = await prisma.organization.findUnique({
|
|
51
60
|
where: { id: organizationId },
|
|
52
61
|
select: {
|
|
62
|
+
name: true,
|
|
63
|
+
logoUrl: true,
|
|
53
64
|
websiteUrl: true,
|
|
54
65
|
supportEmail: true,
|
|
55
66
|
portalLabel: true,
|
|
@@ -57,10 +68,16 @@ export default async function ClientLogin({
|
|
|
57
68
|
});
|
|
58
69
|
|
|
59
70
|
if (portal) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
const orgName = organization?.name || companyName;
|
|
72
|
+
companyName = orgName;
|
|
73
|
+
companyLogoAlt = orgName;
|
|
74
|
+
portalLabel = organization?.portalLabel || `${orgName} Portal`;
|
|
75
|
+
description = `Private portal created by ${orgName} for ${portal.companyName}. Sign in with the credentials you were sent.`;
|
|
76
|
+
companyLogoSrc = getBrandLogoUrl({
|
|
77
|
+
logoUrl: organization?.logoUrl,
|
|
78
|
+
websiteUrl: organization?.websiteUrl,
|
|
79
|
+
fallbackName: orgName,
|
|
80
|
+
});
|
|
64
81
|
}
|
|
65
82
|
if (organization?.websiteUrl) {
|
|
66
83
|
companyUrl = organization.websiteUrl;
|
|
@@ -79,9 +96,19 @@ export default async function ClientLogin({
|
|
|
79
96
|
<PortalLogin
|
|
80
97
|
companyName={companyName}
|
|
81
98
|
companyLogo={
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
99
|
+
companyLogoSrc ? (
|
|
100
|
+
<img
|
|
101
|
+
src={companyLogoSrc}
|
|
102
|
+
alt={companyLogoAlt}
|
|
103
|
+
className="h-7 w-7 rounded-lg object-cover"
|
|
104
|
+
/>
|
|
105
|
+
) : (
|
|
106
|
+
<div className="flex h-7 w-7 items-center justify-center rounded-lg bg-gray-900">
|
|
107
|
+
<span className="text-xs font-bold text-white">
|
|
108
|
+
{companyName[0]?.toUpperCase() || "S"}
|
|
109
|
+
</span>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
85
112
|
}
|
|
86
113
|
companyUrl={companyUrl}
|
|
87
114
|
supportEmail={supportEmail}
|
|
@@ -4,6 +4,36 @@
|
|
|
4
4
|
* All functions return sensible defaults on failure.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Normalize a website or bare domain into an https URL.
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeWebsiteUrl(value?: string | null): string | null {
|
|
11
|
+
if (!value) return null;
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
if (!trimmed) return null;
|
|
14
|
+
|
|
15
|
+
const candidate = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
16
|
+
try {
|
|
17
|
+
return new URL(candidate).toString();
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extract a hostname from a website URL or bare domain.
|
|
25
|
+
*/
|
|
26
|
+
export function getDomainFromWebsite(value?: string | null): string | null {
|
|
27
|
+
const normalized = normalizeWebsiteUrl(value);
|
|
28
|
+
if (!normalized) return null;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
return new URL(normalized).hostname;
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
7
37
|
/**
|
|
8
38
|
* Fetch a company logo URL from a domain.
|
|
9
39
|
* Uses Clearbit Logo API (free, no key required).
|
|
@@ -19,6 +49,23 @@ export function getLogoUrl(domain: string, fallbackName?: string): string {
|
|
|
19
49
|
return `https://ui-avatars.com/api/?name=${initial}&background=111827&color=fff&size=128&bold=true`;
|
|
20
50
|
}
|
|
21
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the best available logo source for a brand.
|
|
54
|
+
* Prefers a stored logo URL, then a website-derived domain, then initials.
|
|
55
|
+
*/
|
|
56
|
+
export function getBrandLogoUrl(options: {
|
|
57
|
+
logoUrl?: string | null;
|
|
58
|
+
websiteUrl?: string | null;
|
|
59
|
+
fallbackName: string;
|
|
60
|
+
}): string {
|
|
61
|
+
if (options.logoUrl) {
|
|
62
|
+
return options.logoUrl;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const domain = getDomainFromWebsite(options.websiteUrl);
|
|
66
|
+
return getLogoUrl(domain ?? "", options.fallbackName);
|
|
67
|
+
}
|
|
68
|
+
|
|
22
69
|
/**
|
|
23
70
|
* Fetch a Gravatar URL for an email address.
|
|
24
71
|
* Falls back to UI Avatars if no Gravatar exists.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.4.
|
|
1
|
+
0.4.26
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PrismaClient } from "@/lib/prisma-client";
|
|
2
|
+
import { getBrandLogoUrl, normalizeWebsiteUrl } from "@/lib/branding";
|
|
2
3
|
import bcrypt from "bcryptjs";
|
|
3
4
|
import crypto from "node:crypto";
|
|
4
5
|
|
|
@@ -16,7 +17,7 @@ async function main() {
|
|
|
16
17
|
const args = process.argv.slice(2);
|
|
17
18
|
|
|
18
19
|
if (args.includes("--help")) {
|
|
19
|
-
console.log("Usage: create-portal --slug <slug> --company <name> --org-id <orgId>");
|
|
20
|
+
console.log("Usage: create-portal --slug <slug> --company <name> --org-id <orgId> [--website <domain-or-url>]");
|
|
20
21
|
console.log("Creates a new client portal with auto-generated credentials.");
|
|
21
22
|
process.exit(0);
|
|
22
23
|
}
|
|
@@ -25,6 +26,7 @@ async function main() {
|
|
|
25
26
|
const slug = getArg("--slug");
|
|
26
27
|
const company = getArg("--company");
|
|
27
28
|
const orgId = getArg("--org-id");
|
|
29
|
+
const website = getArg("--website");
|
|
28
30
|
|
|
29
31
|
if (!slug || !company || !orgId) fail("Missing --slug, --company, or --org-id");
|
|
30
32
|
|
|
@@ -37,6 +39,11 @@ async function main() {
|
|
|
37
39
|
const username = slug;
|
|
38
40
|
const password = crypto.randomBytes(16).toString("base64url");
|
|
39
41
|
const passwordHash = await bcrypt.hash(password, 10);
|
|
42
|
+
const websiteUrl = normalizeWebsiteUrl(website);
|
|
43
|
+
const logoUrl = getBrandLogoUrl({
|
|
44
|
+
websiteUrl,
|
|
45
|
+
fallbackName: company,
|
|
46
|
+
});
|
|
40
47
|
|
|
41
48
|
const prisma = new PrismaClient();
|
|
42
49
|
try {
|
|
@@ -46,19 +53,51 @@ async function main() {
|
|
|
46
53
|
});
|
|
47
54
|
if (existing) fail(`Slug "${slug}" already exists`);
|
|
48
55
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const createData: Record<string, unknown> = {
|
|
57
|
+
organizationId: orgId,
|
|
58
|
+
slug,
|
|
59
|
+
companyName: company,
|
|
60
|
+
logoUrl,
|
|
61
|
+
username,
|
|
62
|
+
passwordHash,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Older scaffolded apps may not have the ClientPortal.websiteUrl column yet.
|
|
66
|
+
if (websiteUrl) {
|
|
67
|
+
createData.websiteUrl = websiteUrl;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let portal;
|
|
71
|
+
try {
|
|
72
|
+
portal = await prisma.clientPortal.create({
|
|
73
|
+
data: createData,
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
77
|
+
if (
|
|
78
|
+
websiteUrl &&
|
|
79
|
+
(message.includes("Unknown arg `websiteUrl`") ||
|
|
80
|
+
message.includes("Unknown argument `websiteUrl`"))
|
|
81
|
+
) {
|
|
82
|
+
delete createData.websiteUrl;
|
|
83
|
+
portal = await prisma.clientPortal.create({
|
|
84
|
+
data: createData,
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
58
90
|
|
|
59
91
|
console.log(JSON.stringify({
|
|
60
92
|
ok: true,
|
|
61
|
-
portal: {
|
|
93
|
+
portal: {
|
|
94
|
+
id: portal.id,
|
|
95
|
+
slug: portal.slug,
|
|
96
|
+
username,
|
|
97
|
+
password,
|
|
98
|
+
websiteUrl: portal.websiteUrl,
|
|
99
|
+
logoUrl: portal.logoUrl,
|
|
100
|
+
},
|
|
62
101
|
}));
|
|
63
102
|
} finally {
|
|
64
103
|
await prisma.$disconnect();
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Overview
|
|
107
107
|
|
|
@@ -104,9 +104,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
104
104
|
|
|
105
105
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
106
106
|
|
|
107
|
-
Read `skills/shared/runtime-principles.md`
|
|
107
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
108
108
|
|
|
109
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
109
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
110
110
|
|
|
111
111
|
## Steps
|
|
112
112
|
|
|
@@ -168,9 +168,15 @@ The script returns `{"valid":true}` or `{"valid":false,"reason":"...","message":
|
|
|
168
168
|
|
|
169
169
|
If invalid, explain the issue and ask for a different slug.
|
|
170
170
|
|
|
171
|
-
Also
|
|
172
|
-
-
|
|
173
|
-
-
|
|
171
|
+
Also determine the client's website domain. This enables auto-branding and is worth a quick best-effort pass:
|
|
172
|
+
- if the domain is explicit in the transcript or user prompt, use it
|
|
173
|
+
- otherwise make one quick best-effort lookup for the official site
|
|
174
|
+
- if one clear candidate stands out, suggest it briefly: `I found <domain> — I'll use that for the logo unless you want a different one`
|
|
175
|
+
- if confidence is low, ask directly
|
|
176
|
+
|
|
177
|
+
Store the confirmed value in `ClientPortal.websiteUrl`.
|
|
178
|
+
Use it to derive `ClientPortal.logoUrl` via `getBrandLogoUrl(...)`.
|
|
179
|
+
If no domain is confirmed, fall back to an initial-based logo.
|
|
174
180
|
|
|
175
181
|
### Step 3: Granola MCP integration (optional)
|
|
176
182
|
|
|
@@ -284,10 +290,10 @@ import { PortalShell } from "@/components/portal-shell";
|
|
|
284
290
|
- The exported component returns `<PortalShell>` with all required props
|
|
285
291
|
|
|
286
292
|
**PortalShell props (all required):**
|
|
287
|
-
- `companyName` — the org's company name (from `
|
|
288
|
-
- `companyLogo` —
|
|
293
|
+
- `companyName` — the org's company name (from `get-org.ts`)
|
|
294
|
+
- `companyLogo` — prefer the org logo via `getBrandLogoUrl({ logoUrl: ORG.logoUrl, websiteUrl: ORG.websiteUrl, fallbackName: ORG.name })`; fall back to initials only if no better source exists
|
|
289
295
|
- `clientName` — the client's company name (from transcript or user input)
|
|
290
|
-
- `clientLogoSrc` —
|
|
296
|
+
- `clientLogoSrc` — use `getBrandLogoUrl({ websiteUrl: <confirmed client website>, fallbackName: clientName })`; store the chosen URL in the `ClientPortal.logoUrl` field
|
|
291
297
|
- `clientLogoAlt` — the client company name
|
|
292
298
|
- `lastUpdated` — today's date formatted as "7 April 2026"
|
|
293
299
|
- `contact` — object with `name`, `title`, `avatarSrc`, `email` (from the `get-org.ts` result, not from ad-hoc config or DB probing)
|
|
@@ -335,12 +341,12 @@ Import only the icons you need from `lucide-react`. Common choices:
|
|
|
335
341
|
Run the create-portal script to register the portal in the database:
|
|
336
342
|
|
|
337
343
|
```bash
|
|
338
|
-
cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/create-portal.ts" --slug <slug> --company "<client_company_name>" --org-id "$ORG_ID"
|
|
344
|
+
cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/create-portal.ts" --slug <slug> --company "<client_company_name>" --org-id "$ORG_ID" --website "<confirmed_client_website_if_any>"
|
|
339
345
|
```
|
|
340
346
|
|
|
341
347
|
This creates the `ClientPortal` record with the slug and company name, links it to
|
|
342
|
-
the Organization,
|
|
343
|
-
returns `username` and `password`.
|
|
348
|
+
the Organization, stores the confirmed client website/logo if available, and
|
|
349
|
+
currently auto-generates initial credentials. The script returns `username` and `password`.
|
|
344
350
|
|
|
345
351
|
Do not dump those credentials into the middle of the flow unless the user asked.
|
|
346
352
|
For onboarding, carry them forward quietly and show them at the access phase.
|
|
@@ -73,9 +73,15 @@ The script returns `{"valid":true}` or `{"valid":false,"reason":"...","message":
|
|
|
73
73
|
|
|
74
74
|
If invalid, explain the issue and ask for a different slug.
|
|
75
75
|
|
|
76
|
-
Also
|
|
77
|
-
-
|
|
78
|
-
-
|
|
76
|
+
Also determine the client's website domain. This enables auto-branding and is worth a quick best-effort pass:
|
|
77
|
+
- if the domain is explicit in the transcript or user prompt, use it
|
|
78
|
+
- otherwise make one quick best-effort lookup for the official site
|
|
79
|
+
- if one clear candidate stands out, suggest it briefly: `I found <domain> — I'll use that for the logo unless you want a different one`
|
|
80
|
+
- if confidence is low, ask directly
|
|
81
|
+
|
|
82
|
+
Store the confirmed value in `ClientPortal.websiteUrl`.
|
|
83
|
+
Use it to derive `ClientPortal.logoUrl` via `getBrandLogoUrl(...)`.
|
|
84
|
+
If no domain is confirmed, fall back to an initial-based logo.
|
|
79
85
|
|
|
80
86
|
### Step 3: Granola MCP integration (optional)
|
|
81
87
|
|
|
@@ -189,10 +195,10 @@ import { PortalShell } from "@/components/portal-shell";
|
|
|
189
195
|
- The exported component returns `<PortalShell>` with all required props
|
|
190
196
|
|
|
191
197
|
**PortalShell props (all required):**
|
|
192
|
-
- `companyName` — the org's company name (from `
|
|
193
|
-
- `companyLogo` —
|
|
198
|
+
- `companyName` — the org's company name (from `get-org.ts`)
|
|
199
|
+
- `companyLogo` — prefer the org logo via `getBrandLogoUrl({ logoUrl: ORG.logoUrl, websiteUrl: ORG.websiteUrl, fallbackName: ORG.name })`; fall back to initials only if no better source exists
|
|
194
200
|
- `clientName` — the client's company name (from transcript or user input)
|
|
195
|
-
- `clientLogoSrc` —
|
|
201
|
+
- `clientLogoSrc` — use `getBrandLogoUrl({ websiteUrl: <confirmed client website>, fallbackName: clientName })`; store the chosen URL in the `ClientPortal.logoUrl` field
|
|
196
202
|
- `clientLogoAlt` — the client company name
|
|
197
203
|
- `lastUpdated` — today's date formatted as "7 April 2026"
|
|
198
204
|
- `contact` — object with `name`, `title`, `avatarSrc`, `email` (from the `get-org.ts` result, not from ad-hoc config or DB probing)
|
|
@@ -240,12 +246,12 @@ Import only the icons you need from `lucide-react`. Common choices:
|
|
|
240
246
|
Run the create-portal script to register the portal in the database:
|
|
241
247
|
|
|
242
248
|
```bash
|
|
243
|
-
cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/create-portal.ts" --slug <slug> --company "<client_company_name>" --org-id "$ORG_ID"
|
|
249
|
+
cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/create-portal.ts" --slug <slug> --company "<client_company_name>" --org-id "$ORG_ID" --website "<confirmed_client_website_if_any>"
|
|
244
250
|
```
|
|
245
251
|
|
|
246
252
|
This creates the `ClientPortal` record with the slug and company name, links it to
|
|
247
|
-
the Organization,
|
|
248
|
-
returns `username` and `password`.
|
|
253
|
+
the Organization, stores the confirmed client website/logo if available, and
|
|
254
|
+
currently auto-generates initial credentials. The script returns `username` and `password`.
|
|
249
255
|
|
|
250
256
|
Do not dump those credentials into the middle of the flow unless the user asked.
|
|
251
257
|
For onboarding, carry them forward quietly and show them at the access phase.
|
|
@@ -109,9 +109,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
109
109
|
|
|
110
110
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
111
111
|
|
|
112
|
-
Read `skills/shared/runtime-principles.md`
|
|
112
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
113
113
|
|
|
114
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
114
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
115
115
|
|
|
116
116
|
## Steps
|
|
117
117
|
|
|
@@ -109,9 +109,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
109
109
|
|
|
110
110
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
111
111
|
|
|
112
|
-
Read `skills/shared/runtime-principles.md`
|
|
112
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
113
113
|
|
|
114
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
114
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
115
115
|
|
|
116
116
|
## Safety Guard
|
|
117
117
|
|
|
@@ -102,9 +102,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
102
102
|
|
|
103
103
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
104
104
|
|
|
105
|
-
Read `skills/shared/runtime-principles.md`
|
|
105
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
106
106
|
|
|
107
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
107
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
108
108
|
|
|
109
109
|
## Steps
|
|
110
110
|
|
|
@@ -168,12 +168,15 @@ Run list-portals and warn about portals missing credentials. This is a warning,
|
|
|
168
168
|
### Cloud Step 2: Run the canonical deploy command
|
|
169
169
|
|
|
170
170
|
Use the built-in deploy command instead of reimplementing the staged cloud protocol in shell.
|
|
171
|
+
Run it directly so progress lines stay visible while the build and publish are in flight.
|
|
171
172
|
|
|
172
173
|
```bash
|
|
173
|
-
DEPLOY_JSON=$(cd "$APP_PATH" && SHOWPANE_APP_PATH="$APP_PATH"
|
|
174
|
+
DEPLOY_JSON=$(cd "$APP_PATH" && SHOWPANE_APP_PATH="$APP_PATH" "$SHOWPANE_BIN/showpane" deploy --wait --json)
|
|
174
175
|
echo "$DEPLOY_JSON"
|
|
175
176
|
```
|
|
176
177
|
|
|
178
|
+
Do not wrap this command in `tail` or another truncating pipe. If you need a shorter summary later, capture the output after the command finishes or read from a temp log separately.
|
|
179
|
+
|
|
177
180
|
That command already owns:
|
|
178
181
|
- type check
|
|
179
182
|
- project-link bootstrap
|
|
@@ -73,12 +73,15 @@ Run list-portals and warn about portals missing credentials. This is a warning,
|
|
|
73
73
|
### Cloud Step 2: Run the canonical deploy command
|
|
74
74
|
|
|
75
75
|
Use the built-in deploy command instead of reimplementing the staged cloud protocol in shell.
|
|
76
|
+
Run it directly so progress lines stay visible while the build and publish are in flight.
|
|
76
77
|
|
|
77
78
|
```bash
|
|
78
|
-
DEPLOY_JSON=$(cd "$APP_PATH" && SHOWPANE_APP_PATH="$APP_PATH"
|
|
79
|
+
DEPLOY_JSON=$(cd "$APP_PATH" && SHOWPANE_APP_PATH="$APP_PATH" "$SHOWPANE_BIN/showpane" deploy --wait --json)
|
|
79
80
|
echo "$DEPLOY_JSON"
|
|
80
81
|
```
|
|
81
82
|
|
|
83
|
+
Do not wrap this command in `tail` or another truncating pipe. If you need a shorter summary later, capture the output after the command finishes or read from a temp log separately.
|
|
84
|
+
|
|
82
85
|
That command already owns:
|
|
83
86
|
- type check
|
|
84
87
|
- project-link bootstrap
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Steps
|
|
107
107
|
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Overview
|
|
107
107
|
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Overview
|
|
107
107
|
|
|
@@ -278,6 +278,7 @@ During this phase:
|
|
|
278
278
|
|
|
279
279
|
- suggest a slug if needed
|
|
280
280
|
- prefer a real company/client name in the portal
|
|
281
|
+
- if the client website is not already obvious, do one quick best-effort domain lookup and suggest the result briefly before drafting
|
|
281
282
|
- prefer useful structure over completeness
|
|
282
283
|
- aim for a credible first draft, not a perfect final artifact
|
|
283
284
|
- do not re-ask for the company name or template if the user already chose them
|
|
@@ -380,6 +381,7 @@ Save checkpoint with phase `access-ready` and `accessMode`.
|
|
|
380
381
|
### Phase 8: Publish to Showpane Cloud
|
|
381
382
|
|
|
382
383
|
Run the `portal-deploy` flow inline.
|
|
384
|
+
Let the deploy command stream its own progress. Do not wrap the long-running publish step in `tail`.
|
|
383
385
|
|
|
384
386
|
This phase is part of onboarding by default. Do not treat publish as an optional
|
|
385
387
|
afterthought unless the user explicitly says they want to stop at local preview.
|
|
@@ -185,6 +185,7 @@ During this phase:
|
|
|
185
185
|
|
|
186
186
|
- suggest a slug if needed
|
|
187
187
|
- prefer a real company/client name in the portal
|
|
188
|
+
- if the client website is not already obvious, do one quick best-effort domain lookup and suggest the result briefly before drafting
|
|
188
189
|
- prefer useful structure over completeness
|
|
189
190
|
- aim for a credible first draft, not a perfect final artifact
|
|
190
191
|
- do not re-ask for the company name or template if the user already chose them
|
|
@@ -287,6 +288,7 @@ Save checkpoint with phase `access-ready` and `accessMode`.
|
|
|
287
288
|
### Phase 8: Publish to Showpane Cloud
|
|
288
289
|
|
|
289
290
|
Run the `portal-deploy` flow inline.
|
|
291
|
+
Let the deploy command stream its own progress. Do not wrap the long-running publish step in `tail`.
|
|
290
292
|
|
|
291
293
|
This phase is part of onboarding by default. Do not treat publish as an optional
|
|
292
294
|
afterthought unless the user explicitly says they want to stop at local preview.
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Overview
|
|
107
107
|
|
|
@@ -93,9 +93,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
93
93
|
|
|
94
94
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
95
95
|
|
|
96
|
-
Read `skills/shared/runtime-principles.md`
|
|
96
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
97
97
|
|
|
98
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
98
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
99
99
|
|
|
100
100
|
## Steps
|
|
101
101
|
|
|
@@ -102,9 +102,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
102
102
|
|
|
103
103
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
104
104
|
|
|
105
|
-
Read `skills/shared/runtime-principles.md`
|
|
105
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
106
106
|
|
|
107
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
107
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
108
108
|
|
|
109
109
|
## Overview
|
|
110
110
|
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Overview
|
|
107
107
|
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Steps
|
|
107
107
|
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Overview
|
|
107
107
|
|
|
@@ -99,9 +99,9 @@ If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
|
99
99
|
|
|
100
100
|
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
101
101
|
|
|
102
|
-
Read `skills/shared/runtime-principles.md`
|
|
102
|
+
Read `skills/shared/runtime-principles.md` directly from that exact path near the start of the skill and apply the relevant product defaults.
|
|
103
103
|
|
|
104
|
-
If `skills/shared/platform-constraints.md` exists, read it
|
|
104
|
+
If `skills/shared/platform-constraints.md` exists, read it directly from that exact path near the start of the skill and apply only the relevant limits. No directory listing is needed first.
|
|
105
105
|
|
|
106
106
|
## Steps
|
|
107
107
|
|
package/dist/index.js
CHANGED
|
@@ -671,8 +671,12 @@ async function syncCloudProjectLink(projectRoot, accessToken) {
|
|
|
671
671
|
writeCloudProjectLink(projectRoot, projectLink);
|
|
672
672
|
}
|
|
673
673
|
function removePath(targetPath) {
|
|
674
|
-
|
|
675
|
-
|
|
674
|
+
let stat;
|
|
675
|
+
try {
|
|
676
|
+
stat = lstatSync(targetPath);
|
|
677
|
+
} catch {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
676
680
|
if (stat.isSymbolicLink() || stat.isFile()) {
|
|
677
681
|
unlinkSync(targetPath);
|
|
678
682
|
return;
|