@getmikk/intent-engine 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/README.md +257 -0
- package/package.json +31 -27
- package/src/conflict-detector.ts +302 -302
- package/src/index.ts +6 -6
- package/src/interpreter.ts +216 -216
- package/src/preflight.ts +44 -44
- package/src/suggester.ts +104 -104
- package/src/types.ts +54 -54
- package/tests/intent-engine.test.ts +210 -210
- package/tests/smoke.test.ts +5 -5
- package/tsconfig.json +15 -15
package/src/suggester.ts
CHANGED
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
import type { MikkContract, MikkLock } from '@getmikk/core'
|
|
2
|
-
import type { Intent, Suggestion } from './types.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Suggester — given validated intents, produces implementation suggestions
|
|
6
|
-
* with affected files, new files, and estimated impact.
|
|
7
|
-
*/
|
|
8
|
-
export class Suggester {
|
|
9
|
-
constructor(
|
|
10
|
-
private contract: MikkContract,
|
|
11
|
-
private lock: MikkLock
|
|
12
|
-
) { }
|
|
13
|
-
|
|
14
|
-
/** Generate suggestions for a list of intents */
|
|
15
|
-
suggest(intents: Intent[]): Suggestion[] {
|
|
16
|
-
return intents.map(intent => this.suggestForIntent(intent))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
private suggestForIntent(intent: Intent): Suggestion {
|
|
20
|
-
const affectedFiles: string[] = []
|
|
21
|
-
const newFiles: string[] = []
|
|
22
|
-
|
|
23
|
-
switch (intent.action) {
|
|
24
|
-
case 'create': {
|
|
25
|
-
// Find the target module's directory pattern
|
|
26
|
-
const module = this.contract.declared.modules.find(m => m.id === intent.target.moduleId)
|
|
27
|
-
if (module) {
|
|
28
|
-
const dir = module.paths[0]?.replace('/**', '') || 'src'
|
|
29
|
-
newFiles.push(`${dir}/${intent.target.name}.ts`)
|
|
30
|
-
} else {
|
|
31
|
-
newFiles.push(`src/${intent.target.name}.ts`)
|
|
32
|
-
}
|
|
33
|
-
break
|
|
34
|
-
}
|
|
35
|
-
case 'modify': {
|
|
36
|
-
if (intent.target.filePath) {
|
|
37
|
-
affectedFiles.push(intent.target.filePath)
|
|
38
|
-
} else {
|
|
39
|
-
// Find files by function name
|
|
40
|
-
const fn = Object.values(this.lock.functions).find(
|
|
41
|
-
f => f.name === intent.target.name
|
|
42
|
-
)
|
|
43
|
-
if (fn) {
|
|
44
|
-
affectedFiles.push(fn.file)
|
|
45
|
-
// Add callers
|
|
46
|
-
for (const callerId of fn.calledBy) {
|
|
47
|
-
const caller = this.lock.functions[callerId]
|
|
48
|
-
if (caller) affectedFiles.push(caller.file)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
break
|
|
53
|
-
}
|
|
54
|
-
case 'delete': {
|
|
55
|
-
if (intent.target.filePath) {
|
|
56
|
-
affectedFiles.push(intent.target.filePath)
|
|
57
|
-
}
|
|
58
|
-
break
|
|
59
|
-
}
|
|
60
|
-
case 'refactor':
|
|
61
|
-
case 'move': {
|
|
62
|
-
if (intent.target.filePath) {
|
|
63
|
-
affectedFiles.push(intent.target.filePath)
|
|
64
|
-
}
|
|
65
|
-
// Add all files that import from the target
|
|
66
|
-
const fn = Object.values(this.lock.functions).find(
|
|
67
|
-
f => f.name === intent.target.name
|
|
68
|
-
)
|
|
69
|
-
if (fn) {
|
|
70
|
-
for (const callerId of fn.calledBy) {
|
|
71
|
-
const caller = this.lock.functions[callerId]
|
|
72
|
-
if (caller && !affectedFiles.includes(caller.file)) {
|
|
73
|
-
affectedFiles.push(caller.file)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
intent,
|
|
83
|
-
affectedFiles: [...new Set(affectedFiles)],
|
|
84
|
-
newFiles,
|
|
85
|
-
estimatedImpact: affectedFiles.length + newFiles.length,
|
|
86
|
-
implementation: this.generateDescription(intent),
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private generateDescription(intent: Intent): string {
|
|
91
|
-
switch (intent.action) {
|
|
92
|
-
case 'create':
|
|
93
|
-
return `Create new ${intent.target.type} "${intent.target.name}" in module ${intent.target.moduleId || 'auto-detected'}`
|
|
94
|
-
case 'modify':
|
|
95
|
-
return `Modify ${intent.target.type} "${intent.target.name}" — ${intent.reason}`
|
|
96
|
-
case 'delete':
|
|
97
|
-
return `Delete ${intent.target.type} "${intent.target.name}" and update all references`
|
|
98
|
-
case 'refactor':
|
|
99
|
-
return `Refactor ${intent.target.type} "${intent.target.name}" in place`
|
|
100
|
-
case 'move':
|
|
101
|
-
return `Move ${intent.target.type} "${intent.target.name}" to new location`
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
1
|
+
import type { MikkContract, MikkLock } from '@getmikk/core'
|
|
2
|
+
import type { Intent, Suggestion } from './types.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Suggester — given validated intents, produces implementation suggestions
|
|
6
|
+
* with affected files, new files, and estimated impact.
|
|
7
|
+
*/
|
|
8
|
+
export class Suggester {
|
|
9
|
+
constructor(
|
|
10
|
+
private contract: MikkContract,
|
|
11
|
+
private lock: MikkLock
|
|
12
|
+
) { }
|
|
13
|
+
|
|
14
|
+
/** Generate suggestions for a list of intents */
|
|
15
|
+
suggest(intents: Intent[]): Suggestion[] {
|
|
16
|
+
return intents.map(intent => this.suggestForIntent(intent))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private suggestForIntent(intent: Intent): Suggestion {
|
|
20
|
+
const affectedFiles: string[] = []
|
|
21
|
+
const newFiles: string[] = []
|
|
22
|
+
|
|
23
|
+
switch (intent.action) {
|
|
24
|
+
case 'create': {
|
|
25
|
+
// Find the target module's directory pattern
|
|
26
|
+
const module = this.contract.declared.modules.find(m => m.id === intent.target.moduleId)
|
|
27
|
+
if (module) {
|
|
28
|
+
const dir = module.paths[0]?.replace('/**', '') || 'src'
|
|
29
|
+
newFiles.push(`${dir}/${intent.target.name}.ts`)
|
|
30
|
+
} else {
|
|
31
|
+
newFiles.push(`src/${intent.target.name}.ts`)
|
|
32
|
+
}
|
|
33
|
+
break
|
|
34
|
+
}
|
|
35
|
+
case 'modify': {
|
|
36
|
+
if (intent.target.filePath) {
|
|
37
|
+
affectedFiles.push(intent.target.filePath)
|
|
38
|
+
} else {
|
|
39
|
+
// Find files by function name
|
|
40
|
+
const fn = Object.values(this.lock.functions).find(
|
|
41
|
+
f => f.name === intent.target.name
|
|
42
|
+
)
|
|
43
|
+
if (fn) {
|
|
44
|
+
affectedFiles.push(fn.file)
|
|
45
|
+
// Add callers
|
|
46
|
+
for (const callerId of fn.calledBy) {
|
|
47
|
+
const caller = this.lock.functions[callerId]
|
|
48
|
+
if (caller) affectedFiles.push(caller.file)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
case 'delete': {
|
|
55
|
+
if (intent.target.filePath) {
|
|
56
|
+
affectedFiles.push(intent.target.filePath)
|
|
57
|
+
}
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
case 'refactor':
|
|
61
|
+
case 'move': {
|
|
62
|
+
if (intent.target.filePath) {
|
|
63
|
+
affectedFiles.push(intent.target.filePath)
|
|
64
|
+
}
|
|
65
|
+
// Add all files that import from the target
|
|
66
|
+
const fn = Object.values(this.lock.functions).find(
|
|
67
|
+
f => f.name === intent.target.name
|
|
68
|
+
)
|
|
69
|
+
if (fn) {
|
|
70
|
+
for (const callerId of fn.calledBy) {
|
|
71
|
+
const caller = this.lock.functions[callerId]
|
|
72
|
+
if (caller && !affectedFiles.includes(caller.file)) {
|
|
73
|
+
affectedFiles.push(caller.file)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
break
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
intent,
|
|
83
|
+
affectedFiles: [...new Set(affectedFiles)],
|
|
84
|
+
newFiles,
|
|
85
|
+
estimatedImpact: affectedFiles.length + newFiles.length,
|
|
86
|
+
implementation: this.generateDescription(intent),
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private generateDescription(intent: Intent): string {
|
|
91
|
+
switch (intent.action) {
|
|
92
|
+
case 'create':
|
|
93
|
+
return `Create new ${intent.target.type} "${intent.target.name}" in module ${intent.target.moduleId || 'auto-detected'}`
|
|
94
|
+
case 'modify':
|
|
95
|
+
return `Modify ${intent.target.type} "${intent.target.name}" — ${intent.reason}`
|
|
96
|
+
case 'delete':
|
|
97
|
+
return `Delete ${intent.target.type} "${intent.target.name}" and update all references`
|
|
98
|
+
case 'refactor':
|
|
99
|
+
return `Refactor ${intent.target.type} "${intent.target.name}" in place`
|
|
100
|
+
case 'move':
|
|
101
|
+
return `Move ${intent.target.type} "${intent.target.name}" to new location`
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
/** A single candidate intent parsed from user prompt */
|
|
4
|
-
export const IntentSchema = z.object({
|
|
5
|
-
action: z.enum(['create', 'modify', 'delete', 'refactor', 'move']),
|
|
6
|
-
target: z.object({
|
|
7
|
-
type: z.enum(['function', 'file', 'module', 'class']),
|
|
8
|
-
name: z.string(),
|
|
9
|
-
moduleId: z.string().optional(),
|
|
10
|
-
filePath: z.string().optional(),
|
|
11
|
-
}),
|
|
12
|
-
reason: z.string(),
|
|
13
|
-
confidence: z.number().min(0).max(1),
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
export type Intent = z.infer<typeof IntentSchema>
|
|
17
|
-
|
|
18
|
-
/** Result of conflict detection */
|
|
19
|
-
export interface ConflictResult {
|
|
20
|
-
hasConflicts: boolean
|
|
21
|
-
conflicts: Conflict[]
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface Conflict {
|
|
25
|
-
type: 'constraint-violation' | 'ownership-conflict' | 'boundary-crossing' | 'missing-dependency'
|
|
26
|
-
severity: 'error' | 'warning'
|
|
27
|
-
message: string
|
|
28
|
-
relatedIntent: Intent
|
|
29
|
-
suggestedFix?: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** A suggestion for how to implement an intent */
|
|
33
|
-
export interface Suggestion {
|
|
34
|
-
intent: Intent
|
|
35
|
-
affectedFiles: string[]
|
|
36
|
-
newFiles: string[]
|
|
37
|
-
estimatedImpact: number
|
|
38
|
-
implementation: string
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Configuration for the AI provider */
|
|
42
|
-
export interface AIProviderConfig {
|
|
43
|
-
provider: 'anthropic' | 'openai' | 'local'
|
|
44
|
-
apiKey?: string
|
|
45
|
-
model?: string
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Preflight result — the final output of the intent pipeline */
|
|
49
|
-
export interface PreflightResult {
|
|
50
|
-
intents: Intent[]
|
|
51
|
-
conflicts: ConflictResult
|
|
52
|
-
suggestions: Suggestion[]
|
|
53
|
-
approved: boolean
|
|
54
|
-
}
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
/** A single candidate intent parsed from user prompt */
|
|
4
|
+
export const IntentSchema = z.object({
|
|
5
|
+
action: z.enum(['create', 'modify', 'delete', 'refactor', 'move']),
|
|
6
|
+
target: z.object({
|
|
7
|
+
type: z.enum(['function', 'file', 'module', 'class']),
|
|
8
|
+
name: z.string(),
|
|
9
|
+
moduleId: z.string().optional(),
|
|
10
|
+
filePath: z.string().optional(),
|
|
11
|
+
}),
|
|
12
|
+
reason: z.string(),
|
|
13
|
+
confidence: z.number().min(0).max(1),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export type Intent = z.infer<typeof IntentSchema>
|
|
17
|
+
|
|
18
|
+
/** Result of conflict detection */
|
|
19
|
+
export interface ConflictResult {
|
|
20
|
+
hasConflicts: boolean
|
|
21
|
+
conflicts: Conflict[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Conflict {
|
|
25
|
+
type: 'constraint-violation' | 'ownership-conflict' | 'boundary-crossing' | 'missing-dependency'
|
|
26
|
+
severity: 'error' | 'warning'
|
|
27
|
+
message: string
|
|
28
|
+
relatedIntent: Intent
|
|
29
|
+
suggestedFix?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A suggestion for how to implement an intent */
|
|
33
|
+
export interface Suggestion {
|
|
34
|
+
intent: Intent
|
|
35
|
+
affectedFiles: string[]
|
|
36
|
+
newFiles: string[]
|
|
37
|
+
estimatedImpact: number
|
|
38
|
+
implementation: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Configuration for the AI provider */
|
|
42
|
+
export interface AIProviderConfig {
|
|
43
|
+
provider: 'anthropic' | 'openai' | 'local'
|
|
44
|
+
apiKey?: string
|
|
45
|
+
model?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Preflight result — the final output of the intent pipeline */
|
|
49
|
+
export interface PreflightResult {
|
|
50
|
+
intents: Intent[]
|
|
51
|
+
conflicts: ConflictResult
|
|
52
|
+
suggestions: Suggestion[]
|
|
53
|
+
approved: boolean
|
|
54
|
+
}
|