sshlg-skills 1.74.0 → 1.74.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/CHANGELOG.md +21 -0
- package/bin/sshlg-skills.js +7 -3
- package/lib/claude-pinned.js +16 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## v1.74.2 (2026-10-22): an unread Codex is not a green step
|
|
2
|
+
|
|
3
|
+
- **B-252 closed:** the 1.74.1 `update` printed "11 of 11 steps, all green" beside "not read:
|
|
4
|
+
codex plugin list failed — spawnSync codex ETIMEDOUT", and Codex stayed at older pins. The
|
|
5
|
+
read took 31.5 s at a load average near 60 against a 30 s limit. A Codex read now has 120 s
|
|
6
|
+
(`SSHLG_CODEX_READ_TIMEOUT_MS` overrides, never below one second), and a Codex that cannot be
|
|
7
|
+
read makes the run PARTIAL instead of green. `test/codex_unread_test.js` (4 cases, red on
|
|
8
|
+
1.74.1).
|
|
9
|
+
|
|
10
|
+
## v1.74.1 (2026-10-22): a git worktree's local scope no longer fails the Claude update
|
|
11
|
+
|
|
12
|
+
- **B-251 closed:** the 1.74.0 `update` on the operator machine failed xr-dev (and rolled it
|
|
13
|
+
back) because one of its local scopes was a linked git worktree. Claude Code 2.1.299 runs a
|
|
14
|
+
local-scope `update` from a worktree against its main repository's record and exits 0
|
|
15
|
+
(measured in a scratch HOME: "updated ... for scope local (<repo>)", the worktree's record
|
|
16
|
+
unchanged), so the transaction's readback saw the wrong record move. The planner
|
|
17
|
+
(`lib/claude-pinned.js`) now leaves a worktree's record as recorded beside a recorded main
|
|
18
|
+
repository, disclosed as `left as recorded: <worktree> (alias of <main>)`, and blocks the member
|
|
19
|
+
by name when the main repository has no record. A submodule checkout is unaffected.
|
|
20
|
+
`test/claude_pinned_test.js` 34 to 37 cases.
|
|
21
|
+
|
|
1
22
|
## v1.74.0 (2026-10-22): the design-review gate, subscription growth, milestones M1 and M2
|
|
2
23
|
|
|
3
24
|
- **Pins:** task-pipeline 1.101.0 (1.100.0's design-review gate: the `design-tester` agent, a
|
package/bin/sshlg-skills.js
CHANGED
|
@@ -875,11 +875,14 @@ function codexPinRef(s, opts) {
|
|
|
875
875
|
* counted like every other `update` step, and a failure lands in the closing summary; a
|
|
876
876
|
* read (`--version`, `plugin list`) is captured and stays quiet.
|
|
877
877
|
*/
|
|
878
|
+
// A read of Codex's plugin list took 31.5 s on a loaded machine (2026-10-22); 30 s timed it
|
|
879
|
+
// out. SSHLG_CODEX_READ_TIMEOUT_MS overrides, never below one second.
|
|
880
|
+
const CODEX_READ_TIMEOUT_MS = Math.max(1000, Number(process.env.SSHLG_CODEX_READ_TIMEOUT_MS) || 120000);
|
|
878
881
|
function codexRunner(cmd, args, opts) {
|
|
879
882
|
if (!opts || !opts.read || !(args.length === 1 && args[0] === '--version'
|
|
880
883
|
|| args[0] === 'plugin' && args[1] === 'list' && (args.length === 2 || args.length === 3 && args[2] === '--json'))) throw new Error('Codex native mutation is deferred');
|
|
881
884
|
const r = spawnSync(cmd, args, { encoding: 'utf8', shell: process.platform === 'win32',
|
|
882
|
-
stdio: ['ignore', 'pipe', 'pipe'], timeout:
|
|
885
|
+
stdio: ['ignore', 'pipe', 'pipe'], timeout: CODEX_READ_TIMEOUT_MS, maxBuffer: 32 * 1024 * 1024 });
|
|
883
886
|
return { status: r.status, signal: r.signal, stdout: r.stdout || '', stderr: r.stderr || '', error: r.error };
|
|
884
887
|
}
|
|
885
888
|
|
|
@@ -921,7 +924,8 @@ function refreshCodexPlugins(f) {
|
|
|
921
924
|
}
|
|
922
925
|
if (out.status === 'unread') {
|
|
923
926
|
log(` not read: codex plugin list failed — ${out.why}. Nothing in Codex was touched.`);
|
|
924
|
-
|
|
927
|
+
// Not looked is not green: the summary reads PARTIAL and names this section.
|
|
928
|
+
return false;
|
|
925
929
|
}
|
|
926
930
|
for (const it of out.items) {
|
|
927
931
|
if (it.action === 'skip') log(` ${it.pluginId}: left alone — ${it.why}`);
|
|
@@ -4090,4 +4094,4 @@ if (require.main === module) {
|
|
|
4090
4094
|
process.exit(main(process.argv));
|
|
4091
4095
|
}
|
|
4092
4096
|
|
|
4093
|
-
module.exports = { cliSpec, pinnedArgv, resolvePayloads, resolveProviders };
|
|
4097
|
+
module.exports = { cliSpec, pinnedArgv, resolvePayloads, resolveProviders, refreshCodexPlugins, CODEX_READ_TIMEOUT_MS };
|
package/lib/claude-pinned.js
CHANGED
|
@@ -55,6 +55,14 @@ function configured(manifest) {
|
|
|
55
55
|
// Claude spells one payload directory differently per record (/var and /private/var on
|
|
56
56
|
// macOS, measured on 2.1.299), so installs are compared by their physical path.
|
|
57
57
|
const physical=p=>{try{return fs.realpathSync(p);}catch(_){return p;}};
|
|
58
|
+
// The main working tree of a linked git worktree (`.git` is a file naming
|
|
59
|
+
// `<main>/.git/worktrees/<name>`), or null. A submodule's `.git/modules/` is not one.
|
|
60
|
+
function worktreeMain(dir) {
|
|
61
|
+
let text;try{text=fs.readFileSync(path.join(dir,'.git'),'utf8');}catch(_){return null;}
|
|
62
|
+
const m=/^gitdir:\s*(.+?)\s*$/m.exec(text);if(!m) return null;
|
|
63
|
+
const gitdir=path.resolve(dir,m[1]),at=gitdir.lastIndexOf(path.sep+'.git'+path.sep+'worktrees'+path.sep);
|
|
64
|
+
return at<0?null:physical(gitdir.slice(0,at));
|
|
65
|
+
}
|
|
58
66
|
function prepare(members,inventory,opts) {
|
|
59
67
|
const requested={profile:path.resolve(opts.profileRoot),home:path.resolve(opts.home)};
|
|
60
68
|
const rows=Source.validate(inventory,members),profile=Bindings.profilePath(requested.profile),home=Bindings.profilePath(requested.home);
|
|
@@ -109,6 +117,14 @@ function prepare(members,inventory,opts) {
|
|
|
109
117
|
for(const r of existing) {
|
|
110
118
|
if(r.scope==='user') {targets.push({scope:'user',projectPath:null,record:r});continue;}
|
|
111
119
|
let real=null;try{real=fs.realpathSync(r.projectPath);}catch(_){}
|
|
120
|
+
// A linked git worktree: Claude runs a local-scope update from it against its main
|
|
121
|
+
// repository's record (measured on 2.1.299, 2026-10-22), so the worktree's own
|
|
122
|
+
// record is left as recorded beside a recorded main, and blocks without one.
|
|
123
|
+
const main=real===r.projectPath?worktreeMain(real):null;
|
|
124
|
+
if(main) {
|
|
125
|
+
if(locals.some(o=>o!==r && o.projectPath===main)) {aliases.push({projectPath:r.projectPath,real:main,record:r});continue;}
|
|
126
|
+
unaddressable=r.projectPath+' is a git worktree of '+main+', which has no record';break;
|
|
127
|
+
}
|
|
112
128
|
if(real===r.projectPath && fs.statSync(real).isDirectory()) targets.push({scope:'local',projectPath:r.projectPath,record:r});
|
|
113
129
|
// A path that resolves to ANOTHER record's project is that project under an
|
|
114
130
|
// old name: Claude never matches it, so it is left as recorded and read back.
|
package/package.json
CHANGED