@jsleekr/graft 6.0.1 → 6.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 +2 -2
- package/dist/codegen/codegen.js +2 -2
- package/dist/index.js +61 -33
- package/dist/playground-entry.d.ts +26 -0
- package/dist/playground-entry.js +93 -0
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
Write `.gft` files to define multi-agent pipelines. The compiler generates `.claude/` harness structures — agents, hooks, orchestration plans, settings — with compile-time token budget analysis.
|
|
10
10
|
|
|
11
|
-
**[Documentation](https://jsleekr.github.io/graft/)** | **[User Guide](docs/guide.md)** | **[Examples](examples/)**
|
|
11
|
+
**[Documentation](https://jsleekr.github.io/graft/)** | **[Playground](https://jsleekr.github.io/graft/playground/)** | **[User Guide](docs/guide.md)** | **[Examples](examples/)**
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -137,7 +137,7 @@ Claude Code reads the .claude/ structure and runs the pipeline
|
|
|
137
137
|
## CLI
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
graft init
|
|
140
|
+
graft init [name] # New project, or add Graft to current dir
|
|
141
141
|
graft compile <file.gft> [--out-dir <dir>] # Compile to .claude/ harness
|
|
142
142
|
graft check <file.gft> # Parse + analyze only
|
|
143
143
|
graft run <file.gft> --input <json> # Compile and execute
|
package/dist/codegen/codegen.js
CHANGED
|
@@ -41,9 +41,9 @@ export function generate(program, report, sourceFile, index, backend) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
// Orchestration
|
|
44
|
+
// Orchestration (separate file — does not overwrite CLAUDE.md)
|
|
45
45
|
files.push({
|
|
46
|
-
path: '.claude/
|
|
46
|
+
path: '.claude/orchestration.md',
|
|
47
47
|
content: be.generateOrchestration(ctx),
|
|
48
48
|
});
|
|
49
49
|
// Settings
|
package/dist/index.js
CHANGED
|
@@ -137,19 +137,23 @@ program
|
|
|
137
137
|
});
|
|
138
138
|
program
|
|
139
139
|
.command('init')
|
|
140
|
-
.description('Scaffold a new Graft project')
|
|
141
|
-
.argument('
|
|
140
|
+
.description('Scaffold a new Graft project, or add Graft to an existing project')
|
|
141
|
+
.argument('[name]', 'project name (omit to set up current directory)')
|
|
142
142
|
.action(async (name) => {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
143
|
+
const isExisting = !name;
|
|
144
|
+
const dir = name ? path.resolve(name) : process.cwd();
|
|
145
|
+
if (name) {
|
|
146
|
+
if (fs.existsSync(dir)) {
|
|
147
|
+
console.error(`Error: directory '${name}' already exists`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
147
151
|
}
|
|
148
|
-
|
|
149
|
-
const baseName = path.basename(name);
|
|
152
|
+
const baseName = path.basename(dir);
|
|
150
153
|
const safeName = baseName.replace(/[^a-zA-Z0-9]/g, '_').replace(/^_+|_+$/g, '') || 'pipeline';
|
|
151
|
-
// Generate pipeline.gft starter template
|
|
152
|
-
|
|
154
|
+
// Generate pipeline.gft starter template (only for new projects)
|
|
155
|
+
if (!isExisting) {
|
|
156
|
+
fs.writeFileSync(path.join(dir, 'pipeline.gft'), `// ${safeName} — a simple two-node pipeline
|
|
153
157
|
|
|
154
158
|
context Input(max_tokens: 500) {
|
|
155
159
|
question: String
|
|
@@ -177,23 +181,30 @@ graph ${safeName}(input: Input, output: Output, budget: 10k) {
|
|
|
177
181
|
Analyst -> Reviewer -> done
|
|
178
182
|
}
|
|
179
183
|
`);
|
|
180
|
-
|
|
184
|
+
}
|
|
185
|
+
// Generate .claude/ config files
|
|
181
186
|
const claudeDir = path.join(dir, '.claude');
|
|
182
187
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
183
188
|
const { buildSystemPrompt } = await import('./generator.js');
|
|
184
189
|
const gftSpec = buildSystemPrompt();
|
|
185
|
-
|
|
190
|
+
// 1. Write .gft spec to a separate file (never overwritten by compile)
|
|
191
|
+
const specPath = path.join(claudeDir, 'graft-spec.md');
|
|
192
|
+
const specContent = `# Graft — .gft Language Reference
|
|
186
193
|
|
|
187
|
-
This
|
|
194
|
+
> Auto-generated by \`graft init\`. This file is the .gft syntax reference
|
|
195
|
+
> that Claude Code reads to understand the Graft language.
|
|
196
|
+
> Do NOT delete this file — \`graft compile\` does not touch it.
|
|
188
197
|
|
|
189
|
-
##
|
|
198
|
+
## Graft — Multi-Agent Pipelines
|
|
199
|
+
|
|
200
|
+
This project uses **Graft** (.gft) for defining multi-agent pipelines.
|
|
190
201
|
|
|
191
202
|
When the user asks to create, modify, or manage pipelines:
|
|
192
203
|
1. Write or edit \`.gft\` files using the syntax below
|
|
193
204
|
2. Run \`graft compile <file.gft>\` to generate the \`.claude/\` harness structure
|
|
194
205
|
3. Run \`graft check <file.gft>\` to validate without generating files
|
|
195
206
|
|
|
196
|
-
|
|
207
|
+
### CLI Commands
|
|
197
208
|
|
|
198
209
|
\`\`\`bash
|
|
199
210
|
graft compile <file.gft> [--out-dir <dir>] # Compile to harness structure
|
|
@@ -204,30 +215,47 @@ graft visualize <file.gft> # Output pipeline DAG as Mermaid
|
|
|
204
215
|
graft watch <file.gft> # Watch and recompile on changes
|
|
205
216
|
\`\`\`
|
|
206
217
|
|
|
207
|
-
|
|
218
|
+
### After Pipeline Execution
|
|
208
219
|
|
|
209
|
-
|
|
220
|
+
\`graft run\` automatically:
|
|
210
221
|
1. Shows a formatted result summary (nodes, tokens, timing)
|
|
211
222
|
2. Validates output against the .gft schema (types, ranges, empty fields)
|
|
212
223
|
3. Suggests .gft modifications if quality issues are found
|
|
213
224
|
|
|
214
|
-
If the quality report shows issues:
|
|
215
|
-
- Empty fields → suggest increasing node output budget
|
|
216
|
-
- Budget exhaustion → suggest adding edge transforms (select, compact, truncate)
|
|
217
|
-
- Node failures → suggest adding on_failure: retry(N)
|
|
218
|
-
- Type mismatches → check the node's prompt and produces schema
|
|
219
|
-
|
|
220
|
-
Apply the suggested fixes to the .gft file, then run \`graft compile\` and \`graft run\` again.
|
|
221
|
-
|
|
222
225
|
${gftSpec}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
`;
|
|
227
|
+
fs.writeFileSync(specPath, specContent);
|
|
228
|
+
// 2. Add Graft reference to CLAUDE.md (short pointer, not the full spec)
|
|
229
|
+
const claudeMdPath = path.join(claudeDir, 'CLAUDE.md');
|
|
230
|
+
const existingClaudeMd = fs.existsSync(claudeMdPath) ? fs.readFileSync(claudeMdPath, 'utf-8') : '';
|
|
231
|
+
const graftReference = `## Graft
|
|
232
|
+
|
|
233
|
+
This project uses Graft for multi-agent pipelines. Read \`.claude/graft-spec.md\` for the full .gft syntax reference. Run \`graft compile <file.gft>\` to generate harness files.`;
|
|
234
|
+
if (existingClaudeMd && existingClaudeMd.includes('graft-spec.md')) {
|
|
235
|
+
console.log(` .claude/CLAUDE.md already references Graft — skipped`);
|
|
236
|
+
}
|
|
237
|
+
else if (existingClaudeMd) {
|
|
238
|
+
fs.writeFileSync(claudeMdPath, existingClaudeMd.trimEnd() + '\n\n' + graftReference + '\n');
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
fs.writeFileSync(claudeMdPath, `# ${safeName}\n\n` + graftReference + '\n');
|
|
242
|
+
}
|
|
243
|
+
if (isExisting) {
|
|
244
|
+
console.log(`\nGraft added to current project.`);
|
|
245
|
+
console.log(` .claude/CLAUDE.md — Graft spec injected`);
|
|
246
|
+
console.log(`\nNext steps:`);
|
|
247
|
+
console.log(` # Write a .gft file, or open Claude Code — it already knows .gft syntax`);
|
|
248
|
+
console.log(` graft compile <file.gft>`);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
console.log(`\nCreated ${name}/`);
|
|
252
|
+
console.log(` pipeline.gft — starter pipeline template`);
|
|
253
|
+
console.log(` .claude/CLAUDE.md — Graft spec for Claude Code`);
|
|
254
|
+
console.log(`\nNext steps:`);
|
|
255
|
+
console.log(` cd ${name}`);
|
|
256
|
+
console.log(` graft compile pipeline.gft`);
|
|
257
|
+
console.log(` # Open in Claude Code — it already knows .gft syntax`);
|
|
258
|
+
}
|
|
231
259
|
console.log('');
|
|
232
260
|
});
|
|
233
261
|
program
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TokenReport } from './analyzer/estimator.js';
|
|
2
|
+
import { Program } from './parser/ast.js';
|
|
3
|
+
import { GeneratedFile } from './codegen/codegen.js';
|
|
4
|
+
export interface PlaygroundResult {
|
|
5
|
+
success: boolean;
|
|
6
|
+
program?: Program;
|
|
7
|
+
report?: TokenReport;
|
|
8
|
+
files?: GeneratedFile[];
|
|
9
|
+
errors: Array<{
|
|
10
|
+
message: string;
|
|
11
|
+
line: number;
|
|
12
|
+
column: number;
|
|
13
|
+
severity: string;
|
|
14
|
+
code?: string;
|
|
15
|
+
formatted: string;
|
|
16
|
+
}>;
|
|
17
|
+
warnings: Array<{
|
|
18
|
+
message: string;
|
|
19
|
+
line: number;
|
|
20
|
+
column: number;
|
|
21
|
+
severity: string;
|
|
22
|
+
code?: string;
|
|
23
|
+
formatted: string;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
26
|
+
export declare function compilePlayground(source: string, filename?: string): PlaygroundResult;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser entry point for the Graft playground.
|
|
3
|
+
* Exports only the compiler pipeline (no fs, no runtime, no CLI).
|
|
4
|
+
*/
|
|
5
|
+
import { Lexer } from './lexer/lexer.js';
|
|
6
|
+
import { Parser } from './parser/parser.js';
|
|
7
|
+
import { ScopeChecker } from './analyzer/scope.js';
|
|
8
|
+
import { TypeChecker } from './analyzer/types.js';
|
|
9
|
+
import { TokenEstimator } from './analyzer/estimator.js';
|
|
10
|
+
import { GraftError } from './errors/diagnostics.js';
|
|
11
|
+
import { ProgramIndex } from './program-index.js';
|
|
12
|
+
import { generate } from './codegen/codegen.js';
|
|
13
|
+
function serializeDiagnostic(e, source, filename) {
|
|
14
|
+
return {
|
|
15
|
+
message: e.message,
|
|
16
|
+
line: e.location.line,
|
|
17
|
+
column: e.location.column,
|
|
18
|
+
severity: e.severity,
|
|
19
|
+
code: e.code,
|
|
20
|
+
formatted: e.format(source, filename),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function compilePlayground(source, filename = 'playground.gft') {
|
|
24
|
+
const errors = [];
|
|
25
|
+
const warnings = [];
|
|
26
|
+
// Lex
|
|
27
|
+
let tokens;
|
|
28
|
+
try {
|
|
29
|
+
const lexer = new Lexer(source);
|
|
30
|
+
tokens = lexer.tokenize();
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
if (e instanceof GraftError) {
|
|
34
|
+
return {
|
|
35
|
+
success: false,
|
|
36
|
+
errors: [serializeDiagnostic(e, source, filename)],
|
|
37
|
+
warnings: [],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
// Parse
|
|
43
|
+
const { program, errors: parseErrors } = new Parser(tokens).parse();
|
|
44
|
+
errors.push(...parseErrors);
|
|
45
|
+
if (parseErrors.length > 0) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
program,
|
|
49
|
+
errors: errors.map(e => serializeDiagnostic(e, source, filename)),
|
|
50
|
+
warnings: [],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Set sourceFile (no path.resolve — browser-safe)
|
|
54
|
+
for (const c of program.contexts)
|
|
55
|
+
c.sourceFile = filename;
|
|
56
|
+
for (const n of program.nodes)
|
|
57
|
+
n.sourceFile = filename;
|
|
58
|
+
// Build ProgramIndex
|
|
59
|
+
const index = new ProgramIndex(program);
|
|
60
|
+
// Analyze
|
|
61
|
+
const scopeDiagnostics = new ScopeChecker(program, index).check();
|
|
62
|
+
const typeDiagnostics = new TypeChecker(program, index).check();
|
|
63
|
+
for (const d of [...scopeDiagnostics, ...typeDiagnostics]) {
|
|
64
|
+
if (d.severity === 'warning')
|
|
65
|
+
warnings.push(d);
|
|
66
|
+
else
|
|
67
|
+
errors.push(d);
|
|
68
|
+
}
|
|
69
|
+
if (errors.length > 0) {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
program,
|
|
73
|
+
errors: errors.map(e => serializeDiagnostic(e, source, filename)),
|
|
74
|
+
warnings: warnings.map(w => serializeDiagnostic(w, source, filename)),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Token analysis
|
|
78
|
+
const report = new TokenEstimator(program, index).estimate();
|
|
79
|
+
warnings.push(...report.warnings);
|
|
80
|
+
// Generate files (if graph exists)
|
|
81
|
+
let files;
|
|
82
|
+
if (program.graphs.length > 0) {
|
|
83
|
+
files = generate(program, report, filename, index);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
program,
|
|
88
|
+
report,
|
|
89
|
+
files,
|
|
90
|
+
errors: [],
|
|
91
|
+
warnings: warnings.map(w => serializeDiagnostic(w, source, filename)),
|
|
92
|
+
};
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsleekr/graft",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "Graft compiler — compile .gft graph DSL to Claude Code harness structures",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,7 +15,17 @@
|
|
|
15
15
|
"dsl",
|
|
16
16
|
"llm",
|
|
17
17
|
"claude",
|
|
18
|
-
"
|
|
18
|
+
"claude-code",
|
|
19
|
+
"multi-agent",
|
|
20
|
+
"pipeline",
|
|
21
|
+
"infrastructure-as-code",
|
|
22
|
+
"ai-agents",
|
|
23
|
+
"token-optimization",
|
|
24
|
+
"orchestration",
|
|
25
|
+
"graph",
|
|
26
|
+
"code-generation",
|
|
27
|
+
"developer-tools",
|
|
28
|
+
"automation"
|
|
19
29
|
],
|
|
20
30
|
"main": "./dist/index.js",
|
|
21
31
|
"types": "./dist/index.d.ts",
|
|
@@ -59,6 +69,7 @@
|
|
|
59
69
|
"test": "vitest run",
|
|
60
70
|
"test:watch": "vitest",
|
|
61
71
|
"check": "tsc --noEmit",
|
|
72
|
+
"build:playground": "npx esbuild src/playground-entry.ts --bundle --format=iife --global-name=GraftCompiler --outfile=docs/playground/graft-compiler.js --platform=browser --alias:node:path=./scripts/playground-shims/node-path.js --alias:node:fs=./scripts/playground-shims/node-fs.js --alias:node:module=./scripts/playground-shims/node-module.js --alias:node:child_process=./scripts/playground-shims/node-fs.js --alias:node:url=./scripts/playground-shims/node-fs.js --footer:js=\"if(typeof window!=='undefined')window.Graft=GraftCompiler;\"",
|
|
62
73
|
"prepublishOnly": "npm run build && npm test"
|
|
63
74
|
},
|
|
64
75
|
"engines": {
|