[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71G1XBH0X1VCTR7269MDGSY":3,"skill-same:01M71G1XBH0X1VCTR7269MDGSY":60},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":32,"linkOnly":33,"markdownContent":34,"markdownBody":35,"frontMatter":36,"refName":38,"commitSha":39,"skillDirectory":40,"forksCount":14,"repositoryHtmlUrl":41,"repositoryHomepageUrl":6,"ownerHtmlUrl":42,"ownerBlogUrl":43,"ownerDescription":44,"updatedAt":45,"install":46,"resources":59},"01M71G1XBH0X1VCTR7269MDGSY","typescript-code-standards",null,"TypeScript code standards enforced across all skills. Loaded by other skills, not invoked directly.","outcomeeng\u002Fplugins","outcomeeng","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F257335685?v=4","01M71FRVRF36XYR3HRSCGFHK2R","Outcome Engineering","01M71FRVS4BZG17DWANFJ7EPNY",1,"MIT","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-code-standards\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[24,25,26,27,28,29,30,31],"standards","typescript code standards","code standards","code","typescript","false","read","across","2026-11-10 10:12:30",false,"---\nname: typescript-code-standards\nuser-invocable: false\ndescription: >-\n  TypeScript code standards enforced across all skills. Loaded by other skills, not invoked directly.\nallowed-tools: Read\n---\n\n\u003Cobjective>\nThe TypeScript code standards every TypeScript skill enforces.\n\u003C\u002Fobjective>\n\n\u003Csuccess_criteria>\nCode follows these standards when tsc strict mode and eslint pass, every manual-review criterion in the rejection summary has been evaluated, and no source-ownership, error-context, security, import-hygiene, async, or code-hygiene rule in this reference remains violated.\n\u003C\u002Fsuccess_criteria>\n\n\u003Creference_note>\nThis is a reference skill. Composing TypeScript skills load these standards explicitly before writing, testing, or auditing. It is not a standalone workflow.\n\nThese standards apply to ALL TypeScript code, including tests and scripts. `\u002Ftypescript-test-standards` adds stricter rules for test code.\n\u003C\u002Freference_note>\n\n\u003Crepo_local_overlay>\nWhen another skill loads this reference inside a repository, it must also check for `spx\u002Flocal\u002Ftypescript.md` at the repository root. Read that file after this reference if it exists and apply it as repo-local routing to the product's governing specs and decisions. A local overlay supplements skill behavior; it does not declare product truth.\n\u003C\u002Frepo_local_overlay>\n\n\u003Ctype_safety>\n\nTypeScript strict mode enforces type safety. All violations are caught by tsc at compile time.\n\n```typescript\n\u002F\u002F ❌ REJECTED: Unqualified any without justification\nfunction process(data: any): any {\n  return data;\n}\n\n\u002F\u002F ✅ REQUIRED: Use concrete types\nfunction process(data: Record\u003Cstring, string>): ProcessResult {\n  return new ProcessResult(data);\n}\n\n\u002F\u002F ❌ REJECTED: @ts-ignore without explanation\n\u002F\u002F @ts-ignore\nconst result = someFunction();\n\n\u002F\u002F ✅ REQUIRED: Use @ts-expect-error with explanation\n\u002F\u002F @ts-expect-error - third-party library missing type definitions\nconst result = someFunction();\n\n\u002F\u002F ❌ REJECTED: Type assertion without narrowing\nfunction getValue(x: string | number): string {\n  return x as string; \u002F\u002F Unsafe\n}\n\n\u002F\u002F ✅ REQUIRED: Type guard before assertion\nfunction getValue(x: string | number): string {\n  if (typeof x === \"string\") {\n    return x;\n  }\n  return x.toString();\n}\n\n\u002F\u002F ❌ REJECTED: Unconstrained generic\nfunction process\u003CT>(value: T): T {\n  return value.toString(); \u002F\u002F Error: toString might not exist\n}\n\n\u002F\u002F ✅ REQUIRED: Properly constrained generic\nfunction process\u003CT extends { toString(): string }>(value: T): string {\n  return value.toString();\n}\n\n\u002F\u002F ❌ REJECTED: Union type used without narrowing\nfunction handle(value: string | number): void {\n  console.log(value.toUpperCase()); \u002F\u002F Error: number has no toUpperCase\n}\n\n\u002F\u002F ✅ REQUIRED: Narrow union before use\nfunction handle(value: string | number): void {\n  if (typeof value === \"string\") {\n    console.log(value.toUpperCase());\n  } else {\n    console.log(value.toString());\n  }\n}\n```\n\n**tsconfig.json strict mode settings:**\n\n```json\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"strictNullChecks\": true,\n    \"strictFunctionTypes\": true,\n    \"strictBindCallApply\": true,\n    \"strictPropertyInitialization\": true,\n    \"noImplicitThis\": true,\n    \"alwaysStrict\": true\n  }\n}\n```\n\n**ESLint rules enforced:**\n\n| Rule                                          | What it catches                            |\n| --------------------------------------------- | ------------------------------------------ |\n| @typescript-eslint\u002Fno-explicit-any            | Unqualified `any` usage                    |\n| @typescript-eslint\u002Fban-ts-comment             | `@ts-ignore` instead of `@ts-expect-error` |\n| @typescript-eslint\u002Fconsistent-type-assertions | Type assertions without justification      |\n\n\u003C\u002Ftype_safety>\n\n\u003Cproduction_constants>\n\nProduction code must name domain-significant literals and export reusable constants from the module that owns them.\n\n```typescript\n\u002F\u002F ✅ REQUIRED: Named reusable constants at module level\nexport const MIN_SCORE = 0;\nexport const MAX_SCORE = 100;\nexport const DEFAULT_SCORE = 85;\n\nexport function validateScore(score: number): boolean {\n  return score >= MIN_SCORE && score \u003C= MAX_SCORE;\n}\n\nvalidateScore(DEFAULT_SCORE);\n\n\u002F\u002F ❌ REJECTED: Inline domain literals\nexport function validateScoreBad(score: number): boolean {\n  return score >= 0 && score \u003C= 100;\n}\n```\n\n**Why named constants matter in production code:**\n\n- Exported constants become the canonical source for tests and other modules\n- Thresholds, command flags, and status tokens stay synchronized\n- Code communicates domain meaning instead of unexplained literals\n\n**ESLint rules enforced:**\n\n| Rule                                | What it catches              |\n| ----------------------------------- | ---------------------------- |\n| no-magic-numbers                    | Literal numbers in code      |\n| @typescript-eslint\u002Fno-magic-numbers | TypeScript-specific literals |\n\n**Rule exemptions:** ESLint already exempts common idiomatic values: `0`, `1`, `-1` in array indexes, and enum values. These do not need constants.\n\n```typescript\n\u002F\u002F ✅ OK: Idiomatic values are exempt\nif (results.length === 0) {\n  return;\n}\nif (count === 1) {\n  doThing();\n}\nconst first = items[0];\nconst last = items[items.length - 1];\n```\n\nFor test values, fixture placement, and inline diagnostics, follow `\u002Ftypescript-test-standards`.\n\n\u003C\u002Fproduction_constants>\n\n\u003Csource_of_truth_registries>\n\nClosed sets must have one runtime source of truth. Use `as const` registries or tuples, then derive unions and schemas from that declaration instead of duplicating string unions by hand.\n\n```typescript\nexport const VERDICT_STATUSES = {\n  APPROVED: \"approved\",\n  REJECTED: \"rejected\",\n} as const;\n\nexport type VerdictStatus = (typeof VERDICT_STATUSES)[keyof typeof VERDICT_STATUSES];\n\nexport const verdictStatusSchema = z.enum(\n  Object.values(VERDICT_STATUSES) as [string, ...string[]],\n);\n```\n\nThe tuple cast is only for `z.enum`'s non-empty tuple signature. Keep the runtime registry as the source of truth; do not add a separate string union to satisfy the schema API.\n\nHand-maintained unions that can drift from the runtime registry are rejected.\n\n\u003C\u002Fsource_of_truth_registries>\n\n\u003Cscript_boundaries>\n\nChecked-in entrypoints in `scripts\u002F` are boundary code.\n\nScripts may:\n\n- parse arguments\n- read and normalize environment variables at the boundary\n- format terminal output\n- dispatch to one orchestrator module or a small dispatcher\n- map the final result to a process exit code at the outermost `main`\n\nScripts must NOT become the home of business logic, workflow policy, or transforms that deserve their own specification.\n\n**Canonical argument parsing**\n\nEvery repository must converge on one argument parsing library. Script entrypoints must use the repo's canonical parser instead of manual parsing with `process.argv`, ad hoc loops, or mixed parser libraries.\n\n```typescript\n\u002F\u002F ❌ REJECTED: manual parsing\nconst args = process.argv.slice(2);\nconst dryRun = args.includes(\"--dry-run\");\nconst target = args[args.indexOf(\"--target\") + 1];\n\n\u002F\u002F ✅ REQUIRED: repository-canonical parser\nconst options = parseArgsWithRepoStandard(process.argv.slice(2));\n```\n\n**Boundary flexibility**\n\n- Relative imports are acceptable inside `scripts\u002F`\n- Boundary env parsing is acceptable inside `scripts\u002F`\n- `console.*` is acceptable inside `scripts\u002F` for terminal output\n\nImported orchestrator modules should still follow the normal codebase standards for configuration, logging, and specification coverage.\n\n\u003C\u002Fscript_boundaries>\n\n\u003Cerror_handling>\n\n```typescript\n\u002F\u002F ❌ REJECTED: Empty catch block (no-empty)\ntry {\n  process();\n} catch (err) {\n  \u002F\u002F Silent failure\n}\n\n\u002F\u002F ❌ REJECTED: Unhandled promise rejection (@typescript-eslint\u002Fno-floating-promises)\nasync function bad(): Promise\u003Cvoid> {\n  fetchData(); \u002F\u002F Promise not awaited or handled\n}\n\n\u002F\u002F ✅ REQUIRED: Catch specific errors with context\ntry {\n  process();\n} catch (err) {\n  if (err instanceof ValidationError) {\n    log.error(\"Invalid input:\", err.message);\n    throw err;\n  }\n  throw new ProcessingError(\"Unexpected error during processing\", { cause: err });\n}\n\n\u002F\u002F ✅ REQUIRED: Handle or await promises\nasync function good(): Promise\u003Cvoid> {\n  await fetchData();\n  \u002F\u002F or\n  fetchData().catch(err => handleError(err));\n}\n\n\u002F\u002F ✅ REQUIRED: Custom error classes for domain errors\nclass ValidationError extends Error {\n  constructor(\n    message: string,\n    public readonly field: string,\n    public readonly value: unknown,\n  ) {\n    super(message);\n    this.name = \"ValidationError\";\n  }\n}\n\n\u002F\u002F ✅ REQUIRED: Error messages include context\nthrow new ValidationError(\n  `Score must be between ${MIN_SCORE} and ${MAX_SCORE}, got ${score}`,\n  \"score\",\n  score,\n);\n```\n\n**ESLint rules enforced:**\n\n| Rule                                    | What it catches              |\n| --------------------------------------- | ---------------------------- |\n| no-empty                                | Empty catch blocks           |\n| @typescript-eslint\u002Fno-floating-promises | Unhandled promise rejections |\n\n\u003C\u002Ferror_handling>\n\n\u003Csecurity>\n\n```typescript\n\u002F\u002F ❌ REJECTED: Hardcoded secrets\nconst API_KEY = \"sk-1234567890\";\nconst password = \"hunter2\";\n\n\u002F\u002F ❌ REJECTED: eval (no-eval)\nconst result = eval(userInput);\n\n\u002F\u002F ❌ REJECTED: new Function (no-new-func)\nconst fn = new Function(\"x\", \"return x + 1\");\n\n\u002F\u002F ❌ REJECTED: child_process.exec with untrusted input\nimport { exec } from \"child_process\";\nexec(`grep ${userInput} file.txt`); \u002F\u002F Shell injection\n\n\u002F\u002F ✅ REQUIRED: Use execFile with argument array\nimport { execFile } from \"child_process\";\nexecFile(\"grep\", [userInput, \"file.txt\"]);\n\n\u002F\u002F ✅ REQUIRED: Load secrets from environment\nconst apiKey = process.env.API_KEY;\nif (!apiKey) {\n  throw new Error(\"API_KEY environment variable required\");\n}\n```\n\nContext matters for security rules—a CLI tool invoked by the user has different trust boundaries than a web service. Reading env vars in `scripts\u002F` is acceptable boundary behavior; imported modules should prefer typed config once past that boundary. See `\u002Faudit-typescript-code` for false positive handling.\n\n**ESLint rules enforced:**\n\n| Rule        | What it catches                             |\n| ----------- | ------------------------------------------- |\n| no-eval     | Use of `eval()`                             |\n| no-new-func | Use of `new Function()`                     |\n| (manual)    | Hardcoded secrets                           |\n| (manual)    | `child_process.exec()` with untrusted input |\n\n\u003C\u002Fsecurity>\n\n\u003Ccode_hygiene>\n\n```typescript\n\u002F\u002F ❌ REJECTED: Unused variables (@typescript-eslint\u002Fno-unused-vars)\nimport { processData } from \".\u002Futils\"; \u002F\u002F Never used\nconst unusedVariable = 42;\n\n\u002F\u002F ❌ REJECTED: Dead code or commented-out code\n\u002F\u002F function oldImplementation() {\n\u002F\u002F   return 42;\n\u002F\u002F }\n\n\u002F\u002F ❌ REJECTED: console.log in application\u002Fruntime modules (no-console)\nfunction process(data: string): void {\n  console.log(\"Processing:\", data);\n  \u002F\u002F ...\n}\n\n\u002F\u002F ✅ REQUIRED: Use proper logging\nimport { logger } from \".\u002Flogger\";\n\nfunction process(data: string): void {\n  logger.debug(\"Processing:\", data);\n  \u002F\u002F ...\n}\n\n\u002F\u002F ✅ REQUIRED: Remove unused imports and variables\nimport { processData } from \".\u002Futils\";\n\nfunction handle(input: string): void {\n  processData(input);\n}\n```\n\n**ESLint rules enforced:**\n\n| Rule                              | What it catches                  |\n| --------------------------------- | -------------------------------- |\n| @typescript-eslint\u002Fno-unused-vars | Unused variables and imports     |\n| no-console                        | `console.log` in runtime modules |\n| (manual)                          | Dead code or commented-out code  |\n\n`scripts\u002F` entrypoints are the exception: console output is acceptable there because they are terminal boundaries. Imported modules should still use the repo's normal logging pattern.\n\n\u003C\u002Fcode_hygiene>\n\n\u003Cimport_hygiene>\n\n**Depth Rules**\n\n| Depth     | Syntax                         | Verdict | Rationale                                 |\n| --------- | ------------------------------ | ------- | ----------------------------------------- |\n| Same dir  | `import { x } from '.\u002Fy'`      | OK      | Module-internal, same package             |\n| 1 level   | `import { x } from '..\u002Fy'`     | REVIEW  | Is this truly module-internal?            |\n| 2+ levels | `import { x } from '..\u002F..\u002F..'` | REJECT  | Use path alias — crosses package boundary |\n\n**Module-Internal vs. Infrastructure**\n\n**Module-internal files** live in the same package and move together. Relative imports are acceptable:\n\n```typescript\n\u002F\u002F ✅ ACCEPTABLE: Same package, files move together\nimport { Position } from \".\u002Fposition\";\nimport { tokenize } from \".\u002Ftokens\";\n```\n\n**Stable shared modules** are project-owned code that does not move when a feature moves. Path aliases are required, using the aliases the repository declares:\n\n```typescript\n\u002F\u002F ❌ REJECTED: Deep relative import to stable shared code\nimport { getRepoRoot } from \"..\u002F..\u002F..\u002F..\u002F..\u002F..\u002Fsrc\u002Fshared\u002Fpaths\";\n\n\u002F\u002F ✅ REQUIRED: Repository-declared path alias\nimport { getRepoRoot } from \"@shared\u002Fpaths\";\n```\n\n`scripts\u002F` entrypoints are the exception: relative imports are acceptable there because they are boundary modules and often need to avoid circular dependency tangles. Imported orchestrator modules should still follow the normal alias rules for stable infrastructure.\n\n**Anti-Patterns**\n\n```typescript\n\u002F\u002F ❌ REJECTED: Deep relative imports\nimport { helper } from \"..\u002F..\u002F..\u002F..\u002Flib\u002Futils\";\n\n\u002F\u002F ❌ REJECTED: Assuming working directory\nimport { helper } from \"lib\u002Futils\"; \u002F\u002F Only works if CWD is product root\n```\n\n**Required Product Setup**\n\n**1. Configure `tsconfig.json` with path aliases:**\n\n```json\n{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@feature\u002F*\": [\"\u003Cfeature-root>\u002F*\"],\n      \"@shared\u002F*\": [\"\u003Cshared-root>\u002F*\"]\n    }\n  }\n}\n```\n\n**2. Common path alias patterns:**\n\n| Alias        | Maps to            | Purpose                  |\n| ------------ | ------------------ | ------------------------ |\n| `@feature\u002F*` | `\u003Cfeature-root>\u002F*` | Feature-owned modules    |\n| `@shared\u002F*`  | `\u003Cshared-root>\u002F*`  | Stable shared modules    |\n| `@testing\u002F*` | `\u003Ctesting-root>\u002F*` | Test infrastructure root |\n\n**3. Usage with path aliases:**\n\n```typescript\n\u002F\u002F ✅ REQUIRED: Stable infrastructure via alias\nimport { processData } from \"@feature\u002Fprocessing\";\nimport { Logger } from \"@shared\u002Flogger\";\n\n\u002F\u002F ✅ ACCEPTABLE: Module-internal relative\nimport { Position } from \".\u002Fposition\";\nimport { tokenize } from \".\u002Ftokens\";\n```\n\n\u003C\u002Fimport_hygiene>\n\n\u003Crejection_criteria_summary>\n\n| Issue                            | Example                                     | Rule\u002FTool                                     |\n| -------------------------------- | ------------------------------------------- | --------------------------------------------- |\n| Unqualified `any`                | `function f(x: any): any`                   | @typescript-eslint\u002Fno-explicit-any            |\n| `@ts-ignore` without reason      | `\u002F\u002F @ts-ignore`                             | @typescript-eslint\u002Fban-ts-comment             |\n| Type assertion without narrowing | `return x as string`                        | @typescript-eslint\u002Fconsistent-type-assertions |\n| Unconstrained generic            | `function f\u003CT>(x: T)`                       | tsc strict mode                               |\n| Union without narrowing          | `value.toUpperCase()` on `string \\| number` | tsc strict mode                               |\n| Unnamed domain literals          | `return score >= 0 && score \u003C= 100`         | @typescript-eslint\u002Fno-magic-numbers           |\n| Empty catch block                | `catch (err) {}`                            | no-empty                                      |\n| Unhandled promise                | `fetchData();` without await                | @typescript-eslint\u002Fno-floating-promises       |\n| Missing error context            | `throw new Error('failed')`                 | manual review                                 |\n| Hardcoded secrets                | `const API_KEY = \"sk-...\"`                  | manual review                                 |\n| `eval()` usage                   | `eval(userInput)`                           | no-eval                                       |\n| `new Function()` usage           | `new Function('x', 'return x')`             | no-new-func                                   |\n| `exec()` with untrusted input    | `exec(\\`grep ${input} file\\`)`              | manual review                                 |\n| Unused variables\u002Fimports         | `import { x } from 'y'; \u002F\u002F never used`      | @typescript-eslint\u002Fno-unused-vars             |\n| `console.log` in production      | `console.log('debug')`                      | no-console                                    |\n| Dead\u002Fcommented code              | `\u002F\u002F function old() { ... }`                 | manual review                                 |\n| Deep relative imports            | `from '..\u002F..\u002F..\u002Flib'`                       | manual review                                 |\n\n\u003C\u002Frejection_criteria_summary>\n","\u003Cobjective>\nThe TypeScript code standards every TypeScript skill enforces.\n\u003C\u002Fobjective>\n\n\u003Csuccess_criteria>\nCode follows these standards when tsc strict mode and eslint pass, every manual-review criterion in the rejection summary has been evaluated, and no source-ownership, error-context, security, import-hygiene, async, or code-hygiene rule in this reference remains violated.\n\u003C\u002Fsuccess_criteria>\n\n\u003Creference_note>\nThis is a reference skill. Composing TypeScript skills load these standards explicitly before writing, testing, or auditing. It is not a standalone workflow.\n\nThese standards apply to ALL TypeScript code, including tests and scripts. `\u002Ftypescript-test-standards` adds stricter rules for test code.\n\u003C\u002Freference_note>\n\n\u003Crepo_local_overlay>\nWhen another skill loads this reference inside a repository, it must also check for `spx\u002Flocal\u002Ftypescript.md` at the repository root. Read that file after this reference if it exists and apply it as repo-local routing to the product's governing specs and decisions. A local overlay supplements skill behavior; it does not declare product truth.\n\u003C\u002Frepo_local_overlay>\n\n\u003Ctype_safety>\n\nTypeScript strict mode enforces type safety. All violations are caught by tsc at compile time.\n\n```typescript\n\u002F\u002F ❌ REJECTED: Unqualified any without justification\nfunction process(data: any): any {\n  return data;\n}\n\n\u002F\u002F ✅ REQUIRED: Use concrete types\nfunction process(data: Record\u003Cstring, string>): ProcessResult {\n  return new ProcessResult(data);\n}\n\n\u002F\u002F ❌ REJECTED: @ts-ignore without explanation\n\u002F\u002F @ts-ignore\nconst result = someFunction();\n\n\u002F\u002F ✅ REQUIRED: Use @ts-expect-error with explanation\n\u002F\u002F @ts-expect-error - third-party library missing type definitions\nconst result = someFunction();\n\n\u002F\u002F ❌ REJECTED: Type assertion without narrowing\nfunction getValue(x: string | number): string {\n  return x as string; \u002F\u002F Unsafe\n}\n\n\u002F\u002F ✅ REQUIRED: Type guard before assertion\nfunction getValue(x: string | number): string {\n  if (typeof x === \"string\") {\n    return x;\n  }\n  return x.toString();\n}\n\n\u002F\u002F ❌ REJECTED: Unconstrained generic\nfunction process\u003CT>(value: T): T {\n  return value.toString(); \u002F\u002F Error: toString might not exist\n}\n\n\u002F\u002F ✅ REQUIRED: Properly constrained generic\nfunction process\u003CT extends { toString(): string }>(value: T): string {\n  return value.toString();\n}\n\n\u002F\u002F ❌ REJECTED: Union type used without narrowing\nfunction handle(value: string | number): void {\n  console.log(value.toUpperCase()); \u002F\u002F Error: number has no toUpperCase\n}\n\n\u002F\u002F ✅ REQUIRED: Narrow union before use\nfunction handle(value: string | number): void {\n  if (typeof value === \"string\") {\n    console.log(value.toUpperCase());\n  } else {\n    console.log(value.toString());\n  }\n}\n```\n\n**tsconfig.json strict mode settings:**\n\n```json\n{\n  \"compilerOptions\": {\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"strictNullChecks\": true,\n    \"strictFunctionTypes\": true,\n    \"strictBindCallApply\": true,\n    \"strictPropertyInitialization\": true,\n    \"noImplicitThis\": true,\n    \"alwaysStrict\": true\n  }\n}\n```\n\n**ESLint rules enforced:**\n\n| Rule                                          | What it catches                            |\n| --------------------------------------------- | ------------------------------------------ |\n| @typescript-eslint\u002Fno-explicit-any            | Unqualified `any` usage                    |\n| @typescript-eslint\u002Fban-ts-comment             | `@ts-ignore` instead of `@ts-expect-error` |\n| @typescript-eslint\u002Fconsistent-type-assertions | Type assertions without justification      |\n\n\u003C\u002Ftype_safety>\n\n\u003Cproduction_constants>\n\nProduction code must name domain-significant literals and export reusable constants from the module that owns them.\n\n```typescript\n\u002F\u002F ✅ REQUIRED: Named reusable constants at module level\nexport const MIN_SCORE = 0;\nexport const MAX_SCORE = 100;\nexport const DEFAULT_SCORE = 85;\n\nexport function validateScore(score: number): boolean {\n  return score >= MIN_SCORE && score \u003C= MAX_SCORE;\n}\n\nvalidateScore(DEFAULT_SCORE);\n\n\u002F\u002F ❌ REJECTED: Inline domain literals\nexport function validateScoreBad(score: number): boolean {\n  return score >= 0 && score \u003C= 100;\n}\n```\n\n**Why named constants matter in production code:**\n\n- Exported constants become the canonical source for tests and other modules\n- Thresholds, command flags, and status tokens stay synchronized\n- Code communicates domain meaning instead of unexplained literals\n\n**ESLint rules enforced:**\n\n| Rule                                | What it catches              |\n| ----------------------------------- | ---------------------------- |\n| no-magic-numbers                    | Literal numbers in code      |\n| @typescript-eslint\u002Fno-magic-numbers | TypeScript-specific literals |\n\n**Rule exemptions:** ESLint already exempts common idiomatic values: `0`, `1`, `-1` in array indexes, and enum values. These do not need constants.\n\n```typescript\n\u002F\u002F ✅ OK: Idiomatic values are exempt\nif (results.length === 0) {\n  return;\n}\nif (count === 1) {\n  doThing();\n}\nconst first = items[0];\nconst last = items[items.length - 1];\n```\n\nFor test values, fixture placement, and inline diagnostics, follow `\u002Ftypescript-test-standards`.\n\n\u003C\u002Fproduction_constants>\n\n\u003Csource_of_truth_registries>\n\nClosed sets must have one runtime source of truth. Use `as const` registries or tuples, then derive unions and schemas from that declaration instead of duplicating string unions by hand.\n\n```typescript\nexport const VERDICT_STATUSES = {\n  APPROVED: \"approved\",\n  REJECTED: \"rejected\",\n} as const;\n\nexport type VerdictStatus = (typeof VERDICT_STATUSES)[keyof typeof VERDICT_STATUSES];\n\nexport const verdictStatusSchema = z.enum(\n  Object.values(VERDICT_STATUSES) as [string, ...string[]],\n);\n```\n\nThe tuple cast is only for `z.enum`'s non-empty tuple signature. Keep the runtime registry as the source of truth; do not add a separate string union to satisfy the schema API.\n\nHand-maintained unions that can drift from the runtime registry are rejected.\n\n\u003C\u002Fsource_of_truth_registries>\n\n\u003Cscript_boundaries>\n\nChecked-in entrypoints in `scripts\u002F` are boundary code.\n\nScripts may:\n\n- parse arguments\n- read and normalize environment variables at the boundary\n- format terminal output\n- dispatch to one orchestrator module or a small dispatcher\n- map the final result to a process exit code at the outermost `main`\n\nScripts must NOT become the home of business logic, workflow policy, or transforms that deserve their own specification.\n\n**Canonical argument parsing**\n\nEvery repository must converge on one argument parsing library. Script entrypoints must use the repo's canonical parser instead of manual parsing with `process.argv`, ad hoc loops, or mixed parser libraries.\n\n```typescript\n\u002F\u002F ❌ REJECTED: manual parsing\nconst args = process.argv.slice(2);\nconst dryRun = args.includes(\"--dry-run\");\nconst target = args[args.indexOf(\"--target\") + 1];\n\n\u002F\u002F ✅ REQUIRED: repository-canonical parser\nconst options = parseArgsWithRepoStandard(process.argv.slice(2));\n```\n\n**Boundary flexibility**\n\n- Relative imports are acceptable inside `scripts\u002F`\n- Boundary env parsing is acceptable inside `scripts\u002F`\n- `console.*` is acceptable inside `scripts\u002F` for terminal output\n\nImported orchestrator modules should still follow the normal codebase standards for configuration, logging, and specification coverage.\n\n\u003C\u002Fscript_boundaries>\n\n\u003Cerror_handling>\n\n```typescript\n\u002F\u002F ❌ REJECTED: Empty catch block (no-empty)\ntry {\n  process();\n} catch (err) {\n  \u002F\u002F Silent failure\n}\n\n\u002F\u002F ❌ REJECTED: Unhandled promise rejection (@typescript-eslint\u002Fno-floating-promises)\nasync function bad(): Promise\u003Cvoid> {\n  fetchData(); \u002F\u002F Promise not awaited or handled\n}\n\n\u002F\u002F ✅ REQUIRED: Catch specific errors with context\ntry {\n  process();\n} catch (err) {\n  if (err instanceof ValidationError) {\n    log.error(\"Invalid input:\", err.message);\n    throw err;\n  }\n  throw new ProcessingError(\"Unexpected error during processing\", { cause: err });\n}\n\n\u002F\u002F ✅ REQUIRED: Handle or await promises\nasync function good(): Promise\u003Cvoid> {\n  await fetchData();\n  \u002F\u002F or\n  fetchData().catch(err => handleError(err));\n}\n\n\u002F\u002F ✅ REQUIRED: Custom error classes for domain errors\nclass ValidationError extends Error {\n  constructor(\n    message: string,\n    public readonly field: string,\n    public readonly value: unknown,\n  ) {\n    super(message);\n    this.name = \"ValidationError\";\n  }\n}\n\n\u002F\u002F ✅ REQUIRED: Error messages include context\nthrow new ValidationError(\n  `Score must be between ${MIN_SCORE} and ${MAX_SCORE}, got ${score}`,\n  \"score\",\n  score,\n);\n```\n\n**ESLint rules enforced:**\n\n| Rule                                    | What it catches              |\n| --------------------------------------- | ---------------------------- |\n| no-empty                                | Empty catch blocks           |\n| @typescript-eslint\u002Fno-floating-promises | Unhandled promise rejections |\n\n\u003C\u002Ferror_handling>\n\n\u003Csecurity>\n\n```typescript\n\u002F\u002F ❌ REJECTED: Hardcoded secrets\nconst API_KEY = \"sk-1234567890\";\nconst password = \"hunter2\";\n\n\u002F\u002F ❌ REJECTED: eval (no-eval)\nconst result = eval(userInput);\n\n\u002F\u002F ❌ REJECTED: new Function (no-new-func)\nconst fn = new Function(\"x\", \"return x + 1\");\n\n\u002F\u002F ❌ REJECTED: child_process.exec with untrusted input\nimport { exec } from \"child_process\";\nexec(`grep ${userInput} file.txt`); \u002F\u002F Shell injection\n\n\u002F\u002F ✅ REQUIRED: Use execFile with argument array\nimport { execFile } from \"child_process\";\nexecFile(\"grep\", [userInput, \"file.txt\"]);\n\n\u002F\u002F ✅ REQUIRED: Load secrets from environment\nconst apiKey = process.env.API_KEY;\nif (!apiKey) {\n  throw new Error(\"API_KEY environment variable required\");\n}\n```\n\nContext matters for security rules—a CLI tool invoked by the user has different trust boundaries than a web service. Reading env vars in `scripts\u002F` is acceptable boundary behavior; imported modules should prefer typed config once past that boundary. See `\u002Faudit-typescript-code` for false positive handling.\n\n**ESLint rules enforced:**\n\n| Rule        | What it catches                             |\n| ----------- | ------------------------------------------- |\n| no-eval     | Use of `eval()`                             |\n| no-new-func | Use of `new Function()`                     |\n| (manual)    | Hardcoded secrets                           |\n| (manual)    | `child_process.exec()` with untrusted input |\n\n\u003C\u002Fsecurity>\n\n\u003Ccode_hygiene>\n\n```typescript\n\u002F\u002F ❌ REJECTED: Unused variables (@typescript-eslint\u002Fno-unused-vars)\nimport { processData } from \".\u002Futils\"; \u002F\u002F Never used\nconst unusedVariable = 42;\n\n\u002F\u002F ❌ REJECTED: Dead code or commented-out code\n\u002F\u002F function oldImplementation() {\n\u002F\u002F   return 42;\n\u002F\u002F }\n\n\u002F\u002F ❌ REJECTED: console.log in application\u002Fruntime modules (no-console)\nfunction process(data: string): void {\n  console.log(\"Processing:\", data);\n  \u002F\u002F ...\n}\n\n\u002F\u002F ✅ REQUIRED: Use proper logging\nimport { logger } from \".\u002Flogger\";\n\nfunction process(data: string): void {\n  logger.debug(\"Processing:\", data);\n  \u002F\u002F ...\n}\n\n\u002F\u002F ✅ REQUIRED: Remove unused imports and variables\nimport { processData } from \".\u002Futils\";\n\nfunction handle(input: string): void {\n  processData(input);\n}\n```\n\n**ESLint rules enforced:**\n\n| Rule                              | What it catches                  |\n| --------------------------------- | -------------------------------- |\n| @typescript-eslint\u002Fno-unused-vars | Unused variables and imports     |\n| no-console                        | `console.log` in runtime modules |\n| (manual)                          | Dead code or commented-out code  |\n\n`scripts\u002F` entrypoints are the exception: console output is acceptable there because they are terminal boundaries. Imported modules should still use the repo's normal logging pattern.\n\n\u003C\u002Fcode_hygiene>\n\n\u003Cimport_hygiene>\n\n**Depth Rules**\n\n| Depth     | Syntax                         | Verdict | Rationale                                 |\n| --------- | ------------------------------ | ------- | ----------------------------------------- |\n| Same dir  | `import { x } from '.\u002Fy'`      | OK      | Module-internal, same package             |\n| 1 level   | `import { x } from '..\u002Fy'`     | REVIEW  | Is this truly module-internal?            |\n| 2+ levels | `import { x } from '..\u002F..\u002F..'` | REJECT  | Use path alias — crosses package boundary |\n\n**Module-Internal vs. Infrastructure**\n\n**Module-internal files** live in the same package and move together. Relative imports are acceptable:\n\n```typescript\n\u002F\u002F ✅ ACCEPTABLE: Same package, files move together\nimport { Position } from \".\u002Fposition\";\nimport { tokenize } from \".\u002Ftokens\";\n```\n\n**Stable shared modules** are project-owned code that does not move when a feature moves. Path aliases are required, using the aliases the repository declares:\n\n```typescript\n\u002F\u002F ❌ REJECTED: Deep relative import to stable shared code\nimport { getRepoRoot } from \"..\u002F..\u002F..\u002F..\u002F..\u002F..\u002Fsrc\u002Fshared\u002Fpaths\";\n\n\u002F\u002F ✅ REQUIRED: Repository-declared path alias\nimport { getRepoRoot } from \"@shared\u002Fpaths\";\n```\n\n`scripts\u002F` entrypoints are the exception: relative imports are acceptable there because they are boundary modules and often need to avoid circular dependency tangles. Imported orchestrator modules should still follow the normal alias rules for stable infrastructure.\n\n**Anti-Patterns**\n\n```typescript\n\u002F\u002F ❌ REJECTED: Deep relative imports\nimport { helper } from \"..\u002F..\u002F..\u002F..\u002Flib\u002Futils\";\n\n\u002F\u002F ❌ REJECTED: Assuming working directory\nimport { helper } from \"lib\u002Futils\"; \u002F\u002F Only works if CWD is product root\n```\n\n**Required Product Setup**\n\n**1. Configure `tsconfig.json` with path aliases:**\n\n```json\n{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@feature\u002F*\": [\"\u003Cfeature-root>\u002F*\"],\n      \"@shared\u002F*\": [\"\u003Cshared-root>\u002F*\"]\n    }\n  }\n}\n```\n\n**2. Common path alias patterns:**\n\n| Alias        | Maps to            | Purpose                  |\n| ------------ | ------------------ | ------------------------ |\n| `@feature\u002F*` | `\u003Cfeature-root>\u002F*` | Feature-owned modules    |\n| `@shared\u002F*`  | `\u003Cshared-root>\u002F*`  | Stable shared modules    |\n| `@testing\u002F*` | `\u003Ctesting-root>\u002F*` | Test infrastructure root |\n\n**3. Usage with path aliases:**\n\n```typescript\n\u002F\u002F ✅ REQUIRED: Stable infrastructure via alias\nimport { processData } from \"@feature\u002Fprocessing\";\nimport { Logger } from \"@shared\u002Flogger\";\n\n\u002F\u002F ✅ ACCEPTABLE: Module-internal relative\nimport { Position } from \".\u002Fposition\";\nimport { tokenize } from \".\u002Ftokens\";\n```\n\n\u003C\u002Fimport_hygiene>\n\n\u003Crejection_criteria_summary>\n\n| Issue                            | Example                                     | Rule\u002FTool                                     |\n| -------------------------------- | ------------------------------------------- | --------------------------------------------- |\n| Unqualified `any`                | `function f(x: any): any`                   | @typescript-eslint\u002Fno-explicit-any            |\n| `@ts-ignore` without reason      | `\u002F\u002F @ts-ignore`                             | @typescript-eslint\u002Fban-ts-comment             |\n| Type assertion without narrowing | `return x as string`                        | @typescript-eslint\u002Fconsistent-type-assertions |\n| Unconstrained generic            | `function f\u003CT>(x: T)`                       | tsc strict mode                               |\n| Union without narrowing          | `value.toUpperCase()` on `string \\| number` | tsc strict mode                               |\n| Unnamed domain literals          | `return score >= 0 && score \u003C= 100`         | @typescript-eslint\u002Fno-magic-numbers           |\n| Empty catch block                | `catch (err) {}`                            | no-empty                                      |\n| Unhandled promise                | `fetchData();` without await                | @typescript-eslint\u002Fno-floating-promises       |\n| Missing error context            | `throw new Error('failed')`                 | manual review                                 |\n| Hardcoded secrets                | `const API_KEY = \"sk-...\"`                  | manual review                                 |\n| `eval()` usage                   | `eval(userInput)`                           | no-eval                                       |\n| `new Function()` usage           | `new Function('x', 'return x')`             | no-new-func                                   |\n| `exec()` with untrusted input    | `exec(\\`grep ${input} file\\`)`              | manual review                                 |\n| Unused variables\u002Fimports         | `import { x } from 'y'; \u002F\u002F never used`      | @typescript-eslint\u002Fno-unused-vars             |\n| `console.log` in production      | `console.log('debug')`                      | no-console                                    |\n| Dead\u002Fcommented code              | `\u002F\u002F function old() { ... }`                 | manual review                                 |\n| Deep relative imports            | `from '..\u002F..\u002F..\u002Flib'`                       | manual review                                 |\n\n\u003C\u002Frejection_criteria_summary>\n",{"name":5,"description":7,"allowed-tools":37,"user-invocable":33},"Read","main","978d51c51d68c99cffb2ba48f7cad9d2f07e5738","src\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-code-standards","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins","https:\u002F\u002Fgithub.com\u002Foutcomeeng","https:\u002F\u002Foutcome.engineering","Spec-driven development for AI-assisted engineering","2026-11-11 22:17:26",{"source":8,"skillName":5,"commands":47,"prompt":57,"zipAvailable":58},[48,51,54],{"tool":49,"command":50},"npx","npx skills add outcomeeng\u002Fplugins --skill typescript-code-standards",{"tool":52,"command":53},"bunx","bunx skills add outcomeeng\u002Fplugins --skill typescript-code-standards",{"tool":55,"command":56},"pnpm","pnpm dlx skills add outcomeeng\u002Fplugins --skill typescript-code-standards","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-code-standards\u002FSKILL.md",true,[],[61,67,102,118,130,144],{"skillNo":62,"name":63,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":66,"publishedAt":32},"01M71G25RZTPQG9KJ00JCHC1F7","plugin","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Ftemplates\u002Fplugin\u002FSKILL.md",[],[63],{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":81,"publishedAt":32},"01M71G23DCX3SV60H63HBBKQ6J","sanitize-powerpoint","ALWAYS invoke this skill when sanitizing, cleaning up, auditing, or aligning a PowerPoint (.pptx) deck — slide-master and layout structure, layout type attributes, stray fonts, non-theme colors, or layout naming. NEVER hand-edit pptx XML without this skill.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fwork\u002Fskills\u002Fsanitize-powerpoint\u002FSKILL.md",[73,78],{"categoryNo":74,"categoryName":75,"taxonomyType":76,"taxonomyTypeDesc":77},"01JBSEEDCAT000000000000003","演示文稿","SCENARIO","使用场景",{"categoryNo":79,"categoryName":80,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000034","行政与办公人员",[82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101],"sanitize","powerpoint","sanitize powerpoint","fonts","type","cleaning","structure","never","attributes","theme","pptx","naming","layout","deck","up","edit","xml","master","invoke","when",{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":32},"01M71G21PQKBCDECHGVYSKPJW5","draw-excalidraw","ALWAYS invoke this skill when creating Excalidraw diagrams, visualizing workflows, architectures, or concepts. NEVER generate Excalidraw JSON without this skill.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Fwork\u002Fskills\u002Fdraw-excalidraw\u002FSKILL.md",[],[109,110,111,100,89,112,113,101,114,115,116,117],"draw","excalidraw","draw excalidraw","generate","workflows","concepts","json","diagrams","architectures",{"skillNo":119,"name":120,"nameZh":6,"description":121,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":122,"categories":123,"keywords":127,"publishedAt":32},"01M71G1ZMNGFD37RZYM620XM2C","typescript-test-standards","TypeScript test standards enforced across all skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-test-standards\u002FSKILL.md",[124],{"categoryNo":125,"categoryName":126,"taxonomyType":76,"taxonomyTypeDesc":77},"01JBSEEDCAT000000000000008","测试与质量",[28,24,128,129,29,30,31],"test","typescript test standards",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":136,"publishedAt":32},"01M71G1TQ3VN45SJ75DFMJDB9W","typescript-architecture-standards","TypeScript ADR conventions enforced across architect and auditor skills. Loaded by other skills, not invoked directly.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Ftypescript-architecture-standards\u002FSKILL.md",[],[24,137,138,139,28,140,141,142,29,143,30,31],"architecture standards","typescript architecture standards","architecture","architect","adr","conventions","auditor",{"skillNo":145,"name":146,"nameZh":6,"description":147,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":148,"categories":149,"keywords":150,"publishedAt":32},"01M71G1RSFGDZR5XBXDX7E02B5","simplify-typescript-code","ALWAYS invoke this skill when simplifying TypeScript implementation while preserving behavior.","https:\u002F\u002Fgithub.com\u002Foutcomeeng\u002Fplugins\u002Fblob\u002Fmain\u002Fsrc\u002Fplugins\u002Ftypescript\u002Fskills\u002Fsimplify-typescript-code\u002FSKILL.md",[],[151,28,27,152,153,100,101,154,155,156,157,158,159,160,97,161,30,162,163,164],"simplify typescript code","simplify","head","implementation","rev","base","status","git","use","grep","tool","behavior","parse","diff"]