@younndai/lyt-skills 0.9.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.
Files changed (43) hide show
  1. package/LICENSE +200 -0
  2. package/NOTICE +23 -0
  3. package/README.md +124 -0
  4. package/dist/commands/skills-install.d.ts +3 -0
  5. package/dist/commands/skills-install.d.ts.map +1 -0
  6. package/dist/commands/skills-install.js +89 -0
  7. package/dist/commands/skills-install.js.map +1 -0
  8. package/dist/commands/skills-list.d.ts +3 -0
  9. package/dist/commands/skills-list.d.ts.map +1 -0
  10. package/dist/commands/skills-list.js +79 -0
  11. package/dist/commands/skills-list.js.map +1 -0
  12. package/dist/commands/skills.d.ts +3 -0
  13. package/dist/commands/skills.d.ts.map +1 -0
  14. package/dist/commands/skills.js +26 -0
  15. package/dist/commands/skills.js.map +1 -0
  16. package/dist/index.d.ts +6 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +19 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/list.d.ts +19 -0
  21. package/dist/list.d.ts.map +1 -0
  22. package/dist/list.js +65 -0
  23. package/dist/list.js.map +1 -0
  24. package/dist/symlink.d.ts +41 -0
  25. package/dist/symlink.d.ts.map +1 -0
  26. package/dist/symlink.js +233 -0
  27. package/dist/symlink.js.map +1 -0
  28. package/package.json +72 -0
  29. package/skills/lyt-capture/SKILL.md +137 -0
  30. package/skills/lyt-decision/SKILL.md +52 -0
  31. package/skills/lyt-handoff/SKILL.md +52 -0
  32. package/skills/lyt-insight/SKILL.md +55 -0
  33. package/skills/lyt-mesh-explore/SKILL.md +190 -0
  34. package/skills/lyt-pattern/SKILL.md +71 -0
  35. package/skills/lyt-plan/SKILL.md +60 -0
  36. package/skills/lyt-pod/SKILL.md +205 -0
  37. package/skills/lyt-primer-context/SKILL.md +206 -0
  38. package/skills/lyt-progress/SKILL.md +48 -0
  39. package/skills/lyt-recall/SKILL.md +98 -0
  40. package/skills/lyt-result/SKILL.md +49 -0
  41. package/skills/lyt-retro/SKILL.md +54 -0
  42. package/skills/lyt-search/SKILL.md +144 -0
  43. package/skills/lyt-sync/SKILL.md +145 -0
package/dist/list.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { type Runtime } from "./symlink.js";
2
+ export type SkillRuntimeState = "symlink" | "copy" | "missing" | "divergent" | "not-a-dir";
3
+ export interface SkillRuntimeRow {
4
+ name: string;
5
+ lytVersion: string | null;
6
+ runtimes: Record<Runtime, SkillRuntimeState>;
7
+ }
8
+ export interface ListSkillsResult {
9
+ sourceDir: string;
10
+ runtimes: readonly Runtime[];
11
+ skills: readonly SkillRuntimeRow[];
12
+ }
13
+ export interface ListSkillsOptions {
14
+ sourceDir?: string | undefined;
15
+ runtimes?: readonly Runtime[] | undefined;
16
+ targetDirOverrides?: Partial<Record<Runtime, string>> | undefined;
17
+ }
18
+ export declare function listSkillsTriRuntime(opts?: ListSkillsOptions): ListSkillsResult;
19
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AAmBA,OAAO,EAKL,KAAK,OAAO,EACb,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;AAE3F,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;IAC1C,kBAAkB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;CACnE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,iBAAsB,GAAG,gBAAgB,CAsBnF"}
package/dist/list.js ADDED
@@ -0,0 +1,65 @@
1
+ /*
2
+ * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { existsSync, lstatSync, readFileSync, readlinkSync, statSync } from "node:fs";
17
+ import { join, resolve } from "node:path";
18
+ import { ALL_RUNTIMES, getBundledSkillsDir, getRuntimeTargetDir, listBundledSkills, } from "./symlink.js";
19
+ export function listSkillsTriRuntime(opts = {}) {
20
+ const sourceDir = opts.sourceDir ? resolve(opts.sourceDir) : getBundledSkillsDir();
21
+ const runtimes = opts.runtimes ?? ALL_RUNTIMES;
22
+ const skillNames = [...listBundledSkills(sourceDir)].sort();
23
+ const skills = skillNames.map((name) => {
24
+ const skillSourceDir = resolve(join(sourceDir, name));
25
+ const lytVersion = readLytVersion(join(skillSourceDir, "SKILL.md"));
26
+ const runtimeStates = {
27
+ claude: "missing",
28
+ codex: "missing",
29
+ agents: "missing",
30
+ };
31
+ for (const runtime of runtimes) {
32
+ const targetBase = opts.targetDirOverrides?.[runtime] ?? getRuntimeTargetDir(runtime);
33
+ const targetSkillDir = join(targetBase, name);
34
+ runtimeStates[runtime] = detectState(targetSkillDir, skillSourceDir);
35
+ }
36
+ return { name, lytVersion, runtimes: runtimeStates };
37
+ });
38
+ return { sourceDir, runtimes, skills };
39
+ }
40
+ function detectState(targetSkillDir, sourceSkillDir) {
41
+ if (!existsSync(targetSkillDir))
42
+ return "missing";
43
+ const stat = lstatSync(targetSkillDir);
44
+ if (stat.isSymbolicLink()) {
45
+ const target = resolve(readlinkSync(targetSkillDir));
46
+ return target === sourceSkillDir ? "symlink" : "divergent";
47
+ }
48
+ if (statSync(targetSkillDir).isDirectory())
49
+ return "copy";
50
+ return "not-a-dir";
51
+ }
52
+ function readLytVersion(skillMdPath) {
53
+ if (!existsSync(skillMdPath))
54
+ return null;
55
+ const md = readFileSync(skillMdPath, "utf8");
56
+ const match = md.match(/^---\n([\s\S]*?)\n---/);
57
+ if (!match)
58
+ return null;
59
+ const fm = match[1];
60
+ const versionMatch = fm.match(/^lyt-version:\s*(\S.*)$/m);
61
+ if (!versionMatch)
62
+ return null;
63
+ return versionMatch[1].trim();
64
+ }
65
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ export type Runtime = "claude" | "codex" | "agents";
2
+ export declare const ALL_RUNTIMES: readonly Runtime[];
3
+ export type SymlinkStatus = "installed-symlink" | "installed-copy" | "installed-copy-fallback" | "already-linked" | "replaced" | "divergent-symlink" | "renamed-collision" | "target-not-a-directory";
4
+ export interface SkillRuntimeResult {
5
+ skill: string;
6
+ runtime: Runtime;
7
+ targetPath: string;
8
+ status: SymlinkStatus;
9
+ message?: string;
10
+ }
11
+ export interface SymlinkResult {
12
+ sourceDir: string;
13
+ runtimes: readonly Runtime[];
14
+ results: readonly SkillRuntimeResult[];
15
+ }
16
+ export interface SymlinkSkillsOptions {
17
+ sourceDir?: string | undefined;
18
+ runtimes?: readonly Runtime[] | undefined;
19
+ skillNames?: readonly string[] | undefined;
20
+ copy?: boolean | undefined;
21
+ force?: boolean | undefined;
22
+ targetDirOverrides?: Partial<Record<Runtime, string>> | undefined;
23
+ /**
24
+ * Test-only seam: override the low-level symlink call. Required for EPERM-
25
+ * fallback coverage because ESM module exports are not vi.spyOn-configurable.
26
+ * Production callers leave this undefined.
27
+ */
28
+ symlinkFnOverride?: ((target: string, path: string, type: "junction" | "dir") => void) | undefined;
29
+ /**
30
+ * Test-only seam: override the collision-rename timestamp. Production
31
+ * callers leave this undefined (real UTC clock). Tests inject a fixed stamp
32
+ * to exercise the repeat-collision disambiguator deterministically (W1.1
33
+ * test d).
34
+ */
35
+ collisionStampFn?: (() => string) | undefined;
36
+ }
37
+ export declare function getRuntimeTargetDir(runtime: Runtime): string;
38
+ export declare function getBundledSkillsDir(): string;
39
+ export declare function listBundledSkills(sourceDir: string): readonly string[];
40
+ export declare function symlinkSkillsTriRuntime(opts?: SymlinkSkillsOptions): SymlinkResult;
41
+ //# sourceMappingURL=symlink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symlink.d.ts","sourceRoot":"","sources":["../src/symlink.ts"],"names":[],"mappings":"AAiCA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpD,eAAO,MAAM,YAAY,EAAE,SAAS,OAAO,EAAkC,CAAC;AAE9E,MAAM,MAAM,aAAa,GACrB,mBAAmB,GACnB,gBAAgB,GAChB,yBAAyB,GACzB,gBAAgB,GAChB,UAAU,GACV,mBAAmB,GAInB,mBAAmB,GACnB,wBAAwB,CAAC;AAE7B,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;IAC1C,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;IAClE;;;;OAIG;IACH,iBAAiB,CAAC,EACd,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,KAAK,KAAK,IAAI,CAAC,GAClE,SAAS,CAAC;IACd;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;CAC/C;AAQD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAE5D;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAStE;AAiCD,wBAAgB,uBAAuB,CAAC,IAAI,GAAE,oBAAyB,GAAG,aAAa,CAyCtF"}
@@ -0,0 +1,233 @@
1
+ /*
2
+ * Copyright 2026 MARLINK TRADING SRL (YounndAI)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, readlinkSync, renameSync, rmSync, statSync, symlinkSync, } from "node:fs";
17
+ import { homedir, platform } from "node:os";
18
+ import { basename, dirname, join, resolve } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+ export const ALL_RUNTIMES = ["claude", "codex", "agents"];
21
+ const RUNTIME_DEFAULT_DIR = {
22
+ claude: () => join(homedir(), ".claude", "skills"),
23
+ codex: () => join(homedir(), ".codex", "skills"),
24
+ agents: () => join(homedir(), ".agents", "skills"),
25
+ };
26
+ export function getRuntimeTargetDir(runtime) {
27
+ return RUNTIME_DEFAULT_DIR[runtime]();
28
+ }
29
+ export function getBundledSkillsDir() {
30
+ const here = dirname(fileURLToPath(import.meta.url));
31
+ return resolve(here, "..", "skills");
32
+ }
33
+ export function listBundledSkills(sourceDir) {
34
+ if (!existsSync(sourceDir)) {
35
+ return [];
36
+ }
37
+ return readdirSync(sourceDir).filter((name) => {
38
+ const fullPath = join(sourceDir, name);
39
+ if (!statSync(fullPath).isDirectory())
40
+ return false;
41
+ return existsSync(join(fullPath, "SKILL.md"));
42
+ });
43
+ }
44
+ function symlinkType() {
45
+ return platform() === "win32" ? "junction" : "dir";
46
+ }
47
+ function createLink(sourceSkillDir, targetSkillDir, copy, symlinkFn) {
48
+ if (copy) {
49
+ cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
50
+ return "installed-copy";
51
+ }
52
+ try {
53
+ symlinkFn(sourceSkillDir, targetSkillDir, symlinkType());
54
+ return "installed-symlink";
55
+ }
56
+ catch (err) {
57
+ const code = err.code;
58
+ if (code === "EPERM" || code === "EACCES") {
59
+ cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
60
+ return "installed-copy-fallback";
61
+ }
62
+ throw err;
63
+ }
64
+ }
65
+ const defaultSymlinkFn = (target, path, type) => {
66
+ symlinkSync(target, path, type);
67
+ };
68
+ export function symlinkSkillsTriRuntime(opts = {}) {
69
+ const sourceDir = opts.sourceDir ? resolve(opts.sourceDir) : getBundledSkillsDir();
70
+ const runtimes = opts.runtimes ?? ALL_RUNTIMES;
71
+ const copy = opts.copy ?? false;
72
+ const force = opts.force ?? false;
73
+ if (!existsSync(sourceDir)) {
74
+ throw new Error(`Bundled skills directory not found at: ${sourceDir}. Did the package build correctly?`);
75
+ }
76
+ const allBundled = listBundledSkills(sourceDir);
77
+ const skillNames = opts.skillNames
78
+ ? allBundled.filter((n) => opts.skillNames.includes(n))
79
+ : allBundled;
80
+ const symlinkFn = opts.symlinkFnOverride ?? defaultSymlinkFn;
81
+ const collisionStampFn = opts.collisionStampFn ?? defaultCollisionStamp;
82
+ const results = [];
83
+ for (const runtime of runtimes) {
84
+ const targetBase = opts.targetDirOverrides?.[runtime] ?? getRuntimeTargetDir(runtime);
85
+ mkdirSync(targetBase, { recursive: true });
86
+ for (const skill of skillNames) {
87
+ const sourceSkillDir = resolve(join(sourceDir, skill));
88
+ const targetSkillDir = join(targetBase, skill);
89
+ const result = installOne({
90
+ sourceSkillDir,
91
+ targetSkillDir,
92
+ copy,
93
+ force,
94
+ symlinkFn,
95
+ collisionStampFn,
96
+ });
97
+ results.push({ skill, runtime, targetPath: targetSkillDir, ...result });
98
+ }
99
+ }
100
+ return { sourceDir, runtimes, results };
101
+ }
102
+ function installOne(input) {
103
+ const { sourceSkillDir, targetSkillDir, copy, force, symlinkFn, collisionStampFn } = input;
104
+ if (!existsSync(targetSkillDir)) {
105
+ return { status: createLink(sourceSkillDir, targetSkillDir, copy, symlinkFn) };
106
+ }
107
+ const stat = lstatSync(targetSkillDir);
108
+ if (stat.isSymbolicLink()) {
109
+ const existingTarget = resolve(readlinkSync(targetSkillDir));
110
+ if (existingTarget === sourceSkillDir) {
111
+ return { status: "already-linked" };
112
+ }
113
+ if (!force) {
114
+ return {
115
+ status: "divergent-symlink",
116
+ message: `existing symlink points to ${existingTarget}; re-run with --force to replace`,
117
+ };
118
+ }
119
+ rmSync(targetSkillDir, { recursive: true, force: true });
120
+ const newStatus = createLink(sourceSkillDir, targetSkillDir, copy, symlinkFn);
121
+ return {
122
+ status: "replaced",
123
+ message: `replaced symlink (was ${existingTarget}); now ${newStatus}`,
124
+ };
125
+ }
126
+ if (stat.isDirectory()) {
127
+ // Collision-safe install (D30.4 / OD-1 — P0). A NON-symlink directory
128
+ // sits at the skill target. We NEVER `rmSync` a directory whose content
129
+ // we did not write — that was the data-loss footgun in the prior
130
+ // `--force` branch (and the silent-refuse without it).
131
+ //
132
+ // - Pristine lyt copy-fallback (content byte-identical to the bundled
133
+ // source) → safe to upgrade in place to the symlink, no backup. This
134
+ // is the heal path for EPERM copy-fallback installs (D30.2 caveat b).
135
+ // - Anything else (the user's OWN dir, or a stale/divergent copy) →
136
+ // RENAME it aside to `<skill>.local-<ts>` (preserve — never lose),
137
+ // then install the lyt symlink over the now-free target.
138
+ //
139
+ // Note: `force` is intentionally NOT consulted here — collision-safety is
140
+ // the default, not an opt-in. A pristine replace and a rename-aside are
141
+ // both non-destructive, so there is nothing for `--force` to gate.
142
+ if (dirsEqual(sourceSkillDir, targetSkillDir)) {
143
+ rmSync(targetSkillDir, { recursive: true, force: true });
144
+ const newStatus = createLink(sourceSkillDir, targetSkillDir, copy, symlinkFn);
145
+ return {
146
+ status: "replaced",
147
+ message: `replaced pristine copy (content matched bundled); now ${newStatus}`,
148
+ };
149
+ }
150
+ const renamedTo = renameAside(targetSkillDir, collisionStampFn);
151
+ const newStatus = createLink(sourceSkillDir, targetSkillDir, copy, symlinkFn);
152
+ return {
153
+ status: "renamed-collision",
154
+ message: `⚠ a non-lyt directory was already at this skill target; set it aside as ` +
155
+ `'${basename(renamedTo)}' (preserved — nothing deleted) and installed the lyt skill (${newStatus})`,
156
+ };
157
+ }
158
+ // `force` is referenced only in the divergent-symlink branch above; reference
159
+ // it here too so the unused-param lint stays quiet on the collision-safe
160
+ // directory branch that deliberately ignores it.
161
+ void force;
162
+ return {
163
+ status: "target-not-a-directory",
164
+ message: `${targetSkillDir} is neither a symlink nor a directory; refusing to touch`,
165
+ };
166
+ }
167
+ // Compact UTC stamp for collision-rename suffixes: `YYYYMMDDTHHMMSSZ`.
168
+ // Colon-free (Windows forbids ':' in filenames), lexically sortable, and
169
+ // human-readable. OD-1 (2026-06-03): chosen over epoch-ms for legibility; the
170
+ // repeat-collision disambiguator in renameAside covers sub-second reruns.
171
+ function defaultCollisionStamp() {
172
+ return new Date()
173
+ .toISOString()
174
+ .replace(/[-:]/g, "")
175
+ .replace(/\.\d{3}Z$/, "Z");
176
+ }
177
+ // Rename a colliding non-lyt directory aside, preserving it. Appends a numeric
178
+ // `-N` disambiguator when the timestamped name already exists (covers two
179
+ // collisions within the same second / the same injected stamp — W1.1 test d).
180
+ // Returns the path it was renamed to. NEVER deletes.
181
+ function renameAside(targetSkillDir, stampFn) {
182
+ const base = `${targetSkillDir}.local-${stampFn()}`;
183
+ let candidate = base;
184
+ let n = 2;
185
+ while (existsSync(candidate)) {
186
+ candidate = `${base}-${n}`;
187
+ n++;
188
+ }
189
+ renameSync(targetSkillDir, candidate);
190
+ return candidate;
191
+ }
192
+ // Recursively compare two directory trees for byte-equality (names + file
193
+ // contents + structure), WITHOUT following symlinks. Detects a pristine lyt
194
+ // copy-fallback (content === bundled source) that is safe to upgrade to a
195
+ // symlink in place. A symlink anywhere in either tree → not equal
196
+ // (conservative: never treat a link-bearing tree as pristine).
197
+ function dirsEqual(a, b) {
198
+ let aNames;
199
+ let bNames;
200
+ try {
201
+ aNames = readdirSync(a).sort();
202
+ bNames = readdirSync(b).sort();
203
+ }
204
+ catch {
205
+ return false;
206
+ }
207
+ if (aNames.length !== bNames.length)
208
+ return false;
209
+ for (let i = 0; i < aNames.length; i++) {
210
+ if (aNames[i] !== bNames[i])
211
+ return false;
212
+ const an = join(a, aNames[i]);
213
+ const bn = join(b, bNames[i]);
214
+ const as = lstatSync(an);
215
+ const bs = lstatSync(bn);
216
+ if (as.isSymbolicLink() || bs.isSymbolicLink())
217
+ return false;
218
+ if (as.isDirectory() !== bs.isDirectory())
219
+ return false;
220
+ if (as.isDirectory()) {
221
+ if (!dirsEqual(an, bn))
222
+ return false;
223
+ }
224
+ else {
225
+ if (as.size !== bs.size)
226
+ return false;
227
+ if (!readFileSync(an).equals(readFileSync(bn)))
228
+ return false;
229
+ }
230
+ }
231
+ return true;
232
+ }
233
+ //# sourceMappingURL=symlink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symlink.js","sourceRoot":"","sources":["../src/symlink.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@younndai/lyt-skills",
3
+ "version": "0.9.0",
4
+ "description": "Lyt harness skills — 15 skills (/lyt-plan, /lyt-progress, /lyt-result, /lyt-retro, /lyt-insight, /lyt-handoff, /lyt-decision, /lyt-pattern, /lyt-capture, /lyt-recall, /lyt-pod, /lyt-mesh-explore, /lyt-primer-context, /lyt-search, /lyt-sync) symlinked tri-runtime into ~/.claude/skills/, ~/.codex/skills/, ~/.agents/skills/. Composed into the `lyt skills install` and `lyt skills list` meta-CLI verbs.",
5
+ "homepage": "https://linkyourthink.com",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/younndai/lyt",
9
+ "directory": "packages/lyt-skills"
10
+ },
11
+ "author": {
12
+ "name": "MARLINK TRADING SRL (YounndAI)",
13
+ "url": "https://younndai.com"
14
+ },
15
+ "contributors": [
16
+ {
17
+ "name": "Alexandru Mareș",
18
+ "url": "https://allemaar.com"
19
+ }
20
+ ],
21
+ "license": "Apache-2.0",
22
+ "engines": {
23
+ "node": ">=20.9",
24
+ "npm": ">=10"
25
+ },
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "skills",
38
+ "README.md",
39
+ "LICENSE",
40
+ "NOTICE"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsc",
44
+ "typecheck": "tsc --noEmit",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "clean": "rimraf --glob dist .turbo *.tsbuildinfo",
48
+ "prepack": "npm run clean && npm run build",
49
+ "prepublishOnly": "node ../../scripts/check-no-vendored-deps.mjs --root ../.."
50
+ },
51
+ "dependencies": {
52
+ "@younndai/lyt-vault": "^0.9.0",
53
+ "commander": "^12.1.0"
54
+ },
55
+ "devDependencies": {
56
+ "@lyt/typescript-config": "*",
57
+ "@types/node": "^22.10.5",
58
+ "rimraf": "*",
59
+ "typescript": "*",
60
+ "vitest": "*"
61
+ },
62
+ "publishConfig": {
63
+ "access": "public"
64
+ },
65
+ "keywords": [
66
+ "lyt",
67
+ "claude-code",
68
+ "skill",
69
+ "harness",
70
+ "younndai"
71
+ ]
72
+ }
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: lyt-capture
3
+ description: >
4
+ Capture a Figment (a markdown note) into a Lyt vault under the 8-field frontmatter contract. Trigger when the user runs /lyt-capture, or says "save this", "capture this", "add to my vault", "remember this in Lyt", or similar phrasing on content they want kept in their Lyt vault. Writes an Obsidian-flavored markdown file under <vault>/notes/ with frontmatter (title, created, modified, tags, purpose, topic, mesh-visibility, weight, meta) per yai.lyt v1 (arc §3). Wraps `lyt pattern run knowledge-capture capture` under the hood. Companion to lyt-recall.
5
+ visibility: public
6
+ lyt-version: 0.2.0
7
+ capabilities: [write]
8
+ runtimes: [claude, codex, agents]
9
+ requires_writable_vault: true
10
+ ---
11
+
12
+ # /lyt-capture
13
+
14
+ Capture a Figment into a Lyt vault under the v1 8-field frontmatter contract. A Figment is a single markdown file with frontmatter that the user can read in Obsidian, link to via `[[wikilinks]]`, and later find via `/lyt-recall`.
15
+
16
+ Under the hood this skill calls `lyt pattern run knowledge-capture capture` — but the harness convention is to fill in the Figment body inline rather than invoke the CLI verb directly, because the user usually has specific content they want captured (not a template stub).
17
+
18
+ ## When to invoke
19
+
20
+ When the user runs `/lyt-capture`, or says something like:
21
+
22
+ - "save this to my vault"
23
+ - "capture this"
24
+ - "add this to Lyt"
25
+ - "remember this"
26
+ - "put this in my notes"
27
+
28
+ If the user pastes content and only says "save it," interpret as `/lyt-capture` on the pasted content.
29
+
30
+ ## Phase 1 — Resolve the target vault
31
+
32
+ Follow this chain, in order, and stop at the first success:
33
+
34
+ 1. **`--vault <path>` argument** — if the user passed one in the invocation, use it as-is (resolve to absolute path if relative).
35
+ 2. **`$LYT_ACTIVE_VAULT` environment variable** — if set, use it.
36
+ 3. **`~/lyt/vaults/<handle>/main/`** — the default convention per the unified `{handle}/{repo}` naming (Windows: `%USERPROFILE%\lyt\vaults\<handle>\main`). Resolve `<handle>` from the pod identity (`identity.yon` / `pod.yon`); never hardcode it.
37
+
38
+ Do **not** guess from cwd. If `--vault` / env var / home convention all miss, ask the user which vault to target — fabricating a path from cwd would silently capture into the wrong vault.
39
+
40
+ If the resolved path **does not exist or is not a Lyt vault** (no `.lyt/vault.yon` inside), do **not** create files in it. Stop and tell the user:
41
+
42
+ > The target vault `<path>` doesn't exist (or isn't a Lyt vault — no `.lyt/vault.yon`). Create it with `lyt vault init <name>`, or pass `--vault <existing-path>`.
43
+
44
+ ## Phase 2 — Build the Figment under the v1 8-field contract
45
+
46
+ Per yai.lyt v1 frontmatter contract (arc §3), every captured Figment carries 8 mandatory frontmatter fields plus a `meta:` escape hatch.
47
+
48
+ 1. **Determine a title.** Use the user's explicit title if given. Otherwise infer a short noun-phrase (5–8 words) from the content. If unsure, ask.
49
+ 2. **Compute the slug** — lowercase, kebab-case the title, strip non-`[a-z0-9-]` characters, max 60 chars.
50
+ 3. **Compute the filename** — `notes/YYYY-MM-DD-<slug>.md` using today's date.
51
+ 4. **If a file already exists at that path**, append `-2`, `-3`, etc. to the slug until the path is free. Never overwrite.
52
+ 5. **Fill the 8 mandatory frontmatter fields:**
53
+
54
+ | Field | Source | Notes |
55
+ | ----------------- | ------------------------------------- | ---------------------------------------------------------------------------------------- |
56
+ | `title` | inferred / explicit | the title from step 1 |
57
+ | `created` | auto | ISO 8601 timestamp, e.g. `2026-05-28T20:36:42.880Z` |
58
+ | `modified` | auto | same as `created` at capture time |
59
+ | `tags` | inferred from content (optional list) | e.g. `[design, lyt]` |
60
+ | `purpose` | **PROMPT the user** if not inferrable | one-line author-stated purpose — "Why is this note worth keeping?" |
61
+ | `topic` | **PROMPT the user** if not inferrable | semantic category — free-text or a tag the user already uses |
62
+ | `mesh-visibility` | default `local` | one of `local` / `parent` / `public` — ask only if user signals a non-default visibility |
63
+ | `weight` | default `3` | int 1–5 importance signal — ask only if user signals a non-default weight |
64
+
65
+ **Do NOT author-fill `links-out-of-vault`.** That field is reserved for the lyt scanner (block-B); it is intentionally absent at capture-write time. The contract is "scanner-filled, not author-filled."
66
+
67
+ The `meta:` blob is a free-form escape hatch (`{}` by default) — fill it only when the content carries a field the 8-field contract does not cover.
68
+
69
+ 6. **Compose the file content** matching the `knowledge-capture/capture` pattern template:
70
+
71
+ ```markdown
72
+ ---
73
+ title: "<title>"
74
+ created: <ISO 8601 timestamp>
75
+ modified: <ISO 8601 timestamp>
76
+ tags: [<inferred tags, optional>]
77
+ purpose: "<one-line author-stated purpose>"
78
+ topic: "<semantic category>"
79
+ mesh-visibility: local
80
+ weight: 3
81
+ meta: {}
82
+ ---
83
+
84
+ <body content — plain Obsidian-flavored markdown>
85
+ ```
86
+
87
+ If the user provides explicit values for `mesh-visibility` (e.g. "share to parent") or `weight` (e.g. "important — weight 5"), apply them; otherwise use the defaults.
88
+
89
+ ## Phase 3 — Write, index, record metrics, confirm
90
+
91
+ 1. Write the file using your `Write` tool.
92
+ 2. **Index it so it's searchable immediately.** The figment you just wrote is on disk but NOT yet in the search/recall/primer caches — index it with:
93
+
94
+ ```
95
+ lyt capture --index-only <relative-path-from-vault-root> --vault <vault>
96
+ ```
97
+
98
+ e.g. `lyt capture --index-only notes/2026-06-10-my-note.md --vault personal/main`. This runs the same index-on-write path the CLI capture uses (FTS + lanes/arcs), so a subsequent `/lyt-recall` or `lyt search` hits with **no** manual `lyt reindex`. It is best-effort: if it reports `index deferred`, the figment is still saved and self-heals on the next search — do not block the capture or re-write the file. 3. **Record the capture metric.** Per arc §9.2 (the v1 ergonomics test), call:
99
+
100
+ ```
101
+ lyt capture-metric record --json '{"time_to_complete_ms":<measured-ms>,"field_values_json":"<json-snapshot>","llm_assist":true}'
102
+ ```
103
+
104
+ where `time_to_complete_ms` is the wall-clock from invocation to write, `field_values_json` is a JSON-encoded snapshot of the 8 fields actually written (so post-hoc analysis can compute fill rates), and `llm_assist` is `true` when this skill helped fill the frontmatter (almost always — set `false` only if the user pre-filled every field manually).
105
+
106
+ If the `lyt capture-metric` invocation fails (e.g. registry unreachable), do not block the capture — the metric is best-effort.
107
+
108
+ 4. Confirm to the user in one line:
109
+
110
+ > Captured to `<relative-path-from-vault-root>` in `<vault-path>`.
111
+
112
+ 5. Do **not** run any git operations. Phase 8's sync watcher (when shipped) will handle commits.
113
+
114
+ ## Rules
115
+
116
+ - **8-field contract is mandatory.** Every captured Figment carries all 8 mandatory fields. `purpose` and `topic` are author-supplied; if not inferrable from content, **prompt the user** rather than fabricating values.
117
+ - **`links-out-of-vault` is scanner-filled.** Never author-fill it. Its absence at capture-write is intentional.
118
+ - **`mesh-visibility` default = `local`; `weight` default = `3`.** Override only when the user signals a non-default.
119
+ - **User Figments are plain Obsidian-flavored markdown.** Wikilinks (`[[other-note]]`), tags (`#tag`), callouts, embeds — all fine. **Do NOT write YON inside user Figments.** YON is reserved for system files (`.lyt/vault.yon`, `.lyt/memscope.yon`, pattern.yon).
120
+ - **Never overwrite an existing Figment.** Always pick a fresh slug suffix.
121
+ - **Never touch files outside `<vault>/notes/`** unless the user explicitly says so.
122
+ - **Never run `git add`/`git commit`** — sync is a Phase 8 concern.
123
+ - If the user's content references other Figments by name, use `[[wikilinks]]` so Obsidian resolves them; don't expand them yourself.
124
+
125
+ ## Direct CLI path (when no content yet)
126
+
127
+ If the user wants a quick capture without you authoring the body inline, run the top-level alias directly:
128
+
129
+ ```
130
+ lyt capture "<the thought>" --vault <vault> --purpose <p> --topic <t>
131
+ ```
132
+
133
+ This is a true alias for `lyt pattern run knowledge-capture capture` (same v1 8-field ceremony) and indexes on write, so the capture is immediately searchable. The `<text>` becomes the title (slug derived from it); the file lands at `<vault>/notes/<date>-<slug>.md`. `purpose` and `topic` are mandatory — pass them as flags (or `--vars purpose=<p> --vars topic=<t>`; the `--vars` flag is **repeatable single** key=value, NOT a comma-joined list), or the command prompts for them on a TTY. `mesh-visibility` and `weight` default to `local` / `3`.
134
+
135
+ ## Companion skill
136
+
137
+ After capturing, the same content is findable via `/lyt-recall <keyword>`.
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: lyt-decision
3
+ description: >
4
+ Record a locked architectural or product decision as a Lyt Figment. Trigger when the user runs /lyt-decision, or says "lock this decision", "record the decision", "decide between options", "this is decided". Wraps `lyt pattern run decision-log decision`. Writes to <vault>/Projects/<project>/work/<date>-decision-<slug>.md.
5
+ visibility: public
6
+ lyt-version: 0.2.0
7
+ capabilities: [write]
8
+ runtimes: [claude, codex, agents]
9
+ requires_writable_vault: true
10
+ ---
11
+
12
+ # /lyt-decision
13
+
14
+ Record a locked decision — the choice, the alternatives, the reasoning. Use when a decision is durable enough that future-you (or a future agent) will need to read it cold.
15
+
16
+ ## When to invoke
17
+
18
+ - `/lyt-decision` slash trigger
19
+ - "lock this decision"
20
+ - "we decided X — record it"
21
+ - after a `/insight-assess` or `/insight-adversarial` lands a locked verdict
22
+ - before starting work whose direction depends on a non-obvious choice
23
+
24
+ ## Phase 1 — Resolve target
25
+
26
+ Same chain as `/lyt-plan`. Slug describes the decision (e.g., `transclusion-priming-files`, `gh-prefix-soft`).
27
+
28
+ ## Phase 2 — Run the verb
29
+
30
+ ```
31
+ lyt pattern run decision-log decision --vault <vault> --project <project> --slug <slug>
32
+ ```
33
+
34
+ ## Phase 3 — Fill the body
35
+
36
+ Sections to fill:
37
+
38
+ - **The decision** — one sentence, imperative voice
39
+ - **Context** — what forced this decision
40
+ - **Alternatives considered** — A / B / C with one-line each
41
+ - **Why this option won** — one paragraph; reference rationale if long-form
42
+ - **Reversibility** — how hard to undo, what downstream work depends on this
43
+ - **Cross-references** — wikilinks to rationale file (if separate), related decisions
44
+
45
+ ## Long-form reasoning
46
+
47
+ If the reasoning is too long for the decision file, write a separate `<date>-rationale-<slug>.md` via `lyt pattern run decision-log rationale --vault <v> --project <p> --slug <s>` and link from the decision file's `supports-decision:` (in the rationale) and `Cross-references` (in the decision).
48
+
49
+ ## Companion skills
50
+
51
+ - `/lyt-insight` — for distilled lessons LEARNED from acting on decisions
52
+ - `/lyt-handoff` — handoffs often reference locked decisions