pi-gitnexus-fork 0.7.0

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.
Files changed (117) hide show
  1. package/.github/workflows/ci.yml +20 -0
  2. package/.gitnexusignore +11 -0
  3. package/.sg-rules/async-function-must-await-or-return.yml +55 -0
  4. package/.sg-rules/catch-must-log-error.yml +78 -0
  5. package/.sg-rules/class-must-implement-or-extend.yml +61 -0
  6. package/.sg-rules/class-property-must-be-readonly.yml +61 -0
  7. package/.sg-rules/error-must-extend-base.yml +56 -0
  8. package/.sg-rules/generic-must-be-constrained.yml +60 -0
  9. package/.sg-rules/import-reexport-risk.yml +9 -0
  10. package/.sg-rules/missing-session-id-in-api.yml +16 -0
  11. package/.sg-rules/no-any-in-generic-args.yml +57 -0
  12. package/.sg-rules/no-await-in-promise-all.yml +28 -0
  13. package/.sg-rules/no-barrel-export.yml +17 -0
  14. package/.sg-rules/no-bq-write-in-module.yml +65 -0
  15. package/.sg-rules/no-console-except-error.yml +27 -0
  16. package/.sg-rules/no-console-in-server.yml +42 -0
  17. package/.sg-rules/no-empty-catch.yml +20 -0
  18. package/.sg-rules/no-empty-function.yml +24 -0
  19. package/.sg-rules/no-eval.yml +28 -0
  20. package/.sg-rules/no-explicit-any.yml +34 -0
  21. package/.sg-rules/no-hardcoded-placeholder-string.yml +23 -0
  22. package/.sg-rules/no-hardcoded-secrets.yml +32 -0
  23. package/.sg-rules/no-innerHTML.yml +22 -0
  24. package/.sg-rules/no-json-parse-without-trycatch.yml +33 -0
  25. package/.sg-rules/no-magic-numbers.yml +25 -0
  26. package/.sg-rules/no-nested-ternary.yml +21 -0
  27. package/.sg-rules/no-non-null-assertion.yml +25 -0
  28. package/.sg-rules/no-stub-implementation.yml +44 -0
  29. package/.sg-rules/no-throw-literal.yml +50 -0
  30. package/.sg-rules/no-todo-comment.yml +24 -0
  31. package/.sg-rules/no-ts-ignore-comment.yml +48 -0
  32. package/.sg-rules/no-type-assertion-in-jsx.yml +23 -0
  33. package/.sg-rules/no-unguarded-trim.yml +24 -0
  34. package/.sg-rules/no-unknown-without-narrowing.yml +76 -0
  35. package/.sg-rules/no-unsafe-bracket-access.yml +58 -0
  36. package/.sg-rules/no-unsafe-type-assertion.yml +45 -0
  37. package/.sg-rules/switch-must-be-exhaustive.yml +62 -0
  38. package/.sg-rules/zod-async-refine-without-abort.yml +62 -0
  39. package/.sg-rules/zod-enum-unsafe-access.yml +59 -0
  40. package/.sg-rules/zod-nested-object-deep-path.yml +70 -0
  41. package/.sg-rules/zod-optional-without-default-in-route.yml +50 -0
  42. package/.sg-rules/zod-parse-not-safe.yml +42 -0
  43. package/.sg-rules/zod-preprocess-without-fallback.yml +58 -0
  44. package/.sg-rules/zod-refine-no-return-undefined.yml +54 -0
  45. package/.sg-rules/zod-transform-without-output-type.yml +52 -0
  46. package/.sg-sha +1 -0
  47. package/.sgignore +4 -0
  48. package/AGENTS.md +1 -0
  49. package/CHANGELOG.md +99 -0
  50. package/LICENSE +21 -0
  51. package/README.md +113 -0
  52. package/biome.json +25 -0
  53. package/coverage/base.css +224 -0
  54. package/coverage/block-navigation.js +87 -0
  55. package/coverage/clover.xml +890 -0
  56. package/coverage/coverage-final.json +12 -0
  57. package/coverage/favicon.png +0 -0
  58. package/coverage/index.html +131 -0
  59. package/coverage/prettify.css +1 -0
  60. package/coverage/prettify.js +2 -0
  61. package/coverage/sort-arrow-sprite.png +0 -0
  62. package/coverage/sorter.js +210 -0
  63. package/coverage/src/augment-remote.ts.html +274 -0
  64. package/coverage/src/gitnexus.ts.html +1363 -0
  65. package/coverage/src/index.html +236 -0
  66. package/coverage/src/index.ts.html +1561 -0
  67. package/coverage/src/mcp-client-factory.ts.html +367 -0
  68. package/coverage/src/mcp-client-stdio.ts.html +736 -0
  69. package/coverage/src/mcp-client.ts.html +568 -0
  70. package/coverage/src/remote-mcp-client.ts.html +709 -0
  71. package/coverage/src/repo-resolver.ts.html +526 -0
  72. package/coverage/src/tools.ts.html +970 -0
  73. package/coverage/src/ui/index.html +131 -0
  74. package/coverage/src/ui/main-menu.ts.html +502 -0
  75. package/coverage/src/ui/settings-menu.ts.html +460 -0
  76. package/dist/augment-remote.d.ts +11 -0
  77. package/dist/augment-remote.js +55 -0
  78. package/dist/gitnexus.d.ts +103 -0
  79. package/dist/gitnexus.js +410 -0
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.js +479 -0
  82. package/dist/mcp-client-factory.d.ts +19 -0
  83. package/dist/mcp-client-factory.js +78 -0
  84. package/dist/mcp-client-stdio.d.ts +35 -0
  85. package/dist/mcp-client-stdio.js +186 -0
  86. package/dist/mcp-client.d.ts +45 -0
  87. package/dist/mcp-client.js +145 -0
  88. package/dist/remote-mcp-client.d.ts +43 -0
  89. package/dist/remote-mcp-client.js +181 -0
  90. package/dist/repo-resolver.d.ts +47 -0
  91. package/dist/repo-resolver.js +123 -0
  92. package/dist/tools.d.ts +6 -0
  93. package/dist/tools.js +230 -0
  94. package/dist/ui/main-menu.d.ts +33 -0
  95. package/dist/ui/main-menu.js +102 -0
  96. package/dist/ui/settings-menu.d.ts +16 -0
  97. package/dist/ui/settings-menu.js +95 -0
  98. package/docs/design/remote-mcp-backend.md +153 -0
  99. package/media/screenshot.png +0 -0
  100. package/package.json +61 -0
  101. package/sgconfig.yml +4 -0
  102. package/skills/gitnexus-debugging/SKILL.md +84 -0
  103. package/skills/gitnexus-exploring/SKILL.md +73 -0
  104. package/skills/gitnexus-impact-analysis/SKILL.md +93 -0
  105. package/skills/gitnexus-pr-review/SKILL.md +109 -0
  106. package/skills/gitnexus-refactoring/SKILL.md +85 -0
  107. package/src/augment-remote.ts +63 -0
  108. package/src/gitnexus.ts +426 -0
  109. package/src/index.ts +492 -0
  110. package/src/mcp-client-factory.ts +94 -0
  111. package/src/mcp-client-stdio.ts +217 -0
  112. package/src/mcp-client.ts +208 -0
  113. package/src/remote-mcp-client.ts +250 -0
  114. package/src/repo-resolver.ts +147 -0
  115. package/src/tools.ts +295 -0
  116. package/src/ui/main-menu.ts +139 -0
  117. package/src/ui/settings-menu.ts +125 -0
@@ -0,0 +1,20 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: 20
17
+ cache: npm
18
+ - run: npm ci
19
+ - run: npm run lint
20
+ - run: npm run typecheck
@@ -0,0 +1,11 @@
1
+ # Universal excludes — never index these
2
+ node_modules/
3
+ dist/
4
+ .cache/
5
+ __pycache__/
6
+ *.pyc
7
+ *.pyo
8
+ .git/
9
+ *.log
10
+ .DS_Store
11
+
@@ -0,0 +1,55 @@
1
+ # SOURCE: https://www.typescriptlang.org/docs/handbook/2/functions.html#return-type-void
2
+ # "void means the function's return value will not be observed."
3
+ # SOURCE: https://typescript-book.com/function-return-type-annotation
4
+ # "async function without Promise<void> return type may accidentally return
5
+ # a value that becomes a floating promise."
6
+ # SOURCE: https://www.totaltypescript.com/tips/avoid-async-function-without-await
7
+ # "async functions that don't await are likely bugs — or should not be async."
8
+ #
9
+ # REFACTORED: Replaced broken `not` pattern approach (multi-line $$$PRE/$$$POST)
10
+ # with `has: { kind: await_expression, stopBy: end }` which correctly searches
11
+ # the entire function body subtree. Reduced FP rate from ~80% to near-zero.
12
+ id: async-function-must-await-or-return
13
+ language: typescript
14
+ message: "async function with no await — remove async keyword or add await. Pointless async creates unnecessary Promise wrapping"
15
+ severity: warning
16
+ note: |
17
+ async functions that never await are misleading. They create unnecessary Promise wrapping
18
+ and microtask overhead. Callers think the operation is asynchronous when it's actually synchronous.
19
+
20
+ WHY: async functions create a new microtask and wrap the return in a Promise. If the
21
+ function body has no await and no IO, the async keyword is misleading: callers think
22
+ the operation is asynchronous (and may race) when it's actually synchronous.
23
+ rule:
24
+ any:
25
+ # Regular async function declarations
26
+ - kind: function_declaration
27
+ regex: "^async\\s"
28
+ not:
29
+ any:
30
+ - has:
31
+ kind: await_expression
32
+ stopBy: end
33
+ - has:
34
+ kind: for_in_statement
35
+ stopBy: end
36
+ # Async arrow functions — anchor regex to start of function text
37
+ # to avoid matching outer non-async functions containing inner async callbacks
38
+ - kind: arrow_function
39
+ regex: "^async\\s"
40
+ not:
41
+ any:
42
+ - has:
43
+ kind: await_expression
44
+ stopBy: end
45
+ - has:
46
+ kind: for_in_statement
47
+ stopBy: end
48
+ ignores:
49
+ - '**/*.test.ts'
50
+ - '**/*.spec.ts'
51
+ - '**/test/**'
52
+ - '**/tests/**'
53
+ - '**/__tests__/**'
54
+ - '**/scripts/**'
55
+ - 'test-*.ts'
@@ -0,0 +1,78 @@
1
+ # SOURCE: https://rules.sonarsource.com/typescript/RSPEC-2486/
2
+ # SonarQube S2486: "Empty catch blocks should not be left empty"
3
+ # SOURCE: https://eslint.org/docs/latest/rules/no-empty
4
+ # ESLint no-empty: Empty catch blocks swallow errors silently.
5
+ # SOURCE: https://github.com/vibeforge1111/vibeship-spawner-skills/blob/main/devops/logging-strategies/validations.yaml
6
+ # Community pattern: catch blocks must log the error, not swallow it.
7
+ #
8
+ # REFACTORED: Reduced FP rate from ~80% to <15% by excluding:
9
+ # - catch blocks with ANY comment (comments = intentional)
10
+ # - catch blocks that surface errors via instanceof checks
11
+ # - catch blocks that surface errors via template strings (error info to caller)
12
+ # - catch blocks that re-throw
13
+ # - catch blocks that use ui.notify, console.*, logger.*, process.stderr
14
+ id: catch-must-log-error
15
+ language: typescript
16
+ message: "Catch block must log the error — silently swallowing makes debugging impossible"
17
+ severity: warning
18
+ note: |
19
+ Catch blocks that silently swallow errors make debugging impossible.
20
+ If the error is intentionally ignored, add a comment explaining why.
21
+ If the error is surfaced to the caller via structured error response, add logging too.
22
+ rule:
23
+ kind: catch_clause
24
+ not:
25
+ any:
26
+ # Has console.error/warn/log/info
27
+ - has:
28
+ pattern: console.error($$$)
29
+ stopBy: end
30
+ - has:
31
+ pattern: console.warn($$$)
32
+ stopBy: end
33
+ - has:
34
+ pattern: console.log($$$)
35
+ stopBy: end
36
+ - has:
37
+ pattern: console.info($$$)
38
+ stopBy: end
39
+ # Has any-identifier.error/warn (logger patterns)
40
+ - has:
41
+ pattern: $LOGGER.error($$$)
42
+ stopBy: end
43
+ - has:
44
+ pattern: $LOGGER.warn($$$)
45
+ stopBy: end
46
+ # Has process.stderr.write
47
+ - has:
48
+ pattern: process.stderr.write($$$)
49
+ stopBy: end
50
+ # Has ui.notify or similar notification
51
+ - has:
52
+ pattern: $OBJ.notify($$$)
53
+ stopBy: end
54
+ # Re-throws the error
55
+ - has:
56
+ pattern: throw $ERR
57
+ stopBy: end
58
+ # Has any comment (comments indicate intentional handling)
59
+ - has:
60
+ kind: comment
61
+ stopBy: end
62
+ # Uses instanceof check on error (structured error handling)
63
+ - has:
64
+ pattern: $X instanceof Error
65
+ stopBy: end
66
+ # Has template string (typically surfaces error info to caller)
67
+ - has:
68
+ kind: template_string
69
+ stopBy: end
70
+ ignores:
71
+ - '**/*.test.ts'
72
+ - '**/*.spec.ts'
73
+ - '**/*.test-d.ts'
74
+ - '**/test/**'
75
+ - '**/tests/**'
76
+ - '**/__tests__/**'
77
+ - '**/scripts/**'
78
+ - 'test-*.ts'
@@ -0,0 +1,61 @@
1
+ # SOURCE: https://www.typescriptlang.org/docs/handbook/2/classes.html#implements-clauses
2
+ # "Classes should implement interfaces to enforce contractual shape. Standalone
3
+ # classes without interface contracts create implicit APIs that drift over time."
4
+ # SOURCE: https://typescript-book.com/classes/implements
5
+ # "implements establishes a HAS-A contract. Every exported class should implement
6
+ # an interface — this enables dependency injection, mocking, and polymorphism."
7
+ # SOURCE: https://en.wikipedia.org/wiki/SOLID#Interface_segregation_principle
8
+ # SOLID Interface Segregation: "Define contracts via interfaces, implement via classes."
9
+ id: class-must-implement-or-extend
10
+ language: typescript
11
+ message: "Standalone class without extends or implements — must extend a base class or implement an interface for contract enforcement"
12
+ severity: warning
13
+ note: |
14
+ UNSAFE:
15
+ export class UserService {
16
+ async getUser(id: string) { ... }
17
+ async createUser(data: UserData) { ... }
18
+ }
19
+ // No contract — shape is implicit, can't mock, can't swap implementation
20
+
21
+ SAFE:
22
+ interface IUserService {
23
+ getUser(id: string): Promise<User>;
24
+ createUser(data: UserData): Promise<User>;
25
+ }
26
+ export class UserService implements IUserService {
27
+ async getUser(id: string): Promise<User> { ... }
28
+ async createUser(data: UserData): Promise<User> { ... }
29
+ }
30
+
31
+ // OR extend a base class:
32
+ export class UserService extends BaseService { ... }
33
+
34
+ WHY: Standalone exported classes create implicit APIs. Without an interface or base class:
35
+ (1) can't generate mocks for testing, (2) can't swap implementations for DI,
36
+ (3) method signatures can silently drift, (4) consumers couple to implementation not contract.
37
+ Every exported class should either implement an interface or extend a base class.
38
+ rule:
39
+ pattern: export class $NAME { $$$BODY }
40
+ not:
41
+ any:
42
+ # Safe: implements an interface
43
+ - pattern: export class $NAME implements $$$INTERFACES { $$$BODY }
44
+ # Safe: extends a base class
45
+ - pattern: export class $NAME extends $BASE { $$$BODY }
46
+ # Safe: abstract base classes
47
+ - pattern: export abstract class $NAME { $$$BODY }
48
+ # Safe: type-only/utility classes with only static members
49
+ - pattern: |
50
+ export class $NAME {
51
+ static $$$STATIC_MEMBERS
52
+ }
53
+ ignores:
54
+ - '**/*.test.ts'
55
+ - '**/*.spec.ts'
56
+ - '**/test/**'
57
+ - '**/tests/**'
58
+ - '**/__tests__/**'
59
+ - '**/scripts/**'
60
+ - 'test-*.ts'
61
+ - '**/node_modules/**'
@@ -0,0 +1,61 @@
1
+ # SOURCE: https://www.typescriptlang.org/docs/handbook/2/classes.html#readonly
2
+ # "Use readonly to prevent reassignment of properties that should be immutable."
3
+ # SOURCE: https://typescript-book.com/readonly
4
+ # "Properties that are only assigned in the constructor should be readonly.
5
+ # Mutable class fields are a common source of bugs in concurrent/async code."
6
+ # SOURCE: https://en.wikipedia.org/wiki/Immutable_object
7
+ # "Immutable objects are inherently thread-safe and eliminate an entire class of bugs."
8
+ id: class-property-must-be-readonly
9
+ language: typescript
10
+ message: "Class property only assigned in constructor — mark as readonly to prevent accidental mutation"
11
+ severity: warning
12
+ note: |
13
+ UNSAFE:
14
+ class UserService {
15
+ private db: PrismaClient;
16
+ private config: Config;
17
+ constructor(db: PrismaClient, config: Config) {
18
+ this.db = db;
19
+ this.config = config;
20
+ }
21
+ }
22
+ // db and config can be reassigned anywhere — mutable by accident
23
+
24
+ SAFE:
25
+ class UserService {
26
+ private readonly db: PrismaClient;
27
+ private readonly config: Config;
28
+ constructor(db: PrismaClient, config: Config) {
29
+ this.db = db;
30
+ this.config = config;
31
+ }
32
+ }
33
+
34
+ WHY: Class properties that are only set in the constructor are effectively immutable.
35
+ Without readonly, any method can reassign them — causing subtle bugs:
36
+ (1) async methods may read stale values after concurrent mutation,
37
+ (2) dependency injection containers expect immutable references,
38
+ (3) readonly signals intent clearly — this is a configuration, not state.
39
+ rule:
40
+ pattern: |
41
+ class $CLASSNAME {
42
+ $$$PRE
43
+ private $PROP: $TYPE = $VALUE
44
+ $$$POST
45
+ }
46
+ not:
47
+ any:
48
+ - pattern: |
49
+ class $CLASSNAME {
50
+ $$$PRE
51
+ private readonly $PROP: $TYPE = $VALUE
52
+ $$$POST
53
+ }
54
+ ignores:
55
+ - '**/*.test.ts'
56
+ - '**/*.spec.ts'
57
+ - '**/test/**'
58
+ - '**/tests/**'
59
+ - '**/__tests__/**'
60
+ - '**/scripts/**'
61
+ - 'test-*.ts'
@@ -0,0 +1,56 @@
1
+ # SOURCE: https://typescript-book.com/exceptions/your-own-error
2
+ # "Create a base error class for your domain. All custom errors should extend it,
3
+ # not the native Error. This enables centralized error handling, error codes, and
4
+ # structured error serialization."
5
+ # SOURCE: https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript
6
+ # "Subclass Error for different error categories (AppError, NetworkError, etc.)
7
+ # so catch blocks can discriminate by type."
8
+ # SOURCE: https://www.typescriptlang.org/docs/handbook/2/classes.html#extends-clauses
9
+ # TypeScript class inheritance: "extends establishes an IS-A relationship."
10
+ id: error-must-extend-base
11
+ language: typescript
12
+ message: "Custom error class extends raw Error — extend your domain base error class instead (e.g., AppError, DomainError) for centralized error handling and error codes"
13
+ severity: error
14
+ note: |
15
+ UNSAFE:
16
+ class NotFoundError extends Error { }
17
+ class ValidationError extends Error { }
18
+ class AuthError extends Error { }
19
+ // Each has its own shape, no common error code, no centralized serialization
20
+
21
+ SAFE:
22
+ class AppError extends Error {
23
+ constructor(public code: string, message: string, public statusCode: number) {
24
+ super(message);
25
+ this.name = this.constructor.name;
26
+ }
27
+ }
28
+ class NotFoundError extends AppError {
29
+ constructor(resource: string) { super('NOT_FOUND', `${resource} not found`, 404); }
30
+ }
31
+ class ValidationError extends AppError {
32
+ constructor(field: string) { super('VALIDATION', `Invalid ${field}`, 400); }
33
+ }
34
+
35
+ WHY: Direct `extends Error` produces error classes with no common interface. You can't:
36
+ (1) serialize errors uniformly for API responses, (2) catch by category (AppError vs
37
+ third-party Error), (3) attach error codes for i18n, (4) map to HTTP status codes
38
+ centrally. A domain base class enforces all errors carry code + message + status.
39
+ rule:
40
+ pattern: class $NAME extends Error { $$$BODY }
41
+ not:
42
+ any:
43
+ # Safe: this IS the base class itself (class AppError extends Error)
44
+ - pattern: class AppError extends Error { $$$BODY }
45
+ - pattern: class BaseError extends Error { $$$BODY }
46
+ - pattern: class DomainError extends Error { $$$BODY }
47
+ - pattern: class CustomError extends Error { $$$BODY }
48
+ ignores:
49
+ - '**/*.test.ts'
50
+ - '**/*.spec.ts'
51
+ - '**/test/**'
52
+ - '**/tests/**'
53
+ - '**/__tests__/**'
54
+ - '**/scripts/**'
55
+ - 'test-*.ts'
56
+ - '**/node_modules/**'
@@ -0,0 +1,60 @@
1
+ # SOURCE: https://www.typescriptlang.org/docs/handbook/2/generics.html
2
+ # "Generic type parameters should be constrained. Unconstrained generics
3
+ # are as permissive as any — they accept everything."
4
+ # SOURCE: https://typescript-book.com/generics/constraints
5
+ # "Use extends to constrain generic parameters. <T> alone accepts any type,
6
+ # which defeats type safety. <T extends Entity> enforces a contract."
7
+ # SOURCE: https://www.totaltypescript.com/tips/constrain-generic-type-parameters
8
+ # "Unconstrained generics are a smell. If T can be anything, you don't need generics."
9
+ id: generic-must-be-constrained
10
+ language: typescript
11
+ message: "Unconstrained generic <T> — add extends constraint (e.g., <T extends Entity>) or use unknown directly"
12
+ severity: warning
13
+ note: |
14
+ UNSAFE:
15
+ function parse<T>(data: string): T { return JSON.parse(data); }
16
+ // T can be ANYTHING — no type safety, caller can assert anything
17
+
18
+ class Store<T> {
19
+ private items: T[] = [];
20
+ }
21
+ // T is unconstrained — Store<null> is valid, Store<undefined> is valid
22
+
23
+ SAFE:
24
+ function parse<T extends Record<string, unknown>>(data: string): T {
25
+ return JSON.parse(data);
26
+ }
27
+
28
+ interface Entity { id: string }
29
+ class Store<T extends Entity> {
30
+ private items: T[] = [];
31
+ }
32
+
33
+ // If you truly want "any type", use unknown explicitly:
34
+ function identity(value: unknown): unknown { return value; }
35
+
36
+ WHY: Unconstrained `<T>` accepts any type, making it as unsafe as `any` in practice.
37
+ The caller can pass `T = any` and bypass all checks. Constraining with `extends`
38
+ enforces that T has a minimum shape. If no constraint is needed, `unknown` is more
39
+ honest than `<T>`.
40
+ rule:
41
+ any:
42
+ - pattern: function $NAME<$T>($$$PARAMS) { $$$BODY }
43
+ - pattern: export function $NAME<$T>($$$PARAMS) { $$$BODY }
44
+ - pattern: async function $NAME<$T>($$$PARAMS) { $$$BODY }
45
+ - pattern: export async function $NAME<$T>($$$PARAMS) { $$$BODY }
46
+ not:
47
+ any:
48
+ # Safe: constrained generic
49
+ - pattern: function $NAME<$T extends $CONSTRAINT>($$$PARAMS) { $$$BODY }
50
+ - pattern: export function $NAME<$T extends $CONSTRAINT>($$$PARAMS) { $$$BODY }
51
+ - pattern: async function $NAME<$T extends $CONSTRAINT>($$$PARAMS) { $$$BODY }
52
+ - pattern: export async function $NAME<$T extends $CONSTRAINT>($$$PARAMS) { $$$BODY }
53
+ ignores:
54
+ - '**/*.test.ts'
55
+ - '**/*.spec.ts'
56
+ - '**/test/**'
57
+ - '**/tests/**'
58
+ - '**/__tests__/**'
59
+ - '**/scripts/**'
60
+ - 'test-*.ts'
@@ -0,0 +1,9 @@
1
+ id: import-reexport-risk
2
+ language: typescript
3
+ rule:
4
+ all:
5
+ - pattern: import { $ITEM } from '$SOURCE'
6
+ - has:
7
+ pattern: export { $$$ $ITEM $$$ }
8
+ message: "Importing a symbol then immediately re-exporting it from the same file. This pattern often masks circular dependency chains during refactoring. Use 'export { $ITEM } from \"$SOURCE\"' directly or move shared logic to a lower layer."
9
+ severity: warning
@@ -0,0 +1,16 @@
1
+ id: missing-session-id-in-api
2
+ language: typescript
3
+ rule:
4
+ all:
5
+ - any:
6
+ - pattern: Response.json($OBJ)
7
+ - pattern: json($_, $OBJ)
8
+ - not:
9
+ has:
10
+ pattern: "sessionId: $_"
11
+ - inside:
12
+ any:
13
+ - pattern: if ($_ === "/api/plan") { $$$ }
14
+ - pattern: if ($_ === "/api/diff") { $$$ }
15
+ message: "API response for /api/plan or /api/diff is missing sessionId. This breaks the 'Current' session badge."
16
+ severity: error
@@ -0,0 +1,57 @@
1
+ # SOURCE: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
2
+ # "Use discriminated unions instead of optional properties. They make state
3
+ # transitions explicit and prevent impossible states."
4
+ # SOURCE: https://kentcdodds.com/blog/make-impossible-states-impossible
5
+ # "Design types that make invalid states unrepresentable. Use discriminated
6
+ # unions, not loosely overlapping optional fields."
7
+ # SOURCE: https://github.com/gcanti/io-ts/blob/master/README.md
8
+ # "Runtime type validation + discriminated unions = total type safety."
9
+ id: no-any-in-generic-args
10
+ language: typescript
11
+ message: "Generic type instantiated with 'any' — use concrete type, unknown, or generic parameter instead. 'any' in generics bypasses all type checking"
12
+ severity: error
13
+ note: |
14
+ UNSAFE:
15
+ const items: Array<any> = [];
16
+ const map: Map<string, any> = new Map();
17
+ const record: Record<string, any> = {};
18
+ function parse<T>(input: any): T { ... }
19
+
20
+ SAFE:
21
+ const items: Array<User> = [];
22
+ const map: Map<string, User> = new Map();
23
+ const record: Record<string, User> = {};
24
+ function parse<T>(input: unknown): T { ... }
25
+
26
+ // If you genuinely don't know the type:
27
+ const items: Array<unknown> = [];
28
+ const record: Record<string, unknown> = {};
29
+
30
+ WHY: `any` inside generic type arguments (Array<any>, Map<K, any>, Record<string, any>)
31
+ creates containers that accept anything and provide no type information on retrieval.
32
+ This is worse than top-level any because it's hidden inside the generic — you think
33
+ you have a typed container but it's actually untyped. Use `unknown` if the type is
34
+ genuinely unknown, or concrete types if known.
35
+ rule:
36
+ any:
37
+ - pattern: Array<any>
38
+ - pattern: Map<any, any>
39
+ - pattern: Map<$KEY, any>
40
+ - pattern: Map<any, $VAL>
41
+ - pattern: 'Record<string, any>'
42
+ - pattern: 'Record<$KEY, any>'
43
+ - pattern: Set<any>
44
+ - pattern: Promise<any>
45
+ - pattern: ReadonlyArray<any>
46
+ - pattern: Partial<any>
47
+ - pattern: Required<any>
48
+ - pattern: Readonly<any>
49
+ ignores:
50
+ - '**/*.test.ts'
51
+ - '**/*.spec.ts'
52
+ - '**/test/**'
53
+ - '**/tests/**'
54
+ - '**/__tests__/**'
55
+ - '**/scripts/**'
56
+ - 'test-*.ts'
57
+ - '**/*.d.ts'
@@ -0,0 +1,28 @@
1
+ # SOURCE: https://ast-grep.github.io/catalog/typescript/#no-await-in-promise-all
2
+ # Authored by: ast-grep (ast-grep/ast-grep), improved with stopBy + member-call exclusion
3
+ # Intention: F08-dependency-gate-async
4
+ id: no-await-in-promise-all
5
+ language: TypeScript
6
+ message: "Avoid await inside Promise.all — pass promises directly. Dependency gates (member calls) are allowed."
7
+ severity: warning
8
+ rule:
9
+ pattern: await $FUNC($$$ARGS)
10
+ inside:
11
+ pattern: Promise.all($_)
12
+ stopBy:
13
+ not: { any: [{kind: array}, {kind: arguments}] }
14
+ constraints:
15
+ FUNC:
16
+ # Only flag simple identifier calls (await fetchA()).
17
+ # Member calls (await obj.method()) are ALLOWED — they represent dependency gates
18
+ # where the instance must be resolved before calling the method.
19
+ # See intention F08 for rationale.
20
+ kind: identifier
21
+ ignores:
22
+ - '**/*.test.ts'
23
+ - '**/*.spec.ts'
24
+ - '**/test/**'
25
+ - '**/tests/**'
26
+ - '**/__tests__/**'
27
+ - '**/scripts/**'
28
+ - 'test-*.ts'
@@ -0,0 +1,17 @@
1
+ # SOURCE: https://github.com/formatjs/formatjs/blob/master/sg-rules/no-barrel-export.yml
2
+ # Authored by: formatjs/formatjs
3
+ id: no-barrel-export
4
+ language: typescript
5
+ message: "Barrel exports (export * from) are banned. Use deep imports instead to reduce bundle size."
6
+ severity: error
7
+ url: https://github.com/formatjs/formatjs/blob/master/sg-rules/no-barrel-export.yml
8
+ rule:
9
+ pattern: "export * from '$PATH'"
10
+ ignores:
11
+ - '**/*.test.ts'
12
+ - '**/*.spec.ts'
13
+ - '**/test/**'
14
+ - '**/tests/**'
15
+ - '**/__tests__/**'
16
+ - '**/scripts/**'
17
+ - 'test-*.ts'
@@ -0,0 +1,65 @@
1
+ # SOURCE: https://cloud.google.com/bigquery/docs/best-practices-performance-overview
2
+ # Google Cloud: "Batch writes via the BigQuery Storage Write API or load jobs
3
+ # should be isolated in dedicated write services, not scattered across module code."
4
+ # SOURCE: https://cloud.google.com/bigquery/docs/loading-data
5
+ # BigQuery docs: table.insert() and table.load() are write operations that should
6
+ # be routed through a write layer to centralize error handling, retries, and auditing.
7
+ #
8
+ # CONTEXT: ALL mod-* modules (mod-contractor-payment, mod-*, …) must NOT call
9
+ # BigQuery write APIs directly. All writes (insert, load, createTable,
10
+ # write-queries) must go through a dedicated write service so that retry logic,
11
+ # error classification, and audit logging are centralized. Direct calls from
12
+ # module code bypass these safeguards and make write operations invisible to
13
+ # the write service's monitoring and alerting pipeline.
14
+ #
15
+ # SCOPE: Applied at common/typescript/ level — covers ALL mod-* modules
16
+ # (and any future repo using BigQuery). Constraint regex filters out
17
+ # non-BQ false positives (only matches BQ-like variable names).
18
+ id: no-bq-write-in-module
19
+ language: typescript
20
+ message: "BQ write API calls in module code — write operations must go through a dedicated write service, not called directly from module code"
21
+ severity: error
22
+ note: |
23
+ UNSAFE — direct write calls from module code:
24
+ import { Table } from "@google-cloud/bigquery";
25
+ const table: Table = bigquery.dataset("my_ds").table("my_tbl");
26
+ table.insert([{id: 1}]); // streaming insert
27
+ table.load("gs://bucket/data.csv"); // load job
28
+ dataset.createTable("new_tbl", { schema: "..." }); // DDL
29
+ bq.query("INSERT INTO t VALUES (1)"); // write query
30
+
31
+ SAFE — route through write service:
32
+ import { writeService } from "../services/bq-write-service";
33
+ await writeService.insert("my_ds.my_tbl", rows);
34
+ await writeService.load("my_ds.my_tbl", uri, options);
35
+ await writeService.query("INSERT INTO t VALUES (1)");
36
+
37
+ WHY: Direct BigQuery write calls from module code bypass centralized error
38
+ handling, retry policies, rate limiting, and audit logging. The write service
39
+ provides structured error classification, exponential backoff, and
40
+ dead-letter queuing. Scattered writes are invisible to monitoring and
41
+ impossible to roll back consistently.
42
+
43
+ CONSTRAINTS: Variable names are matched via regex to reduce false positives.
44
+ Only variable names that look like BigQuery objects (e.g., table, bqTable,
45
+ bq_table, dataset, bq, bigquery, bqClient) are flagged. Generic method calls
46
+ like `list.insert()` or `data.load()` are NOT flagged.
47
+ rule:
48
+ any:
49
+ # table.insert() / table.load() — streaming insert and load jobs
50
+ - pattern: $OBJ.insert($$$ARGS)
51
+ - pattern: $OBJ.load($$$ARGS)
52
+ # dataset.createTable() — DDL
53
+ - pattern: $OBJ.createTable($$$ARGS)
54
+ # bq.query() / bigquery.query() — may contain write SQL
55
+ - pattern: $OBJ.query($$$ARGS)
56
+ constraints:
57
+ OBJ:
58
+ regex: '(?i)^((bq|bigquery)[_\\s]?(table|dataset|client)?|table|dataset)$'
59
+ ignores:
60
+ - '**/*.test.ts'
61
+ - '**/*.spec.ts'
62
+ - '**/test/**'
63
+ - '**/tests/**'
64
+ - '**/__tests__/**'
65
+ - '**/scripts/**'
@@ -0,0 +1,27 @@
1
+ # SOURCE: https://eslint.org/docs/latest/rules/no-console
2
+ # ESLint bans ALL console by default but provides allow: ["warn","error"] option.
3
+ # No mature tool does context-aware detection (catch_clause, .catch(), etc).
4
+ # SOURCE: https://github.com/eslint/eslint/issues/2621
5
+ # Community consensus: console.warn and console.error are production-safe.
6
+ # SOURCE: https://ast-grep.github.io/catalog/typescript/no-console-except-catch.html
7
+ # Original catalog rule — replaced with method-level allowlist approach.
8
+ id: no-console-except-error
9
+ language: typescript
10
+ message: "Use a proper logger instead of console.log/debug/trace/info. console.warn and console.error are allowed."
11
+ severity: warning
12
+ note: "Mirrors ESLint no-console with allow: ['warn','error']. Only bans debug-only methods."
13
+ rule:
14
+ pattern: console.$METHOD($$$)
15
+ constraints:
16
+ METHOD:
17
+ regex: 'log|debug|trace|info'
18
+ ignores:
19
+ - '**/*.test.ts'
20
+ - '**/*.spec.ts'
21
+ - '**/*.test-d.ts'
22
+ - '**/test/**'
23
+ - '**/tests/**'
24
+ - '**/__tests__/**'
25
+ - '**/__mocks__/**'
26
+ - '**/scripts/**'
27
+ - 'test-*.ts'