@litfamily/litopencode 1.0.9 → 1.0.11

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.
@@ -253,6 +253,45 @@ npm uninstall -g @litfamily/litopencode
253
253
  - Public-source retrieval에는 SSRF 검사, redirect 검증, byte 제한, access verdict가
254
254
  있습니다. 가져온 본문은 명령이 아니라 데이터로 처리합니다.
255
255
 
256
+ ## Jev 스킬 힌트 (선택)
257
+
258
+ `chat.message` hook이 TypeSafe의 판단 모델 Jev에게 사용자 턴에 맞는 runtime 스킬을 물을 수
259
+ 있습니다. `LITOPENCODE_JEV=1`과 `TYPESAFE_API_KEY`가 모두 프로세스 환경에 있어야 켜지고, 그렇지
260
+ 않으면 네트워크 요청을 하지 않습니다. 키는 환경 변수에서만 읽고 `Authorization` header로만
261
+ 보내며, 파일·로그·화면에 남기지 않습니다.
262
+
263
+ - **대상 턴.** 루트 세션만 해당합니다. 슬래시 명령(`command.execute.before`가 펼친 명령 포함),
264
+ lit 경로가 이미 처리한 턴, 첨부 파일 본문 같은 synthetic part, 공백을 뺀 4자 미만의
265
+ 프롬프트는 건너뜁니다. OpenCode는 `command.execute.before`가 받은 parts 배열이 아니라 새 배열을
266
+ `chat.message`에 넘기므로, 명령은 세션에 표시를 남기고 그 세션의 다음 `chat.message`가 이
267
+ 표시를 소비합니다.
268
+ - **요청.** 조건에 맞는 턴마다 `POST https://api.typesafe.ai/v1/systemone`을 한 번 보내고
269
+ 다시 시도하지 않으며 리다이렉트도 따르지 않습니다(`redirect: "error"`). 본문에는 `model`,
270
+ `state`, `questions`만 들어갑니다. `state`는 프롬프트 앞 8,000자에서 홈 경로를 `~`로, 이메일을
271
+ `[email]`로, 토큰 형태의 문자열(`password=`/`token=`/`secret=` 형식의 대입과 키 자체 포함)을
272
+ `[secret]`으로 바꾼 뒤 2,000자에서 자릅니다. 잘린 자리에 토큰 문자 8개 이상이 남으면 그것도
273
+ `[secret]`이 됩니다. 호스트 이름이나 고객 이름처럼 토큰 형태가 아닌 내용은 그대로 전송됩니다.
274
+ 후보 목록은 runtime 스킬과 `none`입니다.
275
+ - **붙는 문장.** 힌트와 실패 안내는 `synthetic` text part로 붙습니다. OpenCode는 이 part를
276
+ 모델에 그대로 보내지만, 사용자가 쓴 글로 표시하거나 복사하지 않습니다.
277
+ - **응답.** HTTP 200, 올바른 JSON, 후보 id와 정확히 같은 `answers.which.choice`, 기준 이상의
278
+ `answers.which.confidence`를 모두 만족할 때만 힌트를 붙입니다. 붙는 문장은 LitOpenCode가 정한
279
+ 고정 문장이며 응답 본문을 옮기지 않습니다.
280
+ - **실패 시.** 시간 초과, 네트워크 오류, 200이 아닌 상태, 잘못된 응답, 호출 한도 도달 때는 힌트
281
+ 없이 평소대로 진행합니다. 세션의 첫 실패에만 짧은 안내 한 줄이 붙습니다.
282
+ - **상태.** `litopencode doctor`의 `jevSkillHint` 값이 `Jev skill hint: off`, `on`,
283
+ `flag on but TYPESAFE_API_KEY missing` 가운데 하나로 나옵니다.
284
+
285
+ | 변수 | 기본값 | 효과 |
286
+ | --- | --- | --- |
287
+ | `LITOPENCODE_JEV` | 없음 | `1`이면 키가 있을 때 힌트를 켭니다. |
288
+ | `TYPESAFE_API_KEY` | 없음 | 본인의 TypeSafe 키입니다. 요금은 입력 토큰 100만 개당 약 0.04달러입니다. |
289
+ | `LITOPENCODE_JEV_MODEL` | `jev-1.13.0` | 요청에 담는 모델 이름입니다. |
290
+ | `LITOPENCODE_JEV_TIMEOUT_MS` | `1500` | 제한 시간(ms)이며 최대 `3000`입니다. |
291
+ | `LITOPENCODE_JEV_MAX_CALLS` | `200` | 세션당 요청 수 한도입니다. |
292
+ | `LITOPENCODE_JEV_MIN_CONFIDENCE` | `0.35` | 힌트를 붙이는 최소 확신도입니다. |
293
+ | `LITOPENCODE_JEV_TRACE` | 없음 | `1`이면 요청마다 `.litopencode/logs/jev-skill-hint.jsonl`에 시각, 프롬프트 SHA-256, 고른 id, 확신도, 지연 시간, HTTP 상태, 실패 사유를 한 줄씩 남깁니다. 프롬프트 본문, 키, 응답 본문은 남기지 않습니다. |
294
+
256
295
  ## 패키지 경로
257
296
 
258
297
  - 패키지와 plugin id: `litopencode`
package/docs/reference.md CHANGED
@@ -364,6 +364,49 @@ when discarding that project's ledger and receipts is intentional.
364
364
  Metadata-only overflow emits a bounded digest/omission rule fragment.
365
365
  - session-scoped rule delivery deduplicates matching content and stays bounded after compaction.
366
366
 
367
+ ## Jev skill hint (optional)
368
+
369
+ The `chat.message` hook can ask Jev, TypeSafe's hosted typed-decision model, which runtime skill
370
+ fits a user turn. It is off unless `LITOPENCODE_JEV=1` and `TYPESAFE_API_KEY` are both set in the
371
+ process environment; otherwise it makes no network call. The key is read from the environment
372
+ only. It is sent only in the `Authorization` header and is never written, logged or shown.
373
+
374
+ - **Eligible turns.** Root sessions only. Slash commands (including every command expanded by
375
+ `command.execute.before`), turns a deterministic lit route already claimed, synthetic parts
376
+ such as attached file text, and prompts under four non-space characters are skipped. OpenCode
377
+ hands `chat.message` a new parts array rather than the one `command.execute.before` saw, so a
378
+ command marks its session instead, and that session's next `chat.message` consumes the mark.
379
+ - **Request.** One `POST https://api.typesafe.ai/v1/systemone` per eligible turn, no retry and no
380
+ redirects (`redirect: "error"`). The body holds only `model`, `state` and `questions`. `state`
381
+ is built from the first 8,000 characters of the user's prompt text: home paths become `~`,
382
+ e-mail addresses `[email]`, and token-shaped strings (provider key prefixes, JWTs, PEM blocks,
383
+ `password=`/`token=`/`secret=`-style assignments, runs of 32 or more hex or base64
384
+ characters, and the key itself) `[secret]`. Only then is it cut to 2,000 characters, and a
385
+ run of eight or more token characters left at the cut also becomes `[secret]`. Anything
386
+ without a token shape, such as a hostname or a customer name, is sent as written. The catalog
387
+ is the Runtime Skills list above, each with its summary cut to 300 characters, plus `none`.
388
+ - **Added text.** The hint or the fallback note is a `synthetic` text part: OpenCode still sends
389
+ it to the model, but does not show or copy it as the user's own words.
390
+ - **Answer.** A hint is added only for HTTP 200, parseable JSON, an `answers.which.choice` that is
391
+ exactly a catalog id, and an `answers.which.confidence` at or above the threshold. The added text is
392
+ LitOpenCode's own fixed sentence with that id; no response text is copied into the turn.
393
+ - **Fallback.** On a timeout, network error, non-200 status, invalid answer or reached cap, the
394
+ turn continues as it would without the hint. The first failure in a session adds one short
395
+ `LitOpenCode skill hint unavailable (<reason>); continuing normally.` line; later failures are
396
+ silent.
397
+ - **Status.** `litopencode doctor` reports `jevSkillHint` as `Jev skill hint: off`, `on`, or
398
+ `flag on but TYPESAFE_API_KEY missing`.
399
+
400
+ | Variable | Default | Effect |
401
+ | --- | --- | --- |
402
+ | `LITOPENCODE_JEV` | unset | `1` turns the hint on when the key is also set. |
403
+ | `TYPESAFE_API_KEY` | unset | Your own TypeSafe key. TypeSafe bills about $0.04 per million input tokens. |
404
+ | `LITOPENCODE_JEV_MODEL` | `jev-1.13.0` | Model name sent with each request. |
405
+ | `LITOPENCODE_JEV_TIMEOUT_MS` | `1500` | Hard timeout in milliseconds, capped at `3000`. |
406
+ | `LITOPENCODE_JEV_MAX_CALLS` | `200` | Requests allowed per session. |
407
+ | `LITOPENCODE_JEV_MIN_CONFIDENCE` | `0.35` | Lowest confidence that produces a hint. |
408
+ | `LITOPENCODE_JEV_TRACE` | unset | `1` appends one record per request to `.litopencode/logs/jev-skill-hint.jsonl`: time, prompt SHA-256, chosen id, confidence, latency, HTTP status and fallback reason. It holds no prompt text, key or response body. |
409
+
367
410
  ## Package surface
368
411
 
369
412
  - Package and plugin id: `litopencode`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litopencode",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
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.18.3"
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.18.3",
58
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
59
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
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"
@@ -6,6 +6,6 @@
6
6
  "dependencies": {
7
7
  "opentype.js": "1.3.4",
8
8
  "playwright-core": "1.55.1",
9
- "ws": "8.18.3"
9
+ "ws": "8.22.0"
10
10
  }
11
11
  }
@@ -3273,11 +3273,11 @@
3273
3273
  },
3274
3274
  {
3275
3275
  "path": "package-lock.json",
3276
- "sha256": "20c343e515fe08f7e4b0cf07a5ba0b91462d0f22b4ff3531448206ac24a475f1"
3276
+ "sha256": "141539ca253c3cf4f81988db6f69aaf78574d0c8ca9e9d7e45f56af8f2e96cac"
3277
3277
  },
3278
3278
  {
3279
3279
  "path": "package.json",
3280
- "sha256": "e75e13ba0060e99b21e8e8c945710ecf3a65e44f851f3ebde54ab7f562c96400"
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 bilingual READMEs load from the package CDN must ship in the tarball.
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
- for (const file of ["README.md", "README-Ko-KR.md"]) {
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) if (target.startsWith(prefix)) paths.add(target.slice(prefix.length));
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": 101,
15
- "why": "The bilingual landing pages pin artwork and package links to the current package version."
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": 100,
21
- "why": "The bilingual landing pages pin artwork and package links to the current package version."
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",
@@ -45,10 +57,10 @@
45
57
  "why": "This package manifest is the source of truth used by the lockstep guard."
46
58
  },
47
59
  {
48
- "path": "skills/readme-studio/templates/remotion/package-lock.json",
60
+ "path": "skills/lit-pptx/package-lock.json",
49
61
  "kind": "derived",
50
62
  "occurrences": 2,
51
- "why": "The Remotion template lockfile has 2 occurrence(s) of an unrelated dependency version matching this package release by coincidence; it is not release metadata, and the count follows the current package version."
63
+ "why": "The lit-pptx runtime lockfile has 2 occurrence(s) of an unrelated dependency version matching this package release by coincidence; it is not release metadata, and the count follows the current package version."
52
64
  },
53
65
  {
54
66
  "path": "test/docs.test.mjs",