context-carwash 0.1.6 → 0.1.7
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 +5 -0
- package/README.md +4 -2
- package/package.json +1 -1
- package/src/install/doctor.mjs +17 -2
- package/src/install/install.mjs +17 -1
- package/src/install/update.mjs +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.7
|
|
4
|
+
|
|
5
|
+
- `carwash doctor` checks that the Node Carwash runs with is still there, and says how to fix it if not. Removing that Node, for example an old version in nvm, stops the meter and `/wash` until install is run again. The installer now says which Node that is.
|
|
6
|
+
- Clearer wording on when Carwash goes online, and a reminder to look over `doctor` output before posting it in a public issue.
|
|
7
|
+
|
|
3
8
|
## 0.1.6
|
|
4
9
|
|
|
5
10
|
- Update notices: once a day, a new session checks npm in the background. A line under the meter says when a newer version is out, or when the version you have carries a warning. `CARWASH_NO_UPDATE_CHECK=1` turns it off.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ A small, local-first utility for Claude Code.
|
|
|
5
5
|
- **Meter:** a quiet estimate of the active context window in the status line.
|
|
6
6
|
- **Wash:** when you ask, Claude writes a short handoff brief, and your next `/clear` starts a fresh session that begins with it.
|
|
7
7
|
|
|
8
|
-
Nothing happens automatically, and nothing about you is ever sent. Carwash goes online
|
|
8
|
+
Nothing happens automatically, and nothing about you is ever sent. Carwash goes online only for these: once a day it reads its own public page on npm to see if a newer version is out (you can turn that off); `install` and `doctor` read the same page when you run them, and `install` downloads the newer version if there is one; and `carwash stats --share` sends anonymous totals if you run it and say yes.
|
|
9
9
|
|
|
10
10
|
**Status:** pre-release. The meter and the typed `/wash` command work today. Clickable links in the status line and the one-step restart are not built yet. See [plan-3.md](plan-3.md) for the design.
|
|
11
11
|
|
|
@@ -30,6 +30,8 @@ The installer copies itself to `~/.carwash/app` and makes three changes to Claud
|
|
|
30
30
|
|
|
31
31
|
Start a new Claude Code session afterwards. The first one explains the meter and `/wash` in a short note, shown once.
|
|
32
32
|
|
|
33
|
+
The status line and hooks run with the Node you installed with. If you later remove that Node, for example an old version in nvm, they stop working until you run install again. `carwash doctor` points this out.
|
|
34
|
+
|
|
33
35
|
## The meter
|
|
34
36
|
|
|
35
37
|
```text
|
|
@@ -114,7 +116,7 @@ This shows every field it would send, and sends only if you say yes. It sends to
|
|
|
114
116
|
npx context-carwash doctor
|
|
115
117
|
```
|
|
116
118
|
|
|
117
|
-
This checks that the status line, hooks and `/wash` command are still in place, and shows your Carwash, Node, Claude Code and operating system versions, the newest version on npm, and the last few errors. Paste it into a [new issue](https://github.com/Spobi/context-carwash/issues/new).
|
|
119
|
+
This checks that the status line, hooks and `/wash` command are still in place and that the Node they run with is still there, and shows your Carwash, Node, Claude Code and operating system versions, the newest version on npm, and the last few errors. Paste it into a [new issue](https://github.com/Spobi/context-carwash/issues/new). Issues are public, so look it over first: error details can include folder names.
|
|
118
120
|
|
|
119
121
|
If a hook fails, the status line shows `Carwash hit an error` until you run `doctor`.
|
|
120
122
|
|
package/package.json
CHANGED
package/src/install/doctor.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { readJson } from '../files.mjs';
|
|
|
6
6
|
import { errorLogPath, errorsSeenPath, installRecordPath, settingsPath, skillDir } from '../paths.mjs';
|
|
7
7
|
import { refreshLedger, totals } from '../stats/ledger.mjs';
|
|
8
8
|
import { readyRecords } from '../wash/store.mjs';
|
|
9
|
-
import { isCarwashCommand, isCarwashHook } from './install.mjs';
|
|
9
|
+
import { commandProgram, isCarwashCommand, isCarwashHook } from './install.mjs';
|
|
10
10
|
import { UPDATE_COMMAND } from './notice.mjs';
|
|
11
11
|
import { readSettings, resolveSettingsFile } from './settings.mjs';
|
|
12
12
|
import { isNewer } from './update.mjs';
|
|
@@ -59,7 +59,19 @@ function settingsCheck(env) {
|
|
|
59
59
|
}
|
|
60
60
|
const groups = (event) => (Array.isArray(data?.hooks?.[event]) ? data.hooks[event] : []);
|
|
61
61
|
const hooks = Object.fromEntries(HOOK_EVENTS.map((event) => [event, groups(event).some((group) => Array.isArray(group?.hooks) && group.hooks.some((hook) => isCarwashHook(hook?.command)))]));
|
|
62
|
-
|
|
62
|
+
const hookCommands = HOOK_EVENTS.flatMap((event) => groups(event).flatMap((group) => (Array.isArray(group?.hooks) ? group.hooks.map((hook) => hook?.command) : [])));
|
|
63
|
+
const ours = [data?.statusLine?.command, ...hookCommands].filter((command) => isCarwashCommand(command) || isCarwashHook(command));
|
|
64
|
+
return { readable: true, statusLine: isCarwashCommand(data?.statusLine?.command), hooks, nodes: [...new Set(ours.map(commandProgram).filter(Boolean))] };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The Node that Carwash's commands start. Install writes its path into the
|
|
68
|
+
// settings, so if that Node is removed later (version managers such as nvm
|
|
69
|
+
// keep one folder per version), nothing of Carwash runs, not even the part
|
|
70
|
+
// that would log an error. Plain `node` (Windows) is looked up on PATH.
|
|
71
|
+
function nodeRow(nodes) {
|
|
72
|
+
const missing = nodes.find((node) => /[\\/]/.test(node) && !existsSync(node));
|
|
73
|
+
if (missing) return `MISSING: ${tilde(missing)} · to fix: ${UPDATE_COMMAND}`;
|
|
74
|
+
return nodes.length ? `ok (${nodes.map(tilde).join(', ')})` : null;
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
const date = (iso) => {
|
|
@@ -100,6 +112,8 @@ export function doctorReport({ env = process.env, now = Date.now(), running, new
|
|
|
100
112
|
} else {
|
|
101
113
|
out.push(row('Status line', yes(check.statusLine)));
|
|
102
114
|
out.push(row('Hooks', HOOK_EVENTS.map((event) => `${event} ${yes(check.hooks[event])}`).join(' · ')));
|
|
115
|
+
const node = nodeRow(check.nodes);
|
|
116
|
+
if (node) out.push(row('Node for Carwash', node));
|
|
103
117
|
}
|
|
104
118
|
out.push(row('/wash command', yes(existsSync(join(skillDir(env), 'SKILL.md')))));
|
|
105
119
|
let waiting = '?';
|
|
@@ -123,6 +137,7 @@ export function doctorReport({ env = process.env, now = Date.now(), running, new
|
|
|
123
137
|
if (!errors.length) out.push(`Errors: none logged.`);
|
|
124
138
|
else out.push(`Last ${errors.length === 1 ? 'error' : `${errors.length} errors`} (from ${tilde(errorLogPath(env))}):`, '', ...errors.map((entry) => entry.replace(/^/gm, ' ')));
|
|
125
139
|
out.push('', `Something wrong? Open an issue and paste everything above: ${ISSUES_URL}`);
|
|
140
|
+
out.push('Issues are public, so look it over first: error details can include folder names.');
|
|
126
141
|
return out.join('\n');
|
|
127
142
|
}
|
|
128
143
|
|
package/src/install/install.mjs
CHANGED
|
@@ -69,6 +69,16 @@ export function commandFor(verb, { nodePath, scriptPath, platform = process.plat
|
|
|
69
69
|
|
|
70
70
|
export const statusLineCommand = (options) => commandFor('statusline', options);
|
|
71
71
|
|
|
72
|
+
// The program a command from commandFor starts, with its quoting undone: the
|
|
73
|
+
// Node it was installed with, or plain `node` on Windows.
|
|
74
|
+
export function commandProgram(command) {
|
|
75
|
+
const word = /^\s*('(?:[^']|'\\'')*'|"[^"]*"|[^\s'"]+)/.exec(String(command ?? ''))?.[1];
|
|
76
|
+
if (!word) return null;
|
|
77
|
+
if (word.startsWith("'")) return word.slice(1, -1).replace(/'\\''/g, "'");
|
|
78
|
+
if (word.startsWith('"')) return word.slice(1, -1);
|
|
79
|
+
return word;
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
const runsCarwash = (verb) => (command) => typeof command === 'string' && new RegExp(`carwash\\.mjs['"]?\\s+${verb}\\s*$`).test(command);
|
|
73
83
|
export const isCarwashCommand = runsCarwash('statusline');
|
|
74
84
|
export const isCarwashHook = runsCarwash('hook');
|
|
@@ -178,7 +188,10 @@ export function planInstall({ env = process.env, platform = process.platform } =
|
|
|
178
188
|
};
|
|
179
189
|
const skill = planSkill(env);
|
|
180
190
|
|
|
181
|
-
|
|
191
|
+
// The Node the commands will run with. Windows finds `node` on PATH instead.
|
|
192
|
+
const node = platform === 'win32' ? null : script.nodePath;
|
|
193
|
+
|
|
194
|
+
return { home, appDir, file, settings, record, original, alreadyInstalled, next, skill, node, keepsHooks: isPlainObject(theirHooks) && Object.keys(theirHooks).length > 0 };
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
export function describeInstall(plan) {
|
|
@@ -202,6 +215,9 @@ export function describeInstall(plan) {
|
|
|
202
215
|
}
|
|
203
216
|
lines.push(` 3. Add the /wash command at ${tilde(plan.skill.dir)}`);
|
|
204
217
|
lines.push('', 'Writing a brief is an ordinary Claude reply, so it uses tokens like any other.');
|
|
218
|
+
// Version managers such as nvm keep a folder per Node version, so removing
|
|
219
|
+
// an old version takes this path with it, and nothing of Carwash can start.
|
|
220
|
+
if (plan.node) lines.push(`Carwash runs with the Node at ${tilde(plan.node)}. If you remove that Node later, for example an old version in nvm, run install again.`);
|
|
205
221
|
lines.push('Nothing else is touched. To undo everything: carwash uninstall');
|
|
206
222
|
return lines.join('\n');
|
|
207
223
|
}
|
package/src/install/update.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
|
|
3
|
-
// Whether a newer Context Carwash is on npm, and handing over to it
|
|
4
|
-
// installer
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// Whether a newer Context Carwash is on npm, and handing over to it, for the
|
|
4
|
+
// installer: npx can quietly rerun a copy it downloaded earlier, and people
|
|
5
|
+
// should not have to know to type @latest. The daily check behind the update
|
|
6
|
+
// notice is in notice.mjs. The meter and /wash never go online themselves.
|
|
7
7
|
|
|
8
8
|
const LATEST_URL = 'https://registry.npmjs.org/context-carwash/latest';
|
|
9
9
|
const TIMEOUT_MS = 3000;
|