diff --git a/cspell.json b/cspell.json index ee2191853..cbca828dc 100644 --- a/cspell.json +++ b/cspell.json @@ -36,7 +36,8 @@ "Pydantic", "sdkgen", "ttft", "Strausz", "mycompany", "slugified", "simplejwt", "pytest", "Luca", "Clemenza", "Tessio", "Triaging", "Futurama", "mklink", "slnx", "jqlang", - "benleane" + "benleane", + "TELMU", "Automator" ], "dictionaries": ["en_US", "typescript", "node", "npm", "bash"], "allowCompoundWords": true diff --git a/docs/src/content/docs/reference/cli.md b/docs/src/content/docs/reference/cli.md index 724446258..344cb7c7f 100644 --- a/docs/src/content/docs/reference/cli.md +++ b/docs/src/content/docs/reference/cli.md @@ -605,4 +605,4 @@ squad help - [SDK Reference](./sdk.md) — Build squads with TypeScript - [Remote Control Guide](../features/remote-control.md) — Phone/browser access - [Troubleshooting Guide](../scenarios/troubleshooting.md) — Common issues and fixes -- [Getting Started](../getting-started/index.md) — First steps with Squad +- [Getting Started](../get-started/installation.md) — First steps with Squad diff --git a/packages/squad-sdk/src/platform/azure-devops.ts b/packages/squad-sdk/src/platform/azure-devops.ts index 694303a26..e8b31083b 100644 --- a/packages/squad-sdk/src/platform/azure-devops.ts +++ b/packages/squad-sdk/src/platform/azure-devops.ts @@ -56,8 +56,17 @@ function parseJson(raw: string): T { * * Falls back to a minimal default list when the az CLI call fails * (e.g. no auth, no devops extension, offline). + * + * Results are memoized per org/project to avoid repeated az CLI calls in the + * same process (important for tests and any code that calls this in a loop). */ +const _workItemTypeCache = new Map(); + export function getAvailableWorkItemTypes(org: string, project: string): WorkItemTypeInfo[] { + const cacheKey = `${org}\0${project}`; + const cached = _workItemTypeCache.get(cacheKey); + if (cached) return cached; + const orgUrl = `https://dev.azure.com/${org}`; try { // Timeout after 3 s so tests and CI aren't blocked when az CLI is slow @@ -75,18 +84,22 @@ export function getAvailableWorkItemTypes(org: string, project: string): WorkIte isDisabled?: boolean; }>>(raw); - return types.map((t) => ({ + const result = types.map((t) => ({ name: t.name ?? '', description: t.description ?? '', disabled: t.isDisabled === true, })); + _workItemTypeCache.set(cacheKey, result); + return result; } catch { // Fallback: return common defaults so callers aren't empty-handed - return [ + const defaults = [ { name: 'User Story', description: 'Tracks user-facing functionality', disabled: false }, { name: 'Bug', description: 'Tracks a defect', disabled: false }, { name: 'Task', description: 'Tracks a unit of work', disabled: false }, ]; + _workItemTypeCache.set(cacheKey, defaults); + return defaults; } }