@itcase/config 1.7.16 → 1.7.18
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 +1 -1
- package/package.json +12 -12
- package/smart-commit/cli.js +175 -0
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ npm install --save-dev commitizen @commitlint/cz-commitlint
|
|
|
103
103
|
|
|
104
104
|
## Smart commit
|
|
105
105
|
|
|
106
|
-
`smart-commit` — Cursor Agent (`ask`) планирует коммиты по `SCOPES`, печатает сообщения, спрашивает `[y/N]`. Перед коммитом ждёт и снимает устаревший `.git/index.lock`, если его никто не держит. Lockfile (`package-lock.json` и аналоги) всегда кладётся в тот же коммит, что и соседний `package.json`; при одном только bump deps агент не вызывается.
|
|
106
|
+
`smart-commit` — Cursor Agent (`ask`) планирует коммиты по `SCOPES`, печатает сообщения, спрашивает `[y/N]`. Перед вызовом агента запускаются те же проверки, что у husky в lint-staged. Сначала eslint и stylelint по изменённым js/ts/css/mdx (с `--fix`, как в хуке): если линтер падает, агент не вызывается и ни один коммит не создаётся. Затем проверка типов: `tsc --noEmit --pretty`, либо `npm run typecheck`, если конфиг — `lintStagedMonorepo`. Typecheck идёт только когда среди изменений есть существующие `.ts`/`.tsx`. `--no-verify` пропускает и эти проверки, и хуки. Перед коммитом CLI ждёт и снимает устаревший `.git/index.lock`, если его никто не держит. Lockfile (`package-lock.json` и аналоги) всегда кладётся в тот же коммит, что и соседний `package.json`; при одном только bump deps агент не вызывается.
|
|
107
107
|
|
|
108
108
|
```text
|
|
109
109
|
[random pixel animation]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itcase/config",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.18",
|
|
4
4
|
"author": "ITCase",
|
|
5
5
|
"description": "ITCase Config",
|
|
6
6
|
"engines": {
|
|
@@ -67,20 +67,20 @@
|
|
|
67
67
|
"chokidar-cli": "^3.0.0",
|
|
68
68
|
"commitizen": "^4.3.2",
|
|
69
69
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
70
|
-
"conventional-changelog-writer": "9.
|
|
71
|
-
"cssnano": "^9.
|
|
72
|
-
"cssnano-preset-default": "^9.
|
|
73
|
-
"inquirer": "^14.2.
|
|
70
|
+
"conventional-changelog-writer": "9.3.0",
|
|
71
|
+
"cssnano": "^9.3.2",
|
|
72
|
+
"cssnano-preset-default": "^9.3.2",
|
|
73
|
+
"inquirer": "^14.2.3",
|
|
74
74
|
"log-update": "^8.0.0",
|
|
75
|
-
"postcss": "^8.5.
|
|
75
|
+
"postcss": "^8.5.29",
|
|
76
76
|
"postcss-aspect-ratio-polyfill": "^2.0.0",
|
|
77
77
|
"postcss-clamp": "^4.1.0",
|
|
78
78
|
"postcss-cli": "^12.0.0",
|
|
79
79
|
"postcss-color-hsla-fallback": "^1.0.0",
|
|
80
|
-
"postcss-combine-duplicated-selectors": "^
|
|
80
|
+
"postcss-combine-duplicated-selectors": "^11.0.0",
|
|
81
81
|
"postcss-convert-unit": "^0.1.3",
|
|
82
82
|
"postcss-dark-theme-class": "^2.0.0",
|
|
83
|
-
"postcss-discard-duplicates": "^9.0.
|
|
83
|
+
"postcss-discard-duplicates": "^9.0.5",
|
|
84
84
|
"postcss-each": "^1.1.0",
|
|
85
85
|
"postcss-easings": "^4.0.0",
|
|
86
86
|
"postcss-extend-rule": "^4.0.0",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"postcss-nested-ancestors": "^3.0.0",
|
|
97
97
|
"postcss-normalize": "^13.0.1",
|
|
98
98
|
"postcss-prepend-imports": "^1.0.1",
|
|
99
|
-
"postcss-preset-env": "^11.
|
|
99
|
+
"postcss-preset-env": "^11.6.1",
|
|
100
100
|
"postcss-pxtorem": "^6.1.0",
|
|
101
101
|
"postcss-sort-media-queries": "^6.7.1",
|
|
102
102
|
"postcss-unit": "^0.0.3",
|
|
@@ -117,11 +117,11 @@
|
|
|
117
117
|
"@itcase/lint": "^1.1.126",
|
|
118
118
|
"eslint": "9.39.2",
|
|
119
119
|
"husky": "^9.1.7",
|
|
120
|
-
"inquirer": "^14.2.
|
|
120
|
+
"inquirer": "^14.2.3",
|
|
121
121
|
"lint-staged": "^17.6.0",
|
|
122
122
|
"prettier": "^3.9.9",
|
|
123
|
-
"storybook": "^10.6.
|
|
124
|
-
"stylelint": "^17.
|
|
123
|
+
"storybook": "^10.6.1",
|
|
124
|
+
"stylelint": "^17.16.0",
|
|
125
125
|
"typescript": "^6.0.3"
|
|
126
126
|
}
|
|
127
127
|
}
|
package/smart-commit/cli.js
CHANGED
|
@@ -50,6 +50,29 @@ const UPYACHKA_FRAME_MS = 100
|
|
|
50
50
|
const INDEX_LOCK_RETRIES = 8
|
|
51
51
|
const INDEX_LOCK_WAIT_MS = 250
|
|
52
52
|
const STALE_LOCK_MS = 1500
|
|
53
|
+
const LINT_STAGED_CONFIGS = [
|
|
54
|
+
'lint-staged.config.mjs',
|
|
55
|
+
'lint-staged.config.js',
|
|
56
|
+
'lint-staged.config.cjs',
|
|
57
|
+
'.lintstagedrc.mjs',
|
|
58
|
+
'.lintstagedrc.js',
|
|
59
|
+
'.lintstagedrc.cjs',
|
|
60
|
+
]
|
|
61
|
+
const LINT_STAGED_TS_KEY = '*.{js,jsx,mjs,cjs,ts,tsx}'
|
|
62
|
+
const LINT_STAGED_FILE_GROUPS = [
|
|
63
|
+
{
|
|
64
|
+
key: LINT_STAGED_TS_KEY,
|
|
65
|
+
test: /\.(?:js|jsx|mjs|cjs|ts|tsx)$/,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: '*.css',
|
|
69
|
+
test: /\.css$/,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
key: '*.mdx',
|
|
73
|
+
test: /\.mdx$/,
|
|
74
|
+
},
|
|
75
|
+
]
|
|
53
76
|
const AGENT_WAIT_STEPS = [
|
|
54
77
|
'Asking Cursor Agent',
|
|
55
78
|
'Still thinking',
|
|
@@ -67,6 +90,13 @@ Default model: ${DEFAULT_MODEL} (override with --model / SMART_COMMIT_MODEL).
|
|
|
67
90
|
(husky pre-commit / lint-staged and commit-msg). Commit message
|
|
68
91
|
is still checked by smart-commit before git runs.
|
|
69
92
|
|
|
93
|
+
Before the agent, the same checks as husky lint-staged run.
|
|
94
|
+
eslint and stylelint (--fix) run on changed js/ts/css/mdx, then
|
|
95
|
+
typecheck when changes include .ts/.tsx (tsc --noEmit, or
|
|
96
|
+
npm run typecheck from lint-staged config). A lint or type
|
|
97
|
+
error exits before the agent is called and before any commit.
|
|
98
|
+
--no-verify skips these checks and the hooks.
|
|
99
|
+
|
|
70
100
|
Progress sprite is random (nyan cat, banana, or upyachka).
|
|
71
101
|
Force with --cat, --banana, or --upyachka. Agent log is below.
|
|
72
102
|
`
|
|
@@ -104,6 +134,13 @@ async function main(argv) {
|
|
|
104
134
|
return
|
|
105
135
|
}
|
|
106
136
|
|
|
137
|
+
// Husky lints and typechecks in pre-commit. By then the agent has spent
|
|
138
|
+
// tokens and earlier commits in the plan may already exist.
|
|
139
|
+
if (!option.noVerify) {
|
|
140
|
+
await runHuskyLint(cwd, changedFiles)
|
|
141
|
+
await runHuskyTypecheck(cwd, changedFiles)
|
|
142
|
+
}
|
|
143
|
+
|
|
107
144
|
let plans = tryFastPlan(changedFiles, cwd, scopes, types)
|
|
108
145
|
if (!plans) {
|
|
109
146
|
plans = await planWithAgent({
|
|
@@ -371,6 +408,144 @@ function readReleaseBranches(cwd) {
|
|
|
371
408
|
return match[1].split(/\s+/).filter(Boolean)
|
|
372
409
|
}
|
|
373
410
|
|
|
411
|
+
async function runHuskyLint(cwd, files) {
|
|
412
|
+
const commands = await lintCommandsForFiles(cwd, files)
|
|
413
|
+
if (!commands.length) return
|
|
414
|
+
|
|
415
|
+
for (const entry of commands) {
|
|
416
|
+
runShellCommand(
|
|
417
|
+
cwd,
|
|
418
|
+
'Lint',
|
|
419
|
+
lintDisplay(entry.command, entry.fileCount),
|
|
420
|
+
entry.command,
|
|
421
|
+
'Lint failed. Fix the errors above — the agent was not called.',
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
process.stdout.write('Lint passed.\n\n')
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function lintCommandsForFiles(cwd, files) {
|
|
428
|
+
const config = await loadLintStagedConfig(cwd)
|
|
429
|
+
if (!config) return []
|
|
430
|
+
|
|
431
|
+
const commands = []
|
|
432
|
+
for (const group of LINT_STAGED_FILE_GROUPS) {
|
|
433
|
+
const task = config[group.key]
|
|
434
|
+
if (typeof task !== 'function') continue
|
|
435
|
+
|
|
436
|
+
const matched = files.filter(
|
|
437
|
+
(file) => group.test.test(file) && fs.existsSync(path.resolve(cwd, file)),
|
|
438
|
+
)
|
|
439
|
+
if (!matched.length) continue
|
|
440
|
+
|
|
441
|
+
const absolute = matched.map((file) => path.resolve(cwd, file))
|
|
442
|
+
const result = await task(absolute)
|
|
443
|
+
const list = (Array.isArray(result) ? result : [result]).filter(
|
|
444
|
+
(command) => typeof command === 'string' && isLintCommand(command),
|
|
445
|
+
)
|
|
446
|
+
for (const command of list) {
|
|
447
|
+
commands.push({
|
|
448
|
+
command: command.trim(),
|
|
449
|
+
fileCount: matched.length,
|
|
450
|
+
})
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return commands
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function lintDisplay(command, fileCount) {
|
|
457
|
+
const tool = command.match(/"?(eslint|stylelint)\b/)?.[1] || 'lint'
|
|
458
|
+
const fix = /(?:^|\s)"?--fix"?(?:\s|$)/.test(command) ? ' --fix' : ''
|
|
459
|
+
const filesLabel = fileCount === 1 ? '1 file' : `${fileCount} files`
|
|
460
|
+
return `${tool}${fix} (${filesLabel})`
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function isLintCommand(command) {
|
|
464
|
+
return /^\s*"?(?:eslint|stylelint)\b/.test(command)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async function runHuskyTypecheck(cwd, files) {
|
|
468
|
+
const command = await typecheckCommandForFiles(cwd, files)
|
|
469
|
+
if (!command) return
|
|
470
|
+
|
|
471
|
+
runShellCommand(
|
|
472
|
+
cwd,
|
|
473
|
+
'Typecheck',
|
|
474
|
+
command,
|
|
475
|
+
command,
|
|
476
|
+
'Typecheck failed. Fix the errors above — the agent was not called.',
|
|
477
|
+
)
|
|
478
|
+
process.stdout.write('Typecheck passed.\n\n')
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function runShellCommand(cwd, label, displayCommand, command, failMessage) {
|
|
482
|
+
process.stdout.write(`${paint('1', label)} ${dim(displayCommand)}\n`)
|
|
483
|
+
const bin = path.join(cwd, 'node_modules', '.bin')
|
|
484
|
+
const result = spawnSync(command, {
|
|
485
|
+
cwd: cwd,
|
|
486
|
+
env: {
|
|
487
|
+
...process.env,
|
|
488
|
+
PATH: `${bin}${path.delimiter}${process.env.PATH || ''}`,
|
|
489
|
+
},
|
|
490
|
+
shell: true,
|
|
491
|
+
stdio: 'inherit',
|
|
492
|
+
})
|
|
493
|
+
if (result.error) {
|
|
494
|
+
throw new Error(`${label} failed to start: ${result.error.message}`)
|
|
495
|
+
}
|
|
496
|
+
if (result.status !== 0) {
|
|
497
|
+
throw new Error(failMessage)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async function typecheckCommandForFiles(cwd, files) {
|
|
502
|
+
const tsFiles = files.filter(
|
|
503
|
+
(file) =>
|
|
504
|
+
/\.(ts|tsx)$/.test(file) && fs.existsSync(path.resolve(cwd, file)),
|
|
505
|
+
)
|
|
506
|
+
if (!tsFiles.length) return ''
|
|
507
|
+
|
|
508
|
+
const config = await loadLintStagedConfig(cwd)
|
|
509
|
+
const task = config?.[LINT_STAGED_TS_KEY]
|
|
510
|
+
if (typeof task !== 'function') return 'tsc --noEmit --pretty'
|
|
511
|
+
|
|
512
|
+
const absolute = tsFiles.map((file) => path.resolve(cwd, file))
|
|
513
|
+
const commands = await task(absolute)
|
|
514
|
+
return pickTypecheckCommand(commands)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function pickTypecheckCommand(commands) {
|
|
518
|
+
const list = (Array.isArray(commands) ? commands : [commands]).filter(
|
|
519
|
+
(command) => typeof command === 'string' && command.trim(),
|
|
520
|
+
)
|
|
521
|
+
const explicit = list.find((command) =>
|
|
522
|
+
/^(?:tsc|npm(?:\s+run)?\s+typecheck|pnpm(?:\s+run)?\s+typecheck|yarn(?:\s+run)?\s+typecheck)\b/.test(
|
|
523
|
+
command.trim(),
|
|
524
|
+
),
|
|
525
|
+
)
|
|
526
|
+
if (explicit) return explicit.trim()
|
|
527
|
+
|
|
528
|
+
const other = list.find((command) => !isLintCommand(command))
|
|
529
|
+
return other ? other.trim() : ''
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async function loadLintStagedConfig(cwd) {
|
|
533
|
+
const file = LINT_STAGED_CONFIGS.map((name) => path.join(cwd, name)).find(
|
|
534
|
+
(candidate) => fs.existsSync(candidate),
|
|
535
|
+
)
|
|
536
|
+
if (file) {
|
|
537
|
+
const loaded = await import(pathToFileURL(file).href)
|
|
538
|
+
return loaded.default ?? loaded
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const packageJsonPath = path.join(cwd, 'package.json')
|
|
542
|
+
if (!fs.existsSync(packageJsonPath)) return null
|
|
543
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
544
|
+
const field = packageJson['lint-staged']
|
|
545
|
+
if (field && typeof field === 'object') return field
|
|
546
|
+
return null
|
|
547
|
+
}
|
|
548
|
+
|
|
374
549
|
function listFiles(cwd, option) {
|
|
375
550
|
if (option.staged) {
|
|
376
551
|
return lines(cwd, [
|