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.
Files changed (29) hide show
  1. package/dist/application/use-cases/generate-final-report.js +86 -191
  2. package/dist/application/use-cases/narrator-prompt-template.js +1 -1
  3. package/dist/index.js +10 -6
  4. package/dist/infrastructure/cost/budget-config.js +1 -1
  5. package/dist/infrastructure/cost/spend-aggregator.js +1 -1
  6. package/dist/infrastructure/hooks/claude-sdk-hook.js +7 -1
  7. package/dist/infrastructure/orchestration/agents-config-seed.js +1 -1
  8. package/dist/infrastructure/orchestration/aider-adapter.js +4 -1
  9. package/dist/infrastructure/orchestration/cassette-recorder.js +1 -1
  10. package/dist/infrastructure/orchestration/claude-code-adapter.js +5 -1
  11. package/dist/infrastructure/orchestration/codex-adapter.js +4 -9
  12. package/dist/infrastructure/orchestration/conductor-adapter.js +9 -3
  13. package/dist/infrastructure/orchestration/failover-config.js +1 -1
  14. package/dist/infrastructure/orchestration/generic-cli-adapter.js +2 -2
  15. package/dist/infrastructure/orchestration/graphify-context.js +18 -13
  16. package/dist/infrastructure/orchestration/memory-injector.js +50 -2
  17. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +25 -25
  18. package/dist/infrastructure/orchestration/progress-aggregator.js +41 -6
  19. package/dist/infrastructure/orchestration/session-manager.js +18 -0
  20. package/dist/infrastructure/orchestration/session-persistence.js +2 -2
  21. package/dist/infrastructure/protocols/cloud-sync.js +2 -2
  22. package/dist/infrastructure/protocols/mcp-server-impl.js +66 -2
  23. package/dist/infrastructure/protocols/ui-page-v2.js +260 -40
  24. package/dist/infrastructure/protocols/verify-page.js +42 -0
  25. package/dist/infrastructure/storage/project-thread-mirror.js +1 -1
  26. package/dist/infrastructure/storage/project-thread-reader.js +1 -1
  27. package/dist/infrastructure/storage/project-thread-title-backfill.js +1 -1
  28. package/dist/presentation/cli/commands/init-command.js +66 -55
  29. 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
- // Refuse if .codehere itself is a symlink — would redirect every subsequent write.
442
- if (existsSync(codehereDir) && isSymlink(codehereDir)) {
443
- console.log(` ${error('refused')} .codehere is a symlink — refusing to follow. Remove it first.`);
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
- if (!existsSync(codehereDir)) {
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 home = resolveHome();
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 = ensureGitignore(root);
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.7.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",