@litfamily/litopencode 1.0.10 → 1.0.12
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/CHANGELOG.md +12 -0
- package/README-Ko-KR.md +40 -523
- package/README.md +39 -520
- package/dist/activation.d.ts +2 -0
- package/dist/activation.js +1 -1
- package/dist/jev-skill-hint.d.ts +2 -0
- package/dist/jev-skill-hint.js +28 -1
- package/dist/knowledge.js +3 -0
- package/dist/server.js +7 -1
- package/docs/assets/readme/badge-version.svg +1 -1
- package/package.json +4 -2
- package/skills/lit-typographic-motion/package-lock.json +4 -4
- package/skills/lit-typographic-motion/package.json +1 -1
- package/skills/managed-skill-manifest.json +2 -2
- package/tools/check-pack-payload.mjs +21 -3
- package/tools/version-manifests.json +16 -4
package/dist/activation.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export type ChatMessageActivationOptions = {
|
|
|
26
26
|
readonly onSkillActivation?: (sessionID: string, skillId: string, appendedLit?: boolean) => void | Promise<void>;
|
|
27
27
|
/** Consumes the skill a trusted command activation recorded for this session, if any. */
|
|
28
28
|
readonly trustedInjectedSkill?: (sessionID: string) => string | undefined;
|
|
29
|
+
/** Restores that skill after the root-turn reset; the command already showed its activation toast. */
|
|
30
|
+
readonly onTrustedSkillResume?: (sessionID: string, skillId: string) => void | Promise<void>;
|
|
29
31
|
/** Consumes the mark `command.execute.before` left for this session's next message. */
|
|
30
32
|
readonly isCommandTurn?: (sessionID: string) => boolean;
|
|
31
33
|
readonly skillHint?: (sessionID: string, promptText: string) => Promise<JevSkillHintPart | undefined>;
|
package/dist/activation.js
CHANGED
|
@@ -100,7 +100,7 @@ export function createChatMessageActivationHook(projectRoot, options = {}) {
|
|
|
100
100
|
if (!childSession) {
|
|
101
101
|
await options.onRootUserTurn?.(input.sessionID);
|
|
102
102
|
if (trustedInjectedSkill !== undefined) {
|
|
103
|
-
await options.
|
|
103
|
+
await options.onTrustedSkillResume?.(input.sessionID, trustedInjectedSkill);
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
106
|
}
|
package/dist/jev-skill-hint.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export type JevSkillHintOptions = {
|
|
|
22
22
|
readonly fetch?: JevFetch;
|
|
23
23
|
readonly catalog?: readonly JevCatalogEntry[];
|
|
24
24
|
readonly traceFile?: string;
|
|
25
|
+
/** Project root: every folder between it and the trace file must be a real folder, never a symlink. */
|
|
26
|
+
readonly traceRoot?: string;
|
|
25
27
|
readonly now?: () => number;
|
|
26
28
|
};
|
|
27
29
|
export declare function jevSkillHintStatus(env?: NodeJS.ProcessEnv): JevSkillHintStatus;
|
package/dist/jev-skill-hint.js
CHANGED
|
@@ -155,6 +155,27 @@ function validatedAnswer(raw, allowed, threshold) {
|
|
|
155
155
|
return { confidence };
|
|
156
156
|
return { choice, confidence };
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Walks the trace folder below the project root one level at a time, creating a missing folder
|
|
160
|
+
* without following links. Any symlink or non-folder on the way means no trace.
|
|
161
|
+
*/
|
|
162
|
+
async function realTraceFolder(root, folder) {
|
|
163
|
+
const relative = path.relative(root, folder);
|
|
164
|
+
if (relative.startsWith("..") || path.isAbsolute(relative))
|
|
165
|
+
return false;
|
|
166
|
+
let current = root;
|
|
167
|
+
for (const segment of relative.split(path.sep).filter((part) => part !== "")) {
|
|
168
|
+
current = path.join(current, segment);
|
|
169
|
+
let stat = await fs.lstat(current).catch(() => undefined);
|
|
170
|
+
if (stat === undefined) {
|
|
171
|
+
await fs.mkdir(current).catch(() => undefined);
|
|
172
|
+
stat = await fs.lstat(current).catch(() => undefined);
|
|
173
|
+
}
|
|
174
|
+
if (stat === undefined || stat.isSymbolicLink() || !stat.isDirectory())
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
158
179
|
export function createJevSkillHint(options = {}) {
|
|
159
180
|
const env = options.env ?? process.env;
|
|
160
181
|
const fetchImpl = options.fetch ?? ((url, init) => fetch(url, init));
|
|
@@ -163,8 +184,11 @@ export function createJevSkillHint(options = {}) {
|
|
|
163
184
|
async function trace(record) {
|
|
164
185
|
if (env[`${jevFlag}_TRACE`] !== "1" || options.traceFile === undefined)
|
|
165
186
|
return;
|
|
187
|
+
const folder = path.dirname(options.traceFile);
|
|
188
|
+
const root = options.traceRoot ?? path.dirname(folder);
|
|
166
189
|
try {
|
|
167
|
-
await
|
|
190
|
+
if (!(await realTraceFolder(root, folder)))
|
|
191
|
+
return;
|
|
168
192
|
// Refuse a symlink or other non-file at the trace path; O_NOFOLLOW closes the lstat race.
|
|
169
193
|
const existing = await fs.lstat(options.traceFile).catch(() => undefined);
|
|
170
194
|
if (existing !== undefined && !existing.isFile())
|
|
@@ -172,6 +196,9 @@ export function createJevSkillHint(options = {}) {
|
|
|
172
196
|
const flags = fsConstants.O_WRONLY | fsConstants.O_APPEND | fsConstants.O_CREAT | (fsConstants.O_NOFOLLOW ?? 0);
|
|
173
197
|
const handle = await fs.open(options.traceFile, flags, 0o600);
|
|
174
198
|
try {
|
|
199
|
+
// A folder swapped for a symlink after the first check still gets no record.
|
|
200
|
+
if (!(await realTraceFolder(root, folder)))
|
|
201
|
+
return;
|
|
175
202
|
await handle.appendFile(`${JSON.stringify(record)}\n`, "utf8");
|
|
176
203
|
}
|
|
177
204
|
finally {
|
package/dist/knowledge.js
CHANGED
|
@@ -340,6 +340,9 @@ async function readRegularFileIfPresent(filePath, paths, options = {}) {
|
|
|
340
340
|
try {
|
|
341
341
|
await assertParentStable(parent, paths);
|
|
342
342
|
const descriptorStat = await handle.stat();
|
|
343
|
+
if (descriptorStat.isFile() && descriptorStat.nlink === 0) {
|
|
344
|
+
throw new KnowledgeFileDisappearedError(`Knowledge file disappeared after descriptor open at ${filePath}.`);
|
|
345
|
+
}
|
|
343
346
|
if (!descriptorStat.isFile() || !sameFileIdentity(fileIdentityOf(descriptorStat), fileIdentityOf(pathStat))) {
|
|
344
347
|
throw new KnowledgeStoreError(`Unsafe knowledge file replacement at ${filePath}.`);
|
|
345
348
|
}
|
package/dist/server.js
CHANGED
|
@@ -54,7 +54,10 @@ export function createLitOpenCodePlugin(autoUpdateRunner = runPluginAutoUpdate)
|
|
|
54
54
|
ignitionState.activate(sessionID, skillId, !appendedLit);
|
|
55
55
|
};
|
|
56
56
|
const boundedAuthorityEvent = createBoundedAuthorityEventHook(root, input?.client, loaded.config.boundedAuthority);
|
|
57
|
-
const skillHint = createJevSkillHint({
|
|
57
|
+
const skillHint = createJevSkillHint({
|
|
58
|
+
traceFile: path.join(loaded.paths.logsDir, "jev-skill-hint.jsonl"),
|
|
59
|
+
traceRoot: loaded.paths.projectRoot
|
|
60
|
+
});
|
|
58
61
|
// Keyed by session: the host resolves command parts into a new array before chat.message runs.
|
|
59
62
|
const pendingCommand = new Set();
|
|
60
63
|
const trustedCommandActivations = new Map();
|
|
@@ -96,6 +99,9 @@ export function createLitOpenCodePlugin(autoUpdateRunner = runPluginAutoUpdate)
|
|
|
96
99
|
const skillId = trustedCommandActivations.get(sessionID);
|
|
97
100
|
trustedCommandActivations.delete(sessionID);
|
|
98
101
|
return skillId;
|
|
102
|
+
},
|
|
103
|
+
onTrustedSkillResume: (sessionID, skillId) => {
|
|
104
|
+
ignitionState.activate(sessionID, skillId);
|
|
99
105
|
}
|
|
100
106
|
}),
|
|
101
107
|
"command.execute.before": async (commandInput, commandOutput) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.12"><title>release: 1.0.12</title><g shape-rendering="crispEdges"><rect width="49" height="20" fill="#080d14"/><rect x="49" width="97" height="20" fill="#ff6337"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text x="255" y="140" textLength="390" transform="scale(.1)">release</text><text x="965" y="140" textLength="870" transform="scale(.1)" fill="#080D14">1.0.12</text></g></svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litfamily/litopencode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "LitOpenCode OpenCode plugin bootstrap package.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
"build": "node tools/run-build.mjs",
|
|
29
29
|
"check:pack-payload": "npm run build && npm pack --dry-run --json --ignore-scripts | node tools/check-pack-payload.mjs --stdin",
|
|
30
30
|
"check:version": "node tools/check-version-lockstep.mjs",
|
|
31
|
+
"check:npm-readme": "node tools/readme-for-npm.mjs check",
|
|
31
32
|
"check:managed-skill-manifest": "node tools/gen-managed-skill-manifest.mjs --check",
|
|
32
33
|
"gen:managed-skill-manifest": "node tools/gen-managed-skill-manifest.mjs",
|
|
33
34
|
"prepublishOnly": "node tools/prepublish-test-gate.mjs",
|
|
34
|
-
"prepack": "npm run build",
|
|
35
|
+
"prepack": "npm run build && node tools/readme-for-npm.mjs apply",
|
|
36
|
+
"postpack": "node tools/readme-for-npm.mjs restore",
|
|
35
37
|
"pretest": "npm run build",
|
|
36
38
|
"qa:behavior-replacement": "node tools/run-behavior-replacement-probes.mjs",
|
|
37
39
|
"qa:installed-resource-tamper": "node tools/run-installed-resource-tamper-probe.mjs",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"opentype.js": "1.3.4",
|
|
12
12
|
"playwright-core": "1.55.1",
|
|
13
|
-
"ws": "8.
|
|
13
|
+
"ws": "8.22.0"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"node_modules/opentype.js": {
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"license": "MIT"
|
|
55
55
|
},
|
|
56
56
|
"node_modules/ws": {
|
|
57
|
-
"version": "8.
|
|
58
|
-
"resolved": "https://registry.npmjs.org/ws/-/ws-8.
|
|
59
|
-
"integrity": "sha512-
|
|
57
|
+
"version": "8.22.0",
|
|
58
|
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz",
|
|
59
|
+
"integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==",
|
|
60
60
|
"license": "MIT",
|
|
61
61
|
"engines": {
|
|
62
62
|
"node": ">=10.0.0"
|
|
@@ -3273,11 +3273,11 @@
|
|
|
3273
3273
|
},
|
|
3274
3274
|
{
|
|
3275
3275
|
"path": "package-lock.json",
|
|
3276
|
-
"sha256": "
|
|
3276
|
+
"sha256": "141539ca253c3cf4f81988db6f69aaf78574d0c8ca9e9d7e45f56af8f2e96cac"
|
|
3277
3277
|
},
|
|
3278
3278
|
{
|
|
3279
3279
|
"path": "package.json",
|
|
3280
|
-
"sha256": "
|
|
3280
|
+
"sha256": "5962e5607dba2b2d8e08fbf51e982e8517ee26a84c43ac838934e8394f3551cd"
|
|
3281
3281
|
},
|
|
3282
3282
|
{
|
|
3283
3283
|
"path": "probe.mjs",
|
|
@@ -13,6 +13,9 @@ const FORBIDDEN_PATH_RULES = [
|
|
|
13
13
|
{ label: "repository cover vector", pattern: /^docs\/assets\/cover\.svg$/u },
|
|
14
14
|
{ label: "unapproved README presentation asset", pattern: /^docs\/assets\/readme\/(?!(?:ascii-readme|badge-version|badge-license|lucide-book-open|lucide-play|lucide-shield-check)\.svg$|litopencode-wordmark\.svg$|litopencode-clay-icon\.png$|(?:litfamily-machines|poster)\.png$|ignition-film\.mp4$|ignition-readme\.gif$|(?:Lucide-LICENSE|JetBrainsMono-OFL)\.txt$)/u },
|
|
15
15
|
{ label: "maintainer release checklist", pattern: /^docs\/release-checklist\.md$/u },
|
|
16
|
+
{ label: "npm README source (packed as README.md / README-Ko-KR.md)", pattern: /^README-npm(?:-Ko-KR)?\.md$/u },
|
|
17
|
+
{ label: "GitHub README backup from the npm README swap", pattern: /^\.readme-npm-backup(?:\/|$)/u },
|
|
18
|
+
{ label: "npm README swap script", pattern: /^tools\/readme-for-npm\.mjs$/u },
|
|
16
19
|
{ label: "internal recon/spec document", pattern: /^docs\/(?:recon|spec)(\/|$)/u },
|
|
17
20
|
{ label: "implementation plan", pattern: /(^|\/)plans(\/|$)/u },
|
|
18
21
|
{ label: ".litcodex runtime state", pattern: /(^|\/)\.litcodex(\/|$)/u },
|
|
@@ -70,18 +73,33 @@ const REQUIRED_README_PATHS = [
|
|
|
70
73
|
"docs/assets/readme/poster.png"
|
|
71
74
|
];
|
|
72
75
|
|
|
73
|
-
// Every image the
|
|
76
|
+
// Every image the READMEs show must ship: the npm pages load pinned package-CDN copies, and the
|
|
77
|
+
// GitHub pages load the same files by relative path. Inside a packed artifact the npm sources
|
|
78
|
+
// are absent and README.md / README-Ko-KR.md already hold the npm pages.
|
|
74
79
|
async function readmeImagePaths() {
|
|
75
80
|
const { version } = JSON.parse(await fs.readFile("package.json", "utf8"));
|
|
76
81
|
const prefix = `https://cdn.jsdelivr.net/npm/@litfamily/litopencode@${version}/`;
|
|
77
82
|
const paths = new Set();
|
|
78
|
-
|
|
83
|
+
const sources = [];
|
|
84
|
+
for (const [npmSource, target] of [["README-npm.md", "README.md"], ["README-npm-Ko-KR.md", "README-Ko-KR.md"]]) {
|
|
85
|
+
sources.push(target);
|
|
86
|
+
try {
|
|
87
|
+
await fs.access(npmSource);
|
|
88
|
+
sources.push(npmSource);
|
|
89
|
+
} catch {
|
|
90
|
+
// Packed artifact: the target already is the npm page.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const file of sources) {
|
|
79
94
|
const text = await fs.readFile(file, "utf8");
|
|
80
95
|
const targets = [
|
|
81
96
|
...[...text.matchAll(/!\[[^\]]*\]\(([^)\s]+)\)/gu)].map((match) => match[1]),
|
|
82
97
|
...[...text.matchAll(/\b(?:src|srcset)="([^"\s]+)"/gu)].map((match) => match[1])
|
|
83
98
|
];
|
|
84
|
-
for (const target of targets)
|
|
99
|
+
for (const target of targets) {
|
|
100
|
+
if (target.startsWith(prefix)) paths.add(target.slice(prefix.length));
|
|
101
|
+
else if (target.startsWith("./")) paths.add(target.slice(2));
|
|
102
|
+
}
|
|
85
103
|
}
|
|
86
104
|
return [...paths];
|
|
87
105
|
}
|
|
@@ -11,14 +11,26 @@
|
|
|
11
11
|
{
|
|
12
12
|
"path": "README-Ko-KR.md",
|
|
13
13
|
"kind": "pinned",
|
|
14
|
-
"occurrences":
|
|
15
|
-
"why": "The
|
|
14
|
+
"occurrences": 2,
|
|
15
|
+
"why": "The GitHub landing pages load assets by relative path; the release badge alt text and the checkout-version note repeat the package version."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "README-npm-Ko-KR.md",
|
|
19
|
+
"kind": "pinned",
|
|
20
|
+
"occurrences": 18,
|
|
21
|
+
"why": "The npm package pages pin artwork and package links to the current package version; tools/readme-for-npm.mjs copies them over README.md and README-Ko-KR.md at pack time."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "README-npm.md",
|
|
25
|
+
"kind": "pinned",
|
|
26
|
+
"occurrences": 18,
|
|
27
|
+
"why": "The npm package pages pin artwork and package links to the current package version; tools/readme-for-npm.mjs copies them over README.md and README-Ko-KR.md at pack time."
|
|
16
28
|
},
|
|
17
29
|
{
|
|
18
30
|
"path": "README.md",
|
|
19
31
|
"kind": "pinned",
|
|
20
|
-
"occurrences":
|
|
21
|
-
"why": "The
|
|
32
|
+
"occurrences": 2,
|
|
33
|
+
"why": "The GitHub landing pages load assets by relative path; the release badge alt text and the checkout-version note repeat the package version."
|
|
22
34
|
},
|
|
23
35
|
{
|
|
24
36
|
"path": "docs/assets/readme/badge-version.svg",
|