@mrpatronz/nexusflow 0.1.6 → 0.1.7
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/README.md +2 -0
- package/dist/core/packer.d.ts.map +1 -1
- package/dist/core/packer.js +32 -35
- package/dist/core/packer.js.map +1 -1
- package/package.json +1 -1
- package/src/core/packer.ts +41 -40
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ NexusFlow combines multiple Git repositories into a single feature workspace and
|
|
|
18
18
|
|
|
19
19
|
- **Multi-repo workspaces** — group any set of local Git repos under one feature branch using worktrees
|
|
20
20
|
- **AI context generation** — automatically writes `CLAUDE.md`, `AGENTS.md`, `.github/copilot-instructions.md`, and `.cursor/rules/nexusflow.mdc`
|
|
21
|
+
- **Codebase context packing** — compress an entire multi-repo workspace into a single token-efficient XML file for web-based LLMs using Repomix
|
|
21
22
|
- **Smart codebase analysis** — detects tech stacks, ports, API endpoints, dependencies, and existing AI configs across all projects
|
|
22
23
|
- **Session history & resumption** — browse past conversation transcripts from Antigravity, Claude Code, OpenAI Codex, and GitHub Copilot, then resume where you left off
|
|
23
24
|
- **Service orchestration** — start, stop, and tail logs for all services in a workspace with a single command
|
|
@@ -108,6 +109,7 @@ Open this folder in your editor → your AI assistant picks up the context → i
|
|
|
108
109
|
| `nexusflow list` | List all existing workspaces (alias: `ls`) |
|
|
109
110
|
| `nexusflow open` | Re-open a workspace in your editor |
|
|
110
111
|
| `nexusflow init` | Configure NexusFlow settings |
|
|
112
|
+
| `nexusflow pack` | Pack the workspace codebase into a single token-efficient XML file |
|
|
111
113
|
| `nexusflow start` | Start all services in a workspace (auto-detected) |
|
|
112
114
|
| `nexusflow stop` | Stop all running services |
|
|
113
115
|
| `nexusflow status` | Show running/stopped status and PIDs |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packer.d.ts","sourceRoot":"","sources":["../../src/core/packer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"packer.d.ts","sourceRoot":"","sources":["../../src/core/packer.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACnC,OAAO,CAAC,UAAU,CAAC,CAsFrB"}
|
package/dist/core/packer.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
2
4
|
import * as path from 'node:path';
|
|
3
|
-
import {
|
|
5
|
+
import { runCli } from 'repomix';
|
|
4
6
|
import { loadFeatureConfig } from './workspace.js';
|
|
5
7
|
/**
|
|
6
8
|
* Packs all repositories in a workspace into a single, compressed XML file using Repomix.
|
|
@@ -11,7 +13,13 @@ export async function packWorkspace(workspacePath, options = {}) {
|
|
|
11
13
|
throw new Error(`Workspace not found at ${workspacePath}`);
|
|
12
14
|
}
|
|
13
15
|
const compress = options.compress !== false; // default true
|
|
14
|
-
const
|
|
16
|
+
const outputPath = path.join(workspacePath, 'nexusflow-context.xml');
|
|
17
|
+
// Use a write stream to avoid buffering huge workspaces in memory
|
|
18
|
+
const outStream = createWriteStream(outputPath, { encoding: 'utf-8' });
|
|
19
|
+
outStream.write('<?xml version="1.0" encoding="UTF-8"?>\n');
|
|
20
|
+
outStream.write(`<workspace id="${feature.id}">\n`);
|
|
21
|
+
outStream.write(` <description><![CDATA[${feature.description}]]></description>\n`);
|
|
22
|
+
outStream.write(' <repositories>\n');
|
|
15
23
|
let totalFilesCount = 0;
|
|
16
24
|
let totalCharsCount = 0;
|
|
17
25
|
for (const repoPath of feature.repos) {
|
|
@@ -24,24 +32,22 @@ export async function packWorkspace(workspacePath, options = {}) {
|
|
|
24
32
|
continue;
|
|
25
33
|
}
|
|
26
34
|
const tempXmlPath = path.join(workspacePath, `temp-repomix-${repoName}.xml`);
|
|
27
|
-
// Build repomix arguments
|
|
28
|
-
const args = ['repomix', '--style', 'xml', '--output', tempXmlPath];
|
|
29
|
-
if (compress) {
|
|
30
|
-
args.push('--compress');
|
|
31
|
-
}
|
|
32
35
|
try {
|
|
33
|
-
// Run repomix inside the worktree directory
|
|
34
|
-
await
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
totalFilesCount += fileMatches.length;
|
|
40
|
-
totalCharsCount += xmlContent.length;
|
|
41
|
-
reposXmlData.push({
|
|
42
|
-
repoName,
|
|
43
|
-
xmlContent,
|
|
36
|
+
// Run repomix programmatically inside the worktree directory
|
|
37
|
+
const result = await runCli(['.'], worktreePath, {
|
|
38
|
+
style: 'xml',
|
|
39
|
+
output: tempXmlPath,
|
|
40
|
+
compress,
|
|
41
|
+
quiet: true,
|
|
44
42
|
});
|
|
43
|
+
if (result && result.packResult) {
|
|
44
|
+
totalFilesCount += result.packResult.totalFiles;
|
|
45
|
+
totalCharsCount += result.packResult.totalCharacters;
|
|
46
|
+
}
|
|
47
|
+
outStream.write(` <repository name="${repoName}">\n`);
|
|
48
|
+
// Stream output XML directly to avoid memory limits
|
|
49
|
+
await pipeline(createReadStream(tempXmlPath, { encoding: 'utf-8' }), outStream, { end: false });
|
|
50
|
+
outStream.write('\n </repository>\n');
|
|
45
51
|
}
|
|
46
52
|
catch (error) {
|
|
47
53
|
console.error(`Error packing repository ${repoName}:`, error.message);
|
|
@@ -56,23 +62,14 @@ export async function packWorkspace(workspacePath, options = {}) {
|
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
xmlLines.push(repo.xmlContent);
|
|
68
|
-
xmlLines.push(' </repository>');
|
|
69
|
-
}
|
|
70
|
-
xmlLines.push(' </repositories>');
|
|
71
|
-
xmlLines.push('</workspace>');
|
|
72
|
-
xmlLines.push('');
|
|
73
|
-
const outputXmlContent = xmlLines.join('\n');
|
|
74
|
-
const outputPath = path.join(workspacePath, 'nexusflow-context.xml');
|
|
75
|
-
await fs.writeFile(outputPath, outputXmlContent, 'utf-8');
|
|
65
|
+
outStream.write(' </repositories>\n');
|
|
66
|
+
outStream.write('</workspace>\n');
|
|
67
|
+
outStream.end();
|
|
68
|
+
// Wait for stream to finish writing
|
|
69
|
+
await new Promise((resolve, reject) => {
|
|
70
|
+
outStream.on('finish', resolve);
|
|
71
|
+
outStream.on('error', reject);
|
|
72
|
+
});
|
|
76
73
|
const stats = await fs.stat(outputPath);
|
|
77
74
|
return {
|
|
78
75
|
outputPath,
|
package/dist/core/packer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packer.js","sourceRoot":"","sources":["../../src/core/packer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"packer.js","sourceRoot":"","sources":["../../src/core/packer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/core/packer.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
2
4
|
import * as path from 'node:path';
|
|
3
|
-
import {
|
|
5
|
+
import { runCli } from 'repomix';
|
|
4
6
|
import { loadFeatureConfig } from './workspace.js';
|
|
5
7
|
|
|
6
8
|
export interface PackResult {
|
|
@@ -23,8 +25,15 @@ export async function packWorkspace(
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
const compress = options.compress !== false; // default true
|
|
28
|
+
const outputPath = path.join(workspacePath, 'nexusflow-context.xml');
|
|
29
|
+
|
|
30
|
+
// Use a write stream to avoid buffering huge workspaces in memory
|
|
31
|
+
const outStream = createWriteStream(outputPath, { encoding: 'utf-8' });
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
outStream.write('<?xml version="1.0" encoding="UTF-8"?>\n');
|
|
34
|
+
outStream.write(`<workspace id="${feature.id}">\n`);
|
|
35
|
+
outStream.write(` <description><![CDATA[${feature.description}]]></description>\n`);
|
|
36
|
+
outStream.write(' <repositories>\n');
|
|
28
37
|
|
|
29
38
|
let totalFilesCount = 0;
|
|
30
39
|
let totalCharsCount = 0;
|
|
@@ -41,28 +50,30 @@ export async function packWorkspace(
|
|
|
41
50
|
|
|
42
51
|
const tempXmlPath = path.join(workspacePath, `temp-repomix-${repoName}.xml`);
|
|
43
52
|
|
|
44
|
-
// Build repomix arguments
|
|
45
|
-
const args = ['repomix', '--style', 'xml', '--output', tempXmlPath];
|
|
46
|
-
if (compress) {
|
|
47
|
-
args.push('--compress');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
53
|
try {
|
|
51
|
-
// Run repomix inside the worktree directory
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Run repomix programmatically inside the worktree directory
|
|
55
|
+
const result = await runCli(['.'], worktreePath, {
|
|
56
|
+
style: 'xml',
|
|
57
|
+
output: tempXmlPath,
|
|
58
|
+
compress,
|
|
59
|
+
quiet: true,
|
|
60
|
+
});
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
if (result && result.packResult) {
|
|
63
|
+
totalFilesCount += result.packResult.totalFiles;
|
|
64
|
+
totalCharsCount += result.packResult.totalCharacters;
|
|
65
|
+
}
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
outStream.write(` <repository name="${repoName}">\n`);
|
|
68
|
+
|
|
69
|
+
// Stream output XML directly to avoid memory limits
|
|
70
|
+
await pipeline(
|
|
71
|
+
createReadStream(tempXmlPath, { encoding: 'utf-8' }),
|
|
72
|
+
outStream,
|
|
73
|
+
{ end: false }
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
outStream.write('\n </repository>\n');
|
|
66
77
|
} catch (error: any) {
|
|
67
78
|
console.error(`Error packing repository ${repoName}:`, error.message);
|
|
68
79
|
} finally {
|
|
@@ -75,26 +86,15 @@ export async function packWorkspace(
|
|
|
75
86
|
}
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
xmlLines.push(`<workspace id="${feature.id}">`);
|
|
82
|
-
xmlLines.push(` <description><![CDATA[${feature.description}]]></description>`);
|
|
83
|
-
xmlLines.push(' <repositories>');
|
|
84
|
-
|
|
85
|
-
for (const repo of reposXmlData) {
|
|
86
|
-
xmlLines.push(` <repository name="${repo.repoName}">`);
|
|
87
|
-
xmlLines.push(repo.xmlContent);
|
|
88
|
-
xmlLines.push(' </repository>');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
xmlLines.push(' </repositories>');
|
|
92
|
-
xmlLines.push('</workspace>');
|
|
93
|
-
xmlLines.push('');
|
|
89
|
+
outStream.write(' </repositories>\n');
|
|
90
|
+
outStream.write('</workspace>\n');
|
|
91
|
+
outStream.end();
|
|
94
92
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
// Wait for stream to finish writing
|
|
94
|
+
await new Promise<void>((resolve, reject) => {
|
|
95
|
+
outStream.on('finish', resolve);
|
|
96
|
+
outStream.on('error', reject);
|
|
97
|
+
});
|
|
98
98
|
|
|
99
99
|
const stats = await fs.stat(outputPath);
|
|
100
100
|
|
|
@@ -105,3 +105,4 @@ export async function packWorkspace(
|
|
|
105
105
|
fileSize: stats.size,
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
|
+
|