@itcase/config 1.6.81 → 1.6.82

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.
@@ -48,10 +48,11 @@ itcase_conflict_marker_files() {
48
48
  }
49
49
 
50
50
  itcase_run_lint_staged() {
51
+ # ACMR drops deletions; include D so removed files stay in the commit.
51
52
  if [ -f "./node_modules/lint-staged/bin/lint-staged.js" ]; then
52
- node ./node_modules/lint-staged/bin/lint-staged.js
53
+ node ./node_modules/lint-staged/bin/lint-staged.js --diff-filter=ACDMR
53
54
  return $?
54
55
  fi
55
56
 
56
- npx lint-staged
57
+ npx lint-staged --diff-filter=ACDMR
57
58
  }
@@ -2,10 +2,21 @@ import { existsSync } from 'node:fs'
2
2
  import { resolve } from 'node:path'
3
3
 
4
4
  export default {
5
- '*.(js|jsx|ts|tsx)': (files) => `npx eslint --fix --color ${files.join(' ')}`,
6
- '*.css': (files) =>
7
- `npx stylelint --fix --color --formatter verbose ${files.join(' ')}`,
8
- '*.mdx': (files) => `npx eslint --fix --color ${files.join(' ')}`,
5
+ '*.(js|jsx|ts|tsx)': (files) => {
6
+ const existing = files.filter((file) => existsSync(file))
7
+ if (!existing.length) return 'true'
8
+ return `npx eslint --fix --color ${existing.join(' ')}`
9
+ },
10
+ '*.css': (files) => {
11
+ const existing = files.filter((file) => existsSync(file))
12
+ if (!existing.length) return 'true'
13
+ return `npx stylelint --fix --color --formatter verbose ${existing.join(' ')}`
14
+ },
15
+ '*.mdx': (files) => {
16
+ const existing = files.filter((file) => existsSync(file))
17
+ if (!existing.length) return 'true'
18
+ return `npx eslint --fix --color ${existing.join(' ')}`
19
+ },
9
20
  'package.json': (files) => {
10
21
  // Проверяем, есть ли package.json в корне репозитория
11
22
  const rootPackageJson = resolve(process.cwd(), 'package.json')
@@ -3,17 +3,27 @@ import { resolve } from 'node:path'
3
3
 
4
4
  export default {
5
5
  '*.(ts|tsx)': (files) => {
6
- const tsFiles = files.filter((f) => /\.(ts|tsx)$/.test(f))
6
+ const existing = files.filter((file) => existsSync(file))
7
+ const tsFiles = existing.filter((file) => /\.(ts|tsx)$/.test(file))
7
8
  const commands = []
8
9
  if (tsFiles.length > 0) {
9
10
  commands.push('tsc --noEmit --pretty')
10
11
  }
11
- commands.push(`npx eslint --fix ${files.join(' ')}`)
12
- return commands
12
+ if (existing.length > 0) {
13
+ commands.push(`npx eslint --fix ${existing.join(' ')}`)
14
+ }
15
+ return commands.length ? commands : 'true'
16
+ },
17
+ '*.css': (files) => {
18
+ const existing = files.filter((file) => existsSync(file))
19
+ if (!existing.length) return 'true'
20
+ return `npx stylelint --fix --color --formatter verbose ${existing.join(' ')}`
21
+ },
22
+ '*.mdx': (files) => {
23
+ const existing = files.filter((file) => existsSync(file))
24
+ if (!existing.length) return 'true'
25
+ return `npx eslint --fix ${existing.join(' ')}`
13
26
  },
14
- '*.css': (files) =>
15
- `npx stylelint --fix --color --formatter verbose ${files.join(' ')}`,
16
- '*.mdx': (files) => `npx eslint --fix ${files.join(' ')}`,
17
27
  'package.json': (files) => {
18
28
  // Проверяем, есть ли package.json в корне репозитория
19
29
  const rootPackageJson = resolve(process.cwd(), 'package.json')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itcase/config",
3
- "version": "1.6.81",
3
+ "version": "1.6.82",
4
4
  "author": "ITCase",
5
5
  "description": "ITCase Config",
6
6
  "engines": {
@@ -22,6 +22,8 @@ import { getUpyachkaFrame } from './upyachka.js'
22
22
 
23
23
  const PACKAGE_NAME = '@itcase/config'
24
24
  const CLI_RELATIVE = 'smart-commit/cli.js'
25
+ // Cheap Cursor Models pool — commit messages do not need Auto / frontier models.
26
+ const DEFAULT_MODEL = 'composer-2.5'
25
27
 
26
28
  const MAX_DIFF = 80_000
27
29
  const LOCKFILE_NAMES = new Set([
@@ -54,6 +56,7 @@ const HELP = `smart-commit [--dry-run] [--yes|-y] [--type t] [--model id] [--ver
54
56
  Cursor Agent → commit messages by SCOPES → [y/N] → git commit
55
57
  Run via: npm exec smart-commit (from project with @itcase/config)
56
58
  Needs: agent login (or CURSOR_API_KEY) + commitlint.config.* with SCOPES
59
+ Default model: ${DEFAULT_MODEL} (override with --model / SMART_COMMIT_MODEL).
57
60
 
58
61
  Progress sprite is random (nyan cat, banana, upyachka, or Pac-Man).
59
62
  Force with --cat, --banana, --upyachka, or --pacman. Agent log is below.
@@ -104,7 +107,7 @@ async function main(argv) {
104
107
  (progress) =>
105
108
  askAgent(
106
109
  cwd,
107
- option.model || process.env.SMART_COMMIT_MODEL || '',
110
+ option.model || process.env.SMART_COMMIT_MODEL || DEFAULT_MODEL,
108
111
  prompt,
109
112
  option.verbose,
110
113
  progress.onAgentActivity,
@@ -362,6 +365,7 @@ function listFiles(cwd, option) {
362
365
  ...new Set([
363
366
  ...lines(cwd, ['diff', '--cached', '--name-only', '--diff-filter=ACDMR']),
364
367
  ...lines(cwd, ['diff', '--name-only', '--diff-filter=ACDMR']),
368
+ ...lines(cwd, ['ls-files', '--deleted']),
365
369
  ...lines(cwd, ['ls-files', '--others', '--exclude-standard']),
366
370
  ]),
367
371
  ]
@@ -395,14 +399,15 @@ Scopes (exact): ${buildScopeEnum(scopes).join(', ')}
395
399
  Subject rules:
396
400
  ${promptSubjectRules.map((rule) => `- ${rule}`).join('\n')}
397
401
  Prefer one commit when changes belong to the same feature.
398
- Split by scope. Each listed file in exactly one commit — including new/untracked files (??).
402
+ Split by scope. Each listed file in exactly one commit — including deleted files (D) and new/untracked files (??).
399
403
  Put each lockfile in the same commit as its sibling package.json. Do not make a lockfile-only commit.
400
404
  Do not add Co-authored-by or Made-with trailers.
401
405
  ${type ? `Prefer type "${type}".\n` : ''}Files:
402
406
  ${files
403
407
  .map((file) => {
404
408
  const status =
405
- statusByFile.get(file) || (isUntracked(cwd, file) ? '??' : ' M')
409
+ statusByFile.get(file) ||
410
+ (isUntracked(cwd, file) ? '??' : isOnDisk(cwd, file) ? ' M' : 'D')
406
411
  const lockNote = isLockfile(file) ? ', lockfile' : ''
407
412
  return `- ${file} (${status}${lockNote})`
408
413
  })
@@ -1128,6 +1133,10 @@ async function validateAndNormalizePlans(cwd, plans) {
1128
1133
  }
1129
1134
  }
1130
1135
 
1136
+ function isOnDisk(cwd, file) {
1137
+ return fs.existsSync(path.join(cwd, file))
1138
+ }
1139
+
1131
1140
  function isTracked(cwd, file) {
1132
1141
  return (
1133
1142
  spawnSync('git', ['ls-files', '--error-unmatch', '--', file], {
@@ -1137,16 +1146,25 @@ function isTracked(cwd, file) {
1137
1146
  )
1138
1147
  }
1139
1148
 
1149
+ function isInHead(cwd, file) {
1150
+ return (
1151
+ spawnSync('git', ['cat-file', '-e', `HEAD:${file}`], {
1152
+ cwd: cwd,
1153
+ encoding: 'utf8',
1154
+ }).status === 0
1155
+ )
1156
+ }
1157
+
1140
1158
  function isUntracked(cwd, file) {
1141
- return !isTracked(cwd, file) && fs.existsSync(path.join(cwd, file))
1159
+ return !isTracked(cwd, file) && isOnDisk(cwd, file)
1142
1160
  }
1143
1161
 
1144
1162
  function fileHasChanges(cwd, file) {
1145
- const filePath = path.join(cwd, file)
1163
+ const exists = isOnDisk(cwd, file)
1146
1164
  const tracked = isTracked(cwd, file)
1147
1165
 
1148
- if (!tracked) return fs.existsSync(filePath)
1149
- if (!fs.existsSync(filePath)) return true
1166
+ if (!exists && (tracked || isInHead(cwd, file))) return true
1167
+ if (!tracked) return exists
1150
1168
 
1151
1169
  return (
1152
1170
  spawnSync('git', ['diff', '--quiet', 'HEAD', '--', file], {
@@ -1158,7 +1176,20 @@ function fileHasChanges(cwd, file) {
1158
1176
 
1159
1177
  function stageFiles(cwd, files) {
1160
1178
  if (!files.length) return
1161
- git(cwd, ['add', '-A', '--', ...files])
1179
+
1180
+ const existing = []
1181
+ const removed = []
1182
+ for (const file of files) {
1183
+ if (isOnDisk(cwd, file)) existing.push(file)
1184
+ else removed.push(file)
1185
+ }
1186
+
1187
+ // `git add -A -- deleted/path` warns or misses the deletion when the
1188
+ // parent directory is already gone. `git rm` stages the index removal.
1189
+ if (existing.length) git(cwd, ['add', '-A', '--', ...existing])
1190
+ if (removed.length) {
1191
+ git(cwd, ['rm', '--ignore-unmatch', '-q', '--', ...removed])
1192
+ }
1162
1193
  }
1163
1194
 
1164
1195
  async function confirm(count) {