agent-ecosystem/src/shared/utils/teamMemberName.ts
iliya 2ceed41e00 fix: resolve all CI lint errors and flaky test
- Fix React hooks violations: ref updates during render (useDraftPersistence,
  useChipDraftPersistence, useAttachments), setState in effects across 15+
  components, useCallback self-reference TDZ in useResizableColumns
- Fix TypeScript lint: remove unnecessary type assertions, replace inline
  import() annotations with direct imports, remove unused variables/imports
- Fix SonarJS issues: prefer-regexp-exec, slow-regex in SubagentResolver,
  no-misleading-array-reverse in TeamProvisioningService, use-type-alias
  in ClaudeLogsSection, variable shadowing in ChangeExtractorService
- Fix accessibility: associate labels with controls in filter popovers
- Fix template expression safety: wrap unknown errors with String()
- Fix flaky FileWatcher test: floor instanceCreatedAt to second granularity
  to match filesystem birthtimeMs resolution on Linux
- Replace TODO comments with NOTE where features are intentionally disabled
- Remove unused leadContextByTeam from TeamDetailView store selector

62 files changed across main process, renderer, shared types, and hooks.
All 1646 tests pass, typecheck clean, 0 lint errors.
2026-03-05 21:09:45 +02:00

40 lines
1.3 KiB
TypeScript

export function parseNumericSuffixName(name: string): { base: string; suffix: number } | null {
const trimmed = name.trim();
if (!trimmed) return null;
const match = /^(.+)-(\d+)$/.exec(trimmed);
if (!match?.[1] || !match[2]) return null;
const suffix = Number(match[2]);
if (!Number.isFinite(suffix)) return null;
return { base: match[1], suffix };
}
/**
* Claude CLI auto-suffixes teammate names when a name already exists in config.json
* (e.g. "alice" → "alice-2"). We treat "-2+" as an auto-suffix only when the base
* name also exists among the current set of names.
*
* Important: do NOT treat "-1" as auto-suffix; it's commonly intentional ("dev-1").
*/
export function createCliAutoSuffixNameGuard(
allNames: Iterable<string>
): (name: string) => boolean {
const trimmed: string[] = [];
const seen = new Set<string>();
for (const n of allNames) {
if (typeof n !== 'string') continue;
const t = n.trim();
if (!t) continue;
if (seen.has(t)) continue;
seen.add(t);
trimmed.push(t);
}
const allLower = new Set(trimmed.map((n) => n.toLowerCase()));
return (name: string): boolean => {
const info = parseNumericSuffixName(name);
if (!info) return true;
if (info.suffix < 2) return true;
return !allLower.has(info.base.toLowerCase());
};
}