@esoteric-logic/praxis-harness 2.15.0 → 2.16.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/bin/prompt-compile.js +74 -32
- package/lib/assemblers.js +0 -7
- package/package.json +1 -1
- package/prompts/projects/praxis/CLAUDE.md +0 -3
package/bin/prompt-compile.js
CHANGED
|
@@ -53,7 +53,7 @@ function ok(msg) {
|
|
|
53
53
|
|
|
54
54
|
// ── Main ─────────────────────────────────────────────────────
|
|
55
55
|
|
|
56
|
-
/** Validate a standalone project — check files exist, report char budgets. */
|
|
56
|
+
/** Validate a standalone project — check files exist, report char budgets. Returns result rows for summary. */
|
|
57
57
|
function validateStandalone(projectName, projectDir, projectConfig) {
|
|
58
58
|
console.log(`\nValidating standalone: ${projectName}`);
|
|
59
59
|
|
|
@@ -64,6 +64,7 @@ function validateStandalone(projectName, projectDir, projectConfig) {
|
|
|
64
64
|
{ file: 'project-instructions.md', budget: CHAR_BUDGETS['claude-project'], required: false, label: 'Claude Project' },
|
|
65
65
|
];
|
|
66
66
|
|
|
67
|
+
const results = [];
|
|
67
68
|
let missingGenerable = [];
|
|
68
69
|
|
|
69
70
|
for (const item of inventory) {
|
|
@@ -75,14 +76,18 @@ function validateStandalone(projectName, projectDir, projectConfig) {
|
|
|
75
76
|
const sizeInfo = item.budget < Infinity
|
|
76
77
|
? `${charCount} chars (budget: ${item.budget})`
|
|
77
78
|
: `${charCount} chars, ${lineCount} lines`;
|
|
79
|
+
const overBudget = charCount > item.budget;
|
|
78
80
|
|
|
79
|
-
if (
|
|
81
|
+
if (overBudget) {
|
|
80
82
|
warn(`${item.file} exceeds budget: ${charCount} chars (limit: ${item.budget})`);
|
|
81
83
|
} else {
|
|
82
84
|
ok(`${item.file} — ${sizeInfo}`);
|
|
83
85
|
}
|
|
86
|
+
|
|
87
|
+
results.push({ project: projectName, target: item.label, chars: charCount, budget: item.budget, status: overBudget ? 'OVER' : 'ok' });
|
|
84
88
|
} else if (item.required) {
|
|
85
89
|
warn(`${item.file} MISSING — standalone projects require a system prompt`);
|
|
90
|
+
results.push({ project: projectName, target: item.label, chars: 0, budget: item.budget, status: 'MISSING' });
|
|
86
91
|
} else {
|
|
87
92
|
missingGenerable.push(item.file);
|
|
88
93
|
}
|
|
@@ -111,8 +116,11 @@ function validateStandalone(projectName, projectDir, projectConfig) {
|
|
|
111
116
|
ok(`${refs.length} reference file(s): ${refs.join(', ')}`);
|
|
112
117
|
}
|
|
113
118
|
}
|
|
119
|
+
|
|
120
|
+
return { mode: 'standalone', results };
|
|
114
121
|
}
|
|
115
122
|
|
|
123
|
+
/** Compile a project. Returns { mode, results[] } for summary table. */
|
|
116
124
|
function compileProject(projectName, targets) {
|
|
117
125
|
const projectDir = path.join(PROJECTS_DIR, projectName);
|
|
118
126
|
const configPath = path.join(projectDir, 'prompt-config.yaml');
|
|
@@ -123,22 +131,18 @@ function compileProject(projectName, targets) {
|
|
|
123
131
|
|
|
124
132
|
const projectConfig = yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
125
133
|
|
|
126
|
-
// Standalone mode: validate files, report budgets, skip compilation
|
|
127
134
|
if (projectConfig.mode === 'standalone') {
|
|
128
|
-
validateStandalone(projectName, projectDir, projectConfig);
|
|
129
|
-
return;
|
|
135
|
+
return validateStandalone(projectName, projectDir, projectConfig);
|
|
130
136
|
}
|
|
131
137
|
|
|
132
138
|
const praxisConfig = loadPraxisConfig();
|
|
133
139
|
|
|
134
|
-
// Build vars map: project vars + praxis config + project name
|
|
135
140
|
const vars = {
|
|
136
141
|
...praxisConfig,
|
|
137
142
|
...(projectConfig.vars || {}),
|
|
138
143
|
project: projectConfig.project || projectName,
|
|
139
144
|
};
|
|
140
145
|
|
|
141
|
-
// Load profile: from named profile, project-local blocks, or _base fallback
|
|
142
146
|
let profile;
|
|
143
147
|
if (projectConfig.profile) {
|
|
144
148
|
profile = loadProfile(projectConfig.profile, fail);
|
|
@@ -153,7 +157,7 @@ function compileProject(projectName, targets) {
|
|
|
153
157
|
const profileName = projectConfig.profile || 'project-local';
|
|
154
158
|
console.log(`\nCompiling: ${projectName} (profile: ${profileName})`);
|
|
155
159
|
|
|
156
|
-
const
|
|
160
|
+
const targetAssemblers = {
|
|
157
161
|
'claude-code': assembleClaudeCode,
|
|
158
162
|
'claude-project': assembleClaudeProject,
|
|
159
163
|
'perplexity-space': assemblePerplexitySpace,
|
|
@@ -165,14 +169,14 @@ function compileProject(projectName, targets) {
|
|
|
165
169
|
'perplexity-space': 'space-instructions.md',
|
|
166
170
|
};
|
|
167
171
|
|
|
172
|
+
const results = [];
|
|
173
|
+
|
|
168
174
|
for (const target of targets) {
|
|
169
175
|
const blocks = loadBlocks(profile, target, warn);
|
|
170
|
-
let output =
|
|
176
|
+
let output = targetAssemblers[target](blocks, projectConfig, vars);
|
|
171
177
|
|
|
172
|
-
// Interpolate variables
|
|
173
178
|
output = interpolate(output, vars);
|
|
174
179
|
|
|
175
|
-
// Validate no unresolved placeholders
|
|
176
180
|
const unresolved = findUnresolved(output);
|
|
177
181
|
if (unresolved.length > 0) {
|
|
178
182
|
if (STRICT_MODE) {
|
|
@@ -181,9 +185,9 @@ function compileProject(projectName, targets) {
|
|
|
181
185
|
warn(`Unresolved placeholders in ${target}: ${unresolved.join(', ')}`);
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
// Check character budget
|
|
185
188
|
const budget = CHAR_BUDGETS[target];
|
|
186
|
-
|
|
189
|
+
const overBudget = output.length > budget;
|
|
190
|
+
if (overBudget) {
|
|
187
191
|
if (STRICT_MODE) {
|
|
188
192
|
fail(`[strict] ${target} exceeds budget: ${output.length} chars (limit: ${budget})`);
|
|
189
193
|
}
|
|
@@ -191,32 +195,38 @@ function compileProject(projectName, targets) {
|
|
|
191
195
|
}
|
|
192
196
|
|
|
193
197
|
const outputPath = path.join(projectDir, outputNames[target]);
|
|
198
|
+
let status = 'wrote';
|
|
194
199
|
|
|
195
|
-
// Preview mode: print to stdout instead of writing
|
|
196
200
|
if (PREVIEW_MODE) {
|
|
197
201
|
console.log(`\n--- ${outputNames[target]} (${output.length} chars) ---`);
|
|
198
202
|
console.log(output);
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Diff mode: show diff against existing file before writing
|
|
203
|
-
if (DIFF_MODE && fs.existsSync(outputPath)) {
|
|
203
|
+
status = 'preview';
|
|
204
|
+
} else if (DIFF_MODE && fs.existsSync(outputPath)) {
|
|
204
205
|
const existing = fs.readFileSync(outputPath, 'utf8');
|
|
205
206
|
if (existing === output) {
|
|
206
207
|
ok(`${outputNames[target]} — unchanged (${output.length} chars)`);
|
|
207
|
-
|
|
208
|
+
status = 'unchanged';
|
|
209
|
+
} else {
|
|
210
|
+
const existingLines = existing.split('\n');
|
|
211
|
+
const outputLines = output.split('\n');
|
|
212
|
+
const addedCount = outputLines.filter((l) => !existingLines.includes(l)).length;
|
|
213
|
+
const removedCount = existingLines.filter((l) => !outputLines.includes(l)).length;
|
|
214
|
+
console.log(`\n--- ${outputNames[target]} changed ---`);
|
|
215
|
+
console.log(` +${addedCount} lines added, -${removedCount} lines removed`);
|
|
216
|
+
fs.writeFileSync(outputPath, output, 'utf8');
|
|
217
|
+
ok(`${outputNames[target]} — ${output.length} chars → ${outputPath}`);
|
|
218
|
+
status = 'updated';
|
|
208
219
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const addedCount = outputLines.filter((l) => !existingLines.includes(l)).length;
|
|
213
|
-
const removedCount = existingLines.filter((l) => !outputLines.includes(l)).length;
|
|
214
|
-
console.log(` +${addedCount} lines added, -${removedCount} lines removed`);
|
|
220
|
+
} else {
|
|
221
|
+
fs.writeFileSync(outputPath, output, 'utf8');
|
|
222
|
+
ok(`${outputNames[target]} — ${output.length} chars → ${outputPath}`);
|
|
215
223
|
}
|
|
216
224
|
|
|
217
|
-
|
|
218
|
-
|
|
225
|
+
if (overBudget) status = 'OVER';
|
|
226
|
+
results.push({ project: projectName, target, chars: output.length, budget, status });
|
|
219
227
|
}
|
|
228
|
+
|
|
229
|
+
return { mode: 'compiled', results };
|
|
220
230
|
}
|
|
221
231
|
|
|
222
232
|
// ── CLI ──────────────────────────────────────────────────────
|
|
@@ -225,12 +235,13 @@ function main() {
|
|
|
225
235
|
const args = process.argv.slice(2);
|
|
226
236
|
|
|
227
237
|
if (args.length === 0 || args.includes('--help')) {
|
|
228
|
-
console.log('Usage: prompt-compile <project-name|--all> [options]');
|
|
238
|
+
console.log('Usage: prompt-compile <project-name|--all|--sync> [options]');
|
|
229
239
|
console.log('Options:');
|
|
230
240
|
console.log(' --target <target> claude-code|claude-project|perplexity-space|all');
|
|
231
241
|
console.log(' --preview Print output to stdout without writing files');
|
|
232
242
|
console.log(' --diff Show what changed before writing');
|
|
233
243
|
console.log(' --strict Exit with error on budget overruns or unresolved vars');
|
|
244
|
+
console.log(' --sync Compile all projects with diff, show summary table');
|
|
234
245
|
console.log(' --list List all projects with mode and file status');
|
|
235
246
|
process.exit(0);
|
|
236
247
|
}
|
|
@@ -290,7 +301,12 @@ function main() {
|
|
|
290
301
|
}
|
|
291
302
|
const projectArg = args.find((a) => !a.startsWith('--') && !flagValues.has(a));
|
|
292
303
|
|
|
293
|
-
|
|
304
|
+
const isSync = args.includes('--sync');
|
|
305
|
+
if (isSync) {
|
|
306
|
+
DIFF_MODE = true;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (args.includes('--all') || isSync) {
|
|
294
310
|
const projectDirs = fs.readdirSync(PROJECTS_DIR)
|
|
295
311
|
.filter((d) => d !== '_template' && fs.statSync(path.join(PROJECTS_DIR, d)).isDirectory());
|
|
296
312
|
|
|
@@ -298,16 +314,42 @@ function main() {
|
|
|
298
314
|
fail('No projects found in prompts/projects/');
|
|
299
315
|
}
|
|
300
316
|
|
|
317
|
+
const allResults = [];
|
|
301
318
|
for (const projectName of projectDirs) {
|
|
302
|
-
compileProject(projectName, targets);
|
|
319
|
+
const result = compileProject(projectName, targets);
|
|
320
|
+
if (result) allResults.push(result);
|
|
303
321
|
}
|
|
322
|
+
|
|
323
|
+
printSummaryTable(allResults);
|
|
304
324
|
} else if (projectArg) {
|
|
305
325
|
compileProject(projectArg, targets);
|
|
306
326
|
} else {
|
|
307
|
-
fail('Specify a project name or use --all');
|
|
327
|
+
fail('Specify a project name or use --all / --sync');
|
|
308
328
|
}
|
|
309
329
|
|
|
310
330
|
console.log('\nDone.');
|
|
311
331
|
}
|
|
312
332
|
|
|
333
|
+
/** Print a summary table after --all or --sync compilation. */
|
|
334
|
+
function printSummaryTable(projectResults) {
|
|
335
|
+
if (projectResults.length === 0) return;
|
|
336
|
+
|
|
337
|
+
console.log('\n\x1b[1m── Summary ──────────────────────────────────────────────────────\x1b[0m');
|
|
338
|
+
console.log(
|
|
339
|
+
`${'Project'.padEnd(16)} ${'Mode'.padEnd(12)} ${'Target'.padEnd(18)} ${'Chars'.padEnd(10)} ${'Budget'.padEnd(10)} Status`
|
|
340
|
+
);
|
|
341
|
+
console.log('-'.repeat(78));
|
|
342
|
+
|
|
343
|
+
for (const { mode, results } of projectResults) {
|
|
344
|
+
for (const row of results) {
|
|
345
|
+
const budgetStr = row.budget === Infinity ? '—' : String(row.budget);
|
|
346
|
+
const statusColor = row.status === 'OVER' || row.status === 'MISSING' ? '\x1b[31m' :
|
|
347
|
+
row.status === 'unchanged' ? '\x1b[90m' : '\x1b[32m';
|
|
348
|
+
console.log(
|
|
349
|
+
`${row.project.padEnd(16)} ${mode.padEnd(12)} ${row.target.padEnd(18)} ${String(row.chars).padEnd(10)} ${budgetStr.padEnd(10)} ${statusColor}${row.status}\x1b[0m`
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
313
355
|
main();
|
package/lib/assemblers.js
CHANGED
|
@@ -87,13 +87,6 @@ function assembleClaudeCode(blocks, projectConfig, vars) {
|
|
|
87
87
|
lines.push(append.extra_notes.trim(), '');
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// Vault Project
|
|
91
|
-
if (vars.vault_project_path) {
|
|
92
|
-
lines.push('## Vault Project');
|
|
93
|
-
lines.push(`- **Vault path**: ${vars.vault_project_path}`);
|
|
94
|
-
lines.push('');
|
|
95
|
-
}
|
|
96
|
-
|
|
97
90
|
// Standard footer
|
|
98
91
|
lines.push('## Verification');
|
|
99
92
|
lines.push('- Before marking any task complete, run the test suite');
|
package/package.json
CHANGED
|
@@ -69,9 +69,6 @@ Praxis owns the outer loop: discuss → plan → execute → verify → simplify
|
|
|
69
69
|
- No AI-generated comments or attributions in code or commits
|
|
70
70
|
- Prefer simple, readable code over clever abstractions
|
|
71
71
|
|
|
72
|
-
## Vault Project
|
|
73
|
-
- **Vault path**: /Users/esoteric-mac/Documents/Esoteric Vault/01_Projects/Personal/_active/praxis
|
|
74
|
-
|
|
75
72
|
## Verification
|
|
76
73
|
- Before marking any task complete, run the test suite
|
|
77
74
|
- Check logs before claiming a bug is fixed
|