@slotted/react 0.1.0-beta.4 → 0.1.0-beta.5
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 +16 -1
- package/dist/{action-bar-OACchRZw.js → action-bar-ti3e6FK1.js} +1 -1
- package/dist/action-bar.js +1 -1
- package/dist/{app-shell-CmYvnu_7.js → app-shell-nnSP44OO.js} +271 -267
- package/dist/app-shell.js +1 -1
- package/dist/bin/slotted-agent-docs.mjs +297 -0
- package/dist/{cascader-D1AhTyBL.js → cascader-Cn2Acpcv.js} +227 -210
- package/dist/cascader.js +1 -1
- package/dist/chart-04i6NZQA.js +968 -0
- package/dist/chart.js +1 -1
- package/dist/{checkbox-DfxFHZaU.js → checkbox-B4Sb1eXj.js} +3 -3
- package/dist/{checkbox-group-9sDgrV5Z.js → checkbox-group-LpGMuq9w.js} +2 -2
- package/dist/checkbox-group.js +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/{code-block-CFHf8uTm.js → code-block-CTNiyt0U.js} +1 -1
- package/dist/code-block.js +1 -1
- package/dist/{color-picker-CwbWzCKg.js → color-picker-BcRCeISx.js} +4 -4
- package/dist/color-picker.js +1 -1
- package/dist/{column-manager-CTtizVhm.js → column-manager-BtkYLs5G.js} +4 -4
- package/dist/column-manager.js +1 -1
- package/dist/{combobox-BVMI9lg8.js → combobox-C1D5doWM.js} +227 -198
- package/dist/combobox.js +1 -1
- package/dist/{context-menu-BuST6W94.js → context-menu-B00UIBVC.js} +1 -1
- package/dist/context-menu.js +1 -1
- package/dist/{currency-field-DD9A5U3D.js → currency-field-Dti9JFu0.js} +1 -1
- package/dist/currency-field.js +1 -1
- package/dist/{dashboard-grid-CBMP_-JV.js → dashboard-grid-CoLKLvYL.js} +1 -1
- package/dist/dashboard-grid.js +1 -1
- package/dist/{data-grid-Eb-a4jg3.js → data-grid-DX95Y7mY.js} +18 -18
- package/dist/data-grid.js +1 -1
- package/dist/{data-list-oICmxGkA.js → data-list-CWYTPNm-.js} +2 -2
- package/dist/data-list.js +1 -1
- package/dist/{date-field-Hr-OCU-H.js → date-field-BxEpqAPg.js} +1 -1
- package/dist/date-field.js +1 -1
- package/dist/date-picker-DRk9SZp1.js +308 -0
- package/dist/date-picker.js +1 -1
- package/dist/date-range-picker-6Yz7avTS.js +483 -0
- package/dist/date-range-picker.js +1 -1
- package/dist/{datetime-field-B8TrmsDK.js → datetime-field-D2f9u6im.js} +1 -1
- package/dist/datetime-field.js +1 -1
- package/dist/datetime-picker-STqSWMkH.js +645 -0
- package/dist/datetime-picker.js +1 -1
- package/dist/datetime-range-picker-DQNQwr3K.js +1000 -0
- package/dist/datetime-range-picker.js +1 -1
- package/dist/{detail-panel-CW53lUjp.js → detail-panel-z6WGleP4.js} +1 -1
- package/dist/detail-panel.js +1 -1
- package/dist/docs/AGENTS.snippet.md +2 -0
- package/dist/docs/app-shell.examples.md +1 -1
- package/dist/docs/app-shell.json +4 -4
- package/dist/docs/app-shell.md +2 -2
- package/dist/docs/guidelines/README.md +2 -2
- package/dist/docs/guidelines/index.md +1 -1
- package/dist/docs/guidelines/own-elements.md +1 -1
- package/dist/docs/page-header.json +1 -0
- package/dist/docs/page-header.md +1 -1
- package/dist/docs/skills/build-own-element/SKILL.md +6 -6
- package/dist/docs/skills/compose-screen/SKILL.md +6 -6
- package/dist/docs/skills/review-screen/SKILL.md +6 -6
- package/dist/docs/skills/theme-app/SKILL.md +1 -1
- package/dist/docs/skills/waiting-states/SKILL.md +2 -2
- package/dist/docs/tabs.json +13 -3
- package/dist/docs/tabs.md +3 -2
- package/dist/{entity-picker-Dn1T7JCq.js → entity-picker-BdySwW8F.js} +274 -256
- package/dist/entity-picker.js +1 -1
- package/dist/{event-calendar-CnF3iIZD.js → event-calendar-CiphPXdV.js} +4 -4
- package/dist/event-calendar.js +1 -1
- package/dist/{field-NJaCxl2h.js → field-BxQFpOXS.js} +1 -1
- package/dist/{field-group-hAzVa2O9.js → field-group-DcN_hiZN.js} +1 -1
- package/dist/field-group.js +1 -1
- package/dist/field.js +1 -1
- package/dist/{filter-bar-CHyy1o--.js → filter-bar-C_dXljI_.js} +10 -10
- package/dist/filter-bar.js +1 -1
- package/dist/{hover-card-BJE5dtaQ.js → hover-card-DpVzwvDJ.js} +1 -1
- package/dist/hover-card.js +1 -1
- package/dist/{import-export-center-jF37b909.js → import-export-center-BQnMJz4g.js} +3 -3
- package/dist/import-export-center.js +1 -1
- package/dist/index.js +63 -63
- package/dist/{listbox-Dv856z9I.js → listbox-D0MJcAMm.js} +1 -1
- package/dist/listbox.js +1 -1
- package/dist/{markdown-CeG8mUbI.js → markdown-IiVoh6yi.js} +2 -2
- package/dist/markdown.js +1 -1
- package/dist/menu-parts-D6v_mz6O.js +809 -0
- package/dist/menu.js +1 -1
- package/dist/{menubar-b2CruimJ.js → menubar-DApIg1eR.js} +1 -1
- package/dist/menubar.js +1 -1
- package/dist/{multi-select-DQ5ds0fX.js → multi-select-B8z6ApgW.js} +286 -257
- package/dist/multi-select.js +1 -1
- package/dist/{notification-center-C1iWWl5Y.js → notification-center-CGEEHcRA.js} +2 -2
- package/dist/notification-center.js +1 -1
- package/dist/{number-field-BFIAmv-z.js → number-field-DXk1TAXA.js} +1 -1
- package/dist/number-field.js +1 -1
- package/dist/{otp-input-BNQhuKaT.js → otp-input-CI-kdNgx.js} +1 -1
- package/dist/otp-input.js +1 -1
- package/dist/{popover-Be7JWbGJ.js → popover-DEp8Bf3e.js} +186 -166
- package/dist/popover.js +1 -1
- package/dist/{prompt-composer-BxpdkTpZ.js → prompt-composer-DdcU3S3L.js} +1 -1
- package/dist/prompt-composer.js +1 -1
- package/dist/{radio-group-BaVIP9UH.js → radio-group-D8csBZ8Z.js} +2 -2
- package/dist/radio-group.js +1 -1
- package/dist/{rating-DYBQBzwB.js → rating-rI3b5zII.js} +1 -1
- package/dist/rating.js +1 -1
- package/dist/{saved-views-DlE_--Z9.js → saved-views-B3brBTD9.js} +4 -4
- package/dist/saved-views.js +1 -1
- package/dist/scroll-area-DQ_ansLu.js +112 -0
- package/dist/scroll-area.js +1 -1
- package/dist/{segmented-control-DmuOGiu6.js → segmented-control-C-PzkTtg.js} +2 -2
- package/dist/segmented-control.js +1 -1
- package/dist/{select-M7Io4UL9.js → select-BUylPwpb.js} +241 -203
- package/dist/select.js +1 -1
- package/dist/{side-navigation-CBLtCzuJ.js → side-navigation-Bm-BZma1.js} +156 -140
- package/dist/side-navigation.js +1 -1
- package/dist/{slider-DTes4HLF.js → slider-fp7kyg-6.js} +1 -1
- package/dist/slider.js +1 -1
- package/dist/{split-button-CPgHiy9G.js → split-button-DKRm0j5c.js} +1 -1
- package/dist/split-button.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/{switch-DQzuIhPX.js → switch-aVoc4C2F.js} +1 -1
- package/dist/switch.js +1 -1
- package/dist/{table-U9eJYCuB.js → table-BYEvFr4P.js} +276 -275
- package/dist/table.js +1 -1
- package/dist/{tabs-BtHmjlnM.js → tabs-HWmvOKM4.js} +109 -102
- package/dist/tabs.js +1 -1
- package/dist/{tags-input-CcOkm0AW.js → tags-input-pbv6x1Pi.js} +1 -1
- package/dist/tags-input.js +1 -1
- package/dist/{text-field-DLfIZ0fk.js → text-field-CbQ6UCAP.js} +1 -1
- package/dist/text-field.js +1 -1
- package/dist/{textarea-CBJBJBi6.js → textarea-DJSfhrXH.js} +1 -1
- package/dist/textarea.js +1 -1
- package/dist/{time-field-DrhiCtUl.js → time-field-BI9n607q.js} +1 -1
- package/dist/time-field.js +1 -1
- package/dist/time-picker-D6SlQWNF.js +402 -0
- package/dist/time-picker.js +1 -1
- package/dist/time-range-picker-CcV3_45h.js +751 -0
- package/dist/time-range-picker.js +1 -1
- package/dist/{toggle-group-7iBR9Rn1.js → toggle-group-i7JPO8BM.js} +1 -1
- package/dist/toggle-group.js +1 -1
- package/dist/{toolbar-DDZpEftQ.js → toolbar-B9knmgHA.js} +1 -1
- package/dist/toolbar.js +1 -1
- package/dist/tooltip-CgjIROUy.js +410 -0
- package/dist/tooltip.js +1 -1
- package/dist/{top-navigation-e-tWMGNr.js → top-navigation-DiG2PF5W.js} +2 -2
- package/dist/top-navigation.js +1 -1
- package/dist/{transfer-CqZOTLMP.js → transfer--UOQVeKE.js} +5 -5
- package/dist/transfer.js +1 -1
- package/dist/types/app-shell/app-shell-parts.d.ts +6 -4
- package/dist/types/app-shell/app-shell-parts.d.ts.map +1 -1
- package/dist/types/app-shell/app-shell.d.ts.map +1 -1
- package/dist/types/cascader/cascader.d.ts.map +1 -1
- package/dist/types/chart/chart.d.ts.map +1 -1
- package/dist/types/combobox/combobox-parts.d.ts.map +1 -1
- package/dist/types/date-picker/date-picker.d.ts.map +1 -1
- package/dist/types/date-range-picker/date-range-picker.d.ts.map +1 -1
- package/dist/types/datetime-picker/datetime-picker.d.ts.map +1 -1
- package/dist/types/datetime-range-picker/datetime-range-picker.d.ts.map +1 -1
- package/dist/types/entity-picker/entity-picker-popup.d.ts.map +1 -1
- package/dist/types/menu/menu-parts.d.ts.map +1 -1
- package/dist/types/multi-select/multi-select-parts.d.ts.map +1 -1
- package/dist/types/popover/popover-parts.d.ts.map +1 -1
- package/dist/types/scroll-area/scroll-area.d.ts.map +1 -1
- package/dist/types/select/select-parts.d.ts.map +1 -1
- package/dist/types/side-navigation/side-navigation-parts.d.ts.map +1 -1
- package/dist/types/stat-card/stat-card.types.d.ts +2 -2
- package/dist/types/stat-card/stat-card.types.d.ts.map +1 -1
- package/dist/types/table/table-content.d.ts.map +1 -1
- package/dist/types/tabs/tabs-parts.d.ts +2 -4
- package/dist/types/tabs/tabs-parts.d.ts.map +1 -1
- package/dist/types/tabs/tabs.d.ts.map +1 -1
- package/dist/types/time-picker/time-picker.d.ts.map +1 -1
- package/dist/types/time-range-picker/time-range-picker.d.ts.map +1 -1
- package/dist/types/tooltip/tooltip-parts.d.ts.map +1 -1
- package/dist/{upload-DjnlVR8y.js → upload-CLX5cURk.js} +1 -1
- package/dist/upload.js +1 -1
- package/package.json +10 -7
- package/dist/chart-O0ZhkzJe.js +0 -966
- package/dist/date-picker-D3-y2Jxw.js +0 -295
- package/dist/date-range-picker-y3b82Wmf.js +0 -471
- package/dist/datetime-picker-CIRHVLQp.js +0 -629
- package/dist/datetime-range-picker-DM-1m98z.js +0 -994
- package/dist/menu-parts-W58pVNUt.js +0 -786
- package/dist/scroll-area-BriwmvYY.js +0 -111
- package/dist/time-picker-CRQsMJbQ.js +0 -396
- package/dist/time-range-picker-DlZJsj1e.js +0 -745
- package/dist/tooltip-DbKTPe_i.js +0 -392
package/dist/app-shell.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as e, S as t, T as n, _ as r, a as i, b as a, c as o, d as s, f as c, g as l, h as u, i as d, l as f, m as p, n as m, o as h, p as g, r as _, s as v, t as y, u as b, v as x, w as S, x as C, y as w } from "./app-shell-
|
|
1
|
+
import { C as e, S as t, T as n, _ as r, a as i, b as a, c as o, d as s, f as c, g as l, h as u, i as d, l as f, m as p, n as m, o as h, p as g, r as _, s as v, t as y, u as b, v as x, w as S, x as C, y as w } from "./app-shell-nnSP44OO.js";
|
|
2
2
|
export { y as APP_SHELL_OVERLAY_AT, m as AppShell, i as AppShellActions, h as AppShellAside, v as AppShellAsideBody, o as AppShellAsideFooter, f as AppShellAsideHeader, b as AppShellAsideResizer, s as AppShellAsideToggle, c as AppShellBanner, g as AppShellBrand, p as AppShellCentre, S as AppShellDefaultsProvider, u as AppShellFooter, l as AppShellMain, r as AppShellNav, x as AppShellNavBody, w as AppShellNavFooter, a as AppShellNavHeader, C as AppShellNavRail, t as AppShellNavToggle, e as AppShellProgress, _ as useAppShell, n as useAppShellDefaults, d as useAppShellIfPresent };
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Keeps an application's agent material in step with the installed package.
|
|
4
|
+
*
|
|
5
|
+
* slotted-agent-docs instructions write --file <path> [--app-root <dir>] [--theme <name>] [--theme-css <specifier>]
|
|
6
|
+
* slotted-agent-docs instructions check --file <path> [same flags]
|
|
7
|
+
* slotted-agent-docs skills link --dir <path> [--mode link|copy]
|
|
8
|
+
* slotted-agent-docs skills check --dir <path>
|
|
9
|
+
*
|
|
10
|
+
* `instructions` writes the package's `AGENTS.snippet.md` into a block of the
|
|
11
|
+
* named file, between `<!-- slotted:begin version=… -->` and
|
|
12
|
+
* `<!-- slotted:end -->`; text outside the markers is never touched. `skills`
|
|
13
|
+
* makes `<dir>/<skill>` for each skill the package ships, as a relative link
|
|
14
|
+
* or as a copy. Both `check` subcommands exit 1 when the file or the directory
|
|
15
|
+
* differs from what the matching write would produce.
|
|
16
|
+
*
|
|
17
|
+
* The file and the directory are arguments: the command names no agent.
|
|
18
|
+
*/
|
|
19
|
+
import {
|
|
20
|
+
cpSync,
|
|
21
|
+
existsSync,
|
|
22
|
+
lstatSync,
|
|
23
|
+
mkdirSync,
|
|
24
|
+
readFileSync,
|
|
25
|
+
readdirSync,
|
|
26
|
+
readlinkSync,
|
|
27
|
+
realpathSync,
|
|
28
|
+
rmSync,
|
|
29
|
+
statSync,
|
|
30
|
+
symlinkSync,
|
|
31
|
+
writeFileSync,
|
|
32
|
+
} from 'node:fs';
|
|
33
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
34
|
+
import { fileURLToPath } from 'node:url';
|
|
35
|
+
import { parseArgs } from 'node:util';
|
|
36
|
+
|
|
37
|
+
const BEGIN = /<!-- slotted:begin version=\S* -->/;
|
|
38
|
+
const END = '<!-- slotted:end -->';
|
|
39
|
+
const BASE_SENTENCE = 'Paths below are relative to the directory that holds node_modules.';
|
|
40
|
+
|
|
41
|
+
/** The package that ships this file: the nearest `package.json` named `@slotted/…` above it. */
|
|
42
|
+
function ownPackage() {
|
|
43
|
+
let dir = dirname(realpathSync(fileURLToPath(import.meta.url)));
|
|
44
|
+
for (;;) {
|
|
45
|
+
const manifest = join(dir, 'package.json');
|
|
46
|
+
if (existsSync(manifest)) {
|
|
47
|
+
const { name, version } = JSON.parse(readFileSync(manifest, 'utf8'));
|
|
48
|
+
if (typeof name === 'string' && name.startsWith('@slotted/')) {
|
|
49
|
+
const docs = ['dist/docs', 'docs'].map((path) => join(dir, path)).find(existsSync);
|
|
50
|
+
if (docs === undefined) fail(`${name} has no docs directory`);
|
|
51
|
+
return { name, version, root: dir, docs };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const parent = dirname(dir);
|
|
55
|
+
if (parent === dir) fail('no @slotted package above this command');
|
|
56
|
+
dir = parent;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fail(message) {
|
|
61
|
+
process.stderr.write(`slotted-agent-docs: ${message}\n`);
|
|
62
|
+
process.exit(2);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ── instructions ───────────────────────────────────────────────────────── */
|
|
66
|
+
|
|
67
|
+
function renderBlock(pkg, { appRoot, theme, themeCss }) {
|
|
68
|
+
let snippet = readFileSync(join(pkg.docs, 'AGENTS.snippet.md'), 'utf8');
|
|
69
|
+
if (appRoot !== undefined) {
|
|
70
|
+
const prefix = appRoot.replaceAll('\\', '/').replace(/\/+$/, '') || '.';
|
|
71
|
+
snippet = snippet
|
|
72
|
+
.replace(BASE_SENTENCE, 'Paths below are relative to the directory of this file.')
|
|
73
|
+
.replaceAll('`node_modules/', `\`${prefix}/node_modules/`);
|
|
74
|
+
}
|
|
75
|
+
if (theme !== undefined) {
|
|
76
|
+
snippet = snippet.replaceAll('data-slotted-theme="default"', `data-slotted-theme="${theme}"`);
|
|
77
|
+
}
|
|
78
|
+
if (themeCss !== undefined) {
|
|
79
|
+
snippet = snippet.replaceAll('`@slotted/themes/default.css`', `\`${themeCss}\``);
|
|
80
|
+
}
|
|
81
|
+
if (!snippet.endsWith('\n')) snippet += '\n';
|
|
82
|
+
return `<!-- slotted:begin version=${pkg.version} -->\n${snippet}${END}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The file with the block in place: replaced where it stands, or appended. */
|
|
86
|
+
function withBlock(current, block) {
|
|
87
|
+
const begin = BEGIN.exec(current);
|
|
88
|
+
if (begin !== null) {
|
|
89
|
+
const end = current.indexOf(END, begin.index);
|
|
90
|
+
if (end === -1) fail('a begin marker without an end marker');
|
|
91
|
+
return current.slice(0, begin.index) + block + current.slice(end + END.length);
|
|
92
|
+
}
|
|
93
|
+
if (current === '') return `${block}\n`;
|
|
94
|
+
return `${current}${current.endsWith('\n') ? '' : '\n'}\n${block}\n`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A unified diff of two texts, by lines, with three lines of context. */
|
|
98
|
+
function unifiedDiff(a, b, from, to) {
|
|
99
|
+
const x = a.split('\n');
|
|
100
|
+
const y = b.split('\n');
|
|
101
|
+
const lcs = Array.from({ length: x.length + 1 }, () => new Array(y.length + 1).fill(0));
|
|
102
|
+
for (let i = x.length - 1; i >= 0; i--)
|
|
103
|
+
for (let j = y.length - 1; j >= 0; j--)
|
|
104
|
+
lcs[i][j] = x[i] === y[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
|
105
|
+
const ops = [];
|
|
106
|
+
let i = 0;
|
|
107
|
+
let j = 0;
|
|
108
|
+
while (i < x.length || j < y.length) {
|
|
109
|
+
if (i < x.length && j < y.length && x[i] === y[j]) ops.push([' ', x[i++], j++]);
|
|
110
|
+
else if (j < y.length && (i === x.length || lcs[i][j + 1] >= lcs[i + 1][j]))
|
|
111
|
+
ops.push(['+', y[j++], i]);
|
|
112
|
+
else ops.push(['-', x[i++], j]);
|
|
113
|
+
}
|
|
114
|
+
const lines = [`--- ${from}`, `+++ ${to}`];
|
|
115
|
+
let k = 0;
|
|
116
|
+
while (k < ops.length) {
|
|
117
|
+
if (ops[k][0] === ' ') {
|
|
118
|
+
k++;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
let start = Math.max(0, k - 3);
|
|
122
|
+
let stop = k;
|
|
123
|
+
while (stop < ops.length) {
|
|
124
|
+
let next = stop;
|
|
125
|
+
while (next < ops.length && ops[next][0] === ' ') next++;
|
|
126
|
+
if (next === ops.length || next - stop > 6) break;
|
|
127
|
+
while (next < ops.length && ops[next][0] !== ' ') next++;
|
|
128
|
+
stop = next;
|
|
129
|
+
}
|
|
130
|
+
stop = Math.min(ops.length, stop + 3);
|
|
131
|
+
const hunk = ops.slice(start, stop);
|
|
132
|
+
const oldStart = ops.slice(0, start).filter(([op]) => op !== '+').length + 1;
|
|
133
|
+
const newStart = ops.slice(0, start).filter(([op]) => op !== '-').length + 1;
|
|
134
|
+
const oldCount = hunk.filter(([op]) => op !== '+').length;
|
|
135
|
+
const newCount = hunk.filter(([op]) => op !== '-').length;
|
|
136
|
+
lines.push(`@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`);
|
|
137
|
+
for (const [op, text] of hunk) lines.push(`${op}${text}`);
|
|
138
|
+
k = stop;
|
|
139
|
+
}
|
|
140
|
+
return `${lines.join('\n')}\n`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function instructions(action, values) {
|
|
144
|
+
if (values.file === undefined) fail('--file is required');
|
|
145
|
+
const pkg = ownPackage();
|
|
146
|
+
const block = renderBlock(pkg, {
|
|
147
|
+
appRoot: values['app-root'],
|
|
148
|
+
theme: values.theme,
|
|
149
|
+
themeCss: values['theme-css'],
|
|
150
|
+
});
|
|
151
|
+
const current = existsSync(values.file) ? readFileSync(values.file, 'utf8') : '';
|
|
152
|
+
const next = withBlock(current, block);
|
|
153
|
+
if (action === 'write') {
|
|
154
|
+
if (next !== current) {
|
|
155
|
+
mkdirSync(dirname(resolve(values.file)), { recursive: true });
|
|
156
|
+
writeFileSync(values.file, next);
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
if (next === current) return 0;
|
|
161
|
+
process.stdout.write(unifiedDiff(current, next, values.file, `${values.file} (expected)`));
|
|
162
|
+
return 1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ── skills ─────────────────────────────────────────────────────────────── */
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The skill directories as the application sees them: through its own
|
|
169
|
+
* `node_modules/<package>`, whose path survives an upgrade, rather than the
|
|
170
|
+
* store path a package manager may resolve this file to.
|
|
171
|
+
*/
|
|
172
|
+
function installedSkills(pkg) {
|
|
173
|
+
const docsInPackage = relative(pkg.root, pkg.docs);
|
|
174
|
+
let base = join(pkg.docs, 'skills');
|
|
175
|
+
for (let dir = process.cwd(); ; dir = dirname(dir)) {
|
|
176
|
+
const candidate = join(dir, 'node_modules', ...pkg.name.split('/'), docsInPackage, 'skills');
|
|
177
|
+
if (existsSync(candidate)) {
|
|
178
|
+
base = candidate;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
if (dirname(dir) === dir) break;
|
|
182
|
+
}
|
|
183
|
+
const skills = new Map();
|
|
184
|
+
if (!existsSync(base)) return skills;
|
|
185
|
+
for (const entry of readdirSync(base, { withFileTypes: true })) {
|
|
186
|
+
if (entry.isDirectory() && existsSync(join(base, entry.name, 'SKILL.md')))
|
|
187
|
+
skills.set(entry.name, join(base, entry.name));
|
|
188
|
+
}
|
|
189
|
+
return skills;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** A link this command wrote: one that names the package's skills directory. */
|
|
193
|
+
function isOwnLink(path, pkg) {
|
|
194
|
+
return readlinkSync(path).split(/[\\/]/).join('/').includes(`${pkg.name}/`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function sameTree(a, b) {
|
|
198
|
+
const list = (dir) => readdirSync(dir, { recursive: true, encoding: 'utf8' }).sort();
|
|
199
|
+
const left = list(a);
|
|
200
|
+
const right = list(b);
|
|
201
|
+
if (left.join('\n') !== right.join('\n')) return false;
|
|
202
|
+
return left.every((file) => {
|
|
203
|
+
const x = join(a, file);
|
|
204
|
+
const y = join(b, file);
|
|
205
|
+
if (statSync(x).isDirectory() || statSync(y).isDirectory())
|
|
206
|
+
return statSync(x).isDirectory() === statSync(y).isDirectory();
|
|
207
|
+
return readFileSync(x).equals(readFileSync(y));
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function skillsLink(written, mode, pkg) {
|
|
212
|
+
const dir = resolve(written);
|
|
213
|
+
const skills = installedSkills(pkg);
|
|
214
|
+
if (skills.size === 0) fail(`${pkg.name} ships no skills`);
|
|
215
|
+
mkdirSync(dir, { recursive: true });
|
|
216
|
+
for (const [name, source] of skills) {
|
|
217
|
+
const entry = join(dir, name);
|
|
218
|
+
const target = relative(dir, source);
|
|
219
|
+
const stat = existsSync(entry) || isLink(entry) ? lstatSync(entry) : null;
|
|
220
|
+
if (stat !== null) {
|
|
221
|
+
if (mode === 'link' && stat.isSymbolicLink() && readlinkSync(entry) === target) continue;
|
|
222
|
+
if (mode === 'copy' && stat.isDirectory() && sameTree(entry, source)) continue;
|
|
223
|
+
rmSync(entry, { recursive: true, force: true });
|
|
224
|
+
}
|
|
225
|
+
if (mode === 'link') symlinkSync(target, entry, 'dir');
|
|
226
|
+
else cpSync(source, entry, { recursive: true });
|
|
227
|
+
}
|
|
228
|
+
/* A link to a skill the package no longer ships. */
|
|
229
|
+
for (const entry of readdirSync(dir)) {
|
|
230
|
+
const path = join(dir, entry);
|
|
231
|
+
if (!skills.has(entry) && isLink(path) && isOwnLink(path, pkg)) rmSync(path);
|
|
232
|
+
}
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function isLink(path) {
|
|
237
|
+
try {
|
|
238
|
+
return lstatSync(path).isSymbolicLink();
|
|
239
|
+
} catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function skillsCheck(dir, pkg) {
|
|
245
|
+
const skills = installedSkills(pkg);
|
|
246
|
+
const problems = [];
|
|
247
|
+
for (const [name, source] of skills) {
|
|
248
|
+
const entry = join(dir, name);
|
|
249
|
+
if (isLink(entry)) {
|
|
250
|
+
if (!existsSync(join(entry, 'SKILL.md'))) problems.push(`${entry}: dangling link`);
|
|
251
|
+
else if (realpathSync(entry) !== realpathSync(source))
|
|
252
|
+
problems.push(`${entry}: links to ${readlinkSync(entry)}, not the installed version`);
|
|
253
|
+
} else if (!existsSync(entry)) problems.push(`${entry}: missing`);
|
|
254
|
+
else if (!sameTree(entry, source))
|
|
255
|
+
problems.push(`${entry}: differs from the installed version`);
|
|
256
|
+
}
|
|
257
|
+
if (existsSync(dir)) {
|
|
258
|
+
for (const entry of readdirSync(dir)) {
|
|
259
|
+
const path = join(dir, entry);
|
|
260
|
+
if (!skills.has(entry) && isLink(path) && isOwnLink(path, pkg) && !existsSync(path))
|
|
261
|
+
problems.push(`${path}: dangling link`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (skills.size === 0) problems.push(`${pkg.name}: no installed skills`);
|
|
265
|
+
for (const problem of problems) process.stdout.write(`${problem.split(sep).join('/')}\n`);
|
|
266
|
+
return problems.length === 0 ? 0 : 1;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* ── entry ──────────────────────────────────────────────────────────────── */
|
|
270
|
+
|
|
271
|
+
const [group, action, ...rest] = process.argv.slice(2);
|
|
272
|
+
const { values } = parseArgs({
|
|
273
|
+
args: rest,
|
|
274
|
+
options: {
|
|
275
|
+
file: { type: 'string' },
|
|
276
|
+
'app-root': { type: 'string' },
|
|
277
|
+
theme: { type: 'string' },
|
|
278
|
+
'theme-css': { type: 'string' },
|
|
279
|
+
dir: { type: 'string' },
|
|
280
|
+
mode: { type: 'string', default: 'link' },
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (group === 'instructions' && (action === 'write' || action === 'check')) {
|
|
285
|
+
process.exit(instructions(action, values));
|
|
286
|
+
} else if (group === 'skills' && (action === 'link' || action === 'check')) {
|
|
287
|
+
if (values.dir === undefined) fail('--dir is required');
|
|
288
|
+
if (values.mode !== 'link' && values.mode !== 'copy') fail('--mode is link or copy');
|
|
289
|
+
const pkg = ownPackage();
|
|
290
|
+
process.exit(
|
|
291
|
+
action === 'link' ? skillsLink(values.dir, values.mode, pkg) : skillsCheck(values.dir, pkg),
|
|
292
|
+
);
|
|
293
|
+
} else {
|
|
294
|
+
fail(
|
|
295
|
+
'usage: slotted-agent-docs instructions write|check --file <path> | skills link|check --dir <path>',
|
|
296
|
+
);
|
|
297
|
+
}
|