luciazero 2.0.0 → 2.0.1

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
@@ -7,6 +7,27 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.0.1] - 2026-08-13
11
+
12
+ ### Added
13
+
14
+ - **Explicit, channel-aware updates.** `npx luciazero@latest check-update`
15
+ performs a read-only, five-second npm registry check only when invoked;
16
+ `npx luciazero@latest update` detects classic Claude, its hook mode, and
17
+ Codex, then refreshes every detected install through the existing audited
18
+ installers. It refuses to create a fresh install, downgrade a recognized
19
+ newer one, or trust a malformed version sidecar. Legacy installs without the
20
+ sidecar remain updatable. Classic doctrine customization now receives the
21
+ same managed-snapshot backup protection as skills and agents.
22
+ Plugin and skills-only update commands, Claude plugin auto-update, and GitHub
23
+ release notifications are documented separately in both READMEs.
24
+
25
+ ### Changed
26
+
27
+ - The release workflow uses the runner's GitHub CLI instead of a Node 20-based
28
+ release action. Re-runs replace the existing zip without recreating the
29
+ release, and GitHub Actions no longer emits the deprecated-runtime warning.
30
+
10
31
  ## [2.0.0] - 2026-08-13
11
32
 
12
33
  ### Changed
package/README.md CHANGED
@@ -106,6 +106,35 @@ Classic installs support `--status`; Codex receives the doctrine and skills but
106
106
  not Claude-only hooks/statusline. Installers back up name collisions and remove
107
107
  only exact Luciazero-managed copies on uninstall.
108
108
 
109
+ ## Update
110
+
111
+ Luciazero never changes classic or Codex files in the background.
112
+
113
+ ```bash
114
+ npx luciazero@latest check-update # read-only; contacts npm only now
115
+ npx luciazero@latest update # updates every detected classic/Codex install
116
+ ```
117
+
118
+ `update` preserves whether the Claude classic install uses hooks, repairs stale
119
+ managed files, starts no fresh install when it cannot find one, and stops on a
120
+ known newer version or malformed version metadata. Start a new agent session
121
+ afterward.
122
+
123
+ Other install channels use their own updater:
124
+
125
+ ```bash
126
+ claude plugin update luciazero@luciazero # then run /reload-plugins
127
+ npx skills update # review the scope prompt
128
+ ```
129
+
130
+ The skills command updates every installed skill in the selected scope, not
131
+ only Luciazero; review its prompt before confirming.
132
+
133
+ Claude Code can auto-update the plugin at startup: open `/plugin` →
134
+ **Marketplaces** → **luciazero** → **Enable auto-update**. Third-party
135
+ marketplaces leave this off by default. For release-only notifications, use
136
+ GitHub **Watch → Custom → Releases**.
137
+
109
138
  ## The 9 skills
110
139
 
111
140
  Run `/luciazero-bootstrap` first; the rest activate when their moment arrives.
package/README.th.md CHANGED
@@ -104,6 +104,34 @@ npx luciazero uninstall-codex
104
104
  เฉพาะ Claude Installer สำรองชื่อที่ชน และตอนถอนจะลบเฉพาะสำเนาที่ Luciazero
105
105
  ยืนยันความเป็นเจ้าของได้
106
106
 
107
+ ## อัปเดต
108
+
109
+ Luciazero จะไม่แก้ไฟล์ของ classic หรือ Codex อยู่เบื้องหลัง
110
+
111
+ ```bash
112
+ npx luciazero@latest check-update # อ่านอย่างเดียว ติดต่อ npm เฉพาะตอนนี้
113
+ npx luciazero@latest update # อัปเดต classic/Codex ทุกชุดที่ตรวจพบ
114
+ ```
115
+
116
+ `update` รักษาโหมดเดิมว่า Claude classic ใช้ hook หรือไม่ ซ่อมไฟล์ managed ที่
117
+ เก่า จะไม่เริ่มติดตั้งใหม่ถ้าหา installation เดิมไม่พบ และจะหยุดเมื่อพบเวอร์ชัน
118
+ ที่ใหม่กว่าหรือข้อมูลเวอร์ชันเสีย หลังอัปเดตให้เริ่ม agent session ใหม่
119
+
120
+ ช่องทางอื่นใช้ตัวอัปเดตของช่องทางนั้น:
121
+
122
+ ```bash
123
+ claude plugin update luciazero@luciazero # แล้วรัน /reload-plugins
124
+ npx skills update # ตรวจ scope ใน prompt ก่อนยืนยัน
125
+ ```
126
+
127
+ คำสั่ง skills จะอัปเดต skill ทุกตัวใน scope ที่เลือก ไม่ใช่เฉพาะ Luciazero
128
+ จึงควรตรวจ prompt ก่อนยืนยัน
129
+
130
+ Claude Code อัปเดต plugin ตอนเริ่มโปรแกรมอัตโนมัติได้: เปิด `/plugin` →
131
+ **Marketplaces** → **luciazero** → **Enable auto-update** โดย marketplace
132
+ ภายนอกจะปิดตัวเลือกนี้เป็นค่าเริ่มต้น ถ้าต้องการเพียงการแจ้งเตือน release ให้ใช้
133
+ GitHub **Watch → Custom → Releases**
134
+
107
135
  ## Skill ทั้ง 9 ตัว
108
136
 
109
137
  รัน `/luciazero-bootstrap` ก่อนหนึ่งครั้ง ที่เหลือใช้เมื่อถึงจังหวะของมัน
package/bin/luciazero.js CHANGED
@@ -7,6 +7,8 @@
7
7
  // npx luciazero uninstall -> uninstall.sh
8
8
  // npx luciazero uninstall-codex -> uninstall-codex.sh
9
9
  // npx luciazero discipline [options] -> local stats report
10
+ // npx luciazero check-update [--json] -> explicit npm version check
11
+ // npx luciazero update -> update detected classic installs
10
12
  const { spawnSync } = require("node:child_process");
11
13
  const path = require("node:path");
12
14
 
@@ -16,13 +18,18 @@ const ROUTES = {
16
18
  uninstall: { runtime: "bash", script: "uninstall.sh" },
17
19
  "uninstall-codex": { runtime: "bash", script: "uninstall-codex.sh" },
18
20
  discipline: { runtime: process.execPath, script: "bin/discipline-report.js" },
21
+ "check-update": { runtime: process.execPath, script: "bin/update.js", args: ["check"] },
22
+ update: { runtime: process.execPath, script: "bin/update.js", args: ["update"] },
19
23
  };
20
24
 
21
25
  const args = process.argv.slice(2);
22
26
  let route = "install";
23
27
  if (args[0] && !args[0].startsWith("-")) {
24
28
  if (!Object.prototype.hasOwnProperty.call(ROUTES, args[0])) {
25
- console.error(`luciazero: unknown command '${args[0]}' (install, codex, discipline, uninstall, uninstall-codex)`);
29
+ console.error(
30
+ `luciazero: unknown command '${args[0]}' ` +
31
+ "(install, codex, discipline, check-update, update, uninstall, uninstall-codex)"
32
+ );
26
33
  process.exit(64);
27
34
  }
28
35
  route = args.shift();
@@ -37,7 +44,7 @@ if (process.platform === "win32" && selected.runtime === "bash") {
37
44
  process.exit(1);
38
45
  }
39
46
 
40
- const result = spawnSync(selected.runtime, [script, ...args], { stdio: "inherit" });
47
+ const result = spawnSync(selected.runtime, [script, ...(selected.args || []), ...args], { stdio: "inherit" });
41
48
  if (result.error) {
42
49
  console.error("luciazero: could not run bash: " + result.error.message);
43
50
  process.exit(1);
package/bin/update.js ADDED
@@ -0,0 +1,366 @@
1
+ "use strict";
2
+
3
+ const fs = require("node:fs");
4
+ const os = require("node:os");
5
+ const path = require("node:path");
6
+ const { spawnSync } = require("node:child_process");
7
+
8
+ const ROOT = path.resolve(__dirname, "..");
9
+ const PACKAGE_NAME = "luciazero";
10
+ const PACKAGE_VERSION = require(path.join(ROOT, "package.json")).version;
11
+
12
+ function readText(file) {
13
+ try {
14
+ return fs.readFileSync(file, "utf8");
15
+ } catch (error) {
16
+ if (error && error.code === "ENOENT") return "";
17
+ throw error;
18
+ }
19
+ }
20
+
21
+ function readVersion(directory) {
22
+ const value = readText(path.join(directory, ".luciazero-version")).split(/\r?\n/, 1)[0].trim();
23
+ return value || null;
24
+ }
25
+
26
+ function versionMetadata(directory) {
27
+ const file = path.join(directory, ".luciazero-version");
28
+ return { installedVersion: readVersion(directory), versionFilePresent: fs.existsSync(file) };
29
+ }
30
+
31
+ function detectInstallations(options = {}) {
32
+ const env = options.env || process.env;
33
+ const home = options.home || os.homedir();
34
+ const claudeDir = options.claudeDir || env.CLAUDE_CONFIG_DIR || path.join(home, ".claude");
35
+ const codexDir = options.codexDir || env.CODEX_HOME || path.join(home, ".codex");
36
+ const installations = [];
37
+
38
+ const claudeSettings = readText(path.join(claudeDir, "settings.json"));
39
+ const claudeInstalled =
40
+ fs.existsSync(path.join(claudeDir, ".luciazero-version")) ||
41
+ fs.existsSync(path.join(claudeDir, "luciazero.md")) ||
42
+ fs.existsSync(path.join(claudeDir, ".luciazero-managed"));
43
+ if (claudeInstalled) {
44
+ installations.push({
45
+ channel: "claude-classic",
46
+ configDir: claudeDir,
47
+ ...versionMetadata(claudeDir),
48
+ hooks:
49
+ fs.existsSync(path.join(claudeDir, "hooks", "luciazero-verify.sh")) ||
50
+ claudeSettings.includes("hooks/luciazero-verify.sh"),
51
+ });
52
+ }
53
+
54
+ const codexAgents = readText(path.join(codexDir, "AGENTS.md"));
55
+ const codexInstalled =
56
+ fs.existsSync(path.join(codexDir, ".luciazero-version")) ||
57
+ fs.existsSync(path.join(codexDir, ".luciazero-managed")) ||
58
+ codexAgents.includes("<!-- luciazero:start -->");
59
+ if (codexInstalled) {
60
+ installations.push({
61
+ channel: "codex",
62
+ configDir: codexDir,
63
+ ...versionMetadata(codexDir),
64
+ hooks: false,
65
+ });
66
+ }
67
+
68
+ return installations;
69
+ }
70
+
71
+ function parseSemver(value) {
72
+ const match = String(value || "").match(
73
+ /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/
74
+ );
75
+ if (!match) return null;
76
+ return {
77
+ core: match.slice(1, 4).map(Number),
78
+ prerelease: match[4] ? match[4].split(".") : [],
79
+ };
80
+ }
81
+
82
+ function comparePrerelease(left, right) {
83
+ if (left.length === 0 && right.length === 0) return 0;
84
+ if (left.length === 0) return 1;
85
+ if (right.length === 0) return -1;
86
+ const length = Math.max(left.length, right.length);
87
+ for (let index = 0; index < length; index += 1) {
88
+ if (left[index] === undefined) return -1;
89
+ if (right[index] === undefined) return 1;
90
+ if (left[index] === right[index]) continue;
91
+ const leftNumber = /^\d+$/.test(left[index]);
92
+ const rightNumber = /^\d+$/.test(right[index]);
93
+ if (leftNumber && rightNumber) return Number(left[index]) < Number(right[index]) ? -1 : 1;
94
+ if (leftNumber !== rightNumber) return leftNumber ? -1 : 1;
95
+ return left[index] < right[index] ? -1 : 1;
96
+ }
97
+ return 0;
98
+ }
99
+
100
+ function compareSemver(leftValue, rightValue) {
101
+ const left = parseSemver(leftValue);
102
+ const right = parseSemver(rightValue);
103
+ if (!left || !right) return null;
104
+ for (let index = 0; index < left.core.length; index += 1) {
105
+ if (left.core[index] !== right.core[index]) {
106
+ return left.core[index] < right.core[index] ? -1 : 1;
107
+ }
108
+ }
109
+ return comparePrerelease(left.prerelease, right.prerelease);
110
+ }
111
+
112
+ function installationStatus(installation, latestVersion) {
113
+ const comparison = compareSemver(installation.installedVersion, latestVersion);
114
+ let status = "unknown";
115
+ if (comparison === 0) status = "current";
116
+ else if (comparison === -1) status = "update-available";
117
+ else if (comparison === 1) status = "ahead";
118
+ return { ...installation, status };
119
+ }
120
+
121
+ function buildCheckResult(installations, latestVersion) {
122
+ const checked = installations.map((installation) => installationStatus(installation, latestVersion));
123
+ return {
124
+ package: PACKAGE_NAME,
125
+ bundledVersion: PACKAGE_VERSION,
126
+ latestVersion,
127
+ cliUpdateAvailable: compareSemver(PACKAGE_VERSION, latestVersion) === -1,
128
+ updateAvailable:
129
+ compareSemver(PACKAGE_VERSION, latestVersion) === -1 ||
130
+ checked.some((installation) => installation.status === "update-available"),
131
+ installations: checked,
132
+ };
133
+ }
134
+
135
+ async function fetchLatestVersion(options = {}) {
136
+ const registry = options.registry || process.env.npm_config_registry || "https://registry.npmjs.org/";
137
+ const request = options.fetch || globalThis.fetch;
138
+ if (typeof request !== "function") throw new Error("this Node.js runtime has no fetch support");
139
+ const base = registry.endsWith("/") ? registry : `${registry}/`;
140
+ const endpoint = new URL(`${PACKAGE_NAME}/latest`, base);
141
+ if (!["http:", "https:"].includes(endpoint.protocol)) {
142
+ throw new Error("npm registry must use http or https");
143
+ }
144
+
145
+ const controller = new AbortController();
146
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs || 5000);
147
+ try {
148
+ const response = await request(endpoint, {
149
+ headers: { Accept: "application/json", "User-Agent": `luciazero/${PACKAGE_VERSION}` },
150
+ redirect: "follow",
151
+ signal: controller.signal,
152
+ });
153
+ if (!response.ok) throw new Error(`registry returned HTTP ${response.status}`);
154
+ const payload = await response.json();
155
+ if (!payload || !parseSemver(payload.version)) throw new Error("registry returned an invalid version");
156
+ return payload.version;
157
+ } catch (error) {
158
+ if (error && error.name === "AbortError") throw new Error("registry request timed out");
159
+ throw error;
160
+ } finally {
161
+ clearTimeout(timer);
162
+ }
163
+ }
164
+
165
+ function channelLabel(installation) {
166
+ if (installation.channel === "codex") return "Codex";
167
+ return installation.hooks ? "Claude classic + hooks" : "Claude classic";
168
+ }
169
+
170
+ function writeLine(stream, line = "") {
171
+ stream.write(`${line}\n`);
172
+ }
173
+
174
+ function printSeparateChannelHelp(stream) {
175
+ writeLine(stream, "Claude plugin: claude plugin update luciazero@luciazero, then /reload-plugins");
176
+ writeLine(stream, "Skills only: npx skills update (updates all skills in the selected scope; review the prompt)");
177
+ }
178
+
179
+ function checkHelp(stream) {
180
+ writeLine(stream, "Usage: npx luciazero@latest check-update [--json]");
181
+ writeLine(stream, "Checks the npm registry only when this command is run; it never changes files.");
182
+ }
183
+
184
+ async function runCheck(args = [], dependencies = {}) {
185
+ const stdout = dependencies.stdout || process.stdout;
186
+ const stderr = dependencies.stderr || process.stderr;
187
+ if (args.includes("--help") || args.includes("-h")) {
188
+ checkHelp(stdout);
189
+ return 0;
190
+ }
191
+ const unknown = args.filter((arg) => arg !== "--json");
192
+ if (unknown.length > 0) {
193
+ writeLine(stderr, `luciazero check-update: unknown option '${unknown[0]}'`);
194
+ checkHelp(stderr);
195
+ return 64;
196
+ }
197
+
198
+ const findInstallations = dependencies.detectInstallations || detectInstallations;
199
+ const getLatestVersion = dependencies.fetchLatestVersion || fetchLatestVersion;
200
+ let latestVersion;
201
+ try {
202
+ latestVersion = await getLatestVersion();
203
+ } catch (error) {
204
+ writeLine(stderr, `luciazero check-update: ${error.message}`);
205
+ writeLine(stderr, "No files were changed. Retry later or check https://www.npmjs.com/package/luciazero.");
206
+ return 1;
207
+ }
208
+
209
+ const result = buildCheckResult(findInstallations(), latestVersion);
210
+ if (args.includes("--json")) {
211
+ writeLine(stdout, JSON.stringify(result, null, 2));
212
+ return 0;
213
+ }
214
+
215
+ writeLine(stdout, "Luciazero update check");
216
+ writeLine(stdout, ` latest npm release ${result.latestVersion}`);
217
+ writeLine(stdout, ` this command carries ${result.bundledVersion}`);
218
+ if (result.installations.length === 0) {
219
+ writeLine(stdout, " no classic Claude/Codex installation detected");
220
+ } else {
221
+ for (const installation of result.installations) {
222
+ const installed = installation.installedVersion || "unknown version";
223
+ const suffix = {
224
+ current: "current",
225
+ "update-available": `update available -> ${result.latestVersion}`,
226
+ ahead: "ahead of the latest public release",
227
+ unknown: `cannot compare -> ${result.latestVersion}`,
228
+ }[installation.status];
229
+ writeLine(stdout, ` ${channelLabel(installation)}: ${installed} (${suffix})`);
230
+ }
231
+ }
232
+ const malformed = result.installations.filter(
233
+ (item) => item.versionFilePresent && item.status === "unknown"
234
+ );
235
+ const updatable = result.installations.filter(
236
+ (item) => item.status === "update-available" || (!item.versionFilePresent && item.status === "unknown")
237
+ );
238
+ if (malformed.length > 0) {
239
+ writeLine(stdout);
240
+ writeLine(stdout, "Cannot update installs with a malformed .luciazero-version file.");
241
+ writeLine(stdout, "Repair it, or remove it only after confirming the installed version.");
242
+ }
243
+ if (updatable.length > 0) {
244
+ writeLine(stdout);
245
+ writeLine(stdout, "Update detected classic/Codex installs with:");
246
+ writeLine(stdout, " npx luciazero@latest update");
247
+ } else if (result.cliUpdateAvailable) {
248
+ writeLine(stdout);
249
+ writeLine(stdout, "This command is older than the latest release. Re-run it with:");
250
+ writeLine(stdout, " npx luciazero@latest check-update");
251
+ }
252
+ writeLine(stdout);
253
+ printSeparateChannelHelp(stdout);
254
+ return 0;
255
+ }
256
+
257
+ function updateHelp(stream) {
258
+ writeLine(stream, "Usage: npx luciazero@latest update");
259
+ writeLine(stream, "Updates every detected classic Claude/Codex install and preserves Claude hook mode.");
260
+ }
261
+
262
+ function runInstaller(installation, dependencies = {}) {
263
+ const spawn = dependencies.spawnSync || spawnSync;
264
+ const script = installation.channel === "codex" ? "install-codex.sh" : "install.sh";
265
+ const args = [path.join(ROOT, script)];
266
+ if (installation.channel === "claude-classic" && installation.hooks) args.push("--with-hooks");
267
+ return spawn("bash", args, { env: process.env, stdio: "inherit" });
268
+ }
269
+
270
+ function runUpdate(args = [], dependencies = {}) {
271
+ const stdout = dependencies.stdout || process.stdout;
272
+ const stderr = dependencies.stderr || process.stderr;
273
+ if (args.includes("--help") || args.includes("-h")) {
274
+ updateHelp(stdout);
275
+ return 0;
276
+ }
277
+ if (args.length > 0) {
278
+ writeLine(stderr, `luciazero update: unknown option '${args[0]}'`);
279
+ updateHelp(stderr);
280
+ return 64;
281
+ }
282
+
283
+ const findInstallations = dependencies.detectInstallations || detectInstallations;
284
+ const installations = findInstallations();
285
+ if (installations.length === 0) {
286
+ writeLine(stderr, "No classic Claude/Codex installation detected; nothing changed.");
287
+ printSeparateChannelHelp(stderr);
288
+ return 1;
289
+ }
290
+ const newerInstallations = installations.filter(
291
+ (installation) => compareSemver(installation.installedVersion, PACKAGE_VERSION) === 1
292
+ );
293
+ if (newerInstallations.length > 0) {
294
+ for (const installation of newerInstallations) {
295
+ writeLine(
296
+ stderr,
297
+ `Refusing to downgrade ${channelLabel(installation)} from ` +
298
+ `${installation.installedVersion} to bundled ${PACKAGE_VERSION}.`
299
+ );
300
+ }
301
+ writeLine(stderr, "Run npx luciazero@latest update so the updater itself is current.");
302
+ return 1;
303
+ }
304
+ const invalidMetadata = installations.filter(
305
+ (installation) => installation.versionFilePresent && !parseSemver(installation.installedVersion)
306
+ );
307
+ if (invalidMetadata.length > 0) {
308
+ for (const installation of invalidMetadata) {
309
+ writeLine(
310
+ stderr,
311
+ `Refusing to update ${channelLabel(installation)}: .luciazero-version is malformed.`
312
+ );
313
+ }
314
+ writeLine(stderr, "Repair or remove the malformed sidecar after confirming the installed version, then retry.");
315
+ return 1;
316
+ }
317
+ if (process.platform === "win32") {
318
+ writeLine(stderr, "Luciazero installers need Bash. Run this command inside WSL.");
319
+ return 1;
320
+ }
321
+
322
+ writeLine(stdout, `Updating detected installations with Luciazero ${PACKAGE_VERSION}:`);
323
+ let failed = false;
324
+ for (const installation of installations) {
325
+ writeLine(stdout, `\n-- ${channelLabel(installation)} (${installation.configDir})`);
326
+ const result = runInstaller(installation, dependencies);
327
+ if (result.error || result.status !== 0) {
328
+ failed = true;
329
+ const detail = result.error ? `: ${result.error.message}` : ` (exit ${result.status})`;
330
+ writeLine(stderr, `luciazero update: ${channelLabel(installation)} failed${detail}`);
331
+ }
332
+ }
333
+ if (failed) return 1;
334
+
335
+ writeLine(stdout, "\nUpdated all detected classic/Codex installations.");
336
+ writeLine(stdout, "Start a new agent session so the refreshed doctrine and skills are loaded.");
337
+ return 0;
338
+ }
339
+
340
+ async function main() {
341
+ const [command, ...args] = process.argv.slice(2);
342
+ let status;
343
+ if (command === "check") status = await runCheck(args);
344
+ else if (command === "update") status = runUpdate(args);
345
+ else {
346
+ writeLine(process.stderr, `luciazero update helper: unknown command '${command || ""}'`);
347
+ status = 64;
348
+ }
349
+ process.exitCode = status;
350
+ }
351
+
352
+ if (require.main === module) {
353
+ main().catch((error) => {
354
+ writeLine(process.stderr, `luciazero update helper: ${error.message}`);
355
+ process.exitCode = 1;
356
+ });
357
+ }
358
+
359
+ module.exports = {
360
+ buildCheckResult,
361
+ compareSemver,
362
+ detectInstallations,
363
+ fetchLatestVersion,
364
+ runCheck,
365
+ runUpdate,
366
+ };
package/install.sh CHANGED
@@ -183,7 +183,8 @@ echo "Installing into ${CLAUDE_DIR}"
183
183
  mkdir -p "${CLAUDE_DIR}/skills"
184
184
 
185
185
  # 1. doctrine
186
- cp "${SRC}/claude/${DOCTRINE}" "${CLAUDE_DIR}/${DOCTRINE}"
186
+ install_file "${SRC}/claude/${DOCTRINE}" "${CLAUDE_DIR}/${DOCTRINE}" \
187
+ "${MANAGED_DIR}/${DOCTRINE}" "${DOCTRINE}"
187
188
  echo " ok ${DOCTRINE}"
188
189
 
189
190
  # 2. skills — catalog.txt is the single install/status/uninstall inventory
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "luciazero",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Verification-first discipline for coding agents (Claude Code + Codex CLI): 9-rule doctrine, 9 skills, risk-routed reviewer, fail-open enforcement hooks. npx luciazero installs it.",
5
5
  "repository": { "type": "git", "url": "git+https://github.com/ohm41321/luciazero.git" },
6
6
  "homepage": "https://github.com/ohm41321/luciazero#readme",
package/uninstall.sh CHANGED
@@ -59,9 +59,9 @@ remove_managed_file() {
59
59
 
60
60
  echo "Removing from ${CLAUDE_DIR}"
61
61
 
62
- rm -f "${CLAUDE_DIR}/${DOCTRINE}"
62
+ remove_managed_file "${CLAUDE_DIR}/${DOCTRINE}" \
63
+ "${MANAGED_DIR}/${DOCTRINE}" "${SRC}/claude/${DOCTRINE}" "${DOCTRINE}"
63
64
  rm -f "${CLAUDE_DIR}/.luciazero-version"
64
- echo " ok ${DOCTRINE}"
65
65
 
66
66
  while IFS= read -r SKILL; do
67
67
  remove_managed_tree "${CLAUDE_DIR}/skills/${SKILL}" \