@getmikk/ai-context 1.2.0 → 1.3.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/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { ContextBuilder } from './context-builder.js'
2
- export { ClaudeMdGenerator } from './claude-md-generator.js'
3
- export { ClaudeProvider, GenericProvider, getProvider } from './providers.js'
4
- export type { AIContext, ContextModule, ContextFunction, ContextQuery, ContextProvider } from './types.js'
1
+ export { ContextBuilder } from './context-builder.js'
2
+ export { ClaudeMdGenerator } from './claude-md-generator.js'
3
+ export { ClaudeProvider, GenericProvider, getProvider } from './providers.js'
4
+ export type { AIContext, ContextModule, ContextFunction, ContextQuery, ContextProvider } from './types.js'
package/src/providers.ts CHANGED
@@ -1,155 +1,155 @@
1
- import type { AIContext, ContextProvider } from './types.js'
2
-
3
- /**
4
- * ClaudeProvider — formats context for Anthropic Claude models.
5
- *
6
- * Uses structured XML tags so Claude can parse boundaries clearly.
7
- * Includes meta block so the model knows how much context was trimmed.
8
- */
9
- export class ClaudeProvider implements ContextProvider {
10
- name = 'claude'
11
- maxTokens = 200000
12
-
13
- formatContext(context: AIContext): string {
14
- const lines: string[] = []
15
-
16
- lines.push('<mikk_context>')
17
-
18
- // ── Project ────────────────────────────────────────────────────────
19
- lines.push(`<project name="${esc(context.project.name)}" language="${esc(context.project.language)}">`)
20
- lines.push(` <description>${esc(context.project.description)}</description>`)
21
- lines.push(` <stats modules="${context.project.moduleCount}" functions="${context.project.functionCount}"/>`)
22
- lines.push('</project>')
23
- lines.push('')
24
-
25
- // ── Context quality meta ───────────────────────────────────────────
26
- lines.push('<context_meta>')
27
- lines.push(` <task>${esc(context.meta?.keywords?.join(', ') ?? '')}</task>`)
28
- lines.push(` <seeds_found>${context.meta?.seedCount ?? 0}</seeds_found>`)
29
- lines.push(` <functions_selected>${context.meta?.selectedFunctions ?? 0} of ${context.meta?.totalFunctionsConsidered ?? 0}</functions_selected>`)
30
- lines.push(` <estimated_tokens>${context.meta?.estimatedTokens ?? 0}</estimated_tokens>`)
31
- lines.push('</context_meta>')
32
- lines.push('')
33
-
34
- // ── Modules ────────────────────────────────────────────────────────
35
- for (const mod of context.modules) {
36
- lines.push(`<module id="${esc(mod.id)}" name="${esc(mod.name)}">`)
37
- lines.push(` <description>${esc(mod.description)}</description>`)
38
- if (mod.intent) lines.push(` <intent>${esc(mod.intent)}</intent>`)
39
- lines.push(` <files count="${mod.files.length}">`)
40
- for (const f of mod.files) {
41
- lines.push(` <file>${esc(f)}</file>`)
42
- }
43
- lines.push(' </files>')
44
-
45
- if (mod.functions.length > 0) {
46
- lines.push(' <functions>')
47
- for (const fn of mod.functions) {
48
- const calls = fn.calls.length > 0
49
- ? ` calls="${esc(fn.calls.join(','))}"`
50
- : ''
51
- const calledBy = fn.calledBy.length > 0
52
- ? ` calledBy="${esc(fn.calledBy.join(','))}"`
53
- : ''
54
- lines.push(` <fn name="${esc(fn.name)}" file="${esc(fn.file)}" lines="${fn.startLine}-${fn.endLine}"${calls}${calledBy}>`)
55
- if (fn.purpose) lines.push(` <purpose>${esc(fn.purpose)}</purpose>`)
56
- if (fn.edgeCases && fn.edgeCases.length > 0) {
57
- lines.push(` <edge_cases>${esc(fn.edgeCases.join('; '))}</edge_cases>`)
58
- }
59
- if (fn.errorHandling && fn.errorHandling.length > 0) {
60
- lines.push(` <error_handling>${esc(fn.errorHandling.join('; '))}</error_handling>`)
61
- }
62
- lines.push(' </fn>')
63
- }
64
- lines.push(' </functions>')
65
- }
66
- lines.push('</module>')
67
- lines.push('')
68
- }
69
-
70
- // ── Constraints ────────────────────────────────────────────────────
71
- if (context.constraints.length > 0) {
72
- lines.push('<constraints>')
73
- for (const c of context.constraints) {
74
- lines.push(` <constraint>${esc(c)}</constraint>`)
75
- }
76
- lines.push('</constraints>')
77
- lines.push('')
78
- }
79
-
80
- // ── Decisions ─────────────────────────────────────────────────────
81
- if (context.decisions.length > 0) {
82
- lines.push('<architectural_decisions>')
83
- for (const d of context.decisions) {
84
- lines.push(` <decision title="${esc(d.title)}">${esc(d.reason)}</decision>`)
85
- }
86
- lines.push('</architectural_decisions>')
87
- }
88
-
89
- lines.push('</mikk_context>')
90
- return lines.join('\n')
91
- }
92
- }
93
-
94
- /**
95
- * GenericProvider — clean plain-text format for any model.
96
- * Identical to the natural-language prompt generated by ContextBuilder.
97
- */
98
- export class GenericProvider implements ContextProvider {
99
- name = 'generic'
100
- maxTokens = 128000
101
-
102
- formatContext(context: AIContext): string {
103
- return context.prompt
104
- }
105
- }
106
-
107
- /**
108
- * CompactProvider — ultra-minimal format for small context windows.
109
- * One line per function, no XML, no prose.
110
- */
111
- export class CompactProvider implements ContextProvider {
112
- name = 'compact'
113
- maxTokens = 16000
114
-
115
- formatContext(context: AIContext): string {
116
- const lines: string[] = [
117
- `# ${context.project.name} (${context.project.language})`,
118
- `Task keywords: ${context.meta?.keywords?.join(', ') ?? ''}`,
119
- '',
120
- ]
121
- for (const mod of context.modules) {
122
- lines.push(`## ${mod.name}`)
123
- for (const fn of mod.functions) {
124
- const calls = fn.calls.length > 0 ? ` → ${fn.calls.join(',')}` : ''
125
- lines.push(` ${fn.name} [${fn.file}:${fn.startLine}]${calls}`)
126
- }
127
- lines.push('')
128
- }
129
- if (context.constraints.length > 0) {
130
- lines.push('CONSTRAINTS: ' + context.constraints.join(' | '))
131
- }
132
- return lines.join('\n')
133
- }
134
- }
135
-
136
- export function getProvider(name: string): ContextProvider {
137
- switch (name.toLowerCase()) {
138
- case 'claude':
139
- case 'anthropic':
140
- return new ClaudeProvider()
141
- case 'compact':
142
- return new CompactProvider()
143
- default:
144
- return new GenericProvider()
145
- }
146
- }
147
-
148
- /** Minimal XML attribute escaping */
149
- function esc(s: string): string {
150
- return s
151
- .replace(/&/g, '&amp;')
152
- .replace(/</g, '&lt;')
153
- .replace(/>/g, '&gt;')
154
- .replace(/"/g, '&quot;')
1
+ import type { AIContext, ContextProvider } from './types.js'
2
+
3
+ /**
4
+ * ClaudeProvider — formats context for Anthropic Claude models.
5
+ *
6
+ * Uses structured XML tags so Claude can parse boundaries clearly.
7
+ * Includes meta block so the model knows how much context was trimmed.
8
+ */
9
+ export class ClaudeProvider implements ContextProvider {
10
+ name = 'claude'
11
+ maxTokens = 200000
12
+
13
+ formatContext(context: AIContext): string {
14
+ const lines: string[] = []
15
+
16
+ lines.push('<mikk_context>')
17
+
18
+ // ── Project ────────────────────────────────────────────────────────
19
+ lines.push(`<project name="${esc(context.project.name)}" language="${esc(context.project.language)}">`)
20
+ lines.push(` <description>${esc(context.project.description)}</description>`)
21
+ lines.push(` <stats modules="${context.project.moduleCount}" functions="${context.project.functionCount}"/>`)
22
+ lines.push('</project>')
23
+ lines.push('')
24
+
25
+ // ── Context quality meta ───────────────────────────────────────────
26
+ lines.push('<context_meta>')
27
+ lines.push(` <task>${esc(context.meta?.keywords?.join(', ') ?? '')}</task>`)
28
+ lines.push(` <seeds_found>${context.meta?.seedCount ?? 0}</seeds_found>`)
29
+ lines.push(` <functions_selected>${context.meta?.selectedFunctions ?? 0} of ${context.meta?.totalFunctionsConsidered ?? 0}</functions_selected>`)
30
+ lines.push(` <estimated_tokens>${context.meta?.estimatedTokens ?? 0}</estimated_tokens>`)
31
+ lines.push('</context_meta>')
32
+ lines.push('')
33
+
34
+ // ── Modules ────────────────────────────────────────────────────────
35
+ for (const mod of context.modules) {
36
+ lines.push(`<module id="${esc(mod.id)}" name="${esc(mod.name)}">`)
37
+ lines.push(` <description>${esc(mod.description)}</description>`)
38
+ if (mod.intent) lines.push(` <intent>${esc(mod.intent)}</intent>`)
39
+ lines.push(` <files count="${mod.files.length}">`)
40
+ for (const f of mod.files) {
41
+ lines.push(` <file>${esc(f)}</file>`)
42
+ }
43
+ lines.push(' </files>')
44
+
45
+ if (mod.functions.length > 0) {
46
+ lines.push(' <functions>')
47
+ for (const fn of mod.functions) {
48
+ const calls = fn.calls.length > 0
49
+ ? ` calls="${esc(fn.calls.join(','))}"`
50
+ : ''
51
+ const calledBy = fn.calledBy.length > 0
52
+ ? ` calledBy="${esc(fn.calledBy.join(','))}"`
53
+ : ''
54
+ lines.push(` <fn name="${esc(fn.name)}" file="${esc(fn.file)}" lines="${fn.startLine}-${fn.endLine}"${calls}${calledBy}>`)
55
+ if (fn.purpose) lines.push(` <purpose>${esc(fn.purpose)}</purpose>`)
56
+ if (fn.edgeCases && fn.edgeCases.length > 0) {
57
+ lines.push(` <edge_cases>${esc(fn.edgeCases.join('; '))}</edge_cases>`)
58
+ }
59
+ if (fn.errorHandling && fn.errorHandling.length > 0) {
60
+ lines.push(` <error_handling>${esc(fn.errorHandling.join('; '))}</error_handling>`)
61
+ }
62
+ lines.push(' </fn>')
63
+ }
64
+ lines.push(' </functions>')
65
+ }
66
+ lines.push('</module>')
67
+ lines.push('')
68
+ }
69
+
70
+ // ── Constraints ────────────────────────────────────────────────────
71
+ if (context.constraints.length > 0) {
72
+ lines.push('<constraints>')
73
+ for (const c of context.constraints) {
74
+ lines.push(` <constraint>${esc(c)}</constraint>`)
75
+ }
76
+ lines.push('</constraints>')
77
+ lines.push('')
78
+ }
79
+
80
+ // ── Decisions ─────────────────────────────────────────────────────
81
+ if (context.decisions.length > 0) {
82
+ lines.push('<architectural_decisions>')
83
+ for (const d of context.decisions) {
84
+ lines.push(` <decision title="${esc(d.title)}">${esc(d.reason)}</decision>`)
85
+ }
86
+ lines.push('</architectural_decisions>')
87
+ }
88
+
89
+ lines.push('</mikk_context>')
90
+ return lines.join('\n')
91
+ }
92
+ }
93
+
94
+ /**
95
+ * GenericProvider — clean plain-text format for any model.
96
+ * Identical to the natural-language prompt generated by ContextBuilder.
97
+ */
98
+ export class GenericProvider implements ContextProvider {
99
+ name = 'generic'
100
+ maxTokens = 128000
101
+
102
+ formatContext(context: AIContext): string {
103
+ return context.prompt
104
+ }
105
+ }
106
+
107
+ /**
108
+ * CompactProvider — ultra-minimal format for small context windows.
109
+ * One line per function, no XML, no prose.
110
+ */
111
+ export class CompactProvider implements ContextProvider {
112
+ name = 'compact'
113
+ maxTokens = 16000
114
+
115
+ formatContext(context: AIContext): string {
116
+ const lines: string[] = [
117
+ `# ${context.project.name} (${context.project.language})`,
118
+ `Task keywords: ${context.meta?.keywords?.join(', ') ?? ''}`,
119
+ '',
120
+ ]
121
+ for (const mod of context.modules) {
122
+ lines.push(`## ${mod.name}`)
123
+ for (const fn of mod.functions) {
124
+ const calls = fn.calls.length > 0 ? ` → ${fn.calls.join(',')}` : ''
125
+ lines.push(` ${fn.name} [${fn.file}:${fn.startLine}]${calls}`)
126
+ }
127
+ lines.push('')
128
+ }
129
+ if (context.constraints.length > 0) {
130
+ lines.push('CONSTRAINTS: ' + context.constraints.join(' | '))
131
+ }
132
+ return lines.join('\n')
133
+ }
134
+ }
135
+
136
+ export function getProvider(name: string): ContextProvider {
137
+ switch (name.toLowerCase()) {
138
+ case 'claude':
139
+ case 'anthropic':
140
+ return new ClaudeProvider()
141
+ case 'compact':
142
+ return new CompactProvider()
143
+ default:
144
+ return new GenericProvider()
145
+ }
146
+ }
147
+
148
+ /** Minimal XML attribute escaping */
149
+ function esc(s: string): string {
150
+ return s
151
+ .replace(/&/g, '&amp;')
152
+ .replace(/</g, '&lt;')
153
+ .replace(/>/g, '&gt;')
154
+ .replace(/"/g, '&quot;')
155
155
  }
package/src/types.ts CHANGED
@@ -1,70 +1,70 @@
1
- import type { MikkContract, MikkLock, MikkLockFunction } from '@getmikk/core'
2
-
3
- /** The structured context object passed to AI models */
4
- export interface AIContext {
5
- project: {
6
- name: string
7
- language: string
8
- description: string
9
- moduleCount: number
10
- functionCount: number
11
- }
12
- modules: ContextModule[]
13
- constraints: string[]
14
- decisions: { title: string; reason: string }[]
15
- prompt: string
16
- /** Diagnostic info — helpful for debugging context quality */
17
- meta: {
18
- seedCount: number
19
- totalFunctionsConsidered: number
20
- selectedFunctions: number
21
- estimatedTokens: number
22
- keywords: string[]
23
- }
24
- }
25
-
26
- export interface ContextModule {
27
- id: string
28
- name: string
29
- description: string
30
- intent?: string
31
- functions: ContextFunction[]
32
- files: string[]
33
- }
34
-
35
- export interface ContextFunction {
36
- name: string
37
- file: string
38
- startLine: number
39
- endLine: number
40
- calls: string[]
41
- calledBy: string[]
42
- purpose?: string
43
- errorHandling?: string[]
44
- edgeCases?: string[]
45
- }
46
-
47
- /** Query options for context generation */
48
- export interface ContextQuery {
49
- /** The user's task description — the primary relevance signal */
50
- task: string
51
- /** Specific files to anchor the graph traversal from */
52
- focusFiles?: string[]
53
- /** Specific modules to include */
54
- focusModules?: string[]
55
- /** Max functions to include in output (hard cap) */
56
- maxFunctions?: number
57
- /** Max BFS hops from seed nodes (default 4) */
58
- maxHops?: number
59
- /** Approximate token budget for function listings (default 6000) */
60
- tokenBudget?: number
61
- /** Include call graph arrows (default true) */
62
- includeCallGraph?: boolean
63
- }
64
-
65
- /** Context provider interface for different AI platforms */
66
- export interface ContextProvider {
67
- name: string
68
- formatContext(context: AIContext): string
69
- maxTokens: number
1
+ import type { MikkContract, MikkLock, MikkLockFunction } from '@getmikk/core'
2
+
3
+ /** The structured context object passed to AI models */
4
+ export interface AIContext {
5
+ project: {
6
+ name: string
7
+ language: string
8
+ description: string
9
+ moduleCount: number
10
+ functionCount: number
11
+ }
12
+ modules: ContextModule[]
13
+ constraints: string[]
14
+ decisions: { title: string; reason: string }[]
15
+ prompt: string
16
+ /** Diagnostic info — helpful for debugging context quality */
17
+ meta: {
18
+ seedCount: number
19
+ totalFunctionsConsidered: number
20
+ selectedFunctions: number
21
+ estimatedTokens: number
22
+ keywords: string[]
23
+ }
24
+ }
25
+
26
+ export interface ContextModule {
27
+ id: string
28
+ name: string
29
+ description: string
30
+ intent?: string
31
+ functions: ContextFunction[]
32
+ files: string[]
33
+ }
34
+
35
+ export interface ContextFunction {
36
+ name: string
37
+ file: string
38
+ startLine: number
39
+ endLine: number
40
+ calls: string[]
41
+ calledBy: string[]
42
+ purpose?: string
43
+ errorHandling?: string[]
44
+ edgeCases?: string[]
45
+ }
46
+
47
+ /** Query options for context generation */
48
+ export interface ContextQuery {
49
+ /** The user's task description — the primary relevance signal */
50
+ task: string
51
+ /** Specific files to anchor the graph traversal from */
52
+ focusFiles?: string[]
53
+ /** Specific modules to include */
54
+ focusModules?: string[]
55
+ /** Max functions to include in output (hard cap) */
56
+ maxFunctions?: number
57
+ /** Max BFS hops from seed nodes (default 4) */
58
+ maxHops?: number
59
+ /** Approximate token budget for function listings (default 6000) */
60
+ tokenBudget?: number
61
+ /** Include call graph arrows (default true) */
62
+ includeCallGraph?: boolean
63
+ }
64
+
65
+ /** Context provider interface for different AI platforms */
66
+ export interface ContextProvider {
67
+ name: string
68
+ formatContext(context: AIContext): string
69
+ maxTokens: number
70
70
  }