@itcase/config 1.6.75 → 1.6.76
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/package.json +1 -1
- package/smart-commit/cli.js +95 -10
package/package.json
CHANGED
package/smart-commit/cli.js
CHANGED
|
@@ -24,6 +24,9 @@ const MAX_DIFF = 80_000
|
|
|
24
24
|
const AGENT_TIMEOUT_MS = 120_000
|
|
25
25
|
const AGENT_STEP_MS = 2_500
|
|
26
26
|
const BANANA_FRAME_MS = 100
|
|
27
|
+
const INDEX_LOCK_RETRIES = 8
|
|
28
|
+
const INDEX_LOCK_WAIT_MS = 250
|
|
29
|
+
const STALE_LOCK_MS = 1500
|
|
27
30
|
const AGENT_WAIT_STEPS = [
|
|
28
31
|
'Asking Cursor Agent',
|
|
29
32
|
'Still thinking',
|
|
@@ -115,6 +118,8 @@ async function main(argv) {
|
|
|
115
118
|
)
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
ensureGitIndexReady(cwd)
|
|
122
|
+
|
|
118
123
|
let created = 0
|
|
119
124
|
for (const plan of activePlans) {
|
|
120
125
|
const files = plan.files.filter((file) => fileHasChanges(cwd, file))
|
|
@@ -135,12 +140,13 @@ async function main(argv) {
|
|
|
135
140
|
)
|
|
136
141
|
}
|
|
137
142
|
|
|
138
|
-
|
|
139
|
-
cwd:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
try {
|
|
144
|
+
git(cwd, ['commit', '-m', plan.message], { inherit: true })
|
|
145
|
+
} catch (error) {
|
|
146
|
+
throw new Error(`git commit failed: ${plan.message}\n${error.message}`, {
|
|
147
|
+
cause: error,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
144
150
|
created++
|
|
145
151
|
}
|
|
146
152
|
process.stdout.write(`Created ${created} commit(s).\n`)
|
|
@@ -593,10 +599,89 @@ async function confirm(count) {
|
|
|
593
599
|
}
|
|
594
600
|
}
|
|
595
601
|
|
|
596
|
-
function
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
602
|
+
function isIndexLockError(stderr) {
|
|
603
|
+
return /index\.lock/.test(stderr || '')
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function sleepSync(ms) {
|
|
607
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function indexLockPath(cwd) {
|
|
611
|
+
const result = spawnSync('git', ['rev-parse', '--git-path', 'index.lock'], {
|
|
612
|
+
cwd: cwd,
|
|
613
|
+
encoding: 'utf8',
|
|
614
|
+
})
|
|
615
|
+
const gitPath = (result.stdout || '').trim()
|
|
616
|
+
if (!gitPath) return path.join(cwd, '.git', 'index.lock')
|
|
617
|
+
return path.resolve(cwd, gitPath)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function lockHolders(lockPath) {
|
|
621
|
+
const result = spawnSync('lsof', ['-t', lockPath], { encoding: 'utf8' })
|
|
622
|
+
if (result.error || result.status === 127) return null
|
|
623
|
+
return (result.stdout || '').trim().split('\n').filter(Boolean)
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function clearStaleIndexLock(cwd) {
|
|
627
|
+
const lockPath = indexLockPath(cwd)
|
|
628
|
+
let stat
|
|
629
|
+
try {
|
|
630
|
+
stat = fs.statSync(lockPath)
|
|
631
|
+
} catch {
|
|
632
|
+
return false
|
|
633
|
+
}
|
|
634
|
+
if (Date.now() - stat.mtimeMs < STALE_LOCK_MS) return false
|
|
635
|
+
const holders = lockHolders(lockPath)
|
|
636
|
+
if (holders && holders.length) return false
|
|
637
|
+
try {
|
|
638
|
+
fs.unlinkSync(lockPath)
|
|
639
|
+
return true
|
|
640
|
+
} catch {
|
|
641
|
+
return false
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function ensureGitIndexReady(cwd) {
|
|
646
|
+
for (let attempt = 1; attempt <= INDEX_LOCK_RETRIES; attempt++) {
|
|
647
|
+
const lockPath = indexLockPath(cwd)
|
|
648
|
+
if (!fs.existsSync(lockPath)) return
|
|
649
|
+
if (attempt === 1) {
|
|
650
|
+
process.stderr.write('Waiting for git index.lock to clear…\n')
|
|
651
|
+
}
|
|
652
|
+
clearStaleIndexLock(cwd)
|
|
653
|
+
if (!fs.existsSync(lockPath)) return
|
|
654
|
+
sleepSync(INDEX_LOCK_WAIT_MS * attempt)
|
|
655
|
+
}
|
|
656
|
+
const lockPath = indexLockPath(cwd)
|
|
657
|
+
if (!fs.existsSync(lockPath)) return
|
|
658
|
+
throw new Error(
|
|
659
|
+
`git index.lock still exists: ${lockPath}\n` +
|
|
660
|
+
'Another git process may be running. If it crashed, remove the file and retry.',
|
|
661
|
+
)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function git(cwd, args, option = {}) {
|
|
665
|
+
for (let attempt = 1; attempt <= INDEX_LOCK_RETRIES; attempt++) {
|
|
666
|
+
const result = spawnSync('git', args, {
|
|
667
|
+
cwd: cwd,
|
|
668
|
+
encoding: 'utf8',
|
|
669
|
+
stdio: option.inherit ? ['ignore', 'inherit', 'pipe'] : undefined,
|
|
670
|
+
})
|
|
671
|
+
if (!result.status) return result
|
|
672
|
+
|
|
673
|
+
const stderr = result.stderr || ''
|
|
674
|
+
if (!isIndexLockError(stderr) || attempt === INDEX_LOCK_RETRIES) {
|
|
675
|
+
if (option.inherit && stderr) process.stderr.write(stderr)
|
|
676
|
+
throw new Error(stderr.trim() || `git ${args.join(' ')}`)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (attempt === 1) {
|
|
680
|
+
process.stderr.write('Waiting for git index.lock to clear…\n')
|
|
681
|
+
}
|
|
682
|
+
clearStaleIndexLock(cwd)
|
|
683
|
+
sleepSync(INDEX_LOCK_WAIT_MS * attempt)
|
|
684
|
+
}
|
|
600
685
|
}
|
|
601
686
|
|
|
602
687
|
function out(cwd, args) {
|