opencode-janus 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/README.md +145 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +185 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/exec.d.ts +4 -0
- package/dist/exec.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4143 -0
- package/dist/install.d.ts +25 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/resolver.d.ts +14 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/shell-hook.d.ts +13 -0
- package/dist/shell-hook.d.ts.map +1 -0
- package/dist/test-command.d.ts +9 -0
- package/dist/test-command.d.ts.map +1 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# janus
|
|
2
|
+
|
|
3
|
+
Directory-based OpenCode configuration switching tool. Automatically switch between different OpenCode configurations based on your current working directory.
|
|
4
|
+
|
|
5
|
+
Named after the Roman god of transitions and new beginnings—janus transforms your configuration seamlessly as you move between different projects.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### Via NPM (Recommended)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g janus
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Via Bun (Development)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Clone the repository
|
|
19
|
+
git clone https://github.com/kuitos/janus.git
|
|
20
|
+
cd janus
|
|
21
|
+
|
|
22
|
+
# Install dependencies
|
|
23
|
+
bun install
|
|
24
|
+
|
|
25
|
+
# Build the project
|
|
26
|
+
bun run build
|
|
27
|
+
|
|
28
|
+
# Install globally from source
|
|
29
|
+
npm install -g .
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
Create a configuration file at `~/.config/janus/config.json`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mappings": [
|
|
39
|
+
{
|
|
40
|
+
"match": ["/Users/yourname/work/company/**"],
|
|
41
|
+
"configDir": "/Users/yourname/.config/opencode-company"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"match": ["/Users/yourname/projects/oss/**"],
|
|
45
|
+
"configDir": "/Users/yourname/.config/opencode-oss"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Configuration Format
|
|
52
|
+
|
|
53
|
+
- `mappings`: Array of mapping rules
|
|
54
|
+
- `match`: Array of path patterns (supports glob patterns with `**`)
|
|
55
|
+
- `configDir`: The configuration directory to use for matching paths
|
|
56
|
+
|
|
57
|
+
Each configuration directory should contain:
|
|
58
|
+
- `opencode.json` - OpenCode configuration
|
|
59
|
+
- `oh-my-opencode.json` - oh-my-opencode configuration
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### First Time Setup
|
|
64
|
+
|
|
65
|
+
After installation, create your configuration file at `~/.config/janus/config.json` (see Configuration section).
|
|
66
|
+
|
|
67
|
+
### Install Shell Hook
|
|
68
|
+
|
|
69
|
+
Install the janus hook to automatically set the correct configuration based on your current directory:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
janus install
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The command will automatically detect and install to `.zshrc` (if it exists) or `.bashrc`.
|
|
76
|
+
|
|
77
|
+
After installation, restart your shell or run:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
source ~/.zshrc # or source ~/.bashrc
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Now you can use `opencode` directly - it will automatically use the correct configuration based on your current directory.
|
|
84
|
+
|
|
85
|
+
### Uninstall
|
|
86
|
+
|
|
87
|
+
To uninstall the hook:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
janus uninstall
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
## How It Works
|
|
95
|
+
|
|
96
|
+
1. **Path Matching**: When you run a command, janus checks your current working directory against the configured patterns
|
|
97
|
+
2. **Longest Prefix Priority**: If multiple patterns match, the longest (most specific) pattern wins
|
|
98
|
+
3. **Environment Variable**: The tool sets `OPENCODE_CONFIG_DIR` to point to the matched configuration directory
|
|
99
|
+
4. **Process Isolation**: Each opencode process gets its own configuration, preventing conflicts between different projects
|
|
100
|
+
|
|
101
|
+
## Development
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Run tests
|
|
105
|
+
bun test
|
|
106
|
+
|
|
107
|
+
# Run tests with coverage
|
|
108
|
+
bun test --coverage
|
|
109
|
+
|
|
110
|
+
# Build for production
|
|
111
|
+
bun run build
|
|
112
|
+
|
|
113
|
+
# Type check
|
|
114
|
+
bun run typecheck
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Project Structure
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
src/
|
|
121
|
+
├── types.ts # TypeScript type definitions
|
|
122
|
+
├── config.ts # Configuration loading
|
|
123
|
+
├── config.test.ts # Config tests
|
|
124
|
+
├── resolver.ts # Path matching logic
|
|
125
|
+
├── resolver.test.ts # Resolver tests
|
|
126
|
+
├── install.ts # Shell hook installation/uninstallation
|
|
127
|
+
├── install.test.ts # Install command tests
|
|
128
|
+
├── shell-hook.ts # Shell hook generation
|
|
129
|
+
├── shell-hook.test.ts # Shell hook tests
|
|
130
|
+
├── cli.ts # CLI entry point
|
|
131
|
+
└── cli.test.ts # CLI tests
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT
|
|
137
|
+
|
|
138
|
+
## Contributing
|
|
139
|
+
|
|
140
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
141
|
+
|
|
142
|
+
## Acknowledgments
|
|
143
|
+
|
|
144
|
+
- Built with [Bun](https://bun.sh)
|
|
145
|
+
- Inspired by [direnv](https://direnv.net/) and [projj](https://github.com/popomore/projj)
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAWA,wBAAgB,QAAQ,IAAI,IAAI,CAU/B;AA8CD,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyC1D"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __export = (target, all) => {
|
|
5
|
+
for (var name in all)
|
|
6
|
+
__defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
set: (newValue) => all[name] = () => newValue
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
14
|
+
|
|
15
|
+
// src/cli.ts
|
|
16
|
+
import { parseArgs } from "node:util";
|
|
17
|
+
|
|
18
|
+
// src/install.ts
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
|
|
23
|
+
// src/shell-hook.ts
|
|
24
|
+
function generateShellHook(shell) {
|
|
25
|
+
return `opencode() {
|
|
26
|
+
janus exec -- "$@"
|
|
27
|
+
}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/install.ts
|
|
31
|
+
var HOOK_START_MARKER = "# >>> janus auto-initialization >>>";
|
|
32
|
+
var HOOK_END_MARKER = "# <<< janus auto-initialization <<<";
|
|
33
|
+
function detectShellRcFile() {
|
|
34
|
+
const home = homedir();
|
|
35
|
+
const zshrcPath = join(home, ".zshrc");
|
|
36
|
+
const bashrcPath = join(home, ".bashrc");
|
|
37
|
+
if (existsSync(zshrcPath)) {
|
|
38
|
+
return zshrcPath;
|
|
39
|
+
}
|
|
40
|
+
if (existsSync(bashrcPath)) {
|
|
41
|
+
return bashrcPath;
|
|
42
|
+
}
|
|
43
|
+
return zshrcPath;
|
|
44
|
+
}
|
|
45
|
+
function getShellTypeFromRcFile(rcFile) {
|
|
46
|
+
return rcFile.endsWith(".zshrc") ? "zsh" : "bash";
|
|
47
|
+
}
|
|
48
|
+
function isHookInstalled(rcFilePath) {
|
|
49
|
+
if (!existsSync(rcFilePath)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const content = readFileSync(rcFilePath, "utf-8");
|
|
53
|
+
return content.includes(HOOK_START_MARKER);
|
|
54
|
+
}
|
|
55
|
+
function installHook(rcFilePath, shellType) {
|
|
56
|
+
if (existsSync(rcFilePath)) {
|
|
57
|
+
const content = readFileSync(rcFilePath, "utf-8");
|
|
58
|
+
if (content.includes(HOOK_START_MARKER)) {
|
|
59
|
+
throw new Error("Hook already installed. Use uninstall first.");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const hook = generateShellHook(shellType);
|
|
63
|
+
const hookBlock = `${HOOK_START_MARKER}
|
|
64
|
+
${hook}
|
|
65
|
+
${HOOK_END_MARKER}
|
|
66
|
+
`;
|
|
67
|
+
if (existsSync(rcFilePath)) {
|
|
68
|
+
const content = readFileSync(rcFilePath, "utf-8");
|
|
69
|
+
const newContent = content.endsWith(`
|
|
70
|
+
`) ? content + hookBlock : content + `
|
|
71
|
+
` + hookBlock;
|
|
72
|
+
writeFileSync(rcFilePath, newContent, "utf-8");
|
|
73
|
+
} else {
|
|
74
|
+
writeFileSync(rcFilePath, hookBlock, "utf-8");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function uninstallHook(rcFilePath) {
|
|
78
|
+
if (!existsSync(rcFilePath)) {
|
|
79
|
+
throw new Error(`RC file not found: ${rcFilePath}`);
|
|
80
|
+
}
|
|
81
|
+
const content = readFileSync(rcFilePath, "utf-8");
|
|
82
|
+
if (!content.includes(HOOK_START_MARKER)) {
|
|
83
|
+
throw new Error("Hook not installed.");
|
|
84
|
+
}
|
|
85
|
+
const hookRegex = new RegExp(`\\n?${escapeRegex(HOOK_START_MARKER)}[\\s\\S]*?${escapeRegex(HOOK_END_MARKER)}\\n?`, "g");
|
|
86
|
+
const newContent = content.replace(hookRegex, `
|
|
87
|
+
`).trim();
|
|
88
|
+
writeFileSync(rcFilePath, newContent ? newContent + `
|
|
89
|
+
` : "", "utf-8");
|
|
90
|
+
}
|
|
91
|
+
function escapeRegex(str) {
|
|
92
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/cli.ts
|
|
96
|
+
function showHelp() {
|
|
97
|
+
console.log(`Usage: janus <command> [options]
|
|
98
|
+
|
|
99
|
+
Commands:
|
|
100
|
+
install Install shell hook to .zshrc or .bashrc
|
|
101
|
+
uninstall Uninstall shell hook from shell RC file
|
|
102
|
+
|
|
103
|
+
Options:
|
|
104
|
+
--help, -h Show this help message
|
|
105
|
+
`);
|
|
106
|
+
}
|
|
107
|
+
async function handleInstall() {
|
|
108
|
+
try {
|
|
109
|
+
const rcFile = detectShellRcFile();
|
|
110
|
+
const shellType = getShellTypeFromRcFile(rcFile);
|
|
111
|
+
if (isHookInstalled(rcFile)) {
|
|
112
|
+
console.error("Error: Hook already installed.");
|
|
113
|
+
console.error(`To reinstall, run: janus uninstall && janus install`);
|
|
114
|
+
return 1;
|
|
115
|
+
}
|
|
116
|
+
installHook(rcFile, shellType);
|
|
117
|
+
console.log(`✓ Successfully installed janus hook to ${rcFile}`);
|
|
118
|
+
console.log(` Please restart your shell or run: source ${rcFile}`);
|
|
119
|
+
return 0;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function handleUninstall() {
|
|
126
|
+
try {
|
|
127
|
+
const rcFile = detectShellRcFile();
|
|
128
|
+
if (!isHookInstalled(rcFile)) {
|
|
129
|
+
console.error("Error: Hook not installed.");
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
uninstallHook(rcFile);
|
|
133
|
+
console.log(`✓ Successfully uninstalled janus hook from ${rcFile}`);
|
|
134
|
+
console.log(` Please restart your shell or run: source ${rcFile}`);
|
|
135
|
+
return 0;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function main(args) {
|
|
142
|
+
try {
|
|
143
|
+
const options = {
|
|
144
|
+
help: {
|
|
145
|
+
type: "boolean",
|
|
146
|
+
short: "h"
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const { values, positionals } = parseArgs({
|
|
150
|
+
args,
|
|
151
|
+
options,
|
|
152
|
+
allowPositionals: true
|
|
153
|
+
});
|
|
154
|
+
if (values.help) {
|
|
155
|
+
showHelp();
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
if (positionals.length === 0) {
|
|
159
|
+
console.error("Error: No command specified");
|
|
160
|
+
console.error("Use --help for usage information");
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
const command = positionals[0];
|
|
164
|
+
if (command === "install") {
|
|
165
|
+
return await handleInstall();
|
|
166
|
+
} else if (command === "uninstall") {
|
|
167
|
+
return await handleUninstall();
|
|
168
|
+
} else {
|
|
169
|
+
console.error(`Error: Unknown command: ${command}`);
|
|
170
|
+
console.error("Use --help for usage information");
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (__require.main == __require.module) {
|
|
179
|
+
const exitCode = await main(process.argv.slice(2));
|
|
180
|
+
process.exit(exitCode);
|
|
181
|
+
}
|
|
182
|
+
export {
|
|
183
|
+
showHelp,
|
|
184
|
+
main
|
|
185
|
+
};
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpD,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAerD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C"}
|
package/dist/exec.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Mapping } from './types';
|
|
2
|
+
export declare function execWithConfig(configDir: string, opencodeArgs: string[]): Promise<number>;
|
|
3
|
+
export declare function exec(cwd: string, mappings: Mapping[], opencodeArgs: string[]): Promise<number>;
|
|
4
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,MAAM,CAAC,CAWjB;AAED,wBAAsB,IAAI,CACxB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,OAAO,EAAE,EACnB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,MAAM,CAAC,CAQjB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { loadConfig } from './config';
|
|
2
|
+
export { testPath, formatTestResult } from './test-command';
|
|
3
|
+
export { generateShellHook } from './shell-hook';
|
|
4
|
+
export { exec } from './exec';
|
|
5
|
+
export { resolvePath, matchesPattern, findLongestMatch } from './resolver';
|
|
6
|
+
export type { Config, Mapping } from './types';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC"}
|