@codeyam/codeyam-cli 0.1.0-staging.a890816 → 0.1.0-staging.ae0de75
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 +6 -6
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +6 -6
- package/analyzer-template/packages/ai/package.json +1 -1
- package/analyzer-template/packages/aws/package.json +1 -1
- package/analyzer-template/packages/database/package.json +3 -3
- package/analyzer-template/packages/github/package.json +1 -1
- package/codeyam-cli/src/commands/editor.js +892 -90
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/init.js +6 -1
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +246 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +126 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +295 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js +270 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +100 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +147 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +76 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js +134 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/project.test.js +65 -0
- package/codeyam-cli/src/utils/__tests__/project.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js +121 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js.map +1 -0
- package/codeyam-cli/src/utils/buildFlags.js +4 -0
- package/codeyam-cli/src/utils/buildFlags.js.map +1 -0
- package/codeyam-cli/src/utils/editorAudit.js +82 -0
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -0
- package/codeyam-cli/src/utils/editorDevServer.js +98 -0
- package/codeyam-cli/src/utils/editorDevServer.js.map +1 -0
- package/codeyam-cli/src/utils/editorJournal.js +137 -0
- package/codeyam-cli/src/utils/editorJournal.js.map +1 -0
- package/codeyam-cli/src/utils/editorMockState.js +248 -0
- package/codeyam-cli/src/utils/editorMockState.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js +64 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreview.js +66 -0
- package/codeyam-cli/src/utils/editorPreview.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js +56 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -0
- package/codeyam-cli/src/utils/git.js +51 -0
- package/codeyam-cli/src/utils/git.js.map +1 -1
- package/codeyam-cli/src/utils/install-skills.js +28 -17
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/project.js +15 -5
- package/codeyam-cli/src/utils/project.js.map +1 -1
- package/codeyam-cli/src/utils/scenarioMarkers.js +134 -0
- package/codeyam-cli/src/utils/scenarioMarkers.js.map +1 -0
- package/codeyam-cli/src/utils/testRunner.js +1 -1
- package/codeyam-cli/src/utils/testRunner.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-wkqC0AQk.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-audit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-load-commit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor-CdjF_fX6.js +8 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D8ILZMR0.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-B17TBSS6.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/manifest-b8fd6b07.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{root-DiRdBreB.js → root-DUKqhFlb.js} +7 -7
- package/codeyam-cli/src/webserver/build/client/assets/xterm-BqvuqXEL.js +27 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-BzAbACSx.js → index-BLhjL9Xi.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DyMuI5mU.js +363 -0
- 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/editorProxy.js +182 -14
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +175 -0
- package/codeyam-cli/src/webserver/server.js +61 -12
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +29 -103
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/editor-step-hook.py +6 -4
- package/codeyam-cli/templates/{codeyam-dev-mode.md → skills/codeyam-dev-mode/SKILL.md} +1 -1
- package/codeyam-cli/templates/{codeyam-editor.md → skills/codeyam-editor/SKILL.md} +5 -4
- package/codeyam-cli/templates/{codeyam-memory.md → skills/codeyam-memory/SKILL.md} +215 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/deprecated-prompt.md +100 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.sh +108 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.sh +69 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/misleading-api-prompt.md +117 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/analyze-prompt.md +46 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.sh +12 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter.jq +45 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.sh +139 -0
- package/package.json +2 -2
- package/scripts/npm-post-install.cjs +12 -0
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-CcG8YTLx.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/editor-W_IGJ2Kd.js +0 -7
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-D6SEzMCu.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/globals-BZB_H1w2.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-8daa4147.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/xterm-DMSzMhqy.js +0 -9
- package/codeyam-cli/src/webserver/build/server/assets/server-build-OdUocH6P.js +0 -362
- package/scripts/finalize-analyzer.cjs +0 -13
- /package/codeyam-cli/templates/{codeyam-diagnose.md → commands/codeyam-diagnose.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-debug.md → skills/codeyam-debug/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-new-rule.md → skills/codeyam-new-rule/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-setup.md → skills/codeyam-setup/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-sim.md → skills/codeyam-sim/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-test.md → skills/codeyam-test/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-verify.md → skills/codeyam-verify/SKILL.md} +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { validateCommitSha, verifyCommitExists, gitStash, gitCheckout, } from "../git.js";
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import * as os from 'os';
|
|
6
|
+
describe('git editor utilities', () => {
|
|
7
|
+
describe('validateCommitSha', () => {
|
|
8
|
+
it('should accept a valid 7-char short SHA', () => {
|
|
9
|
+
expect(validateCommitSha('abc1234')).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
it('should accept a valid 40-char full SHA', () => {
|
|
12
|
+
expect(validateCommitSha('abc1234567890def1234567890abc123456789de')).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it('should accept uppercase hex chars', () => {
|
|
15
|
+
expect(validateCommitSha('ABC1234DEF')).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
it('should reject too-short SHAs (< 7 chars)', () => {
|
|
18
|
+
expect(validateCommitSha('abc12')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it('should reject too-long SHAs (> 40 chars)', () => {
|
|
21
|
+
expect(validateCommitSha('a'.repeat(41))).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
it('should reject non-hex characters', () => {
|
|
24
|
+
expect(validateCommitSha('abcdefg')).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it('should reject empty string', () => {
|
|
27
|
+
expect(validateCommitSha('')).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
it('should reject command injection attempts', () => {
|
|
30
|
+
expect(validateCommitSha('abc1234; rm -rf /')).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('verifyCommitExists', () => {
|
|
34
|
+
let tempDir;
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-test-'));
|
|
37
|
+
execSync('git init', { cwd: tempDir, stdio: 'pipe' });
|
|
38
|
+
execSync('git config user.email "test@test.com"', {
|
|
39
|
+
cwd: tempDir,
|
|
40
|
+
stdio: 'pipe',
|
|
41
|
+
});
|
|
42
|
+
execSync('git config user.name "Test"', { cwd: tempDir, stdio: 'pipe' });
|
|
43
|
+
fs.writeFileSync(path.join(tempDir, 'file.txt'), 'hello');
|
|
44
|
+
execSync('git add -A && git commit -m "initial"', {
|
|
45
|
+
cwd: tempDir,
|
|
46
|
+
stdio: 'pipe',
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
51
|
+
});
|
|
52
|
+
it('should return true for an existing commit', () => {
|
|
53
|
+
const sha = execSync('git rev-parse HEAD', {
|
|
54
|
+
cwd: tempDir,
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
}).trim();
|
|
57
|
+
expect(verifyCommitExists(tempDir, sha)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
it('should return false for a non-existent commit', () => {
|
|
60
|
+
expect(verifyCommitExists(tempDir, 'deadbeefdeadbeef')).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('gitStash', () => {
|
|
64
|
+
let tempDir;
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-stash-test-'));
|
|
67
|
+
execSync('git init', { cwd: tempDir, stdio: 'pipe' });
|
|
68
|
+
execSync('git config user.email "test@test.com"', {
|
|
69
|
+
cwd: tempDir,
|
|
70
|
+
stdio: 'pipe',
|
|
71
|
+
});
|
|
72
|
+
execSync('git config user.name "Test"', { cwd: tempDir, stdio: 'pipe' });
|
|
73
|
+
fs.writeFileSync(path.join(tempDir, 'file.txt'), 'hello');
|
|
74
|
+
execSync('git add -A && git commit -m "initial"', {
|
|
75
|
+
cwd: tempDir,
|
|
76
|
+
stdio: 'pipe',
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
afterEach(() => {
|
|
80
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
81
|
+
});
|
|
82
|
+
it('should stash uncommitted changes', () => {
|
|
83
|
+
fs.writeFileSync(path.join(tempDir, 'file.txt'), 'modified');
|
|
84
|
+
const result = gitStash(tempDir, 'test stash');
|
|
85
|
+
expect(result.stashed).toBe(true);
|
|
86
|
+
// File should be restored to committed state
|
|
87
|
+
const content = fs.readFileSync(path.join(tempDir, 'file.txt'), 'utf8');
|
|
88
|
+
expect(content).toBe('hello');
|
|
89
|
+
});
|
|
90
|
+
it('should return stashed=false when nothing to stash', () => {
|
|
91
|
+
const result = gitStash(tempDir, 'test stash');
|
|
92
|
+
expect(result.stashed).toBe(false);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('gitCheckout', () => {
|
|
96
|
+
let tempDir;
|
|
97
|
+
let firstSha;
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-checkout-test-'));
|
|
100
|
+
execSync('git init', { cwd: tempDir, stdio: 'pipe' });
|
|
101
|
+
execSync('git config user.email "test@test.com"', {
|
|
102
|
+
cwd: tempDir,
|
|
103
|
+
stdio: 'pipe',
|
|
104
|
+
});
|
|
105
|
+
execSync('git config user.name "Test"', { cwd: tempDir, stdio: 'pipe' });
|
|
106
|
+
fs.writeFileSync(path.join(tempDir, 'file.txt'), 'v1');
|
|
107
|
+
execSync('git add -A && git commit -m "first"', {
|
|
108
|
+
cwd: tempDir,
|
|
109
|
+
stdio: 'pipe',
|
|
110
|
+
});
|
|
111
|
+
firstSha = execSync('git rev-parse HEAD', {
|
|
112
|
+
cwd: tempDir,
|
|
113
|
+
encoding: 'utf8',
|
|
114
|
+
}).trim();
|
|
115
|
+
fs.writeFileSync(path.join(tempDir, 'file.txt'), 'v2');
|
|
116
|
+
execSync('git add -A && git commit -m "second"', {
|
|
117
|
+
cwd: tempDir,
|
|
118
|
+
stdio: 'pipe',
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
afterEach(() => {
|
|
122
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
123
|
+
});
|
|
124
|
+
it('should checkout a specific commit', () => {
|
|
125
|
+
gitCheckout(tempDir, firstSha);
|
|
126
|
+
const content = fs.readFileSync(path.join(tempDir, 'file.txt'), 'utf8');
|
|
127
|
+
expect(content).toBe('v1');
|
|
128
|
+
});
|
|
129
|
+
it('should throw on invalid ref', () => {
|
|
130
|
+
expect(() => gitCheckout(tempDir, 'nonexistent_ref')).toThrow();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=git.editor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.editor.test.js","sourceRoot":"","sources":["../../../../../src/utils/__tests__/git.editor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { findProjectRoot, isForbiddenProjectRoot } from "../project.js";
|
|
5
|
+
describe('isForbiddenProjectRoot', () => {
|
|
6
|
+
it('returns true for the home directory', () => {
|
|
7
|
+
expect(isForbiddenProjectRoot(os.homedir())).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
it('returns true for the filesystem root', () => {
|
|
10
|
+
expect(isForbiddenProjectRoot('/')).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it('returns true for home dir with trailing slash', () => {
|
|
13
|
+
expect(isForbiddenProjectRoot(os.homedir() + '/')).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it('returns false for a normal project directory', () => {
|
|
16
|
+
expect(isForbiddenProjectRoot(path.join(os.homedir(), 'projects', 'my-app'))).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('returns false for a directory just inside home', () => {
|
|
19
|
+
expect(isForbiddenProjectRoot(path.join(os.homedir(), 'my-project'))).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('findProjectRoot', () => {
|
|
23
|
+
let tempDir;
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codeyam-test-'));
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
29
|
+
});
|
|
30
|
+
it('finds a project in the start directory', () => {
|
|
31
|
+
const codeyamDir = path.join(tempDir, '.codeyam');
|
|
32
|
+
fs.mkdirSync(codeyamDir);
|
|
33
|
+
fs.writeFileSync(path.join(codeyamDir, 'config.json'), JSON.stringify({ projectSlug: 'test' }));
|
|
34
|
+
expect(findProjectRoot(tempDir)).toBe(tempDir);
|
|
35
|
+
});
|
|
36
|
+
it('finds a project in a parent directory', () => {
|
|
37
|
+
const codeyamDir = path.join(tempDir, '.codeyam');
|
|
38
|
+
fs.mkdirSync(codeyamDir);
|
|
39
|
+
fs.writeFileSync(path.join(codeyamDir, 'config.json'), JSON.stringify({ projectSlug: 'test' }));
|
|
40
|
+
const subDir = path.join(tempDir, 'src', 'components');
|
|
41
|
+
fs.mkdirSync(subDir, { recursive: true });
|
|
42
|
+
expect(findProjectRoot(subDir)).toBe(tempDir);
|
|
43
|
+
});
|
|
44
|
+
it('returns null when no project is found', () => {
|
|
45
|
+
const subDir = path.join(tempDir, 'src');
|
|
46
|
+
fs.mkdirSync(subDir, { recursive: true });
|
|
47
|
+
expect(findProjectRoot(subDir)).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
it('does not return the home directory as a project root', () => {
|
|
50
|
+
// Even if ~/.codeyam/config.json exists, findProjectRoot should skip it.
|
|
51
|
+
// We can't create files in the real home dir in a test, but we can verify
|
|
52
|
+
// the guard logic by checking that findProjectRoot stops at home.
|
|
53
|
+
// Start from a subdirectory of home with no .codeyam anywhere —
|
|
54
|
+
// it should return null without ever checking ~.
|
|
55
|
+
const subDir = path.join(os.homedir(), 'nonexistent-codeyam-test-dir');
|
|
56
|
+
// Don't create it — findProjectRoot uses existsSync on config.json, not the dir
|
|
57
|
+
expect(findProjectRoot(subDir)).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
it('does not return the filesystem root as a project root', () => {
|
|
60
|
+
// Starting from /tmp/something with no .codeyam, should return null
|
|
61
|
+
// (and not check / for .codeyam/config.json)
|
|
62
|
+
expect(findProjectRoot(tempDir)).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=project.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.test.js","sourceRoot":"","sources":["../../../../../src/utils/__tests__/project.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { transformScenarioMarkers, createMarkerTransformer, } from "../scenarioMarkers.js";
|
|
2
|
+
const PORT = '3111';
|
|
3
|
+
describe('scenarioMarkers', () => {
|
|
4
|
+
describe('transformScenarioMarkers', () => {
|
|
5
|
+
it('should transform a simple marker into an OSC 8 hyperlink', () => {
|
|
6
|
+
const input = '{{scenario:Default:abc-123}}';
|
|
7
|
+
const result = transformScenarioMarkers(input, PORT);
|
|
8
|
+
expect(result).toContain('\x1b]8;;');
|
|
9
|
+
expect(result).toContain('http://localhost:3111/editor?scenario=abc-123&ref=link');
|
|
10
|
+
expect(result).toContain('Default');
|
|
11
|
+
expect(result).not.toContain('{{scenario:');
|
|
12
|
+
});
|
|
13
|
+
it('should transform a marker with spaces in the name', () => {
|
|
14
|
+
const input = '{{scenario:Long Name:abc-123}}';
|
|
15
|
+
const result = transformScenarioMarkers(input, PORT);
|
|
16
|
+
expect(result).toContain('Long Name');
|
|
17
|
+
expect(result).not.toContain('{{scenario:');
|
|
18
|
+
});
|
|
19
|
+
it('should transform multiple markers in one string', () => {
|
|
20
|
+
const input = '| DrinkCard | {{scenario:Default:id1}} |\n| | {{scenario:Dark Mode:id2}} |';
|
|
21
|
+
const result = transformScenarioMarkers(input, PORT);
|
|
22
|
+
expect(result).not.toContain('{{scenario:');
|
|
23
|
+
expect(result).toContain('Default');
|
|
24
|
+
expect(result).toContain('Dark Mode');
|
|
25
|
+
});
|
|
26
|
+
it('should return input unchanged when no markers present', () => {
|
|
27
|
+
const input = 'Hello world, no markers here';
|
|
28
|
+
expect(transformScenarioMarkers(input, PORT)).toBe(input);
|
|
29
|
+
});
|
|
30
|
+
it('should handle markers with ANSI escape codes interleaved', () => {
|
|
31
|
+
// Claude Code's renderer inserts ANSI styling within marker text
|
|
32
|
+
const input = '\x1b[33m{{scenario:\x1b[1mDefault\x1b[22m:\x1b[0mabc-123\x1b[33m}}\x1b[0m';
|
|
33
|
+
const result = transformScenarioMarkers(input, PORT);
|
|
34
|
+
expect(result).toContain('Default');
|
|
35
|
+
expect(result).not.toContain('{{scenario:');
|
|
36
|
+
});
|
|
37
|
+
it('should pad the replacement to maintain table alignment', () => {
|
|
38
|
+
const input = '{{scenario:Tea:abc-123}}';
|
|
39
|
+
const result = transformScenarioMarkers(input, PORT);
|
|
40
|
+
// The original marker is 24 chars visible, "Tea" is 3 chars → 21 spaces of padding
|
|
41
|
+
const linkEnd = '\x1b]8;;\x07';
|
|
42
|
+
const afterLink = result.slice(result.lastIndexOf(linkEnd) + linkEnd.length);
|
|
43
|
+
// Should have padding spaces
|
|
44
|
+
expect(afterLink.length).toBeGreaterThan(0);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('createMarkerTransformer (cross-chunk buffering)', () => {
|
|
48
|
+
it('should handle a marker split across two chunks', () => {
|
|
49
|
+
const transform = createMarkerTransformer(PORT);
|
|
50
|
+
const out1 = transform('before {{scenario:Def');
|
|
51
|
+
const out2 = transform('ault:abc-123}} after');
|
|
52
|
+
const combined = out1 + out2;
|
|
53
|
+
expect(combined).toContain('Default');
|
|
54
|
+
expect(combined).not.toContain('{{scenario:');
|
|
55
|
+
expect(combined).toContain('before');
|
|
56
|
+
expect(combined).toContain('after');
|
|
57
|
+
});
|
|
58
|
+
it('should handle a marker split across three chunks', () => {
|
|
59
|
+
const transform = createMarkerTransformer(PORT);
|
|
60
|
+
const out1 = transform('│ {{scenario:');
|
|
61
|
+
const out2 = transform('Long Name:');
|
|
62
|
+
const out3 = transform('abc-123}} │');
|
|
63
|
+
const combined = out1 + out2 + out3;
|
|
64
|
+
expect(combined).toContain('Long Name');
|
|
65
|
+
expect(combined).not.toContain('{{scenario:');
|
|
66
|
+
});
|
|
67
|
+
it('should handle complete markers in a single chunk', () => {
|
|
68
|
+
const transform = createMarkerTransformer(PORT);
|
|
69
|
+
const result = transform('│ {{scenario:Default:abc-123}} │');
|
|
70
|
+
expect(result).toContain('Default');
|
|
71
|
+
expect(result).not.toContain('{{scenario:');
|
|
72
|
+
});
|
|
73
|
+
it('should handle multiple complete markers in a single chunk', () => {
|
|
74
|
+
const transform = createMarkerTransformer(PORT);
|
|
75
|
+
const result = transform('{{scenario:A:id1}} and {{scenario:B:id2}}');
|
|
76
|
+
expect(result).not.toContain('{{scenario:');
|
|
77
|
+
expect(result).toContain('A');
|
|
78
|
+
expect(result).toContain('B');
|
|
79
|
+
});
|
|
80
|
+
it('should discard stale buffer on carriage return (Ink line rewrite)', () => {
|
|
81
|
+
const transform = createMarkerTransformer(PORT);
|
|
82
|
+
// Ink renders partial: "│ {{scenario:Lo"
|
|
83
|
+
const out1 = transform('│ {{scenario:Lo');
|
|
84
|
+
// Ink rewrites the line with more content
|
|
85
|
+
const out2 = transform('\r\x1b[K│ {{scenario:Long Na');
|
|
86
|
+
// Ink completes the line
|
|
87
|
+
const out3 = transform('me:abc-123}} │\n');
|
|
88
|
+
const combined = out1 + out2 + out3;
|
|
89
|
+
// The final output should have the transformed marker, not the raw one
|
|
90
|
+
expect(combined).toContain('Long Name');
|
|
91
|
+
expect(combined).not.toContain('{{scenario:');
|
|
92
|
+
});
|
|
93
|
+
it('should handle carriage return mid-buffer without losing the new marker', () => {
|
|
94
|
+
const transform = createMarkerTransformer(PORT);
|
|
95
|
+
// Buffer has partial marker, then line gets rewritten with a complete marker
|
|
96
|
+
const out1 = transform('{{scenario:Te');
|
|
97
|
+
const out2 = transform('\r\x1b[K{{scenario:Tea:id1}} │');
|
|
98
|
+
const combined = out1 + out2;
|
|
99
|
+
expect(combined).toContain('Tea');
|
|
100
|
+
expect(combined).not.toContain('{{scenario:');
|
|
101
|
+
});
|
|
102
|
+
it('should handle ANSI codes within a split marker', () => {
|
|
103
|
+
const transform = createMarkerTransformer(PORT);
|
|
104
|
+
const out1 = transform('\x1b[33m{{scenario:\x1b[1mDef');
|
|
105
|
+
const out2 = transform('ault\x1b[22m:abc-123\x1b[0m}}');
|
|
106
|
+
const combined = out1 + out2;
|
|
107
|
+
expect(combined).toContain('Default');
|
|
108
|
+
expect(combined).not.toContain('{{scenario:');
|
|
109
|
+
});
|
|
110
|
+
it('should flush stale partial on carriage return even without \\x1b[K', () => {
|
|
111
|
+
const transform = createMarkerTransformer(PORT);
|
|
112
|
+
const out1 = transform('│ {{scenario:Partia');
|
|
113
|
+
// Just \r, no erase sequence — Ink sometimes does this
|
|
114
|
+
const out2 = transform('\r│ {{scenario:Full Name:id1}} │');
|
|
115
|
+
const combined = out1 + out2;
|
|
116
|
+
expect(combined).toContain('Full Name');
|
|
117
|
+
expect(combined).not.toContain('{{scenario:');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=scenarioMarkers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenarioMarkers.test.js","sourceRoot":"","sources":["../../../../../src/utils/__tests__/scenarioMarkers.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildFlags.js","sourceRoot":"","sources":["../../../../src/utils/buildFlags.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAC5B,OAAO,0BAA0B,KAAK,WAAW;IAC/C,CAAC,CAAC,0BAA0B;IAC5B,CAAC,CAAC,KAAK,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure logic for `codeyam editor audit` — zero I/O, fully testable.
|
|
3
|
+
*
|
|
4
|
+
* Cross-references glossary entries against registered scenarios (components)
|
|
5
|
+
* and test files (functions) to detect gaps Claude may have missed.
|
|
6
|
+
*/
|
|
7
|
+
// ─── Logic ───────────────────────────────────────────────────────────
|
|
8
|
+
const COMPONENT_RETURN_PATTERNS = ['JSX', 'React', 'Element', 'ReactNode'];
|
|
9
|
+
/**
|
|
10
|
+
* Returns true when the entry's returnType indicates a React component
|
|
11
|
+
* (contains "JSX", "React", "Element", or "ReactNode").
|
|
12
|
+
*/
|
|
13
|
+
export function isComponent(entry) {
|
|
14
|
+
if (!entry.returnType)
|
|
15
|
+
return false;
|
|
16
|
+
return COMPONENT_RETURN_PATTERNS.some((pat) => entry.returnType.includes(pat));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Split glossary entries into components (JSX-returning) and functions (everything else).
|
|
20
|
+
*/
|
|
21
|
+
export function classifyGlossaryEntries(entries) {
|
|
22
|
+
const components = [];
|
|
23
|
+
const functions = [];
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
if (isComponent(entry)) {
|
|
26
|
+
components.push(entry);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
functions.push(entry);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { components, functions };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Given classified entries and coverage data, produce a structured audit result.
|
|
36
|
+
*
|
|
37
|
+
* @param components - Glossary entries classified as components
|
|
38
|
+
* @param functions - Glossary entries classified as functions
|
|
39
|
+
* @param scenarioCounts - Map of component_name → number of registered scenarios
|
|
40
|
+
* @param testFileExistence - Map of testFile path → whether the file exists on disk
|
|
41
|
+
*/
|
|
42
|
+
export function computeAudit({ components, functions, scenarioCounts, testFileExistence, }) {
|
|
43
|
+
const componentResults = components.map((c) => {
|
|
44
|
+
const count = scenarioCounts[c.name] || 0;
|
|
45
|
+
return {
|
|
46
|
+
name: c.name,
|
|
47
|
+
filePath: c.filePath,
|
|
48
|
+
scenarioCount: count,
|
|
49
|
+
status: count > 0 ? 'ok' : 'missing',
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
const functionResults = functions.map((f) => {
|
|
53
|
+
const exists = f.testFile
|
|
54
|
+
? (testFileExistence[f.testFile] ?? false)
|
|
55
|
+
: false;
|
|
56
|
+
return {
|
|
57
|
+
name: f.name,
|
|
58
|
+
filePath: f.filePath,
|
|
59
|
+
testFile: f.testFile,
|
|
60
|
+
testFileExists: exists,
|
|
61
|
+
status: exists ? 'ok' : 'missing',
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
const componentsOk = componentResults.filter((c) => c.status === 'ok').length;
|
|
65
|
+
const componentsMissing = componentResults.length - componentsOk;
|
|
66
|
+
const functionsOk = functionResults.filter((f) => f.status === 'ok').length;
|
|
67
|
+
const functionsMissing = functionResults.length - functionsOk;
|
|
68
|
+
return {
|
|
69
|
+
components: componentResults,
|
|
70
|
+
functions: functionResults,
|
|
71
|
+
summary: {
|
|
72
|
+
totalComponents: componentResults.length,
|
|
73
|
+
componentsOk,
|
|
74
|
+
componentsMissing,
|
|
75
|
+
totalFunctions: functionResults.length,
|
|
76
|
+
functionsOk,
|
|
77
|
+
functionsMissing,
|
|
78
|
+
allPassing: componentsMissing === 0 && functionsMissing === 0,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=editorAudit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorAudit.js","sourceRoot":"","sources":["../../../../src/utils/editorAudit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev server process management utilities for the editor.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from inline logic in api.editor-dev-server.ts.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
/**
|
|
9
|
+
* Detect the package manager and start script for a project.
|
|
10
|
+
*
|
|
11
|
+
* Returns `{ command, args }` on success, or `{ error }` if the project
|
|
12
|
+
* lacks a runnable dev/start script or package.json.
|
|
13
|
+
*/
|
|
14
|
+
export function detectPackageManagerAndScript(projectRoot) {
|
|
15
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
16
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
17
|
+
return { error: 'No package.json found.' };
|
|
18
|
+
}
|
|
19
|
+
let command = 'npm';
|
|
20
|
+
let args = ['run', 'dev'];
|
|
21
|
+
try {
|
|
22
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
23
|
+
// Detect package manager from lockfile
|
|
24
|
+
if (fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))) {
|
|
25
|
+
command = 'pnpm';
|
|
26
|
+
}
|
|
27
|
+
else if (fs.existsSync(path.join(projectRoot, 'yarn.lock'))) {
|
|
28
|
+
command = 'yarn';
|
|
29
|
+
}
|
|
30
|
+
else if (fs.existsSync(path.join(projectRoot, 'bun.lockb'))) {
|
|
31
|
+
command = 'bun';
|
|
32
|
+
}
|
|
33
|
+
// Check for dev or start script
|
|
34
|
+
if (!pkg.scripts?.dev) {
|
|
35
|
+
if (pkg.scripts?.start) {
|
|
36
|
+
args = ['run', 'start'];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return { error: 'No "dev" or "start" script found in package.json.' };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Use defaults (npm run dev)
|
|
45
|
+
}
|
|
46
|
+
return { command, args };
|
|
47
|
+
}
|
|
48
|
+
/** URL patterns matched in dev server output to detect the running URL. */
|
|
49
|
+
const URL_PATTERNS = [
|
|
50
|
+
/Local:\s+(https?:\/\/[^\s]+)/, // Vite
|
|
51
|
+
/Ready on\s+(https?:\/\/[^\s]+)/i, // Next.js
|
|
52
|
+
/started at\s+(https?:\/\/[^\s]+)/i, // Remix
|
|
53
|
+
/listening on\s+(https?:\/\/[^\s]+)/i, // Generic
|
|
54
|
+
/http:\/\/localhost:\d+/, // Bare URL
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Extract a dev server URL from process output text.
|
|
58
|
+
* Matches common patterns from Vite, Next.js, Remix, and generic servers.
|
|
59
|
+
* Strips ANSI codes from the result.
|
|
60
|
+
* Returns null if no URL is found.
|
|
61
|
+
*/
|
|
62
|
+
export function extractDevServerUrl(text) {
|
|
63
|
+
for (const pattern of URL_PATTERNS) {
|
|
64
|
+
const match = text.match(pattern);
|
|
65
|
+
if (match) {
|
|
66
|
+
const url = match[1] || match[0];
|
|
67
|
+
return stripAnsiCodes(url).trim();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Remove ANSI escape sequences from text.
|
|
74
|
+
*/
|
|
75
|
+
export function stripAnsiCodes(text) {
|
|
76
|
+
return text.replace(/\x1b\[[0-9;]*m/g, '');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Determine what action to take when the dev server process exits.
|
|
80
|
+
*
|
|
81
|
+
* - Quick failure (< 10s) on first attempt → retry
|
|
82
|
+
* - Non-zero exit after retry or after long uptime → error
|
|
83
|
+
* - Zero or null exit → stopped normally
|
|
84
|
+
*/
|
|
85
|
+
export function determineProcessExitState(params) {
|
|
86
|
+
const { exitCode, uptime, retryCount } = params;
|
|
87
|
+
const quickFailure = uptime < 10000;
|
|
88
|
+
if (exitCode !== 0 && exitCode !== null && quickFailure && retryCount === 0) {
|
|
89
|
+
return { action: 'retry' };
|
|
90
|
+
}
|
|
91
|
+
else if (exitCode !== 0 && exitCode !== null) {
|
|
92
|
+
return { action: 'error' };
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
return { action: 'stopped' };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=editorDevServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorDevServer.js","sourceRoot":"","sources":["../../../../src/utils/editorDevServer.ts"],"names":[],"mappings":"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"}
|