@construct-space/cli 1.8.1 → 1.9.1
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/dist/index.js +120 -11
- package/dist/templates/space/full/skill-data.md.tmpl +11 -15
- package/dist/templates/space/full/skill-ui.md.tmpl +23 -18
- package/dist/templates/space/full/space.manifest.json.tmpl +1 -5
- package/dist/templates/space/skill.md.tmpl +24 -10
- package/dist/templates/space/space.manifest.json.tmpl +1 -1
- package/dist/templates/space/vite.config.ts.tmpl +10 -2
- package/package.json +1 -1
- package/templates/space/full/skill-data.md.tmpl +11 -15
- package/templates/space/full/skill-ui.md.tmpl +23 -18
- package/templates/space/full/space.manifest.json.tmpl +1 -5
- package/templates/space/skill.md.tmpl +24 -10
- package/templates/space/space.manifest.json.tmpl +1 -1
- package/templates/space/vite.config.ts.tmpl +10 -2
package/dist/index.js
CHANGED
|
@@ -3090,6 +3090,83 @@ var init_whoami = __esm(() => {
|
|
|
3090
3090
|
init_auth();
|
|
3091
3091
|
});
|
|
3092
3092
|
|
|
3093
|
+
// src/commands/org.ts
|
|
3094
|
+
var exports_org = {};
|
|
3095
|
+
__export(exports_org, {
|
|
3096
|
+
orgStatus: () => orgStatus
|
|
3097
|
+
});
|
|
3098
|
+
async function orgStatus() {
|
|
3099
|
+
let creds;
|
|
3100
|
+
try {
|
|
3101
|
+
creds = load2();
|
|
3102
|
+
} catch {
|
|
3103
|
+
console.error(source_default.red("Not signed in."));
|
|
3104
|
+
console.error(source_default.dim("Run 'construct login' first."));
|
|
3105
|
+
process.exit(1);
|
|
3106
|
+
}
|
|
3107
|
+
let s;
|
|
3108
|
+
try {
|
|
3109
|
+
const res = await fetch(ACCOUNTS_SCOPE_URL3, {
|
|
3110
|
+
headers: { Authorization: `Bearer ${creds.token}`, Accept: "application/json" }
|
|
3111
|
+
});
|
|
3112
|
+
if (!res.ok) {
|
|
3113
|
+
console.error(source_default.red(`Scope lookup failed (${res.status}).`));
|
|
3114
|
+
console.error(source_default.dim("Token may be expired. Try: construct login"));
|
|
3115
|
+
process.exit(1);
|
|
3116
|
+
}
|
|
3117
|
+
s = await res.json();
|
|
3118
|
+
} catch (err) {
|
|
3119
|
+
console.error(source_default.red(`Could not reach accounts service: ${err?.message || err}`));
|
|
3120
|
+
process.exit(1);
|
|
3121
|
+
}
|
|
3122
|
+
if (!s.authenticated || !s.user) {
|
|
3123
|
+
console.error(source_default.red("Token rejected."));
|
|
3124
|
+
process.exit(1);
|
|
3125
|
+
}
|
|
3126
|
+
if (s.scope === "org" && s.org) {
|
|
3127
|
+
console.log(source_default.cyan("Scope: ") + source_default.bold(`org \u2014 ${s.org.name || s.org.slug || s.org.id}`));
|
|
3128
|
+
if (s.org.slug)
|
|
3129
|
+
console.log(source_default.dim(` slug: ${s.org.slug}`));
|
|
3130
|
+
if (s.org.id)
|
|
3131
|
+
console.log(source_default.dim(` id: ${s.org.id}`));
|
|
3132
|
+
if (s.roles && s.roles.length) {
|
|
3133
|
+
console.log(source_default.dim(` roles: ${s.roles.join(", ")}`));
|
|
3134
|
+
}
|
|
3135
|
+
} else {
|
|
3136
|
+
console.log(source_default.cyan("Scope: ") + source_default.bold("personal"));
|
|
3137
|
+
console.log(source_default.dim(" (switch to an org at https://my.construct.space)"));
|
|
3138
|
+
}
|
|
3139
|
+
console.log();
|
|
3140
|
+
console.log(source_default.cyan("Signed in as"));
|
|
3141
|
+
console.log(` ${s.user.email || s.user.username || s.user.uuid || "(unknown)"}`);
|
|
3142
|
+
if (s.user.uuid)
|
|
3143
|
+
console.log(source_default.dim(` ${s.user.uuid}`));
|
|
3144
|
+
console.log();
|
|
3145
|
+
console.log(source_default.cyan("Publisher (from auth.json)"));
|
|
3146
|
+
if (creds.publisherKey) {
|
|
3147
|
+
const kind = creds.publisherKind || "user";
|
|
3148
|
+
const expected = s.scope === "org" ? "org" : "user";
|
|
3149
|
+
const matches = kind === expected;
|
|
3150
|
+
console.log(` ${source_default.bold(creds.publisherName || "(unnamed)")} ${source_default.dim(`[${kind}]`)}`);
|
|
3151
|
+
console.log(source_default.dim(` key: ${creds.publisherKey.slice(0, 14)}\u2026`));
|
|
3152
|
+
if (!matches) {
|
|
3153
|
+
console.log(source_default.yellow(` \u26A0 publisher kind (${kind}) doesn't match active scope (${expected}).`));
|
|
3154
|
+
console.log(source_default.dim(" Restart the desktop app or run 'construct login' to re-sync."));
|
|
3155
|
+
}
|
|
3156
|
+
} else {
|
|
3157
|
+
console.log(source_default.dim(" (none \u2014 enroll as a developer to get a publisher key)"));
|
|
3158
|
+
}
|
|
3159
|
+
if (s.developer) {
|
|
3160
|
+
console.log();
|
|
3161
|
+
console.log(source_default.dim("developer capability: ") + source_default.green("enabled"));
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
var ACCOUNTS_SCOPE_URL3 = "https://my.construct.space/api/accounts/me/scope";
|
|
3165
|
+
var init_org = __esm(() => {
|
|
3166
|
+
init_source();
|
|
3167
|
+
init_auth();
|
|
3168
|
+
});
|
|
3169
|
+
|
|
3093
3170
|
// src/lib/graphClient.ts
|
|
3094
3171
|
function graphBaseURL() {
|
|
3095
3172
|
return process.env.GRAPH_URL || "https://graph.construct.space";
|
|
@@ -5373,7 +5450,9 @@ async function scaffold(nameArg, options) {
|
|
|
5373
5450
|
join2(name, "src", "pages"),
|
|
5374
5451
|
join2(name, "src", "components"),
|
|
5375
5452
|
join2(name, "src", "composables"),
|
|
5376
|
-
join2(name, "
|
|
5453
|
+
join2(name, "scripts"),
|
|
5454
|
+
join2(name, "references"),
|
|
5455
|
+
join2(name, "assets"),
|
|
5377
5456
|
join2(name, "agent", "hooks"),
|
|
5378
5457
|
join2(name, "agent", "tools"),
|
|
5379
5458
|
join2(name, "widgets", "summary")
|
|
@@ -5394,7 +5473,7 @@ async function scaffold(nameArg, options) {
|
|
|
5394
5473
|
"style.css.tmpl": join2(name, "src", "style.css"),
|
|
5395
5474
|
"index.vue.tmpl": join2(name, "src", "pages", "index.vue"),
|
|
5396
5475
|
"config.md.tmpl": join2(name, "agent", "config.md"),
|
|
5397
|
-
"skill.md.tmpl": join2(name, "
|
|
5476
|
+
"skill.md.tmpl": join2(name, "SKILL.md"),
|
|
5398
5477
|
"safety.json.tmpl": join2(name, "agent", "hooks", "safety.json"),
|
|
5399
5478
|
"tsconfig.json.tmpl": join2(name, "tsconfig.json"),
|
|
5400
5479
|
"eslint.config.js.tmpl": join2(name, "eslint.config.js"),
|
|
@@ -5417,9 +5496,9 @@ async function scaffold(nameArg, options) {
|
|
|
5417
5496
|
}
|
|
5418
5497
|
if (isFull) {
|
|
5419
5498
|
writeTemplate(templateDir, "full/settings.vue.tmpl", join2(name, "src", "pages", "settings.vue"), data);
|
|
5420
|
-
writeTemplate(templateDir, "full/skill-data.md.tmpl", join2(name, "
|
|
5421
|
-
writeTemplate(templateDir, "full/skill-ui.md.tmpl", join2(name, "
|
|
5422
|
-
console.log(source_default.blue("Full preset: settings page +
|
|
5499
|
+
writeTemplate(templateDir, "full/skill-data.md.tmpl", join2(name, "references", "data.md"), data);
|
|
5500
|
+
writeTemplate(templateDir, "full/skill-ui.md.tmpl", join2(name, "references", "ui.md"), data);
|
|
5501
|
+
console.log(source_default.blue("Full preset: settings page + references/data.md + references/ui.md"));
|
|
5423
5502
|
}
|
|
5424
5503
|
if (options?.withTests) {
|
|
5425
5504
|
mkdirSync(join2(name, "e2e"), { recursive: true });
|
|
@@ -10468,6 +10547,13 @@ async function publish(options) {
|
|
|
10468
10547
|
const yes = options?.yes ?? false;
|
|
10469
10548
|
const wantPrivate = options?.private ?? false;
|
|
10470
10549
|
const wantPublic = options?.public ?? false;
|
|
10550
|
+
const scopeOpt = options?.scope ? options.scope.toLowerCase() : undefined;
|
|
10551
|
+
if (scopeOpt && scopeOpt !== "user" && scopeOpt !== "org") {
|
|
10552
|
+
console.error(source_default.red(`--scope must be 'user' or 'org', got '${options?.scope}'.`));
|
|
10553
|
+
process.exit(1);
|
|
10554
|
+
}
|
|
10555
|
+
const wantOrgScope = scopeOpt === "org";
|
|
10556
|
+
const wantUserScope = scopeOpt === "user";
|
|
10471
10557
|
if (wantPrivate && wantPublic) {
|
|
10472
10558
|
console.error(source_default.red("Cannot combine --private and --public. Pick one."));
|
|
10473
10559
|
process.exit(1);
|
|
@@ -10561,10 +10647,19 @@ async function publish(options) {
|
|
|
10561
10647
|
console.log(source_default.dim(" (Org Settings \u2192 Developer), then re-run this command."));
|
|
10562
10648
|
console.log();
|
|
10563
10649
|
}
|
|
10564
|
-
if (
|
|
10650
|
+
if (wantUserScope && creds.publisherKind === "org" && !options?.apiKey) {
|
|
10651
|
+
console.error(source_default.red("--scope=user, but the active publisher in auth.json is an org publisher."));
|
|
10652
|
+
console.error(source_default.dim(" Switch context to personal in the desktop app, or pass --api-key with a personal key."));
|
|
10653
|
+
process.exit(1);
|
|
10654
|
+
}
|
|
10655
|
+
if (wantOrgScope && creds.publisherKind === "user" && !options?.apiKey) {
|
|
10656
|
+
creds = { ...creds, publisherKey: undefined, publisherKind: undefined, publisherName: undefined };
|
|
10657
|
+
}
|
|
10658
|
+
if (wantPrivate && creds.publisherKind !== "org" && !wantOrgScope) {
|
|
10565
10659
|
console.error(source_default.red("--private requires an org publisher key."));
|
|
10566
10660
|
console.error(source_default.dim(" Personal publishes are always public."));
|
|
10567
|
-
console.error(source_default.dim("
|
|
10661
|
+
console.error(source_default.dim(" Pass --scope=org to publish as your active org, or enrol an org"));
|
|
10662
|
+
console.error(source_default.dim(" from the desktop app (Org Settings \u2192 Developer) first."));
|
|
10568
10663
|
process.exit(1);
|
|
10569
10664
|
}
|
|
10570
10665
|
console.log();
|
|
@@ -10609,12 +10704,24 @@ async function publish(options) {
|
|
|
10609
10704
|
const uploadSpinner = ora("Uploading & building...").start();
|
|
10610
10705
|
try {
|
|
10611
10706
|
const explicitKey = options?.apiKey || process.env.CONSTRUCT_PUBLISHER_KEY;
|
|
10612
|
-
|
|
10707
|
+
let initialKey = explicitKey || creds.publisherKey;
|
|
10708
|
+
if (wantOrgScope && !explicitKey && !initialKey) {
|
|
10709
|
+
uploadSpinner.text = "Fetching org publisher key\u2026";
|
|
10710
|
+
const orgKey = await fetchOrgPublisherKey(creds.portal, creds.token);
|
|
10711
|
+
if (!orgKey) {
|
|
10712
|
+
uploadSpinner.fail("No org publisher available for --scope=org");
|
|
10713
|
+
console.error(source_default.dim(" Switch into an org context in the desktop app, or pass --api-key"));
|
|
10714
|
+
console.error(source_default.dim(" with a csk_live_* key the org owns."));
|
|
10715
|
+
unlinkSync2(tarballPath);
|
|
10716
|
+
process.exit(1);
|
|
10717
|
+
}
|
|
10718
|
+
initialKey = orgKey;
|
|
10719
|
+
}
|
|
10613
10720
|
let result;
|
|
10614
10721
|
try {
|
|
10615
10722
|
result = await uploadSource(creds.portal, creds.token, initialKey, tarballPath, m, { private: wantPrivate, public: wantPublic });
|
|
10616
10723
|
} catch (e) {
|
|
10617
|
-
if (e?.ownerKind === "org" && !creds.publisherKey) {
|
|
10724
|
+
if (e?.ownerKind === "org" && !creds.publisherKey && !wantUserScope) {
|
|
10618
10725
|
uploadSpinner.text = "Fetching org publisher key\u2026";
|
|
10619
10726
|
const orgKey = await fetchOrgPublisherKey(creds.portal, creds.token);
|
|
10620
10727
|
if (!orgKey)
|
|
@@ -11593,7 +11700,7 @@ function graphFork(newSpaceID) {
|
|
|
11593
11700
|
// package.json
|
|
11594
11701
|
var package_default = {
|
|
11595
11702
|
name: "@construct-space/cli",
|
|
11596
|
-
version: "1.
|
|
11703
|
+
version: "1.9.1",
|
|
11597
11704
|
description: "Construct CLI \u2014 scaffold, build, develop, and publish spaces",
|
|
11598
11705
|
type: "module",
|
|
11599
11706
|
bin: {
|
|
@@ -11645,7 +11752,7 @@ program2.command("scaffold [name]").alias("new").alias("create").description("Cr
|
|
|
11645
11752
|
program2.command("build").description("Build the space (generate entry + run Vite)").option("--entry-only", "Only generate src/entry.ts").action(async (opts) => build(opts));
|
|
11646
11753
|
program2.command("dev").description("Start dev mode with file watching and live reload").action(async () => dev());
|
|
11647
11754
|
program2.command("install").alias("run").description("Install built space to Construct spaces directory").action(() => install());
|
|
11648
|
-
program2.command("publish").description("Publish a space to the Construct registry").option("-y, --yes", "Skip all confirmation prompts").option("--bump <type>", "Auto-bump version (patch, minor, major)").option("--private", "Publish as org-private (catalog-listed only inside the owning org). Requires an org publisher key.").option("--public", "Flip a previously-private space back to the public catalog on this publish. Without --private or --public, visibility is preserved on update (and defaults to public on first publish).").option("--api-key <key>", "Publisher API key (csk_live_\u2026). Overrides any key stored in the active profile. Also reads CONSTRUCT_PUBLISHER_KEY.").action(async (opts) => publish(opts));
|
|
11755
|
+
program2.command("publish").description("Publish a space to the Construct registry").option("-y, --yes", "Skip all confirmation prompts").option("--bump <type>", "Auto-bump version (patch, minor, major)").option("--private", "Publish as org-private (catalog-listed only inside the owning org). Requires an org publisher key.").option("--public", "Flip a previously-private space back to the public catalog on this publish. Without --private or --public, visibility is preserved on update (and defaults to public on first publish).").option("--scope <scope>", "Publish as 'user' (personal) or 'org'. Without this flag, the active publisher in auth.json decides.").option("--api-key <key>", "Publisher API key (csk_live_\u2026). Overrides any key stored in the active profile. Also reads CONSTRUCT_PUBLISHER_KEY.").action(async (opts) => publish(opts));
|
|
11649
11756
|
program2.command("validate").description("Validate space.manifest.json").action(() => validate3());
|
|
11650
11757
|
program2.command("check").description("Run type-check (vue-tsc) and linter (eslint)").action(() => check());
|
|
11651
11758
|
program2.command("clean").description("Remove build artifacts").option("--all", "Also remove node_modules and lockfiles").action((opts) => clean(opts));
|
|
@@ -11653,6 +11760,8 @@ program2.command("login").description("Authenticate with Construct").option("--p
|
|
|
11653
11760
|
program2.command("logout").description("Sign out").action(() => logout());
|
|
11654
11761
|
program2.command("update").description("Update the CLI to the latest version").action(() => update());
|
|
11655
11762
|
program2.command("whoami").alias("status").description("Show the signed-in user + current org scope").action(async () => (await Promise.resolve().then(() => (init_whoami(), exports_whoami))).whoami());
|
|
11763
|
+
var org = program2.command("org").description("Inspect the active organization context");
|
|
11764
|
+
org.command("status").description("Show active scope, publisher, and roles").action(async () => (await Promise.resolve().then(() => (init_org(), exports_org))).orgStatus());
|
|
11656
11765
|
var graph = program2.command("graph").description("Construct Graph \u2014 data models and GraphQL");
|
|
11657
11766
|
graph.command("init").description("Initialize Graph in a space project").action(() => graphInit());
|
|
11658
11767
|
graph.command("generate <model> [fields...]").alias("g").description("Generate a data model").option("--access <rules>", "Access rules (e.g. read:member,create:member,update:owner,delete:admin)").action((model, fields, opts) => generate2(model, fields, opts));
|
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
id: {{.ID}}-data
|
|
3
|
-
name: {{.DisplayName}} Data Management
|
|
4
|
-
description: Skill for managing {{.DisplayName}} data and content
|
|
5
|
-
trigger: {{.ID}} data|content|manage
|
|
6
|
-
category: space
|
|
7
|
-
tools: [read_file, list_dir, glob, grep, write_file]
|
|
8
|
-
---
|
|
1
|
+
# {{.DisplayName}} — data
|
|
9
2
|
|
|
10
|
-
|
|
3
|
+
Reference doc for the agent when working with {{.DisplayName}} data.
|
|
11
4
|
|
|
12
|
-
|
|
5
|
+
## Storage model
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
7
|
+
Describe where {{.DisplayName}} data lives (Graph models, local storage, server API…). Edit this section to match the actual model.
|
|
8
|
+
|
|
9
|
+
## CRUD conventions
|
|
10
|
+
|
|
11
|
+
- Validate inputs before writing
|
|
12
|
+
- Prefer the space's actions API over raw file or DB writes
|
|
13
|
+
- Batch bulk changes when possible
|
|
14
|
+
- Read existing rows before modifying — changes should be additive when in doubt
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
- Use
|
|
17
|
-
-
|
|
18
|
-
|
|
1
|
+
# {{.DisplayName}} — UI
|
|
2
|
+
|
|
3
|
+
Reference doc for the agent when adjusting {{.DisplayName}}'s UI.
|
|
4
|
+
|
|
5
|
+
## Design tokens
|
|
6
|
+
|
|
7
|
+
Use Construct CSS variables instead of hardcoded values:
|
|
8
|
+
|
|
9
|
+
- `--app-foreground`, `--app-background`
|
|
10
|
+
- `--app-accent`, `--app-accent-fg`
|
|
11
|
+
- `--app-border`, `--app-muted`
|
|
12
|
+
|
|
13
|
+
## Layout
|
|
14
|
+
|
|
15
|
+
- Live inside the host shell — avoid full-bleed layouts that conflict with the sidebar/header
|
|
16
|
+
- Use the `@construct-space/ui` primitives (`Card`, `Button`, `Badge`, …) for consistency
|
|
17
|
+
- Prefer flex/grid over absolute positioning
|
|
18
|
+
|
|
19
|
+
## Accessibility
|
|
20
|
+
|
|
21
|
+
- Maintain 4.5:1 contrast for body text
|
|
22
|
+
- Visible focus rings on every interactive element
|
|
23
|
+
- Don't rely on color alone for state
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
trigger: {{.ID}}|help
|
|
2
|
+
name: {{.ID}}
|
|
3
|
+
description: Help the user with tasks inside the {{.DisplayName}} space. Use when the user is working in {{.DisplayName}}, mentions {{.ID}}-related concepts, or asks what this space can do.
|
|
4
|
+
allowed-tools: Read Glob Grep
|
|
6
5
|
category: space
|
|
7
|
-
|
|
6
|
+
scope: any
|
|
8
7
|
---
|
|
9
8
|
|
|
10
|
-
# {{.DisplayName}}
|
|
9
|
+
# {{.DisplayName}}
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
Help the user with tasks inside the {{.DisplayName}} space.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- The user is inside the {{.DisplayName}} space
|
|
16
|
+
- The user types `{{.ID}}` or asks about {{.DisplayName}}-related work
|
|
17
|
+
- The user invokes a {{.DisplayName}} action
|
|
13
18
|
|
|
14
19
|
## Instructions
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
20
|
+
|
|
21
|
+
- Read existing files in the space before suggesting changes
|
|
22
|
+
- Prefer space actions (see `src/actions.ts`) over generic file edits
|
|
23
|
+
- Surface the space's conventions when proposing new code
|
|
24
|
+
|
|
25
|
+
## Resources
|
|
26
|
+
|
|
27
|
+
The agent loads these on demand:
|
|
28
|
+
|
|
29
|
+
- `references/` — domain-specific docs (data model, UI conventions, …)
|
|
30
|
+
- `scripts/` — supporting executables
|
|
31
|
+
- `assets/` — templates and static resources
|
|
@@ -7,7 +7,16 @@ import { resolve } from 'path'
|
|
|
7
7
|
* Host-provided externals — these are supplied by Construct at runtime
|
|
8
8
|
* via window.__CONSTRUCT__. Do NOT bundle them; they must stay external.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Source of truth: construct-app/frontend/lib/spaceHost.ts
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT — only add a package here AFTER confirming spaceHost.ts
|
|
13
|
+
* actually puts it on window.__CONSTRUCT__. Externalising a package the
|
|
14
|
+
* host doesn't expose silently substitutes `undefined`; you'll see
|
|
15
|
+
* "X is not a function" at runtime. When in doubt, bundle it (omit from
|
|
16
|
+
* this list) — bundle bloat is cheaper than a broken space.
|
|
17
|
+
*
|
|
18
|
+
* Notably NOT externalised:
|
|
19
|
+
* @construct-space/graph — host does not expose this. Bundle it.
|
|
11
20
|
*/
|
|
12
21
|
const hostExternals = [
|
|
13
22
|
'vue',
|
|
@@ -21,7 +30,6 @@ const hostExternals = [
|
|
|
21
30
|
'zod',
|
|
22
31
|
'@construct-space/ui',
|
|
23
32
|
'@construct-space/sdk',
|
|
24
|
-
'@construct-space/graph',
|
|
25
33
|
'@construct/sdk', // legacy alias — keep during cutover
|
|
26
34
|
]
|
|
27
35
|
|
package/package.json
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
id: {{.ID}}-data
|
|
3
|
-
name: {{.DisplayName}} Data Management
|
|
4
|
-
description: Skill for managing {{.DisplayName}} data and content
|
|
5
|
-
trigger: {{.ID}} data|content|manage
|
|
6
|
-
category: space
|
|
7
|
-
tools: [read_file, list_dir, glob, grep, write_file]
|
|
8
|
-
---
|
|
1
|
+
# {{.DisplayName}} — data
|
|
9
2
|
|
|
10
|
-
|
|
3
|
+
Reference doc for the agent when working with {{.DisplayName}} data.
|
|
11
4
|
|
|
12
|
-
|
|
5
|
+
## Storage model
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
7
|
+
Describe where {{.DisplayName}} data lives (Graph models, local storage, server API…). Edit this section to match the actual model.
|
|
8
|
+
|
|
9
|
+
## CRUD conventions
|
|
10
|
+
|
|
11
|
+
- Validate inputs before writing
|
|
12
|
+
- Prefer the space's actions API over raw file or DB writes
|
|
13
|
+
- Batch bulk changes when possible
|
|
14
|
+
- Read existing rows before modifying — changes should be additive when in doubt
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
- Use
|
|
17
|
-
-
|
|
18
|
-
|
|
1
|
+
# {{.DisplayName}} — UI
|
|
2
|
+
|
|
3
|
+
Reference doc for the agent when adjusting {{.DisplayName}}'s UI.
|
|
4
|
+
|
|
5
|
+
## Design tokens
|
|
6
|
+
|
|
7
|
+
Use Construct CSS variables instead of hardcoded values:
|
|
8
|
+
|
|
9
|
+
- `--app-foreground`, `--app-background`
|
|
10
|
+
- `--app-accent`, `--app-accent-fg`
|
|
11
|
+
- `--app-border`, `--app-muted`
|
|
12
|
+
|
|
13
|
+
## Layout
|
|
14
|
+
|
|
15
|
+
- Live inside the host shell — avoid full-bleed layouts that conflict with the sidebar/header
|
|
16
|
+
- Use the `@construct-space/ui` primitives (`Card`, `Button`, `Badge`, …) for consistency
|
|
17
|
+
- Prefer flex/grid over absolute positioning
|
|
18
|
+
|
|
19
|
+
## Accessibility
|
|
20
|
+
|
|
21
|
+
- Maintain 4.5:1 contrast for body text
|
|
22
|
+
- Visible focus rings on every interactive element
|
|
23
|
+
- Don't rely on color alone for state
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
trigger: {{.ID}}|help
|
|
2
|
+
name: {{.ID}}
|
|
3
|
+
description: Help the user with tasks inside the {{.DisplayName}} space. Use when the user is working in {{.DisplayName}}, mentions {{.ID}}-related concepts, or asks what this space can do.
|
|
4
|
+
allowed-tools: Read Glob Grep
|
|
6
5
|
category: space
|
|
7
|
-
|
|
6
|
+
scope: any
|
|
8
7
|
---
|
|
9
8
|
|
|
10
|
-
# {{.DisplayName}}
|
|
9
|
+
# {{.DisplayName}}
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
Help the user with tasks inside the {{.DisplayName}} space.
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
- The user is inside the {{.DisplayName}} space
|
|
16
|
+
- The user types `{{.ID}}` or asks about {{.DisplayName}}-related work
|
|
17
|
+
- The user invokes a {{.DisplayName}} action
|
|
13
18
|
|
|
14
19
|
## Instructions
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
20
|
+
|
|
21
|
+
- Read existing files in the space before suggesting changes
|
|
22
|
+
- Prefer space actions (see `src/actions.ts`) over generic file edits
|
|
23
|
+
- Surface the space's conventions when proposing new code
|
|
24
|
+
|
|
25
|
+
## Resources
|
|
26
|
+
|
|
27
|
+
The agent loads these on demand:
|
|
28
|
+
|
|
29
|
+
- `references/` — domain-specific docs (data model, UI conventions, …)
|
|
30
|
+
- `scripts/` — supporting executables
|
|
31
|
+
- `assets/` — templates and static resources
|
|
@@ -7,7 +7,16 @@ import { resolve } from 'path'
|
|
|
7
7
|
* Host-provided externals — these are supplied by Construct at runtime
|
|
8
8
|
* via window.__CONSTRUCT__. Do NOT bundle them; they must stay external.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Source of truth: construct-app/frontend/lib/spaceHost.ts
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT — only add a package here AFTER confirming spaceHost.ts
|
|
13
|
+
* actually puts it on window.__CONSTRUCT__. Externalising a package the
|
|
14
|
+
* host doesn't expose silently substitutes `undefined`; you'll see
|
|
15
|
+
* "X is not a function" at runtime. When in doubt, bundle it (omit from
|
|
16
|
+
* this list) — bundle bloat is cheaper than a broken space.
|
|
17
|
+
*
|
|
18
|
+
* Notably NOT externalised:
|
|
19
|
+
* @construct-space/graph — host does not expose this. Bundle it.
|
|
11
20
|
*/
|
|
12
21
|
const hostExternals = [
|
|
13
22
|
'vue',
|
|
@@ -21,7 +30,6 @@ const hostExternals = [
|
|
|
21
30
|
'zod',
|
|
22
31
|
'@construct-space/ui',
|
|
23
32
|
'@construct-space/sdk',
|
|
24
|
-
'@construct-space/graph',
|
|
25
33
|
'@construct/sdk', // legacy alias — keep during cutover
|
|
26
34
|
]
|
|
27
35
|
|