@lorekit/cli 1.27.0 → 1.28.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 +4 -2
- package/bin/lorekit.mjs +4 -2
- package/package.json +1 -1
- package/src/deeplink-pure.mjs +43 -2
- package/src/link.mjs +4 -0
package/README.md
CHANGED
|
@@ -321,6 +321,7 @@ lorekit link global # the Explorer filtered to global scop
|
|
|
321
321
|
lorekit link repo::owner/repo prefer-guards # open one lesson's detail sheet
|
|
322
322
|
lorekit link global::prefer-guards --json # { url, surface, base, params }
|
|
323
323
|
lorekit url --q "flaky test" --owner personal # search + ownership filter
|
|
324
|
+
lorekit link global --tags "perf,ci" # Explorer filtered to labels
|
|
324
325
|
```
|
|
325
326
|
|
|
326
327
|
With no arguments it links to the cwd's **most-specific scope** ("share what I'm
|
|
@@ -331,8 +332,9 @@ that lesson's detail sheet. It sets **both** the `lesson` param (which opens the
|
|
|
331
332
|
sheet) and `scope` — not because scope is needed to find the lesson (the sidebar
|
|
332
333
|
reads one unfiltered recent set), but so the Explorer list *behind* the sheet is
|
|
333
334
|
filtered to the lesson's own scope. Filter flags mirror the Explorer: `--q`
|
|
334
|
-
(search), `--owner <all|personal|orgId>`, `--
|
|
335
|
-
`--
|
|
335
|
+
(search), `--owner <all|personal|orgId>`, `--tags <a,b,c>` (label filter, AND
|
|
336
|
+
across labels; comma-separated or a JSON array), `--range`/`--from`/`--to`,
|
|
337
|
+
`--archived`, `--view <scope|time>`.
|
|
336
338
|
|
|
337
339
|
Every param is `encodeURIComponent(JSON.stringify(value))` — the exact inverse of
|
|
338
340
|
how the dashboard's `useUrlState` reads it back (`JSON.parse`, falling back to the
|
package/bin/lorekit.mjs
CHANGED
|
@@ -85,8 +85,8 @@ ${c.bold('Commands')}
|
|
|
85
85
|
link (url) Print a shareable dashboard deep-link URL for the current context,
|
|
86
86
|
a scope, or a specific lesson (opens its detail sheet). No args
|
|
87
87
|
links to the cwd's most-specific scope. Filter flags mirror the
|
|
88
|
-
Explorer (--q / --owner / --range / --archived / --view);
|
|
89
|
-
LOREKIT_APP_URL override the dashboard host. --json. Pipe it:
|
|
88
|
+
Explorer (--q / --owner / --tags / --range / --archived / --view);
|
|
89
|
+
--base or LOREKIT_APP_URL override the dashboard host. --json. Pipe it:
|
|
90
90
|
lorekit link | pbcopy.
|
|
91
91
|
bootstrap Apply the BYOD schema to a user-supplied Supabase database.
|
|
92
92
|
Only needed when using LOREKIT_STORAGE_URL / LOREKIT_STORAGE_ANON_KEY.
|
|
@@ -506,6 +506,7 @@ ${c.bold('Options')}
|
|
|
506
506
|
--scope <scope> Scope to link to (when no positional scope is given)
|
|
507
507
|
--q <text> Pre-fill the Explorer search box
|
|
508
508
|
--owner <o> Ownership filter: all | personal | <orgId>
|
|
509
|
+
--tags <a,b,c> Label filter (AND across labels); comma-separated or a JSON array
|
|
509
510
|
--range <json> Date range as {"from":"YYYY-MM-DD","to":"YYYY-MM-DD"}
|
|
510
511
|
--from <date> Range start (shorthand for --range)
|
|
511
512
|
--to <date> Range end (shorthand for --range)
|
|
@@ -521,6 +522,7 @@ ${c.bold('Examples')}
|
|
|
521
522
|
npx @lorekit/cli link repo::owner/repo prefer-guards # open one lesson's detail sheet
|
|
522
523
|
npx @lorekit/cli link global::prefer-guards --json # { url, surface, base, params }
|
|
523
524
|
npx @lorekit/cli url --q "flaky test" --owner personal # search + ownership filter
|
|
525
|
+
npx @lorekit/cli link global --tags "perf,ci" # Explorer filtered to labels
|
|
524
526
|
`,
|
|
525
527
|
migrate: `${c.bold('lorekit migrate')} — relocate a LoreKit-format local store into the current layout
|
|
526
528
|
|
package/package.json
CHANGED
package/src/deeplink-pure.mjs
CHANGED
|
@@ -29,14 +29,17 @@ export const LORE_PARAM_DEFAULTS = {
|
|
|
29
29
|
q: '', // string search query
|
|
30
30
|
range: null, // { from, to } | null (DateRange, "YYYY-MM-DD")
|
|
31
31
|
owner: 'all', // 'all' | 'personal' | { orgId }
|
|
32
|
+
tags: [], // string[] — label filter (AND across labels); [] means "no filter"
|
|
32
33
|
view: 'scope', // 'scope' | 'time'
|
|
33
34
|
archived: false, // boolean
|
|
34
35
|
lesson: null, // { scope, key } | null — opens the detail sheet
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
// A stable, readable param order (also makes URLs deterministic for tests).
|
|
38
|
-
//
|
|
39
|
-
|
|
39
|
+
// Mirrors the `useUrlState` call order in `LoreExplorer.tsx` (+ the `lesson`
|
|
40
|
+
// param last), so `tags` sits between `owner` and `view`. `scope` precedes
|
|
41
|
+
// `lesson` so a lesson link reads `?scope=…&lesson=…`.
|
|
42
|
+
const PARAM_ORDER = ['scope', 'q', 'range', 'owner', 'tags', 'view', 'archived', 'lesson'];
|
|
40
43
|
|
|
41
44
|
// Strip trailing slashes from a base URL, falling back to the default when the
|
|
42
45
|
// input is empty/absent. Pure.
|
|
@@ -154,6 +157,44 @@ export function parseViewArg(view) {
|
|
|
154
157
|
return view === 'time' ? 'time' : 'scope';
|
|
155
158
|
}
|
|
156
159
|
|
|
160
|
+
// Coerce the `--tags` flag to a normalized `string[]` label filter, mirroring the
|
|
161
|
+
// web app's `normalizeTags` (`packages/web/src/lib/tag-filter.ts`): trim each
|
|
162
|
+
// entry, drop empties, de-duplicate, preserve first-seen order. Accepts either a
|
|
163
|
+
// JSON array string (`'["perf","ci"]'`) or the friendlier comma-separated form
|
|
164
|
+
// (`'perf, ci'`); a malformed JSON array falls back to comma-splitting rather
|
|
165
|
+
// than throwing. Returns `[]` for absent/empty input (the default → omitted from
|
|
166
|
+
// the URL). Pure.
|
|
167
|
+
export function parseTagsArg(tags) {
|
|
168
|
+
if (Array.isArray(tags)) return normalizeTagList(tags);
|
|
169
|
+
if (typeof tags !== 'string' || !tags.trim()) return [];
|
|
170
|
+
const s = tags.trim();
|
|
171
|
+
if (s.startsWith('[')) {
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(s);
|
|
174
|
+
if (Array.isArray(parsed)) return normalizeTagList(parsed);
|
|
175
|
+
} catch {
|
|
176
|
+
/* malformed JSON array → fall through to comma-splitting */
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return normalizeTagList(s.split(','));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Trim, drop non-string/empty entries, and de-duplicate preserving order. The
|
|
183
|
+
// CLI-side twin of the web's `normalizeTags`; kept inline to keep this module
|
|
184
|
+
// zero-import. Pure.
|
|
185
|
+
function normalizeTagList(list) {
|
|
186
|
+
const seen = new Set();
|
|
187
|
+
const out = [];
|
|
188
|
+
for (const item of list) {
|
|
189
|
+
if (typeof item !== 'string') continue;
|
|
190
|
+
const t = item.trim();
|
|
191
|
+
if (!t || seen.has(t)) continue;
|
|
192
|
+
seen.add(t);
|
|
193
|
+
out.push(t);
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
157
198
|
// Coerce the date-range flags to a `{ from, to }` DateRange or null. `--range`
|
|
158
199
|
// (a JSON object string) wins; else `--from`/`--to` shorthand builds one (both
|
|
159
200
|
// keys always present so the shape matches the app's DateRange). A malformed
|
package/src/link.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
parseOwnerArg,
|
|
26
26
|
parseViewArg,
|
|
27
27
|
parseRangeArg,
|
|
28
|
+
parseTagsArg,
|
|
28
29
|
resolveScopeArg,
|
|
29
30
|
surfaceFor,
|
|
30
31
|
} from './deeplink-pure.mjs';
|
|
@@ -66,6 +67,7 @@ export async function link(args) {
|
|
|
66
67
|
const owner = parseOwnerArg(args.owner);
|
|
67
68
|
const view = parseViewArg(args.view);
|
|
68
69
|
const range = parseRangeArg(args);
|
|
70
|
+
const tags = parseTagsArg(args.tags);
|
|
69
71
|
const archived = Boolean(args.archived);
|
|
70
72
|
|
|
71
73
|
const gaveAnyInput =
|
|
@@ -75,6 +77,7 @@ export async function link(args) {
|
|
|
75
77
|
owner !== 'all' ||
|
|
76
78
|
view !== 'scope' ||
|
|
77
79
|
range !== null ||
|
|
80
|
+
tags.length > 0 ||
|
|
78
81
|
archived;
|
|
79
82
|
|
|
80
83
|
// Bare `lorekit link` (no scope, no lesson, no filters) → the cwd's
|
|
@@ -90,6 +93,7 @@ export async function link(args) {
|
|
|
90
93
|
if (key) params.lesson = { scope, key };
|
|
91
94
|
if (q) params.q = q;
|
|
92
95
|
if (owner !== 'all') params.owner = owner;
|
|
96
|
+
if (tags.length) params.tags = tags;
|
|
93
97
|
if (view !== 'scope') params.view = view;
|
|
94
98
|
if (range !== null) params.range = range;
|
|
95
99
|
if (archived) params.archived = true;
|