cue-ai 0.5.0 → 0.7.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.
- package/README.md +757 -110
- package/package.json +5 -5
- package/profiles/README.md +12 -12
- package/profiles/SCHEMA.md +31 -3
- package/profiles/_cache/README.md +1 -1
- package/profiles/_types.ts +26 -1
- package/profiles/backend/profile.yaml +1 -0
- package/profiles/career/profile.yaml +13 -0
- package/profiles/core/profile.yaml +76 -9
- package/profiles/creative-media/README.md +1 -1
- package/profiles/cybersecurity/profile.yaml +779 -756
- package/profiles/ecc/profile.yaml +39 -0
- package/profiles/event-design/profile.yaml +10 -0
- package/profiles/fleet-control/README.md +1 -1
- package/profiles/frontend/profile.yaml +14 -0
- package/profiles/full/README.md +1 -1
- package/profiles/go-api/profile.yaml +1 -0
- package/profiles/marketing/profile.yaml +12 -1
- package/profiles/predict-everything/profile.yaml +9 -0
- package/profiles/rust/profile.yaml +22 -3
- package/profiles/rust-cli/profile.yaml +14 -0
- package/profiles/rust-core/profile.yaml +35 -0
- package/profiles/rust-embedded/profile.yaml +11 -0
- package/profiles/rust-ffi/profile.yaml +13 -0
- package/profiles/rust-game/profile.yaml +11 -0
- package/profiles/rust-wasm/profile.yaml +11 -0
- package/profiles/rust-web/profile.yaml +17 -0
- package/profiles/schema.json +44 -4
- package/profiles/trendradar/profile.yaml +11 -0
- package/resources/mcps/README.md +39 -164
- package/resources/mcps/configs/claude.sanitized.json +55 -0
- package/resources/mcps/configs/claude_runtime.sanitized.json +47 -0
- package/resources/skills/README.md +70 -113
- package/resources/skills/skills/event-design/wedding-invitations/SKILL.md +43 -0
- package/resources/skills/skills/meta/acpx/SKILL.md +78 -0
- package/resources/skills/skills/meta/cue-usage/SKILL.md +24 -0
- package/resources/skills/skills/meta/profile-fit-monitor/SKILL.md +24 -0
- package/resources/skills/skills/predict-everything/mirofish/SKILL.md +75 -0
- package/resources/skills/skills/research/trendradar/SKILL.md +88 -0
- package/resources/skills/skills/rust/async-tokio/SKILL.md +27 -0
- package/resources/skills/skills/rust/axum-api/SKILL.md +38 -0
- package/resources/skills/skills/rust/bacon-watch/SKILL.md +24 -0
- package/resources/skills/skills/rust/bevy/SKILL.md +43 -0
- package/resources/skills/skills/rust/bindgen/SKILL.md +39 -0
- package/resources/skills/skills/rust/cargo-audit/SKILL.md +26 -0
- package/resources/skills/skills/rust/cargo-basics/SKILL.md +28 -0
- package/resources/skills/skills/rust/cargo-chef/SKILL.md +43 -0
- package/resources/skills/skills/rust/cargo-edit/SKILL.md +26 -0
- package/resources/skills/skills/rust/cargo-expand/SKILL.md +24 -0
- package/resources/skills/skills/rust/cargo-flamegraph/SKILL.md +26 -0
- package/resources/skills/skills/rust/cargo-fuzz/SKILL.md +34 -0
- package/resources/skills/skills/rust/cargo-hack/SKILL.md +26 -0
- package/resources/skills/skills/rust/cargo-msrv/SKILL.md +30 -0
- package/resources/skills/skills/rust/cargo-mutants/SKILL.md +26 -0
- package/resources/skills/skills/rust/cargo-nextest/SKILL.md +24 -0
- package/resources/skills/skills/rust/cargo-readme/SKILL.md +36 -0
- package/resources/skills/skills/rust/cbindgen/SKILL.md +41 -0
- package/resources/skills/skills/rust/chisel-tool/SKILL.md +32 -0
- package/resources/skills/skills/rust/clap-cli/SKILL.md +44 -0
- package/resources/skills/skills/rust/clippy-and-fmt/SKILL.md +25 -0
- package/resources/skills/skills/rust/cross-compile/SKILL.md +26 -0
- package/resources/skills/skills/rust/embedded/SKILL.md +33 -0
- package/resources/skills/skills/rust/error-handling/SKILL.md +32 -0
- package/resources/skills/skills/rust/just-runner/SKILL.md +26 -0
- package/resources/skills/skills/rust/mdbook/SKILL.md +25 -0
- package/resources/skills/skills/rust/napi-rs/SKILL.md +32 -0
- package/resources/skills/skills/rust/no-std/SKILL.md +42 -0
- package/resources/skills/skills/rust/property-testing/SKILL.md +35 -0
- package/resources/skills/skills/rust/pyo3/SKILL.md +40 -0
- package/resources/skills/skills/rust/ratatui-tui/SKILL.md +36 -0
- package/resources/skills/skills/rust/release-plz/SKILL.md +27 -0
- package/resources/skills/skills/rust/reqwest/SKILL.md +37 -0
- package/resources/skills/skills/rust/sccache/SKILL.md +28 -0
- package/resources/skills/skills/rust/serde/SKILL.md +30 -0
- package/resources/skills/skills/rust/snapshot-testing/SKILL.md +30 -0
- package/resources/skills/skills/rust/sqlx-cli/SKILL.md +33 -0
- package/resources/skills/skills/rust/tracing/SKILL.md +36 -0
- package/resources/skills/skills/rust/typos-spellcheck/SKILL.md +31 -0
- package/resources/skills/skills/rust/uniffi/SKILL.md +38 -0
- package/resources/skills/skills/rust/wasm-rust/SKILL.md +27 -0
- package/resources/skills/skills/security/agentshield/SKILL.md +119 -0
- package/src/commands/_index.ts +47 -3
- package/src/commands/cli.test.ts +192 -0
- package/src/commands/cli.ts +303 -0
- package/src/commands/current.ts +1 -1
- package/src/commands/debug.test.ts +62 -0
- package/src/commands/debug.ts +212 -0
- package/src/commands/discover.scoring.test.ts +216 -0
- package/src/commands/discover.test.ts +145 -0
- package/src/commands/discover.ts +2618 -0
- package/src/commands/eval-behavior.test.ts +56 -0
- package/src/commands/eval-behavior.ts +189 -0
- package/src/commands/eval.test.ts +102 -0
- package/src/commands/eval.ts +348 -0
- package/src/commands/evolve.ts +291 -0
- package/src/commands/failures.test.ts +78 -0
- package/src/commands/failures.ts +393 -0
- package/src/commands/feedback.ts +219 -0
- package/src/commands/init.ts +26 -0
- package/src/commands/launch.e2e.test.ts +9 -1
- package/src/commands/launch.ts +174 -11
- package/src/commands/lint-skill.ts +157 -0
- package/src/commands/marketplace.ts +763 -2
- package/src/commands/new.ts +1 -1
- package/src/commands/optimizer.ts +92 -28
- package/src/commands/profile-draft-skill.test.ts +96 -0
- package/src/commands/profile-draft-skill.ts +287 -0
- package/src/commands/profile-evolve.test.ts +126 -0
- package/src/commands/profile-evolve.ts +0 -0
- package/src/commands/profile-suggest.ts +223 -0
- package/src/commands/profile.ts +41 -0
- package/src/commands/quick.ts +2 -17
- package/src/commands/scan.ts +2 -2
- package/src/commands/score.ts +1 -1
- package/src/commands/share.ts +1 -1
- package/src/commands/sources.ts +2 -2
- package/src/commands/submit-profile.ts +262 -0
- package/src/commands/upgrade.ts +1 -1
- package/src/commands/use.ts +35 -5
- package/src/commands/validate.ts +1 -1
- package/src/index.ts +66 -0
- package/src/lib/analytics.ts +48 -2
- package/src/lib/claude-binary.ts +39 -0
- package/src/lib/cli-extractor.ts +77 -0
- package/src/lib/cluster-skills.test.ts +268 -0
- package/src/lib/cluster-skills.ts +290 -0
- package/src/lib/credentials-sync.test.ts +208 -0
- package/src/lib/credentials-sync.ts +205 -0
- package/src/lib/mcp-materializer.test.ts +1 -1
- package/src/lib/persona-playbooks.test.ts +111 -0
- package/src/lib/pr-poster.test.ts +243 -0
- package/src/lib/pr-poster.ts +285 -0
- package/src/lib/pr-throttle.test.ts +148 -0
- package/src/lib/pr-throttle.ts +209 -0
- package/src/lib/profile-generator.test.ts +1 -1
- package/src/lib/profile-generator.ts +2 -2
- package/src/lib/profile-linter.test.ts +6 -3
- package/src/lib/profile-linter.ts +71 -8
- package/src/lib/profile-loader.test.ts +1 -1
- package/src/lib/profile-loader.ts +16 -0
- package/src/lib/resolver-local.test.ts +1 -1
- package/src/lib/resolver-npx.test.ts +76 -1
- package/src/lib/resolver-npx.ts +35 -3
- package/src/lib/resolver-plugins.test.ts +1 -1
- package/src/lib/runtime-materializer.test.ts +191 -7
- package/src/lib/runtime-materializer.ts +310 -42
- package/src/lib/scan-plugins.test.ts +1 -1
- package/src/lib/skill-linter.test.ts +174 -0
- package/src/lib/skill-linter.ts +507 -0
- package/src/lib/skill-subset.test.ts +95 -0
- package/src/lib/skill-subset.ts +166 -0
- package/src/lib/star-prompt.ts +1 -1
- package/src/lib/uvx-installer.test.ts +229 -0
- package/src/lib/uvx-installer.ts +278 -0
|
@@ -187,7 +187,7 @@ describe("materializeRuntime", () => {
|
|
|
187
187
|
expect(st.isFile()).toBe(true);
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
-
test("credentialsSource:
|
|
190
|
+
test("credentialsSource: preserves account-level settings but isolates MCPs + plugins per profile", async () => {
|
|
191
191
|
const credSrc = join(root, "creds");
|
|
192
192
|
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
193
193
|
await mkdir(credSrc, { recursive: true });
|
|
@@ -197,8 +197,14 @@ describe("materializeRuntime", () => {
|
|
|
197
197
|
permissions: { allow: ["Bash(*)"], defaultMode: "auto" },
|
|
198
198
|
trustedDirectories: ["/home/user/work"],
|
|
199
199
|
skipAutoPermissionPrompt: true,
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
// These two MUST NOT leak into the profile runtime — the profile is
|
|
201
|
+
// the sole source of truth for MCPs + plugins. Otherwise every MCP
|
|
202
|
+
// the user has registered globally appears in EVERY profile, defeating
|
|
203
|
+
// isolation. Pinned by this test (regression: profiles like
|
|
204
|
+
// `cybersecurity` with `mcps: []` were inheriting random user-scoped
|
|
205
|
+
// MCPs like `teherguminet-admin` because of the merge).
|
|
206
|
+
enabledPlugins: { "user-globally-installed@marketplace": true },
|
|
207
|
+
mcpServers: { userGloballyInstalledMcp: { command: "x" } },
|
|
202
208
|
}),
|
|
203
209
|
);
|
|
204
210
|
|
|
@@ -213,17 +219,16 @@ describe("materializeRuntime", () => {
|
|
|
213
219
|
});
|
|
214
220
|
|
|
215
221
|
const settings = JSON.parse(await readFile(join(out.runtimeDir, "settings.json"), "utf8"));
|
|
216
|
-
// Account-level settings preserved
|
|
222
|
+
// Account-level settings preserved (these are user-scoped, not profile-scoped)
|
|
217
223
|
expect(settings.permissions).toEqual({ allow: ["Bash(*)"], defaultMode: "auto" });
|
|
218
224
|
expect(settings.trustedDirectories).toEqual(["/home/user/work"]);
|
|
219
225
|
expect(settings.skipAutoPermissionPrompt).toBe(true);
|
|
220
|
-
// Profile plugins/mcps
|
|
226
|
+
// Profile plugins/mcps are EXCLUSIVE — only what the profile declared.
|
|
227
|
+
// The account-level entries from the source settings.json must NOT leak.
|
|
221
228
|
expect(settings.enabledPlugins).toEqual({
|
|
222
|
-
"existing@marketplace": true,
|
|
223
229
|
"frontend-design@claude-plugins-official": true,
|
|
224
230
|
});
|
|
225
231
|
expect(settings.mcpServers).toEqual({
|
|
226
|
-
existingMcp: { command: "x" },
|
|
227
232
|
"claude-mem": { command: "claude-mem" },
|
|
228
233
|
});
|
|
229
234
|
});
|
|
@@ -286,4 +291,183 @@ describe("materializeRuntime", () => {
|
|
|
286
291
|
expect(claudemd).not.toContain("$(date)");
|
|
287
292
|
expect(claudemd).toMatch(/generated \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
288
293
|
});
|
|
294
|
+
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
// Rules / commands / hooks — ECC-derived resource paths
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
test("commands: symlinks each ref into commands/ and lists them in CLAUDE.md", async () => {
|
|
300
|
+
// Materializer resolves command refs against <repo>/resources/commands/<ref>.md
|
|
301
|
+
// — we already vendor a few of these, so use a known-good one.
|
|
302
|
+
const profile: ResolvedProfile = {
|
|
303
|
+
...sampleProfile,
|
|
304
|
+
name: "test-cmds",
|
|
305
|
+
inheritanceChain: ["test-cmds"],
|
|
306
|
+
rules: [], hooks: [],
|
|
307
|
+
commands: ["code-review", "checkpoint"],
|
|
308
|
+
};
|
|
309
|
+
const out = await materializeRuntime({
|
|
310
|
+
profile, agent: "claude-code",
|
|
311
|
+
runtimeRoot: join(root, "runtime"),
|
|
312
|
+
skillSourceLookup: async (id) => `/fake/source/${id}`,
|
|
313
|
+
mcpRegistry: {},
|
|
314
|
+
userClaudeMd: "",
|
|
315
|
+
});
|
|
316
|
+
const cmdLink = await readlink(join(out.runtimeDir, "commands", "code-review.md"));
|
|
317
|
+
expect(cmdLink).toContain("resources/commands/code-review.md");
|
|
318
|
+
const claudemd = await readFile(join(out.runtimeDir, "CLAUDE.md"), "utf8");
|
|
319
|
+
expect(claudemd).toContain("## Available Commands");
|
|
320
|
+
expect(claudemd).toContain("/code-review");
|
|
321
|
+
expect(claudemd).toContain("/checkpoint");
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("rules: symlinks into rules/ + writes index (NOT inlined bodies)", async () => {
|
|
325
|
+
const profile: ResolvedProfile = {
|
|
326
|
+
...sampleProfile,
|
|
327
|
+
name: "test-rules",
|
|
328
|
+
inheritanceChain: ["test-rules"],
|
|
329
|
+
commands: [], hooks: [],
|
|
330
|
+
rules: ["common/security", "common/testing"],
|
|
331
|
+
};
|
|
332
|
+
const out = await materializeRuntime({
|
|
333
|
+
profile, agent: "claude-code",
|
|
334
|
+
runtimeRoot: join(root, "runtime"),
|
|
335
|
+
skillSourceLookup: async (id) => `/fake/source/${id}`,
|
|
336
|
+
mcpRegistry: {},
|
|
337
|
+
userClaudeMd: "",
|
|
338
|
+
});
|
|
339
|
+
const link = await readlink(join(out.runtimeDir, "rules", "security.md"));
|
|
340
|
+
expect(link).toContain("resources/rules/common/security.md");
|
|
341
|
+
const claudemd = await readFile(join(out.runtimeDir, "CLAUDE.md"), "utf8");
|
|
342
|
+
// Index reference present, but the rule body must NOT be inlined — the
|
|
343
|
+
// whole point of the symlink-only approach is to skip the token bleed.
|
|
344
|
+
expect(claudemd).toContain("## Rules (2)");
|
|
345
|
+
expect(claudemd).toContain("`rules/security.md`");
|
|
346
|
+
expect(claudemd).not.toMatch(/^## Security Review Triggers/m);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("hooks: merges hook JSON into settings.json under matching event keys", async () => {
|
|
350
|
+
const profile: ResolvedProfile = {
|
|
351
|
+
...sampleProfile,
|
|
352
|
+
name: "test-hooks",
|
|
353
|
+
inheritanceChain: ["test-hooks"],
|
|
354
|
+
rules: [], commands: [],
|
|
355
|
+
hooks: ["bash-quality-preflight.json", "session-summary.json"],
|
|
356
|
+
};
|
|
357
|
+
const out = await materializeRuntime({
|
|
358
|
+
profile, agent: "claude-code",
|
|
359
|
+
runtimeRoot: join(root, "runtime"),
|
|
360
|
+
skillSourceLookup: async (id) => `/fake/source/${id}`,
|
|
361
|
+
mcpRegistry: {},
|
|
362
|
+
userClaudeMd: "",
|
|
363
|
+
});
|
|
364
|
+
const settings = JSON.parse(await readFile(join(out.runtimeDir, "settings.json"), "utf8"));
|
|
365
|
+
expect(settings.hooks.PreToolUse).toBeArray();
|
|
366
|
+
expect(settings.hooks.PreToolUse[0].matcher).toBe("Bash");
|
|
367
|
+
expect(settings.hooks.Stop).toBeArray();
|
|
368
|
+
expect(settings.hooks.Stop[0].hooks[0].id).toBe("cue:stop:session-summary");
|
|
369
|
+
// Symlinks also created under hooks/
|
|
370
|
+
const link = await readlink(join(out.runtimeDir, "hooks", "bash-quality-preflight.json"));
|
|
371
|
+
expect(link).toContain("resources/hooks/bash-quality-preflight.json");
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Claude Code reads MCP servers from .claude.json (top-level `mcpServers`),
|
|
375
|
+
// NOT from settings.json. The materializer must therefore merge profile MCPs
|
|
376
|
+
// into .claude.json — and copy (not symlink) it so mutations don't leak back
|
|
377
|
+
// into the shared account file.
|
|
378
|
+
test("merges profile MCPs into .claude.json + copies (not symlinks) it", async () => {
|
|
379
|
+
const credSrc = join(root, "creds");
|
|
380
|
+
const { mkdir, writeFile, lstat } = await import("node:fs/promises");
|
|
381
|
+
await mkdir(credSrc, { recursive: true });
|
|
382
|
+
await writeFile(
|
|
383
|
+
join(credSrc, ".claude.json"),
|
|
384
|
+
JSON.stringify({
|
|
385
|
+
numStartups: 42,
|
|
386
|
+
oauthAccount: { emailAddress: "u@example.com" },
|
|
387
|
+
mcpServers: { "preexisting": { command: "/bin/pre" } },
|
|
388
|
+
}),
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const out = await materializeRuntime({
|
|
392
|
+
profile: sampleProfile,
|
|
393
|
+
agent: "claude-code",
|
|
394
|
+
runtimeRoot: join(root, "runtime"),
|
|
395
|
+
skillSourceLookup: async (id) => `/fake/source/${id}`,
|
|
396
|
+
mcpRegistry: { "claude-mem": { command: "claude-mem", args: [] } },
|
|
397
|
+
userClaudeMd: "",
|
|
398
|
+
credentialsSource: credSrc,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Must be a real file, not a symlink — otherwise mutations leak back to
|
|
402
|
+
// the source account file and pollute other profiles sharing the account.
|
|
403
|
+
const st = await lstat(join(out.runtimeDir, ".claude.json"));
|
|
404
|
+
expect(st.isSymbolicLink()).toBe(false);
|
|
405
|
+
expect(st.isFile()).toBe(true);
|
|
406
|
+
|
|
407
|
+
// Profile MCPs merged in under top-level `mcpServers`, preserving the
|
|
408
|
+
// source's preexisting entries and other top-level fields.
|
|
409
|
+
const cj = JSON.parse(await readFile(join(out.runtimeDir, ".claude.json"), "utf8"));
|
|
410
|
+
expect(cj.mcpServers["claude-mem"]).toEqual({ command: "claude-mem", args: [] });
|
|
411
|
+
expect(cj.mcpServers["preexisting"]).toEqual({ command: "/bin/pre" });
|
|
412
|
+
expect(cj.numStartups).toBe(42);
|
|
413
|
+
expect(cj.oauthAccount).toEqual({ emailAddress: "u@example.com" });
|
|
414
|
+
|
|
415
|
+
// Source .claude.json untouched — proof the copy isolates per-profile writes.
|
|
416
|
+
const src = JSON.parse(await readFile(join(credSrc, ".claude.json"), "utf8"));
|
|
417
|
+
expect(src.mcpServers).toEqual({ "preexisting": { command: "/bin/pre" } });
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Cache-hit path must also re-sync MCPs into .claude.json, so adding/removing
|
|
421
|
+
// an MCP to a profile takes effect even when the profile hash hasn't changed
|
|
422
|
+
// for unrelated reasons. (In practice, adding an MCP changes the hash — but
|
|
423
|
+
// an account swap with a different source .claude.json triggers a cache hit.)
|
|
424
|
+
test("cache hit: refreshes .claude.json mcpServers from current registry", async () => {
|
|
425
|
+
const credSrc = join(root, "creds");
|
|
426
|
+
const { mkdir, writeFile } = await import("node:fs/promises");
|
|
427
|
+
await mkdir(credSrc, { recursive: true });
|
|
428
|
+
await writeFile(join(credSrc, ".claude.json"), JSON.stringify({ numStartups: 1 }));
|
|
429
|
+
|
|
430
|
+
const args = {
|
|
431
|
+
profile: sampleProfile,
|
|
432
|
+
agent: "claude-code" as const,
|
|
433
|
+
runtimeRoot: join(root, "runtime"),
|
|
434
|
+
skillSourceLookup: async (id: string) => `/fake/source/${id}`,
|
|
435
|
+
mcpRegistry: { "claude-mem": { command: "claude-mem-v1" } },
|
|
436
|
+
userClaudeMd: "",
|
|
437
|
+
credentialsSource: credSrc,
|
|
438
|
+
};
|
|
439
|
+
await materializeRuntime(args);
|
|
440
|
+
|
|
441
|
+
// Second build: same profile (hash hit) but registry changed.
|
|
442
|
+
const second = await materializeRuntime({
|
|
443
|
+
...args,
|
|
444
|
+
mcpRegistry: { "claude-mem": { command: "claude-mem-v2" } },
|
|
445
|
+
});
|
|
446
|
+
expect(second.rebuilt).toBe(false);
|
|
447
|
+
|
|
448
|
+
const cj = JSON.parse(await readFile(join(second.runtimeDir, ".claude.json"), "utf8"));
|
|
449
|
+
expect(cj.mcpServers["claude-mem"]).toEqual({ command: "claude-mem-v2" });
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
test("missing rule/command/hook ref is non-fatal", async () => {
|
|
453
|
+
const profile: ResolvedProfile = {
|
|
454
|
+
...sampleProfile,
|
|
455
|
+
name: "test-missing",
|
|
456
|
+
inheritanceChain: ["test-missing"],
|
|
457
|
+
rules: ["does/not/exist"],
|
|
458
|
+
commands: ["ghost-command"],
|
|
459
|
+
hooks: ["nope.json"],
|
|
460
|
+
};
|
|
461
|
+
const out = await materializeRuntime({
|
|
462
|
+
profile, agent: "claude-code",
|
|
463
|
+
runtimeRoot: join(root, "runtime"),
|
|
464
|
+
skillSourceLookup: async (id) => `/fake/source/${id}`,
|
|
465
|
+
mcpRegistry: {},
|
|
466
|
+
userClaudeMd: "",
|
|
467
|
+
});
|
|
468
|
+
expect(out.rebuilt).toBe(true);
|
|
469
|
+
// No symlinks created for missing refs — directories may exist but be empty.
|
|
470
|
+
const settings = JSON.parse(await readFile(join(out.runtimeDir, "settings.json"), "utf8"));
|
|
471
|
+
expect(settings.hooks).toBeUndefined();
|
|
472
|
+
});
|
|
289
473
|
});
|