@intend-it/core 4.0.1 → 4.0.3
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 +24 -12
- package/dist/index.js +122 -34
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
### ✨ Key Features
|
|
21
21
|
|
|
22
22
|
- **🧠 AI Code Generation** - Transforms natural language steps into actual code
|
|
23
|
+
- **🌐 Global Context** - Define file-level rules using the `context` keyword
|
|
23
24
|
- **🔀 Hybrid Orchestration** - Mix AI steps with deterministic function calls
|
|
24
25
|
- **🔄 Self-Correction Loop** - Validates output and auto-fixes errors
|
|
25
26
|
- **⚡ Smart Caching (CAS)** - Content-addressable storage for instant rebuilds
|
|
@@ -113,29 +114,40 @@ interface AIGeneratorOptions {
|
|
|
113
114
|
}
|
|
114
115
|
```
|
|
115
116
|
|
|
116
|
-
##
|
|
117
|
+
## 🔒 Build Determinism & Lockfiles
|
|
117
118
|
|
|
118
|
-
The
|
|
119
|
+
The core package includes a `LockfileManager` that ensures generated code is deterministic and reusable.
|
|
119
120
|
|
|
120
121
|
```typescript
|
|
121
|
-
import {
|
|
122
|
+
import { LockfileManager, computeHash } from "@intend-it/core";
|
|
122
123
|
|
|
123
|
-
const
|
|
124
|
+
const lockManager = new LockfileManager("./intend.lock");
|
|
124
125
|
|
|
125
|
-
//
|
|
126
|
-
const
|
|
127
|
-
const
|
|
126
|
+
// Calculate hashes
|
|
127
|
+
const sourceHash = computeHash(intentSource);
|
|
128
|
+
const configHash = computeHash(JSON.stringify(config));
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
// Try to get from lockfile
|
|
131
|
+
const cachedCode = lockManager.getEntry(relativePath, sourceHash, configHash);
|
|
132
|
+
|
|
133
|
+
if (cachedCode) {
|
|
134
|
+
return cachedCode; // Instant build!
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
// Generate and
|
|
137
|
+
// Generate new code and save to lockfile
|
|
135
138
|
const result = await generator.generate(ast);
|
|
136
|
-
|
|
139
|
+
lockManager.setEntry(relativePath, sourceHash, configHash, result.code, {
|
|
140
|
+
model: "gemini-2.0-flash",
|
|
141
|
+
provider: "gemini"
|
|
142
|
+
});
|
|
143
|
+
lockManager.save();
|
|
137
144
|
```
|
|
138
145
|
|
|
146
|
+
### Why a Lockfile?
|
|
147
|
+
- **Determinism**: Prevents minor AI fluctuations from changing your production code.
|
|
148
|
+
- **Performance**: Skips expensive LLM calls for unchanged intentions.
|
|
149
|
+
- **Auditability**: Changes to generated code appear in your git diffs, making it easy to review what the AI has changed.
|
|
150
|
+
|
|
139
151
|
## Architecture
|
|
140
152
|
|
|
141
153
|
```
|
package/dist/index.js
CHANGED
|
@@ -169173,9 +169173,10 @@ ${request.prompt}`;
|
|
|
169173
169173
|
}
|
|
169174
169174
|
// src/ai/prompt-builder.ts
|
|
169175
169175
|
class PromptBuilder {
|
|
169176
|
-
buildContext(intent, fileImports, importedIntents = []) {
|
|
169176
|
+
buildContext(intent, fileImports, importedIntents = [], fileContext) {
|
|
169177
169177
|
const returnType = this.formatReturnType(intent.returnType);
|
|
169178
169178
|
return {
|
|
169179
|
+
fileContext,
|
|
169179
169180
|
functionName: intent.name,
|
|
169180
169181
|
parameters: intent.parameters,
|
|
169181
169182
|
returnType,
|
|
@@ -169209,6 +169210,11 @@ class PromptBuilder {
|
|
|
169209
169210
|
const lines = [];
|
|
169210
169211
|
lines.push(`Implement this TypeScript function step:
|
|
169211
169212
|
`);
|
|
169213
|
+
if (context.fileContext) {
|
|
169214
|
+
lines.push(`GLOBAL CONTEXT / RULES:`);
|
|
169215
|
+
lines.push(context.fileContext);
|
|
169216
|
+
lines.push("");
|
|
169217
|
+
}
|
|
169212
169218
|
lines.push(`Function: ${context.functionName}`);
|
|
169213
169219
|
lines.push(`Parameters: ${this.formatParameters(context.parameters)}`);
|
|
169214
169220
|
lines.push(`Return Type: ${context.returnType}
|
|
@@ -169270,6 +169276,11 @@ Generate ONLY the TypeScript code for this step (no explanations, no markdown).`
|
|
|
169270
169276
|
const lines = [];
|
|
169271
169277
|
lines.push(`Implement this complete TypeScript function:
|
|
169272
169278
|
`);
|
|
169279
|
+
if (context.fileContext) {
|
|
169280
|
+
lines.push(`GLOBAL CONTEXT / RULES:`);
|
|
169281
|
+
lines.push(context.fileContext);
|
|
169282
|
+
lines.push("");
|
|
169283
|
+
}
|
|
169273
169284
|
lines.push(`Function: ${context.functionName}`);
|
|
169274
169285
|
lines.push(`Parameters: ${this.formatParameters(context.parameters)}`);
|
|
169275
169286
|
lines.push(`Return Type: ${context.returnType}
|
|
@@ -169297,22 +169308,7 @@ Generate ONLY the TypeScript code for this step (no explanations, no markdown).`
|
|
|
169297
169308
|
lines.push("");
|
|
169298
169309
|
}
|
|
169299
169310
|
lines.push(`Implementation Steps (in order):`);
|
|
169300
|
-
context.steps
|
|
169301
|
-
let line = `${i + 1}. `;
|
|
169302
|
-
if (step.type === "Call") {
|
|
169303
|
-
const args = step.args.map((a) => a.type === "String" ? JSON.stringify(a.value) : a.value).join(", ");
|
|
169304
|
-
line += `Call: ${step.intent}(${args})`;
|
|
169305
|
-
} else {
|
|
169306
|
-
line += `"${step.instruction}"`;
|
|
169307
|
-
}
|
|
169308
|
-
if (step.resultVariable) {
|
|
169309
|
-
line += ` => const ${step.resultVariable}`;
|
|
169310
|
-
if (step.type === "Step" && step.resultType) {
|
|
169311
|
-
line += `: ${step.resultType}`;
|
|
169312
|
-
}
|
|
169313
|
-
}
|
|
169314
|
-
lines.push(line);
|
|
169315
|
-
});
|
|
169311
|
+
lines.push(this.formatSteps(context.steps, 0));
|
|
169316
169312
|
lines.push("");
|
|
169317
169313
|
if (context.ensures.length > 0) {
|
|
169318
169314
|
lines.push(`Postconditions (must be true at the end):`);
|
|
@@ -169349,27 +169345,58 @@ Return ONLY the function body code (no function signature, no markdown).`);
|
|
|
169349
169345
|
}
|
|
169350
169346
|
return type;
|
|
169351
169347
|
}
|
|
169348
|
+
formatSteps(steps, indentLevel) {
|
|
169349
|
+
const indent = " ".repeat(indentLevel);
|
|
169350
|
+
const lines = [];
|
|
169351
|
+
steps.forEach((step, i) => {
|
|
169352
|
+
let line = `${indent}${i + 1}. `;
|
|
169353
|
+
if (step.type === "Call") {
|
|
169354
|
+
const args = step.args.map((a) => a.type === "String" ? JSON.stringify(a.value) : a.value).join(", ");
|
|
169355
|
+
line += `Call: ${step.intent}(${args})`;
|
|
169356
|
+
} else {
|
|
169357
|
+
line += `"${step.instruction}"`;
|
|
169358
|
+
}
|
|
169359
|
+
if (step.resultVariable) {
|
|
169360
|
+
line += ` => ${step.variableKind || "const"} ${step.resultVariable}`;
|
|
169361
|
+
if (step.type === "Step" && step.resultType) {
|
|
169362
|
+
line += `: ${step.resultType}`;
|
|
169363
|
+
}
|
|
169364
|
+
}
|
|
169365
|
+
lines.push(line);
|
|
169366
|
+
if (step.type === "Step" && step.nestedBody) {
|
|
169367
|
+
lines.push(`${indent} BLOCK START (This step contains a nested logic block):`);
|
|
169368
|
+
lines.push(this.formatSteps(step.nestedBody.steps, indentLevel + 1));
|
|
169369
|
+
lines.push(`${indent} BLOCK END`);
|
|
169370
|
+
}
|
|
169371
|
+
});
|
|
169372
|
+
return lines.join(`
|
|
169373
|
+
`);
|
|
169374
|
+
}
|
|
169352
169375
|
buildReviewerSystemPrompt() {
|
|
169353
|
-
return `You are a senior code reviewer specializing in
|
|
169376
|
+
return `You are a pragmatic senior code reviewer specializing in ensuring TypeScript code is functionally correct and safe.
|
|
169377
|
+
|
|
169378
|
+
Your task is to review generated code for logical errors that would cause runtime failures or violate the business intent.
|
|
169354
169379
|
|
|
169355
|
-
|
|
169380
|
+
Rules for Approval:
|
|
169381
|
+
- BE PRAGMATIC: If the code is functionally correct and will not crash, APPROVE it.
|
|
169382
|
+
- DO NOT be pedantic about 'step' names. If a step asks to "Call GenerateID" and the code uses "crypto.randomUUID()", this is CORRECT.
|
|
169383
|
+
- DO NOT fail for unused variables (e.g. intermediate variables used in object construction).
|
|
169384
|
+
- DO NOT fail for minor stylistic choices or variable shadowing unless it causes a bug.
|
|
169356
169385
|
|
|
169357
|
-
|
|
169358
|
-
1.
|
|
169359
|
-
2.
|
|
169360
|
-
3.
|
|
169361
|
-
4.
|
|
169362
|
-
5. TYPE
|
|
169363
|
-
6. DEAD CODE: Code that can never be reached.
|
|
169386
|
+
Critical Issues to detect (FAIL only for these):
|
|
169387
|
+
1. BUGGY SHADOWING: An inner variable/function with the same name as the outer, causing a reference error or calling the wrong logic.
|
|
169388
|
+
2. INFINITE LOOPS: Loops or recursion with no exit condition.
|
|
169389
|
+
3. LOGIC GAP: The code completely ignores a requirement (e.g. failing to save a record that was requested).
|
|
169390
|
+
4. CRASH POTENTIAL: Accessing properties of 'undefined' without checks, or unhandled 'null' returns.
|
|
169391
|
+
5. TYPE CONTRACT VIOLATION: Returning a type that deviates from the signature.
|
|
169364
169392
|
|
|
169365
169393
|
Response Format:
|
|
169366
|
-
- If
|
|
169367
|
-
- If there are
|
|
169394
|
+
- If functionally correct and safe code, respond with exactly: APPROVED
|
|
169395
|
+
- If there are CRITICAL ISSUES, respond with:
|
|
169368
169396
|
ISSUES:
|
|
169369
|
-
- [
|
|
169370
|
-
- [Issue 2 description]
|
|
169397
|
+
- [Critical issue description]
|
|
169371
169398
|
...
|
|
169372
|
-
FIX: [
|
|
169399
|
+
FIX: [Short description of required fix]`;
|
|
169373
169400
|
}
|
|
169374
169401
|
buildReviewerPrompt(context, code) {
|
|
169375
169402
|
const lines = [];
|
|
@@ -169908,7 +169935,7 @@ class AICodeGenerator {
|
|
|
169908
169935
|
}
|
|
169909
169936
|
}
|
|
169910
169937
|
}
|
|
169911
|
-
const context = this.promptBuilder.buildContext(intent, file.imports, importedIntents);
|
|
169938
|
+
const context = this.promptBuilder.buildContext(intent, file.imports, importedIntents, file.context);
|
|
169912
169939
|
totalSteps += context.steps.length;
|
|
169913
169940
|
if (this.mode === "step-by-step") {
|
|
169914
169941
|
currentCode = await this.generateFull(currentCode, context, intent.name);
|
|
@@ -169951,7 +169978,8 @@ class AICodeGenerator {
|
|
|
169951
169978
|
console.log(errorMsg.split(`
|
|
169952
169979
|
`).map((l) => ` ${l}`).join(`
|
|
169953
169980
|
`));
|
|
169954
|
-
console.log(`
|
|
169981
|
+
console.log(`
|
|
169982
|
+
Retrying with AI auto-correction...
|
|
169955
169983
|
`);
|
|
169956
169984
|
prompt += `
|
|
169957
169985
|
|
|
@@ -169988,7 +170016,8 @@ ${errorMsg}`);
|
|
|
169988
170016
|
\uD83D\uDD0D Logic review found issues for ${functionName} (Attempt ${attempts}/${limit}):`);
|
|
169989
170017
|
console.log(` ${logicIssues.issues.slice(0, 3).join(`
|
|
169990
170018
|
`)}`);
|
|
169991
|
-
console.log(`
|
|
170019
|
+
console.log(`
|
|
170020
|
+
Retrying with AI auto-correction...
|
|
169992
170021
|
`);
|
|
169993
170022
|
prompt += `
|
|
169994
170023
|
|
|
@@ -170178,6 +170207,64 @@ function computeHash(content, config) {
|
|
|
170178
170207
|
hash.update(JSON.stringify(config));
|
|
170179
170208
|
return hash.digest("hex");
|
|
170180
170209
|
}
|
|
170210
|
+
// src/lockfile/manager.ts
|
|
170211
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
170212
|
+
|
|
170213
|
+
class LockfileManager {
|
|
170214
|
+
filePath;
|
|
170215
|
+
lockfile;
|
|
170216
|
+
constructor(filePath) {
|
|
170217
|
+
this.filePath = filePath;
|
|
170218
|
+
this.lockfile = this.load();
|
|
170219
|
+
}
|
|
170220
|
+
load() {
|
|
170221
|
+
if (!existsSync2(this.filePath)) {
|
|
170222
|
+
return {
|
|
170223
|
+
version: "1.0",
|
|
170224
|
+
files: {}
|
|
170225
|
+
};
|
|
170226
|
+
}
|
|
170227
|
+
try {
|
|
170228
|
+
const content = readFileSync2(this.filePath, "utf-8");
|
|
170229
|
+
return JSON.parse(content);
|
|
170230
|
+
} catch (error) {
|
|
170231
|
+
console.warn(`Failed to read lockfile at ${this.filePath}. Starting fresh.`);
|
|
170232
|
+
return {
|
|
170233
|
+
version: "1.0",
|
|
170234
|
+
files: {}
|
|
170235
|
+
};
|
|
170236
|
+
}
|
|
170237
|
+
}
|
|
170238
|
+
save() {
|
|
170239
|
+
try {
|
|
170240
|
+
writeFileSync2(this.filePath, JSON.stringify(this.lockfile, null, 2), "utf-8");
|
|
170241
|
+
} catch (error) {
|
|
170242
|
+
throw new Error(`Failed to write lockfile: ${error.message}`);
|
|
170243
|
+
}
|
|
170244
|
+
}
|
|
170245
|
+
getEntry(relativePath, sourceHash, configHash) {
|
|
170246
|
+
const entry = this.lockfile.files[relativePath];
|
|
170247
|
+
if (!entry)
|
|
170248
|
+
return null;
|
|
170249
|
+
if (entry.sourceHash === sourceHash && entry.configHash === configHash) {
|
|
170250
|
+
return entry.generatedCode;
|
|
170251
|
+
}
|
|
170252
|
+
return null;
|
|
170253
|
+
}
|
|
170254
|
+
setEntry(relativePath, sourceHash, configHash, code, metadata) {
|
|
170255
|
+
this.lockfile.files[relativePath] = {
|
|
170256
|
+
sourceHash,
|
|
170257
|
+
configHash,
|
|
170258
|
+
generatedCode: code,
|
|
170259
|
+
model: metadata?.model,
|
|
170260
|
+
provider: metadata?.provider,
|
|
170261
|
+
timestamp: Date.now()
|
|
170262
|
+
};
|
|
170263
|
+
}
|
|
170264
|
+
getLockfile() {
|
|
170265
|
+
return this.lockfile;
|
|
170266
|
+
}
|
|
170267
|
+
}
|
|
170181
170268
|
export {
|
|
170182
170269
|
generateTypeScript,
|
|
170183
170270
|
debugLogger,
|
|
@@ -170186,6 +170273,7 @@ export {
|
|
|
170186
170273
|
TypeScriptGenerator,
|
|
170187
170274
|
PromptBuilder,
|
|
170188
170275
|
OllamaProvider,
|
|
170276
|
+
LockfileManager,
|
|
170189
170277
|
GeminiProvider,
|
|
170190
170278
|
GeminiClient,
|
|
170191
170279
|
FileSystemCAS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intend-it/core",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "Core compiler and AI integration for the Intend programming language",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@intend-it/parser": "^1.3.
|
|
38
|
+
"@intend-it/parser": "^1.3.3",
|
|
39
39
|
"@google/generative-ai": "^0.21.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
@@ -43,6 +43,6 @@
|
|
|
43
43
|
"typescript": "^5.0.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@intend-it/parser": ">=1.3.
|
|
46
|
+
"@intend-it/parser": ">=1.3.3"
|
|
47
47
|
}
|
|
48
48
|
}
|