wize-dev-kit 0.26.1 → 0.27.2
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/ARCH.md +2 -1
- package/CHANGELOG.md +64 -0
- package/README.es.md +28 -6
- package/README.md +28 -6
- package/README.pt-BR.md +28 -6
- package/adapters/continue/render.js +2 -3
- package/adapters/copilot/render.js +3 -3
- package/adapters/cursor/render.js +2 -3
- package/adapters/generic/render.js +2 -3
- package/adapters/opencode/render.js +3 -4
- package/adapters/windsurf/render.js +2 -3
- package/docs/WORKTREES.md +150 -0
- package/docs/WORKTREES.pt-BR.md +150 -0
- package/docs/harnesses/antigravity.md +2 -0
- package/docs/harnesses/antigravity.pt-BR.md +2 -0
- package/docs/harnesses/claude-code.md +2 -0
- package/docs/harnesses/claude-code.pt-BR.md +2 -0
- package/docs/harnesses/codex.md +3 -1
- package/docs/harnesses/codex.pt-BR.md +3 -1
- package/docs/harnesses/continue.md +2 -0
- package/docs/harnesses/continue.pt-BR.md +2 -0
- package/docs/harnesses/copilot.md +2 -0
- package/docs/harnesses/copilot.pt-BR.md +2 -0
- package/docs/harnesses/cursor.md +2 -0
- package/docs/harnesses/cursor.pt-BR.md +2 -0
- package/docs/harnesses/generic.md +2 -0
- package/docs/harnesses/generic.pt-BR.md +2 -0
- package/docs/harnesses/hermes.md +2 -0
- package/docs/harnesses/hermes.pt-BR.md +2 -0
- package/docs/harnesses/kimi-code.md +2 -0
- package/docs/harnesses/kimi-code.pt-BR.md +2 -0
- package/docs/harnesses/kiro.md +2 -0
- package/docs/harnesses/kiro.pt-BR.md +2 -0
- package/docs/harnesses/opencode.md +2 -0
- package/docs/harnesses/opencode.pt-BR.md +2 -0
- package/docs/harnesses/windsurf.md +2 -0
- package/docs/harnesses/windsurf.pt-BR.md +2 -0
- package/package.json +1 -1
- package/src/orchestrator-skills/module.yaml +1 -1
- package/src/orchestrator-skills/wize/skill.md +17 -7
- package/src/orchestrator-skills/wize-help/skill.md +20 -7
- package/src/orchestrator-skills/wize-update/skill.md +33 -27
- package/tools/installer/commands/doctor.js +186 -40
- package/tools/installer/commands/document-project.js +13 -3
- package/tools/installer/commands/hooks.js +215 -0
- package/tools/installer/commands/hydrate.js +298 -0
- package/tools/installer/commands/sync.js +50 -23
- package/tools/installer/commands/update.js +596 -120
- package/tools/installer/commands/version-check.js +69 -10
- package/tools/installer/document-project/modes/full-rescan.js +1 -1
- package/tools/installer/document-project/modes/initial-scan.js +5 -4
- package/tools/installer/document-project/modes/quick.js +11 -4
- package/tools/installer/git-context.js +219 -0
- package/tools/installer/git-ops.js +211 -0
- package/tools/installer/install-plan.js +136 -0
- package/tools/installer/marker.js +67 -0
- package/tools/installer/project-root.js +68 -0
- package/tools/installer/project-status.js +171 -0
- package/tools/installer/render-shared.js +170 -4
- package/tools/installer/render-targets.js +112 -0
- package/tools/installer/semver.js +59 -0
- package/tools/installer/setup-helpers.js +89 -31
- package/tools/installer/templates/hydrate.sh +163 -0
- package/tools/installer/templates/wize-kit-update.yml +38 -0
- package/tools/installer/toml.js +133 -0
- package/tools/installer/util.js +16 -0
- package/tools/installer/version-check.js +167 -37
- package/tools/installer/wize-cli.js +216 -78
- package/tools/installer/sync.js +0 -29
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Small helpers shared by the installer modules.
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
// realpath with a lexical fallback for paths that do not exist.
|
|
9
|
+
function real(p) {
|
|
10
|
+
try { return fs.realpathSync(p); } catch { return path.resolve(p); }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Same members, ignoring order and duplicates.
|
|
14
|
+
const sameSet = (a, b) => JSON.stringify([...new Set(a)].sort()) === JSON.stringify([...new Set(b)].sort());
|
|
15
|
+
|
|
16
|
+
module.exports = { real, sameSet };
|
|
@@ -5,18 +5,24 @@
|
|
|
5
5
|
// the registry is misbehaving.
|
|
6
6
|
//
|
|
7
7
|
// Strategy:
|
|
8
|
-
// - Cache the registry answer in $XDG_CACHE_HOME (or ~/.cache)
|
|
9
|
-
//
|
|
8
|
+
// - Cache the registry answer in $XDG_CACHE_HOME (or ~/.cache) as
|
|
9
|
+
// { version, fetched_at, attempted_at }. The 1h TTL runs from fetched_at.
|
|
10
|
+
// - Hints never wait on the network: getLatestNoWait() returns whatever is
|
|
11
|
+
// cached (any age) and, when it is stale, starts a detached refresher.
|
|
12
|
+
// - The explicit `version-check` command resolves via fetch with a 1.5s
|
|
13
|
+
// timeout. Failure is silent and falls back to the cache.
|
|
10
14
|
// - Compare with the kit version baked into the running CLI.
|
|
11
|
-
// - Expose a `printUpdateHintIfAny(currentVersion)` helper for the CLI.
|
|
12
15
|
|
|
13
16
|
'use strict';
|
|
14
17
|
|
|
15
18
|
const fs = require('node:fs');
|
|
16
19
|
const os = require('node:os');
|
|
17
20
|
const path = require('node:path');
|
|
21
|
+
const childProcess = require('node:child_process');
|
|
22
|
+
const { semverGreater, compareVersions } = require('./semver.js');
|
|
18
23
|
|
|
19
24
|
const CACHE_TTL_MS = 60 * 60 * 1000; // 1h
|
|
25
|
+
const ATTEMPT_THROTTLE_MS = 10 * 60 * 1000; // 10 min between background refresh attempts
|
|
20
26
|
const NETWORK_TIMEOUT_MS = 1500;
|
|
21
27
|
|
|
22
28
|
function cacheFile() {
|
|
@@ -24,17 +30,43 @@ function cacheFile() {
|
|
|
24
30
|
return path.join(base, 'wize-dev-kit', 'registry-version.json');
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
function
|
|
33
|
+
function toMs(v) {
|
|
34
|
+
if (typeof v === 'number' && Number.isFinite(v)) return v;
|
|
35
|
+
if (typeof v === 'string') { const t = Date.parse(v); if (Number.isFinite(t)) return t; }
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The cache entry regardless of age: { version, fetchedAt, attemptedAt }.
|
|
40
|
+
// fetched_at drives the TTL; the file mtime only stands in for a legacy payload
|
|
41
|
+
// that has none. Null when the file is missing or unreadable.
|
|
42
|
+
function readCacheEntry() {
|
|
28
43
|
const f = cacheFile();
|
|
29
44
|
try {
|
|
30
45
|
const stat = fs.statSync(f);
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
const payload = JSON.parse(fs.readFileSync(f, 'utf-8'));
|
|
47
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
48
|
+
const fetchedAt = toMs(payload.fetched_at);
|
|
49
|
+
return {
|
|
50
|
+
version: typeof payload.version === 'string' && payload.version ? payload.version : null,
|
|
51
|
+
fetchedAt: fetchedAt === null ? stat.mtimeMs : fetchedAt,
|
|
52
|
+
attemptedAt: toMs(payload.attempted_at) || 0,
|
|
53
|
+
payload
|
|
54
|
+
};
|
|
33
55
|
} catch (_) {
|
|
34
56
|
return null;
|
|
35
57
|
}
|
|
36
58
|
}
|
|
37
59
|
|
|
60
|
+
function isFresh(entry, now = Date.now()) {
|
|
61
|
+
return !!(entry && entry.version && now - entry.fetchedAt <= CACHE_TTL_MS);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Returns the cached payload only while it is fresh.
|
|
65
|
+
function readCache() {
|
|
66
|
+
const entry = readCacheEntry();
|
|
67
|
+
return isFresh(entry) ? entry.payload : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
38
70
|
function writeCache(payload) {
|
|
39
71
|
const f = cacheFile();
|
|
40
72
|
try {
|
|
@@ -43,7 +75,19 @@ function writeCache(payload) {
|
|
|
43
75
|
} catch (_) { /* cache failure is harmless */ }
|
|
44
76
|
}
|
|
45
77
|
|
|
46
|
-
|
|
78
|
+
// Records a refresh attempt without touching the cached answer or its age.
|
|
79
|
+
function markAttempted(now = Date.now()) {
|
|
80
|
+
const entry = readCacheEntry();
|
|
81
|
+
writeCache({
|
|
82
|
+
version: entry ? entry.version : null,
|
|
83
|
+
fetched_at: entry ? entry.fetchedAt : 0,
|
|
84
|
+
attempted_at: now
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// { latest, versions } from the registry packument, or null on any failure.
|
|
89
|
+
// `versions` lists every published version (null when the packument has none).
|
|
90
|
+
async function fetchRegistryInfo() {
|
|
47
91
|
const ctrl = new AbortController();
|
|
48
92
|
const timer = setTimeout(() => ctrl.abort(), NETWORK_TIMEOUT_MS);
|
|
49
93
|
try {
|
|
@@ -54,7 +98,9 @@ async function fetchLatestFromRegistry() {
|
|
|
54
98
|
if (!res.ok) return null;
|
|
55
99
|
const body = await res.json();
|
|
56
100
|
const latest = body && body['dist-tags'] && body['dist-tags'].latest;
|
|
57
|
-
|
|
101
|
+
if (typeof latest !== 'string') return null;
|
|
102
|
+
const versions = body.versions && typeof body.versions === 'object' ? Object.keys(body.versions) : null;
|
|
103
|
+
return { latest, versions };
|
|
58
104
|
} catch (_) {
|
|
59
105
|
return null; // any error → silent fallback
|
|
60
106
|
} finally {
|
|
@@ -62,41 +108,107 @@ async function fetchLatestFromRegistry() {
|
|
|
62
108
|
}
|
|
63
109
|
}
|
|
64
110
|
|
|
111
|
+
async function fetchLatestFromRegistry() {
|
|
112
|
+
const info = await fetchRegistryInfo();
|
|
113
|
+
return info ? info.latest : null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Resolves the registry-latest version: fresh cache first, then the network
|
|
117
|
+
// (capped, abortable). On failure the previous answer survives as a stale
|
|
118
|
+
// cache hit. Never throws.
|
|
119
|
+
// -> { latest, source: 'cache'|'network'|'none', stale }
|
|
120
|
+
async function resolveLatest({ skipCache = false } = {}) {
|
|
121
|
+
const entry = readCacheEntry();
|
|
122
|
+
if (!skipCache && isFresh(entry)) return { latest: entry.version, source: 'cache', stale: false };
|
|
123
|
+
const info = await fetchRegistryInfo();
|
|
124
|
+
if (info) {
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
writeCache({ version: info.latest, fetched_at: now, attempted_at: now });
|
|
127
|
+
return { latest: info.latest, source: 'network', stale: false, versions: info.versions };
|
|
128
|
+
}
|
|
129
|
+
markAttempted();
|
|
130
|
+
if (entry && entry.version) return { latest: entry.version, source: 'cache', stale: true };
|
|
131
|
+
return { latest: null, source: 'none', stale: false };
|
|
132
|
+
}
|
|
133
|
+
|
|
65
134
|
// Returns the registry-latest version, hitting the cache first. Never throws,
|
|
66
|
-
// never blocks meaningfully (network call is capped + abortable).
|
|
135
|
+
// never blocks meaningfully (network call is capped + abortable). A stale
|
|
136
|
+
// cache is not an answer here: when the fetch fails this resolves to null.
|
|
67
137
|
async function getLatestVersion({ skipCache = false } = {}) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (c && c.version) return c.version;
|
|
71
|
-
}
|
|
72
|
-
const fresh = await fetchLatestFromRegistry();
|
|
73
|
-
if (fresh) writeCache({ version: fresh, fetched_at: Date.now() });
|
|
74
|
-
return fresh;
|
|
138
|
+
const r = await resolveLatest({ skipCache });
|
|
139
|
+
return r.stale ? null : r.latest;
|
|
75
140
|
}
|
|
76
141
|
|
|
77
|
-
function
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
for (let i = 0; i < 3; i++) {
|
|
81
|
-
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
82
|
-
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
142
|
+
function isCI(env = process.env) {
|
|
143
|
+
const v = env.CI;
|
|
144
|
+
return !!v && v !== '0' && v.toLowerCase() !== 'false';
|
|
85
145
|
}
|
|
86
146
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
147
|
+
function refresherArgs() {
|
|
148
|
+
return [path.join(__dirname, 'wize-cli.js'), 'version-check', '--refresh', '--quiet'];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Stale-while-revalidate: returns the cached version whatever its age (null
|
|
152
|
+
// when there is none) and never waits on the network. When the cache is
|
|
153
|
+
// older than the TTL and no attempt was made in the last 10 minutes, stamps
|
|
154
|
+
// attempted_at and starts a detached `version-check --refresh --quiet`.
|
|
155
|
+
// No refresher runs when updates are disabled, in CI, or with
|
|
156
|
+
// WIZE_NO_BG_REFRESH=1. `spawn` is injectable for tests.
|
|
157
|
+
function getLatestNoWait({ spawn = childProcess.spawn, env = process.env, now = Date.now() } = {}) {
|
|
158
|
+
const entry = readCacheEntry();
|
|
159
|
+
const version = entry ? entry.version : null;
|
|
160
|
+
try {
|
|
161
|
+
if (env.WIZE_DISABLE_UPDATE_CHECK === '1' || env.WIZE_NO_BG_REFRESH === '1' || isCI(env)) return version;
|
|
162
|
+
if (isFresh(entry, now)) return version;
|
|
163
|
+
if (entry && now - entry.attemptedAt < ATTEMPT_THROTTLE_MS) return version;
|
|
164
|
+
markAttempted(now);
|
|
165
|
+
const child = spawn(process.execPath, refresherArgs(), {
|
|
166
|
+
detached: true,
|
|
167
|
+
stdio: 'ignore',
|
|
168
|
+
env: { ...env, WIZE_NO_BG_REFRESH: '1' }
|
|
169
|
+
});
|
|
170
|
+
if (child && typeof child.on === 'function') child.on('error', () => {});
|
|
171
|
+
if (child && typeof child.unref === 'function') child.unref();
|
|
172
|
+
} catch (_) { /* a failed spawn never disturbs the command */ }
|
|
173
|
+
return version;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// One-line hints for a human's terminal. Never waits on the network: the
|
|
177
|
+
// registry answer comes from getLatestNoWait. Silent when stdout is not a
|
|
178
|
+
// TTY, in CI, when updates are disabled, or when `--json` is in argv.
|
|
179
|
+
// When the project's kit_version trails the running kit, that line comes
|
|
180
|
+
// first.
|
|
181
|
+
async function printUpdateHintIfAny(currentVersion, opts = {}) {
|
|
182
|
+
const {
|
|
183
|
+
log = console.log,
|
|
184
|
+
isTTY = process.stdout.isTTY,
|
|
185
|
+
env = process.env,
|
|
186
|
+
argv = process.argv.slice(2),
|
|
187
|
+
projectRoot = null,
|
|
188
|
+
spawn
|
|
189
|
+
} = opts;
|
|
190
|
+
if (env.WIZE_DISABLE_UPDATE_CHECK === '1') return;
|
|
92
191
|
if (!isTTY) return; // don't spam pipes
|
|
192
|
+
if (isCI(env)) return;
|
|
193
|
+
if (argv.includes('--json')) return;
|
|
93
194
|
try {
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
96
|
-
|
|
195
|
+
const lines = [];
|
|
196
|
+
if (projectRoot) {
|
|
197
|
+
const { loadProjectConfig } = require('./toml.js');
|
|
198
|
+
const cfg = loadProjectConfig(projectRoot);
|
|
199
|
+
const pin = cfg.project && cfg.project.kit_version;
|
|
200
|
+
if (pin && compareVersions(pin, currentVersion) < 0) {
|
|
201
|
+
lines.push(` ↑ wize-dev-kit: project pinned ${pin}, kit ${currentVersion} -> run npx wize-dev-kit@latest update (or /wize-update)`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const latest = getLatestNoWait({ env, ...(spawn ? { spawn } : {}) });
|
|
205
|
+
if (latest && semverGreater(latest, currentVersion)) {
|
|
206
|
+
lines.push(` ↑ Update available: wize-dev-kit ${currentVersion} → ${latest}`);
|
|
207
|
+
lines.push(' Run `npx wize-dev-kit@latest update` in your project to refresh adapters.');
|
|
208
|
+
}
|
|
209
|
+
if (lines.length) {
|
|
97
210
|
log('');
|
|
98
|
-
|
|
99
|
-
log(` Run \`npx wize-dev-kit@latest update\` in your project to refresh adapters.`);
|
|
211
|
+
for (const l of lines) log(l);
|
|
100
212
|
log('');
|
|
101
213
|
}
|
|
102
214
|
} catch (_) {
|
|
@@ -123,15 +235,33 @@ async function getVersionCheckResult(currentVersion, { skipCache = false } = {})
|
|
|
123
235
|
};
|
|
124
236
|
}
|
|
125
237
|
|
|
238
|
+
// Same answer plus where it came from. Unlike getVersionCheckResult it falls
|
|
239
|
+
// back to a stale cache when the registry is unreachable.
|
|
240
|
+
async function getVersionCheckDetailed(currentVersion, { skipCache = false } = {}) {
|
|
241
|
+
if (process.env.WIZE_DISABLE_UPDATE_CHECK === '1') {
|
|
242
|
+
return { installed: currentVersion, latest: null, updateAvailable: false, disabled: true, latest_source: 'none' };
|
|
243
|
+
}
|
|
244
|
+
const r = await resolveLatest({ skipCache });
|
|
245
|
+
return {
|
|
246
|
+
installed: currentVersion,
|
|
247
|
+
latest: r.latest,
|
|
248
|
+
updateAvailable: r.latest ? semverGreater(r.latest, currentVersion) : false,
|
|
249
|
+
latest_source: r.source,
|
|
250
|
+
...(r.versions ? { published_versions: r.versions } : {})
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
126
254
|
module.exports = {
|
|
127
|
-
CACHE_TTL_MS,
|
|
128
|
-
NETWORK_TIMEOUT_MS,
|
|
129
255
|
cacheFile,
|
|
130
256
|
readCache,
|
|
131
257
|
writeCache,
|
|
132
258
|
fetchLatestFromRegistry,
|
|
259
|
+
resolveLatest,
|
|
133
260
|
getLatestVersion,
|
|
261
|
+
getLatestNoWait,
|
|
134
262
|
semverGreater,
|
|
263
|
+
isCI,
|
|
135
264
|
printUpdateHintIfAny,
|
|
136
|
-
getVersionCheckResult
|
|
265
|
+
getVersionCheckResult,
|
|
266
|
+
getVersionCheckDetailed
|
|
137
267
|
};
|