triss-coworker 0.13.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 +546 -0
- package/bin/triss.js +263 -0
- package/docs/configuration.md +275 -0
- package/docs/extending.md +255 -0
- package/docs/integrations/confluence.md +65 -0
- package/docs/integrations/github.md +74 -0
- package/docs/integrations/gitlab.md +71 -0
- package/docs/integrations/jira.md +88 -0
- package/docs/integrations/linear.md +87 -0
- package/docs/mcp.md +267 -0
- package/docs/testing/test-plan-main.md +326 -0
- package/package.json +54 -0
- package/src/client.js +104 -0
- package/src/commands/ask.js +97 -0
- package/src/commands/chat.js +59 -0
- package/src/commands/commit-msg.js +91 -0
- package/src/commands/completion.js +179 -0
- package/src/commands/config.js +525 -0
- package/src/commands/extract.js +59 -0
- package/src/commands/fetch.js +35 -0
- package/src/commands/init.js +194 -0
- package/src/commands/mcp.js +135 -0
- package/src/commands/review.js +170 -0
- package/src/commands/status.js +75 -0
- package/src/commands/usage.js +77 -0
- package/src/commands/write.js +58 -0
- package/src/config.js +47 -0
- package/src/git.js +75 -0
- package/src/integrations/_atlassian.js +32 -0
- package/src/integrations/_contract.js +189 -0
- package/src/integrations/_registry.js +85 -0
- package/src/integrations/confluence/client.js +78 -0
- package/src/integrations/confluence/commands.js +94 -0
- package/src/integrations/confluence/index.js +71 -0
- package/src/integrations/github/client.js +112 -0
- package/src/integrations/github/commands.js +112 -0
- package/src/integrations/github/index.js +99 -0
- package/src/integrations/gitlab/client.js +97 -0
- package/src/integrations/gitlab/commands.js +119 -0
- package/src/integrations/gitlab/index.js +88 -0
- package/src/integrations/jira/adf.js +82 -0
- package/src/integrations/jira/client.js +97 -0
- package/src/integrations/jira/commands.js +163 -0
- package/src/integrations/jira/index.js +104 -0
- package/src/integrations/linear/client.js +166 -0
- package/src/integrations/linear/commands.js +133 -0
- package/src/integrations/linear/index.js +105 -0
- package/src/mcp/handlers.js +773 -0
- package/src/mcp/install.js +276 -0
- package/src/mcp/review-core.js +130 -0
- package/src/mcp/server.js +56 -0
- package/src/mcp/tools.js +665 -0
- package/src/models.js +20 -0
- package/src/net.js +89 -0
- package/src/paths.js +162 -0
- package/src/picker.js +137 -0
- package/src/safety.js +49 -0
- package/src/secrets.js +249 -0
- package/src/usage.js +163 -0
- package/src/web.js +172 -0
- package/templates/claude.md +151 -0
- package/templates/codex.md +51 -0
package/bin/triss.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const [nodeMajor] = process.versions.node.split('.').map(Number);
|
|
3
|
+
if (nodeMajor < 18) {
|
|
4
|
+
process.stderr.write(
|
|
5
|
+
`triss requires Node.js >= 18 (you are on ${process.versions.node}).\n` +
|
|
6
|
+
`Upgrade via nvm/fnm or https://nodejs.org/.\n`,
|
|
7
|
+
);
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import pc from 'picocolors';
|
|
13
|
+
import { runAsk } from '../src/commands/ask.js';
|
|
14
|
+
import { runWrite } from '../src/commands/write.js';
|
|
15
|
+
import { runExtract } from '../src/commands/extract.js';
|
|
16
|
+
import { runFetch } from '../src/commands/fetch.js';
|
|
17
|
+
import { runReview } from '../src/commands/review.js';
|
|
18
|
+
import { runChat } from '../src/commands/chat.js';
|
|
19
|
+
import { runUsage } from '../src/commands/usage.js';
|
|
20
|
+
import {
|
|
21
|
+
runMcpServe,
|
|
22
|
+
runMcpInstall,
|
|
23
|
+
runMcpUninstall,
|
|
24
|
+
runMcpStatus,
|
|
25
|
+
} from '../src/commands/mcp.js';
|
|
26
|
+
import { runCommitMsg } from '../src/commands/commit-msg.js';
|
|
27
|
+
import { runInit } from '../src/commands/init.js';
|
|
28
|
+
import { runStatus } from '../src/commands/status.js';
|
|
29
|
+
import { runCompletion } from '../src/commands/completion.js';
|
|
30
|
+
import {
|
|
31
|
+
runWizard,
|
|
32
|
+
runSet,
|
|
33
|
+
runGet,
|
|
34
|
+
runList,
|
|
35
|
+
runPath,
|
|
36
|
+
runEdit,
|
|
37
|
+
runUnset,
|
|
38
|
+
} from '../src/commands/config.js';
|
|
39
|
+
import { loadIntegrations } from '../src/integrations/_registry.js';
|
|
40
|
+
|
|
41
|
+
const program = new Command();
|
|
42
|
+
program
|
|
43
|
+
.name('triss')
|
|
44
|
+
.description(
|
|
45
|
+
'Cheap DeepSeek coworker for AI coding agents. Delegate bulk reads, ' +
|
|
46
|
+
'boilerplate generation, chat extraction, and tracker I/O to save tokens.',
|
|
47
|
+
)
|
|
48
|
+
.version('0.12.1');
|
|
49
|
+
|
|
50
|
+
program
|
|
51
|
+
.command('init')
|
|
52
|
+
.description('Add the triss delegation block to your project (or globally)')
|
|
53
|
+
.option('-g, --global', 'install into the target agent global rules file instead of the current project')
|
|
54
|
+
.option('-t, --target <agent>', 'target agent (claude | codex | both); omit for an interactive prompt')
|
|
55
|
+
.option('-f, --force', 'force-replace an existing triss block without diffing')
|
|
56
|
+
.option('-s, --setup', 'after writing CLAUDE.md, run `triss config wizard` to fill in credentials')
|
|
57
|
+
.action(wrap(runInit));
|
|
58
|
+
|
|
59
|
+
program
|
|
60
|
+
.command('ask')
|
|
61
|
+
.description('Delegate bulk reading (files, URLs, and/or piped stdin) to DeepSeek; returns a structured summary')
|
|
62
|
+
.option('-p, --paths <paths...>', 'files or globs to read')
|
|
63
|
+
.option('-u, --urls <urls...>', 'http(s) URLs to fetch and convert to markdown')
|
|
64
|
+
.option('--stdin', 'read piped stdin as an additional source (for `cmd | triss ask --stdin ...`)')
|
|
65
|
+
.requiredOption('-q, --question <text>', 'question to answer about the corpus')
|
|
66
|
+
.option('-m, --model <name>', 'model preset (flash | pro) or full model id')
|
|
67
|
+
.option('--max-tokens <n>', 'token budget for reasoning + answer', (v) => parseInt(v, 10), 8192)
|
|
68
|
+
.option('--system <text>', 'override the system prompt')
|
|
69
|
+
.option('--no-stream', "disable streaming output (default streams when stdout is a TTY)")
|
|
70
|
+
.action(wrap(runAsk));
|
|
71
|
+
|
|
72
|
+
program
|
|
73
|
+
.command('write')
|
|
74
|
+
.description('Delegate boilerplate generation to DeepSeek')
|
|
75
|
+
.requiredOption('-s, --spec <text>', 'what to write')
|
|
76
|
+
.requiredOption('-t, --target <path>', 'output file path')
|
|
77
|
+
.option('-c, --context <path>', 'reference file to mimic in style')
|
|
78
|
+
.option('-m, --model <name>', 'model preset (flash | pro) or full model id')
|
|
79
|
+
.option('--max-tokens <n>', 'token budget for reasoning + output', (v) => parseInt(v, 10), 16384)
|
|
80
|
+
.action(wrap(runWrite));
|
|
81
|
+
|
|
82
|
+
program
|
|
83
|
+
.command('extract')
|
|
84
|
+
.description('Convert Claude Code JSONL transcripts into readable text')
|
|
85
|
+
.argument('<jsonl>', 'path to a .jsonl session file')
|
|
86
|
+
.option('-o, --output <path>', 'write to file instead of stdout')
|
|
87
|
+
.action((jsonl, opts) => wrap(runExtract)({ jsonl, ...opts }));
|
|
88
|
+
|
|
89
|
+
program
|
|
90
|
+
.command('fetch')
|
|
91
|
+
.description('Fetch URL(s) and return readable markdown; --question summarises via DeepSeek')
|
|
92
|
+
.argument('<urls...>', 'one or more http(s) URLs')
|
|
93
|
+
.option('-q, --question <text>', 'have DeepSeek summarise the fetched corpus')
|
|
94
|
+
.option('-m, --model <name>', 'model preset (flash | pro) or full model id')
|
|
95
|
+
.option('--max-tokens <n>', 'token budget for the summary')
|
|
96
|
+
.option('--timeout <ms>', 'per-request timeout in ms (default 30000)')
|
|
97
|
+
.option('--json', 'output JSON array of {url, markdown}')
|
|
98
|
+
.action((urls, opts) => wrap(runFetch)(urls, opts));
|
|
99
|
+
|
|
100
|
+
program
|
|
101
|
+
.command('chat [prompt]')
|
|
102
|
+
.description('Ask the worker model anything — no corpus, just a bare prompt.')
|
|
103
|
+
.option('--stdin', 'read prompt from piped stdin instead of [prompt] arg')
|
|
104
|
+
.option('-s, --system <text>', 'system prompt (e.g. role / persona)')
|
|
105
|
+
.option('-m, --model <name>', 'model preset (flash | pro) or full model id')
|
|
106
|
+
.option('--max-tokens <n>', 'token budget (default 4096)')
|
|
107
|
+
.option('--no-stream', 'disable streaming output')
|
|
108
|
+
.action((prompt, opts) => wrap(runChat)(prompt, opts));
|
|
109
|
+
|
|
110
|
+
program
|
|
111
|
+
.command('commit-msg')
|
|
112
|
+
.description('Generate a Git commit message from staged changes (Conventional Commits by default).')
|
|
113
|
+
.option('--apply', 'run `git commit -m <generated>` immediately instead of printing')
|
|
114
|
+
.option('--no-conventional', 'use a free-form commit subject instead of Conventional Commits')
|
|
115
|
+
.option('--type <type>', 'force the conventional type (feat | fix | refactor | …)')
|
|
116
|
+
.option('--scope <scope>', 'force the conventional scope')
|
|
117
|
+
.option('-m, --model <name>', 'model preset (flash | pro) or full id')
|
|
118
|
+
.option('--max-tokens <n>', 'token budget (default 2048)')
|
|
119
|
+
.action(wrap(runCommitMsg));
|
|
120
|
+
|
|
121
|
+
program
|
|
122
|
+
.command('review [pr]')
|
|
123
|
+
.description('Code review via DeepSeek. No arg: current branch vs default base. With <pr>: GitHub PR via gh.')
|
|
124
|
+
.option('-b, --base <branch>', 'compare against this branch (default: auto-detect origin/HEAD or main/master/develop)')
|
|
125
|
+
.option('--skip-issue', "don't try to look up a Jira/Linear ticket from the branch/PR title")
|
|
126
|
+
.option('-q, --question <text>', 'override the default review question')
|
|
127
|
+
.option('-m, --model <name>', 'model preset (flash | pro) or full model id (default: pro)')
|
|
128
|
+
.option('--max-tokens <n>', 'token budget for the review (default 8192)')
|
|
129
|
+
.option('--no-stream', 'disable streaming output')
|
|
130
|
+
.action((pr, opts) => wrap(runReview)(pr, opts));
|
|
131
|
+
|
|
132
|
+
program
|
|
133
|
+
.command('usage')
|
|
134
|
+
.description('Cumulative cost / token usage summary across all triss calls')
|
|
135
|
+
.option('--since <period>', 'period like 24h, 7d, 4w (default 24h)')
|
|
136
|
+
.option('--month', 'shortcut for --since 30d')
|
|
137
|
+
.option('--by-project', 'break down by working directory')
|
|
138
|
+
.option('--by-model', 'break down by model name')
|
|
139
|
+
.option('--by-label', 'break down by call source (ask, review, chat, …)')
|
|
140
|
+
.option('--json', 'dump raw log records as JSON')
|
|
141
|
+
.option('--reset', 'clear the usage log (cannot be undone)')
|
|
142
|
+
.action(wrap(runUsage));
|
|
143
|
+
|
|
144
|
+
program
|
|
145
|
+
.command('status')
|
|
146
|
+
.description('Show current configuration: API key, models, .env sources')
|
|
147
|
+
.action(wrap(runStatus));
|
|
148
|
+
|
|
149
|
+
program
|
|
150
|
+
.command('completion <shell>')
|
|
151
|
+
.description('Print shell completion script (bash | zsh). eval `triss completion bash`')
|
|
152
|
+
.action((shell) => wrap(runCompletion)(shell, program));
|
|
153
|
+
|
|
154
|
+
const config = program
|
|
155
|
+
.command('config')
|
|
156
|
+
.description('Manage credentials in ~/.config/triss/.env (global) or ./.triss.env (project)');
|
|
157
|
+
|
|
158
|
+
config
|
|
159
|
+
.command('wizard [target]')
|
|
160
|
+
.description('Interactive setup. Optional target: deepseek | jira | linear | …')
|
|
161
|
+
.option('-g, --global', 'save to ~/.config/triss/.env (default if not asked)')
|
|
162
|
+
.option('-l, --local', 'save to ./.triss.env (project-local override)')
|
|
163
|
+
.option('-f, --force', 're-prompt for keys that are already set')
|
|
164
|
+
.option('--standard', 'API key + one model only — skip the standard/advanced prompt')
|
|
165
|
+
.option('--advanced', 'full wizard with presets, base URL, integrations — skip the prompt')
|
|
166
|
+
.action(wrap(runWizard));
|
|
167
|
+
|
|
168
|
+
config
|
|
169
|
+
.command('set <KEY> [value]')
|
|
170
|
+
.description('Set a single variable. Without value, prompts (masked for *_KEY/_TOKEN). Use "-" to read from stdin.')
|
|
171
|
+
.option('-g, --global', 'save to global env file')
|
|
172
|
+
.option('-l, --local', 'save to project env file (./.triss.env)')
|
|
173
|
+
.action(wrap(runSet));
|
|
174
|
+
|
|
175
|
+
config
|
|
176
|
+
.command('get <KEY>')
|
|
177
|
+
.description('Print the active value (masked for secrets)')
|
|
178
|
+
.option('-g, --global', 'check only the global file')
|
|
179
|
+
.option('-l, --local', 'check only the project file')
|
|
180
|
+
.action(wrap(runGet));
|
|
181
|
+
|
|
182
|
+
config
|
|
183
|
+
.command('list')
|
|
184
|
+
.description('List variables from all (or one) env file')
|
|
185
|
+
.option('-g, --global', 'only the global file')
|
|
186
|
+
.option('-l, --local', 'only the project file')
|
|
187
|
+
.action(wrap(runList));
|
|
188
|
+
|
|
189
|
+
config
|
|
190
|
+
.command('path')
|
|
191
|
+
.description('Print path(s) of triss env file(s)')
|
|
192
|
+
.option('-g, --global', 'only the global path')
|
|
193
|
+
.option('-l, --local', 'only the project path')
|
|
194
|
+
.action(wrap(runPath));
|
|
195
|
+
|
|
196
|
+
config
|
|
197
|
+
.command('edit')
|
|
198
|
+
.description('Open the env file in $EDITOR ($VISUAL > $EDITOR > vi)')
|
|
199
|
+
.option('-g, --global', 'edit the global file')
|
|
200
|
+
.option('-l, --local', 'edit the project file')
|
|
201
|
+
.action(wrap(runEdit));
|
|
202
|
+
|
|
203
|
+
config
|
|
204
|
+
.command('unset <KEY>')
|
|
205
|
+
.description('Remove a variable from an env file')
|
|
206
|
+
.option('-g, --global', 'global file')
|
|
207
|
+
.option('-l, --local', 'project file')
|
|
208
|
+
.action(wrap(runUnset));
|
|
209
|
+
|
|
210
|
+
function wrap(fn) {
|
|
211
|
+
return async (...args) => {
|
|
212
|
+
try {
|
|
213
|
+
await fn(...args);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
process.stderr.write(pc.red(`✗ ${err.message || err}\n`));
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const mcp = program
|
|
222
|
+
.command('mcp')
|
|
223
|
+
.description('MCP server: register Triss as a tool provider for Claude Code (and Codex soon)');
|
|
224
|
+
|
|
225
|
+
mcp
|
|
226
|
+
.command('serve', { isDefault: true })
|
|
227
|
+
.description('Run the stdio MCP server. Claude Code starts this for you — no need to run it directly.')
|
|
228
|
+
.action(wrap(runMcpServe));
|
|
229
|
+
|
|
230
|
+
mcp
|
|
231
|
+
.command('install')
|
|
232
|
+
.description('Register triss as MCP server for Claude Code (~/.claude.json) or Codex (~/.codex/config.toml)')
|
|
233
|
+
.option('-g, --global', 'install into the agent global config (default)')
|
|
234
|
+
.option('-l, --local', 'install into <cwd>/.mcp.json (claude only)')
|
|
235
|
+
.option('-t, --target <agent>', 'target agent (claude | codex | both); omit for an interactive prompt')
|
|
236
|
+
.option('--command <cmd>', 'override the executable name (default: triss)')
|
|
237
|
+
.option('--args <args>', "override args, space-separated (default: 'mcp serve')")
|
|
238
|
+
.action(wrap(runMcpInstall));
|
|
239
|
+
|
|
240
|
+
mcp
|
|
241
|
+
.command('uninstall')
|
|
242
|
+
.description('Remove the triss MCP entry from the config')
|
|
243
|
+
.option('-g, --global', 'remove from the agent global config (default)')
|
|
244
|
+
.option('-l, --local', 'remove from <cwd>/.mcp.json (claude only)')
|
|
245
|
+
.option('-t, --target <agent>', 'target agent (claude | codex | both); omit for an interactive prompt')
|
|
246
|
+
.action(wrap(runMcpUninstall));
|
|
247
|
+
|
|
248
|
+
mcp
|
|
249
|
+
.command('status')
|
|
250
|
+
.description('Show whether triss is registered and with what command')
|
|
251
|
+
.option('-g, --global', '(default) check the agent global config')
|
|
252
|
+
.option('-l, --local', 'check <cwd>/.mcp.json (claude only)')
|
|
253
|
+
.option('-t, --target <agent>', 'target agent (claude | codex | both). Default: both')
|
|
254
|
+
.action(wrap(runMcpStatus));
|
|
255
|
+
|
|
256
|
+
// Plugin-style integrations (jira, linear, ...). See docs/extending.md.
|
|
257
|
+
const integrations = await loadIntegrations();
|
|
258
|
+
for (const manifest of integrations) {
|
|
259
|
+
const sub = program.command(manifest.name).description(manifest.description || manifest.name);
|
|
260
|
+
manifest.register(sub, { wrap });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
await program.parseAsync(process.argv);
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Triss reads its credentials from `.env` files. **You should never need to
|
|
4
|
+
create or edit those files by hand** — `triss config` handles everything.
|
|
5
|
+
|
|
6
|
+
## TL;DR
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
triss config wizard # interactive setup, prompts for every credential
|
|
10
|
+
triss status # verify what's loaded and from where
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That is the entire happy path.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Where credentials live
|
|
18
|
+
|
|
19
|
+
Triss looks at two files, in this precedence order (highest first):
|
|
20
|
+
|
|
21
|
+
| Scope | Path | Purpose |
|
|
22
|
+
| --------- | --------------------------------- | --------------------------------------------------- |
|
|
23
|
+
| `process.env` | exported in your shell | Always wins. Use for CI / one-off overrides. |
|
|
24
|
+
| `local` | `<project>/.triss.env` | **Per-project** credentials. Overrides global here. |
|
|
25
|
+
| `global` | `~/.config/triss/.env` | Default for every project. |
|
|
26
|
+
|
|
27
|
+
**Same project, different Jira instances?** Put the org-wide DeepSeek key in
|
|
28
|
+
the global file, and the project-specific Jira `ATLASSIAN_*` triple in
|
|
29
|
+
`./.triss.env` for that repo. When you `cd` into another repo with its own
|
|
30
|
+
`.triss.env`, Triss automatically uses *that* repo's credentials. The
|
|
31
|
+
global Jira credentials (if any) keep working everywhere else.
|
|
32
|
+
|
|
33
|
+
`.triss.env` is auto-`chmod 600` and auto-added to that project's
|
|
34
|
+
`.gitignore` (if a `.gitignore` or `.git` exists). It is intentionally a
|
|
35
|
+
different filename from the more common `.env` so it never collides with
|
|
36
|
+
your application's own `.env`.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## `triss config` reference
|
|
41
|
+
|
|
42
|
+
### `triss config wizard [target]`
|
|
43
|
+
|
|
44
|
+
Interactive prompt-driven setup.
|
|
45
|
+
|
|
46
|
+
When invoked **without a target**, the first question is **Standard vs
|
|
47
|
+
Advanced**:
|
|
48
|
+
|
|
49
|
+
| Mode | What it asks | Use when |
|
|
50
|
+
| --------- | ------------------------------------------------------------------------ | ----------------------------------------- |
|
|
51
|
+
| Standard | API key + one worker-model name (written to both `flash` & `pro`) | You just want it to work. Default. |
|
|
52
|
+
| Advanced | Every variable for every integration, with confirm-before-each provider | You need Jira/Linear, separate presets, custom base URL |
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
triss config wizard # asks Standard / Advanced first
|
|
56
|
+
triss config wizard --standard # straight into Standard
|
|
57
|
+
triss config wizard --advanced # straight into Advanced
|
|
58
|
+
triss config wizard linear # targeted: only Linear, no mode prompt
|
|
59
|
+
triss config wizard linear --local # targeted, project-local
|
|
60
|
+
triss config wizard --advanced --force # re-prompt for already-set keys
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Behaviour:
|
|
64
|
+
|
|
65
|
+
- Asks "Global or Project?" up front (skip with `--global` / `--local`).
|
|
66
|
+
- Standard mode never asks about non-core integrations — run
|
|
67
|
+
`triss config wizard --advanced` (or a targeted invocation) later when
|
|
68
|
+
you actually need Jira/Linear.
|
|
69
|
+
- Advanced mode asks "Configure jira? (y/N)" before each non-core
|
|
70
|
+
provider so you only walk through the ones you use.
|
|
71
|
+
- Skips already-set values unless `--force`.
|
|
72
|
+
- Masks secret inputs (anything matching `*KEY*`, `*TOKEN*`, `*SECRET*`,
|
|
73
|
+
`*PASS*`).
|
|
74
|
+
- Uses every integration's `envVars` declaration — when you add a new
|
|
75
|
+
integration, the Advanced wizard picks up its variables for free.
|
|
76
|
+
|
|
77
|
+
### `triss config set <KEY> [value]`
|
|
78
|
+
|
|
79
|
+
Set a single variable.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
triss config set DEEPSEEK_API_KEY # interactive masked prompt → global
|
|
83
|
+
triss config set ATLASSIAN_API_TOKEN --local # interactive → project
|
|
84
|
+
triss config set DEEPSEEK_FLASH_MODEL deepseek-chat # value as argument → global
|
|
85
|
+
echo "$KEY" | triss config set LINEAR_API_KEY - # read from stdin (CI-friendly)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `triss config get <KEY>`
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
triss config get DEEPSEEK_API_KEY # → "global sk-d…294" (masked)
|
|
92
|
+
triss config get DEEPSEEK_API_KEY --local
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Exits with code 1 if the key is missing.
|
|
96
|
+
|
|
97
|
+
### `triss config list [--global|--local]`
|
|
98
|
+
|
|
99
|
+
Lists every variable in each env file, with masking on secrets.
|
|
100
|
+
|
|
101
|
+
### `triss config path [--global|--local]`
|
|
102
|
+
|
|
103
|
+
Prints the absolute path(s) Triss will use, with `exists` / `missing`.
|
|
104
|
+
|
|
105
|
+
### `triss config edit [--global|--local]`
|
|
106
|
+
|
|
107
|
+
Opens the env file in `$VISUAL` → `$EDITOR` → `vi`. Without a flag, asks
|
|
108
|
+
which file you mean.
|
|
109
|
+
|
|
110
|
+
### `triss config unset <KEY> [--global|--local]`
|
|
111
|
+
|
|
112
|
+
Removes a variable.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Known credentials
|
|
117
|
+
|
|
118
|
+
`triss config wizard` knows about:
|
|
119
|
+
|
|
120
|
+
| Target | Variables | Required |
|
|
121
|
+
| ---------- | ------------------------------------------------------------------------ | ----------------------------------------- |
|
|
122
|
+
| `deepseek` | `DEEPSEEK_API_KEY`, `DEEPSEEK_BASE_URL`, `DEEPSEEK_FLASH_MODEL`, `DEEPSEEK_PRO_MODEL` | only `DEEPSEEK_API_KEY` is required |
|
|
123
|
+
| `jira` | `ATLASSIAN_BASE_URL`, `ATLASSIAN_EMAIL`, `ATLASSIAN_API_TOKEN` | all three |
|
|
124
|
+
| `linear` | `LINEAR_API_KEY`, `LINEAR_API_URL` | only `LINEAR_API_KEY` is required |
|
|
125
|
+
|
|
126
|
+
When you add a new integration (see [extending.md](extending.md)), its
|
|
127
|
+
`envVars` declaration is automatically picked up — no wizard changes needed.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Recipes — common setups end-to-end
|
|
132
|
+
|
|
133
|
+
### A. Single shared workspace (most users)
|
|
134
|
+
|
|
135
|
+
One DeepSeek key, one Jira workspace, one Linear team. Everything global.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# install (once)
|
|
139
|
+
curl -fsSL https://raw.githubusercontent.com/ayleen/triss-coworker/main/install.sh | bash
|
|
140
|
+
|
|
141
|
+
# wizard — Standard mode auto-installs MCP + global CLAUDE.md
|
|
142
|
+
triss config wizard --standard
|
|
143
|
+
# (later, when you want Jira/Linear too)
|
|
144
|
+
triss config wizard jira
|
|
145
|
+
triss config wizard linear
|
|
146
|
+
|
|
147
|
+
triss status # verify everything is "ready"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Restart your Claude Code session. Done — every project sees the same
|
|
151
|
+
credentials.
|
|
152
|
+
|
|
153
|
+
### B. Different Jira instances per project
|
|
154
|
+
|
|
155
|
+
You work for two clients with separate Atlassian Cloud orgs. Same
|
|
156
|
+
DeepSeek key everywhere, but `~/work-acme/` should hit Acme's Jira and
|
|
157
|
+
`~/work-beta/` should hit Beta's.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# global once — DeepSeek key + MCP server + global CLAUDE.md fallback
|
|
161
|
+
triss config wizard --standard
|
|
162
|
+
|
|
163
|
+
# per-project — Acme
|
|
164
|
+
cd ~/work-acme
|
|
165
|
+
triss config wizard jira --local
|
|
166
|
+
# → ATLASSIAN_BASE_URL=https://acme.atlassian.net
|
|
167
|
+
# → ATLASSIAN_EMAIL=you@acme.com
|
|
168
|
+
# → ATLASSIAN_API_TOKEN=ATATT-acme...
|
|
169
|
+
# writes <pwd>/.triss.env, chmod 600, adds to .gitignore.
|
|
170
|
+
|
|
171
|
+
# per-project — Beta
|
|
172
|
+
cd ~/work-beta
|
|
173
|
+
triss config wizard jira --local
|
|
174
|
+
# → different Atlassian credentials, written to a separate .triss.env
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
What happens when you open Claude Code:
|
|
178
|
+
|
|
179
|
+
| In `~/work-acme` | MCP server reads `~/work-acme/.triss.env` → `triss_jira_*` tools call Acme's Jira |
|
|
180
|
+
| In `~/work-beta` | MCP server reads `~/work-beta/.triss.env` → `triss_jira_*` tools call Beta's Jira |
|
|
181
|
+
| In any other dir | No project `.triss.env` → no Jira tools at all (just core) |
|
|
182
|
+
|
|
183
|
+
You don't reconfigure anything — the right credentials follow the
|
|
184
|
+
working directory automatically.
|
|
185
|
+
|
|
186
|
+
Verification: `triss status` from inside each project shows the
|
|
187
|
+
`[local]` source tag next to the ATLASSIAN_* rows.
|
|
188
|
+
|
|
189
|
+
### C. Personal DeepSeek key per project
|
|
190
|
+
|
|
191
|
+
Want to charge a specific project to a separate billing account? Put
|
|
192
|
+
its `DEEPSEEK_API_KEY` in `<project>/.triss.env`. The global key in
|
|
193
|
+
`~/.config/triss/.env` keeps working everywhere else.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
cd ~/special-project
|
|
197
|
+
triss config set DEEPSEEK_API_KEY --local
|
|
198
|
+
# (masked prompt → ./.triss.env)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### D. CI / scripts
|
|
202
|
+
|
|
203
|
+
`process.env` always wins over `.env` files, so in CI you just export
|
|
204
|
+
what you need:
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
env:
|
|
208
|
+
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
|
209
|
+
ATLASSIAN_BASE_URL: ${{ vars.ATLASSIAN_BASE_URL }}
|
|
210
|
+
ATLASSIAN_EMAIL: ${{ vars.ATLASSIAN_EMAIL }}
|
|
211
|
+
ATLASSIAN_API_TOKEN: ${{ secrets.ATLASSIAN_API_TOKEN }}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`triss` commands then run unattended without needing any `.env` file.
|
|
215
|
+
|
|
216
|
+
### E. Switching projects mid-session
|
|
217
|
+
|
|
218
|
+
You added `.triss.env` to a project after Claude Code was already
|
|
219
|
+
running there. As of v0.9.3, **no restart needed** — `.env` files are
|
|
220
|
+
re-read on every `tools/list` request, so the next time the agent or
|
|
221
|
+
`claude /mcp` lists tools, the new ones appear (or disappear).
|
|
222
|
+
|
|
223
|
+
The only thing that requires a restart is the *initial* MCP-server
|
|
224
|
+
registration — i.e. running `triss mcp install` for the first time.
|
|
225
|
+
After that, credential changes are live.
|
|
226
|
+
|
|
227
|
+
## Troubleshooting
|
|
228
|
+
|
|
229
|
+
**"No DeepSeek API key found"** — `triss config wizard deepseek`.
|
|
230
|
+
|
|
231
|
+
**Wrong credentials picked up** — `triss status` shows the source for each
|
|
232
|
+
variable in `[global]` / `[local]` / `[env]` brackets. If you expected
|
|
233
|
+
project-local to win and it didn't, check that the file is named exactly
|
|
234
|
+
`.triss.env` (not `.env`) in your project root, and that you ran the
|
|
235
|
+
command from that directory.
|
|
236
|
+
|
|
237
|
+
**Switching organisations / Jira instances** — drop a `.triss.env` in the
|
|
238
|
+
project root with the project-specific keys; nothing else needs to change.
|
|
239
|
+
|
|
240
|
+
**Enabled an integration after `triss init`** — the agent's CLAUDE.md is
|
|
241
|
+
re-rendered on every `triss init`, including only integrations whose
|
|
242
|
+
required env vars are set. Run `triss init` again and your new
|
|
243
|
+
integration's delegation rules will appear in the file.
|
|
244
|
+
|
|
245
|
+
**Cost tracking** — every worker call appends to
|
|
246
|
+
`~/.cache/triss/usage.jsonl`. Inspect with `triss usage`. To opt out
|
|
247
|
+
entirely, set `TRISS_USAGE_LOG=0` before any call. To override the
|
|
248
|
+
baked-in DeepSeek list prices (e.g. you point Triss at a different
|
|
249
|
+
provider, or want the discounted rate), set
|
|
250
|
+
`TRISS_PRICE_<MODEL>=<input_cache_miss>,<input_cache_hit>,<output>` in
|
|
251
|
+
USD per token. Example: `TRISS_PRICE_DEEPSEEK_V4_PRO=4.35e-7,3.625e-9,8.7e-7`
|
|
252
|
+
applies the 75% promotional pricing to the pro preset.
|
|
253
|
+
|
|
254
|
+
**Claude Code integration step in the wizard** — split by mode:
|
|
255
|
+
|
|
256
|
+
| Mode in `triss config wizard` | What happens at the end |
|
|
257
|
+
| ----------------------------- | ----------------------- |
|
|
258
|
+
| **Standard** | Installs both paths automatically (MCP server + global CLAUDE.md). No question asked — Standard's job is to make sensible defaults work. |
|
|
259
|
+
| **Advanced** | Asks: 1) Both (default), 2) MCP only, 3) CLAUDE.md only, 4) Skip. |
|
|
260
|
+
|
|
261
|
+
The two paths cooperate: MCP is the primary tool surface; CLAUDE.md
|
|
262
|
+
acts as a fallback in case the MCP server can't load. Both can run
|
|
263
|
+
side-by-side without conflict.
|
|
264
|
+
|
|
265
|
+
Targeted invocations (`triss config wizard jira`) never touch the
|
|
266
|
+
integration paths — credential changes happen with no side effects.
|
|
267
|
+
Re-run the wizard to revisit; the install commands are idempotent.
|
|
268
|
+
|
|
269
|
+
When MCP is registered, `triss init` automatically prepends a hint to
|
|
270
|
+
the CLAUDE.md block that tells the agent to prefer the native MCP tools
|
|
271
|
+
over the Bash invocations. So you don't get duplicate routing logic in
|
|
272
|
+
the agent's prompt — just one path, with the other as fallback.
|
|
273
|
+
|
|
274
|
+
**CI** — set the variables as environment variables directly. `process.env`
|
|
275
|
+
wins over both files.
|