codehere 0.7.0 → 0.8.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/dist/application/use-cases/generate-final-report.js +86 -191
- package/dist/application/use-cases/narrator-prompt-template.js +1 -1
- package/dist/index.js +10 -6
- package/dist/infrastructure/cost/budget-config.js +1 -1
- package/dist/infrastructure/cost/spend-aggregator.js +1 -1
- package/dist/infrastructure/hooks/claude-sdk-hook.js +7 -1
- package/dist/infrastructure/orchestration/agents-config-seed.js +1 -1
- package/dist/infrastructure/orchestration/aider-adapter.js +4 -1
- package/dist/infrastructure/orchestration/cassette-recorder.js +1 -1
- package/dist/infrastructure/orchestration/claude-code-adapter.js +5 -1
- package/dist/infrastructure/orchestration/codex-adapter.js +4 -9
- package/dist/infrastructure/orchestration/conductor-adapter.js +9 -3
- package/dist/infrastructure/orchestration/failover-config.js +1 -1
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +2 -2
- package/dist/infrastructure/orchestration/graphify-context.js +18 -13
- package/dist/infrastructure/orchestration/memory-injector.js +50 -2
- package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +25 -25
- package/dist/infrastructure/orchestration/progress-aggregator.js +41 -6
- package/dist/infrastructure/orchestration/session-manager.js +18 -0
- package/dist/infrastructure/orchestration/session-persistence.js +2 -2
- package/dist/infrastructure/protocols/cloud-sync.js +2 -2
- package/dist/infrastructure/protocols/mcp-server-impl.js +66 -2
- package/dist/infrastructure/protocols/ui-page-v2.js +260 -40
- package/dist/infrastructure/protocols/verify-page.js +42 -0
- package/dist/infrastructure/storage/project-thread-mirror.js +1 -1
- package/dist/infrastructure/storage/project-thread-reader.js +1 -1
- package/dist/infrastructure/storage/project-thread-title-backfill.js +1 -1
- package/dist/presentation/cli/commands/init-command.js +66 -55
- package/package.json +2 -1
|
@@ -430,69 +430,81 @@ function writeTemplate(codehereDir, tmpl, force) {
|
|
|
430
430
|
writeFileSync(target, tmpl.body);
|
|
431
431
|
return 'created';
|
|
432
432
|
}
|
|
433
|
+
export function scaffoldProjectAt(root, options = {}) {
|
|
434
|
+
const codehereDir = join(root, '.codehere');
|
|
435
|
+
const empty = { created: [], skipped: [], refused: [], refusedDirs: [] };
|
|
436
|
+
const noEpisodic = { result: 'failed', sourcePath: '', targetPath: '' };
|
|
437
|
+
// Refuse if .codehere itself is a symlink — would redirect every subsequent write.
|
|
438
|
+
if (existsSync(codehereDir) && isSymlink(codehereDir)) {
|
|
439
|
+
return { ok: false, reason: 'codehere-dir-is-symlink', ...empty, episodic: noEpisodic, gitignore: { result: 'skipped' } };
|
|
440
|
+
}
|
|
441
|
+
if (!existsSync(codehereDir)) {
|
|
442
|
+
mkdirSync(codehereDir, { recursive: true });
|
|
443
|
+
}
|
|
444
|
+
else if (!statSync(codehereDir).isDirectory()) {
|
|
445
|
+
return { ok: false, reason: 'codehere-not-a-directory', ...empty, episodic: noEpisodic, gitignore: { result: 'skipped' } };
|
|
446
|
+
}
|
|
447
|
+
// F3 layout: ensure all directories exist before writing any seeds.
|
|
448
|
+
// Per-segment symlink check on EACH directory's ancestors; if .codehere/memory
|
|
449
|
+
// (or skills, or protocols, etc.) is a symlink, refuse to mkdir into it.
|
|
450
|
+
const refusedDirs = [];
|
|
451
|
+
for (const dir of F3_DIRECTORIES) {
|
|
452
|
+
const offending = findSymlinkAncestor(codehereDir, dir);
|
|
453
|
+
const fullPath = join(codehereDir, dir);
|
|
454
|
+
if (offending !== null || isSymlink(fullPath)) {
|
|
455
|
+
refusedDirs.push(dir);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
mkdirSync(fullPath, { recursive: true });
|
|
459
|
+
}
|
|
460
|
+
const created = [];
|
|
461
|
+
const skipped = [];
|
|
462
|
+
const refused = [];
|
|
463
|
+
// v0.4.1 templates first (CONTEXT/HANDOVER/PROMPTS at the top level).
|
|
464
|
+
for (const tmpl of TEMPLATES) {
|
|
465
|
+
const outcome = writeTemplate(codehereDir, tmpl, !!options.force);
|
|
466
|
+
if (outcome === 'created')
|
|
467
|
+
created.push(tmpl.filename);
|
|
468
|
+
else if (outcome === 'skipped')
|
|
469
|
+
skipped.push(tmpl.filename);
|
|
470
|
+
else
|
|
471
|
+
refused.push(tmpl.filename);
|
|
472
|
+
}
|
|
473
|
+
// F3 v0.5 seed files (memory layers + skills + protocols).
|
|
474
|
+
for (const tmpl of F3_TEMPLATES) {
|
|
475
|
+
const outcome = writeTemplate(codehereDir, tmpl, !!options.force);
|
|
476
|
+
if (outcome === 'created')
|
|
477
|
+
created.push(tmpl.filename);
|
|
478
|
+
else if (outcome === 'skipped')
|
|
479
|
+
skipped.push(tmpl.filename);
|
|
480
|
+
else
|
|
481
|
+
refused.push(tmpl.filename);
|
|
482
|
+
}
|
|
483
|
+
// F3 Task 3 — episodic AGENT_LEARNINGS.jsonl symlink to user's session log.
|
|
484
|
+
const episodic = ensureEpisodicSymlink(codehereDir, resolveHome());
|
|
485
|
+
const gitignore = options.noGitignore ? { result: 'skipped' } : ensureGitignore(root);
|
|
486
|
+
return { ok: true, created, skipped, refused, refusedDirs, episodic, gitignore };
|
|
487
|
+
}
|
|
433
488
|
export function executeInitCommand(options = {}) {
|
|
434
489
|
try {
|
|
435
490
|
const { root, type } = resolveTargetRoot();
|
|
436
|
-
const codehereDir = join(root, '.codehere');
|
|
437
491
|
newline();
|
|
438
492
|
console.log(colors.bold('codehere init'));
|
|
439
493
|
console.log(colors.dim(` scaffolding .codehere/ at ${type} root: ${root}`));
|
|
440
494
|
newline();
|
|
441
|
-
|
|
442
|
-
if (
|
|
443
|
-
|
|
495
|
+
const r = scaffoldProjectAt(root, options);
|
|
496
|
+
if (!r.ok) {
|
|
497
|
+
if (r.reason === 'codehere-dir-is-symlink') {
|
|
498
|
+
console.log(` ${error('refused')} .codehere is a symlink — refusing to follow. Remove it first.`);
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
console.log(` ${error('refused')} .codehere exists but is not a directory.`);
|
|
502
|
+
}
|
|
444
503
|
newline();
|
|
445
504
|
process.exitCode = 1;
|
|
446
505
|
return;
|
|
447
506
|
}
|
|
448
|
-
|
|
449
|
-
mkdirSync(codehereDir, { recursive: true });
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
452
|
-
const codehereStat = statSync(codehereDir);
|
|
453
|
-
if (!codehereStat.isDirectory()) {
|
|
454
|
-
console.log(` ${error('refused')} .codehere exists but is not a directory.`);
|
|
455
|
-
newline();
|
|
456
|
-
process.exitCode = 1;
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
// F3 layout: ensure all directories exist before writing any seeds.
|
|
461
|
-
// Per-segment symlink check on EACH directory's ancestors; if .codehere/memory
|
|
462
|
-
// (or skills, or protocols, etc.) is a symlink, refuse to mkdir into it.
|
|
463
|
-
const refusedDirs = [];
|
|
464
|
-
for (const dir of F3_DIRECTORIES) {
|
|
465
|
-
const offending = findSymlinkAncestor(codehereDir, dir);
|
|
466
|
-
const fullPath = join(codehereDir, dir);
|
|
467
|
-
if (offending !== null || isSymlink(fullPath)) {
|
|
468
|
-
refusedDirs.push(dir);
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
mkdirSync(fullPath, { recursive: true });
|
|
472
|
-
}
|
|
473
|
-
const created = [];
|
|
474
|
-
const skipped = [];
|
|
475
|
-
const refused = [];
|
|
476
|
-
// v0.4.1 templates first (CONTEXT/HANDOVER/PROMPTS at the top level).
|
|
477
|
-
for (const tmpl of TEMPLATES) {
|
|
478
|
-
const outcome = writeTemplate(codehereDir, tmpl, !!options.force);
|
|
479
|
-
if (outcome === 'created')
|
|
480
|
-
created.push(tmpl.filename);
|
|
481
|
-
else if (outcome === 'skipped')
|
|
482
|
-
skipped.push(tmpl.filename);
|
|
483
|
-
else
|
|
484
|
-
refused.push(tmpl.filename);
|
|
485
|
-
}
|
|
486
|
-
// F3 v0.5 seed files (memory layers + skills + protocols).
|
|
487
|
-
for (const tmpl of F3_TEMPLATES) {
|
|
488
|
-
const outcome = writeTemplate(codehereDir, tmpl, !!options.force);
|
|
489
|
-
if (outcome === 'created')
|
|
490
|
-
created.push(tmpl.filename);
|
|
491
|
-
else if (outcome === 'skipped')
|
|
492
|
-
skipped.push(tmpl.filename);
|
|
493
|
-
else
|
|
494
|
-
refused.push(tmpl.filename);
|
|
495
|
-
}
|
|
507
|
+
const { created, skipped, refused, refusedDirs } = r;
|
|
496
508
|
for (const f of created) {
|
|
497
509
|
console.log(` ${success(`created`)} .codehere/${f}`);
|
|
498
510
|
}
|
|
@@ -506,8 +518,7 @@ export function executeInitCommand(options = {}) {
|
|
|
506
518
|
console.log(` ${error(`refused`)} .codehere/${d}/ ${colors.dim('(intermediate symlink — remove it first)')}`);
|
|
507
519
|
}
|
|
508
520
|
// F3 Task 3 — episodic AGENT_LEARNINGS.jsonl symlink to user's session log.
|
|
509
|
-
const
|
|
510
|
-
const episodic = ensureEpisodicSymlink(codehereDir, home);
|
|
521
|
+
const episodic = r.episodic;
|
|
511
522
|
switch (episodic.result) {
|
|
512
523
|
case 'created':
|
|
513
524
|
console.log(` ${success('linked ')} .codehere/memory/episodic/AGENT_LEARNINGS.jsonl → ${episodic.targetPath}`);
|
|
@@ -529,7 +540,7 @@ export function executeInitCommand(options = {}) {
|
|
|
529
540
|
break;
|
|
530
541
|
}
|
|
531
542
|
if (!options.noGitignore) {
|
|
532
|
-
const gitignoreResult =
|
|
543
|
+
const gitignoreResult = r.gitignore;
|
|
533
544
|
newline();
|
|
534
545
|
switch (gitignoreResult.result) {
|
|
535
546
|
case 'updated':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codehere",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "A PM for your AI coding agents. Delegate, orchestrate, and audit Claude Code, Codex, Aider, OpenCode, Gemini, and any user-configured CLI agent from one local web UI — every AI action traced, every file scored, every spawn warmed with project memory.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"node": ">=20"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
|
+
"prepare": "bash ../scripts/install-hooks.sh || true",
|
|
20
21
|
"prebuild": "rm -rf dist",
|
|
21
22
|
"build": "tsc",
|
|
22
23
|
"postbuild": "chmod +x dist/index.js",
|