@litfamily/litclaude 1.0.18 → 1.0.19

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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.19 - 2026-10-01
4
+
5
+ - Automatic handoff now finds the handoff it asked for after compaction even when the model formats the marker line, for example as a bullet, in backticks or in bold. A handoff from another session is still ignored.
6
+ - An upgrade no longer stops with an ownership conflict when a bundled Python script left a `__pycache__` folder in the installed plugin.
7
+
3
8
  ## 1.0.18 - 2026-09-30
4
9
 
5
10
  - `gpt-6.1-sol` is now the recommended coding-lead alternative, since OpenAI lists `gpt-6-sol` as the previous generation. `gpt-6-sol` still works for anyone who already chose it. Claude Code picks the model and LitClaude sets no OpenAI route of its own, so this shows only in the model notes in the README (English and Korean) and in `docs/agents.md`.
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitClaude robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/cover-motion.webp" width="100%" alt="LitFamily motion cover: five armored robots power on one by one, the LitClaude robot wakes with glowing eyes and a lit frame, then LITFAMILY and KEEP THE WORK LIT. light up." /></picture></p>
2
2
 
3
3
  <h1 align="center">LitClaude</h1>
4
4
  <p align="center"><strong>Keep the work lit.</strong></p>
5
5
  <p align="center">Plan, build, and check your work in Claude Code. Leave the next session a place to begin.</p>
6
6
  <p align="center">
7
- <img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/readme/badge-version.svg" alt="1.0.18" />
8
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
7
+ <img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/readme/badge-version.svg" alt="1.0.19" />
8
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
9
9
  </p>
10
10
  <p align="center">
11
- <a href="https://github.com/wjgoarxiv/litclaude#readme">Full guide and skills gallery on GitHub</a> · <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/README_ko-KR.md">한국어</a>
11
+ <a href="https://github.com/wjgoarxiv/litclaude#readme">Full guide and skills gallery on GitHub</a> · <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/README_ko-KR.md">한국어</a>
12
12
  </p>
13
13
 
14
14
  LitClaude is a Claude Code plugin. Add `lit` to a request and Claude pins the goal, writes a
@@ -125,7 +125,7 @@ changed by hand or doesn't look like one LitClaude made, it refuses to touch it.
125
125
  ## More
126
126
 
127
127
  - [Full guide and skills gallery on GitHub](https://github.com/wjgoarxiv/litclaude#readme)
128
- - [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/privacy.md)
129
- - [Migration and ownership conflicts](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/migration.md)
130
- - [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/CHANGELOG.md)
131
- - [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/LICENSE)
128
+ - [Privacy and network behavior](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/privacy.md)
129
+ - [Migration and ownership conflicts](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/migration.md)
130
+ - [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/CHANGELOG.md)
131
+ - [MIT license](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/LICENSE)
package/README_ko-KR.md CHANGED
@@ -1,14 +1,14 @@
1
- <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitClaude 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
1
+ <p align="center"><picture><source media="(prefers-reduced-motion: reduce)" srcset="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/cover-motion-still.webp" /><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/cover-motion.webp" width="100%" alt="LitFamily 모션 커버: 다섯 로봇 패널이 차례로 켜지고, LitClaude 로봇의 눈과 테두리가 빛난 뒤 LITFAMILY와 KEEP THE WORK LIT. 문구가 밝아지는 영상" /></picture></p>
2
2
 
3
3
  <h1 align="center">LitClaude</h1>
4
4
  <p align="center"><strong>Keep the work lit.</strong></p>
5
5
  <p align="center">Claude Code에서 계획하고, 만들고, 확인한 일을 다음 세션으로 이어가세요.</p>
6
6
  <p align="center">
7
- <img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/readme/badge-version.svg" alt="1.0.18" />
8
- <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
7
+ <img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/readme/badge-version.svg" alt="1.0.19" />
8
+ <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/LICENSE"><img src="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/assets/readme/badge-license.svg" alt="MIT license" /></a>
9
9
  </p>
10
10
  <p align="center">
11
- <a href="https://github.com/wjgoarxiv/litclaude/blob/main/README_ko-KR.md">전체 안내와 스킬 갤러리는 GitHub에서</a> · <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/README.md">English</a>
11
+ <a href="https://github.com/wjgoarxiv/litclaude/blob/main/README_ko-KR.md">전체 안내와 스킬 갤러리는 GitHub에서</a> · <a href="https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/README.md">English</a>
12
12
  </p>
13
13
 
14
14
  LitClaude는 Claude Code 플러그인입니다. 요청 끝에 `lit`을 붙이면 Claude가 목표를 먼저
@@ -119,7 +119,7 @@ npm exec --yes --package @litfamily/litclaude@latest -- litclaude uninstall
119
119
  ## 더 보기
120
120
 
121
121
  - [전체 안내와 스킬 갤러리 (GitHub)](https://github.com/wjgoarxiv/litclaude/blob/main/README_ko-KR.md)
122
- - [개인정보와 네트워크](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/privacy.md)
123
- - [이전 안내와 소유권 충돌](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/docs/migration.md)
124
- - [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/CHANGELOG.md)
125
- - [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.18/LICENSE)
122
+ - [개인정보와 네트워크](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/privacy.md)
123
+ - [이전 안내와 소유권 충돌](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/docs/migration.md)
124
+ - [Release history](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/CHANGELOG.md)
125
+ - [MIT 라이선스](https://cdn.jsdelivr.net/npm/@litfamily/litclaude@1.0.19/LICENSE)
@@ -1,6 +1,6 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, rmdirSync, writeFileSync } from "node:fs";
3
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
4
  import { compareStableSemver } from "./update-notifier.mjs";
5
5
 
6
6
  const stableVersion = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
@@ -20,6 +20,9 @@ const legacyVendorPathMap = [
20
20
  ["060_autoresearch-skill", "autoresearch", "25ce45fe792de02e04019e6d0c871738e6a1b8b6cb9a91cd2486b3d4cbb715b9"],
21
21
  ["064_autoconference-skill", "autoconference", "d305d2d41721c0196cc5aaa09610f1eb4499f7ba53504bd72bdbefdd21ca7da1"],
22
22
  ];
23
+ // Python writes bytecode next to the scripts it runs. That cache is generated, never user data, so ownership
24
+ // checks skip it. A symlink or a plain file named __pycache__ is not a cache and is still reported.
25
+ const isBytecodeCache = (name, value) => (value.isDirectory() && name === "__pycache__") || (value.isFile() && name.endsWith(".pyc"));
23
26
  const conflict = (path, detail = "") => { throw new Error(`INSTALL_OWNERSHIP_CONFLICT: ${path}; use a compatible CLI or preserve this modified, foreign, or unsupported state before retrying${detail ? `. ${detail}` : ""}`); };
24
27
  const stat = (path) => {
25
28
  try { return lstatSync(path); } catch (error) { if (error.code === "ENOENT") return null; throw error; }
@@ -45,6 +48,7 @@ function treeHash(root) {
45
48
  for (const name of readdirSync(path).sort()) {
46
49
  if (!prefix && name === receiptName) continue;
47
50
  const next = join(path, name), rel = prefix ? `${prefix}/${name}` : name, value = lstatSync(next);
51
+ if (isBytecodeCache(name, value)) continue;
48
52
  if (!prefix && name === hostUseMarker && value.isDirectory() && readdirSync(next).length === 0) continue;
49
53
  if (value.isDirectory()) { entries.push([rel, "directory"]); walk(next, rel); }
50
54
  else if (value.isFile() && value.nlink === 1) entries.push([rel, "file", createHash("sha256").update(readFileSync(next)).digest("hex")]);
@@ -59,6 +63,7 @@ function detailedTree(root, { receipt = false } = {}) {
59
63
  const rows = [];
60
64
  function walk(path, prefix = "") {
61
65
  const value = lstatSync(path);
66
+ if (prefix && isBytecodeCache(prefix.split("/").at(-1), value)) return;
62
67
  if (value.isSymbolicLink()) conflict(path, "A preserved skill tree cannot contain symlinks");
63
68
  if (value.isDirectory()) {
64
69
  rows.push([prefix, "directory", value.mode & 0o777]);
@@ -169,7 +174,7 @@ export function preserveModifiedLegacySkill(plan, litHome) {
169
174
  try {
170
175
  const currentRows = detailedTree(plan.skillPath, { receipt: true });
171
176
  if (!sameRowList(currentRows, plan.skillRows)) conflict(plan.skillPath, "The legacy skill changed after the ownership preflight");
172
- cpSync(plan.skillPath, join(staging, "skill"), { recursive: true, errorOnExist: true, force: false });
177
+ cpSync(plan.skillPath, join(staging, "skill"), { recursive: true, errorOnExist: true, force: false, filter: (source) => !isBytecodeCache(basename(source), lstatSync(source)) });
173
178
  if (!sameRowList(detailedTree(join(staging, "skill"), { receipt: true }), plan.skillRows)) {
174
179
  conflict(plan.skillPath, "The staged preservation copy does not match the source");
175
180
  }
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.18"><title>release: 1.0.18</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.18</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="146" height="20" role="img" aria-label="release: 1.0.19"><title>release: 1.0.19</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.19</text></g></svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litfamily/litclaude",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "Claude Code-native workflow distribution.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "litclaude",
3
3
  "description": "Claude Code-native workflow plugin with a bounded-authority start-work lifecycle.",
4
- "version": "1.0.18",
4
+ "version": "1.0.19",
5
5
  "author": {
6
6
  "name": "LitClaude contributors"
7
7
  },
@@ -9,7 +9,7 @@ import {
9
9
  } from "../lib/wikify-knowledge.mjs";
10
10
 
11
11
  const protocolVersion = "2024-11-05";
12
- const serverVersion = "1.0.18";
12
+ const serverVersion = "1.0.19";
13
13
 
14
14
  const publicSourceReadTool = {
15
15
  name: "public_source_read",
@@ -243,6 +243,20 @@ const candidateFiles = (cwd, stateRoot) => {
243
243
  return files;
244
244
  };
245
245
 
246
+ // A model often dresses the marker up (bullet, bold label, backticks, an HTML comment). Each line is read
247
+ // with that Markdown decoration removed, then must hold the label and this exact id with a boundary after it.
248
+ const markerOnLine = (line, nonce) => {
249
+ const plain = line.replace(/<!--|-->|[*_`]/gu, "").replace(/\s+/gu, " ");
250
+ const label = "Auto-handoff id: ";
251
+ for (let at = plain.indexOf(label); at !== -1; at = plain.indexOf(label, at + 1)) {
252
+ const start = at + label.length;
253
+ if (plain.startsWith(nonce, start) && !/^[0-9a-f]/iu.test(plain.slice(start + nonce.length, start + nonce.length + 1))) return true;
254
+ }
255
+ return false;
256
+ };
257
+
258
+ const hasMarker = (text, nonce) => text.split(/\r?\n/u).some((line) => markerOnLine(line, nonce));
259
+
246
260
  const readHandoff = (path, { nonce, firedAt }) => {
247
261
  let fd;
248
262
  try {
@@ -251,7 +265,7 @@ const readHandoff = (path, { nonce, firedAt }) => {
251
265
  fd = openSync(path, constants.O_RDONLY | NO_FOLLOW);
252
266
  if (!fstatSync(fd).isFile()) return null;
253
267
  const text = readFileSync(fd, "utf8");
254
- return text.includes(`Auto-handoff id: ${nonce}`) ? text : null;
268
+ return hasMarker(text, nonce) ? text : null;
255
269
  } catch {
256
270
  return null;
257
271
  } finally {