@itcase/config 1.7.17 → 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 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]`. Перед вызовом агента запускается та же проверка типов, что у husky в lint-staged: `tsc --noEmit --pretty`, либо `npm run typecheck`, если конфиг — `lintStagedMonorepo`. Проверка идёт только когда среди изменений есть существующие `.ts`/`.tsx`. Если типы не сходятся, агент не вызывается. `--no-verify` пропускает и эту проверку, и хуки. Перед коммитом CLI ждёт и снимает устаревший `.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.17",
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.2.1",
71
- "cssnano": "^9.1.1",
72
- "cssnano-preset-default": "^9.1.1",
73
- "inquirer": "^14.2.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.28",
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": "^10.0.3",
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.4",
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.5.4",
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.2",
120
+ "inquirer": "^14.2.3",
121
121
  "lint-staged": "^17.6.0",
122
122
  "prettier": "^3.9.9",
123
- "storybook": "^10.6.0",
124
- "stylelint": "^17.15.0",
123
+ "storybook": "^10.6.1",
124
+ "stylelint": "^17.16.0",
125
125
  "typescript": "^6.0.3"
126
126
  }
127
127
  }
@@ -59,6 +59,20 @@ const LINT_STAGED_CONFIGS = [
59
59
  '.lintstagedrc.cjs',
60
60
  ]
61
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
+ ]
62
76
  const AGENT_WAIT_STEPS = [
63
77
  'Asking Cursor Agent',
64
78
  'Still thinking',
@@ -76,10 +90,12 @@ Default model: ${DEFAULT_MODEL} (override with --model / SMART_COMMIT_MODEL).
76
90
  (husky pre-commit / lint-staged and commit-msg). Commit message
77
91
  is still checked by smart-commit before git runs.
78
92
 
79
- Before the agent, the same typecheck as husky lint-staged runs
80
- when changes include .ts/.tsx (tsc --noEmit, or npm run typecheck
81
- from lint-staged config). A type error exits before the agent
82
- is called. --no-verify skips this check too.
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.
83
99
 
84
100
  Progress sprite is random (nyan cat, banana, or upyachka).
85
101
  Force with --cat, --banana, or --upyachka. Agent log is below.
@@ -118,8 +134,10 @@ async function main(argv) {
118
134
  return
119
135
  }
120
136
 
121
- // Husky typechecks in pre-commit, after the agent has already spent tokens.
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.
122
139
  if (!option.noVerify) {
140
+ await runHuskyLint(cwd, changedFiles)
123
141
  await runHuskyTypecheck(cwd, changedFiles)
124
142
  }
125
143
 
@@ -390,11 +408,78 @@ function readReleaseBranches(cwd) {
390
408
  return match[1].split(/\s+/).filter(Boolean)
391
409
  }
392
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
+
393
467
  async function runHuskyTypecheck(cwd, files) {
394
468
  const command = await typecheckCommandForFiles(cwd, files)
395
469
  if (!command) return
396
470
 
397
- process.stdout.write(`${paint('1', 'Typecheck')} ${dim(command)}\n`)
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`)
398
483
  const bin = path.join(cwd, 'node_modules', '.bin')
399
484
  const result = spawnSync(command, {
400
485
  cwd: cwd,
@@ -406,14 +491,11 @@ async function runHuskyTypecheck(cwd, files) {
406
491
  stdio: 'inherit',
407
492
  })
408
493
  if (result.error) {
409
- throw new Error(`Typecheck failed to start: ${result.error.message}`)
494
+ throw new Error(`${label} failed to start: ${result.error.message}`)
410
495
  }
411
496
  if (result.status !== 0) {
412
- throw new Error(
413
- 'Typecheck failed. Fix the errors above — the agent was not called.',
414
- )
497
+ throw new Error(failMessage)
415
498
  }
416
- process.stdout.write('Typecheck passed.\n\n')
417
499
  }
418
500
 
419
501
  async function typecheckCommandForFiles(cwd, files) {
@@ -443,9 +525,7 @@ function pickTypecheckCommand(commands) {
443
525
  )
444
526
  if (explicit) return explicit.trim()
445
527
 
446
- const other = list.find(
447
- (command) => !/^\s*"?(?:eslint|stylelint)\b/.test(command),
448
- )
528
+ const other = list.find((command) => !isLintCommand(command))
449
529
  return other ? other.trim() : ''
450
530
  }
451
531