- 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.
40 lines
1.3 KiB
TypeScript
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());
|
|
};
|
|
}
|