@esoteric-logic/praxis-harness 2.16.0 → 3.0.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 +60 -0
- package/base/skills/px-prompt/SKILL.md +917 -107
- package/bin/praxis.js +73 -1
- package/bin/prompt-compile.js +129 -26
- package/bin/prompt-knowledge.js +152 -0
- package/lib/assemblers.js +25 -6
- package/lib/loader.js +172 -13
- package/package.json +3 -2
- package/prompts/blocks/behaviors/first-action-rule.md +21 -0
- package/prompts/blocks/behaviors/no-flattery.md +1 -2
- package/prompts/blocks/behaviors/phase-aware-reasoning.md +41 -0
- package/prompts/blocks/behaviors/radical-candor.md +23 -0
- package/prompts/blocks/context/mcp-servers.md +1 -1
- package/prompts/blocks/domains/federal-cost-analysis.md +33 -0
- package/prompts/blocks/domains/govcon-capture.md +89 -0
- package/prompts/blocks/domains/govcon-proposal.md +153 -0
- package/prompts/blocks/domains/pamasi-framework.md +58 -0
- package/prompts/blocks/domains/proposal-writing-rules.md +59 -0
- package/prompts/blocks/domains/red-team-review.md +45 -0
- package/prompts/blocks/formats/perplexity-generation.md +37 -0
- package/prompts/blocks/formats/scorecard-output.md +51 -0
- package/prompts/blocks/identity/federal-deal-sa.md +81 -0
- package/prompts/blocks/skills/mermaid-diagrams.md +39 -0
- package/prompts/{projects → personal}/praxis/CLAUDE.md +2 -3
- package/prompts/personal/praxis/project-instructions-claude-desktop.md +30 -0
- package/prompts/{projects/praxis/space-instructions.md → personal/praxis/space-instructions-perplexity.md} +2 -1
- package/prompts/profiles/_base.yaml +1 -0
- package/prompts/profiles/maximus-sa.yaml +27 -0
- package/prompts/projects/_template/prompt-config.yaml +4 -0
- package/prompts/templates/knowledge/architecture-constraints.md +19 -0
- package/prompts/templates/knowledge/corporate-reference.md +25 -0
- package/prompts/templates/knowledge/deal-context.md +27 -0
- package/prompts/work/elect/client-config.yaml +9 -0
- package/prompts/work/elect/deals/azure-architecture/CLAUDE.md +61 -0
- package/prompts/work/elect/deals/azure-architecture/prompt-config.yaml +16 -0
- package/prompts/work/elect/deals/azure-architecture/space-instructions-perplexity.md +39 -0
- package/prompts/work/elect/deals/azure-architecture/system-prompt.md +72 -0
- package/prompts/work/maximus/client-config.yaml +81 -0
- package/prompts/{projects/maximus/system-prompt.md → work/maximus/deals/dha-tricare/CLAUDE.md} +279 -314
- package/prompts/work/maximus/deals/dha-tricare/knowledge/deal-context.md +21 -0
- package/prompts/work/maximus/deals/dha-tricare/knowledge/maximus-corporate.md +30 -0
- package/prompts/work/maximus/deals/dha-tricare/project-instructions-claude-desktop.md +58 -0
- package/prompts/work/maximus/deals/dha-tricare/prompt-config.yaml +41 -0
- package/prompts/work/maximus/deals/dha-tricare/references/dha-tricare-intel.md +104 -0
- package/prompts/work/maximus/deals/dha-tricare/space-instructions-perplexity.md +42 -0
- package/prompts/work/maximus/references/maximus-corporate.md +39 -0
- package/prompts/projects/maximus/prompt-config.yaml +0 -13
- package/prompts/projects/maximus/space-instructions.md +0 -67
- package/prompts/projects/praxis/project-instructions.md +0 -24
- /package/prompts/{projects → personal}/praxis/prompt-config.yaml +0 -0
- /package/prompts/{projects → work}/maximus/references/maturity-questions.md +0 -0
- /package/prompts/{projects → work}/maximus/references/phase-maturity-matrix.md +0 -0
- /package/prompts/{projects → work}/maximus/references/proposal-writing-standards.md +0 -0
package/bin/praxis.js
CHANGED
|
@@ -384,6 +384,77 @@ function uninstall() {
|
|
|
384
384
|
console.log('');
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
// ── Scaffold ────────────────────────────────────────────────
|
|
388
|
+
|
|
389
|
+
function scaffold() {
|
|
390
|
+
const subCmd = process.argv[3];
|
|
391
|
+
if (subCmd !== 'new') {
|
|
392
|
+
fail('Usage: praxis scaffold new <project-name> [--profile <name>] [--description "<text>"]');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const projectName = process.argv[4];
|
|
396
|
+
if (!projectName || projectName.startsWith('--')) {
|
|
397
|
+
fail('Specify a project name: praxis scaffold new <project-name>');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const PROJECTS_DIR = path.join(PKG_DIR, 'prompts', 'projects');
|
|
401
|
+
const TEMPLATE_DIR = path.join(PROJECTS_DIR, '_template');
|
|
402
|
+
const projectDir = path.join(PROJECTS_DIR, projectName);
|
|
403
|
+
|
|
404
|
+
if (fs.existsSync(projectDir)) {
|
|
405
|
+
fail(`Project already exists: ${projectDir}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Parse flags
|
|
409
|
+
const args = process.argv.slice(5);
|
|
410
|
+
let profileName = '_base';
|
|
411
|
+
let description = '';
|
|
412
|
+
for (let i = 0; i < args.length; i++) {
|
|
413
|
+
if (args[i] === '--profile' && args[i + 1]) { profileName = args[++i]; }
|
|
414
|
+
if (args[i] === '--description' && args[i + 1]) { description = args[++i]; }
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
header(`Scaffolding project: ${projectName}`);
|
|
418
|
+
|
|
419
|
+
// Create project directory structure
|
|
420
|
+
fs.mkdirSync(path.join(projectDir, 'references'), { recursive: true });
|
|
421
|
+
fs.mkdirSync(path.join(projectDir, 'knowledge'), { recursive: true });
|
|
422
|
+
|
|
423
|
+
// Read template config
|
|
424
|
+
const templatePath = path.join(TEMPLATE_DIR, 'prompt-config.yaml');
|
|
425
|
+
if (!fs.existsSync(templatePath)) {
|
|
426
|
+
fail(`Template not found: ${templatePath}`);
|
|
427
|
+
}
|
|
428
|
+
let configContent = fs.readFileSync(templatePath, 'utf8');
|
|
429
|
+
configContent = configContent.replace('{{project_name}}', projectName);
|
|
430
|
+
configContent = configContent.replace('{{project_description}}', description);
|
|
431
|
+
|
|
432
|
+
configContent = configContent.replace('profile: null', `profile: ${profileName}`);
|
|
433
|
+
|
|
434
|
+
fs.writeFileSync(path.join(projectDir, 'prompt-config.yaml'), configContent, 'utf8');
|
|
435
|
+
|
|
436
|
+
ok(`Created ${projectDir}/prompt-config.yaml`);
|
|
437
|
+
ok(`Created ${projectDir}/references/`);
|
|
438
|
+
ok(`Created ${projectDir}/knowledge/`);
|
|
439
|
+
dim(`Profile: ${profileName}`);
|
|
440
|
+
if (description) dim(`Description: ${description}`);
|
|
441
|
+
|
|
442
|
+
// Preview compile
|
|
443
|
+
console.log('');
|
|
444
|
+
dim('Preview compiled output:');
|
|
445
|
+
spawnSync('node', [
|
|
446
|
+
path.join(PKG_DIR, 'bin', 'prompt-compile.js'),
|
|
447
|
+
projectName, '--preview',
|
|
448
|
+
], { stdio: 'inherit', cwd: PKG_DIR });
|
|
449
|
+
|
|
450
|
+
console.log('');
|
|
451
|
+
header('Next steps');
|
|
452
|
+
dim(`1. Edit prompts/projects/${projectName}/prompt-config.yaml`);
|
|
453
|
+
dim(`2. Add reference files to prompts/projects/${projectName}/references/`);
|
|
454
|
+
dim(`3. Run: node bin/prompt-compile.js ${projectName}`);
|
|
455
|
+
console.log('');
|
|
456
|
+
}
|
|
457
|
+
|
|
387
458
|
// ── Help ─────────────────────────────────────────────────────
|
|
388
459
|
|
|
389
460
|
function printHelp() {
|
|
@@ -397,6 +468,7 @@ Commands:
|
|
|
397
468
|
update Re-copy from latest npm package version
|
|
398
469
|
health Verify install integrity
|
|
399
470
|
uninstall Remove Praxis-owned files from ~/.claude/
|
|
471
|
+
scaffold Scaffold a new prompt project
|
|
400
472
|
|
|
401
473
|
Flags:
|
|
402
474
|
--help, -h Show this help
|
|
@@ -407,7 +479,7 @@ Flags:
|
|
|
407
479
|
// ── Main ─────────────────────────────────────────────────────
|
|
408
480
|
|
|
409
481
|
const arg = process.argv[2] || 'install';
|
|
410
|
-
const commands = { install, update, health, uninstall };
|
|
482
|
+
const commands = { install, update, health, uninstall, scaffold };
|
|
411
483
|
|
|
412
484
|
if (arg === '--help' || arg === '-h') { printHelp(); }
|
|
413
485
|
else if (arg === '--version' || arg === '-v') { console.log(VERSION); }
|
package/bin/prompt-compile.js
CHANGED
|
@@ -8,11 +8,17 @@ const yaml = require('js-yaml');
|
|
|
8
8
|
const {
|
|
9
9
|
TARGETS,
|
|
10
10
|
PROMPTS_DIR,
|
|
11
|
+
WORK_DIR,
|
|
12
|
+
PERSONAL_DIR,
|
|
11
13
|
loadPraxisConfig,
|
|
12
14
|
loadProfile,
|
|
13
15
|
mergeProfiles,
|
|
14
16
|
loadBlocks,
|
|
15
17
|
applyOverrides,
|
|
18
|
+
loadClientConfig,
|
|
19
|
+
discoverAllDeals,
|
|
20
|
+
resolveProject,
|
|
21
|
+
mergeClientDealConfig,
|
|
16
22
|
} = require('../lib/loader');
|
|
17
23
|
|
|
18
24
|
const {
|
|
@@ -25,6 +31,29 @@ const {
|
|
|
25
31
|
|
|
26
32
|
const PROJECTS_DIR = path.join(PROMPTS_DIR, 'projects');
|
|
27
33
|
|
|
34
|
+
/** Discover all compilable projects — clients hierarchy + legacy flat projects. */
|
|
35
|
+
function discoverAllProjects() {
|
|
36
|
+
const results = [];
|
|
37
|
+
|
|
38
|
+
// New hierarchy: clients/*/deals/*/
|
|
39
|
+
for (const entry of discoverAllDeals()) {
|
|
40
|
+
results.push({ name: entry.displayName, dir: entry.dealDir, client: entry.client, deal: entry.deal, clientDir: entry.clientDir });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Legacy: projects/*/ (skip _template, skip projects that also exist in clients)
|
|
44
|
+
if (fs.existsSync(PROJECTS_DIR)) {
|
|
45
|
+
const dealNames = new Set(results.map((r) => r.deal));
|
|
46
|
+
const legacyDirs = fs.readdirSync(PROJECTS_DIR)
|
|
47
|
+
.filter((d) => d !== '_template' && fs.statSync(path.join(PROJECTS_DIR, d)).isDirectory())
|
|
48
|
+
.filter((d) => !dealNames.has(d));
|
|
49
|
+
|
|
50
|
+
for (const name of legacyDirs) {
|
|
51
|
+
results.push({ name, dir: path.join(PROJECTS_DIR, name), client: null, deal: name, clientDir: null });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
}
|
|
56
|
+
|
|
28
57
|
const CHAR_BUDGETS = {
|
|
29
58
|
'claude-code': Infinity,
|
|
30
59
|
'claude-project': 2500,
|
|
@@ -58,10 +87,10 @@ function validateStandalone(projectName, projectDir, projectConfig) {
|
|
|
58
87
|
console.log(`\nValidating standalone: ${projectName}`);
|
|
59
88
|
|
|
60
89
|
const inventory = [
|
|
61
|
-
{ file: 'system-prompt.md', budget: Infinity, required: true, label: 'System Prompt (
|
|
90
|
+
{ file: 'system-prompt.md', budget: Infinity, required: true, label: 'System Prompt (Source)' },
|
|
62
91
|
{ file: 'CLAUDE.md', budget: Infinity, required: false, label: 'Claude Code' },
|
|
63
|
-
{ file: 'space-instructions.md', budget: CHAR_BUDGETS['perplexity-space'], required: false, label: 'Perplexity Space' },
|
|
64
|
-
{ file: 'project-instructions.md', budget: CHAR_BUDGETS['claude-project'], required: false, label: 'Claude
|
|
92
|
+
{ file: 'space-instructions-perplexity.md', budget: CHAR_BUDGETS['perplexity-space'], required: false, label: 'Perplexity Space' },
|
|
93
|
+
{ file: 'project-instructions-claude-desktop.md', budget: CHAR_BUDGETS['claude-project'], required: false, label: 'Claude Desktop' },
|
|
65
94
|
];
|
|
66
95
|
|
|
67
96
|
const results = [];
|
|
@@ -121,15 +150,35 @@ function validateStandalone(projectName, projectDir, projectConfig) {
|
|
|
121
150
|
}
|
|
122
151
|
|
|
123
152
|
/** Compile a project. Returns { mode, results[] } for summary table. */
|
|
124
|
-
function compileProject(projectName, targets) {
|
|
125
|
-
|
|
126
|
-
|
|
153
|
+
function compileProject(projectName, targets, projectDirOverride, clientDirOverride) {
|
|
154
|
+
// Resolve project path — supports "client/deal", "deal", or direct dir override
|
|
155
|
+
let projectDir;
|
|
156
|
+
let clientDir = clientDirOverride || null;
|
|
157
|
+
if (projectDirOverride) {
|
|
158
|
+
projectDir = projectDirOverride;
|
|
159
|
+
} else {
|
|
160
|
+
const resolved = resolveProject(projectName);
|
|
161
|
+
if (!resolved.dealDir || !fs.existsSync(resolved.dealDir)) {
|
|
162
|
+
fail(`Project not found: ${projectName}\nRun /px-prompt <project-name> to create one.`);
|
|
163
|
+
}
|
|
164
|
+
projectDir = resolved.dealDir;
|
|
165
|
+
clientDir = resolved.clientDir;
|
|
166
|
+
}
|
|
127
167
|
|
|
168
|
+
const configPath = path.join(projectDir, 'prompt-config.yaml');
|
|
128
169
|
if (!fs.existsSync(configPath)) {
|
|
129
170
|
fail(`Project config not found: ${configPath}\nRun /px-prompt <project-name> to create one.`);
|
|
130
171
|
}
|
|
131
172
|
|
|
132
|
-
|
|
173
|
+
let projectConfig = yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
174
|
+
|
|
175
|
+
// Merge client config if in hierarchy
|
|
176
|
+
if (clientDir) {
|
|
177
|
+
const clientConfig = loadClientConfig(clientDir);
|
|
178
|
+
if (clientConfig) {
|
|
179
|
+
projectConfig = mergeClientDealConfig(clientConfig, projectConfig);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
133
182
|
|
|
134
183
|
if (projectConfig.mode === 'standalone') {
|
|
135
184
|
return validateStandalone(projectName, projectDir, projectConfig);
|
|
@@ -165,8 +214,8 @@ function compileProject(projectName, targets) {
|
|
|
165
214
|
|
|
166
215
|
const outputNames = {
|
|
167
216
|
'claude-code': 'CLAUDE.md',
|
|
168
|
-
'claude-project': 'project-instructions.md',
|
|
169
|
-
'perplexity-space': 'space-instructions.md',
|
|
217
|
+
'claude-project': 'project-instructions-claude-desktop.md',
|
|
218
|
+
'perplexity-space': 'space-instructions-perplexity.md',
|
|
170
219
|
};
|
|
171
220
|
|
|
172
221
|
const results = [];
|
|
@@ -242,41 +291,96 @@ function main() {
|
|
|
242
291
|
console.log(' --diff Show what changed before writing');
|
|
243
292
|
console.log(' --strict Exit with error on budget overruns or unresolved vars');
|
|
244
293
|
console.log(' --sync Compile all projects with diff, show summary table');
|
|
294
|
+
console.log(' --dashboard Rich project index with budgets and staleness');
|
|
245
295
|
console.log(' --list List all projects with mode and file status');
|
|
246
296
|
process.exit(0);
|
|
247
297
|
}
|
|
248
298
|
|
|
249
299
|
// --list mode: show all projects
|
|
250
300
|
if (args.includes('--list')) {
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
if (projectDirs.length === 0) {
|
|
301
|
+
const allProjects = discoverAllProjects();
|
|
302
|
+
if (allProjects.length === 0) {
|
|
254
303
|
console.log('No projects found.');
|
|
255
304
|
process.exit(0);
|
|
256
305
|
}
|
|
257
|
-
console.log(`${'Project'.padEnd(
|
|
258
|
-
console.log('-'.repeat(
|
|
259
|
-
for (const
|
|
260
|
-
const
|
|
261
|
-
const cfgPath = path.join(dir, 'prompt-config.yaml');
|
|
306
|
+
console.log(`${'Project'.padEnd(28)} ${'Mode'.padEnd(12)} ${'System Prompt'.padEnd(15)} ${'Claude Desktop'.padEnd(15)} ${'Perplexity'.padEnd(15)} ${'CLAUDE.md'.padEnd(12)} Refs`);
|
|
307
|
+
console.log('-'.repeat(105));
|
|
308
|
+
for (const proj of allProjects) {
|
|
309
|
+
const cfgPath = path.join(proj.dir, 'prompt-config.yaml');
|
|
262
310
|
const cfg = fs.existsSync(cfgPath) ? yaml.load(fs.readFileSync(cfgPath, 'utf8')) : {};
|
|
263
311
|
const mode = cfg.mode || 'compiled';
|
|
264
312
|
const fileStatus = (f) => {
|
|
265
|
-
const p = path.join(dir, f);
|
|
313
|
+
const p = path.join(proj.dir, f);
|
|
266
314
|
if (!fs.existsSync(p)) return '—';
|
|
267
315
|
return `${fs.readFileSync(p, 'utf8').length} chars`;
|
|
268
316
|
};
|
|
269
|
-
const refsDir = path.join(dir, 'references');
|
|
317
|
+
const refsDir = path.join(proj.dir, 'references');
|
|
270
318
|
const refCount = fs.existsSync(refsDir)
|
|
271
319
|
? fs.readdirSync(refsDir).filter((f) => f.endsWith('.md')).length
|
|
272
320
|
: 0;
|
|
273
321
|
console.log(
|
|
274
|
-
`${name.padEnd(
|
|
322
|
+
`${proj.name.padEnd(28)} ${mode.padEnd(12)} ${fileStatus('system-prompt.md').padEnd(15)} ${fileStatus('project-instructions-claude-desktop.md').padEnd(15)} ${fileStatus('space-instructions-perplexity.md').padEnd(15)} ${fileStatus('CLAUDE.md').padEnd(12)} ${refCount}`
|
|
275
323
|
);
|
|
276
324
|
}
|
|
277
325
|
process.exit(0);
|
|
278
326
|
}
|
|
279
327
|
|
|
328
|
+
// --dashboard mode: rich project index with staleness and budgets
|
|
329
|
+
if (args.includes('--dashboard')) {
|
|
330
|
+
const allProjects = discoverAllProjects();
|
|
331
|
+
if (allProjects.length === 0) {
|
|
332
|
+
console.log('No projects found.');
|
|
333
|
+
process.exit(0);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const STALE_DAYS = 30;
|
|
337
|
+
const now = Date.now();
|
|
338
|
+
|
|
339
|
+
console.log('\n\x1b[1mPROMPT ENGINE DASHBOARD\x1b[0m');
|
|
340
|
+
console.log('\x1b[90m' + '━'.repeat(110) + '\x1b[0m');
|
|
341
|
+
console.log(
|
|
342
|
+
`${'Project'.padEnd(24)} ${'Mode'.padEnd(12)} ${'Perplexity'.padEnd(14)} ${'Claude Proj'.padEnd(14)} ${'CLAUDE.md'.padEnd(14)} ${'Refs'.padEnd(6)} ${'Updated'.padEnd(12)} Stale?`
|
|
343
|
+
);
|
|
344
|
+
console.log('\x1b[90m' + '─'.repeat(110) + '\x1b[0m');
|
|
345
|
+
|
|
346
|
+
for (const proj of allProjects) {
|
|
347
|
+
const cfgPath = path.join(proj.dir, 'prompt-config.yaml');
|
|
348
|
+
const cfg = fs.existsSync(cfgPath) ? yaml.load(fs.readFileSync(cfgPath, 'utf8')) : {};
|
|
349
|
+
const mode = cfg.mode || 'compiled';
|
|
350
|
+
|
|
351
|
+
const fileBudget = (f, budget) => {
|
|
352
|
+
const p = path.join(proj.dir, f);
|
|
353
|
+
if (!fs.existsSync(p)) return '\x1b[90m—\x1b[0m';
|
|
354
|
+
const chars = fs.readFileSync(p, 'utf8').length;
|
|
355
|
+
const icon = chars > budget ? '\x1b[33m⚠\x1b[0m' : '\x1b[32m✓\x1b[0m';
|
|
356
|
+
return `${chars} ${budget < Infinity ? icon : ''}`;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
const refsDir = path.join(proj.dir, 'references');
|
|
360
|
+
const refCount = fs.existsSync(refsDir)
|
|
361
|
+
? fs.readdirSync(refsDir).filter((f) => f.endsWith('.md')).length
|
|
362
|
+
: 0;
|
|
363
|
+
|
|
364
|
+
let latestMtime = 0;
|
|
365
|
+
const allFiles = fs.readdirSync(proj.dir).filter((f) => f.endsWith('.md') || f.endsWith('.yaml'));
|
|
366
|
+
for (const f of allFiles) {
|
|
367
|
+
const stat = fs.statSync(path.join(proj.dir, f));
|
|
368
|
+
if (stat.mtimeMs > latestMtime) latestMtime = stat.mtimeMs;
|
|
369
|
+
}
|
|
370
|
+
const updated = latestMtime > 0 ? new Date(latestMtime).toISOString().slice(0, 10) : '—';
|
|
371
|
+
const daysSince = latestMtime > 0 ? Math.floor((now - latestMtime) / 86400000) : 999;
|
|
372
|
+
const stale = daysSince > STALE_DAYS ? '\x1b[31mYes\x1b[0m' : '\x1b[32mNo\x1b[0m';
|
|
373
|
+
|
|
374
|
+
console.log(
|
|
375
|
+
`${proj.name.padEnd(24)} ${mode.padEnd(12)} ${fileBudget('space-instructions-perplexity.md', CHAR_BUDGETS['perplexity-space']).padEnd(23)} ${fileBudget('project-instructions-claude-desktop.md', CHAR_BUDGETS['claude-project']).padEnd(23)} ${fileBudget('CLAUDE.md', Infinity).padEnd(23)} ${String(refCount).padEnd(6)} ${updated.padEnd(12)} ${stale}`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.log('\x1b[90m' + '━'.repeat(110) + '\x1b[0m');
|
|
380
|
+
console.log('\x1b[90mStaleness: >30 days since last file change. Budgets: ✓ under, ⚠ over.\x1b[0m\n');
|
|
381
|
+
process.exit(0);
|
|
382
|
+
}
|
|
383
|
+
|
|
280
384
|
// Parse global flags
|
|
281
385
|
PREVIEW_MODE = args.includes('--preview');
|
|
282
386
|
DIFF_MODE = args.includes('--diff');
|
|
@@ -307,16 +411,15 @@ function main() {
|
|
|
307
411
|
}
|
|
308
412
|
|
|
309
413
|
if (args.includes('--all') || isSync) {
|
|
310
|
-
const
|
|
311
|
-
.filter((d) => d !== '_template' && fs.statSync(path.join(PROJECTS_DIR, d)).isDirectory());
|
|
414
|
+
const allProjects = discoverAllProjects();
|
|
312
415
|
|
|
313
|
-
if (
|
|
314
|
-
fail('No projects found
|
|
416
|
+
if (allProjects.length === 0) {
|
|
417
|
+
fail('No projects found.');
|
|
315
418
|
}
|
|
316
419
|
|
|
317
420
|
const allResults = [];
|
|
318
|
-
for (const
|
|
319
|
-
const result = compileProject(
|
|
421
|
+
for (const proj of allProjects) {
|
|
422
|
+
const result = compileProject(proj.name, targets, proj.dir, proj.clientDir);
|
|
320
423
|
if (result) allResults.push(result);
|
|
321
424
|
}
|
|
322
425
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renders knowledge-file templates for a project.
|
|
6
|
+
*
|
|
7
|
+
* Reads `knowledge_packs` from the project's prompt-config.yaml.
|
|
8
|
+
* Each entry specifies a template and vars. Renders to projects/<name>/knowledge/.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const yaml = require('js-yaml');
|
|
14
|
+
const { interpolate, findUnresolved } = require('../lib/assemblers');
|
|
15
|
+
const { parseFrontmatter, loadPraxisConfig, resolveProject, loadClientConfig, mergeClientDealConfig } = require('../lib/loader');
|
|
16
|
+
|
|
17
|
+
const PKG_DIR = path.resolve(__dirname, '..');
|
|
18
|
+
const TEMPLATES_DIR = path.join(PKG_DIR, 'prompts', 'templates', 'knowledge');
|
|
19
|
+
|
|
20
|
+
function fail(msg) {
|
|
21
|
+
console.error(`\x1b[31mERROR:\x1b[0m ${msg}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function warn(msg) {
|
|
26
|
+
console.error(`\x1b[33mWARN:\x1b[0m ${msg}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ok(msg) {
|
|
30
|
+
console.log(`\x1b[32m\u2713\x1b[0m ${msg}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function main() {
|
|
34
|
+
const args = process.argv.slice(2);
|
|
35
|
+
|
|
36
|
+
if (args.length === 0 || args.includes('--help')) {
|
|
37
|
+
console.log('Usage: prompt-knowledge <project-name> [--preview] [--diff] [--list-templates]');
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (args.includes('--list-templates')) {
|
|
42
|
+
if (!fs.existsSync(TEMPLATES_DIR)) {
|
|
43
|
+
console.log('No templates found.');
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
const templates = fs.readdirSync(TEMPLATES_DIR).filter((f) => f.endsWith('.md'));
|
|
47
|
+
if (templates.length === 0) {
|
|
48
|
+
console.log('No templates found.');
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
console.log('\nAvailable knowledge templates:\n');
|
|
52
|
+
for (const t of templates) {
|
|
53
|
+
const content = fs.readFileSync(path.join(TEMPLATES_DIR, t), 'utf8');
|
|
54
|
+
const { meta } = parseFrontmatter(content);
|
|
55
|
+
console.log(` ${(meta.id || t).padEnd(28)} ${meta.description || '(no description)'}`);
|
|
56
|
+
if (meta.vars) {
|
|
57
|
+
console.log(` vars: ${meta.vars.join(', ')}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
console.log('');
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const preview = args.includes('--preview');
|
|
65
|
+
const diff = args.includes('--diff');
|
|
66
|
+
const projectName = args.find((a) => !a.startsWith('--'));
|
|
67
|
+
|
|
68
|
+
if (!projectName) fail('Specify a project name.');
|
|
69
|
+
|
|
70
|
+
const resolved = resolveProject(projectName);
|
|
71
|
+
if (!resolved.dealDir || !fs.existsSync(resolved.dealDir)) {
|
|
72
|
+
fail(`Project not found: ${projectName}`);
|
|
73
|
+
}
|
|
74
|
+
const projectDir = resolved.dealDir;
|
|
75
|
+
const configPath = path.join(projectDir, 'prompt-config.yaml');
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(configPath)) {
|
|
78
|
+
fail(`Project config not found: ${configPath}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let projectConfig = yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
82
|
+
|
|
83
|
+
// Merge client config if in hierarchy
|
|
84
|
+
if (resolved.clientDir) {
|
|
85
|
+
const clientConfig = loadClientConfig(resolved.clientDir);
|
|
86
|
+
if (clientConfig) {
|
|
87
|
+
projectConfig = mergeClientDealConfig(clientConfig, projectConfig);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const praxisConfig = loadPraxisConfig();
|
|
92
|
+
const globalVars = {
|
|
93
|
+
...praxisConfig,
|
|
94
|
+
...(projectConfig.vars || {}),
|
|
95
|
+
project: projectConfig.project || projectConfig.deal || projectName,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const packs = projectConfig.knowledge_packs || [];
|
|
99
|
+
if (packs.length === 0) {
|
|
100
|
+
console.log(`No knowledge_packs defined in ${configPath}`);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const knowledgeDir = path.join(projectDir, 'knowledge');
|
|
105
|
+
if (!preview && !fs.existsSync(knowledgeDir)) {
|
|
106
|
+
fs.mkdirSync(knowledgeDir, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log(`\nRendering knowledge packs for: ${projectName}\n`);
|
|
110
|
+
|
|
111
|
+
for (const pack of packs) {
|
|
112
|
+
const templatePath = path.join(TEMPLATES_DIR, `${pack.template}.md`);
|
|
113
|
+
if (!fs.existsSync(templatePath)) {
|
|
114
|
+
warn(`Template not found: ${pack.template} \u2014 skipping`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const templateContent = fs.readFileSync(templatePath, 'utf8');
|
|
119
|
+
const { body } = parseFrontmatter(templateContent);
|
|
120
|
+
|
|
121
|
+
const vars = { ...globalVars, ...(pack.vars || {}) };
|
|
122
|
+
const output = interpolate(body, vars);
|
|
123
|
+
|
|
124
|
+
const unresolved = findUnresolved(output);
|
|
125
|
+
if (unresolved.length > 0) {
|
|
126
|
+
warn(`Unresolved in ${pack.output}: ${unresolved.join(', ')} \u2014 these become placeholders for manual fill`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const outputPath = path.join(knowledgeDir, pack.output);
|
|
130
|
+
|
|
131
|
+
if (preview) {
|
|
132
|
+
console.log(`--- ${pack.output} (${output.length} chars, targets: ${(pack.targets || ['all']).join(', ')}) ---`);
|
|
133
|
+
console.log(output);
|
|
134
|
+
console.log('');
|
|
135
|
+
} else if (diff && fs.existsSync(outputPath)) {
|
|
136
|
+
const existing = fs.readFileSync(outputPath, 'utf8');
|
|
137
|
+
if (existing === output) {
|
|
138
|
+
ok(`${pack.output} \u2014 unchanged`);
|
|
139
|
+
} else {
|
|
140
|
+
fs.writeFileSync(outputPath, output, 'utf8');
|
|
141
|
+
ok(`${pack.output} \u2014 updated (${output.length} chars)`);
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
fs.writeFileSync(outputPath, output, 'utf8');
|
|
145
|
+
ok(`${pack.output} \u2014 ${output.length} chars \u2192 ${outputPath}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.log('\nDone.');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
main();
|
package/lib/assemblers.js
CHANGED
|
@@ -55,6 +55,13 @@ function assembleClaudeCode(blocks, projectConfig, vars) {
|
|
|
55
55
|
for (const block of domainBlocks) lines.push(block.content, '');
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// Skills
|
|
59
|
+
const skillBlocks = blocks.filter((b) => b.category === 'skills');
|
|
60
|
+
if (skillBlocks.length > 0) {
|
|
61
|
+
lines.push('## Skills & Techniques');
|
|
62
|
+
for (const block of skillBlocks) lines.push(block.content, '');
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
// Formats
|
|
59
66
|
const formatBlocks = blocks.filter((b) => b.category === 'formats');
|
|
60
67
|
if (formatBlocks.length > 0) {
|
|
@@ -117,24 +124,28 @@ function assembleClaudeProject(blocks, projectConfig, vars) {
|
|
|
117
124
|
const behaviorBlocks = blocks.filter((b) => b.category === 'behaviors');
|
|
118
125
|
if (behaviorBlocks.length > 0) {
|
|
119
126
|
lines.push('## Behavioral Constraints');
|
|
120
|
-
for (const block of behaviorBlocks) lines.push(
|
|
121
|
-
lines.push('');
|
|
127
|
+
for (const block of behaviorBlocks) lines.push(block.content, '');
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
// Layer 3: Domain Expertise
|
|
125
131
|
const domainBlocks = blocks.filter((b) => b.category === 'domains');
|
|
126
132
|
if (domainBlocks.length > 0) {
|
|
127
133
|
lines.push('## Domain Expertise');
|
|
128
|
-
for (const block of domainBlocks) lines.push(
|
|
129
|
-
|
|
134
|
+
for (const block of domainBlocks) lines.push(block.content, '');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Layer 3a: Skills & Techniques
|
|
138
|
+
const skillBlocks = blocks.filter((b) => b.category === 'skills');
|
|
139
|
+
if (skillBlocks.length > 0) {
|
|
140
|
+
lines.push('## Skills & Techniques');
|
|
141
|
+
for (const block of skillBlocks) lines.push(block.content, '');
|
|
130
142
|
}
|
|
131
143
|
|
|
132
144
|
// Layer 3b: Output Format
|
|
133
145
|
const formatBlocks = blocks.filter((b) => b.category === 'formats');
|
|
134
146
|
if (formatBlocks.length > 0) {
|
|
135
147
|
lines.push('## Output Format');
|
|
136
|
-
for (const block of formatBlocks) lines.push(
|
|
137
|
-
lines.push('');
|
|
148
|
+
for (const block of formatBlocks) lines.push(block.content, '');
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
// Additional context
|
|
@@ -203,6 +214,14 @@ function assemblePerplexitySpace(blocks, projectConfig, vars) {
|
|
|
203
214
|
lines.push('');
|
|
204
215
|
}
|
|
205
216
|
|
|
217
|
+
// Capabilities (skills)
|
|
218
|
+
const skillBlocks = blocks.filter((b) => b.category === 'skills');
|
|
219
|
+
if (skillBlocks.length > 0) {
|
|
220
|
+
lines.push('## Capabilities');
|
|
221
|
+
for (const block of skillBlocks) lines.push(block.content);
|
|
222
|
+
lines.push('');
|
|
223
|
+
}
|
|
224
|
+
|
|
206
225
|
// Research Domains
|
|
207
226
|
const append = (projectConfig.overrides || {}).perplexity_space_append || {};
|
|
208
227
|
if (append.research_domains) {
|