@geolonia/yuuhitsu 0.1.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/LICENSE +21 -0
- package/README.md +164 -0
- package/dist/cli/commands/translate.d.ts +3 -0
- package/dist/cli/commands/translate.d.ts.map +1 -0
- package/dist/cli/commands/translate.js +56 -0
- package/dist/cli/commands/translate.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +33 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +52 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +17 -0
- package/dist/logger.js.map +1 -0
- package/dist/provider/claude.d.ts +9 -0
- package/dist/provider/claude.d.ts.map +1 -0
- package/dist/provider/claude.js +69 -0
- package/dist/provider/claude.js.map +1 -0
- package/dist/provider/gemini.d.ts +9 -0
- package/dist/provider/gemini.d.ts.map +1 -0
- package/dist/provider/gemini.js +77 -0
- package/dist/provider/gemini.js.map +1 -0
- package/dist/provider/index.d.ts +6 -0
- package/dist/provider/index.d.ts.map +1 -0
- package/dist/provider/index.js +16 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/provider/interface.d.ts +30 -0
- package/dist/provider/interface.d.ts.map +1 -0
- package/dist/provider/interface.js +2 -0
- package/dist/provider/interface.js.map +1 -0
- package/dist/provider/ollama.d.ts +9 -0
- package/dist/provider/ollama.d.ts.map +1 -0
- package/dist/provider/ollama.js +60 -0
- package/dist/provider/ollama.js.map +1 -0
- package/dist/tasks/stream.d.ts +7 -0
- package/dist/tasks/stream.d.ts.map +1 -0
- package/dist/tasks/stream.js +13 -0
- package/dist/tasks/stream.js.map +1 -0
- package/dist/tasks/translate.d.ts +19 -0
- package/dist/tasks/translate.d.ts.map +1 -0
- package/dist/tasks/translate.js +101 -0
- package/dist/tasks/translate.js.map +1 -0
- package/package.json +40 -0
- package/src/templates/fix-links.md +7 -0
- package/src/templates/generate-docs.md +14 -0
- package/src/templates/generate-tests.md +14 -0
- package/src/templates/research.md +16 -0
- package/src/templates/sync-docs.md +11 -0
- package/src/templates/translate.md +18 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Geolonia Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# yuuhitsu (右筆)
|
|
2
|
+
|
|
3
|
+
AI-powered document operations CLI
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
**yuuhitsu** (右筆, meaning "secretary" or "scribe" in feudal Japan) is a command-line tool that automates document operations using AI. The name refers to scribes who served feudal lords, writing and managing official documents on their behalf — this tool serves engineers in the same way, handling translation, documentation generation, and document synchronization.
|
|
8
|
+
|
|
9
|
+
### Key Capabilities
|
|
10
|
+
|
|
11
|
+
- **Markdown Translation**: Translate documents while preserving structure, code blocks, and formatting
|
|
12
|
+
- **Multi-Provider Support**: Switch between Claude (Anthropic), Gemini (Google), and Ollama (local) with a single config line change
|
|
13
|
+
- **Streaming Output**: See translation progress in real-time
|
|
14
|
+
- **Dry-Run Mode**: Preview operations without making API calls
|
|
15
|
+
- **Prompt Templates**: Customize AI behavior with configurable templates
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
### Translation (Available Now)
|
|
20
|
+
|
|
21
|
+
- Translate Markdown documents between languages
|
|
22
|
+
- Preserve document structure (headings, links, code blocks, tables)
|
|
23
|
+
- Support for large files with automatic chunking
|
|
24
|
+
- Real-time streaming output
|
|
25
|
+
- Retry logic with exponential backoff for API failures
|
|
26
|
+
|
|
27
|
+
### Coming Soon
|
|
28
|
+
|
|
29
|
+
- **generate-docs**: Generate documentation from source code or specifications
|
|
30
|
+
- **sync-docs**: Convert external Markdown to VitePress-compatible format
|
|
31
|
+
- **research**: Perform web research and generate structured reports
|
|
32
|
+
- **fix-links**: Detect and fix dead links in documentation
|
|
33
|
+
- **generate-tests**: Generate test scaffolding from source code
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g yuuhitsu
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Basic Usage
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Translate a document to Japanese
|
|
47
|
+
yuuhitsu translate --input README.md --lang ja
|
|
48
|
+
|
|
49
|
+
# Translate to English
|
|
50
|
+
yuuhitsu translate --input docs.md --lang en --output docs.en.md
|
|
51
|
+
|
|
52
|
+
# Preview without API calls
|
|
53
|
+
yuuhitsu translate --input README.md --lang ja --dry-run
|
|
54
|
+
|
|
55
|
+
# Use a specific config file
|
|
56
|
+
yuuhitsu translate --input README.md --lang ja --config ./custom.config.yaml
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
Create a `yuuhitsu.config.yaml` file in your project root:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
# AI Provider Selection
|
|
65
|
+
provider: claude # Options: claude, gemini, ollama
|
|
66
|
+
model: claude-sonnet-4-5-20250929
|
|
67
|
+
|
|
68
|
+
# Optional Settings
|
|
69
|
+
outputDir: ./translated
|
|
70
|
+
templates: ./templates
|
|
71
|
+
log:
|
|
72
|
+
enabled: true
|
|
73
|
+
path: ./yuuhitsu.log
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Environment Variables
|
|
77
|
+
|
|
78
|
+
Create a `.env` file or set environment variables for API authentication:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# For Claude (Anthropic)
|
|
82
|
+
ANTHROPIC_API_KEY=your_api_key_here
|
|
83
|
+
|
|
84
|
+
# For Gemini (Google)
|
|
85
|
+
GOOGLE_API_KEY=your_api_key_here
|
|
86
|
+
|
|
87
|
+
# Ollama requires no API key (runs locally)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Supported Providers
|
|
91
|
+
|
|
92
|
+
| Provider | SDK | Environment Variable | Use Case |
|
|
93
|
+
|----------|-----|---------------------|----------|
|
|
94
|
+
| Claude | `@anthropic-ai/sdk` | `ANTHROPIC_API_KEY` | High-quality translation, research tasks |
|
|
95
|
+
| Gemini | `@google/genai` | `GOOGLE_API_KEY` | Fast processing, cost-effective |
|
|
96
|
+
| Ollama | `openai` (compatible) | *(none)* | Local execution, privacy, offline use |
|
|
97
|
+
|
|
98
|
+
## Commands
|
|
99
|
+
|
|
100
|
+
### `translate`
|
|
101
|
+
|
|
102
|
+
Translate Markdown documents between languages.
|
|
103
|
+
|
|
104
|
+
**Options:**
|
|
105
|
+
|
|
106
|
+
- `--input <path>` (required): Input Markdown file path
|
|
107
|
+
- `--output <path>`: Output file path (defaults to input file with `.{lang}.md` suffix)
|
|
108
|
+
- `--lang <code>` (required): Target language code (e.g., `en`, `ja`, `zh`, `es`)
|
|
109
|
+
- `--provider <name>`: Override config provider (claude, gemini, ollama)
|
|
110
|
+
- `--model <name>`: Override config model
|
|
111
|
+
- `--dry-run`: Show what would be done without making API calls
|
|
112
|
+
- `--stream`: Enable streaming output (default: true)
|
|
113
|
+
- `--config <path>`: Config file path (default: `./yuuhitsu.config.yaml`)
|
|
114
|
+
- `--verbose`: Enable verbose output
|
|
115
|
+
|
|
116
|
+
**Example:**
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
yuuhitsu translate \
|
|
120
|
+
--input ./docs/guide.md \
|
|
121
|
+
--output ./docs/guide.ja.md \
|
|
122
|
+
--lang ja \
|
|
123
|
+
--provider claude \
|
|
124
|
+
--model claude-sonnet-4-5-20250929
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Clone the repository
|
|
131
|
+
git clone https://github.com/geolonia/yuuhitsu.git
|
|
132
|
+
cd yuuhitsu
|
|
133
|
+
|
|
134
|
+
# Install dependencies
|
|
135
|
+
npm install
|
|
136
|
+
|
|
137
|
+
# Run tests
|
|
138
|
+
npm test
|
|
139
|
+
|
|
140
|
+
# Build the project
|
|
141
|
+
npm run build
|
|
142
|
+
|
|
143
|
+
# Run locally (development)
|
|
144
|
+
npm run dev -- translate --input test.md --lang ja
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Running Tests
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Run all tests
|
|
151
|
+
npm test
|
|
152
|
+
|
|
153
|
+
# Watch mode
|
|
154
|
+
npm run test:watch
|
|
155
|
+
|
|
156
|
+
# Type checking
|
|
157
|
+
npm run lint
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
MIT — See [LICENSE](./LICENSE)
|
|
163
|
+
|
|
164
|
+
Copyright (c) 2026 Geolonia Inc.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/translate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,eAAO,MAAM,gBAAgB,SA8DzB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { loadConfig } from "../../config.js";
|
|
5
|
+
import { createProvider } from "../../provider/index.js";
|
|
6
|
+
import { translateFile } from "../../tasks/translate.js";
|
|
7
|
+
import { formatError, AppError } from "../../errors.js";
|
|
8
|
+
export const translateCommand = new Command("translate")
|
|
9
|
+
.description("Translate a Markdown document to another language")
|
|
10
|
+
.requiredOption("--input <file>", "Input Markdown file")
|
|
11
|
+
.requiredOption("--lang <code>", "Target language code (e.g., ja, en, zh, ko)")
|
|
12
|
+
.option("--output <file>", "Output file path (default: <input>.<lang>.md)")
|
|
13
|
+
.action(async (opts, cmd) => {
|
|
14
|
+
const globalOpts = cmd.parent?.opts() ?? {};
|
|
15
|
+
const configPath = globalOpts.config ?? "./yuuhitsu.config.yaml";
|
|
16
|
+
const dryRun = globalOpts.dryRun ?? false;
|
|
17
|
+
const verbose = globalOpts.verbose ?? false;
|
|
18
|
+
try {
|
|
19
|
+
// Validate input file exists
|
|
20
|
+
if (!existsSync(opts.input)) {
|
|
21
|
+
throw new AppError(`Input file not found: ${opts.input}`, "Check the file path and try again.");
|
|
22
|
+
}
|
|
23
|
+
// Load config
|
|
24
|
+
const config = await loadConfig(configPath);
|
|
25
|
+
if (verbose) {
|
|
26
|
+
process.stderr.write(`${chalk.gray(`Provider: ${config.provider}, Model: ${config.model}`)}\n`);
|
|
27
|
+
}
|
|
28
|
+
// Dry-run mode
|
|
29
|
+
if (dryRun) {
|
|
30
|
+
const outputPath = opts.output || `${opts.input.replace(/\.md$/, "")}.${opts.lang}.md`;
|
|
31
|
+
process.stdout.write(`${chalk.cyan("[dry-run]")} Would translate:\n` +
|
|
32
|
+
` Input: ${opts.input}\n` +
|
|
33
|
+
` Output: ${outputPath}\n` +
|
|
34
|
+
` Language: ${opts.lang}\n` +
|
|
35
|
+
` Provider: ${config.provider}\n` +
|
|
36
|
+
` Model: ${config.model}\n`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Create provider
|
|
40
|
+
const provider = createProvider(config.provider, config.model);
|
|
41
|
+
// Execute translation
|
|
42
|
+
const result = await translateFile({
|
|
43
|
+
provider,
|
|
44
|
+
inputPath: opts.input,
|
|
45
|
+
outputPath: opts.output,
|
|
46
|
+
targetLang: opts.lang,
|
|
47
|
+
});
|
|
48
|
+
process.stdout.write(`${chalk.green("✓")} Translated to ${result.outputPath}\n` +
|
|
49
|
+
` Tokens: ${result.usage.totalTokens} (${result.chunks} chunk${result.chunks > 1 ? "s" : ""})\n`);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
process.stderr.write(formatError(err) + "\n");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=translate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../../src/cli/commands/translate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoBpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAoB9B,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { readFileSync } from "fs";
|
|
4
|
+
import { join, dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { formatError } from "../errors.js";
|
|
7
|
+
import { translateCommand } from "./commands/translate.js";
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
function getVersion() {
|
|
10
|
+
try {
|
|
11
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8"));
|
|
12
|
+
return pkg.version;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return "0.0.0";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const program = new Command();
|
|
19
|
+
program
|
|
20
|
+
.name("yuuhitsu")
|
|
21
|
+
.description("右筆 (Yuuhitsu) - AI-powered document operations CLI")
|
|
22
|
+
.version(getVersion())
|
|
23
|
+
.option("--config <path>", "Config file path", "./yuuhitsu.config.yaml")
|
|
24
|
+
.option("--dry-run", "Show what would be done without making API calls")
|
|
25
|
+
.option("--verbose", "Enable verbose output");
|
|
26
|
+
// Register commands
|
|
27
|
+
program.addCommand(translateCommand);
|
|
28
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
29
|
+
process.stderr.write(formatError(err) + "\n");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
});
|
|
32
|
+
export { program };
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAC7D,CAAC;QACF,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CACV,oDAAoD,CACrD;KACA,OAAO,CAAC,UAAU,EAAE,CAAC;KACrB,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,EAAE,wBAAwB,CAAC;KACvE,MAAM,CAAC,WAAW,EAAE,kDAAkD,CAAC;KACvE,MAAM,CAAC,WAAW,EAAE,uBAAuB,CAAC,CAAC;AAEhD,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAErC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const SUPPORTED_PROVIDERS: readonly ["claude", "gemini", "ollama"];
|
|
2
|
+
export type ProviderName = (typeof SUPPORTED_PROVIDERS)[number];
|
|
3
|
+
export interface AppConfig {
|
|
4
|
+
provider: ProviderName;
|
|
5
|
+
model: string;
|
|
6
|
+
templates?: string;
|
|
7
|
+
outputDir?: string;
|
|
8
|
+
log?: {
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
path?: string;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare function loadConfig(configPath: string, envDir?: string): Promise<AppConfig>;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,mBAAmB,yCAA0C,CAAC;AACpE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE;QACJ,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,CAAC,CAmDpB"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { parse } from "yaml";
|
|
3
|
+
import { config as dotenvConfig } from "dotenv";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
const SUPPORTED_PROVIDERS = ["claude", "gemini", "ollama"];
|
|
6
|
+
export async function loadConfig(configPath, envDir) {
|
|
7
|
+
// Load .env - from envDir if provided, otherwise from cwd
|
|
8
|
+
if (envDir) {
|
|
9
|
+
dotenvConfig({ path: join(envDir, ".env") });
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
dotenvConfig(); // Loads from cwd by default
|
|
13
|
+
}
|
|
14
|
+
let content;
|
|
15
|
+
try {
|
|
16
|
+
content = readFileSync(configPath, "utf-8");
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
20
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
21
|
+
}
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
const raw = parse(content);
|
|
25
|
+
if (!raw || typeof raw !== "object") {
|
|
26
|
+
throw new Error("Config file is empty or invalid YAML");
|
|
27
|
+
}
|
|
28
|
+
const provider = raw.provider;
|
|
29
|
+
if (!SUPPORTED_PROVIDERS.includes(provider)) {
|
|
30
|
+
throw new Error(`Unsupported provider: "${provider}". Supported providers: ${SUPPORTED_PROVIDERS.join(", ")}`);
|
|
31
|
+
}
|
|
32
|
+
const model = raw.model;
|
|
33
|
+
if (!model || typeof model !== "string") {
|
|
34
|
+
throw new Error("Config must specify a model");
|
|
35
|
+
}
|
|
36
|
+
const config = {
|
|
37
|
+
provider,
|
|
38
|
+
model,
|
|
39
|
+
};
|
|
40
|
+
if (raw.templates)
|
|
41
|
+
config.templates = raw.templates;
|
|
42
|
+
if (raw.outputDir)
|
|
43
|
+
config.outputDir = raw.outputDir;
|
|
44
|
+
if (raw.log) {
|
|
45
|
+
config.log = {
|
|
46
|
+
enabled: raw.log.enabled ?? false,
|
|
47
|
+
path: raw.log.path,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return config;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,mBAAmB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAC;AAcpE,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,UAAkB,EAClB,MAAe;IAEf,0DAA0D;IAC1D,IAAI,MAAM,EAAE,CAAC;QACX,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,YAAY,EAAE,CAAC,CAAC,4BAA4B;IAC9C,CAAC;IAED,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,GAAG,YAAY,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnE,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAE3B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,0BAA0B,QAAQ,2BAA2B,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9F,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACxB,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,MAAM,GAAc;QACxB,QAAQ;QACR,KAAK;KACN,CAAC;IAEF,IAAI,GAAG,CAAC,SAAS;QAAE,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IACpD,IAAI,GAAG,CAAC,SAAS;QAAE,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IACpD,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,MAAM,CAAC,GAAG,GAAG;YACX,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,KAAK;YACjC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI;SACnB,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEjB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAK1C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAQlD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export class AppError extends Error {
|
|
3
|
+
hint;
|
|
4
|
+
constructor(message, hint) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "AppError";
|
|
7
|
+
this.hint = hint;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function formatError(error) {
|
|
11
|
+
if (error instanceof AppError) {
|
|
12
|
+
return `${chalk.red("Error:")} ${error.message}\n\n${chalk.yellow("Hint:")} ${error.hint}`;
|
|
13
|
+
}
|
|
14
|
+
if (error instanceof Error) {
|
|
15
|
+
return `${chalk.red("Error:")} ${error.message}`;
|
|
16
|
+
}
|
|
17
|
+
return `${chalk.red("Error:")} ${String(error)}`;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjB,IAAI,CAAS;IAE7B,YAAY,OAAe,EAAE,IAAY;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7F,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;IACnD,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACnD,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface LogEntry {
|
|
2
|
+
provider: string;
|
|
3
|
+
model: string;
|
|
4
|
+
taskType: string;
|
|
5
|
+
inputTokens: number;
|
|
6
|
+
outputTokens: number;
|
|
7
|
+
latencyMs: number;
|
|
8
|
+
success: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class ExecutionLogger {
|
|
12
|
+
private logPath;
|
|
13
|
+
constructor(logPath: string);
|
|
14
|
+
log(entry: LogEntry): void;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;IAK3B,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;CAO3B"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
export class ExecutionLogger {
|
|
4
|
+
logPath;
|
|
5
|
+
constructor(logPath) {
|
|
6
|
+
this.logPath = logPath;
|
|
7
|
+
mkdirSync(dirname(logPath), { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
log(entry) {
|
|
10
|
+
const record = {
|
|
11
|
+
timestamp: new Date().toISOString(),
|
|
12
|
+
...entry,
|
|
13
|
+
};
|
|
14
|
+
appendFileSync(this.logPath, JSON.stringify(record) + "\n", "utf-8");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAa/B,MAAM,OAAO,eAAe;IAClB,OAAO,CAAS;IAExB,YAAY,OAAe;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,GAAG,CAAC,KAAe;QACjB,MAAM,MAAM,GAAG;YACb,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,GAAG,KAAK;SACT,CAAC;QACF,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;IACvE,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AIProvider, ChatRequest, ChatResponse, StreamChunk } from "./interface.js";
|
|
2
|
+
export declare class ClaudeProvider implements AIProvider {
|
|
3
|
+
private client;
|
|
4
|
+
private model;
|
|
5
|
+
constructor(model: string);
|
|
6
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
7
|
+
chatStream(request: ChatRequest): AsyncIterable<StreamChunk>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/provider/claude.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,EAAE,MAAM;IAYnB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAiChD,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,CAAC,WAAW,CAAC;CA6BpE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
export class ClaudeProvider {
|
|
3
|
+
client;
|
|
4
|
+
model;
|
|
5
|
+
constructor(model) {
|
|
6
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
7
|
+
if (!apiKey) {
|
|
8
|
+
throw new Error("ANTHROPIC_API_KEY environment variable is not set. " +
|
|
9
|
+
"Get your API key at https://console.anthropic.com/settings/keys");
|
|
10
|
+
}
|
|
11
|
+
this.client = new Anthropic({ apiKey });
|
|
12
|
+
this.model = model;
|
|
13
|
+
}
|
|
14
|
+
async chat(request) {
|
|
15
|
+
const systemMessage = request.messages.find((m) => m.role === "system");
|
|
16
|
+
const userMessages = request.messages
|
|
17
|
+
.filter((m) => m.role !== "system")
|
|
18
|
+
.map((m) => ({
|
|
19
|
+
role: m.role,
|
|
20
|
+
content: m.content,
|
|
21
|
+
}));
|
|
22
|
+
const response = await this.client.messages.create({
|
|
23
|
+
model: request.model || this.model,
|
|
24
|
+
max_tokens: request.maxTokens ?? 4096,
|
|
25
|
+
...(systemMessage ? { system: systemMessage.content } : {}),
|
|
26
|
+
messages: userMessages,
|
|
27
|
+
...(request.temperature !== undefined
|
|
28
|
+
? { temperature: request.temperature }
|
|
29
|
+
: {}),
|
|
30
|
+
});
|
|
31
|
+
const textBlock = response.content.find((b) => b.type === "text");
|
|
32
|
+
return {
|
|
33
|
+
content: textBlock?.text ?? "",
|
|
34
|
+
model: response.model,
|
|
35
|
+
usage: {
|
|
36
|
+
promptTokens: response.usage.input_tokens,
|
|
37
|
+
completionTokens: response.usage.output_tokens,
|
|
38
|
+
totalTokens: response.usage.input_tokens + response.usage.output_tokens,
|
|
39
|
+
},
|
|
40
|
+
finishReason: response.stop_reason ?? "unknown",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async *chatStream(request) {
|
|
44
|
+
const systemMessage = request.messages.find((m) => m.role === "system");
|
|
45
|
+
const userMessages = request.messages
|
|
46
|
+
.filter((m) => m.role !== "system")
|
|
47
|
+
.map((m) => ({
|
|
48
|
+
role: m.role,
|
|
49
|
+
content: m.content,
|
|
50
|
+
}));
|
|
51
|
+
const stream = this.client.messages.stream({
|
|
52
|
+
model: request.model || this.model,
|
|
53
|
+
max_tokens: request.maxTokens ?? 4096,
|
|
54
|
+
...(systemMessage ? { system: systemMessage.content } : {}),
|
|
55
|
+
messages: userMessages,
|
|
56
|
+
...(request.temperature !== undefined
|
|
57
|
+
? { temperature: request.temperature }
|
|
58
|
+
: {}),
|
|
59
|
+
});
|
|
60
|
+
for await (const event of stream) {
|
|
61
|
+
if (event.type === "content_block_delta" &&
|
|
62
|
+
event.delta.type === "text_delta") {
|
|
63
|
+
yield { content: event.delta.text, done: false };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
yield { content: "", done: true };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/provider/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AIProvider, ChatRequest, ChatResponse, StreamChunk } from "./interface.js";
|
|
2
|
+
export declare class GeminiProvider implements AIProvider {
|
|
3
|
+
private client;
|
|
4
|
+
private model;
|
|
5
|
+
constructor(model: string);
|
|
6
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
7
|
+
chatStream(request: ChatRequest): AsyncIterable<StreamChunk>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/provider/gemini.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,EAAE,MAAM;IAYnB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAuChD,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,CAAC,WAAW,CAAC;CA+BpE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { GoogleGenAI } from "@google/genai";
|
|
2
|
+
export class GeminiProvider {
|
|
3
|
+
client;
|
|
4
|
+
model;
|
|
5
|
+
constructor(model) {
|
|
6
|
+
const apiKey = process.env.GOOGLE_API_KEY;
|
|
7
|
+
if (!apiKey) {
|
|
8
|
+
throw new Error("GOOGLE_API_KEY environment variable is not set. " +
|
|
9
|
+
"Get your API key at https://aistudio.google.com/apikey");
|
|
10
|
+
}
|
|
11
|
+
this.client = new GoogleGenAI({ apiKey });
|
|
12
|
+
this.model = model;
|
|
13
|
+
}
|
|
14
|
+
async chat(request) {
|
|
15
|
+
const systemMessage = request.messages.find((m) => m.role === "system");
|
|
16
|
+
const userMessages = request.messages.filter((m) => m.role !== "system");
|
|
17
|
+
// Build contents for Gemini
|
|
18
|
+
const contents = userMessages.map((m) => ({
|
|
19
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
20
|
+
parts: [{ text: m.content }],
|
|
21
|
+
}));
|
|
22
|
+
const response = await this.client.models.generateContent({
|
|
23
|
+
model: request.model || this.model,
|
|
24
|
+
contents,
|
|
25
|
+
...(systemMessage
|
|
26
|
+
? { config: { systemInstruction: systemMessage.content } }
|
|
27
|
+
: {}),
|
|
28
|
+
...(request.temperature !== undefined
|
|
29
|
+
? { config: { temperature: request.temperature } }
|
|
30
|
+
: {}),
|
|
31
|
+
...(request.maxTokens
|
|
32
|
+
? { config: { maxOutputTokens: request.maxTokens } }
|
|
33
|
+
: {}),
|
|
34
|
+
});
|
|
35
|
+
const text = response.text ?? "";
|
|
36
|
+
const usage = response.usageMetadata;
|
|
37
|
+
return {
|
|
38
|
+
content: text,
|
|
39
|
+
model: request.model || this.model,
|
|
40
|
+
usage: {
|
|
41
|
+
promptTokens: usage?.promptTokenCount ?? 0,
|
|
42
|
+
completionTokens: usage?.candidatesTokenCount ?? 0,
|
|
43
|
+
totalTokens: usage?.totalTokenCount ?? 0,
|
|
44
|
+
},
|
|
45
|
+
finishReason: response.candidates?.[0]?.finishReason ?? "unknown",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async *chatStream(request) {
|
|
49
|
+
const systemMessage = request.messages.find((m) => m.role === "system");
|
|
50
|
+
const userMessages = request.messages.filter((m) => m.role !== "system");
|
|
51
|
+
const contents = userMessages.map((m) => ({
|
|
52
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
53
|
+
parts: [{ text: m.content }],
|
|
54
|
+
}));
|
|
55
|
+
const response = await this.client.models.generateContentStream({
|
|
56
|
+
model: request.model || this.model,
|
|
57
|
+
contents,
|
|
58
|
+
...(systemMessage
|
|
59
|
+
? { config: { systemInstruction: systemMessage.content } }
|
|
60
|
+
: {}),
|
|
61
|
+
...(request.temperature !== undefined
|
|
62
|
+
? { config: { temperature: request.temperature } }
|
|
63
|
+
: {}),
|
|
64
|
+
...(request.maxTokens
|
|
65
|
+
? { config: { maxOutputTokens: request.maxTokens } }
|
|
66
|
+
: {}),
|
|
67
|
+
});
|
|
68
|
+
for await (const chunk of response) {
|
|
69
|
+
const text = chunk.text ?? "";
|
|
70
|
+
if (text) {
|
|
71
|
+
yield { content: text, done: false };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
yield { content: "", done: true };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=gemini.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/provider/gemini.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProviderName } from "../config.js";
|
|
2
|
+
import type { AIProvider } from "./interface.js";
|
|
3
|
+
export declare function createProvider(provider: ProviderName, model: string): AIProvider;
|
|
4
|
+
export type { AIProvider } from "./interface.js";
|
|
5
|
+
export type { ChatMessage, ChatRequest, ChatResponse, StreamChunk, } from "./interface.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKjD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,EACtB,KAAK,EAAE,MAAM,GACZ,UAAU,CAaZ;AAED,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,WAAW,GACZ,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ClaudeProvider } from "./claude.js";
|
|
2
|
+
import { GeminiProvider } from "./gemini.js";
|
|
3
|
+
import { OllamaProvider } from "./ollama.js";
|
|
4
|
+
export function createProvider(provider, model) {
|
|
5
|
+
switch (provider) {
|
|
6
|
+
case "claude":
|
|
7
|
+
return new ClaudeProvider(model);
|
|
8
|
+
case "gemini":
|
|
9
|
+
return new GeminiProvider(model);
|
|
10
|
+
case "ollama":
|
|
11
|
+
return new OllamaProvider(model);
|
|
12
|
+
default:
|
|
13
|
+
throw new Error(`Unsupported provider: "${provider}". Supported providers: claude, gemini, ollama`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/provider/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,UAAU,cAAc,CAC5B,QAAsB,EACtB,KAAa;IAEb,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,QAAQ;YACX,OAAO,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,QAAQ;YACX,OAAO,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC;QACnC;YACE,MAAM,IAAI,KAAK,CACb,0BAA0B,QAAQ,gDAAgD,CACnF,CAAC;IACN,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface ChatMessage {
|
|
2
|
+
role: "system" | "user" | "assistant";
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ChatRequest {
|
|
6
|
+
model: string;
|
|
7
|
+
messages: ChatMessage[];
|
|
8
|
+
temperature?: number;
|
|
9
|
+
maxTokens?: number;
|
|
10
|
+
stream?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ChatResponse {
|
|
13
|
+
content: string;
|
|
14
|
+
model: string;
|
|
15
|
+
usage: {
|
|
16
|
+
promptTokens: number;
|
|
17
|
+
completionTokens: number;
|
|
18
|
+
totalTokens: number;
|
|
19
|
+
};
|
|
20
|
+
finishReason: string;
|
|
21
|
+
}
|
|
22
|
+
export interface StreamChunk {
|
|
23
|
+
content: string;
|
|
24
|
+
done: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface AIProvider {
|
|
27
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
28
|
+
chatStream(request: ChatRequest): AsyncIterable<StreamChunk>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/provider/interface.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAClD,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;CAC9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interface.js","sourceRoot":"","sources":["../../src/provider/interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AIProvider, ChatRequest, ChatResponse, StreamChunk } from "./interface.js";
|
|
2
|
+
export declare class OllamaProvider implements AIProvider {
|
|
3
|
+
private client;
|
|
4
|
+
private model;
|
|
5
|
+
constructor(model: string, baseUrl?: string);
|
|
6
|
+
chat(request: ChatRequest): Promise<ChatResponse>;
|
|
7
|
+
chatStream(request: ChatRequest): AsyncIterable<StreamChunk>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=ollama.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/provider/ollama.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAExB,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;IAQrC,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA4BhD,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,CAAC,WAAW,CAAC;CAwBpE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
export class OllamaProvider {
|
|
3
|
+
client;
|
|
4
|
+
model;
|
|
5
|
+
constructor(model, baseUrl) {
|
|
6
|
+
this.client = new OpenAI({
|
|
7
|
+
baseURL: baseUrl ?? "http://localhost:11434/v1",
|
|
8
|
+
apiKey: "ollama", // Ollama doesn't need a real key but the SDK requires one
|
|
9
|
+
});
|
|
10
|
+
this.model = model;
|
|
11
|
+
}
|
|
12
|
+
async chat(request) {
|
|
13
|
+
const messages = request.messages.map((m) => ({
|
|
14
|
+
role: m.role,
|
|
15
|
+
content: m.content,
|
|
16
|
+
}));
|
|
17
|
+
const response = await this.client.chat.completions.create({
|
|
18
|
+
model: request.model || this.model,
|
|
19
|
+
messages,
|
|
20
|
+
...(request.temperature !== undefined
|
|
21
|
+
? { temperature: request.temperature }
|
|
22
|
+
: {}),
|
|
23
|
+
...(request.maxTokens ? { max_tokens: request.maxTokens } : {}),
|
|
24
|
+
});
|
|
25
|
+
const choice = response.choices[0];
|
|
26
|
+
return {
|
|
27
|
+
content: choice?.message?.content ?? "",
|
|
28
|
+
model: response.model,
|
|
29
|
+
usage: {
|
|
30
|
+
promptTokens: response.usage?.prompt_tokens ?? 0,
|
|
31
|
+
completionTokens: response.usage?.completion_tokens ?? 0,
|
|
32
|
+
totalTokens: response.usage?.total_tokens ?? 0,
|
|
33
|
+
},
|
|
34
|
+
finishReason: choice?.finish_reason ?? "unknown",
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async *chatStream(request) {
|
|
38
|
+
const messages = request.messages.map((m) => ({
|
|
39
|
+
role: m.role,
|
|
40
|
+
content: m.content,
|
|
41
|
+
}));
|
|
42
|
+
const stream = await this.client.chat.completions.create({
|
|
43
|
+
model: request.model || this.model,
|
|
44
|
+
messages,
|
|
45
|
+
stream: true,
|
|
46
|
+
...(request.temperature !== undefined
|
|
47
|
+
? { temperature: request.temperature }
|
|
48
|
+
: {}),
|
|
49
|
+
...(request.maxTokens ? { max_tokens: request.maxTokens } : {}),
|
|
50
|
+
});
|
|
51
|
+
for await (const chunk of stream) {
|
|
52
|
+
const content = chunk.choices[0]?.delta?.content ?? "";
|
|
53
|
+
if (content) {
|
|
54
|
+
yield { content, done: false };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
yield { content: "", done: true };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/provider/ollama.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AIProvider, ChatRequest } from "../provider/interface.js";
|
|
2
|
+
export interface StreamOptions {
|
|
3
|
+
onChunk?: (chunk: string) => void;
|
|
4
|
+
onDone?: () => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function streamResponse(provider: AIProvider, request: ChatRequest, options?: StreamOptions): Promise<string>;
|
|
7
|
+
//# sourceMappingURL=stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/tasks/stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAe,MAAM,0BAA0B,CAAC;AAErF,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,MAAM,CAAC,CAajB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export async function streamResponse(provider, request, options) {
|
|
2
|
+
let fullContent = "";
|
|
3
|
+
for await (const chunk of provider.chatStream(request)) {
|
|
4
|
+
if (chunk.done) {
|
|
5
|
+
options?.onDone?.();
|
|
6
|
+
break;
|
|
7
|
+
}
|
|
8
|
+
fullContent += chunk.content;
|
|
9
|
+
options?.onChunk?.(chunk.content);
|
|
10
|
+
}
|
|
11
|
+
return fullContent;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/tasks/stream.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,QAAoB,EACpB,OAAoB,EACpB,OAAuB;IAEvB,IAAI,WAAW,GAAG,EAAE,CAAC;IAErB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YACpB,MAAM;QACR,CAAC;QACD,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AIProvider } from "../provider/interface.js";
|
|
2
|
+
export interface TranslateOptions {
|
|
3
|
+
provider: AIProvider;
|
|
4
|
+
inputPath: string;
|
|
5
|
+
outputPath?: string;
|
|
6
|
+
targetLang: string;
|
|
7
|
+
templateContent?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface TranslateResult {
|
|
10
|
+
outputPath: string;
|
|
11
|
+
usage: {
|
|
12
|
+
promptTokens: number;
|
|
13
|
+
completionTokens: number;
|
|
14
|
+
totalTokens: number;
|
|
15
|
+
};
|
|
16
|
+
chunks: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function translateFile(options: TranslateOptions): Promise<TranslateResult>;
|
|
19
|
+
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/tasks/translate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,0BAA0B,CAAC;AAIxE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,UAAU,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE;QACL,YAAY,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC;CAChB;AAsED,wBAAsB,aAAa,CACjC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAoD1B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
2
|
+
import { dirname, basename, extname, join } from "path";
|
|
3
|
+
const CHUNK_SIZE = 50 * 1024; // 50KB
|
|
4
|
+
const DEFAULT_TEMPLATE = `You are a professional translator. Translate the following Markdown document to {{targetLanguage}}.
|
|
5
|
+
|
|
6
|
+
Rules:
|
|
7
|
+
- Preserve all Markdown formatting (headings, links, code blocks, tables, lists)
|
|
8
|
+
- Do not translate code blocks, URLs, or file paths
|
|
9
|
+
- Do not translate frontmatter keys (only translate values where appropriate)
|
|
10
|
+
- Maintain the same document structure
|
|
11
|
+
- Produce natural, fluent text in the target language
|
|
12
|
+
|
|
13
|
+
Additional rules for Japanese translation:
|
|
14
|
+
- Use full-width punctuation: 。、?! (not .,?!)
|
|
15
|
+
- Add half-width spaces around English words and numbers (e.g., "Vela とは", "NGSIv2 は", "3 つの")
|
|
16
|
+
- Use natural Japanese terms for technical words where appropriate (e.g., "registration" → "登録", "subscription" → "サブスクリプション")
|
|
17
|
+
- Keep product names, proper nouns, and abbreviations unchanged (e.g., Vela, FIWARE, NGSIv2, NGSI-LD, MCP)`;
|
|
18
|
+
function buildPrompt(content, targetLang, templateContent) {
|
|
19
|
+
const template = templateContent || DEFAULT_TEMPLATE;
|
|
20
|
+
const systemPrompt = template
|
|
21
|
+
.replace(/\{\{targetLanguage\}\}/g, targetLang)
|
|
22
|
+
.replace(/\{\{content\}\}/g, "");
|
|
23
|
+
return [
|
|
24
|
+
{ role: "system", content: systemPrompt },
|
|
25
|
+
{ role: "user", content },
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
function resolveOutputPath(inputPath, targetLang, outputPath) {
|
|
29
|
+
if (outputPath)
|
|
30
|
+
return outputPath;
|
|
31
|
+
const dir = dirname(inputPath);
|
|
32
|
+
const ext = extname(inputPath);
|
|
33
|
+
const base = basename(inputPath, ext);
|
|
34
|
+
return join(dir, `${base}.${targetLang}${ext}`);
|
|
35
|
+
}
|
|
36
|
+
function splitIntoChunks(content) {
|
|
37
|
+
if (content.length <= CHUNK_SIZE) {
|
|
38
|
+
return [content];
|
|
39
|
+
}
|
|
40
|
+
const chunks = [];
|
|
41
|
+
const lines = content.split("\n");
|
|
42
|
+
let currentChunk = "";
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
if (currentChunk.length + line.length + 1 > CHUNK_SIZE && currentChunk.length > 0) {
|
|
45
|
+
chunks.push(currentChunk);
|
|
46
|
+
currentChunk = line + "\n";
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
currentChunk += line + "\n";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (currentChunk.trim().length > 0) {
|
|
53
|
+
chunks.push(currentChunk);
|
|
54
|
+
}
|
|
55
|
+
return chunks;
|
|
56
|
+
}
|
|
57
|
+
export async function translateFile(options) {
|
|
58
|
+
const { provider, inputPath, targetLang, templateContent } = options;
|
|
59
|
+
// Read input file
|
|
60
|
+
let content;
|
|
61
|
+
try {
|
|
62
|
+
content = readFileSync(inputPath, "utf-8");
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
66
|
+
throw new Error(`Input file not found: ${inputPath}`);
|
|
67
|
+
}
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
// Check for empty file
|
|
71
|
+
if (content.trim().length === 0) {
|
|
72
|
+
throw new Error(`Input file is empty: ${inputPath}`);
|
|
73
|
+
}
|
|
74
|
+
const resolvedOutput = resolveOutputPath(inputPath, targetLang, options.outputPath);
|
|
75
|
+
// Ensure output directory exists
|
|
76
|
+
mkdirSync(dirname(resolvedOutput), { recursive: true });
|
|
77
|
+
// Split into chunks if needed
|
|
78
|
+
const chunks = splitIntoChunks(content);
|
|
79
|
+
const translatedParts = [];
|
|
80
|
+
let totalUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
81
|
+
for (const chunk of chunks) {
|
|
82
|
+
const messages = buildPrompt(chunk, targetLang, templateContent);
|
|
83
|
+
const response = await provider.chat({
|
|
84
|
+
model: "",
|
|
85
|
+
messages,
|
|
86
|
+
});
|
|
87
|
+
translatedParts.push(response.content);
|
|
88
|
+
totalUsage.promptTokens += response.usage.promptTokens;
|
|
89
|
+
totalUsage.completionTokens += response.usage.completionTokens;
|
|
90
|
+
totalUsage.totalTokens += response.usage.totalTokens;
|
|
91
|
+
}
|
|
92
|
+
const translatedContent = translatedParts.join("");
|
|
93
|
+
// Write output
|
|
94
|
+
writeFileSync(resolvedOutput, translatedContent, "utf-8");
|
|
95
|
+
return {
|
|
96
|
+
outputPath: resolvedOutput,
|
|
97
|
+
usage: totalUsage,
|
|
98
|
+
chunks: chunks.length,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=translate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/tasks/translate.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geolonia/yuuhitsu",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "右筆 (Yuuhitsu) - AI-powered document operations CLI. Translate, generate, and sync documents using Claude, Gemini, or Ollama.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"yuuhitsu": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"prepare": "tsc",
|
|
12
|
+
"dev": "tsx src/cli/index.ts",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"lint": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"keywords": ["ai", "cli", "translation", "documentation"],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src/templates",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
27
|
+
"@google/genai": "^0.7.0",
|
|
28
|
+
"openai": "^4.77.0",
|
|
29
|
+
"commander": "^13.0.0",
|
|
30
|
+
"yaml": "^2.7.0",
|
|
31
|
+
"dotenv": "^16.4.0",
|
|
32
|
+
"chalk": "^5.4.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"vitest": "^3.0.0",
|
|
36
|
+
"typescript": "^5.7.0",
|
|
37
|
+
"tsx": "^4.19.0",
|
|
38
|
+
"@types/node": "^22.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
You are a technical documentation writer. Generate comprehensive documentation for the following source code.
|
|
2
|
+
|
|
3
|
+
Output format: {{format}}
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
- Include a brief description of the module/file purpose
|
|
7
|
+
- Document all exported functions, classes, and interfaces
|
|
8
|
+
- Include parameter descriptions and return types
|
|
9
|
+
- Provide usage examples where helpful
|
|
10
|
+
- Use clear, concise language
|
|
11
|
+
|
|
12
|
+
Source code:
|
|
13
|
+
|
|
14
|
+
{{content}}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
You are a test engineer. Generate a test suite for the following source code.
|
|
2
|
+
|
|
3
|
+
Framework: {{framework}}
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
- Create a describe block for each exported function/class
|
|
7
|
+
- Write it blocks covering normal cases, edge cases, and error cases
|
|
8
|
+
- Use meaningful test descriptions
|
|
9
|
+
- Include necessary imports
|
|
10
|
+
- Mock external dependencies where appropriate
|
|
11
|
+
|
|
12
|
+
Source code:
|
|
13
|
+
|
|
14
|
+
{{content}}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
You are a research analyst. Research the following topic and produce a structured report.
|
|
2
|
+
|
|
3
|
+
Topic: {{query}}
|
|
4
|
+
|
|
5
|
+
Output format:
|
|
6
|
+
## Summary
|
|
7
|
+
(Brief overview of findings)
|
|
8
|
+
|
|
9
|
+
## Analysis
|
|
10
|
+
(Detailed analysis and comparison)
|
|
11
|
+
|
|
12
|
+
## Recommendations
|
|
13
|
+
(Actionable recommendations)
|
|
14
|
+
|
|
15
|
+
## Sources
|
|
16
|
+
(List sources used)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Convert the following Markdown document to VitePress-compatible format.
|
|
2
|
+
|
|
3
|
+
Rules:
|
|
4
|
+
- Add VitePress frontmatter (title, description)
|
|
5
|
+
- Ensure heading hierarchy starts at h1
|
|
6
|
+
- Convert any non-standard Markdown to VitePress-compatible syntax
|
|
7
|
+
- Preserve all content and links
|
|
8
|
+
|
|
9
|
+
Document:
|
|
10
|
+
|
|
11
|
+
{{content}}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
You are a professional translator. Translate the following Markdown document to {{targetLanguage}}.
|
|
2
|
+
|
|
3
|
+
Rules:
|
|
4
|
+
- Preserve all Markdown formatting (headings, links, code blocks, tables, lists)
|
|
5
|
+
- Do not translate code blocks, URLs, or file paths
|
|
6
|
+
- Do not translate frontmatter keys (only translate values where appropriate)
|
|
7
|
+
- Maintain the same document structure
|
|
8
|
+
- Produce natural, fluent text in the target language
|
|
9
|
+
|
|
10
|
+
Additional rules for Japanese translation:
|
|
11
|
+
- Use full-width punctuation: 。、?! (not .,?!)
|
|
12
|
+
- Add half-width spaces around English words and numbers (e.g., "Vela とは", "NGSIv2 は", "3 つの")
|
|
13
|
+
- Use natural Japanese terms for technical words where appropriate (e.g., "registration" → "登録", "subscription" → "サブスクリプション")
|
|
14
|
+
- Keep product names, proper nouns, and abbreviations unchanged (e.g., Vela, FIWARE, NGSIv2, NGSI-LD, MCP)
|
|
15
|
+
|
|
16
|
+
Document to translate:
|
|
17
|
+
|
|
18
|
+
{{content}}
|