@codeyam/codeyam-cli 0.1.18 → 0.1.20

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 (85) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +1 -1
  4. package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +9 -6
  5. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +9 -12
  6. package/analyzer-template/packages/database/package.json +1 -1
  7. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +19 -15
  8. package/analyzer-template/packages/database/src/lib/loadEntity.ts +8 -4
  9. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  10. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +1 -1
  11. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  12. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts.map +1 -1
  13. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js +1 -1
  14. package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js.map +1 -1
  15. package/analyzer-template/project/analyzeFileEntities.ts +26 -0
  16. package/background/src/lib/virtualized/project/analyzeFileEntities.js +22 -0
  17. package/background/src/lib/virtualized/project/analyzeFileEntities.js.map +1 -1
  18. package/codeyam-cli/src/cli.js +15 -0
  19. package/codeyam-cli/src/cli.js.map +1 -1
  20. package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js +9 -9
  21. package/codeyam-cli/src/commands/editor.js +570 -300
  22. package/codeyam-cli/src/commands/editor.js.map +1 -1
  23. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +273 -1
  24. package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
  25. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js +67 -0
  26. package/codeyam-cli/src/utils/__tests__/editorGuardMiddleware.test.js.map +1 -0
  27. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +20 -0
  28. package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
  29. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +16 -1
  30. package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -1
  31. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js +84 -0
  32. package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js.map +1 -0
  33. package/codeyam-cli/src/utils/analysisRunner.js +8 -6
  34. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  35. package/codeyam-cli/src/utils/editorAudit.js +73 -4
  36. package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
  37. package/codeyam-cli/src/utils/editorGuard.js +36 -0
  38. package/codeyam-cli/src/utils/editorGuard.js.map +1 -0
  39. package/codeyam-cli/src/utils/editorScenarios.js +37 -5
  40. package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
  41. package/codeyam-cli/src/utils/queue/job.js +6 -3
  42. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  43. package/codeyam-cli/src/utils/screenshotHash.js +26 -0
  44. package/codeyam-cli/src/utils/screenshotHash.js.map +1 -0
  45. package/codeyam-cli/src/utils/simulationGateMiddleware.js +9 -0
  46. package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
  47. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +28 -1
  48. package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
  49. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +38 -7
  50. package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
  51. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +135 -0
  52. package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -0
  53. package/codeyam-cli/src/webserver/app/lib/clientErrors.js +12 -0
  54. package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
  55. package/codeyam-cli/src/webserver/build/client/assets/{editor.entity.(_sha)-Bnx7yUP0.js → editor.entity.(_sha)-DII1pg_z.js} +13 -13
  56. package/codeyam-cli/src/webserver/build/client/assets/globals-Yn9W3zp3.css +1 -0
  57. package/codeyam-cli/src/webserver/build/client/assets/{manifest-b9d4d267.js → manifest-cdf2c0a7.js} +1 -1
  58. package/codeyam-cli/src/webserver/build/client/assets/{root-DB3O9_9j.js → root-BxUQigda.js} +5 -5
  59. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-B_PsTAb1.js +13 -0
  60. package/codeyam-cli/src/webserver/build/server/assets/{index-D4meMKy3.js → index-CjLhfz6Z.js} +1 -1
  61. package/codeyam-cli/src/webserver/build/server/assets/{init-odGJ_c2-.js → init-BEqlbI84.js} +1 -1
  62. package/codeyam-cli/src/webserver/build/server/assets/{server-build-TmPfF7pT.js → server-build-YI63xTu4.js} +112 -111
  63. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  64. package/codeyam-cli/src/webserver/build-info.json +5 -5
  65. package/codeyam-cli/src/webserver/scripts/journalCapture.ts +17 -0
  66. package/codeyam-cli/src/webserver/server.js +52 -14
  67. package/codeyam-cli/src/webserver/server.js.map +1 -1
  68. package/codeyam-cli/src/webserver/terminalServer.js +129 -22
  69. package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
  70. package/codeyam-cli/templates/__tests__/editor-step-hook.prompt-capture.test.ts +118 -0
  71. package/codeyam-cli/templates/codeyam-editor-claude.md +2 -0
  72. package/codeyam-cli/templates/codeyam-editor-reference.md +1 -1
  73. package/codeyam-cli/templates/editor-step-hook.py +93 -46
  74. package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +19 -1
  75. package/package.json +1 -1
  76. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +2 -2
  77. package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
  78. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +9 -7
  79. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  80. package/packages/database/src/lib/loadAnalysis.js +1 -1
  81. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  82. package/packages/database/src/lib/loadEntity.js +1 -1
  83. package/packages/database/src/lib/loadEntity.js.map +1 -1
  84. package/codeyam-cli/src/webserver/build/client/assets/globals-fAqOD9ex.css +0 -1
  85. package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-CGwTN3V2.js +0 -13
@@ -1,6 +1,6 @@
1
1
  import Database from 'better-sqlite3';
2
2
  import { Kysely, SqliteDialect } from 'kysely';
3
- import { isComponent, classifyGlossaryEntries, computeAudit, filterGlossaryByChangeStatus, resolveAuditSessionScope, queryScenarioCounts, queryPageScenarioCounts, queryIncompleteEntities, queryMiscategorizedScenarios, isOnlyIncompleteEntities, isAutoRemediable, identifyScenariosNeedingRecapture, detectDuplicateNames, } from "../editorAudit.js";
3
+ import { isComponent, classifyGlossaryEntries, computeAudit, filterGlossaryByChangeStatus, resolveAuditSessionScope, queryScenarioCounts, queryPageScenarioCounts, queryIncompleteEntities, queryMiscategorizedScenarios, queryUnassociatedScenarios, isOnlyIncompleteEntities, isAutoRemediable, identifyScenariosNeedingRecapture, detectDuplicateNames, } from "../editorAudit.js";
4
4
  describe('editorAudit', () => {
5
5
  describe('isComponent', () => {
6
6
  it('should return true for JSX.Element return type', () => {
@@ -2676,5 +2676,277 @@ describe('editorAudit', () => {
2676
2676
  expect(result.summary.componentsMissing).toBe(1);
2677
2677
  });
2678
2678
  });
2679
+ // ── queryUnassociatedScenarios ──────────────────────────────────────
2680
+ describe('queryUnassociatedScenarios', () => {
2681
+ let db;
2682
+ let rawDb;
2683
+ const projectId = 'test-project-id';
2684
+ beforeEach(async () => {
2685
+ rawDb = new Database(':memory:');
2686
+ db = new Kysely({ dialect: new SqliteDialect({ database: rawDb }) });
2687
+ await db.schema
2688
+ .createTable('editor_scenarios')
2689
+ .addColumn('id', 'varchar', (col) => col.primaryKey())
2690
+ .addColumn('project_id', 'varchar', (col) => col.notNull())
2691
+ .addColumn('name', 'varchar', (col) => col.notNull())
2692
+ .addColumn('component_name', 'varchar')
2693
+ .addColumn('component_path', 'varchar')
2694
+ .addColumn('entity_sha', 'varchar')
2695
+ .addColumn('display_name', 'varchar')
2696
+ .addColumn('page_file_path', 'varchar')
2697
+ .addColumn('url', 'varchar')
2698
+ .addColumn('type', 'varchar')
2699
+ .addColumn('created_at', 'datetime')
2700
+ .addColumn('updated_at', 'datetime')
2701
+ .execute();
2702
+ });
2703
+ afterEach(async () => {
2704
+ await db.destroy();
2705
+ });
2706
+ it('should return empty when all scenarios have entity_sha', async () => {
2707
+ await db
2708
+ .insertInto('editor_scenarios')
2709
+ .values({
2710
+ id: 'sc-1',
2711
+ project_id: projectId,
2712
+ name: 'Header - Default',
2713
+ component_name: 'Header',
2714
+ component_path: 'src/components/Header.tsx',
2715
+ entity_sha: 'sha-header',
2716
+ created_at: '2026-03-16 23:00:00',
2717
+ updated_at: '2026-03-16 23:00:00',
2718
+ })
2719
+ .execute();
2720
+ const result = await queryUnassociatedScenarios(db, projectId, null);
2721
+ expect(result).toEqual([]);
2722
+ });
2723
+ it('should find component scenarios with NULL entity_sha', async () => {
2724
+ // This reproduces the Margo testapp bug: subagent registered scenarios
2725
+ // but entity records didn't exist yet, so entity_sha was never set
2726
+ await db
2727
+ .insertInto('editor_scenarios')
2728
+ .values({
2729
+ id: 'sc-1',
2730
+ project_id: projectId,
2731
+ name: 'SearchBar - Default',
2732
+ component_name: 'SearchBar',
2733
+ component_path: 'src/components/SearchBar.tsx',
2734
+ entity_sha: null,
2735
+ created_at: '2026-03-20 18:45:00',
2736
+ updated_at: '2026-03-20 18:45:00',
2737
+ })
2738
+ .execute();
2739
+ await db
2740
+ .insertInto('editor_scenarios')
2741
+ .values({
2742
+ id: 'sc-2',
2743
+ project_id: projectId,
2744
+ name: 'SearchBar - With Results',
2745
+ component_name: 'SearchBar',
2746
+ component_path: 'src/components/SearchBar.tsx',
2747
+ entity_sha: null,
2748
+ created_at: '2026-03-20 18:45:05',
2749
+ updated_at: '2026-03-20 18:45:05',
2750
+ })
2751
+ .execute();
2752
+ const result = await queryUnassociatedScenarios(db, projectId, null);
2753
+ expect(result).toHaveLength(1);
2754
+ expect(result[0].name).toBe('SearchBar');
2755
+ expect(result[0].filePath).toBe('src/components/SearchBar.tsx');
2756
+ expect(result[0].scenarioCount).toBe(2);
2757
+ expect(result[0].scenarioNames).toEqual(expect.arrayContaining([
2758
+ 'SearchBar - Default',
2759
+ 'SearchBar - With Results',
2760
+ ]));
2761
+ });
2762
+ it('should find page scenarios with NULL entity_sha', async () => {
2763
+ await db
2764
+ .insertInto('editor_scenarios')
2765
+ .values({
2766
+ id: 'sc-1',
2767
+ project_id: projectId,
2768
+ name: 'Full Page — Rich Library',
2769
+ component_name: null,
2770
+ component_path: null,
2771
+ page_file_path: 'src/library/LibraryApp.tsx',
2772
+ entity_sha: null,
2773
+ created_at: '2026-03-20 18:50:00',
2774
+ updated_at: '2026-03-20 18:50:00',
2775
+ })
2776
+ .execute();
2777
+ const result = await queryUnassociatedScenarios(db, projectId, null);
2778
+ expect(result).toHaveLength(1);
2779
+ expect(result[0].name).toBe('LibraryApp');
2780
+ expect(result[0].filePath).toBe('src/library/LibraryApp.tsx');
2781
+ expect(result[0].scenarioCount).toBe(1);
2782
+ });
2783
+ it('should ignore scenarios without any file path (orphans without component_path or page_file_path)', async () => {
2784
+ // Scenarios with no file path at all can't be associated — they're not
2785
+ // actionable, so don't report them as unassociated
2786
+ await db
2787
+ .insertInto('editor_scenarios')
2788
+ .values({
2789
+ id: 'sc-1',
2790
+ project_id: projectId,
2791
+ name: 'Some Orphan',
2792
+ component_name: null,
2793
+ component_path: null,
2794
+ page_file_path: null,
2795
+ entity_sha: null,
2796
+ created_at: '2026-03-20 18:50:00',
2797
+ updated_at: '2026-03-20 18:50:00',
2798
+ })
2799
+ .execute();
2800
+ const result = await queryUnassociatedScenarios(db, projectId, null);
2801
+ expect(result).toEqual([]);
2802
+ });
2803
+ it('should group multiple components separately', async () => {
2804
+ // Two different components both missing entity_sha
2805
+ await db
2806
+ .insertInto('editor_scenarios')
2807
+ .values({
2808
+ id: 'sc-1',
2809
+ project_id: projectId,
2810
+ name: 'FullPageHeader - Default',
2811
+ component_name: 'FullPageHeader',
2812
+ component_path: 'src/components/FullPageHeader.tsx',
2813
+ entity_sha: null,
2814
+ created_at: '2026-03-20 18:45:00',
2815
+ updated_at: '2026-03-20 18:45:00',
2816
+ })
2817
+ .execute();
2818
+ await db
2819
+ .insertInto('editor_scenarios')
2820
+ .values({
2821
+ id: 'sc-2',
2822
+ project_id: projectId,
2823
+ name: 'SaveConfirmation - Visible',
2824
+ component_name: 'SaveConfirmation',
2825
+ component_path: 'src/components/SaveConfirmation.tsx',
2826
+ entity_sha: null,
2827
+ created_at: '2026-03-20 19:00:00',
2828
+ updated_at: '2026-03-20 19:00:00',
2829
+ })
2830
+ .execute();
2831
+ const result = await queryUnassociatedScenarios(db, projectId, null);
2832
+ expect(result).toHaveLength(2);
2833
+ const names = result.map((r) => r.name).sort();
2834
+ expect(names).toEqual(['FullPageHeader', 'SaveConfirmation']);
2835
+ });
2836
+ it('should only include scenarios from the specified project', async () => {
2837
+ await db
2838
+ .insertInto('editor_scenarios')
2839
+ .values({
2840
+ id: 'sc-1',
2841
+ project_id: 'other-project',
2842
+ name: 'SearchBar - Default',
2843
+ component_name: 'SearchBar',
2844
+ component_path: 'src/components/SearchBar.tsx',
2845
+ entity_sha: null,
2846
+ created_at: '2026-03-20 18:45:00',
2847
+ updated_at: '2026-03-20 18:45:00',
2848
+ })
2849
+ .execute();
2850
+ const result = await queryUnassociatedScenarios(db, projectId, null);
2851
+ expect(result).toEqual([]);
2852
+ });
2853
+ it('should scope to feature session when featureStartedAt is provided', async () => {
2854
+ // Pre-existing unassociated scenario (before session)
2855
+ await db
2856
+ .insertInto('editor_scenarios')
2857
+ .values({
2858
+ id: 'sc-old',
2859
+ project_id: projectId,
2860
+ name: 'OldComponent - Default',
2861
+ component_name: 'OldComponent',
2862
+ component_path: 'src/components/OldComponent.tsx',
2863
+ entity_sha: null,
2864
+ created_at: '2026-03-19 10:00:00',
2865
+ updated_at: '2026-03-19 10:00:00',
2866
+ })
2867
+ .execute();
2868
+ // New unassociated scenario (during session)
2869
+ await db
2870
+ .insertInto('editor_scenarios')
2871
+ .values({
2872
+ id: 'sc-new',
2873
+ project_id: projectId,
2874
+ name: 'NewComponent - Default',
2875
+ component_name: 'NewComponent',
2876
+ component_path: 'src/components/NewComponent.tsx',
2877
+ entity_sha: null,
2878
+ created_at: '2026-03-20 18:45:00',
2879
+ updated_at: '2026-03-20 18:45:00',
2880
+ })
2881
+ .execute();
2882
+ const result = await queryUnassociatedScenarios(db, projectId, '2026-03-20T18:00:00.000Z');
2883
+ // Should only find the session-scoped one
2884
+ expect(result).toHaveLength(1);
2885
+ expect(result[0].name).toBe('NewComponent');
2886
+ });
2887
+ it('should include re-registered scenarios (updated_at in session) even if created before', async () => {
2888
+ await db
2889
+ .insertInto('editor_scenarios')
2890
+ .values({
2891
+ id: 'sc-1',
2892
+ project_id: projectId,
2893
+ name: 'SearchBar - Default',
2894
+ component_name: 'SearchBar',
2895
+ component_path: 'src/components/SearchBar.tsx',
2896
+ entity_sha: null,
2897
+ created_at: '2026-03-19 10:00:00',
2898
+ updated_at: '2026-03-20 18:45:00', // re-registered during session
2899
+ })
2900
+ .execute();
2901
+ const result = await queryUnassociatedScenarios(db, projectId, '2026-03-20T18:00:00.000Z');
2902
+ expect(result).toHaveLength(1);
2903
+ expect(result[0].name).toBe('SearchBar');
2904
+ });
2905
+ it('should not include scenarios with entity_sha set (even if stale)', async () => {
2906
+ // This scenario has an entity_sha — even if it's stale, that's a
2907
+ // different problem (handled by queryIncompleteEntities)
2908
+ await db
2909
+ .insertInto('editor_scenarios')
2910
+ .values({
2911
+ id: 'sc-1',
2912
+ project_id: projectId,
2913
+ name: 'Header - Default',
2914
+ component_name: 'Header',
2915
+ component_path: 'src/components/Header.tsx',
2916
+ entity_sha: 'sha-old-version',
2917
+ created_at: '2026-03-20 18:45:00',
2918
+ updated_at: '2026-03-20 18:45:00',
2919
+ })
2920
+ .execute();
2921
+ const result = await queryUnassociatedScenarios(db, projectId, null);
2922
+ expect(result).toEqual([]);
2923
+ });
2924
+ });
2925
+ // ── isAutoRemediable with unassociatedScenarios ────────────────────
2926
+ describe('isAutoRemediable with unassociatedScenarios', () => {
2927
+ it('should return true when unassociatedScenarios is the only failure', () => {
2928
+ expect(isAutoRemediable({ unassociatedScenarios: 3 }, false)).toBe(true);
2929
+ });
2930
+ it('should return true when both incompleteEntities and unassociatedScenarios are the only failures', () => {
2931
+ expect(isAutoRemediable({ incompleteEntities: 1, unassociatedScenarios: 2 }, false)).toBe(true);
2932
+ });
2933
+ it('should return false when unassociatedScenarios exist alongside other failures', () => {
2934
+ expect(isAutoRemediable({ unassociatedScenarios: 2, componentsMissing: 1 }, false)).toBe(false);
2935
+ });
2936
+ it('should return false when already attempted', () => {
2937
+ expect(isAutoRemediable({ unassociatedScenarios: 3 }, true)).toBe(false);
2938
+ });
2939
+ });
2940
+ describe('isOnlyIncompleteEntities with unassociatedScenarios', () => {
2941
+ it('should return true when only unassociatedScenarios present', () => {
2942
+ expect(isOnlyIncompleteEntities({ unassociatedScenarios: 5 })).toBe(true);
2943
+ });
2944
+ it('should return false when unassociatedScenarios present with other failures', () => {
2945
+ expect(isOnlyIncompleteEntities({
2946
+ unassociatedScenarios: 5,
2947
+ functionsMissing: 1,
2948
+ })).toBe(false);
2949
+ });
2950
+ });
2679
2951
  });
2680
2952
  //# sourceMappingURL=editorAudit.test.js.map