quiver-cli 1.2.0 → 1.3.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/cli.js +237 -10
- package/package.json +1 -1
- package/template/.agents/skills/agent-browser/SKILL.md +1 -0
- package/template/.agents/skills/apps/skybridge/SKILL.md +2 -0
- package/template/.agents/skills/data/prisma-cli/SKILL.md +25 -7
- package/template/.agents/skills/data/prisma-cli/references/agent-safety.md +27 -0
- package/template/.agents/skills/data/prisma-cli/references/complete.md +22 -0
- package/template/.agents/skills/data/prisma-cli/references/db-push.md +2 -0
- package/template/.agents/skills/data/prisma-cli/references/init.md +3 -0
- package/template/.agents/skills/data/prisma-cli/references/mcp.md +2 -1
- package/template/.agents/skills/data/prisma-cli/references/migrate-dev.md +1 -1
- package/template/.agents/skills/data/prisma-cli/references/migrate-reset.md +2 -0
- package/template/.agents/skills/data/prisma-client-api/SKILL.md +1 -1
- package/template/.agents/skills/data/prisma-client-api/references/constructor.md +13 -0
- package/template/.agents/skills/data/prisma-client-api/references/raw-queries.md +4 -0
- package/template/.agents/skills/design/impeccable/SKILL.md +5 -5
- package/template/.agents/skills/design/impeccable/reference/android.md +6 -0
- package/template/.agents/skills/design/impeccable/reference/animate.md +3 -0
- package/template/.agents/skills/design/impeccable/reference/bolder.md +3 -1
- package/template/.agents/skills/design/impeccable/reference/craft-floor.md +2 -0
- package/template/.agents/skills/design/impeccable/reference/critique.md +23 -5
- package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +15 -68
- package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +13 -12
- package/template/.agents/skills/design/impeccable/reference/distill.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/doctor.md +1 -0
- package/template/.agents/skills/design/impeccable/reference/document.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/extract.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/hooks.md +17 -11
- package/template/.agents/skills/design/impeccable/reference/init.md +9 -3
- package/template/.agents/skills/design/impeccable/reference/ios.md +6 -0
- package/template/.agents/skills/design/impeccable/reference/new-work.md +69 -29
- package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/polish.md +13 -5
- package/template/.agents/skills/design/impeccable/reference/quieter.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/routing.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/visualize.md +21 -22
- package/template/.agents/skills/design/impeccable/scripts/build-phase.mjs +1022 -0
- package/template/.agents/skills/design/impeccable/scripts/comp-diff.mjs +391 -0
- package/template/.agents/skills/design/impeccable/scripts/comp-spec.mjs +513 -0
- package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +297 -41
- package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +10 -19
- package/template/.agents/skills/design/impeccable/scripts/context.mjs +124 -9
- package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +279 -19
- package/template/.agents/skills/design/impeccable/scripts/data/font-index-failures.json +121 -0
- package/template/.agents/skills/design/impeccable/scripts/data/font-index.json +1 -0
- package/template/.agents/skills/design/impeccable/scripts/detect.mjs +9 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +192 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +10 -16
- package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +339 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +1482 -722
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +1 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +64 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +580 -29
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +62 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +31 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +23 -22
- package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +18 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +471 -370
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/color.mjs +474 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +17 -2
- package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +14 -21
- package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +81 -48
- package/template/.agents/skills/design/impeccable/scripts/font-match.mjs +457 -0
- package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +219 -12
- package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +93 -15
- package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +41 -19
- package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +507 -117
- package/template/.agents/skills/design/impeccable/scripts/hook.mjs +10 -9
- package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +40 -1
- package/template/.agents/skills/design/impeccable/scripts/lib/design-parser.mjs +120 -82
- package/template/.agents/skills/design/impeccable/scripts/lib/font-fingerprint.mjs +564 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/font-index.mjs +130 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/hero-checks.mjs +246 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/image-metrics.mjs +306 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +41 -59
- package/template/.agents/skills/design/impeccable/scripts/lib/is-generated.mjs +5 -2
- package/template/.agents/skills/design/impeccable/scripts/lib/live-path-globs.mjs +37 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/open-system-browser.mjs +26 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/png.mjs +281 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/raster.mjs +194 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +26 -19
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +31 -3
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +93 -17
- package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +9 -11
- package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +31 -2
- package/template/.agents/skills/design/impeccable/scripts/live/project-ignores.mjs +139 -0
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +10 -2
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +26 -2
- package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +15 -27
- package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +4 -25
- package/template/.agents/skills/design/impeccable/scripts/live/ui-surfaces.mjs +75 -0
- package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +21 -37
- package/template/.agents/skills/design/impeccable/scripts/live-browser-ignores.js +242 -0
- package/template/.agents/skills/design/impeccable/scripts/live-browser.js +62 -23
- package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +68 -112
- package/template/.agents/skills/design/impeccable/scripts/live-copy-edit-agent.mjs +132 -15
- package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +2 -42
- package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +5 -4
- package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +56 -19
- package/template/.agents/skills/design/impeccable/scripts/live.mjs +12 -37
- package/template/.agents/skills/design/impeccable/scripts/pin.mjs +8 -5
- package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +1006 -155
- package/template/.agents/skills/design/shadcn/rules/chat.md +26 -0
- package/template/.agents/skills/hono/SKILL.md +17 -3
- package/template/.agents/skills/integrations/langfuse/SKILL.md +8 -4
- package/template/.agents/skills/integrations/langfuse/references/cli.md +1 -1
- package/template/.agents/skills/integrations/langfuse/references/create-dataset.md +35 -0
- package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +9 -10
- package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +8 -6
- package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +34 -0
- package/template/.agents/skills/integrations/langfuse/references/setting-up-evals.md +65 -0
- package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +20 -40
- package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +4 -30
- package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +3 -1
- package/template/.agents/skills/supabase/CHANGELOG.md +7 -0
- package/template/.agents/skills/supabase/SKILL.md +5 -1
- package/template/.agents/skills/writing/humanizer/LICENSE +21 -0
- package/template/.agents/skills/writing/humanizer/README.md +209 -0
- package/template/.agents/skills/writing/humanizer/SKILL.md +173 -338
- package/template/.agents/upstreams.json +21 -22
- package/template/.agents/skills/design/impeccable/scripts/live/ui-core.mjs +0 -180
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
writeLiveServerInfo,
|
|
49
49
|
} from './lib/impeccable-paths.mjs';
|
|
50
50
|
import { countByPage as countPendingByPage } from './live/manual-edits-buffer.mjs';
|
|
51
|
+
import { collectProjectDetectorIgnores } from './live/project-ignores.mjs';
|
|
51
52
|
import {
|
|
52
53
|
createManualApplyController,
|
|
53
54
|
summarizeManualApplyFailures,
|
|
@@ -181,8 +182,16 @@ function chatAgentLikelyActive() {
|
|
|
181
182
|
// cap at 10 MB to guard against runaway writes from a misbehaving client.
|
|
182
183
|
const MAX_ANNOTATION_BYTES = 10 * 1024 * 1024;
|
|
183
184
|
|
|
185
|
+
const POLLER_OWNED_EVENT_FIELDS = ['_instructions', '_completionAck', '_acceptResult'];
|
|
186
|
+
|
|
187
|
+
function stripPollerOwnedEventFields(event) {
|
|
188
|
+
if (!event || typeof event !== 'object') return;
|
|
189
|
+
for (const key of POLLER_OWNED_EVENT_FIELDS) delete event[key];
|
|
190
|
+
}
|
|
191
|
+
|
|
184
192
|
function enqueueEvent(event) {
|
|
185
193
|
if (!event) return;
|
|
194
|
+
stripPollerOwnedEventFields(event);
|
|
186
195
|
// Dedupe by (session, type), except mount failures, which are per-variant:
|
|
187
196
|
// variant 2 failing must not be swallowed because variant 1's failure is
|
|
188
197
|
// still queued.
|
|
@@ -689,16 +698,24 @@ function isLoopbackOrigin(origin) {
|
|
|
689
698
|
function createRequestHandler({ detectScript, liveScriptParts }) {
|
|
690
699
|
return (req, res) => {
|
|
691
700
|
const url = new URL(req.url, `http://localhost:${state.port}`);
|
|
692
|
-
//
|
|
693
|
-
// loopback origin
|
|
694
|
-
//
|
|
695
|
-
//
|
|
696
|
-
//
|
|
697
|
-
//
|
|
698
|
-
//
|
|
699
|
-
//
|
|
701
|
+
// Token-or-loopback CORS. Reflect the caller's Origin when it is a
|
|
702
|
+
// loopback origin OR the request carries the valid session token, always
|
|
703
|
+
// paired with `Vary: Origin` so an intermediary cache never serves a
|
|
704
|
+
// response authorized for one origin to another. A remote page (e.g.
|
|
705
|
+
// https://evil.example probing the port from a tab open on the same
|
|
706
|
+
// machine) has no token and gets no Access-Control-Allow-Origin, so its
|
|
707
|
+
// JS-initiated fetch cannot read any response. The token branch exists for
|
|
708
|
+
// dev servers on non-localhost loopback aliases (ddev's *.ddev.site,
|
|
709
|
+
// Valet's *.test, hosts-file entries): the injected classic <script src>
|
|
710
|
+
// delivers the token to the page regardless of origin, every overlay
|
|
711
|
+
// request carries it in the query string (preflights included, since
|
|
712
|
+
// OPTIONS hits the same URL), and a token bearer is already fully
|
|
713
|
+
// authorized on every route — the token is the security boundary, not the
|
|
714
|
+
// origin. Requests with no Origin header (script tags, curl, the agent's
|
|
715
|
+
// own fetches) are not subject to CORS and keep working; no ACAO header
|
|
716
|
+
// is needed for them.
|
|
700
717
|
const origin = req.headers.origin;
|
|
701
|
-
if (origin && isLoopbackOrigin(origin)) {
|
|
718
|
+
if (origin && (isLoopbackOrigin(origin) || url.searchParams.get('token') === state.token)) {
|
|
702
719
|
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
703
720
|
res.setHeader('Vary', 'Origin');
|
|
704
721
|
}
|
|
@@ -738,9 +755,20 @@ function createRequestHandler({ detectScript, liveScriptParts }) {
|
|
|
738
755
|
commandPrefix: IMPECCABLE_COMMAND_PREFIX,
|
|
739
756
|
appRoot: process.cwd(),
|
|
740
757
|
parts,
|
|
758
|
+
// Read per request rather than cached, so editing the config and
|
|
759
|
+
// reloading the tab is enough to pick up a new waiver. Config comes
|
|
760
|
+
// from every root the session spans (appRoot, contextRoot, repoRoot):
|
|
761
|
+
// in a monorepo the hook and the CLI key it at the repo root, which
|
|
762
|
+
// is not the appRoot this process chdir'd onto.
|
|
763
|
+
projectIgnores: collectProjectDetectorIgnores({
|
|
764
|
+
appRoot: process.cwd(),
|
|
765
|
+
contextRoot: LIVE_ROOTS?.contextRoot,
|
|
766
|
+
repoRoot: LIVE_ROOTS?.repoRoot,
|
|
767
|
+
scriptsDir: __dirname,
|
|
768
|
+
}),
|
|
741
769
|
});
|
|
742
770
|
res.writeHead(200, {
|
|
743
|
-
'Content-Type': 'application/javascript',
|
|
771
|
+
'Content-Type': 'application/javascript; charset=utf-8',
|
|
744
772
|
'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
|
|
745
773
|
'Pragma': 'no-cache',
|
|
746
774
|
});
|
|
@@ -749,7 +777,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) {
|
|
|
749
777
|
}
|
|
750
778
|
if (p === '/detect.js' || p === '/') {
|
|
751
779
|
if (!detectScript) { res.writeHead(404); res.end('Not available'); return; }
|
|
752
|
-
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
|
780
|
+
res.writeHead(200, { 'Content-Type': 'application/javascript; charset=utf-8' });
|
|
753
781
|
res.end(detectScript);
|
|
754
782
|
return;
|
|
755
783
|
}
|
|
@@ -865,7 +893,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) {
|
|
|
865
893
|
// { present, parsed, sidecar, hasMd, hasSidecar,
|
|
866
894
|
// mdNewerThanJson, parseError?, sidecarError? }
|
|
867
895
|
// - parsed: output of parseDesignMd (frontmatter
|
|
868
|
-
// +
|
|
896
|
+
// + the canonical sections) when DESIGN.md exists.
|
|
869
897
|
// - sidecar: .impeccable/design.json contents when present.
|
|
870
898
|
// Expected shape: schemaVersion 2, carrying
|
|
871
899
|
// extensions + components + narrative.
|
|
@@ -928,15 +956,23 @@ function createRequestHandler({ detectScript, liveScriptParts }) {
|
|
|
928
956
|
const filePath = url.searchParams.get('path');
|
|
929
957
|
if (!filePath || filePath.includes('..')) { res.writeHead(400); res.end('Bad path'); return; }
|
|
930
958
|
const absPath = path.resolve(process.cwd(), filePath);
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
959
|
+
let realRoot, realTarget;
|
|
960
|
+
try {
|
|
961
|
+
realRoot = fs.realpathSync(process.cwd());
|
|
962
|
+
realTarget = fs.realpathSync(absPath);
|
|
963
|
+
} catch {
|
|
964
|
+
res.writeHead(404); res.end('File not found'); return;
|
|
965
|
+
}
|
|
966
|
+
// Confine to the project root after symlink resolution. A bare
|
|
967
|
+
// `startsWith(cwd)` string check lets a sibling dir whose name extends the
|
|
968
|
+
// root name (projeto -> projeto-backup) slip through; compare on the
|
|
969
|
+
// relative path instead (same pattern as sessionFileMetadataFromPollReply
|
|
970
|
+
// below). An empty rel means the request resolved to the root directory
|
|
971
|
+
// itself, which this file route never serves.
|
|
972
|
+
const rel = path.relative(realRoot, realTarget);
|
|
937
973
|
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) { res.writeHead(403); res.end('Forbidden'); return; }
|
|
938
974
|
let content;
|
|
939
|
-
try { content = fs.readFileSync(
|
|
975
|
+
try { content = fs.readFileSync(realTarget, 'utf-8'); }
|
|
940
976
|
catch { res.writeHead(404); res.end('File not found'); return; }
|
|
941
977
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
942
978
|
res.end(content);
|
|
@@ -1018,6 +1054,7 @@ function createRequestHandler({ detectScript, liveScriptParts }) {
|
|
|
1018
1054
|
res.end(JSON.stringify({ error }));
|
|
1019
1055
|
return;
|
|
1020
1056
|
}
|
|
1057
|
+
stripPollerOwnedEventFields(msg);
|
|
1021
1058
|
if (msg.type === 'agent_phase') {
|
|
1022
1059
|
recordAgentPhase(msg.id, msg.phase, {
|
|
1023
1060
|
...(Number.isFinite(msg.durationMs) ? { durationMs: msg.durationMs } : {}),
|
|
@@ -17,13 +17,14 @@
|
|
|
17
17
|
* node live.mjs --help
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { execFileSync } from 'node:child_process';
|
|
21
21
|
import fs from 'node:fs';
|
|
22
22
|
import path from 'node:path';
|
|
23
23
|
import { fileURLToPath } from 'node:url';
|
|
24
24
|
import { resolveTargetSelection } from './context.mjs';
|
|
25
25
|
import { resolveFiles } from './live-inject.mjs';
|
|
26
26
|
import { readLiveServerInfo } from './lib/impeccable-paths.mjs';
|
|
27
|
+
import { livePathGlobToRegex } from './lib/live-path-globs.mjs';
|
|
27
28
|
import { resolveSurfaceBrief } from './lib/surface-briefs.mjs';
|
|
28
29
|
import { resolveLiveTarget } from './live-target.mjs';
|
|
29
30
|
import { bootInstructions } from './live/instructions.mjs';
|
|
@@ -240,7 +241,7 @@ function scanForDrift(rootDir, resolvedFiles, config) {
|
|
|
240
241
|
// Files matching the user's `exclude` globs are intentional omissions,
|
|
241
242
|
// not drift. Compile them to regexes so the orphan list stays signal.
|
|
242
243
|
const userExcludeRegexes = (Array.isArray(config.exclude) ? config.exclude : [])
|
|
243
|
-
.map(
|
|
244
|
+
.map(livePathGlobToRegex);
|
|
244
245
|
const isUserExcluded = (rel) => userExcludeRegexes.some((re) => re.test(rel));
|
|
245
246
|
|
|
246
247
|
const orphans = [];
|
|
@@ -278,49 +279,23 @@ function scanForDrift(rootDir, resolvedFiles, config) {
|
|
|
278
279
|
};
|
|
279
280
|
}
|
|
280
281
|
|
|
281
|
-
/**
|
|
282
|
-
* Same glob-to-regex mapping used by live-inject.mjs. Kept inline here
|
|
283
|
-
* to avoid a circular import (live-inject.mjs already imports nothing
|
|
284
|
-
* from live.mjs). The two must stay in sync.
|
|
285
|
-
*/
|
|
286
|
-
function globToRegex(pattern) {
|
|
287
|
-
let re = '';
|
|
288
|
-
let i = 0;
|
|
289
|
-
while (i < pattern.length) {
|
|
290
|
-
const c = pattern[i];
|
|
291
|
-
if (c === '*') {
|
|
292
|
-
if (pattern[i + 1] === '*') {
|
|
293
|
-
if (pattern[i + 2] === '/') { re += '(?:.*/)?'; i += 3; }
|
|
294
|
-
else { re += '.*'; i += 2; }
|
|
295
|
-
} else {
|
|
296
|
-
re += '[^/]*';
|
|
297
|
-
i += 1;
|
|
298
|
-
}
|
|
299
|
-
} else if (c === '?') {
|
|
300
|
-
re += '[^/]';
|
|
301
|
-
i += 1;
|
|
302
|
-
} else if (/[.+^${}()|[\]\\]/.test(c)) {
|
|
303
|
-
re += '\\' + c;
|
|
304
|
-
i += 1;
|
|
305
|
-
} else {
|
|
306
|
-
re += c;
|
|
307
|
-
i += 1;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return new RegExp('^' + re + '$');
|
|
311
|
-
}
|
|
312
|
-
|
|
313
282
|
// ---------------------------------------------------------------------------
|
|
314
283
|
// Helpers
|
|
315
284
|
// ---------------------------------------------------------------------------
|
|
316
285
|
|
|
317
286
|
function runScript(name, args, options = {}) {
|
|
318
287
|
const scriptPath = path.join(__dirname, name);
|
|
319
|
-
const cmd = `node "${scriptPath}" ${args.map(a => `"${a}"`).join(' ')}`;
|
|
320
288
|
try {
|
|
321
|
-
|
|
289
|
+
// argv form, never a shell: string interpolation into double quotes would
|
|
290
|
+
// let a `"` or `$(...)` in any future caller's arg escape into the shell
|
|
291
|
+
// (issue #476).
|
|
292
|
+
return execFileSync(process.execPath, [scriptPath, ...args], {
|
|
293
|
+
encoding: 'utf-8',
|
|
294
|
+
cwd: options.cwd || process.cwd(),
|
|
295
|
+
timeout: 15_000,
|
|
296
|
+
});
|
|
322
297
|
} catch (err) {
|
|
323
|
-
//
|
|
298
|
+
// execFileSync throws on non-zero exit; return stdout if any
|
|
324
299
|
return err.stdout || err.message || '';
|
|
325
300
|
}
|
|
326
301
|
}
|
|
@@ -21,7 +21,8 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
21
21
|
|
|
22
22
|
// All known harness directories
|
|
23
23
|
const HARNESS_DIRS = [
|
|
24
|
-
'.claude', '.cursor', '.gemini', '.codex', '.agents', '.github', '.grok',
|
|
24
|
+
'.claude', '.cursor', '.gemini', '.codex', '.agents', '.agent', '.github', '.grok',
|
|
25
|
+
'.hermes',
|
|
25
26
|
'.trae', '.trae-cn', '.pi', '.opencode', '.kiro', '.rovodev', '.vibe', '.qoder',
|
|
26
27
|
];
|
|
27
28
|
|
|
@@ -93,15 +94,17 @@ function commandPrefixForSkillsDir(skillsDir) {
|
|
|
93
94
|
return CODEX_HARNESSES.has(basename(dirname(skillsDir))) ? '$' : '/';
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
function generatePinnedSkill(command, metadata, commandPrefix) {
|
|
97
|
+
function generatePinnedSkill(command, metadata, commandPrefix, isCodex) {
|
|
97
98
|
const desc = metadata[command]?.description || `Shortcut for ${commandPrefix}impeccable ${command}.`;
|
|
98
99
|
const hint = metadata[command]?.argumentHint || '[target]';
|
|
100
|
+
const providerFrontmatter = isCodex
|
|
101
|
+
? `metadata:\n argument-hint: "${hint}"`
|
|
102
|
+
: `argument-hint: "${hint}"\nuser-invocable: true`;
|
|
99
103
|
|
|
100
104
|
return `---
|
|
101
105
|
name: ${command}
|
|
102
106
|
description: "${desc}"
|
|
103
|
-
|
|
104
|
-
user-invocable: true
|
|
107
|
+
${providerFrontmatter}
|
|
105
108
|
---
|
|
106
109
|
|
|
107
110
|
${PIN_MARKER}
|
|
@@ -128,7 +131,7 @@ function pin(command, projectRoot) {
|
|
|
128
131
|
|
|
129
132
|
for (const skillsDir of harnessDirs) {
|
|
130
133
|
const commandPrefix = commandPrefixForSkillsDir(skillsDir);
|
|
131
|
-
const content = generatePinnedSkill(command, metadata, commandPrefix);
|
|
134
|
+
const content = generatePinnedSkill(command, metadata, commandPrefix, commandPrefix === '$');
|
|
132
135
|
// Check if skill already exists (and isn't a pin)
|
|
133
136
|
const skillDir = join(skillsDir, command);
|
|
134
137
|
if (existsSync(skillDir)) {
|