@intend-it/core 4.0.2 → 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 +23 -12
- package/dist/index.js +107 -31
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -114,29 +114,40 @@ interface AIGeneratorOptions {
|
|
|
114
114
|
}
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
##
|
|
117
|
+
## 🔒 Build Determinism & Lockfiles
|
|
118
118
|
|
|
119
|
-
The
|
|
119
|
+
The core package includes a `LockfileManager` that ensures generated code is deterministic and reusable.
|
|
120
120
|
|
|
121
121
|
```typescript
|
|
122
|
-
import {
|
|
122
|
+
import { LockfileManager, computeHash } from "@intend-it/core";
|
|
123
123
|
|
|
124
|
-
const
|
|
124
|
+
const lockManager = new LockfileManager("./intend.lock");
|
|
125
125
|
|
|
126
|
-
//
|
|
127
|
-
const
|
|
128
|
-
const
|
|
126
|
+
// Calculate hashes
|
|
127
|
+
const sourceHash = computeHash(intentSource);
|
|
128
|
+
const configHash = computeHash(JSON.stringify(config));
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
// Try to get from lockfile
|
|
131
|
+
const cachedCode = lockManager.getEntry(relativePath, sourceHash, configHash);
|
|
132
|
+
|
|
133
|
+
if (cachedCode) {
|
|
134
|
+
return cachedCode; // Instant build!
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
// Generate and
|
|
137
|
+
// Generate new code and save to lockfile
|
|
136
138
|
const result = await generator.generate(ast);
|
|
137
|
-
|
|
139
|
+
lockManager.setEntry(relativePath, sourceHash, configHash, result.code, {
|
|
140
|
+
model: "gemini-2.0-flash",
|
|
141
|
+
provider: "gemini"
|
|
142
|
+
});
|
|
143
|
+
lockManager.save();
|
|
138
144
|
```
|
|
139
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
|
+
|
|
140
151
|
## Architecture
|
|
141
152
|
|
|
142
153
|
```
|
package/dist/index.js
CHANGED
|
@@ -169308,22 +169308,7 @@ Generate ONLY the TypeScript code for this step (no explanations, no markdown).`
|
|
|
169308
169308
|
lines.push("");
|
|
169309
169309
|
}
|
|
169310
169310
|
lines.push(`Implementation Steps (in order):`);
|
|
169311
|
-
context.steps
|
|
169312
|
-
let line = `${i + 1}. `;
|
|
169313
|
-
if (step.type === "Call") {
|
|
169314
|
-
const args = step.args.map((a) => a.type === "String" ? JSON.stringify(a.value) : a.value).join(", ");
|
|
169315
|
-
line += `Call: ${step.intent}(${args})`;
|
|
169316
|
-
} else {
|
|
169317
|
-
line += `"${step.instruction}"`;
|
|
169318
|
-
}
|
|
169319
|
-
if (step.resultVariable) {
|
|
169320
|
-
line += ` => const ${step.resultVariable}`;
|
|
169321
|
-
if (step.type === "Step" && step.resultType) {
|
|
169322
|
-
line += `: ${step.resultType}`;
|
|
169323
|
-
}
|
|
169324
|
-
}
|
|
169325
|
-
lines.push(line);
|
|
169326
|
-
});
|
|
169311
|
+
lines.push(this.formatSteps(context.steps, 0));
|
|
169327
169312
|
lines.push("");
|
|
169328
169313
|
if (context.ensures.length > 0) {
|
|
169329
169314
|
lines.push(`Postconditions (must be true at the end):`);
|
|
@@ -169360,27 +169345,58 @@ Return ONLY the function body code (no function signature, no markdown).`);
|
|
|
169360
169345
|
}
|
|
169361
169346
|
return type;
|
|
169362
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
|
+
}
|
|
169363
169375
|
buildReviewerSystemPrompt() {
|
|
169364
|
-
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.
|
|
169365
169379
|
|
|
169366
|
-
|
|
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.
|
|
169367
169385
|
|
|
169368
|
-
|
|
169369
|
-
1.
|
|
169370
|
-
2.
|
|
169371
|
-
3.
|
|
169372
|
-
4.
|
|
169373
|
-
5. TYPE
|
|
169374
|
-
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.
|
|
169375
169392
|
|
|
169376
169393
|
Response Format:
|
|
169377
|
-
- If
|
|
169378
|
-
- If there are
|
|
169394
|
+
- If functionally correct and safe code, respond with exactly: APPROVED
|
|
169395
|
+
- If there are CRITICAL ISSUES, respond with:
|
|
169379
169396
|
ISSUES:
|
|
169380
|
-
- [
|
|
169381
|
-
- [Issue 2 description]
|
|
169397
|
+
- [Critical issue description]
|
|
169382
169398
|
...
|
|
169383
|
-
FIX: [
|
|
169399
|
+
FIX: [Short description of required fix]`;
|
|
169384
169400
|
}
|
|
169385
169401
|
buildReviewerPrompt(context, code) {
|
|
169386
169402
|
const lines = [];
|
|
@@ -170000,7 +170016,8 @@ ${errorMsg}`);
|
|
|
170000
170016
|
\uD83D\uDD0D Logic review found issues for ${functionName} (Attempt ${attempts}/${limit}):`);
|
|
170001
170017
|
console.log(` ${logicIssues.issues.slice(0, 3).join(`
|
|
170002
170018
|
`)}`);
|
|
170003
|
-
console.log(`
|
|
170019
|
+
console.log(`
|
|
170020
|
+
Retrying with AI auto-correction...
|
|
170004
170021
|
`);
|
|
170005
170022
|
prompt += `
|
|
170006
170023
|
|
|
@@ -170190,6 +170207,64 @@ function computeHash(content, config) {
|
|
|
170190
170207
|
hash.update(JSON.stringify(config));
|
|
170191
170208
|
return hash.digest("hex");
|
|
170192
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
|
+
}
|
|
170193
170268
|
export {
|
|
170194
170269
|
generateTypeScript,
|
|
170195
170270
|
debugLogger,
|
|
@@ -170198,6 +170273,7 @@ export {
|
|
|
170198
170273
|
TypeScriptGenerator,
|
|
170199
170274
|
PromptBuilder,
|
|
170200
170275
|
OllamaProvider,
|
|
170276
|
+
LockfileManager,
|
|
170201
170277
|
GeminiProvider,
|
|
170202
170278
|
GeminiClient,
|
|
170203
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
|
}
|