@jsleekr/graft 6.0.0 → 6.0.2

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 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 <name> # Scaffold project + inject CLAUDE.md spec
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/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('<name>', 'project name')
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 dir = path.resolve(name);
144
- if (fs.existsSync(dir)) {
145
- console.error(`Error: directory '${name}' already exists`);
146
- process.exit(1);
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
- fs.mkdirSync(dir, { recursive: true });
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
- fs.writeFileSync(path.join(dir, 'pipeline.gft'), `// ${safeName} — a simple two-node pipeline
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,29 @@ graph ${safeName}(input: Input, output: Output, budget: 10k) {
177
181
  Analyst -> Reviewer -> done
178
182
  }
179
183
  `);
180
- // Generate .claude/CLAUDE.md with .gft spec so Claude Code natively understands Graft
184
+ }
185
+ // Generate or append .claude/CLAUDE.md with .gft spec
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
- fs.writeFileSync(path.join(claudeDir, 'CLAUDE.md'), `# ${safeName}
190
+ const claudeMdPath = path.join(claudeDir, 'CLAUDE.md');
191
+ const existingClaudeMd = fs.existsSync(claudeMdPath) ? fs.readFileSync(claudeMdPath, 'utf-8') : '';
192
+ if (existingClaudeMd && existingClaudeMd.includes('graft compile')) {
193
+ console.log(` .claude/CLAUDE.md already contains Graft config — skipped`);
194
+ }
195
+ else {
196
+ const graftSection = `
197
+ ## Graft — Multi-Agent Pipelines
186
198
 
187
199
  This project uses **Graft** (.gft) for defining multi-agent pipelines.
188
200
 
189
- ## Working with .gft files
190
-
191
201
  When the user asks to create, modify, or manage pipelines:
192
202
  1. Write or edit \`.gft\` files using the syntax below
193
203
  2. Run \`graft compile <file.gft>\` to generate the \`.claude/\` harness structure
194
204
  3. Run \`graft check <file.gft>\` to validate without generating files
195
205
 
196
- ## CLI Commands
206
+ ### CLI Commands
197
207
 
198
208
  \`\`\`bash
199
209
  graft compile <file.gft> [--out-dir <dir>] # Compile to harness structure
@@ -204,30 +214,40 @@ graft visualize <file.gft> # Output pipeline DAG as Mermaid
204
214
  graft watch <file.gft> # Watch and recompile on changes
205
215
  \`\`\`
206
216
 
207
- ## After Pipeline Execution
217
+ ### After Pipeline Execution
208
218
 
209
- When a pipeline run completes, \`graft run\` automatically:
219
+ \`graft run\` automatically:
210
220
  1. Shows a formatted result summary (nodes, tokens, timing)
211
221
  2. Validates output against the .gft schema (types, ranges, empty fields)
212
222
  3. Suggests .gft modifications if quality issues are found
213
223
 
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
224
  ${gftSpec}
223
- `);
224
- console.log(`\nCreated ${name}/`);
225
- console.log(` pipeline.gft — starter pipeline template`);
226
- console.log(` .claude/CLAUDE.md — Graft spec for Claude Code`);
227
- console.log(`\nNext steps:`);
228
- console.log(` cd ${name}`);
229
- console.log(` graft compile pipeline.gft`);
230
- console.log(` # Open in Claude Code — it already knows .gft syntax`);
225
+ `;
226
+ if (existingClaudeMd) {
227
+ // Append Graft section to existing CLAUDE.md
228
+ fs.writeFileSync(claudeMdPath, existingClaudeMd.trimEnd() + '\n\n' + graftSection.trim() + '\n');
229
+ }
230
+ else {
231
+ // Create new CLAUDE.md with Graft header
232
+ fs.writeFileSync(claudeMdPath, `# ${safeName}\n` + graftSection);
233
+ }
234
+ } // end else (not already configured)
235
+ if (isExisting) {
236
+ console.log(`\nGraft added to current project.`);
237
+ console.log(` .claude/CLAUDE.md — Graft spec injected`);
238
+ console.log(`\nNext steps:`);
239
+ console.log(` # Write a .gft file, or open Claude Code — it already knows .gft syntax`);
240
+ console.log(` graft compile <file.gft>`);
241
+ }
242
+ else {
243
+ console.log(`\nCreated ${name}/`);
244
+ console.log(` pipeline.gft — starter pipeline template`);
245
+ console.log(` .claude/CLAUDE.md — Graft spec for Claude Code`);
246
+ console.log(`\nNext steps:`);
247
+ console.log(` cd ${name}`);
248
+ console.log(` graft compile pipeline.gft`);
249
+ console.log(` # Open in Claude Code — it already knows .gft syntax`);
250
+ }
231
251
  console.log('');
232
252
  });
233
253
  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
+ }
@@ -89,7 +89,8 @@ function formatNodeTokens(nr) {
89
89
  return `${total.toLocaleString()} tok`;
90
90
  }
91
91
  function renderBar(fraction, width) {
92
- const filled = Math.round(fraction * width);
92
+ const clamped = Math.max(0, Math.min(1, fraction));
93
+ const filled = Math.round(clamped * width);
93
94
  const empty = width - filled;
94
95
  const bar = '\u2588'.repeat(filled) + '\u2591'.repeat(empty);
95
96
  return `[${bar}]`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsleekr/graft",
3
- "version": "6.0.0",
3
+ "version": "6.0.2",
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
- "graph"
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": {