@codeyam/codeyam-cli 0.1.19 → 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.
- package/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +1 -1
- package/analyzer-template/packages/analyze/src/lib/files/analyze/findOrCreateEntity.ts +9 -6
- package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +9 -12
- package/analyzer-template/packages/database/package.json +1 -1
- package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +19 -15
- package/analyzer-template/packages/database/src/lib/loadEntity.ts +8 -4
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntity.js.map +1 -1
- package/analyzer-template/project/analyzeFileEntities.ts +26 -0
- package/background/src/lib/virtualized/project/analyzeFileEntities.js +22 -0
- package/background/src/lib/virtualized/project/analyzeFileEntities.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +252 -62
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +273 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +20 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js +84 -0
- package/codeyam-cli/src/utils/__tests__/screenshotHash.test.js.map +1 -0
- package/codeyam-cli/src/utils/analysisRunner.js +8 -6
- package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
- package/codeyam-cli/src/utils/editorAudit.js +73 -4
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarios.js +36 -4
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/queue/job.js +6 -3
- package/codeyam-cli/src/utils/queue/job.js.map +1 -1
- package/codeyam-cli/src/utils/screenshotHash.js +26 -0
- package/codeyam-cli/src/utils/screenshotHash.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js +28 -1
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +22 -2
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js +57 -1
- package/codeyam-cli/src/webserver/__tests__/stripClaudeCommand.test.js.map +1 -1
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js +12 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{editor.entity.(_sha)-CGzKlIHg.js → editor.entity.(_sha)-DII1pg_z.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{manifest-2ef99f38.js → manifest-cdf2c0a7.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-B_PsTAb1.js +13 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-Cd-ufawF.js → index-CjLhfz6Z.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-CzeBGOto.js → init-BEqlbI84.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{server-build-Dht7CKXY.js → server-build-YI63xTu4.js} +94 -93
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/scripts/journalCapture.ts +17 -0
- package/codeyam-cli/src/webserver/server.js +52 -14
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +51 -10
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +21 -0
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +19 -1
- package/package.json +1 -1
- package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js +2 -2
- package/packages/analyze/src/lib/files/analyze/findOrCreateEntity.js.map +1 -1
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +9 -7
- package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
- package/packages/database/src/lib/loadAnalysis.js +1 -1
- package/packages/database/src/lib/loadAnalysis.js.map +1 -1
- package/packages/database/src/lib/loadEntity.js +1 -1
- package/packages/database/src/lib/loadEntity.js.map +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/analysisRunner-BPmOG9bE.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
|