ccnav 1.0.0 → 1.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 +58 -4
- package/bin/ccnav.mjs +42 -2
- package/package.json +1 -1
- package/src/actions.mjs +1 -1
- package/src/app.mjs +6 -1
- package/src/platform.mjs +104 -15
- package/src/providers/claude.mjs +2 -0
- package/src/providers/codex.mjs +3 -0
package/README.md
CHANGED
|
@@ -6,15 +6,24 @@ Works on Windows, macOS and Linux with Node 20 or later. Everything that differs
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g ccnav # the -g matters
|
|
11
|
+
ccnav --install-shell # optional: leave your shell in the session's directory afterwards
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Without `-g`, npm treats the current folder as a project and puts the command in
|
|
15
|
+
`./node_modules/.bin`, which is not on PATH — the install succeeds and `ccnav` still
|
|
16
|
+
reports `command not found`. If that happens after a `-g` install, npm's global bin
|
|
17
|
+
folder is missing from PATH; `npm prefix -g` prints the folder whose `bin` belongs there.
|
|
18
|
+
|
|
19
|
+
From a checkout instead:
|
|
10
20
|
|
|
11
21
|
```sh
|
|
12
22
|
npm install
|
|
13
23
|
npm link # puts `ccnav` on PATH
|
|
14
|
-
ccnav --install-shell # optional: leave your shell in the session's directory afterwards
|
|
15
24
|
```
|
|
16
25
|
|
|
17
|
-
Or build a tarball once and install it anywhere
|
|
26
|
+
Or build a tarball once and install it anywhere:
|
|
18
27
|
|
|
19
28
|
```sh
|
|
20
29
|
npm pack # -> ccnav-1.0.0.tgz
|
|
@@ -23,7 +32,21 @@ npm install -g ./ccnav-1.0.0.tgz
|
|
|
23
32
|
|
|
24
33
|
`--install-shell` writes a function to the right file for your shell: the PowerShell `$PROFILE` on Windows, `~/.zshrc` for zsh (macOS default), `~/.bash_profile` (macOS) or `~/.bashrc` (Linux) for bash, and `~/.config/fish/functions/ccnav.fish` for fish. Running it again replaces the block rather than adding a second one.
|
|
25
34
|
|
|
26
|
-
New tabs (`t`) use Windows Terminal, iTerm2, WezTerm, kitty, GNOME Terminal or Konsole. Terminal.app gets a new window, because real tabs need Accessibility permission.
|
|
35
|
+
New tabs (`t`) use Windows Terminal, iTerm2, WezTerm, kitty, GNOME Terminal or Konsole. Terminal.app gets a new window, because real tabs need Accessibility permission. On macOS the first new tab asks for permission to control the terminal; if it is refused, allow it again under System Settings → Privacy & Security → Automation.
|
|
36
|
+
|
|
37
|
+
## Agents
|
|
38
|
+
|
|
39
|
+
Sessions are listed whether or not the agent that wrote them is still installed, so an
|
|
40
|
+
old machine's transcripts stay visible. Resuming one whose agent is missing offers to
|
|
41
|
+
install it:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
codex is not installed. Install it now with "npm install -g @openai/codex"? (y/N)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Answer `y` and ccnav runs that install, then launches the agent so you can sign in and
|
|
48
|
+
finish its own setup. Answer `n` and nothing is installed. `claude` maps to
|
|
49
|
+
`@anthropic-ai/claude-code` and `codex` to `@openai/codex`.
|
|
27
50
|
|
|
28
51
|
## Use
|
|
29
52
|
|
|
@@ -34,7 +57,38 @@ ccnav --codex only Codex sessions
|
|
|
34
57
|
ccnav --list print sessions and exit
|
|
35
58
|
ccnav --json print sessions as JSON and exit
|
|
36
59
|
ccnav --model opus any other arguments are passed to the agent
|
|
60
|
+
ccnav here new session in the current directory, running unattended
|
|
61
|
+
ccnav here --codex the same, without being asked which agent
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Start one here
|
|
65
|
+
|
|
66
|
+
`ccnav here` ignores the session list entirely. It asks which agent, then starts a
|
|
67
|
+
**new** session in the current directory with that agent's permission prompts turned
|
|
68
|
+
off, so it runs unattended:
|
|
69
|
+
|
|
37
70
|
```
|
|
71
|
+
New session in /Users/you/project
|
|
72
|
+
|
|
73
|
+
1) ◆ claude claude --dangerously-skip-permissions
|
|
74
|
+
2) ● codex codex --dangerously-bypass-approvals-and-sandbox
|
|
75
|
+
|
|
76
|
+
Which agent? [1]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Pressing enter takes the first agent; `1`/`2` or a name (`codex`, `cl`) picks one.
|
|
80
|
+
`ccnav here --claude` or `--codex` skips the question, and any other arguments go to
|
|
81
|
+
the agent (`ccnav here --claude --model opus`). The exact command is printed before it
|
|
82
|
+
runs, and ctrl-D at the prompt starts nothing.
|
|
83
|
+
|
|
84
|
+
What those flags mean is worth being clear about. Claude's
|
|
85
|
+
`--dangerously-skip-permissions` bypasses every permission check. Codex's
|
|
86
|
+
`--dangerously-bypass-approvals-and-sandbox` skips approvals *and* runs without a
|
|
87
|
+
sandbox — its own help calls it EXTREMELY DANGEROUS and intends it for environments
|
|
88
|
+
that are already sandboxed. Neither confines the agent to the directory you started in.
|
|
89
|
+
Codex removed `--full-auto` in 0.156, so the sandboxed middle ground is now spelled
|
|
90
|
+
`--ask-for-approval never --sandbox workspace-write` if you would rather edit
|
|
91
|
+
`src/providers/codex.mjs` to use that instead.
|
|
38
92
|
|
|
39
93
|
| Key | Action |
|
|
40
94
|
|---|---|
|
package/bin/ccnav.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { render } from 'ink';
|
|
4
4
|
import { App } from '../src/app.mjs';
|
|
5
|
-
import { ago, humanSize, listSessions, providerOf, shortPath } from '../src/sessions.mjs';
|
|
6
|
-
import { installShellFunction, launchAgent, reportCwd } from '../src/actions.mjs';
|
|
5
|
+
import { AGENTS, PROVIDERS, ago, humanSize, listSessions, providerOf, shortPath } from '../src/sessions.mjs';
|
|
6
|
+
import { askUser, ensureAgent, installShellFunction, launchAgent, reportCwd } from '../src/actions.mjs';
|
|
7
7
|
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
|
|
@@ -17,6 +17,9 @@ usage:
|
|
|
17
17
|
ccnav --codex only Codex sessions
|
|
18
18
|
ccnav --list print sessions as a table and exit
|
|
19
19
|
ccnav --json print sessions as JSON and exit
|
|
20
|
+
ccnav here skip the picker: start a NEW session in the current
|
|
21
|
+
directory, running unattended with no permission prompts
|
|
22
|
+
(ccnav here --claude / --codex picks the agent outright)
|
|
20
23
|
ccnav --install-shell add a shell function (PowerShell, zsh, bash or fish) so
|
|
21
24
|
your shell is left in the session's directory afterwards
|
|
22
25
|
|
|
@@ -24,6 +27,40 @@ Any other arguments are passed to the agent (e.g. ccnav --model opus).`);
|
|
|
24
27
|
process.exit(0);
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
// `ccnav here` never touches the session list: it starts a new one in this directory.
|
|
31
|
+
async function chooseAgent() {
|
|
32
|
+
if (!process.stdin.isTTY) {
|
|
33
|
+
console.error('ccnav here needs an interactive terminal, or name the agent: ccnav here --claude');
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
console.log(`\nNew session in ${process.cwd()}\n`);
|
|
37
|
+
AGENTS.forEach((a, i) => {
|
|
38
|
+
const p = PROVIDERS[a];
|
|
39
|
+
console.log(` ${i + 1}) ${p.glyph} ${a.padEnd(7)} ${p.command} ${p.AUTO_ARGS.join(' ')}`);
|
|
40
|
+
});
|
|
41
|
+
const answer = await askUser(`\nWhich agent? [1] `);
|
|
42
|
+
// EOF is not a choice: never start an unattended agent nobody actually asked for.
|
|
43
|
+
if (answer === null) { console.error('\nNo answer given, nothing started.'); process.exit(1); }
|
|
44
|
+
if (!answer) return AGENTS[0];
|
|
45
|
+
const byNumber = AGENTS[Number(answer) - 1];
|
|
46
|
+
if (byNumber) return byNumber;
|
|
47
|
+
const byName = AGENTS.find(a => a.startsWith(answer.toLowerCase()));
|
|
48
|
+
if (byName) return byName;
|
|
49
|
+
console.error(`Not an agent: ${answer}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (args[0] === 'here') {
|
|
54
|
+
const rest = args.slice(1);
|
|
55
|
+
const named = AGENTS.find(a => rest.includes(`--${a}`));
|
|
56
|
+
const extra = rest.filter(a => a !== '--' && !AGENTS.some(x => a === `--${x}`));
|
|
57
|
+
const provider = PROVIDERS[named || await chooseAgent()];
|
|
58
|
+
if (!await ensureAgent(provider.command)) process.exit(127);
|
|
59
|
+
const argv = [...provider.AUTO_ARGS, ...extra];
|
|
60
|
+
console.log(`\n\u2192 ${process.cwd()}\n\u2192 ${provider.command} ${argv.join(' ')}\n`);
|
|
61
|
+
process.exit(await launchAgent(provider.command, process.cwd(), argv));
|
|
62
|
+
}
|
|
63
|
+
|
|
27
64
|
if (args.includes('--install-shell')) {
|
|
28
65
|
const { shell, file, reload } = installShellFunction();
|
|
29
66
|
console.log(`ccnav ${shell} function written to ${file}\nto use it now: ${reload}`);
|
|
@@ -64,6 +101,9 @@ const { action, session } = choice;
|
|
|
64
101
|
const provider = providerOf(session);
|
|
65
102
|
const agentArgs = provider.launchArgs(action, session, passthrough);
|
|
66
103
|
|
|
104
|
+
// The agent may not be installed yet; offer to install it rather than just failing.
|
|
105
|
+
if (!await ensureAgent(provider.command)) process.exit(127);
|
|
106
|
+
|
|
67
107
|
reportCwd(session.cwd);
|
|
68
108
|
console.log(`\n→ ${session.cwd}\n→ ${provider.command} ${agentArgs.join(' ')}\n`);
|
|
69
109
|
process.exit(await launchAgent(provider.command, session.cwd, agentArgs));
|
package/package.json
CHANGED
package/src/actions.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
export { copyToClipboard, installShellFunction, launchAgent, openFolder, openInNewTab } from './platform.mjs';
|
|
3
|
+
export { askUser, copyToClipboard, ensureAgent, hasCommand, installShellFunction, launchAgent, openFolder, openInNewTab } from './platform.mjs';
|
|
4
4
|
|
|
5
5
|
// Sessions touched this recently may be open in another terminal.
|
|
6
6
|
export const LIVE_WINDOW_MS = 10 * 60 * 1000;
|
package/src/app.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { Box, Text, useApp, useInput, useStdout } from 'ink';
|
|
3
3
|
import { AGENTS, ago, humanSize, providerOf } from './sessions.mjs';
|
|
4
|
-
import { archiveSession, copyToClipboard, isMaybeLive, openFolder, openInNewTab, searchTranscript } from './actions.mjs';
|
|
4
|
+
import { archiveSession, copyToClipboard, hasCommand, isMaybeLive, openFolder, openInNewTab, searchTranscript } from './actions.mjs';
|
|
5
5
|
|
|
6
6
|
const h = React.createElement;
|
|
7
7
|
|
|
@@ -212,6 +212,11 @@ export function App({ sessions: initialSessions, onChoose, passthrough = [] }) {
|
|
|
212
212
|
else if (input === 't' && sel && requireDir()) {
|
|
213
213
|
try {
|
|
214
214
|
const p = providerOf(sel);
|
|
215
|
+
// A new tab has nowhere to report an install prompt, so send them to enter instead.
|
|
216
|
+
if (!hasCommand(p.command)) {
|
|
217
|
+
flash(`${p.command} is not installed — press enter to resume and ccnav will offer to install it.`, 'yellow');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
215
220
|
const where = openInNewTab(p.command, sel.cwd, p.launchArgs('resume', sel, passthrough), oneLine(sel.title) || sel.id);
|
|
216
221
|
flash(`Opened "${oneLine(sel.title) || sel.id}" in a new ${where}`);
|
|
217
222
|
} catch (e) { flash(`Could not open a new tab: ${e.message}`, 'red'); }
|
package/src/platform.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import readline from 'node:readline';
|
|
5
6
|
import { spawn, spawnSync } from 'node:child_process';
|
|
6
7
|
|
|
7
8
|
export const IS_WIN = process.platform === 'win32';
|
|
@@ -11,16 +12,20 @@ const has = cmd => IS_WIN
|
|
|
11
12
|
? spawnSync('where', [cmd], { stdio: 'ignore' }).status === 0
|
|
12
13
|
: spawnSync('sh', ['-c', `command -v ${cmd}`], { stdio: 'ignore' }).status === 0;
|
|
13
14
|
|
|
15
|
+
// Exported so the picker can warn before opening a tab that would only fail.
|
|
16
|
+
export const hasCommand = has;
|
|
17
|
+
|
|
14
18
|
// POSIX single-quote escaping for building `sh -c` / AppleScript command lines.
|
|
15
19
|
const shq = s => `'${String(s).replace(/'/g, `'\\''`)}'`;
|
|
16
20
|
// Windows command lines: quote args with spaces, strip embedded quotes (args are UUIDs and flags).
|
|
17
21
|
const winq = s => /[\s"]/.test(s) ? `"${String(s).replace(/"/g, '')}"` : s;
|
|
18
22
|
const osaString = s => `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
19
23
|
|
|
20
|
-
const
|
|
21
|
-
claude: '
|
|
22
|
-
codex: '
|
|
24
|
+
export const INSTALL_PKG = {
|
|
25
|
+
claude: '@anthropic-ai/claude-code',
|
|
26
|
+
codex: '@openai/codex',
|
|
23
27
|
};
|
|
28
|
+
const hint = cmd => INSTALL_PKG[cmd] ? ` Install it: npm install -g ${INSTALL_PKG[cmd]}` : '';
|
|
24
29
|
|
|
25
30
|
// Runs the agent in the foreground with the session's directory as cwd; resolves with its exit code.
|
|
26
31
|
export function launchAgent(cmd, cwd, args) {
|
|
@@ -31,7 +36,7 @@ export function launchAgent(cmd, cwd, args) {
|
|
|
31
36
|
: spawn(cmd, args, { cwd, stdio: 'inherit' });
|
|
32
37
|
child.on('error', e => {
|
|
33
38
|
console.error(e.code === 'ENOENT'
|
|
34
|
-
? `${cmd} was not found on PATH.${
|
|
39
|
+
? `${cmd} was not found on PATH.${hint(cmd)}`
|
|
35
40
|
: `could not start ${cmd}: ${e.message}`);
|
|
36
41
|
resolve(127);
|
|
37
42
|
});
|
|
@@ -39,13 +44,85 @@ export function launchAgent(cmd, cwd, args) {
|
|
|
39
44
|
});
|
|
40
45
|
}
|
|
41
46
|
|
|
47
|
+
// ---- installing a missing agent ----
|
|
48
|
+
|
|
49
|
+
// Resolves the trimmed answer, or null when stdin closed without one (ctrl-D, a closed pipe).
|
|
50
|
+
// Resolving on 'close' rather than only in the callback keeps the promise from hanging on EOF.
|
|
51
|
+
export function askUser(question) {
|
|
52
|
+
return new Promise(resolve => {
|
|
53
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
54
|
+
let answer = null;
|
|
55
|
+
rl.question(question, a => { answer = String(a).trim().toLowerCase(); rl.close(); });
|
|
56
|
+
rl.on('close', () => resolve(answer));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Offers to install the agent when it is missing. Resolves true if it is on PATH afterwards.
|
|
61
|
+
// The agent's own first-run login/setup then runs as usual when it launches.
|
|
62
|
+
export async function ensureAgent(cmd) {
|
|
63
|
+
if (has(cmd)) return true;
|
|
64
|
+
|
|
65
|
+
const pkg = INSTALL_PKG[cmd];
|
|
66
|
+
if (!pkg) {
|
|
67
|
+
console.error(`${cmd} was not found on PATH.`);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// Without a terminal there is nobody to answer the prompt (e.g. piped output).
|
|
71
|
+
if (!process.stdin.isTTY) {
|
|
72
|
+
console.error(`${cmd} is not installed.${hint(cmd)}`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (!has('npm')) {
|
|
76
|
+
console.error(`${cmd} is not installed, and npm is not available to install it.`);
|
|
77
|
+
console.error(`Install Node.js 20 or later, then run: npm install -g ${pkg}`);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const answer = await askUser(`\n${cmd} is not installed. Install it now with "npm install -g ${pkg}"? (y/N) `);
|
|
82
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
83
|
+
console.log(`Skipped. You can install it later: npm install -g ${pkg}`);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log(`\nnpm install -g ${pkg}\n`);
|
|
88
|
+
const r = spawnSync(IS_WIN ? 'npm.cmd' : 'npm', ['install', '-g', pkg], { stdio: 'inherit', shell: IS_WIN });
|
|
89
|
+
if (r.error || r.status !== 0) {
|
|
90
|
+
console.error(`\nInstall failed${r.error ? `: ${r.error.message}` : ` (npm exited with ${r.status})`}.`);
|
|
91
|
+
console.error(`If it was a permissions error, npm's global folder is not writable by you.`);
|
|
92
|
+
console.error(`Check where it points with: npm prefix -g`);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (!has(cmd)) {
|
|
96
|
+
console.error(`\n${pkg} installed, but "${cmd}" is still not on PATH.`);
|
|
97
|
+
console.error(`npm's global bin folder is probably not in PATH. Find it with: npm prefix -g`);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
console.log(`\n${cmd} installed.\n`);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---- new terminal tabs ----
|
|
105
|
+
|
|
106
|
+
const run = (cmd, argv) => {
|
|
107
|
+
const r = spawnSync(cmd, argv, { encoding: 'utf8' });
|
|
108
|
+
if (r.error) throw new Error(r.error.message);
|
|
109
|
+
if (r.status !== 0) {
|
|
110
|
+
// Keep the tool's own message: AppleScript errors are the only clue to a denied permission.
|
|
111
|
+
const detail = String(r.stderr || '').trim().split('\n').find(Boolean);
|
|
112
|
+
throw new Error(detail || `${cmd} exited with ${r.status}`);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// macOS asks for Automation permission the first time; a denial comes back as -1743.
|
|
117
|
+
function macAutomationError(e, app) {
|
|
118
|
+
const msg = e?.message || String(e);
|
|
119
|
+
return /-1743|not authoriz/i.test(msg)
|
|
120
|
+
? `macOS blocked ccnav from controlling ${app}. Allow your terminal under System Settings → Privacy & Security → Automation, then try again.`
|
|
121
|
+
: msg;
|
|
122
|
+
}
|
|
123
|
+
|
|
42
124
|
// Opens a new terminal tab (or window) in `cwd` running `cmd` with `args`. Throws if nothing worked.
|
|
43
125
|
export function openInNewTab(cmd, cwd, args, title = cmd) {
|
|
44
|
-
const run = (cmd, argv) => {
|
|
45
|
-
const r = spawnSync(cmd, argv, { stdio: 'ignore' });
|
|
46
|
-
if (r.error || r.status !== 0) throw new Error(`${cmd} failed${r.error ? `: ${r.error.message}` : ` (exit ${r.status})`}`);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
126
|
if (IS_WIN) {
|
|
50
127
|
const argv = ['-w', '0', 'new-tab', '--title', title.replace(/[;"]/g, ' ').slice(0, 40), '-d', cwd,
|
|
51
128
|
'pwsh', '-NoExit', '-Command', cmd, ...args];
|
|
@@ -68,15 +145,27 @@ export function openInNewTab(cmd, cwd, args, title = cmd) {
|
|
|
68
145
|
}
|
|
69
146
|
if (IS_MAC) {
|
|
70
147
|
if (term === 'iTerm.app') {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
'-e',
|
|
74
|
-
|
|
148
|
+
try {
|
|
149
|
+
// A tab needs a window to live in: iTerm can be running with none open.
|
|
150
|
+
run('osascript', ['-e', [
|
|
151
|
+
'tell application "iTerm"',
|
|
152
|
+
' activate',
|
|
153
|
+
' if (count of windows) = 0 then',
|
|
154
|
+
' create window with default profile',
|
|
155
|
+
' else',
|
|
156
|
+
' tell current window to create tab with default profile',
|
|
157
|
+
' end if',
|
|
158
|
+
` tell current session of current window to write text ${osaString(shellCmd)}`,
|
|
159
|
+
'end tell',
|
|
160
|
+
].join('\n')]);
|
|
161
|
+
} catch (e) { throw new Error(macAutomationError(e, 'iTerm')); }
|
|
75
162
|
return 'iTerm tab';
|
|
76
163
|
}
|
|
77
164
|
// Terminal.app (and anything else): a new Terminal window. Real tabs would need Accessibility permission.
|
|
78
|
-
|
|
79
|
-
'-e',
|
|
165
|
+
try {
|
|
166
|
+
run('osascript', ['-e', `tell application "Terminal" to do script ${osaString(shellCmd)}`,
|
|
167
|
+
'-e', 'tell application "Terminal" to activate']);
|
|
168
|
+
} catch (e) { throw new Error(macAutomationError(e, 'Terminal')); }
|
|
80
169
|
return 'Terminal window';
|
|
81
170
|
}
|
|
82
171
|
if (has('gnome-terminal')) {
|
package/src/providers/claude.mjs
CHANGED
|
@@ -8,6 +8,8 @@ export const agent = 'claude';
|
|
|
8
8
|
export const command = 'claude';
|
|
9
9
|
export const glyph = '◆';
|
|
10
10
|
export const color = 'cyan';
|
|
11
|
+
// `ccnav here`: a fresh session that never stops to ask for permission.
|
|
12
|
+
export const AUTO_ARGS = ['--dangerously-skip-permissions'];
|
|
11
13
|
|
|
12
14
|
export const HOME_DIR = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
13
15
|
export const SESSIONS_ROOT = path.join(HOME_DIR, 'projects');
|
package/src/providers/codex.mjs
CHANGED
|
@@ -8,6 +8,9 @@ export const agent = 'codex';
|
|
|
8
8
|
export const command = 'codex';
|
|
9
9
|
export const glyph = '●';
|
|
10
10
|
export const color = 'green';
|
|
11
|
+
// `ccnav here`: codex removed --full-auto in 0.156, so the policy is named directly.
|
|
12
|
+
// This is the counterpart to Claude's --dangerously-skip-permissions: no approvals, no sandbox.
|
|
13
|
+
export const AUTO_ARGS = ['--dangerously-bypass-approvals-and-sandbox'];
|
|
11
14
|
|
|
12
15
|
export const HOME_DIR = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
|
|
13
16
|
export const SESSIONS_ROOT = path.join(HOME_DIR, 'sessions');
|