@etherond/topo-cli 0.10.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/README.md +132 -0
- package/bin/topo.js +2 -0
- package/dist/commands/sync.d.ts +3 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +84 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +21 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +71 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/files.d.ts +14 -0
- package/dist/lib/files.d.ts.map +1 -0
- package/dist/lib/files.js +140 -0
- package/dist/lib/files.js.map +1 -0
- package/dist/lib/types.d.ts +26 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# @topo/cli
|
|
2
|
+
|
|
3
|
+
CLI tool for syncing product context from Topo to your local project.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
No installation required - run directly with npx:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @topo/cli sync
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install globally:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @topo/cli
|
|
17
|
+
topo sync
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
1. Create an API token in Topo: **Project Settings → API Tokens**
|
|
23
|
+
2. Add the token to your project's `.env` file:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
TOPO_API_TOKEN=topo_xxx
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or set it in your shell environment:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
export TOPO_API_TOKEN=topo_xxx
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Sync product context to default directory (.topo)
|
|
39
|
+
npx @topo/cli sync
|
|
40
|
+
|
|
41
|
+
# Specify output directory
|
|
42
|
+
npx @topo/cli sync --output ./my-docs
|
|
43
|
+
|
|
44
|
+
# Use consolidated format (fewer, larger files)
|
|
45
|
+
npx @topo/cli sync --format consolidated
|
|
46
|
+
|
|
47
|
+
# Pass token directly (alternative to env var)
|
|
48
|
+
npx @topo/cli sync --token topo_xxx
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Options
|
|
52
|
+
|
|
53
|
+
| Option | Description | Default |
|
|
54
|
+
|--------|-------------|---------|
|
|
55
|
+
| `-o, --output <dir>` | Output directory | `.topo` |
|
|
56
|
+
| `-t, --token <token>` | API token | `TOPO_API_TOKEN` env var |
|
|
57
|
+
| `-f, --format <format>` | `structured` or `consolidated` | `structured` |
|
|
58
|
+
| `--base-url <url>` | API base URL (for development) | `https://topo.prosaic.fr` |
|
|
59
|
+
|
|
60
|
+
## Output
|
|
61
|
+
|
|
62
|
+
The CLI creates markdown files in a hidden `.topo` directory (signaling these are managed files, not for manual editing):
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
.topo/
|
|
66
|
+
├── foundations.md
|
|
67
|
+
├── ux.md
|
|
68
|
+
└── features/
|
|
69
|
+
└── {area}/{feature}.md
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Integration with Claude Code
|
|
73
|
+
|
|
74
|
+
When using the Topo MCP server, Claude will automatically suggest running this CLI to sync product context. Just run the command Claude provides.
|
|
75
|
+
|
|
76
|
+
## Local Development Installation
|
|
77
|
+
|
|
78
|
+
To use the CLI from a local clone of the Topo repository (without publishing to npm), you can link it to your project:
|
|
79
|
+
|
|
80
|
+
### Option 1: npm link (recommended)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# In the topo/packages/cli directory, build and create a global link
|
|
84
|
+
cd /path/to/topo/packages/cli
|
|
85
|
+
npm install
|
|
86
|
+
npm run build
|
|
87
|
+
npm link
|
|
88
|
+
|
|
89
|
+
# In your Next.js project, link to the local package
|
|
90
|
+
cd /path/to/your-nextjs-project
|
|
91
|
+
npm link @topo/cli
|
|
92
|
+
|
|
93
|
+
# Now you can run the CLI
|
|
94
|
+
npx topo sync
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
To unlink later:
|
|
98
|
+
```bash
|
|
99
|
+
cd /path/to/your-nextjs-project
|
|
100
|
+
npm unlink @topo/cli
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Option 2: Direct path execution
|
|
104
|
+
|
|
105
|
+
Run the CLI directly without linking:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# From your project directory
|
|
109
|
+
node /path/to/topo/packages/cli/dist/index.js sync
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Option 3: Add as a local dependency
|
|
113
|
+
|
|
114
|
+
In your Next.js project's `package.json`:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"devDependencies": {
|
|
119
|
+
"@topo/cli": "file:/path/to/topo/packages/cli"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Then run:
|
|
125
|
+
```bash
|
|
126
|
+
npm install
|
|
127
|
+
npx topo sync
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Requirements
|
|
131
|
+
|
|
132
|
+
- Node.js 18+
|
package/bin/topo.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAInD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuG9D"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ApiClient, AuthenticationError, AuthorizationError, NotFoundError, } from '../lib/api.js';
|
|
2
|
+
import { writeFiles, PermissionError } from '../lib/files.js';
|
|
3
|
+
const MAX_FILES_TO_DISPLAY = 7;
|
|
4
|
+
export async function sync(options) {
|
|
5
|
+
const token = options.token || process.env.TOPO_API_TOKEN;
|
|
6
|
+
if (!token) {
|
|
7
|
+
console.error('Error: TOPO_API_TOKEN environment variable is not set.\n');
|
|
8
|
+
console.error('Set it with: export TOPO_API_TOKEN=your_token_here\n');
|
|
9
|
+
console.error('You can create an API token in Topo: Project Settings → API Tokens');
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
console.log('Fetching product context from Topo...');
|
|
13
|
+
try {
|
|
14
|
+
const client = new ApiClient(options.baseUrl, token);
|
|
15
|
+
const exportData = await client.fetchProductExport();
|
|
16
|
+
console.log('Writing files...\n');
|
|
17
|
+
const result = await writeFiles(options.output, exportData.files, exportData.projectId);
|
|
18
|
+
if (result.errors.length > 0) {
|
|
19
|
+
console.error('Some files could not be written:');
|
|
20
|
+
for (const err of result.errors) {
|
|
21
|
+
console.error(` - ${err.path}: ${err.error}`);
|
|
22
|
+
}
|
|
23
|
+
console.error('');
|
|
24
|
+
}
|
|
25
|
+
if (result.written.length > 0) {
|
|
26
|
+
console.log(`✓ Synced ${result.written.length} files to ${options.output}`);
|
|
27
|
+
// Display file list
|
|
28
|
+
const displayFiles = result.written.slice(0, MAX_FILES_TO_DISPLAY);
|
|
29
|
+
for (const file of displayFiles) {
|
|
30
|
+
console.log(` - ${file}`);
|
|
31
|
+
}
|
|
32
|
+
if (result.written.length > MAX_FILES_TO_DISPLAY) {
|
|
33
|
+
const remaining = result.written.length - MAX_FILES_TO_DISPLAY;
|
|
34
|
+
console.log(` ... and ${remaining} more`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log('No files were written.');
|
|
39
|
+
}
|
|
40
|
+
if (result.removed.length > 0) {
|
|
41
|
+
console.log(`\n✓ Removed ${result.removed.length} stale files`);
|
|
42
|
+
const displayRemoved = result.removed.slice(0, MAX_FILES_TO_DISPLAY);
|
|
43
|
+
for (const file of displayRemoved) {
|
|
44
|
+
console.log(` - ${file}`);
|
|
45
|
+
}
|
|
46
|
+
if (result.removed.length > MAX_FILES_TO_DISPLAY) {
|
|
47
|
+
const remaining = result.removed.length - MAX_FILES_TO_DISPLAY;
|
|
48
|
+
console.log(` ... and ${remaining} more`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Exit with error code if some files failed
|
|
52
|
+
if (result.errors.length > 0) {
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
if (err instanceof AuthenticationError) {
|
|
58
|
+
console.error(`Error: ${err.message}`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (err instanceof AuthorizationError) {
|
|
62
|
+
console.error(`Error: ${err.message}`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
if (err instanceof NotFoundError) {
|
|
66
|
+
console.error(`Error: ${err.message}`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
if (err instanceof PermissionError) {
|
|
70
|
+
console.error(`Error: ${err.message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
// Network errors
|
|
74
|
+
if (err instanceof TypeError && err.message?.includes('fetch')) {
|
|
75
|
+
console.error('Error: Network error. Please check your internet connection and try again.');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
// Unknown errors
|
|
79
|
+
const error = err;
|
|
80
|
+
console.error(`Error: ${error.message || 'An unexpected error occurred'}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { sync } from "./commands/sync.js";
|
|
4
|
+
const DEFAULT_BASE_URL = "https://topo.prosaic.fr";
|
|
5
|
+
const DEFAULT_OUTPUT = ".topo";
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name("topo")
|
|
9
|
+
.description("Topo CLI - Sync product context to your local project")
|
|
10
|
+
.version("1.0.0");
|
|
11
|
+
program
|
|
12
|
+
.command("sync")
|
|
13
|
+
.description("Sync product context from Topo to local files")
|
|
14
|
+
.option("-o, --output <dir>", "Output directory", DEFAULT_OUTPUT)
|
|
15
|
+
.option("-t, --token <token>", "API token (or use TOPO_API_TOKEN env var)")
|
|
16
|
+
.option("--base-url <url>", "Topo API base URL (for development)", DEFAULT_BASE_URL)
|
|
17
|
+
.action(async (opts) => {
|
|
18
|
+
const options = {
|
|
19
|
+
output: opts.output,
|
|
20
|
+
token: opts.token,
|
|
21
|
+
baseUrl: opts.baseUrl,
|
|
22
|
+
};
|
|
23
|
+
await sync(options);
|
|
24
|
+
});
|
|
25
|
+
program.parse();
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAG1C,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AACnD,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,MAAM,CAAC;KACZ,WAAW,CAAC,uDAAuD,CAAC;KACpE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,+CAA+C,CAAC;KAC5D,MAAM,CAAC,oBAAoB,EAAE,kBAAkB,EAAE,cAAc,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,2CAA2C,CAAC;KAC1E,MAAM,CACL,kBAAkB,EAClB,qCAAqC,EACrC,gBAAgB,CACjB;KACA,MAAM,CAAC,KAAK,EAAE,IAAyD,EAAE,EAAE;IAC1E,MAAM,OAAO,GAAgB;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;IAEF,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;AACtB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ExportResponse } from './types.js';
|
|
2
|
+
export declare class ApiClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private token;
|
|
5
|
+
constructor(baseUrl: string, token: string);
|
|
6
|
+
fetchProductExport(): Promise<ExportResponse>;
|
|
7
|
+
}
|
|
8
|
+
export declare class AuthenticationError extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class AuthorizationError extends Error {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class NotFoundError extends Error {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
export declare class ApiRequestError extends Error {
|
|
18
|
+
status: number;
|
|
19
|
+
constructor(message: string, status: number);
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAY,MAAM,YAAY,CAAC;AAE3D,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAKpC,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC;CAiDpD;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,MAAM,EAAE,MAAM,CAAC;gBAEH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAK5C"}
|
package/dist/lib/api.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export class ApiClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
token;
|
|
4
|
+
constructor(baseUrl, token) {
|
|
5
|
+
this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
6
|
+
this.token = token;
|
|
7
|
+
}
|
|
8
|
+
async fetchProductExport() {
|
|
9
|
+
const url = new URL('/api/cli/export', this.baseUrl);
|
|
10
|
+
const response = await fetch(url.toString(), {
|
|
11
|
+
method: 'GET',
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${this.token}`,
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
const status = response.status;
|
|
19
|
+
if (status === 401) {
|
|
20
|
+
throw new AuthenticationError('Invalid API token. Please check your TOPO_API_TOKEN.\n\n' +
|
|
21
|
+
'You can create a new token in Topo: Project Settings → API Tokens');
|
|
22
|
+
}
|
|
23
|
+
if (status === 403) {
|
|
24
|
+
throw new AuthorizationError('Access denied. Your token may not have access to this project.');
|
|
25
|
+
}
|
|
26
|
+
if (status === 404) {
|
|
27
|
+
throw new NotFoundError('Project not found. Please check that your token is valid.');
|
|
28
|
+
}
|
|
29
|
+
// Try to get error message from response
|
|
30
|
+
let errorMessage = `Request failed with status ${status}`;
|
|
31
|
+
try {
|
|
32
|
+
const errorBody = (await response.json());
|
|
33
|
+
if (errorBody.message || errorBody.error) {
|
|
34
|
+
errorMessage = errorBody.message || errorBody.error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Ignore JSON parse errors
|
|
39
|
+
}
|
|
40
|
+
throw new ApiRequestError(errorMessage, status);
|
|
41
|
+
}
|
|
42
|
+
return (await response.json());
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class AuthenticationError extends Error {
|
|
46
|
+
constructor(message) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = 'AuthenticationError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export class AuthorizationError extends Error {
|
|
52
|
+
constructor(message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = 'AuthorizationError';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export class NotFoundError extends Error {
|
|
58
|
+
constructor(message) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = 'NotFoundError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export class ApiRequestError extends Error {
|
|
64
|
+
status;
|
|
65
|
+
constructor(message, status) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.name = 'ApiRequestError';
|
|
68
|
+
this.status = status;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/lib/api.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,SAAS;IACZ,OAAO,CAAS;IAChB,KAAK,CAAS;IAEtB,YAAY,OAAe,EAAE,KAAa;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,wBAAwB;QACnE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAErD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YAC3C,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;gBACrC,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;YAE/B,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;gBACnB,MAAM,IAAI,mBAAmB,CAC3B,0DAA0D;oBACxD,mEAAmE,CACtE,CAAC;YACJ,CAAC;YAED,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;gBACnB,MAAM,IAAI,kBAAkB,CAC1B,gEAAgE,CACjE,CAAC;YACJ,CAAC;YAED,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;gBACnB,MAAM,IAAI,aAAa,CACrB,2DAA2D,CAC5D,CAAC;YACJ,CAAC;YAED,yCAAyC;YACzC,IAAI,YAAY,GAAG,8BAA8B,MAAM,EAAE,CAAC;YAC1D,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAa,CAAC;gBACtD,IAAI,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;oBACzC,YAAY,GAAG,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC;gBACtD,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,2BAA2B;YAC7B,CAAC;YAED,MAAM,IAAI,eAAe,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmB,CAAC;IACnD,CAAC;CACF;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,MAAM,CAAS;IAEf,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ExportFile } from './types.js';
|
|
2
|
+
export interface WriteResult {
|
|
3
|
+
written: string[];
|
|
4
|
+
removed: string[];
|
|
5
|
+
errors: Array<{
|
|
6
|
+
path: string;
|
|
7
|
+
error: string;
|
|
8
|
+
}>;
|
|
9
|
+
}
|
|
10
|
+
export declare function writeFiles(outputDir: string, files: ExportFile[], projectId: string): Promise<WriteResult>;
|
|
11
|
+
export declare class PermissionError extends Error {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/lib/files.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAI3D,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,wBAAsB,UAAU,CAC9B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,EAAE,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAwGtB;AAqDD,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
const MANIFEST_FILENAME = '.topo-manifest.json';
|
|
4
|
+
export async function writeFiles(outputDir, files, projectId) {
|
|
5
|
+
const result = {
|
|
6
|
+
written: [],
|
|
7
|
+
removed: [],
|
|
8
|
+
errors: [],
|
|
9
|
+
};
|
|
10
|
+
// Ensure output directory exists
|
|
11
|
+
try {
|
|
12
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
const error = err;
|
|
16
|
+
if (error.code === 'EACCES') {
|
|
17
|
+
throw new PermissionError(`Permission denied: Cannot create output directory '${outputDir}'`);
|
|
18
|
+
}
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
// Read existing manifest and remove stale files
|
|
22
|
+
const manifestPath = path.join(outputDir, MANIFEST_FILENAME);
|
|
23
|
+
const existingManifest = await readManifest(manifestPath);
|
|
24
|
+
const newFilePaths = new Set(files.map((f) => f.path));
|
|
25
|
+
if (existingManifest) {
|
|
26
|
+
const staleFiles = existingManifest.files.filter((f) => !newFilePaths.has(f));
|
|
27
|
+
for (const staleFile of staleFiles) {
|
|
28
|
+
const fullPath = path.join(outputDir, staleFile);
|
|
29
|
+
try {
|
|
30
|
+
await fs.unlink(fullPath);
|
|
31
|
+
result.removed.push(staleFile);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const error = err;
|
|
35
|
+
// Ignore if file doesn't exist (already removed)
|
|
36
|
+
if (error.code !== 'ENOENT') {
|
|
37
|
+
result.errors.push({
|
|
38
|
+
path: staleFile,
|
|
39
|
+
error: `Failed to remove: ${error.message || 'Unknown error'}`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Clean up empty directories left after removing files
|
|
45
|
+
await cleanupEmptyDirectories(outputDir, staleFiles);
|
|
46
|
+
}
|
|
47
|
+
// Collect all unique directories that need to be created
|
|
48
|
+
const directories = new Set();
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
const fullPath = path.join(outputDir, file.path);
|
|
51
|
+
const dir = path.dirname(fullPath);
|
|
52
|
+
if (dir !== outputDir) {
|
|
53
|
+
directories.add(dir);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Create all directories
|
|
57
|
+
for (const dir of directories) {
|
|
58
|
+
try {
|
|
59
|
+
await fs.mkdir(dir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const error = err;
|
|
63
|
+
if (error.code === 'EACCES') {
|
|
64
|
+
throw new PermissionError(`Permission denied: Cannot create directory '${dir}'`);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Write all files
|
|
70
|
+
await Promise.all(files.map(async (file) => {
|
|
71
|
+
const fullPath = path.join(outputDir, file.path);
|
|
72
|
+
try {
|
|
73
|
+
await fs.writeFile(fullPath, file.content, 'utf-8');
|
|
74
|
+
result.written.push(file.path);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const error = err;
|
|
78
|
+
result.errors.push({
|
|
79
|
+
path: file.path,
|
|
80
|
+
error: error.code === 'EACCES'
|
|
81
|
+
? 'Permission denied'
|
|
82
|
+
: error.message || 'Unknown error',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
// Write manifest with successfully written files
|
|
87
|
+
const manifest = {
|
|
88
|
+
syncedAt: new Date().toISOString(),
|
|
89
|
+
projectId,
|
|
90
|
+
files: result.written,
|
|
91
|
+
};
|
|
92
|
+
await writeManifest(manifestPath, manifest);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
async function readManifest(manifestPath) {
|
|
96
|
+
try {
|
|
97
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
98
|
+
return JSON.parse(content);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
const error = err;
|
|
102
|
+
// Return null if manifest doesn't exist or is invalid
|
|
103
|
+
if (error.code === 'ENOENT') {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
// Log warning for corrupted manifest but continue
|
|
107
|
+
console.warn(`Warning: Could not read manifest file: ${error.message}`);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function writeManifest(manifestPath, manifest) {
|
|
112
|
+
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
113
|
+
}
|
|
114
|
+
async function cleanupEmptyDirectories(outputDir, removedFiles) {
|
|
115
|
+
// Get unique directories from removed files, sorted by depth (deepest first)
|
|
116
|
+
const directories = [
|
|
117
|
+
...new Set(removedFiles
|
|
118
|
+
.map((f) => path.dirname(f))
|
|
119
|
+
.filter((d) => d !== '.')),
|
|
120
|
+
].sort((a, b) => b.split(path.sep).length - a.split(path.sep).length);
|
|
121
|
+
for (const dir of directories) {
|
|
122
|
+
const fullPath = path.join(outputDir, dir);
|
|
123
|
+
try {
|
|
124
|
+
const entries = await fs.readdir(fullPath);
|
|
125
|
+
if (entries.length === 0) {
|
|
126
|
+
await fs.rmdir(fullPath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Ignore errors - directory might not exist or not be empty
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export class PermissionError extends Error {
|
|
135
|
+
constructor(message) {
|
|
136
|
+
super(message);
|
|
137
|
+
this.name = 'PermissionError';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/lib/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SyncOptions {
|
|
2
|
+
output: string;
|
|
3
|
+
token?: string;
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ExportFile {
|
|
7
|
+
path: string;
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ExportResponse {
|
|
11
|
+
exportedAt: string;
|
|
12
|
+
projectId: string;
|
|
13
|
+
projectName: string;
|
|
14
|
+
version: string;
|
|
15
|
+
files: ExportFile[];
|
|
16
|
+
}
|
|
17
|
+
export interface ApiError {
|
|
18
|
+
error: string;
|
|
19
|
+
message?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface SyncManifest {
|
|
22
|
+
syncedAt: string;
|
|
23
|
+
projectId: string;
|
|
24
|
+
files: string[];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@etherond/topo-cli",
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Topo CLI - Sync product context to your local project",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"topo": "./bin/topo.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"topo",
|
|
20
|
+
"cli",
|
|
21
|
+
"product",
|
|
22
|
+
"context",
|
|
23
|
+
"ai",
|
|
24
|
+
"claude"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"dotenv": "^16.4.5"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.10.0",
|
|
34
|
+
"typescript": "^5.3.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|