context-carwash 0.1.1 → 0.1.3
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/README.md +3 -3
- package/package.json +1 -1
- package/src/cli.mjs +16 -2
- package/src/install/update.mjs +53 -0
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 leaves your machine.
|
|
8
|
+
Nothing happens automatically, and nothing leaves your machine. The one exception: the installer asks npm whether a newer version is out.
|
|
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
|
|
|
@@ -18,7 +18,7 @@ npx context-carwash preview # sample meters in your terminal, nothing instal
|
|
|
18
18
|
npx context-carwash install # lists what it will change, then asks
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
To update later, run `npx context-carwash
|
|
21
|
+
To update later, run `npx context-carwash install` again. Waiting briefs are kept. If npx reruns an older copy it downloaded before, the installer notices that npm has a newer version and installs that one instead.
|
|
22
22
|
|
|
23
23
|
The installer copies itself to `~/.carwash/app` and makes three changes to Claude Code, all listed before you confirm:
|
|
24
24
|
|
|
@@ -76,7 +76,7 @@ Things worth knowing:
|
|
|
76
76
|
|
|
77
77
|
## Where things are kept
|
|
78
78
|
|
|
79
|
-
Briefs are stored in `~/.carwash`, readable only by you, and never inside your project. Nothing is uploaded, and there is no telemetry.
|
|
79
|
+
Briefs are stored in `~/.carwash`, readable only by you, and never inside your project. Nothing is uploaded, and there is no telemetry. The installer reads the newest version number from npm, and nothing else goes online; set `CARWASH_NO_UPDATE_CHECK=1` to skip even that.
|
|
80
80
|
|
|
81
81
|
If a hook fails it stays silent and writes the error to `~/.carwash/errors.log`.
|
|
82
82
|
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { appendFileSync, existsSync, statSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { createInterface } from 'node:readline';
|
|
3
3
|
import { applyInstall, applyUninstall, describeInstall, describeUninstall, packageVersion, planInstall, planUninstall } from './install/install.mjs';
|
|
4
4
|
import { UserError } from './install/settings.mjs';
|
|
5
|
+
import { isNewer, latestVersion, runRelease } from './install/update.mjs';
|
|
5
6
|
import { errorLogPath, welcomedPath } from './paths.mjs';
|
|
6
7
|
import { render } from './statusline/render.mjs';
|
|
7
8
|
import { statusline } from './statusline/run.mjs';
|
|
@@ -24,7 +25,7 @@ Inside Claude Code, once installed:
|
|
|
24
25
|
/wash load [id] Make a waiting brief the one /clear will use
|
|
25
26
|
/wash help Explain the meter and the options
|
|
26
27
|
|
|
27
|
-
Nothing happens automatically,
|
|
28
|
+
Nothing happens automatically. Only install goes online, to ask npm for the newest version.`;
|
|
28
29
|
|
|
29
30
|
async function readAll(stream) {
|
|
30
31
|
if (stream.isTTY) return '';
|
|
@@ -87,7 +88,7 @@ function logError(env, error) {
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
export async function main(argv, { env = process.env, stdin = process.stdin, stdout = process.stdout, stderr = process.stderr } = {}) {
|
|
91
|
+
export async function main(argv, { env = process.env, stdin = process.stdin, stdout = process.stdout, stderr = process.stderr, latest = latestVersion, handOff = runRelease } = {}) {
|
|
91
92
|
const [command, ...rest] = argv;
|
|
92
93
|
const yes = rest.includes('--yes') || rest.includes('-y');
|
|
93
94
|
try {
|
|
@@ -106,6 +107,19 @@ export async function main(argv, { env = process.env, stdin = process.stdin, std
|
|
|
106
107
|
return 0;
|
|
107
108
|
}
|
|
108
109
|
case 'install': {
|
|
110
|
+
// npx may be rerunning an old download. If npm has something newer,
|
|
111
|
+
// that release does the install. Once only: the newer copy is not
|
|
112
|
+
// asked to check again.
|
|
113
|
+
if (!env.CARWASH_HANDED_OFF) {
|
|
114
|
+
const current = packageVersion();
|
|
115
|
+
const newest = await latest({ env });
|
|
116
|
+
if (isNewer(newest, current)) {
|
|
117
|
+
stdout.write(`This is Context Carwash ${current}, and ${newest} is out. Installing ${newest} instead.\n\n`);
|
|
118
|
+
const status = handOff(newest, argv, { env });
|
|
119
|
+
if (status !== null) return status;
|
|
120
|
+
stdout.write(`Could not start ${newest}, so continuing with ${current}. For the newer one: npx context-carwash@latest install\n\n`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
109
123
|
const plan = planInstall({ env });
|
|
110
124
|
stdout.write(describeInstall(plan) + '\n\n');
|
|
111
125
|
if (!(await confirm('Proceed?', { yes, stdin, stdout }))) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
|
|
3
|
+
// Whether a newer Context Carwash is on npm, and handing over to it. Only the
|
|
4
|
+
// installer asks, since npx can quietly rerun a copy it downloaded earlier, and
|
|
5
|
+
// people should not have to know to type @latest. The meter and /wash never
|
|
6
|
+
// use the network.
|
|
7
|
+
|
|
8
|
+
const LATEST_URL = 'https://registry.npmjs.org/context-carwash/latest';
|
|
9
|
+
const TIMEOUT_MS = 3000;
|
|
10
|
+
|
|
11
|
+
const parse = (version) => {
|
|
12
|
+
const found = /^(\d+)\.(\d+)\.(\d+)$/.exec(String(version ?? '').trim());
|
|
13
|
+
return found ? found.slice(1).map(Number) : null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// True when `latest` is a later release than `current`. Anything unreadable is
|
|
17
|
+
// not newer.
|
|
18
|
+
export function isNewer(latest, current) {
|
|
19
|
+
const a = parse(latest);
|
|
20
|
+
const b = parse(current);
|
|
21
|
+
if (!a || !b) return false;
|
|
22
|
+
for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] > b[i];
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// The version npm calls latest, or null when it cannot say in time. Offline,
|
|
27
|
+
// slow or opted out (CARWASH_NO_UPDATE_CHECK) all mean no answer, never an error.
|
|
28
|
+
export async function latestVersion({ env = process.env, timeoutMs = TIMEOUT_MS } = {}) {
|
|
29
|
+
if (env.CARWASH_NO_UPDATE_CHECK) return null;
|
|
30
|
+
try {
|
|
31
|
+
const response = await fetch(LATEST_URL, { signal: AbortSignal.timeout(timeoutMs), headers: { accept: 'application/json' } });
|
|
32
|
+
if (!response.ok) return null;
|
|
33
|
+
const { version } = await response.json();
|
|
34
|
+
return parse(version) ? version : null;
|
|
35
|
+
} catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Run `carwash <args>` from the given release instead of this one, in this
|
|
41
|
+
// terminal, so its questions reach the person. Pinned to the exact version, so
|
|
42
|
+
// the newer copy finds itself up to date and does not hand off again. Resolves
|
|
43
|
+
// to its exit code, or null if npx could not be started.
|
|
44
|
+
export function runRelease(version, args, { env = process.env, platform = process.platform } = {}) {
|
|
45
|
+
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
46
|
+
// npx.cmd is a batch file, which Windows only runs through a shell.
|
|
47
|
+
const result = spawnSync(npx, ['--yes', `context-carwash@${version}`, ...args], {
|
|
48
|
+
stdio: 'inherit',
|
|
49
|
+
env: { ...env, CARWASH_HANDED_OFF: version },
|
|
50
|
+
shell: platform === 'win32',
|
|
51
|
+
});
|
|
52
|
+
return result.error || result.status === null ? null : result.status;
|
|
53
|
+
}
|