@tarunspandit/codexflow 0.29.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/AGENTS.example.md +18 -0
- package/CHANGELOG.md +311 -0
- package/CHATGPT_PROMPT.md +12 -0
- package/CODEX_PROMPT.md +12 -0
- package/CONTRIBUTING.md +51 -0
- package/DOMAIN_SETUP.md +241 -0
- package/FAQ.md +327 -0
- package/FAQ_ZH.md +279 -0
- package/LICENSE +21 -0
- package/NOTICE +9 -0
- package/PUBLIC_LAUNCH_CHECKLIST.md +111 -0
- package/README.md +287 -0
- package/README_ZH.md +461 -0
- package/SECURITY.md +145 -0
- package/config.example.env +31 -0
- package/dist/analysis/cache.js +27 -0
- package/dist/analysis/cache.js.map +1 -0
- package/dist/analysis/classify.js +102 -0
- package/dist/analysis/classify.js.map +1 -0
- package/dist/analysis/extract.js +139 -0
- package/dist/analysis/extract.js.map +1 -0
- package/dist/analysis/graph.js +22 -0
- package/dist/analysis/graph.js.map +1 -0
- package/dist/analysis/impact.js +167 -0
- package/dist/analysis/impact.js.map +1 -0
- package/dist/analysis/index.js +215 -0
- package/dist/analysis/index.js.map +1 -0
- package/dist/analysis/inventory.js +51 -0
- package/dist/analysis/inventory.js.map +1 -0
- package/dist/analysis/providers.js +24 -0
- package/dist/analysis/providers.js.map +1 -0
- package/dist/analysis/rank.js +27 -0
- package/dist/analysis/rank.js.map +1 -0
- package/dist/analysis/types.js +8 -0
- package/dist/analysis/types.js.map +1 -0
- package/dist/bashOps.js +233 -0
- package/dist/bashOps.js.map +1 -0
- package/dist/capabilitiesOps.js +365 -0
- package/dist/capabilitiesOps.js.map +1 -0
- package/dist/codexSessions.js +379 -0
- package/dist/codexSessions.js.map +1 -0
- package/dist/config.js +289 -0
- package/dist/config.js.map +1 -0
- package/dist/fsOps.js +286 -0
- package/dist/fsOps.js.map +1 -0
- package/dist/gitOps.js +79 -0
- package/dist/gitOps.js.map +1 -0
- package/dist/guard.js +198 -0
- package/dist/guard.js.map +1 -0
- package/dist/http.js +1671 -0
- package/dist/http.js.map +1 -0
- package/dist/proContext.js +274 -0
- package/dist/proContext.js.map +1 -0
- package/dist/profileStore.js +89 -0
- package/dist/profileStore.js.map +1 -0
- package/dist/projectCatalog.js +134 -0
- package/dist/projectCatalog.js.map +1 -0
- package/dist/redact.js +73 -0
- package/dist/redact.js.map +1 -0
- package/dist/searchOps.js +186 -0
- package/dist/searchOps.js.map +1 -0
- package/dist/server.js +2502 -0
- package/dist/server.js.map +1 -0
- package/dist/stdio.js +36 -0
- package/dist/stdio.js.map +1 -0
- package/dist/toolCardWidget.js +1155 -0
- package/dist/toolCardWidget.js.map +1 -0
- package/dist/workspaceOps.js +229 -0
- package/dist/workspaceOps.js.map +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/favicon.svg +5 -0
- package/docs/index.html +638 -0
- package/docs/og.png +0 -0
- package/docs/script.js +80 -0
- package/docs/star.svg +11 -0
- package/docs/styles.css +1229 -0
- package/docs/zh.html +436 -0
- package/package.json +94 -0
- package/scripts/analysis-cli-smoke.mjs +81 -0
- package/scripts/analysis-smoke.mjs +179 -0
- package/scripts/clean.mjs +6 -0
- package/scripts/cli-smoke.mjs +168 -0
- package/scripts/codexflow.mjs +4375 -0
- package/scripts/doctor-smoke.mjs +90 -0
- package/scripts/execute-handoff-smoke.mjs +1110 -0
- package/scripts/http-smoke.mjs +812 -0
- package/scripts/pro-apply.mjs +141 -0
- package/scripts/pro-bundle.mjs +121 -0
- package/scripts/pro-smoke.mjs +95 -0
- package/scripts/settings-smoke.mjs +756 -0
- package/scripts/smoke.mjs +1194 -0
- package/scripts/stress.mjs +835 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import fsp from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.log(`Apply a planning-model response to the agent handoff file
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
codexflow pro-apply --root /path/to/repo --file plan.md
|
|
12
|
+
cat plan.md | codexflow pro-apply --root /path/to/repo --stdin
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--root <dir> Workspace root. Default: current directory.
|
|
16
|
+
--file <path> Plan file to read.
|
|
17
|
+
--stdin Read the plan from stdin.
|
|
18
|
+
--title <text> Add a heading when the plan does not already start with one.
|
|
19
|
+
--append Append to .ai-bridge/current-plan.md instead of overwriting.
|
|
20
|
+
--help Show this message.
|
|
21
|
+
`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const out = {};
|
|
26
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
27
|
+
const raw = argv[i];
|
|
28
|
+
if (!raw.startsWith('--')) continue;
|
|
29
|
+
const eq = raw.indexOf('=');
|
|
30
|
+
const key = eq === -1 ? raw.slice(2) : raw.slice(2, eq);
|
|
31
|
+
const inlineValue = eq === -1 ? undefined : raw.slice(eq + 1);
|
|
32
|
+
if (key === 'help') out.help = true;
|
|
33
|
+
else if (key === 'stdin') out.stdin = true;
|
|
34
|
+
else if (key === 'append') out.append = true;
|
|
35
|
+
else {
|
|
36
|
+
const next = inlineValue ?? argv[i + 1];
|
|
37
|
+
if (!next || next.startsWith('--')) throw new Error(`Missing value for --${key}`);
|
|
38
|
+
if (inlineValue === undefined) i += 1;
|
|
39
|
+
out[key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = next;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function requireBuild() {
|
|
46
|
+
const distPath = path.resolve('dist/fsOps.js');
|
|
47
|
+
if (!fs.existsSync(distPath)) {
|
|
48
|
+
throw new Error('Missing dist/fsOps.js. Run npm install && npm run build first.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function readStdin() {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
55
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolvePlanPath(args) {
|
|
59
|
+
const callerCwd = process.env.CODEXFLOW_CALLER_CWD || process.cwd();
|
|
60
|
+
return path.isAbsolute(args.file) ? args.file : path.resolve(callerCwd, args.file);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function readPlan(args) {
|
|
64
|
+
if (args.stdin && args.file) throw new Error('Use either --stdin or --file, not both.');
|
|
65
|
+
if (args.stdin) return readStdin();
|
|
66
|
+
if (!args.file) throw new Error('Missing --file <path> or --stdin.');
|
|
67
|
+
return fsp.readFile(resolvePlanPath(args), 'utf8');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizePlan(rawPlan, title) {
|
|
71
|
+
const trimmed = rawPlan.trim();
|
|
72
|
+
if (!trimmed) throw new Error('Plan is empty.');
|
|
73
|
+
if (trimmed.startsWith('#')) return `${trimmed}\n`;
|
|
74
|
+
return `# ${title || 'Planning Model Handoff'}\n\nUpdated: ${new Date().toISOString()}\n\n${trimmed}\n`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function jsonlEvent(event, data) {
|
|
78
|
+
return `${JSON.stringify({ ts: new Date().toISOString(), event, ...data })}\n`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function main() {
|
|
82
|
+
const args = parseArgs(process.argv.slice(2));
|
|
83
|
+
if (args.help) {
|
|
84
|
+
usage();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
requireBuild();
|
|
88
|
+
|
|
89
|
+
const [{ loadConfig }, { WorkspaceManager, PathGuard }, { ensureAiBridge, readTextFile, writeTextFile }] = await Promise.all([
|
|
90
|
+
import('../dist/config.js'),
|
|
91
|
+
import('../dist/guard.js'),
|
|
92
|
+
import('../dist/fsOps.js')
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
const config = loadConfig(process.argv.slice(2));
|
|
96
|
+
const guard = new PathGuard(config);
|
|
97
|
+
const workspaces = new WorkspaceManager(config);
|
|
98
|
+
const workspace = workspaces.openWorkspace(config.defaultRoot);
|
|
99
|
+
|
|
100
|
+
const planPath = `${config.contextDir}/current-plan.md`;
|
|
101
|
+
const rawPlan = await readPlan(args);
|
|
102
|
+
const newPlan = normalizePlan(rawPlan, args.title);
|
|
103
|
+
await ensureAiBridge(config, guard, workspace);
|
|
104
|
+
let content = newPlan;
|
|
105
|
+
|
|
106
|
+
if (args.append) {
|
|
107
|
+
const resolved = guard.resolve(workspace, planPath);
|
|
108
|
+
const existing = await readTextFile(config, guard, workspace, planPath, { maxBytes: config.maxReadBytes });
|
|
109
|
+
const rawExisting = await fsp.readFile(resolved.absPath, 'utf8');
|
|
110
|
+
content = `${rawExisting.trimEnd()}\n\n---\n\n${newPlan}`;
|
|
111
|
+
void existing;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const result = await writeTextFile(config, guard, workspace, planPath, content, {
|
|
115
|
+
createDirs: true,
|
|
116
|
+
overwrite: true
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const logRel = `${config.contextDir}/session-log.jsonl`;
|
|
120
|
+
const executionLogRel = `${config.contextDir}/execution-log.jsonl`;
|
|
121
|
+
const logResolved = guard.resolve(workspace, logRel, { forWrite: true });
|
|
122
|
+
const executionLogResolved = guard.resolve(workspace, executionLogRel, { forWrite: true });
|
|
123
|
+
const event = jsonlEvent('pro_apply', {
|
|
124
|
+
plan_path: planPath,
|
|
125
|
+
source_file: args.file ? resolvePlanPath(args) : 'stdin',
|
|
126
|
+
append: Boolean(args.append)
|
|
127
|
+
});
|
|
128
|
+
await fsp.appendFile(logResolved.absPath, event, 'utf8');
|
|
129
|
+
await fsp.appendFile(executionLogResolved.absPath, event, 'utf8');
|
|
130
|
+
|
|
131
|
+
console.log(`Wrote ${path.join(workspace.root, planPath)}`);
|
|
132
|
+
console.log(`Bytes: ${result.bytes}`);
|
|
133
|
+
console.log(`Diff stats: +${result.diff.additions} -${result.diff.deletions}`);
|
|
134
|
+
console.log(`Session log: ${path.join(workspace.root, logRel)}`);
|
|
135
|
+
console.log(`Execution log: ${path.join(workspace.root, executionLogRel)}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
main().catch((error) => {
|
|
139
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
|
|
7
|
+
function usage() {
|
|
8
|
+
console.log(`CodexFlow context bundle
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
codexflow pro-bundle --root /path/to/repo --copy
|
|
12
|
+
codexflow pro-bundle --root /path/to/repo --path src/App.tsx --glob "src/**/*.ts"
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--root <dir> Workspace root. Default: current directory.
|
|
16
|
+
--path <file> Extra file to include. Can be repeated.
|
|
17
|
+
--glob <pattern> Extra glob to include. Can be repeated.
|
|
18
|
+
--title <text> Context title.
|
|
19
|
+
--max-files <n> Maximum file contents to include. Default: 24.
|
|
20
|
+
--max-file-bytes <n> Maximum bytes per included file. Default: 60000.
|
|
21
|
+
--max-total-bytes <n> Maximum bytes in .ai-bridge/pro-context.md.
|
|
22
|
+
--no-important-files Do not auto-include root config/docs such as AGENTS.md, README.md, package.json.
|
|
23
|
+
--no-changed-files Do not auto-include currently changed files from git status.
|
|
24
|
+
--no-diff Do not include git diff.
|
|
25
|
+
--no-ai-bridge Do not include existing .ai-bridge files.
|
|
26
|
+
--copy Copy generated context to the macOS clipboard with pbcopy.
|
|
27
|
+
--help Show this message.
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseArgs(argv) {
|
|
32
|
+
const out = { paths: [], globs: [] };
|
|
33
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
34
|
+
const raw = argv[i];
|
|
35
|
+
if (!raw.startsWith('--')) continue;
|
|
36
|
+
const eq = raw.indexOf('=');
|
|
37
|
+
const key = eq === -1 ? raw.slice(2) : raw.slice(2, eq);
|
|
38
|
+
const inlineValue = eq === -1 ? undefined : raw.slice(eq + 1);
|
|
39
|
+
if (key === 'help') out.help = true;
|
|
40
|
+
else if (key === 'copy') out.copy = true;
|
|
41
|
+
else if (key === 'no-important-files') out.noImportantFiles = true;
|
|
42
|
+
else if (key === 'no-changed-files') out.noChangedFiles = true;
|
|
43
|
+
else if (key === 'no-diff') out.noDiff = true;
|
|
44
|
+
else if (key === 'no-ai-bridge') out.noAiBridge = true;
|
|
45
|
+
else {
|
|
46
|
+
const next = inlineValue ?? argv[i + 1];
|
|
47
|
+
if (!next || next.startsWith('--')) throw new Error(`Missing value for --${key}`);
|
|
48
|
+
if (inlineValue === undefined) i += 1;
|
|
49
|
+
if (key === 'path') out.paths.push(next);
|
|
50
|
+
else if (key === 'glob') out.globs.push(next);
|
|
51
|
+
else out[key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = next;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function requireBuild() {
|
|
58
|
+
const distPath = path.resolve('dist/proContext.js');
|
|
59
|
+
if (!fs.existsSync(distPath)) {
|
|
60
|
+
throw new Error('Missing dist/proContext.js. Run npm install && npm run build first.');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function numberArg(value) {
|
|
65
|
+
if (value === undefined) return undefined;
|
|
66
|
+
const parsed = Number(value);
|
|
67
|
+
if (!Number.isFinite(parsed)) throw new Error(`Invalid number: ${value}`);
|
|
68
|
+
return parsed;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function main() {
|
|
72
|
+
const args = parseArgs(process.argv.slice(2));
|
|
73
|
+
if (args.help) {
|
|
74
|
+
usage();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
requireBuild();
|
|
78
|
+
|
|
79
|
+
const [{ loadConfig }, { WorkspaceManager, PathGuard }, { exportProContext }] = await Promise.all([
|
|
80
|
+
import('../dist/config.js'),
|
|
81
|
+
import('../dist/guard.js'),
|
|
82
|
+
import('../dist/proContext.js')
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
const config = loadConfig(process.argv.slice(2));
|
|
86
|
+
const guard = new PathGuard(config);
|
|
87
|
+
const workspaces = new WorkspaceManager(config);
|
|
88
|
+
const workspace = workspaces.openWorkspace(config.defaultRoot);
|
|
89
|
+
const result = await exportProContext(config, guard, workspace, {
|
|
90
|
+
title: args.title,
|
|
91
|
+
selectedPaths: args.paths,
|
|
92
|
+
extraGlobs: args.globs,
|
|
93
|
+
includeImportantFiles: !args.noImportantFiles,
|
|
94
|
+
includeChangedFiles: !args.noChangedFiles,
|
|
95
|
+
includeDiff: !args.noDiff,
|
|
96
|
+
includeAiBridge: !args.noAiBridge,
|
|
97
|
+
maxFiles: numberArg(args.maxFiles),
|
|
98
|
+
maxFileBytes: numberArg(args.maxFileBytes),
|
|
99
|
+
maxTotalBytes: numberArg(args.maxTotalBytes)
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const absPath = path.join(workspace.root, result.path);
|
|
103
|
+
console.log(`Wrote ${absPath}`);
|
|
104
|
+
console.log(`Bytes: ${result.bytes}`);
|
|
105
|
+
console.log(`Files included: ${result.filesIncluded.length}`);
|
|
106
|
+
console.log(`Files skipped: ${result.filesSkipped.length}`);
|
|
107
|
+
console.log(`Truncated: ${result.truncated}`);
|
|
108
|
+
|
|
109
|
+
if (args.copy) {
|
|
110
|
+
const copied = spawnSync('pbcopy', { input: result.markdown, encoding: 'utf8' });
|
|
111
|
+
if (copied.status !== 0) {
|
|
112
|
+
throw new Error(`pbcopy failed: ${copied.stderr || copied.error?.message || `exit ${copied.status}`}`);
|
|
113
|
+
}
|
|
114
|
+
console.log('Copied Pro context to clipboard.');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
main().catch((error) => {
|
|
119
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
120
|
+
process.exit(1);
|
|
121
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
function run(args, options = {}) {
|
|
7
|
+
const result = spawnSync(process.execPath, args, {
|
|
8
|
+
cwd: path.resolve('.'),
|
|
9
|
+
encoding: 'utf8',
|
|
10
|
+
...options
|
|
11
|
+
});
|
|
12
|
+
if (result.status !== 0) {
|
|
13
|
+
throw new Error(`${args.join(' ')} failed\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function runFail(args, options = {}) {
|
|
19
|
+
const result = spawnSync(process.execPath, args, {
|
|
20
|
+
cwd: path.resolve('.'),
|
|
21
|
+
encoding: 'utf8',
|
|
22
|
+
...options
|
|
23
|
+
});
|
|
24
|
+
if (result.status === 0) {
|
|
25
|
+
throw new Error(`${args.join(' ')} unexpectedly passed\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'codexflow-pro-smoke-'));
|
|
31
|
+
const codexflowCli = path.resolve('scripts/codexflow.mjs');
|
|
32
|
+
await fs.writeFile(path.join(root, 'README.md'), '# Demo\n', 'utf8');
|
|
33
|
+
await fs.writeFile(path.join(root, 'demo.txt'), 'alpha\nbeta\n', 'utf8');
|
|
34
|
+
|
|
35
|
+
run(['scripts/pro-bundle.mjs', `--root=${root}`, '--path=demo.txt', '--max-files', '4', '--max-total-bytes', '80000']);
|
|
36
|
+
const proContext = await fs.readFile(path.join(root, '.ai-bridge', 'pro-context.md'), 'utf8');
|
|
37
|
+
if (!proContext.includes('CodexFlow Context Bundle') || !proContext.includes('demo.txt')) {
|
|
38
|
+
throw new Error('pro context bundle did not include expected content');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const selectedRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'codexflow-pro-selected-'));
|
|
42
|
+
await fs.writeFile(path.join(selectedRoot, 'README.md'), '# Selected\n', 'utf8');
|
|
43
|
+
await fs.writeFile(path.join(selectedRoot, 'demo.txt'), 'selected only\n', 'utf8');
|
|
44
|
+
run([
|
|
45
|
+
'scripts/pro-bundle.mjs',
|
|
46
|
+
`--root=${selectedRoot}`,
|
|
47
|
+
'--path=demo.txt',
|
|
48
|
+
'--no-important-files',
|
|
49
|
+
'--no-changed-files',
|
|
50
|
+
'--no-diff',
|
|
51
|
+
'--no-ai-bridge',
|
|
52
|
+
'--max-files', '4',
|
|
53
|
+
'--max-total-bytes', '80000'
|
|
54
|
+
]);
|
|
55
|
+
const selectedBridgeFiles = await fs.readdir(path.join(selectedRoot, '.ai-bridge'));
|
|
56
|
+
if (selectedBridgeFiles.join(',') !== 'pro-context.md') {
|
|
57
|
+
throw new Error(`selected-only pro context created unexpected bridge files: ${selectedBridgeFiles.join(', ')}`);
|
|
58
|
+
}
|
|
59
|
+
const exactProContext = await fs.readFile(path.join(selectedRoot, '.ai-bridge', 'pro-context.md'), 'utf8');
|
|
60
|
+
if (!exactProContext.includes('Auto-include important root files: no') || !exactProContext.includes('Auto-include changed files: no')) {
|
|
61
|
+
throw new Error('selected-only pro context did not record disabled auto-inclusion settings');
|
|
62
|
+
}
|
|
63
|
+
if (!exactProContext.includes('### demo.txt') || exactProContext.includes('### README.md')) {
|
|
64
|
+
throw new Error('selected-only pro context did not include exactly the requested file');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
run([codexflowCli, 'pro-bundle', `--root=${root}`, '--path=demo.txt', '--max-files=4', '--max-total-bytes=80000']);
|
|
68
|
+
|
|
69
|
+
const planFile = path.join(root, 'plan.md');
|
|
70
|
+
await fs.writeFile(planFile, 'Inspect demo.txt and keep changes narrow.\n', 'utf8');
|
|
71
|
+
run(['scripts/pro-apply.mjs', `--root=${root}`, `--file=${planFile}`, '--title', 'Smoke Pro Plan']);
|
|
72
|
+
run([codexflowCli, 'pro-apply', `--root=${root}`, '--file=plan.md', '--title', 'Relative Pro Plan'], { cwd: root });
|
|
73
|
+
const currentPlan = await fs.readFile(path.join(root, '.ai-bridge', 'current-plan.md'), 'utf8');
|
|
74
|
+
if (!currentPlan.includes('Relative Pro Plan') || !currentPlan.includes('Inspect demo.txt')) {
|
|
75
|
+
throw new Error('pro apply did not write expected current-plan content');
|
|
76
|
+
}
|
|
77
|
+
const executionLog = await fs.readFile(path.join(root, '.ai-bridge', 'execution-log.jsonl'), 'utf8');
|
|
78
|
+
if (!executionLog.includes('"event":"pro_apply"')) {
|
|
79
|
+
throw new Error('pro apply did not append execution-log event');
|
|
80
|
+
}
|
|
81
|
+
const proApplyEvents = executionLog.trim().split('\n').map((line) => JSON.parse(line));
|
|
82
|
+
if (!proApplyEvents.some((event) => event.source_file === planFile)) {
|
|
83
|
+
throw new Error(`pro apply logged the wrong source file\n${executionLog}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const missingRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'codexflow-pro-missing-'));
|
|
87
|
+
runFail(['scripts/pro-apply.mjs', `--root=${missingRoot}`, '--file=missing-plan.md'], { env: { ...process.env, CODEXFLOW_CALLER_CWD: missingRoot } });
|
|
88
|
+
try {
|
|
89
|
+
await fs.stat(path.join(missingRoot, '.ai-bridge'));
|
|
90
|
+
throw new Error('failed pro-apply created .ai-bridge before validating input');
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log('✓ pro CLI smoke test passed');
|