@syrin/cli 1.3.1 → 1.4.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 (150) hide show
  1. package/README.md +36 -0
  2. package/dist/cli/commands/config.d.ts +47 -0
  3. package/dist/cli/commands/config.js +360 -0
  4. package/dist/cli/commands/dev.d.ts +6 -0
  5. package/dist/cli/commands/dev.js +67 -15
  6. package/dist/cli/commands/doctor.js +49 -13
  7. package/dist/cli/commands/init.d.ts +2 -0
  8. package/dist/cli/commands/init.js +89 -18
  9. package/dist/cli/commands/status.d.ts +10 -0
  10. package/dist/cli/commands/status.js +162 -0
  11. package/dist/cli/index.js +211 -12
  12. package/dist/cli/prompts/init-prompt.d.ts +18 -0
  13. package/dist/cli/prompts/init-prompt.js +159 -99
  14. package/dist/cli/utils/command-error-handler.js +2 -5
  15. package/dist/config/env-checker.d.ts +12 -2
  16. package/dist/config/env-checker.js +88 -38
  17. package/dist/config/env-templates.d.ts +15 -0
  18. package/dist/config/env-templates.js +49 -0
  19. package/dist/config/generator.js +17 -0
  20. package/dist/config/global-loader.d.ts +50 -0
  21. package/dist/config/global-loader.js +244 -0
  22. package/dist/config/loader.d.ts +28 -0
  23. package/dist/config/loader.js +95 -9
  24. package/dist/config/merger.d.ts +37 -0
  25. package/dist/config/merger.js +68 -0
  26. package/dist/config/schema.d.ts +26 -1
  27. package/dist/config/schema.js +73 -8
  28. package/dist/config/types.d.ts +19 -0
  29. package/dist/config/types.js +26 -1
  30. package/dist/constants/messages.d.ts +7 -0
  31. package/dist/constants/messages.js +8 -0
  32. package/dist/constants/paths.d.ts +6 -0
  33. package/dist/constants/paths.js +10 -0
  34. package/dist/events/emitter.js +7 -7
  35. package/dist/index.js +0 -0
  36. package/dist/presentation/config-ui.d.ts +34 -0
  37. package/dist/presentation/config-ui.js +139 -0
  38. package/dist/presentation/doctor-ui.d.ts +11 -0
  39. package/dist/presentation/doctor-ui.js +52 -1
  40. package/dist/presentation/init-ui.d.ts +9 -0
  41. package/dist/presentation/init-ui.js +33 -0
  42. package/dist/runtime/analysis/analyser.js +2 -2
  43. package/dist/runtime/analysis/rules/warnings/w104-generic-description.d.ts +1 -1
  44. package/dist/runtime/analysis/rules/warnings/w104-generic-description.js +1 -1
  45. package/dist/runtime/dev/event-mapper.js +19 -3
  46. package/dist/runtime/dev/session.d.ts +4 -0
  47. package/dist/runtime/dev/session.js +52 -3
  48. package/dist/runtime/llm/ollama.js +4 -4
  49. package/dist/runtime/mcp/client/manager.js +3 -3
  50. package/dist/runtime/sandbox/executor.js +5 -5
  51. package/dist/runtime/test/orchestrator.js +4 -4
  52. package/dist/utils/editor.d.ts +37 -0
  53. package/dist/utils/editor.js +137 -0
  54. package/dist/utils/logger.d.ts +24 -6
  55. package/dist/utils/logger.js +51 -8
  56. package/package.json +4 -4
  57. package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.d.ts +0 -22
  58. package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.js +0 -30
  59. package/dist/runtime/analysis/rules/errors/e002-underspecified-input.d.ts +0 -24
  60. package/dist/runtime/analysis/rules/errors/e002-underspecified-input.js +0 -52
  61. package/dist/runtime/analysis/rules/errors/e003-type-mismatch.d.ts +0 -23
  62. package/dist/runtime/analysis/rules/errors/e003-type-mismatch.js +0 -73
  63. package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.d.ts +0 -23
  64. package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.js +0 -47
  65. package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.d.ts +0 -25
  66. package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.js +0 -73
  67. package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.d.ts +0 -22
  68. package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.js +0 -57
  69. package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.d.ts +0 -23
  70. package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.js +0 -56
  71. package/dist/runtime/analysis/rules/errors/e008-circular-dependency.d.ts +0 -22
  72. package/dist/runtime/analysis/rules/errors/e008-circular-dependency.js +0 -84
  73. package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.d.ts +0 -23
  74. package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.js +0 -89
  75. package/dist/runtime/analysis/rules/errors/e010-non-serializable.d.ts +0 -25
  76. package/dist/runtime/analysis/rules/errors/e010-non-serializable.js +0 -46
  77. package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.d.ts +0 -24
  78. package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.js +0 -33
  79. package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.d.ts +0 -39
  80. package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.js +0 -40
  81. package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.d.ts +0 -37
  82. package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.js +0 -34
  83. package/dist/runtime/analysis/rules/errors/e013-output-explosion.d.ts +0 -39
  84. package/dist/runtime/analysis/rules/errors/e013-output-explosion.js +0 -36
  85. package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.d.ts +0 -42
  86. package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.js +0 -46
  87. package/dist/runtime/analysis/rules/errors/e014-output-explosion.d.ts +0 -39
  88. package/dist/runtime/analysis/rules/errors/e014-output-explosion.js +0 -36
  89. package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.d.ts +0 -42
  90. package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.js +0 -46
  91. package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.d.ts +0 -44
  92. package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.js +0 -66
  93. package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.d.ts +0 -43
  94. package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.js +0 -42
  95. package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.d.ts +0 -44
  96. package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.js +0 -66
  97. package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.d.ts +0 -57
  98. package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.js +0 -80
  99. package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.d.ts +0 -43
  100. package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.js +0 -42
  101. package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.d.ts +0 -57
  102. package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.js +0 -80
  103. package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.d.ts +0 -38
  104. package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.js +0 -37
  105. package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.d.ts +0 -38
  106. package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.js +0 -37
  107. package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.d.ts +0 -65
  108. package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.js +0 -109
  109. package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.d.ts +0 -65
  110. package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.js +0 -109
  111. package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.d.ts +0 -22
  112. package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.js +0 -39
  113. package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.d.ts +0 -24
  114. package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.js +0 -40
  115. package/dist/runtime/analysis/rules/warnings/w003-missing-examples.d.ts +0 -22
  116. package/dist/runtime/analysis/rules/warnings/w003-missing-examples.js +0 -84
  117. package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.d.ts +0 -23
  118. package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.js +0 -96
  119. package/dist/runtime/analysis/rules/warnings/w005-generic-description.d.ts +0 -53
  120. package/dist/runtime/analysis/rules/warnings/w005-generic-description.js +0 -108
  121. package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.d.ts +0 -22
  122. package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.js +0 -44
  123. package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.d.ts +0 -23
  124. package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.js +0 -37
  125. package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.d.ts +0 -22
  126. package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.js +0 -97
  127. package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.d.ts +0 -23
  128. package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.js +0 -88
  129. package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.d.ts +0 -22
  130. package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.js +0 -81
  131. package/dist/runtime/analysis/rules/warnings/w021-weak-schema.d.ts +0 -40
  132. package/dist/runtime/analysis/rules/warnings/w021-weak-schema.js +0 -32
  133. package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.d.ts +0 -39
  134. package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.js +0 -36
  135. package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.d.ts +0 -38
  136. package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.js +0 -36
  137. package/dist/runtime/test/dependency-tracker.d.ts +0 -66
  138. package/dist/runtime/test/dependency-tracker.js +0 -80
  139. package/dist/runtime/test/formatters.d.ts +0 -18
  140. package/dist/runtime/test/formatters.js +0 -172
  141. package/dist/runtime/test/input-generator.d.ts +0 -33
  142. package/dist/runtime/test/input-generator.js +0 -498
  143. package/dist/runtime/test/mcp-root-detector.d.ts +0 -31
  144. package/dist/runtime/test/mcp-root-detector.js +0 -105
  145. package/dist/runtime/test/retry-tester.d.ts +0 -44
  146. package/dist/runtime/test/retry-tester.js +0 -103
  147. package/dist/runtime/test/synthetic-input-generator.d.ts +0 -11
  148. package/dist/runtime/test/synthetic-input-generator.js +0 -154
  149. package/dist/runtime/test/test-runner.d.ts +0 -28
  150. package/dist/runtime/test/test-runner.js +0 -55
@@ -1,25 +0,0 @@
1
- /**
2
- * E005: Hard Tool Ambiguity
3
- *
4
- * Condition:
5
- * - Two or more tools:
6
- * - Overlapping descriptions
7
- * - Overlapping schemas
8
- * - No clear differentiator
9
- *
10
- * Why:
11
- * - Tool selection becomes nondeterministic
12
- * - Agent behavior changes across runs/models
13
- */
14
- import { BaseRule } from '../base.js';
15
- import type { AnalysisContext, Diagnostic } from '../../types.js';
16
- declare class E005ToolAmbiguityRule extends BaseRule {
17
- readonly id: "E005";
18
- readonly severity: "error";
19
- readonly ruleName = "Hard Tool Ambiguity";
20
- readonly description = "Multiple tools match the same intent. LLM tool selection is ambiguous.";
21
- check(ctx: AnalysisContext): Diagnostic[];
22
- }
23
- export declare const E005ToolAmbiguity: E005ToolAmbiguityRule;
24
- export {};
25
- //# sourceMappingURL=e005-tool-ambiguity.d.ts.map
@@ -1,73 +0,0 @@
1
- /**
2
- * E005: Hard Tool Ambiguity
3
- *
4
- * Condition:
5
- * - Two or more tools:
6
- * - Overlapping descriptions
7
- * - Overlapping schemas
8
- * - No clear differentiator
9
- *
10
- * Why:
11
- * - Tool selection becomes nondeterministic
12
- * - Agent behavior changes across runs/models
13
- */
14
- import { BaseRule } from '../base.js';
15
- import { ERROR_CODES } from '../error-codes.js';
16
- /**
17
- * Calculate similarity between two sets of tokens.
18
- */
19
- function tokenSimilarity(tokens1, tokens2) {
20
- if (tokens1.size === 0 || tokens2.size === 0) {
21
- return 0.0;
22
- }
23
- const intersection = new Set([...tokens1].filter(t => tokens2.has(t)));
24
- const union = new Set([...tokens1, ...tokens2]);
25
- return intersection.size / union.size;
26
- }
27
- /**
28
- * Calculate schema overlap between two tools.
29
- */
30
- function schemaOverlap(inputs1, inputs2) {
31
- const names1 = new Set(inputs1.map(i => i.name.toLowerCase()));
32
- const names2 = new Set(inputs2.map(i => i.name.toLowerCase()));
33
- const nameOverlapSet = new Set([...names1].filter(n => names2.has(n)));
34
- const nameOverlap = nameOverlapSet.size;
35
- const totalNames = new Set([...names1, ...names2]).size;
36
- if (totalNames === 0) {
37
- return 0.0;
38
- }
39
- return nameOverlap / totalNames;
40
- }
41
- class E005ToolAmbiguityRule extends BaseRule {
42
- id = ERROR_CODES.E005;
43
- severity = 'error';
44
- ruleName = 'Hard Tool Ambiguity';
45
- description = 'Multiple tools match the same intent. LLM tool selection is ambiguous.';
46
- check(ctx) {
47
- const diagnostics = [];
48
- for (let i = 0; i < ctx.tools.length; i++) {
49
- const tool1 = ctx.tools[i];
50
- if (!tool1)
51
- continue;
52
- for (let j = i + 1; j < ctx.tools.length; j++) {
53
- const tool2 = ctx.tools[j];
54
- if (!tool2)
55
- continue;
56
- // Calculate description similarity
57
- const descSimilarity = tokenSimilarity(tool1.descriptionTokens, tool2.descriptionTokens);
58
- // Calculate schema overlap
59
- const inputOverlap = schemaOverlap(tool1.inputs, tool2.inputs);
60
- const outputOverlap = schemaOverlap(tool1.outputs, tool2.outputs);
61
- const schemaOverlapScore = (inputOverlap + outputOverlap) / 2;
62
- // If both description and schema are very similar, it's ambiguous
63
- // Threshold: >0.7 description similarity AND >0.5 schema overlap
64
- if (descSimilarity > 0.7 && schemaOverlapScore > 0.5) {
65
- diagnostics.push(this.createDiagnostic(`Multiple tools match the same intent: "${tool1.name}", "${tool2.name}". LLM tool selection is ambiguous.`, undefined, undefined, `Differentiate "${tool1.name}" and "${tool2.name}" by making their descriptions and schemas more distinct.`));
66
- }
67
- }
68
- }
69
- return diagnostics;
70
- }
71
- }
72
- export const E005ToolAmbiguity = new E005ToolAmbiguityRule();
73
- //# sourceMappingURL=e005-tool-ambiguity.js.map
@@ -1,22 +0,0 @@
1
- /**
2
- * E006: Required Input Not Mentioned in Description
3
- *
4
- * Condition:
5
- * - Required input exists
6
- * - Tool description does not reference it directly or indirectly
7
- *
8
- * Why:
9
- * - LLM does not know parameter exists or matters
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- declare class E006ParamNotInDescriptionRule extends BaseRule {
14
- readonly id: "E006";
15
- readonly severity: "error";
16
- readonly ruleName = "Required Input Not Mentioned in Description";
17
- readonly description = "Required parameter is not referenced in tool description. LLM may not know parameter exists.";
18
- check(ctx: AnalysisContext): Diagnostic[];
19
- }
20
- export declare const E006ParamNotInDescription: E006ParamNotInDescriptionRule;
21
- export {};
22
- //# sourceMappingURL=e006-param-not-in-description.d.ts.map
@@ -1,57 +0,0 @@
1
- /**
2
- * E006: Required Input Not Mentioned in Description
3
- *
4
- * Condition:
5
- * - Required input exists
6
- * - Tool description does not reference it directly or indirectly
7
- *
8
- * Why:
9
- * - LLM does not know parameter exists or matters
10
- */
11
- import { BaseRule } from '../base.js';
12
- import { ERROR_CODES } from '../error-codes.js';
13
- /**
14
- * Check if a parameter name appears in the description (case-insensitive).
15
- */
16
- function isParameterMentioned(description, paramName) {
17
- const descLower = description.toLowerCase();
18
- const paramLower = paramName.toLowerCase();
19
- // Exact match
20
- if (descLower.includes(paramLower)) {
21
- return true;
22
- }
23
- // Check if words from param name appear in description
24
- const paramWords = paramLower.split(/\W+/).filter(w => w.length > 2);
25
- const descWords = descLower.split(/\W+/);
26
- for (const word of paramWords) {
27
- if (descWords.includes(word)) {
28
- return true;
29
- }
30
- }
31
- return false;
32
- }
33
- class E006ParamNotInDescriptionRule extends BaseRule {
34
- id = ERROR_CODES.E006;
35
- severity = 'error';
36
- ruleName = 'Required Input Not Mentioned in Description';
37
- description = 'Required parameter is not referenced in tool description. LLM may not know parameter exists.';
38
- check(ctx) {
39
- const diagnostics = [];
40
- for (const tool of ctx.tools) {
41
- const description = tool.description || '';
42
- for (const input of tool.inputs) {
43
- // Only check required inputs
44
- if (!input.required) {
45
- continue;
46
- }
47
- // Check if parameter is mentioned in description
48
- if (!isParameterMentioned(description, input.name)) {
49
- diagnostics.push(this.createDiagnostic(`Required parameter "${input.name}" is not referenced in "${tool.name}" description.`, tool.name, input.name, `Mention "${input.name}" in the tool description so the LLM knows it exists and is required.`));
50
- }
51
- }
52
- }
53
- return diagnostics;
54
- }
55
- }
56
- export const E006ParamNotInDescription = new E006ParamNotInDescriptionRule();
57
- //# sourceMappingURL=e006-param-not-in-description.js.map
@@ -1,23 +0,0 @@
1
- /**
2
- * E007: Output Used Downstream but Not Guaranteed
3
- *
4
- * Condition:
5
- * - Tool output is optional / nullable
6
- * - Used downstream without fallback
7
- *
8
- * Why:
9
- * - Silent null propagation
10
- * - Hard-to-debug failures
11
- */
12
- import { BaseRule } from '../base.js';
13
- import type { AnalysisContext, Diagnostic } from '../../types.js';
14
- declare class E007OutputNotGuaranteedRule extends BaseRule {
15
- readonly id: "E007";
16
- readonly severity: "error";
17
- readonly ruleName = "Output Used Downstream but Not Guaranteed";
18
- readonly description = "Output of tool is not guaranteed, but is used by downstream tools without fallback.";
19
- check(ctx: AnalysisContext): Diagnostic[];
20
- }
21
- export declare const E007OutputNotGuaranteed: E007OutputNotGuaranteedRule;
22
- export {};
23
- //# sourceMappingURL=e007-output-not-guaranteed.d.ts.map
@@ -1,56 +0,0 @@
1
- /**
2
- * E007: Output Used Downstream but Not Guaranteed
3
- *
4
- * Condition:
5
- * - Tool output is optional / nullable
6
- * - Used downstream without fallback
7
- *
8
- * Why:
9
- * - Silent null propagation
10
- * - Hard-to-debug failures
11
- */
12
- import { BaseRule } from '../base.js';
13
- import { ERROR_CODES } from '../error-codes.js';
14
- class E007OutputNotGuaranteedRule extends BaseRule {
15
- id = ERROR_CODES.E007;
16
- severity = 'error';
17
- ruleName = 'Output Used Downstream but Not Guaranteed';
18
- description = 'Output of tool is not guaranteed, but is used by downstream tools without fallback.';
19
- check(ctx) {
20
- const diagnostics = [];
21
- // Only check high-confidence dependencies (>= 0.8)
22
- const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
23
- for (const dep of highConfidenceDeps) {
24
- // Find the output field
25
- const fromTool = ctx.indexes.toolIndex.get(dep.fromTool.toLowerCase());
26
- if (!fromTool) {
27
- continue;
28
- }
29
- const fromField = fromTool.outputs.find(f => f.name === dep.fromField);
30
- if (!fromField) {
31
- continue;
32
- }
33
- // Find the downstream input field
34
- const toTool = ctx.indexes.toolIndex.get(dep.toTool.toLowerCase());
35
- if (!toTool) {
36
- continue;
37
- }
38
- const toField = toTool.inputs.find(f => f.name === dep.toField);
39
- if (!toField) {
40
- continue;
41
- }
42
- // Check if output is not guaranteed and downstream requires it without fallback
43
- // Optional upstream always triggers when downstream requires it
44
- // Nullable upstream only triggers when downstream is required AND not nullable (can't handle null)
45
- if ((!fromField.required && toField.required === true) ||
46
- (fromField.nullable === true &&
47
- toField.required === true &&
48
- toField.nullable !== true)) {
49
- diagnostics.push(this.createDiagnostic(`Output of "${dep.fromTool}" (field: "${dep.fromField}") is nullable but is used by "${dep.toTool}" without fallback.`, dep.fromTool, dep.fromField, `Make the output of "${dep.fromTool}" non-nullable, or ensure "${dep.toTool}" handles null values.`));
50
- }
51
- }
52
- return diagnostics;
53
- }
54
- }
55
- export const E007OutputNotGuaranteed = new E007OutputNotGuaranteedRule();
56
- //# sourceMappingURL=e007-output-not-guaranteed.js.map
@@ -1,22 +0,0 @@
1
- /**
2
- * E008: Circular Tool Dependency
3
- *
4
- * Condition:
5
- * - Tool dependency graph contains a cycle
6
- *
7
- * Why:
8
- * - LLMs cannot reason about cycles
9
- * - Execution becomes undefined
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- declare class E008CircularDependencyRule extends BaseRule {
14
- readonly id: "E008";
15
- readonly severity: "error";
16
- readonly ruleName = "Circular Tool Dependency";
17
- readonly description = "Circular dependency detected between tools. Execution becomes undefined.";
18
- check(ctx: AnalysisContext): Diagnostic[];
19
- }
20
- export declare const E008CircularDependency: E008CircularDependencyRule;
21
- export {};
22
- //# sourceMappingURL=e008-circular-dependency.d.ts.map
@@ -1,84 +0,0 @@
1
- /**
2
- * E008: Circular Tool Dependency
3
- *
4
- * Condition:
5
- * - Tool dependency graph contains a cycle
6
- *
7
- * Why:
8
- * - LLMs cannot reason about cycles
9
- * - Execution becomes undefined
10
- */
11
- import { BaseRule } from '../base.js';
12
- import { ERROR_CODES } from '../error-codes.js';
13
- /**
14
- * Detect cycles in the dependency graph using DFS.
15
- */
16
- function detectCycles(dependencies) {
17
- // Build adjacency list
18
- const graph = new Map();
19
- for (const dep of dependencies) {
20
- if (!graph.has(dep.fromTool)) {
21
- graph.set(dep.fromTool, []);
22
- }
23
- graph.get(dep.fromTool).push(dep.toTool);
24
- }
25
- const cycles = [];
26
- const visited = new Set();
27
- const recStack = new Set();
28
- function dfs(tool, path) {
29
- visited.add(tool);
30
- recStack.add(tool);
31
- path.push(tool);
32
- const neighbors = graph.get(tool) || [];
33
- for (const neighbor of neighbors) {
34
- if (!visited.has(neighbor)) {
35
- dfs(neighbor, [...path]);
36
- }
37
- else if (recStack.has(neighbor)) {
38
- // Found a cycle
39
- const cycleStart = path.indexOf(neighbor);
40
- if (cycleStart !== -1) {
41
- cycles.push([...path.slice(cycleStart), neighbor]);
42
- }
43
- }
44
- }
45
- recStack.delete(tool);
46
- }
47
- // Check all tools
48
- for (const dep of dependencies) {
49
- if (!visited.has(dep.fromTool)) {
50
- dfs(dep.fromTool, []);
51
- }
52
- }
53
- return cycles;
54
- }
55
- class E008CircularDependencyRule extends BaseRule {
56
- id = ERROR_CODES.E008;
57
- severity = 'error';
58
- ruleName = 'Circular Tool Dependency';
59
- description = 'Circular dependency detected between tools. Execution becomes undefined.';
60
- check(ctx) {
61
- const diagnostics = [];
62
- // Only check high-confidence dependencies (>= 0.8) for cycles
63
- const highConfidenceDeps = ctx.dependencies.filter(d => d.confidence >= 0.8);
64
- if (highConfidenceDeps.length === 0) {
65
- return diagnostics;
66
- }
67
- const cycles = detectCycles(highConfidenceDeps);
68
- // Report each cycle
69
- const reportedCycles = new Set();
70
- for (const cycle of cycles) {
71
- // Create a canonical representation of the cycle (sort a copy to avoid mutating original)
72
- const cycleKey = [...cycle].sort().join(' → ');
73
- if (reportedCycles.has(cycleKey)) {
74
- continue;
75
- }
76
- reportedCycles.add(cycleKey);
77
- const cycleStr = cycle.join(' → ');
78
- diagnostics.push(this.createDiagnostic(`Circular dependency detected between tools: ${cycleStr}.`, undefined, undefined, `Break the cycle by removing or restructuring dependencies between: ${cycle.join(', ')}.`));
79
- }
80
- return diagnostics;
81
- }
82
- }
83
- export const E008CircularDependency = new E008CircularDependencyRule();
84
- //# sourceMappingURL=e008-circular-dependency.js.map
@@ -1,23 +0,0 @@
1
- /**
2
- * E009: Tool Depends on User Input Indirectly
3
- *
4
- * Condition:
5
- * - Tool expects data
6
- * - Only source is implicit user memory / conversation
7
- * - No explicit tool provides it
8
- *
9
- * Why:
10
- * - Relies on hallucinated context
11
- */
12
- import { BaseRule } from '../base.js';
13
- import type { AnalysisContext, Diagnostic } from '../../types.js';
14
- declare class E009ImplicitUserInputRule extends BaseRule {
15
- readonly id: "E009";
16
- readonly severity: "error";
17
- readonly ruleName = "Tool Depends on User Input Indirectly";
18
- readonly description = "Tool depends on implicit user context with no explicit source.";
19
- check(ctx: AnalysisContext): Diagnostic[];
20
- }
21
- export declare const E009ImplicitUserInput: E009ImplicitUserInputRule;
22
- export {};
23
- //# sourceMappingURL=e009-implicit-user-input.d.ts.map
@@ -1,89 +0,0 @@
1
- /**
2
- * E009: Tool Depends on User Input Indirectly
3
- *
4
- * Condition:
5
- * - Tool expects data
6
- * - Only source is implicit user memory / conversation
7
- * - No explicit tool provides it
8
- *
9
- * Why:
10
- * - Relies on hallucinated context
11
- */
12
- import { BaseRule } from '../base.js';
13
- import { ERROR_CODES } from '../error-codes.js';
14
- class E009ImplicitUserInputRule extends BaseRule {
15
- id = ERROR_CODES.E009;
16
- severity = 'error';
17
- ruleName = 'Tool Depends on User Input Indirectly';
18
- description = 'Tool depends on implicit user context with no explicit source.';
19
- check(ctx) {
20
- const diagnostics = [];
21
- for (const tool of ctx.tools) {
22
- for (const input of tool.inputs) {
23
- if (!input.required) {
24
- continue;
25
- }
26
- // Check if there's any tool that provides this input
27
- // Look for output fields with similar names
28
- const fieldName = input.name.toLowerCase();
29
- let hasExplicitSource = false;
30
- for (const otherTool of ctx.tools) {
31
- if (otherTool.name === tool.name) {
32
- continue;
33
- }
34
- for (const output of otherTool.outputs) {
35
- const outputName = output.name.toLowerCase();
36
- // Check for exact match or token-based match
37
- if (outputName === fieldName) {
38
- hasExplicitSource = true;
39
- break;
40
- }
41
- // Token-based matching: split on non-alphanumeric and camelCase boundaries
42
- const fieldTokens = new Set(fieldName
43
- .split(/[^\w]+|(?<=[a-z])(?=[A-Z])/)
44
- .filter(t => t.length > 0));
45
- const outputTokens = new Set(outputName
46
- .split(/[^\w]+|(?<=[a-z])(?=[A-Z])/)
47
- .filter(t => t.length > 0));
48
- // Check for token intersection
49
- const hasTokenMatch = Array.from(fieldTokens).some(token => outputTokens.has(token) && token.length >= 3) ||
50
- Array.from(outputTokens).some(token => fieldTokens.has(token) && token.length >= 3);
51
- if (hasTokenMatch) {
52
- hasExplicitSource = true;
53
- break;
54
- }
55
- }
56
- if (hasExplicitSource) {
57
- break;
58
- }
59
- }
60
- // Also check dependencies
61
- if (!hasExplicitSource) {
62
- const hasDependency = ctx.dependencies.some(d => d.toTool === tool.name &&
63
- d.toField === input.name &&
64
- d.confidence >= 0.6);
65
- if (hasDependency) {
66
- hasExplicitSource = true;
67
- }
68
- }
69
- // If no explicit source found and input name suggests user data
70
- const userDataIndicators = [
71
- 'user',
72
- 'person',
73
- 'name',
74
- 'email',
75
- 'location',
76
- 'address',
77
- 'preference',
78
- ];
79
- const looksLikeUserData = userDataIndicators.some(indicator => fieldName.includes(indicator));
80
- if (!hasExplicitSource && looksLikeUserData) {
81
- diagnostics.push(this.createDiagnostic(`Tool "${tool.name}" depends on implicit user context (parameter: "${input.name}") with no explicit source.`, tool.name, input.name, `Create an explicit tool to provide "${input.name}", or ensure it's clearly documented as user input.`));
82
- }
83
- }
84
- }
85
- return diagnostics;
86
- }
87
- }
88
- export const E009ImplicitUserInput = new E009ImplicitUserInputRule();
89
- //# sourceMappingURL=e009-implicit-user-input.js.map
@@ -1,25 +0,0 @@
1
- /**
2
- * E010: Non-Serializable Output
3
- *
4
- * Condition:
5
- * - Output schema contains:
6
- * - functions
7
- * - class instances
8
- * - unsupported types
9
- *
10
- * Why:
11
- * - Breaks MCP contract
12
- * - Breaks recording & replay later
13
- */
14
- import { BaseRule } from '../base.js';
15
- import type { AnalysisContext, Diagnostic } from '../../types.js';
16
- declare class E010NonSerializableRule extends BaseRule {
17
- readonly id: "E010";
18
- readonly severity: "error";
19
- readonly ruleName = "Non-Serializable Output";
20
- readonly description = "Output of tool is not serializable. Breaks MCP contract.";
21
- check(ctx: AnalysisContext): Diagnostic[];
22
- }
23
- export declare const E010NonSerializable: E010NonSerializableRule;
24
- export {};
25
- //# sourceMappingURL=e010-non-serializable.d.ts.map
@@ -1,46 +0,0 @@
1
- /**
2
- * E010: Non-Serializable Output
3
- *
4
- * Condition:
5
- * - Output schema contains:
6
- * - functions
7
- * - class instances
8
- * - unsupported types
9
- *
10
- * Why:
11
- * - Breaks MCP contract
12
- * - Breaks recording & replay later
13
- */
14
- import { BaseRule } from '../base.js';
15
- import { ERROR_CODES } from '../error-codes.js';
16
- /**
17
- * Check if a type is non-serializable.
18
- */
19
- function isNonSerializableType(type) {
20
- const nonSerializableTypes = new Set([
21
- 'function',
22
- 'undefined',
23
- 'symbol',
24
- 'bigint',
25
- ]);
26
- return nonSerializableTypes.has(type.toLowerCase());
27
- }
28
- class E010NonSerializableRule extends BaseRule {
29
- id = ERROR_CODES.E010;
30
- severity = 'error';
31
- ruleName = 'Non-Serializable Output';
32
- description = 'Output of tool is not serializable. Breaks MCP contract.';
33
- check(ctx) {
34
- const diagnostics = [];
35
- for (const tool of ctx.tools) {
36
- for (const output of tool.outputs) {
37
- if (isNonSerializableType(output.type)) {
38
- diagnostics.push(this.createDiagnostic(`Output of "${tool.name}" (field: "${output.name}") has non-serializable type "${output.type}".`, tool.name, output.name, `Change the output type to a serializable type (string, number, boolean, object, array).`));
39
- }
40
- }
41
- }
42
- return diagnostics;
43
- }
44
- }
45
- export const E010NonSerializable = new E010NonSerializableRule();
46
- //# sourceMappingURL=e010-non-serializable.js.map
@@ -1,24 +0,0 @@
1
- /**
2
- * E011: Missing Tool Description
3
- *
4
- * Condition:
5
- * - Tool has no description OR
6
- * - Tool description is empty or only whitespace
7
- *
8
- * Why:
9
- * - LLM cannot understand what the tool does
10
- * - Tool selection becomes ambiguous
11
- * - Critical for tool discovery and usage
12
- */
13
- import { BaseRule } from '../base.js';
14
- import type { AnalysisContext, Diagnostic } from '../../types.js';
15
- declare class E011MissingToolDescriptionRule extends BaseRule {
16
- readonly id: "E011";
17
- readonly severity: "error";
18
- readonly ruleName = "Missing Tool Description";
19
- readonly description = "Tool is missing a description. LLM cannot understand what the tool does.";
20
- check(ctx: AnalysisContext): Diagnostic[];
21
- }
22
- export declare const E011MissingToolDescription: E011MissingToolDescriptionRule;
23
- export {};
24
- //# sourceMappingURL=e011-missing-tool-description.d.ts.map
@@ -1,33 +0,0 @@
1
- /**
2
- * E011: Missing Tool Description
3
- *
4
- * Condition:
5
- * - Tool has no description OR
6
- * - Tool description is empty or only whitespace
7
- *
8
- * Why:
9
- * - LLM cannot understand what the tool does
10
- * - Tool selection becomes ambiguous
11
- * - Critical for tool discovery and usage
12
- */
13
- import { BaseRule } from '../base.js';
14
- import { ERROR_CODES } from '../error-codes.js';
15
- class E011MissingToolDescriptionRule extends BaseRule {
16
- id = ERROR_CODES.E011;
17
- severity = 'error';
18
- ruleName = 'Missing Tool Description';
19
- description = 'Tool is missing a description. LLM cannot understand what the tool does.';
20
- check(ctx) {
21
- const diagnostics = [];
22
- for (const tool of ctx.tools) {
23
- // Check if description is missing or empty/whitespace only
24
- const hasDescription = Boolean(tool.description && tool.description.trim().length > 0);
25
- if (!hasDescription) {
26
- diagnostics.push(this.createDiagnostic(`Tool "${tool.name}" is missing a description.`, tool.name, undefined, `Add a clear description to "${tool.name}" explaining what it does, what inputs it expects, and what it returns.`));
27
- }
28
- }
29
- return diagnostics;
30
- }
31
- }
32
- export const E011MissingToolDescription = new E011MissingToolDescriptionRule();
33
- //# sourceMappingURL=e011-missing-tool-description.js.map
@@ -1,39 +0,0 @@
1
- /**
2
- * E012: Side Effect Detected
3
- *
4
- * Condition: Tool attempts filesystem writes to project files (not temp directory)
5
- *
6
- * Why this is fatal:
7
- * - Tools should not mutate project state
8
- * - Breaks isolation and testability
9
- * - Makes behavior unpredictable
10
- */
11
- import { BaseRule } from '../base.js';
12
- import type { AnalysisContext, Diagnostic } from '../../types.js';
13
- /**
14
- * Context for behavioral validation results.
15
- * This extends the static analysis context with runtime observation data.
16
- */
17
- export interface BehavioralContext {
18
- /** Tool name */
19
- toolName: string;
20
- /** Side effects detected */
21
- sideEffects: Array<{
22
- operation: string;
23
- path: string;
24
- }>;
25
- }
26
- declare class E012SideEffectDetectedRule extends BaseRule {
27
- readonly id: "E012";
28
- readonly severity: "error";
29
- readonly ruleName = "Side Effect Detected";
30
- readonly description = "Tool attempted filesystem write to project files. Tools should not mutate project state.";
31
- check(_ctx: AnalysisContext): Diagnostic[];
32
- /**
33
- * Check with behavioral context (called from test orchestrator).
34
- */
35
- checkWithBehavioralContext(behavioralCtx: BehavioralContext): Diagnostic[];
36
- }
37
- export declare const E012SideEffectDetected: E012SideEffectDetectedRule;
38
- export {};
39
- //# sourceMappingURL=e012-side-effect-detected.d.ts.map