@cloudstreamsoftware/claude-tools 1.0.0 → 1.1.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.
- package/README.md +152 -37
- package/agents/INDEX.md +183 -0
- package/agents/architect.md +247 -0
- package/agents/build-error-resolver.md +555 -0
- package/agents/catalyst-deployer.md +132 -0
- package/agents/code-reviewer.md +121 -0
- package/agents/compliance-auditor.md +148 -0
- package/agents/creator-architect.md +395 -0
- package/agents/deluge-reviewer.md +98 -0
- package/agents/doc-updater.md +471 -0
- package/agents/e2e-runner.md +711 -0
- package/agents/planner.md +122 -0
- package/agents/refactor-cleaner.md +309 -0
- package/agents/security-reviewer.md +582 -0
- package/agents/tdd-guide.md +302 -0
- package/config/versions.json +63 -0
- package/dist/hooks/hooks.json +209 -0
- package/dist/index.js +47 -0
- package/dist/lib/asset-value.js +609 -0
- package/dist/lib/client-manager.js +300 -0
- package/dist/lib/command-matcher.js +242 -0
- package/dist/lib/cross-session-patterns.js +754 -0
- package/dist/lib/intent-classifier.js +1075 -0
- package/dist/lib/package-manager.js +374 -0
- package/dist/lib/recommendation-engine.js +597 -0
- package/dist/lib/session-memory.js +489 -0
- package/dist/lib/skill-effectiveness.js +486 -0
- package/dist/lib/skill-matcher.js +595 -0
- package/dist/lib/tutorial-metrics.js +242 -0
- package/dist/lib/tutorial-progress.js +209 -0
- package/dist/lib/tutorial-renderer.js +431 -0
- package/dist/lib/utils.js +380 -0
- package/dist/lib/verify-formatter.js +143 -0
- package/dist/lib/workflow-state.js +249 -0
- package/hooks/hooks.json +209 -0
- package/package.json +5 -1
- package/scripts/aggregate-sessions.js +290 -0
- package/scripts/branch-name-validator.js +291 -0
- package/scripts/build.js +101 -0
- package/scripts/commands/client-switch.js +231 -0
- package/scripts/deprecate-skill.js +610 -0
- package/scripts/diagnose.js +324 -0
- package/scripts/doc-freshness.js +168 -0
- package/scripts/generate-weekly-digest.js +393 -0
- package/scripts/health-check.js +270 -0
- package/scripts/hooks/credential-check.js +101 -0
- package/scripts/hooks/evaluate-session.js +81 -0
- package/scripts/hooks/pre-compact.js +66 -0
- package/scripts/hooks/prompt-analyzer.js +276 -0
- package/scripts/hooks/prompt-router.js +422 -0
- package/scripts/hooks/quality-gate-enforcer.js +371 -0
- package/scripts/hooks/session-end.js +156 -0
- package/scripts/hooks/session-start.js +195 -0
- package/scripts/hooks/skill-injector.js +333 -0
- package/scripts/hooks/suggest-compact.js +58 -0
- package/scripts/lib/asset-value.js +609 -0
- package/scripts/lib/client-manager.js +300 -0
- package/scripts/lib/command-matcher.js +242 -0
- package/scripts/lib/cross-session-patterns.js +754 -0
- package/scripts/lib/intent-classifier.js +1075 -0
- package/scripts/lib/package-manager.js +374 -0
- package/scripts/lib/recommendation-engine.js +597 -0
- package/scripts/lib/session-memory.js +489 -0
- package/scripts/lib/skill-effectiveness.js +486 -0
- package/scripts/lib/skill-matcher.js +595 -0
- package/scripts/lib/tutorial-metrics.js +242 -0
- package/scripts/lib/tutorial-progress.js +209 -0
- package/scripts/lib/tutorial-renderer.js +431 -0
- package/scripts/lib/utils.js +380 -0
- package/scripts/lib/verify-formatter.js +143 -0
- package/scripts/lib/workflow-state.js +249 -0
- package/scripts/onboard.js +363 -0
- package/scripts/quarterly-report.js +692 -0
- package/scripts/setup-package-manager.js +204 -0
- package/scripts/sync-upstream.js +391 -0
- package/scripts/test.js +108 -0
- package/scripts/tutorial-runner.js +351 -0
- package/scripts/validate-all.js +201 -0
- package/scripts/verifiers/agents.js +245 -0
- package/scripts/verifiers/config.js +186 -0
- package/scripts/verifiers/environment.js +123 -0
- package/scripts/verifiers/hooks.js +188 -0
- package/scripts/verifiers/index.js +38 -0
- package/scripts/verifiers/persistence.js +140 -0
- package/scripts/verifiers/plugin.js +215 -0
- package/scripts/verifiers/skills.js +209 -0
- package/scripts/verify-setup.js +164 -0
- package/skills/INDEX.md +157 -0
- package/skills/backend-patterns/SKILL.md +586 -0
- package/skills/backend-patterns/catalyst-patterns.md +128 -0
- package/skills/bigquery-patterns/SKILL.md +27 -0
- package/skills/bigquery-patterns/performance-optimization.md +518 -0
- package/skills/bigquery-patterns/query-patterns.md +372 -0
- package/skills/bigquery-patterns/schema-design.md +78 -0
- package/skills/cloudstream-project-template/SKILL.md +20 -0
- package/skills/cloudstream-project-template/structure.md +65 -0
- package/skills/coding-standards/SKILL.md +524 -0
- package/skills/coding-standards/deluge-standards.md +83 -0
- package/skills/compliance-patterns/SKILL.md +28 -0
- package/skills/compliance-patterns/hipaa/audit-requirements.md +251 -0
- package/skills/compliance-patterns/hipaa/baa-process.md +298 -0
- package/skills/compliance-patterns/hipaa/data-archival-strategy.md +387 -0
- package/skills/compliance-patterns/hipaa/phi-handling.md +52 -0
- package/skills/compliance-patterns/pci-dss/saq-a-requirements.md +307 -0
- package/skills/compliance-patterns/pci-dss/tokenization-patterns.md +382 -0
- package/skills/compliance-patterns/pci-dss/zoho-checkout-patterns.md +56 -0
- package/skills/compliance-patterns/soc2/access-controls.md +344 -0
- package/skills/compliance-patterns/soc2/audit-logging.md +458 -0
- package/skills/compliance-patterns/soc2/change-management.md +403 -0
- package/skills/compliance-patterns/soc2/deluge-execution-logging.md +407 -0
- package/skills/consultancy-workflows/SKILL.md +19 -0
- package/skills/consultancy-workflows/client-isolation.md +21 -0
- package/skills/consultancy-workflows/documentation-automation.md +454 -0
- package/skills/consultancy-workflows/handoff-procedures.md +257 -0
- package/skills/consultancy-workflows/knowledge-capture.md +513 -0
- package/skills/consultancy-workflows/time-tracking.md +26 -0
- package/skills/continuous-learning/SKILL.md +84 -0
- package/skills/continuous-learning/config.json +18 -0
- package/skills/continuous-learning/evaluate-session.sh +60 -0
- package/skills/continuous-learning-v2/SKILL.md +126 -0
- package/skills/continuous-learning-v2/config.json +61 -0
- package/skills/frontend-patterns/SKILL.md +635 -0
- package/skills/frontend-patterns/zoho-widget-patterns.md +103 -0
- package/skills/gcp-data-engineering/SKILL.md +36 -0
- package/skills/gcp-data-engineering/bigquery/performance-optimization.md +337 -0
- package/skills/gcp-data-engineering/dataflow/error-handling.md +496 -0
- package/skills/gcp-data-engineering/dataflow/pipeline-patterns.md +444 -0
- package/skills/gcp-data-engineering/dbt/model-organization.md +63 -0
- package/skills/gcp-data-engineering/dbt/testing-patterns.md +503 -0
- package/skills/gcp-data-engineering/medallion-architecture/bronze-layer.md +60 -0
- package/skills/gcp-data-engineering/medallion-architecture/gold-layer.md +311 -0
- package/skills/gcp-data-engineering/medallion-architecture/layer-transitions.md +517 -0
- package/skills/gcp-data-engineering/medallion-architecture/silver-layer.md +305 -0
- package/skills/gcp-data-engineering/zoho-to-gcp/data-extraction.md +543 -0
- package/skills/gcp-data-engineering/zoho-to-gcp/real-time-vs-batch.md +337 -0
- package/skills/security-review/SKILL.md +498 -0
- package/skills/security-review/compliance-checklist.md +53 -0
- package/skills/strategic-compact/SKILL.md +67 -0
- package/skills/tdd-workflow/SKILL.md +413 -0
- package/skills/tdd-workflow/zoho-testing.md +124 -0
- package/skills/tutorial/SKILL.md +249 -0
- package/skills/tutorial/docs/ACCESSIBILITY.md +169 -0
- package/skills/tutorial/lessons/00-philosophy-and-workflow.md +198 -0
- package/skills/tutorial/lessons/01-basics.md +81 -0
- package/skills/tutorial/lessons/02-training.md +86 -0
- package/skills/tutorial/lessons/03-commands.md +109 -0
- package/skills/tutorial/lessons/04-workflows.md +115 -0
- package/skills/tutorial/lessons/05-compliance.md +116 -0
- package/skills/tutorial/lessons/06-zoho.md +121 -0
- package/skills/tutorial/lessons/07-hooks-system.md +277 -0
- package/skills/tutorial/lessons/08-mcp-servers.md +316 -0
- package/skills/tutorial/lessons/09-client-management.md +215 -0
- package/skills/tutorial/lessons/10-testing-e2e.md +260 -0
- package/skills/tutorial/lessons/11-skills-deep-dive.md +272 -0
- package/skills/tutorial/lessons/12-rules-system.md +326 -0
- package/skills/tutorial/lessons/13-golden-standard-graduation.md +213 -0
- package/skills/tutorial/lessons/14-fork-setup-and-sync.md +312 -0
- package/skills/tutorial/lessons/15-living-examples-system.md +221 -0
- package/skills/tutorial/tracks/accelerated/README.md +134 -0
- package/skills/tutorial/tracks/accelerated/assessment/checkpoint-1.md +161 -0
- package/skills/tutorial/tracks/accelerated/assessment/checkpoint-2.md +175 -0
- package/skills/tutorial/tracks/accelerated/day-1-core-concepts.md +234 -0
- package/skills/tutorial/tracks/accelerated/day-2-essential-commands.md +270 -0
- package/skills/tutorial/tracks/accelerated/day-3-workflow-mastery.md +305 -0
- package/skills/tutorial/tracks/accelerated/day-4-compliance-zoho.md +304 -0
- package/skills/tutorial/tracks/accelerated/day-5-hooks-skills.md +344 -0
- package/skills/tutorial/tracks/accelerated/day-6-client-testing.md +386 -0
- package/skills/tutorial/tracks/accelerated/day-7-graduation.md +369 -0
- package/skills/zoho-patterns/CHANGELOG.md +108 -0
- package/skills/zoho-patterns/SKILL.md +446 -0
- package/skills/zoho-patterns/analytics/dashboard-patterns.md +352 -0
- package/skills/zoho-patterns/analytics/zoho-to-bigquery-pipeline.md +427 -0
- package/skills/zoho-patterns/catalyst/appsail-deployment.md +349 -0
- package/skills/zoho-patterns/catalyst/context-close-patterns.md +354 -0
- package/skills/zoho-patterns/catalyst/cron-batch-processing.md +374 -0
- package/skills/zoho-patterns/catalyst/function-patterns.md +439 -0
- package/skills/zoho-patterns/creator/form-design.md +304 -0
- package/skills/zoho-patterns/creator/publish-api-patterns.md +313 -0
- package/skills/zoho-patterns/creator/widget-integration.md +306 -0
- package/skills/zoho-patterns/creator/workflow-automation.md +253 -0
- package/skills/zoho-patterns/deluge/api-patterns.md +468 -0
- package/skills/zoho-patterns/deluge/batch-processing.md +403 -0
- package/skills/zoho-patterns/deluge/cross-app-integration.md +356 -0
- package/skills/zoho-patterns/deluge/error-handling.md +423 -0
- package/skills/zoho-patterns/deluge/syntax-reference.md +65 -0
- package/skills/zoho-patterns/integration/cors-proxy-architecture.md +426 -0
- package/skills/zoho-patterns/integration/crm-books-native-sync.md +277 -0
- package/skills/zoho-patterns/integration/oauth-token-management.md +461 -0
- package/skills/zoho-patterns/integration/zoho-flow-patterns.md +334 -0
|
@@ -0,0 +1,1075 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intent Classifier - Semantic Intent Detection for Prompt Routing
|
|
3
|
+
*
|
|
4
|
+
* Detects user intent from natural language prompts and returns
|
|
5
|
+
* confidence scores for routing to appropriate commands/agents.
|
|
6
|
+
*
|
|
7
|
+
* Performance Target: <10ms per classification
|
|
8
|
+
*
|
|
9
|
+
* @module intent-classifier
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Intent types supported by the classifier
|
|
14
|
+
*/
|
|
15
|
+
const INTENT_TYPES = {
|
|
16
|
+
PLANNING: 'PLANNING',
|
|
17
|
+
TESTING: 'TESTING',
|
|
18
|
+
REVIEW: 'REVIEW',
|
|
19
|
+
SECURITY: 'SECURITY',
|
|
20
|
+
COMPLIANCE: 'COMPLIANCE',
|
|
21
|
+
ZOHO: 'ZOHO',
|
|
22
|
+
DEPLOY: 'DEPLOY',
|
|
23
|
+
REFACTOR: 'REFACTOR',
|
|
24
|
+
DEBUG: 'DEBUG',
|
|
25
|
+
// New intent types for full documentation alignment
|
|
26
|
+
LEARNING: 'LEARNING',
|
|
27
|
+
SETUP: 'SETUP',
|
|
28
|
+
HANDOFF: 'HANDOFF',
|
|
29
|
+
E2E: 'E2E',
|
|
30
|
+
DOCUMENTATION: 'DOCUMENTATION',
|
|
31
|
+
UNKNOWN: 'UNKNOWN',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Confidence thresholds
|
|
36
|
+
*/
|
|
37
|
+
const THRESHOLDS = {
|
|
38
|
+
AUTO_CONVERT: 0.75, // High confidence - auto-convert without asking (lowered for better UX)
|
|
39
|
+
SUGGEST: 0.5, // Medium confidence - suggest but ask
|
|
40
|
+
MINIMUM: 0.25, // Below this, classify as UNKNOWN (lowered to support single-typo matches)
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Intent patterns with weighted keywords and phrases
|
|
45
|
+
*
|
|
46
|
+
* Structure:
|
|
47
|
+
* - keywords: Single words that indicate intent (lower weight)
|
|
48
|
+
* - phrases: Multi-word patterns that strongly indicate intent (higher weight)
|
|
49
|
+
* - priority: Used for tie-breaking when multiple intents match
|
|
50
|
+
*/
|
|
51
|
+
const INTENT_PATTERNS = {
|
|
52
|
+
PLANNING: {
|
|
53
|
+
keywords: [
|
|
54
|
+
'plan',
|
|
55
|
+
'design',
|
|
56
|
+
'architect',
|
|
57
|
+
'approach',
|
|
58
|
+
'strategy',
|
|
59
|
+
'structure',
|
|
60
|
+
'organize',
|
|
61
|
+
'figure',
|
|
62
|
+
'think',
|
|
63
|
+
// Development verbs - critical for feature requests
|
|
64
|
+
'create',
|
|
65
|
+
'implement',
|
|
66
|
+
'build',
|
|
67
|
+
'develop',
|
|
68
|
+
'make',
|
|
69
|
+
'add',
|
|
70
|
+
'integrate',
|
|
71
|
+
'feature',
|
|
72
|
+
'module',
|
|
73
|
+
'component',
|
|
74
|
+
],
|
|
75
|
+
phrases: [
|
|
76
|
+
/\b(how should|what's the best way|best approach)\b/i,
|
|
77
|
+
/\b(plan (for|the|this|a|an))\b/i,
|
|
78
|
+
/\b(need to plan)\b/i,
|
|
79
|
+
/\b(design (the|a|an))\b/i,
|
|
80
|
+
/\b(architect (a|an|the))\b/i,
|
|
81
|
+
/\b(help me (plan|design|architect))\b/i,
|
|
82
|
+
/\b(implementation strategy)\b/i,
|
|
83
|
+
/\b(figure out (how|the))\b/i,
|
|
84
|
+
/\bapproach (for|this|the)\b/i,
|
|
85
|
+
/\blets plan\b/i,
|
|
86
|
+
/\bwant to architect\b/i,
|
|
87
|
+
// Development verb phrases - critical for feature requests
|
|
88
|
+
/\b(add\s+(a\s+)?(feature|functionality|capability|endpoint|module|component))\b/i,
|
|
89
|
+
/\b(add\s+(a\s+)?(new\s+)?[a-z]+\s+(feature|to))\b/i,
|
|
90
|
+
/\b(create\s+(a|an|the|new))\b/i,
|
|
91
|
+
/\b(implement\s+(the|a|an|new))\b/i,
|
|
92
|
+
/\b(build\s+(the|a|an|new))\b/i,
|
|
93
|
+
/\b(develop\s+(the|a|an))\b/i,
|
|
94
|
+
/\b(integrate\s+(with|the|a))\b/i,
|
|
95
|
+
/\b(need\s+to\s+(create|build|implement|add|develop))\b/i,
|
|
96
|
+
/\b(help\s+me\s+(create|build|implement|add|develop))\b/i,
|
|
97
|
+
/\b(want\s+to\s+(create|build|implement|add|develop))\b/i,
|
|
98
|
+
/\bnew\s+(module|feature|system|api|endpoint|service|component)\b/i,
|
|
99
|
+
],
|
|
100
|
+
priority: 3,
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
TESTING: {
|
|
104
|
+
keywords: [
|
|
105
|
+
'test',
|
|
106
|
+
'tests',
|
|
107
|
+
'testing',
|
|
108
|
+
'coverage',
|
|
109
|
+
'tdd',
|
|
110
|
+
'unit',
|
|
111
|
+
'integration',
|
|
112
|
+
'spec',
|
|
113
|
+
'jest',
|
|
114
|
+
'vitest',
|
|
115
|
+
// Technical synonyms
|
|
116
|
+
'validate',
|
|
117
|
+
'validation',
|
|
118
|
+
'verify',
|
|
119
|
+
'verification',
|
|
120
|
+
'assert',
|
|
121
|
+
'assertions',
|
|
122
|
+
'suite',
|
|
123
|
+
'specs',
|
|
124
|
+
'mocha',
|
|
125
|
+
'ava',
|
|
126
|
+
'pytest',
|
|
127
|
+
'qa',
|
|
128
|
+
'mock',
|
|
129
|
+
'stub',
|
|
130
|
+
'fixture',
|
|
131
|
+
],
|
|
132
|
+
phrases: [
|
|
133
|
+
/\b(write tests?)\b/i,
|
|
134
|
+
/\b(test (the|this|a|an))\b/i,
|
|
135
|
+
/\b(add (test )?coverage)\b/i,
|
|
136
|
+
/\b(help (me )?(with )?tdd)\b/i,
|
|
137
|
+
/\b(unit tests?)\b/i,
|
|
138
|
+
/\b(integration tests?)\b/i,
|
|
139
|
+
/\b(test coverage)\b/i,
|
|
140
|
+
/\bhelp me test\b/i,
|
|
141
|
+
/\btest.*class\b/i,
|
|
142
|
+
/\btest.*endpoint\b/i,
|
|
143
|
+
/\btest.*api\b/i,
|
|
144
|
+
// Technical synonym phrases
|
|
145
|
+
/\b(test suite)\b/i,
|
|
146
|
+
/\b(run (the )?tests?)\b/i,
|
|
147
|
+
/\b(validation (tests?|suite|logic))\b/i,
|
|
148
|
+
/\b(verify (the|this|that))\b/i,
|
|
149
|
+
/\b(quality (check|assurance))\b/i,
|
|
150
|
+
],
|
|
151
|
+
priority: 3,
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
REVIEW: {
|
|
155
|
+
keywords: [
|
|
156
|
+
'review',
|
|
157
|
+
'check',
|
|
158
|
+
'look',
|
|
159
|
+
'examine',
|
|
160
|
+
'audit',
|
|
161
|
+
'inspect',
|
|
162
|
+
'pr',
|
|
163
|
+
// Technical synonyms
|
|
164
|
+
'critique',
|
|
165
|
+
'evaluate',
|
|
166
|
+
'assess',
|
|
167
|
+
'analyze',
|
|
168
|
+
'feedback',
|
|
169
|
+
'approval',
|
|
170
|
+
'lgtm',
|
|
171
|
+
],
|
|
172
|
+
phrases: [
|
|
173
|
+
/\b(review (this|the|my))\b/i,
|
|
174
|
+
/\b(check (this|the|my))\b/i,
|
|
175
|
+
/\b(look at (this|the|these))\b/i,
|
|
176
|
+
/\b(audit (this|the))\b/i,
|
|
177
|
+
/\b(review my pr)\b/i,
|
|
178
|
+
/\b(code review)\b/i,
|
|
179
|
+
/\breview.*code\b/i,
|
|
180
|
+
/\bcheck.*implementation\b/i,
|
|
181
|
+
/\baudit.*file\b/i,
|
|
182
|
+
// Technical synonym phrases
|
|
183
|
+
/\b(give (me )?feedback)\b/i,
|
|
184
|
+
/\b(need (your )?approval)\b/i,
|
|
185
|
+
/\b(pull request)\b/i,
|
|
186
|
+
],
|
|
187
|
+
priority: 2,
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
SECURITY: {
|
|
191
|
+
keywords: [
|
|
192
|
+
'security',
|
|
193
|
+
'vulnerability',
|
|
194
|
+
'vulnerabilities',
|
|
195
|
+
'xss',
|
|
196
|
+
'injection',
|
|
197
|
+
'auth',
|
|
198
|
+
'authentication',
|
|
199
|
+
'credential',
|
|
200
|
+
'credentials',
|
|
201
|
+
'owasp',
|
|
202
|
+
'secure',
|
|
203
|
+
],
|
|
204
|
+
phrases: [
|
|
205
|
+
/\b(security (check|review|audit|scan|vulnerabilit))\b/i,
|
|
206
|
+
/\b(check for (security|vulnerabilit))\b/i,
|
|
207
|
+
/\bxss\b/i,
|
|
208
|
+
/\bsql injection\b/i,
|
|
209
|
+
/\bowasp\b/i,
|
|
210
|
+
/\b(credential (handling|management))\b/i,
|
|
211
|
+
/\b(authentication (code|logic|flow))\b/i,
|
|
212
|
+
/\bvalidate.*credential\b/i,
|
|
213
|
+
],
|
|
214
|
+
priority: 4,
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
COMPLIANCE: {
|
|
218
|
+
keywords: ['hipaa', 'soc2', 'pci', 'compliance', 'compliant', 'phi', 'ephi'],
|
|
219
|
+
phrases: [
|
|
220
|
+
/\bhipaa\b/i,
|
|
221
|
+
/\bsoc ?2\b/i,
|
|
222
|
+
/\bpci(-dss)?\b/i,
|
|
223
|
+
/\b(ensure|check|audit).*compliance\b/i,
|
|
224
|
+
/\bcompliance (check|audit|review|violation)\b/i,
|
|
225
|
+
/\bphi (handling|data)\b/i,
|
|
226
|
+
/\bpci compliant\b/i,
|
|
227
|
+
],
|
|
228
|
+
priority: 5, // Highest priority - compliance is critical
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
ZOHO: {
|
|
232
|
+
keywords: ['zoho', 'creator', 'catalyst', 'deluge', 'crm', 'books', 'analytics'],
|
|
233
|
+
phrases: [
|
|
234
|
+
/\bzoho\b/i,
|
|
235
|
+
/\b(zoho )?(creator|catalyst)\b/i,
|
|
236
|
+
/\bdeluge\b/i,
|
|
237
|
+
/\b(creator (app|form|report))\b/i,
|
|
238
|
+
/\b(catalyst (function|deploy))\b/i,
|
|
239
|
+
/\bcrm (workflow|automation)\b/i,
|
|
240
|
+
/\bzoho books\b/i,
|
|
241
|
+
],
|
|
242
|
+
priority: 4,
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
DEPLOY: {
|
|
246
|
+
keywords: [
|
|
247
|
+
'deploy',
|
|
248
|
+
'deployment',
|
|
249
|
+
'release',
|
|
250
|
+
'ship',
|
|
251
|
+
'production',
|
|
252
|
+
'prod',
|
|
253
|
+
'publish',
|
|
254
|
+
'live',
|
|
255
|
+
// Technical synonyms
|
|
256
|
+
'launch',
|
|
257
|
+
'rollout',
|
|
258
|
+
'staging',
|
|
259
|
+
'promote',
|
|
260
|
+
'ci',
|
|
261
|
+
'cd',
|
|
262
|
+
'pipeline',
|
|
263
|
+
],
|
|
264
|
+
phrases: [
|
|
265
|
+
/\bdeploy (to|this|the)\b/i,
|
|
266
|
+
/\bpush to prod(uction)?\b/i,
|
|
267
|
+
/\brelease (the|a|this|new)\b/i,
|
|
268
|
+
/\bship (this|the|it)\b/i,
|
|
269
|
+
/\bgo live\b/i,
|
|
270
|
+
/\bto production\b/i,
|
|
271
|
+
// Technical synonym phrases
|
|
272
|
+
/\b(launch (the|this|to))\b/i,
|
|
273
|
+
/\b(roll ?out)\b/i,
|
|
274
|
+
/\b(promote to (prod|production|staging))\b/i,
|
|
275
|
+
/\b(ci.?cd pipeline)\b/i,
|
|
276
|
+
],
|
|
277
|
+
priority: 3,
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
REFACTOR: {
|
|
281
|
+
keywords: [
|
|
282
|
+
'refactor',
|
|
283
|
+
'cleanup',
|
|
284
|
+
'clean',
|
|
285
|
+
'consolidate',
|
|
286
|
+
'simplify',
|
|
287
|
+
'remove',
|
|
288
|
+
'dead',
|
|
289
|
+
'unused',
|
|
290
|
+
'duplicate',
|
|
291
|
+
],
|
|
292
|
+
phrases: [
|
|
293
|
+
/\brefactor\b/i,
|
|
294
|
+
/\bclean ?up\b/i,
|
|
295
|
+
/\bdead code\b/i,
|
|
296
|
+
/\bunused (import|code|variable|function)\b/i,
|
|
297
|
+
/\bremove (unused|dead|duplicate)\b/i,
|
|
298
|
+
/\bconsolidate\b/i,
|
|
299
|
+
],
|
|
300
|
+
priority: 2,
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
DEBUG: {
|
|
304
|
+
keywords: [
|
|
305
|
+
'fix',
|
|
306
|
+
'bug',
|
|
307
|
+
'error',
|
|
308
|
+
'issue',
|
|
309
|
+
'problem',
|
|
310
|
+
'broken',
|
|
311
|
+
'failing',
|
|
312
|
+
'debug',
|
|
313
|
+
'investigate',
|
|
314
|
+
'wrong',
|
|
315
|
+
'crash',
|
|
316
|
+
'fail',
|
|
317
|
+
// Technical synonyms
|
|
318
|
+
'troubleshoot',
|
|
319
|
+
'diagnose',
|
|
320
|
+
'trace',
|
|
321
|
+
'stacktrace',
|
|
322
|
+
'exception',
|
|
323
|
+
'regression',
|
|
324
|
+
'hotfix',
|
|
325
|
+
'patch',
|
|
326
|
+
],
|
|
327
|
+
phrases: [
|
|
328
|
+
/\bfix (this|the|a)?\s*(bug|error|issue)?\b/i,
|
|
329
|
+
/\bbug\b/i,
|
|
330
|
+
/\bnot working\b/i,
|
|
331
|
+
/\bkeeps? (failing|crashing|erroring|happening)\b/i,
|
|
332
|
+
/\bdebug\b/i,
|
|
333
|
+
/\binvestigate\b/i,
|
|
334
|
+
/\berror\b/i,
|
|
335
|
+
/\bbroken\b/i,
|
|
336
|
+
/\bfailing\b/i,
|
|
337
|
+
// Technical synonym phrases
|
|
338
|
+
/\b(troubleshoot (this|the))\b/i,
|
|
339
|
+
/\b(diagnose (this|the))\b/i,
|
|
340
|
+
/\b(root cause)\b/i,
|
|
341
|
+
/\b(stack trace)\b/i,
|
|
342
|
+
/\b(regression in)\b/i,
|
|
343
|
+
],
|
|
344
|
+
priority: 2,
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
// New intent types for full documentation alignment
|
|
348
|
+
|
|
349
|
+
LEARNING: {
|
|
350
|
+
keywords: [
|
|
351
|
+
'learn',
|
|
352
|
+
'extract',
|
|
353
|
+
'pattern',
|
|
354
|
+
'patterns',
|
|
355
|
+
'reusable',
|
|
356
|
+
'insight',
|
|
357
|
+
'capture',
|
|
358
|
+
'knowledge',
|
|
359
|
+
],
|
|
360
|
+
phrases: [
|
|
361
|
+
/\b(learn from|extract patterns?|capture (this|the) patterns?)\b/i,
|
|
362
|
+
/\b(make.*reusable)\b/i,
|
|
363
|
+
/\b(extract (a|the|this) patterns?)\b/i,
|
|
364
|
+
/\b(save.*for.*future)\b/i,
|
|
365
|
+
/\b(reusable patterns?)\b/i,
|
|
366
|
+
/\b(patterns?\s+from\s+(this|the))\b/i,
|
|
367
|
+
],
|
|
368
|
+
priority: 2,
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
SETUP: {
|
|
372
|
+
keywords: ['setup', 'configure', 'install', 'initialize', 'onboard', 'bootstrap', 'init'],
|
|
373
|
+
phrases: [
|
|
374
|
+
/\b(set up|setup)\s+(the|a|an|new|my)?\b/i,
|
|
375
|
+
/\b(configure\s+(the|for|my))\b/i,
|
|
376
|
+
/\b(initialize\s+(the|a|new|my))\b/i,
|
|
377
|
+
/\b(install\s+(the|dependencies|packages|all))\b/i,
|
|
378
|
+
/\b(bootstrap\s+(the|a|new))\b/i,
|
|
379
|
+
/\b(get.*started)\b/i,
|
|
380
|
+
/\b(initial setup)\b/i,
|
|
381
|
+
],
|
|
382
|
+
priority: 2,
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
HANDOFF: {
|
|
386
|
+
keywords: ['handoff', 'transfer', 'deliverable', 'handover', 'transition', 'turnover'],
|
|
387
|
+
phrases: [
|
|
388
|
+
/\b(hand ?off|handover)\b/i,
|
|
389
|
+
/\b(transfer to|transition to)\b/i,
|
|
390
|
+
/\b(create deliverable)\b/i,
|
|
391
|
+
/\b(client (handoff|documentation|delivery))\b/i,
|
|
392
|
+
/\b(prepare.*for.*handoff)\b/i,
|
|
393
|
+
/\b(deliverable.*for.*client)\b/i,
|
|
394
|
+
],
|
|
395
|
+
priority: 2,
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
E2E: {
|
|
399
|
+
keywords: ['e2e', 'playwright', 'cypress', 'selenium', 'journey', 'puppeteer'],
|
|
400
|
+
phrases: [
|
|
401
|
+
/\b(e2e|end[- ]to[- ]end)\b/i,
|
|
402
|
+
/\b(playwright|cypress|selenium|puppeteer)\b/i,
|
|
403
|
+
/\b(user (flow|journey))\b/i,
|
|
404
|
+
/\b(browser test)\b/i,
|
|
405
|
+
/\b(e2e test)\b/i,
|
|
406
|
+
/\b(functional test)\b/i,
|
|
407
|
+
/\b(acceptance test)\b/i,
|
|
408
|
+
],
|
|
409
|
+
priority: 3,
|
|
410
|
+
},
|
|
411
|
+
|
|
412
|
+
DOCUMENTATION: {
|
|
413
|
+
keywords: ['docs', 'document', 'documentation', 'readme', 'contributing', 'jsdoc', 'wiki'],
|
|
414
|
+
phrases: [
|
|
415
|
+
/\b(update|write|generate)\s+(docs|documentation|readme)\b/i,
|
|
416
|
+
/\b(document (the|this|a))\b/i,
|
|
417
|
+
/\b(add.*documentation)\b/i,
|
|
418
|
+
/\b(update.*readme)\b/i,
|
|
419
|
+
/\b(generate.*docs)\b/i,
|
|
420
|
+
/\b(write.*docs)\b/i,
|
|
421
|
+
],
|
|
422
|
+
priority: 2,
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Calculate Levenshtein distance between two strings
|
|
428
|
+
* Used for typo tolerance in keyword matching
|
|
429
|
+
* @param {string} a - First string
|
|
430
|
+
* @param {string} b - Second string
|
|
431
|
+
* @returns {number} Edit distance
|
|
432
|
+
*/
|
|
433
|
+
function levenshteinDistance(a, b) {
|
|
434
|
+
if (a.length === 0) return b.length;
|
|
435
|
+
if (b.length === 0) return a.length;
|
|
436
|
+
|
|
437
|
+
// Early bailout for very different lengths
|
|
438
|
+
if (Math.abs(a.length - b.length) > 3) return Math.max(a.length, b.length);
|
|
439
|
+
|
|
440
|
+
const matrix = [];
|
|
441
|
+
|
|
442
|
+
// Initialize matrix
|
|
443
|
+
for (let i = 0; i <= b.length; i++) {
|
|
444
|
+
matrix[i] = [i];
|
|
445
|
+
}
|
|
446
|
+
for (let j = 0; j <= a.length; j++) {
|
|
447
|
+
matrix[0][j] = j;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Fill in the rest
|
|
451
|
+
for (let i = 1; i <= b.length; i++) {
|
|
452
|
+
for (let j = 1; j <= a.length; j++) {
|
|
453
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
454
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
455
|
+
} else {
|
|
456
|
+
matrix[i][j] = Math.min(
|
|
457
|
+
matrix[i - 1][j - 1] + 1, // substitution
|
|
458
|
+
matrix[i][j - 1] + 1, // insertion
|
|
459
|
+
matrix[i - 1][j] + 1 // deletion
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return matrix[b.length][a.length];
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Check if a word is a typo of a keyword
|
|
470
|
+
* @param {string} word - Word to check
|
|
471
|
+
* @param {string} keyword - Target keyword
|
|
472
|
+
* @returns {boolean} True if word is likely a typo of keyword
|
|
473
|
+
*/
|
|
474
|
+
function isTypoMatch(word, keyword) {
|
|
475
|
+
if (!word || !keyword) return false;
|
|
476
|
+
|
|
477
|
+
const a = word.toLowerCase();
|
|
478
|
+
const b = keyword.toLowerCase();
|
|
479
|
+
|
|
480
|
+
// Exact match
|
|
481
|
+
if (a === b) return true;
|
|
482
|
+
|
|
483
|
+
// No typo tolerance for short words (< 4 chars)
|
|
484
|
+
if (b.length < 4) return a === b;
|
|
485
|
+
|
|
486
|
+
const distance = levenshteinDistance(a, b);
|
|
487
|
+
|
|
488
|
+
// Allow up to 2 edits for longer words, 1 edit for medium words
|
|
489
|
+
const maxAllowed = b.length >= 6 ? 2 : 1;
|
|
490
|
+
|
|
491
|
+
return distance <= maxAllowed;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Common typos for critical keywords (optimization)
|
|
496
|
+
*/
|
|
497
|
+
const COMMON_TYPOS = {
|
|
498
|
+
test: ['tets', 'tset', 'testt', 'tet', 'teast'],
|
|
499
|
+
deploy: ['deplay', 'deplyo', 'delpoy', 'depoly'],
|
|
500
|
+
review: ['reveiw', 'reviw', 'revew', 'reivew'],
|
|
501
|
+
plan: ['pln', 'paln', 'plna', 'plaan'],
|
|
502
|
+
security: ['secuirty', 'securty', 'secutiry', 'securtiy'],
|
|
503
|
+
refactor: ['refacter', 'refacor', 'refactr', 'refactro'],
|
|
504
|
+
build: ['biuld', 'buld', 'buidl', 'bulid'],
|
|
505
|
+
create: ['craete', 'creat', 'cerate', 'cereate'],
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Find typo matches for a word against a list of keywords
|
|
510
|
+
* @param {string} word - Word to check
|
|
511
|
+
* @param {string[]} keywords - List of keywords to match against
|
|
512
|
+
* @returns {string|null} Matched keyword or null
|
|
513
|
+
*/
|
|
514
|
+
function findTypoMatch(word, keywords) {
|
|
515
|
+
const lowerWord = word.toLowerCase();
|
|
516
|
+
|
|
517
|
+
// First check common typos dictionary (fast path)
|
|
518
|
+
for (const [keyword, typos] of Object.entries(COMMON_TYPOS)) {
|
|
519
|
+
if (keywords.includes(keyword) && typos.includes(lowerWord)) {
|
|
520
|
+
return keyword;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Fallback to Levenshtein for other typos
|
|
525
|
+
for (const keyword of keywords) {
|
|
526
|
+
if (isTypoMatch(lowerWord, keyword)) {
|
|
527
|
+
return keyword;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Negation words that negate intent when preceding keywords
|
|
536
|
+
*/
|
|
537
|
+
const NEGATION_PATTERNS = {
|
|
538
|
+
// Explicit negation words
|
|
539
|
+
words: /\b(don'?t|do not|no|never|skip|avoid|without|not|isn'?t|won'?t|cannot|can'?t|shouldn'?t|wouldn'?t|exclude|stop|halt|cease|prevent)\b/i,
|
|
540
|
+
// Negation phrases (more specific patterns)
|
|
541
|
+
phrases: [
|
|
542
|
+
/\b(don'?t|do not)\s+\w*\s*(test|deploy|review|plan|build|create|refactor)/i,
|
|
543
|
+
/\b(skip|avoid|no)\s+(the\s+)?(test|deploy|security|review|plan)/i,
|
|
544
|
+
/\b(not\s+(for|ready|to|going))\s+/i,
|
|
545
|
+
/\bnot\s+yet\b/i,
|
|
546
|
+
/\bno\s+need\s+(to|for)\b/i,
|
|
547
|
+
],
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Check if a keyword is negated in the prompt
|
|
552
|
+
* @param {string} prompt - Full prompt
|
|
553
|
+
* @param {string} keyword - Keyword to check
|
|
554
|
+
* @returns {boolean} True if keyword appears to be negated
|
|
555
|
+
*/
|
|
556
|
+
function isKeywordNegated(prompt, keyword) {
|
|
557
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
558
|
+
const keywordRegex = new RegExp(`\\b${keyword}\\b`, 'gi');
|
|
559
|
+
let match;
|
|
560
|
+
|
|
561
|
+
while ((match = keywordRegex.exec(lowerPrompt)) !== null) {
|
|
562
|
+
// Check the 50 characters before the keyword for negation
|
|
563
|
+
const beforeKeyword = lowerPrompt.slice(Math.max(0, match.index - 50), match.index);
|
|
564
|
+
|
|
565
|
+
// Look for negation words within 3 words of the keyword
|
|
566
|
+
const words = beforeKeyword.trim().split(/\s+/).slice(-4); // Last 4 words
|
|
567
|
+
const nearbyText = words.join(' ');
|
|
568
|
+
|
|
569
|
+
if (NEGATION_PATTERNS.words.test(nearbyText)) {
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Detect all negated intents in a prompt
|
|
579
|
+
* @param {string} prompt - User prompt
|
|
580
|
+
* @returns {string[]} Array of negated intent types
|
|
581
|
+
*/
|
|
582
|
+
function detectNegatedIntents(prompt) {
|
|
583
|
+
const negatedIntents = [];
|
|
584
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
585
|
+
|
|
586
|
+
// Check each intent's keywords for negation
|
|
587
|
+
for (const [intentType, patterns] of Object.entries(INTENT_PATTERNS)) {
|
|
588
|
+
for (const keyword of patterns.keywords) {
|
|
589
|
+
if (lowerPrompt.includes(keyword) && isKeywordNegated(prompt, keyword)) {
|
|
590
|
+
if (!negatedIntents.includes(intentType)) {
|
|
591
|
+
negatedIntents.push(intentType);
|
|
592
|
+
}
|
|
593
|
+
break; // One negated keyword is enough
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return negatedIntents;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Sentence type constants
|
|
603
|
+
*/
|
|
604
|
+
const SENTENCE_TYPES = {
|
|
605
|
+
QUESTION: 'QUESTION',
|
|
606
|
+
COMMAND: 'COMMAND',
|
|
607
|
+
STATEMENT: 'STATEMENT',
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Question word patterns for detecting inquiries
|
|
612
|
+
*/
|
|
613
|
+
const QUESTION_PATTERNS = {
|
|
614
|
+
// Question words at start of sentence
|
|
615
|
+
startWords: /^(should|would|could|can|is|are|do|does|will|what|how|why|when|where|which|who|whom|whose|shall|may|might|has|have|had|was|were)\s/i,
|
|
616
|
+
// Question mark at end
|
|
617
|
+
endMark: /\?\s*$/,
|
|
618
|
+
// Conditional/hypothetical patterns
|
|
619
|
+
conditional: /^(if|whether)\s/i,
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Detect if prompt is a question (inquiry) vs command/statement
|
|
624
|
+
* @param {string} prompt - User prompt
|
|
625
|
+
* @returns {object} { isQuestion: boolean, sentenceType: string }
|
|
626
|
+
*/
|
|
627
|
+
function detectSentenceType(prompt) {
|
|
628
|
+
if (!prompt) return { isQuestion: false, sentenceType: SENTENCE_TYPES.STATEMENT };
|
|
629
|
+
|
|
630
|
+
const trimmed = prompt.trim();
|
|
631
|
+
|
|
632
|
+
// Check for question mark
|
|
633
|
+
if (QUESTION_PATTERNS.endMark.test(trimmed)) {
|
|
634
|
+
return { isQuestion: true, sentenceType: SENTENCE_TYPES.QUESTION };
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Check for question words at start
|
|
638
|
+
if (QUESTION_PATTERNS.startWords.test(trimmed)) {
|
|
639
|
+
return { isQuestion: true, sentenceType: SENTENCE_TYPES.QUESTION };
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Check for conditional/hypothetical
|
|
643
|
+
if (QUESTION_PATTERNS.conditional.test(trimmed)) {
|
|
644
|
+
return { isQuestion: true, sentenceType: SENTENCE_TYPES.QUESTION };
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Check for command words at start (imperative)
|
|
648
|
+
if (/^(please|let's|lets|let me|let us|help me|can you|could you|would you)\s/i.test(trimmed)) {
|
|
649
|
+
return { isQuestion: false, sentenceType: SENTENCE_TYPES.COMMAND };
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return { isQuestion: false, sentenceType: SENTENCE_TYPES.STATEMENT };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Check if prompt is primarily non-ASCII (likely non-English)
|
|
657
|
+
* @param {string} prompt - User prompt
|
|
658
|
+
* @returns {boolean} True if majority is non-ASCII
|
|
659
|
+
*/
|
|
660
|
+
function isNonAscii(prompt) {
|
|
661
|
+
if (!prompt) return false;
|
|
662
|
+
// Count printable ASCII characters (space to tilde)
|
|
663
|
+
const asciiChars = (prompt.match(/[\x20-\x7E]/g) || []).length;
|
|
664
|
+
const totalChars = prompt.replace(/\s/g, '').length; // Exclude whitespace
|
|
665
|
+
if (totalChars === 0) return false;
|
|
666
|
+
const ratio = asciiChars / totalChars;
|
|
667
|
+
return ratio < 0.5; // Less than 50% ASCII
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Check if prompt starts with an explicit slash command
|
|
672
|
+
* @param {string} prompt - User prompt
|
|
673
|
+
* @returns {boolean} True if starts with /command
|
|
674
|
+
*/
|
|
675
|
+
function isExplicitCommand(prompt) {
|
|
676
|
+
if (!prompt) return false;
|
|
677
|
+
return /^\/\w+/.test(prompt.trim());
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Contextual reference patterns (pronouns, anaphora)
|
|
682
|
+
* These indicate the user is referring to something from prior context
|
|
683
|
+
*/
|
|
684
|
+
const CONTEXTUAL_REFERENCE_PATTERNS = [
|
|
685
|
+
/\b(that|this|it|those|these|the same)\b/i,
|
|
686
|
+
/\b(do (it|that|the same|this))\b/i,
|
|
687
|
+
/\b(proceed|continue|go ahead|yes|okay|ok|sure)\b/i,
|
|
688
|
+
/\b(what (we|I) (said|discussed|mentioned))\b/i,
|
|
689
|
+
/\b(as (mentioned|discussed|before))\b/i,
|
|
690
|
+
/\b(like (before|last time))\b/i,
|
|
691
|
+
/\b(same (thing|as|approach))\b/i,
|
|
692
|
+
];
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Check if prompt contains contextual references requiring prior context
|
|
696
|
+
* @param {string} prompt - User prompt
|
|
697
|
+
* @returns {boolean} True if prompt has contextual references
|
|
698
|
+
*/
|
|
699
|
+
function hasContextualReference(prompt) {
|
|
700
|
+
if (!prompt) return false;
|
|
701
|
+
return CONTEXTUAL_REFERENCE_PATTERNS.some((pattern) => pattern.test(prompt));
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Prompt length limits for performance
|
|
706
|
+
*/
|
|
707
|
+
const PROMPT_LIMITS = {
|
|
708
|
+
MAX_CHARS: 2000, // Truncate after this
|
|
709
|
+
PRIORITY_WINDOW: 500, // First N chars have highest weight
|
|
710
|
+
TAIL_WINDOW: 500, // Last N chars to keep
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Intelligently truncate long prompts for classification
|
|
715
|
+
* Keeps beginning (likely has intent) and end (may have clarifications)
|
|
716
|
+
* @param {string} prompt - Original prompt
|
|
717
|
+
* @returns {object} { text: string, wasTruncated: boolean }
|
|
718
|
+
*/
|
|
719
|
+
function truncateForClassification(prompt) {
|
|
720
|
+
if (!prompt || prompt.length <= PROMPT_LIMITS.MAX_CHARS) {
|
|
721
|
+
return { text: prompt || '', wasTruncated: false };
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Keep first 500 chars (likely contains intent)
|
|
725
|
+
const start = prompt.slice(0, PROMPT_LIMITS.PRIORITY_WINDOW);
|
|
726
|
+
|
|
727
|
+
// Keep last 500 chars (may contain clarifications/specifics)
|
|
728
|
+
const end = prompt.slice(-PROMPT_LIMITS.TAIL_WINDOW);
|
|
729
|
+
|
|
730
|
+
// Combine with indicator
|
|
731
|
+
return {
|
|
732
|
+
text: `${start} ... ${end}`,
|
|
733
|
+
wasTruncated: true,
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Calculate match score for a single intent
|
|
739
|
+
* @param {string} prompt - Normalized prompt
|
|
740
|
+
* @param {object} patterns - Intent pattern config
|
|
741
|
+
* @param {string[]} negatedIntents - Intents that are negated in the prompt
|
|
742
|
+
* @param {string} intentType - The intent type being scored
|
|
743
|
+
* @returns {object} Score and match details
|
|
744
|
+
*/
|
|
745
|
+
function calculateIntentScore(prompt, patterns, negatedIntents = [], intentType = '') {
|
|
746
|
+
let score = 0;
|
|
747
|
+
const matches = [];
|
|
748
|
+
const normalizedPrompt = prompt.toLowerCase();
|
|
749
|
+
let hasTypoMatch = false;
|
|
750
|
+
|
|
751
|
+
// If this intent is negated, return zero score
|
|
752
|
+
if (negatedIntents.includes(intentType)) {
|
|
753
|
+
return { score: 0, matches: [], priority: patterns.priority, isNegated: true, typoMatch: false };
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Check keywords (weight: 0.2 each, max contribution 0.6 - raised cap for better multi-keyword scoring)
|
|
757
|
+
let keywordScore = 0;
|
|
758
|
+
const matchedKeywords = new Set();
|
|
759
|
+
|
|
760
|
+
// First pass: exact keyword matches
|
|
761
|
+
for (const keyword of patterns.keywords) {
|
|
762
|
+
const regex = new RegExp(`\\b${keyword}\\b`, 'i');
|
|
763
|
+
if (regex.test(normalizedPrompt)) {
|
|
764
|
+
// Skip keywords that are individually negated
|
|
765
|
+
if (!isKeywordNegated(prompt, keyword)) {
|
|
766
|
+
keywordScore += 0.2;
|
|
767
|
+
matches.push({ type: 'keyword', match: keyword });
|
|
768
|
+
matchedKeywords.add(keyword);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Second pass: typo matching (only for words not already matched, with reduced weight)
|
|
774
|
+
const words = normalizedPrompt.split(/\s+/);
|
|
775
|
+
for (const word of words) {
|
|
776
|
+
// Skip very short words and already matched words
|
|
777
|
+
if (word.length < 3) continue;
|
|
778
|
+
|
|
779
|
+
const typoKeyword = findTypoMatch(word, patterns.keywords);
|
|
780
|
+
if (typoKeyword && !matchedKeywords.has(typoKeyword)) {
|
|
781
|
+
// Check negation for the original typo word too
|
|
782
|
+
if (!isKeywordNegated(prompt, word)) {
|
|
783
|
+
keywordScore += 0.2; // Same as keyword weight (typo info tracked in match type)
|
|
784
|
+
matches.push({ type: 'typo', match: word, correctedTo: typoKeyword });
|
|
785
|
+
matchedKeywords.add(typoKeyword);
|
|
786
|
+
hasTypoMatch = true;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
score += Math.min(keywordScore, 0.6);
|
|
792
|
+
|
|
793
|
+
// Check phrases (higher weight: 0.35 each, max 3 phrases count)
|
|
794
|
+
let phraseCount = 0;
|
|
795
|
+
for (const phrase of patterns.phrases) {
|
|
796
|
+
if (phrase.test(normalizedPrompt)) {
|
|
797
|
+
if (phraseCount < 3) {
|
|
798
|
+
score += 0.35;
|
|
799
|
+
phraseCount++;
|
|
800
|
+
}
|
|
801
|
+
matches.push({ type: 'phrase', match: phrase.toString() });
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Apply priority weight (normalize to keep score in 0-1 range)
|
|
806
|
+
const priorityBoost = (patterns.priority - 1) * 0.05;
|
|
807
|
+
score = Math.min(score + priorityBoost, 1.0);
|
|
808
|
+
|
|
809
|
+
return { score, matches, priority: patterns.priority, isNegated: false, typoMatch: hasTypoMatch };
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Classify user intent from natural language prompt
|
|
814
|
+
*
|
|
815
|
+
* @param {string} prompt - User prompt to classify
|
|
816
|
+
* @param {object} context - Optional context for enhanced detection
|
|
817
|
+
* @param {string[]} context.technologies - Detected technologies in session
|
|
818
|
+
* @param {string} context.complianceMode - Active compliance mode (hipaa/soc2/pci-dss)
|
|
819
|
+
* @param {string[]} context.recentFiles - Recently accessed files
|
|
820
|
+
* @param {string} context.lastClassifiedIntent - Last classified intent from session
|
|
821
|
+
* @param {number} context.conversationTurn - Current conversation turn number
|
|
822
|
+
* @returns {object} Classification result
|
|
823
|
+
*/
|
|
824
|
+
function classifyIntent(prompt, context = {}) {
|
|
825
|
+
const { lastClassifiedIntent = null } = context;
|
|
826
|
+
// Handle null/undefined/empty input
|
|
827
|
+
if (!prompt || typeof prompt !== 'string' || prompt.trim().length === 0) {
|
|
828
|
+
return {
|
|
829
|
+
primaryIntent: INTENT_TYPES.UNKNOWN,
|
|
830
|
+
confidence: 0,
|
|
831
|
+
secondaryIntents: [],
|
|
832
|
+
negatedIntents: [],
|
|
833
|
+
autoConvert: false,
|
|
834
|
+
suggestOnly: false,
|
|
835
|
+
ambiguous: false,
|
|
836
|
+
isInquiry: false,
|
|
837
|
+
sentenceType: SENTENCE_TYPES.STATEMENT,
|
|
838
|
+
wasTruncated: false,
|
|
839
|
+
skipRouting: false,
|
|
840
|
+
reason: 'empty_input',
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const trimmedPrompt = prompt.trim();
|
|
845
|
+
|
|
846
|
+
// Truncate very long prompts for performance
|
|
847
|
+
const { text: normalizedPrompt, wasTruncated } = truncateForClassification(trimmedPrompt);
|
|
848
|
+
|
|
849
|
+
// Check for explicit slash commands first
|
|
850
|
+
if (isExplicitCommand(normalizedPrompt)) {
|
|
851
|
+
return {
|
|
852
|
+
primaryIntent: INTENT_TYPES.UNKNOWN,
|
|
853
|
+
confidence: 0,
|
|
854
|
+
secondaryIntents: [],
|
|
855
|
+
negatedIntents: [],
|
|
856
|
+
autoConvert: false,
|
|
857
|
+
suggestOnly: false,
|
|
858
|
+
ambiguous: false,
|
|
859
|
+
isInquiry: false,
|
|
860
|
+
sentenceType: SENTENCE_TYPES.COMMAND,
|
|
861
|
+
wasTruncated,
|
|
862
|
+
skipRouting: true,
|
|
863
|
+
reason: 'explicit_command',
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// Check for non-ASCII (likely non-English) before length check
|
|
868
|
+
if (isNonAscii(normalizedPrompt)) {
|
|
869
|
+
return {
|
|
870
|
+
primaryIntent: INTENT_TYPES.UNKNOWN,
|
|
871
|
+
confidence: 0,
|
|
872
|
+
secondaryIntents: [],
|
|
873
|
+
negatedIntents: [],
|
|
874
|
+
autoConvert: false,
|
|
875
|
+
suggestOnly: false,
|
|
876
|
+
ambiguous: false,
|
|
877
|
+
isInquiry: false,
|
|
878
|
+
sentenceType: SENTENCE_TYPES.STATEMENT,
|
|
879
|
+
wasTruncated,
|
|
880
|
+
skipRouting: true,
|
|
881
|
+
reason: 'non_ascii',
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Check for contextual references (requires session context)
|
|
886
|
+
const hasContextRef = hasContextualReference(normalizedPrompt);
|
|
887
|
+
|
|
888
|
+
// If prompt is primarily contextual reference (short and refers to prior context)
|
|
889
|
+
// and we have session context, inherit intent
|
|
890
|
+
if (hasContextRef && lastClassifiedIntent && INTENT_TYPES[lastClassifiedIntent]) {
|
|
891
|
+
// Check if prompt has NO strong intent keywords (is purely contextual)
|
|
892
|
+
const explicitIntentKeywords =
|
|
893
|
+
/\b(plan|test|review|design|fix|deploy|refactor|build|create|add|setup|e2e|docs|handoff|learn|security|compliance)\b/i;
|
|
894
|
+
|
|
895
|
+
if (!explicitIntentKeywords.test(normalizedPrompt)) {
|
|
896
|
+
return {
|
|
897
|
+
primaryIntent: lastClassifiedIntent,
|
|
898
|
+
confidence: 0.6, // Reduced confidence for inherited intent
|
|
899
|
+
secondaryIntents: [],
|
|
900
|
+
negatedIntents: [],
|
|
901
|
+
autoConvert: false, // Don't auto-convert inherited intents
|
|
902
|
+
suggestOnly: true,
|
|
903
|
+
ambiguous: false,
|
|
904
|
+
isInquiry: false,
|
|
905
|
+
sentenceType: SENTENCE_TYPES.STATEMENT,
|
|
906
|
+
wasTruncated,
|
|
907
|
+
requiresContext: true, // Flag that this was context-dependent
|
|
908
|
+
inheritedFrom: 'session', // Mark as inherited
|
|
909
|
+
skipRouting: false,
|
|
910
|
+
reason: null,
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Check for very short prompts - but allow if explicit intent keyword found
|
|
916
|
+
if (normalizedPrompt.length < 10) {
|
|
917
|
+
// Allow short prompts with explicit high-confidence intent keywords
|
|
918
|
+
const explicitIntentKeywords =
|
|
919
|
+
/\b(plan|test|review|design|fix|deploy|refactor|build|create|add|setup|e2e|docs|handoff|learn)\b/i;
|
|
920
|
+
|
|
921
|
+
if (!explicitIntentKeywords.test(normalizedPrompt)) {
|
|
922
|
+
return {
|
|
923
|
+
primaryIntent: INTENT_TYPES.UNKNOWN,
|
|
924
|
+
confidence: 0.2,
|
|
925
|
+
secondaryIntents: [],
|
|
926
|
+
negatedIntents: [],
|
|
927
|
+
autoConvert: false,
|
|
928
|
+
suggestOnly: false,
|
|
929
|
+
ambiguous: false,
|
|
930
|
+
isInquiry: false,
|
|
931
|
+
sentenceType: SENTENCE_TYPES.STATEMENT,
|
|
932
|
+
wasTruncated: false,
|
|
933
|
+
requiresContext: hasContextRef, // Flag if context would help
|
|
934
|
+
skipRouting: false,
|
|
935
|
+
reason: 'too_short',
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
// Continue to classification for short prompts with explicit intent keywords
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Detect negated intents first
|
|
942
|
+
const negatedIntents = detectNegatedIntents(normalizedPrompt);
|
|
943
|
+
|
|
944
|
+
// Detect sentence type (question vs command)
|
|
945
|
+
const { isQuestion, sentenceType } = detectSentenceType(normalizedPrompt);
|
|
946
|
+
|
|
947
|
+
// Calculate scores for all intents
|
|
948
|
+
const scores = [];
|
|
949
|
+
for (const [intentType, patterns] of Object.entries(INTENT_PATTERNS)) {
|
|
950
|
+
const result = calculateIntentScore(normalizedPrompt, patterns, negatedIntents, intentType);
|
|
951
|
+
if (result.score > 0) {
|
|
952
|
+
scores.push({
|
|
953
|
+
intent: intentType,
|
|
954
|
+
score: result.score,
|
|
955
|
+
matches: result.matches,
|
|
956
|
+
priority: result.priority,
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// Apply context boosts
|
|
962
|
+
if (context.complianceMode) {
|
|
963
|
+
const complianceScore = scores.find((s) => s.intent === 'COMPLIANCE');
|
|
964
|
+
if (complianceScore) {
|
|
965
|
+
complianceScore.score = Math.min(complianceScore.score + 0.2, 1.0);
|
|
966
|
+
} else {
|
|
967
|
+
// Check if prompt mentions data handling in compliance context
|
|
968
|
+
if (/\b(data|patient|user|customer|handling|export|import)\b/i.test(normalizedPrompt)) {
|
|
969
|
+
scores.push({
|
|
970
|
+
intent: 'COMPLIANCE',
|
|
971
|
+
score: 0.5,
|
|
972
|
+
matches: [{ type: 'context', match: 'compliance_mode_active' }],
|
|
973
|
+
priority: 5,
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (context.technologies) {
|
|
980
|
+
// Boost Zoho if Zoho technologies detected
|
|
981
|
+
if (context.technologies.some((t) => /zoho|creator|catalyst|deluge/i.test(t))) {
|
|
982
|
+
const zohoScore = scores.find((s) => s.intent === 'ZOHO');
|
|
983
|
+
if (zohoScore) {
|
|
984
|
+
zohoScore.score = Math.min(zohoScore.score + 0.15, 1.0);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// Sort by score (descending), then by priority (descending)
|
|
990
|
+
scores.sort((a, b) => {
|
|
991
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
992
|
+
return b.priority - a.priority;
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
// No matches found
|
|
996
|
+
if (scores.length === 0 || scores[0].score < THRESHOLDS.MINIMUM) {
|
|
997
|
+
return {
|
|
998
|
+
primaryIntent: INTENT_TYPES.UNKNOWN,
|
|
999
|
+
confidence: scores.length > 0 ? scores[0].score : 0,
|
|
1000
|
+
secondaryIntents: [],
|
|
1001
|
+
negatedIntents,
|
|
1002
|
+
autoConvert: false,
|
|
1003
|
+
suggestOnly: false,
|
|
1004
|
+
ambiguous: true,
|
|
1005
|
+
isInquiry: isQuestion,
|
|
1006
|
+
sentenceType,
|
|
1007
|
+
wasTruncated,
|
|
1008
|
+
skipRouting: false,
|
|
1009
|
+
reason: 'no_strong_match',
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const primaryResult = scores[0];
|
|
1014
|
+
const secondaryIntents = scores
|
|
1015
|
+
.slice(1)
|
|
1016
|
+
.filter((s) => s.score >= THRESHOLDS.MINIMUM)
|
|
1017
|
+
.map((s) => s.intent);
|
|
1018
|
+
|
|
1019
|
+
// Check for ambiguity (top two scores very close)
|
|
1020
|
+
const isAmbiguous =
|
|
1021
|
+
scores.length > 1 &&
|
|
1022
|
+
Math.abs(primaryResult.score - scores[1].score) < 0.1 &&
|
|
1023
|
+
primaryResult.score < THRESHOLDS.AUTO_CONVERT;
|
|
1024
|
+
|
|
1025
|
+
// For questions, don't auto-convert - user is asking, not commanding
|
|
1026
|
+
const effectiveAutoConvert = !isQuestion && primaryResult.score >= THRESHOLDS.AUTO_CONVERT;
|
|
1027
|
+
|
|
1028
|
+
return {
|
|
1029
|
+
primaryIntent: primaryResult.intent,
|
|
1030
|
+
confidence: primaryResult.score,
|
|
1031
|
+
secondaryIntents,
|
|
1032
|
+
negatedIntents, // NEW: Intents that user explicitly wants to avoid
|
|
1033
|
+
matches: primaryResult.matches,
|
|
1034
|
+
autoConvert: effectiveAutoConvert,
|
|
1035
|
+
suggestOnly:
|
|
1036
|
+
primaryResult.score >= THRESHOLDS.SUGGEST && primaryResult.score < THRESHOLDS.AUTO_CONVERT,
|
|
1037
|
+
ambiguous: isAmbiguous,
|
|
1038
|
+
isInquiry: isQuestion, // NEW: User is asking about this, not commanding
|
|
1039
|
+
sentenceType, // NEW: QUESTION, COMMAND, or STATEMENT
|
|
1040
|
+
wasTruncated, // NEW: True if prompt was truncated for performance
|
|
1041
|
+
requiresContext: hasContextRef && primaryResult.score < THRESHOLDS.SUGGEST, // NEW: Context would help
|
|
1042
|
+
skipRouting: false,
|
|
1043
|
+
reason: null,
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
module.exports = {
|
|
1048
|
+
classifyIntent,
|
|
1049
|
+
INTENT_TYPES,
|
|
1050
|
+
THRESHOLDS,
|
|
1051
|
+
// Exported for testing
|
|
1052
|
+
isNonAscii,
|
|
1053
|
+
isExplicitCommand,
|
|
1054
|
+
calculateIntentScore,
|
|
1055
|
+
INTENT_PATTERNS,
|
|
1056
|
+
// Negation detection exports
|
|
1057
|
+
isKeywordNegated,
|
|
1058
|
+
detectNegatedIntents,
|
|
1059
|
+
NEGATION_PATTERNS,
|
|
1060
|
+
// Typo tolerance exports
|
|
1061
|
+
levenshteinDistance,
|
|
1062
|
+
isTypoMatch,
|
|
1063
|
+
findTypoMatch,
|
|
1064
|
+
COMMON_TYPOS,
|
|
1065
|
+
// Question detection exports
|
|
1066
|
+
detectSentenceType,
|
|
1067
|
+
SENTENCE_TYPES,
|
|
1068
|
+
QUESTION_PATTERNS,
|
|
1069
|
+
// Long prompt handling exports
|
|
1070
|
+
truncateForClassification,
|
|
1071
|
+
PROMPT_LIMITS,
|
|
1072
|
+
// Session context exports
|
|
1073
|
+
hasContextualReference,
|
|
1074
|
+
CONTEXTUAL_REFERENCE_PATTERNS,
|
|
1075
|
+
};
|