@atollhq/skill-codex 0.4.22 → 0.4.24
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 +22 -8
- package/bin/install.mjs +179 -43
- package/package.json +1 -1
- package/skill/SKILL.md +263 -5
- package/skill/agents/openai.yaml +11 -0
- package/skill/references/api-endpoints.md +118 -17
- package/skill/references/api-fields.md +184 -5
package/README.md
CHANGED
|
@@ -6,13 +6,27 @@ Gives your Codex agent the ability to manage tasks, goals, KPIs, initiatives, mi
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
Install or refresh the local skill files without configuring credentials:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx --yes @atollhq/skill-codex@latest
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Bare invocation and `--install-only` only update `~/.codex/skills/atoll/`, the profile-neutral routing hint, and compatibility references. They do not inspect or change Atoll credentials, profiles, shell files, or repo configuration, even when `ATOLL_*` variables are present.
|
|
16
|
+
|
|
17
|
+
Configure a named profile explicitly:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx --yes @atollhq/skill-codex@latest --profile agent-a --key sk_atoll_... --org org-uuid --project project-id --team team-id
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For intentional environment configuration, use `--configure`:
|
|
24
|
+
|
|
9
25
|
```bash
|
|
10
|
-
npx @atollhq/skill-codex@latest --
|
|
11
|
-
# or
|
|
12
|
-
ATOLL_API_KEY=sk_atoll_... ATOLL_ORG_ID=your-org-id npx @atollhq/skill-codex@latest
|
|
26
|
+
ATOLL_API_KEY=sk_atoll_... ATOLL_ORG_ID=org-uuid npx --yes @atollhq/skill-codex@latest --configure
|
|
13
27
|
```
|
|
14
28
|
|
|
15
|
-
|
|
29
|
+
Configuration mode supports `--project`, `--team`, and `--base-url`. Use `--no-project`, `--no-team`, or `--no-base-url` to clear saved defaults. Pass `--profile` to store credentials and defaults only in that named Atoll CLI profile. The installer does not write global `ATOLL_*` exports in profile mode. Use `atoll --profile agent-a ...` for profile-scoped commands.
|
|
16
30
|
|
|
17
31
|
For multi-org setups, keep global Codex instructions neutral and bind profiles per repo:
|
|
18
32
|
|
|
@@ -23,18 +37,18 @@ npx @atollhq/skill-codex@latest --profile agent-a --key sk_atoll_... --org your-
|
|
|
23
37
|
|
|
24
38
|
`--write-project-instructions` writes a small managed block to repo-local instruction files. It requires `--profile`, defaults to the current directory, writes `AGENTS.md` by default, and can also write `CLAUDE.md` with `--instruction-files agents,claude`. If the target file already has unmanaged Atoll profile guidance, pass `--force-project-instructions` after reviewing the replacement.
|
|
25
39
|
|
|
26
|
-
Get an agent API key from **Agents** in the Atoll app. Integration keys are
|
|
40
|
+
Get an agent API key from **Agents** in the Atoll app. Integration keys are managed from **Settings > Integrations**.
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
Every invocation installs the local skill assets, routing hint, and compatibility references. Configuration mode additionally:
|
|
29
43
|
|
|
30
44
|
1. Installs the `atoll` skill to `~/.codex/skills/atoll/`
|
|
31
45
|
2. Appends (or updates) a small profile-neutral Atoll skill routing hint in `~/.codex/AGENTS.md`
|
|
32
46
|
3. Copies API reference files to `~/.codex/atoll-references/`
|
|
33
47
|
4. Creates or updates the named Atoll CLI profile when `--profile` is provided
|
|
34
|
-
5. Appends Atoll env var exports to your shell profile (`~/.zshrc` or `~/.bashrc`)
|
|
48
|
+
5. Appends Atoll env var exports to your shell profile (`~/.zshrc` or `~/.bashrc`) when `--configure` is used without a profile
|
|
35
49
|
6. Optionally writes repo-local profile guidance when `--write-project-instructions` is provided
|
|
36
50
|
|
|
37
|
-
For profile mode, Codex has the Atoll skill immediately, global Codex guidance points to that skill without embedding the full Atoll guide, and terminal commands can use `atoll --profile agent-a ...`. For env-var mode, the installer writes `ATOLL_ENV_MODE=1` with the credential exports; open a fresh shell or `source` your profile.
|
|
51
|
+
For profile mode, Codex has the Atoll skill immediately, global Codex guidance points to that skill without embedding the full Atoll guide, and terminal commands can use `atoll --profile agent-a ...`. For explicitly requested env-var mode, the installer writes `ATOLL_ENV_MODE=1` with the credential exports; open a fresh shell or `source` your profile.
|
|
38
52
|
|
|
39
53
|
Use `@latest` in the `npx` command so npm does not reuse a stale cached installer. In profile mode, the installer prints its package version and a verification command; run `atoll --profile agent-a agent-context --json` if you need to confirm the profile was created.
|
|
40
54
|
|
package/bin/install.mjs
CHANGED
|
@@ -22,45 +22,86 @@ import { fileURLToPath } from 'node:url'
|
|
|
22
22
|
|
|
23
23
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
24
24
|
const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))
|
|
25
|
+
const configurationFlags = new Set([
|
|
26
|
+
'--configure',
|
|
27
|
+
'--profile',
|
|
28
|
+
'--key',
|
|
29
|
+
'--org',
|
|
30
|
+
'--project',
|
|
31
|
+
'--no-project',
|
|
32
|
+
'--team',
|
|
33
|
+
'--no-team',
|
|
34
|
+
'--base-url',
|
|
35
|
+
'--no-base-url',
|
|
36
|
+
'--write-project-instructions',
|
|
37
|
+
'--project-dir',
|
|
38
|
+
'--instruction-files',
|
|
39
|
+
'--force-project-instructions',
|
|
40
|
+
])
|
|
41
|
+
const valueFlags = {
|
|
42
|
+
'--profile': 'profile',
|
|
43
|
+
'--key': 'key',
|
|
44
|
+
'--org': 'org',
|
|
45
|
+
'--project': 'project',
|
|
46
|
+
'--team': 'team',
|
|
47
|
+
'--base-url': 'baseUrl',
|
|
48
|
+
'--project-dir': 'projectDir',
|
|
49
|
+
'--instruction-files': 'instructionFiles',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function hasConfigurationIntent(argv) {
|
|
53
|
+
return argv.slice(2).some((arg) => configurationFlags.has(arg.split('=', 1)[0]))
|
|
54
|
+
}
|
|
25
55
|
|
|
26
56
|
function parseArgs(argv) {
|
|
27
57
|
const args = {}
|
|
28
58
|
for (let i = 2; i < argv.length; i++) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
else if (
|
|
42
|
-
else if (
|
|
59
|
+
const argument = argv[i]
|
|
60
|
+
const separator = argument.indexOf('=')
|
|
61
|
+
const flag = separator === -1 ? argument : argument.slice(0, separator)
|
|
62
|
+
const inlineValue = separator === -1 ? undefined : argument.slice(separator + 1)
|
|
63
|
+
const nextValue = inlineValue ?? argv[i + 1]
|
|
64
|
+
const valueKey = valueFlags[flag]
|
|
65
|
+
if (valueKey) {
|
|
66
|
+
if (!nextValue || nextValue.startsWith('-')) {
|
|
67
|
+
throw new Error(`${flag} requires a non-empty value`)
|
|
68
|
+
}
|
|
69
|
+
args[valueKey] = nextValue
|
|
70
|
+
if (inlineValue === undefined) i++
|
|
71
|
+
} else if (flag === '--no-project') args.clearProject = true
|
|
72
|
+
else if (flag === '--no-team') args.clearTeam = true
|
|
73
|
+
else if (flag === '--no-base-url') args.clearBaseUrl = true
|
|
74
|
+
else if (flag === '--install-only') args.installOnly = true
|
|
75
|
+
else if (flag === '--configure') args.configure = true
|
|
76
|
+
else if (flag === '--write-project-instructions') args.writeProjectInstructions = true
|
|
77
|
+
else if (flag === '--force-project-instructions') args.forceProjectInstructions = true
|
|
78
|
+
else if (flag === '--help' || flag === '-h') args.help = true
|
|
79
|
+
else throw new Error(`Unknown option: ${flag}`)
|
|
43
80
|
}
|
|
44
81
|
return args
|
|
45
82
|
}
|
|
46
83
|
|
|
47
84
|
function printUsage() {
|
|
48
85
|
console.log(`
|
|
49
|
-
Usage: npx @atollhq/skill-codex@latest
|
|
50
|
-
or:
|
|
86
|
+
Usage: npx @atollhq/skill-codex@latest --install-only
|
|
87
|
+
or: npx @atollhq/skill-codex@latest --configure [--profile <name>] --key <api-key> --org <org-id> [--project <id>] [--team <id-or-slug>] [--base-url <url>]
|
|
88
|
+
or: ATOLL_API_KEY=<api-key> ATOLL_ORG_ID=<org-id> npx @atollhq/skill-codex@latest --configure
|
|
51
89
|
|
|
52
90
|
Options:
|
|
53
|
-
--profile Atoll CLI profile name to create/update.
|
|
54
|
-
--key Atoll API key (sk_atoll_...).
|
|
55
|
-
--org Organization ID.
|
|
56
|
-
--project Default project ID.
|
|
91
|
+
--profile Atoll CLI profile name to create/update. Enables configuration mode.
|
|
92
|
+
--key Atoll API key (sk_atoll_...). Used in configuration mode; defaults to ATOLL_API_KEY.
|
|
93
|
+
--org Organization ID. Used in configuration mode; defaults to ATOLL_ORG_ID.
|
|
94
|
+
--project Default project ID. Used in configuration mode; defaults to ATOLL_PROJECT.
|
|
57
95
|
--no-project Clear any saved default project.
|
|
58
|
-
--team Default team ID or slug.
|
|
96
|
+
--team Default team ID or slug. Used in configuration mode; defaults to ATOLL_TEAM.
|
|
59
97
|
--no-team Clear any saved default team.
|
|
60
|
-
--base-url Atoll base URL.
|
|
98
|
+
--base-url Atoll base URL. Used in configuration mode; defaults to ATOLL_BASE_URL.
|
|
61
99
|
--no-base-url Clear any saved custom base URL.
|
|
100
|
+
--install-only
|
|
101
|
+
Install or refresh local skill files without reading or changing credentials.
|
|
102
|
+
--configure Explicitly enable profile or environment credential configuration.
|
|
62
103
|
--write-project-instructions
|
|
63
|
-
Write a small repo-local Atoll profile block. Requires --profile.
|
|
104
|
+
Write a small repo-local Atoll profile block. Requires --configure and --profile.
|
|
64
105
|
--project-dir
|
|
65
106
|
Directory for repo-local instructions. Defaults to the current directory.
|
|
66
107
|
--instruction-files
|
|
@@ -72,24 +113,35 @@ Options:
|
|
|
72
113
|
Installs the Atoll integration for Codex CLI:
|
|
73
114
|
- Installs the atoll skill to ~/.codex/skills/atoll/
|
|
74
115
|
- Writes a small AGENTS.md routing hint to ~/.codex/
|
|
75
|
-
-
|
|
116
|
+
- Leaves authentication and profile configuration unchanged by default
|
|
117
|
+
- Creates/updates credentials only when --configure or another configuration flag is provided
|
|
76
118
|
- Optionally writes repo-local AGENTS.md/CLAUDE.md profile guidance with --write-project-instructions
|
|
77
|
-
- Otherwise writes Atoll env vars to your shell profile for env-var mode
|
|
78
119
|
`)
|
|
79
120
|
}
|
|
80
121
|
|
|
81
122
|
const args = parseArgs(process.argv)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (args.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
else args.baseUrl ??= process.env.ATOLL_BASE_URL
|
|
123
|
+
const configurationIntent = hasConfigurationIntent(process.argv)
|
|
124
|
+
|
|
125
|
+
if (args.installOnly && configurationIntent) {
|
|
126
|
+
console.error('Error: --install-only cannot be combined with credential or profile configuration flags')
|
|
127
|
+
printUsage()
|
|
128
|
+
process.exit(1)
|
|
129
|
+
}
|
|
90
130
|
|
|
91
131
|
if (args.help) { printUsage(); process.exit(0) }
|
|
92
132
|
|
|
133
|
+
const configureMode = configurationIntent
|
|
134
|
+
if (configureMode) {
|
|
135
|
+
args.key ??= process.env.ATOLL_API_KEY
|
|
136
|
+
args.org ??= process.env.ATOLL_ORG_ID
|
|
137
|
+
if (args.clearProject) delete args.project
|
|
138
|
+
else args.project ??= process.env.ATOLL_PROJECT
|
|
139
|
+
if (args.clearTeam) delete args.team
|
|
140
|
+
else args.team ??= process.env.ATOLL_TEAM
|
|
141
|
+
if (args.clearBaseUrl) delete args.baseUrl
|
|
142
|
+
else args.baseUrl ??= process.env.ATOLL_BASE_URL
|
|
143
|
+
}
|
|
144
|
+
|
|
93
145
|
console.log(`Running @atollhq/skill-codex installer v${packageJson.version}`)
|
|
94
146
|
|
|
95
147
|
const instructionFileChoices = (args.instructionFiles ?? 'agents')
|
|
@@ -118,13 +170,13 @@ if (args.writeProjectInstructions && !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(args.p
|
|
|
118
170
|
process.exit(1)
|
|
119
171
|
}
|
|
120
172
|
|
|
121
|
-
if (!args.key || !args.org) {
|
|
173
|
+
if (configureMode && (!args.key || !args.org)) {
|
|
122
174
|
console.error('Error: provide --key and --org, or set ATOLL_API_KEY and ATOLL_ORG_ID\n')
|
|
123
175
|
printUsage()
|
|
124
176
|
process.exit(1)
|
|
125
177
|
}
|
|
126
178
|
|
|
127
|
-
if (!args.key.startsWith('sk_atoll_')) {
|
|
179
|
+
if (configureMode && !args.key.startsWith('sk_atoll_')) {
|
|
128
180
|
console.error('Error: API key must start with sk_atoll_')
|
|
129
181
|
process.exit(1)
|
|
130
182
|
}
|
|
@@ -164,6 +216,52 @@ function ensurePrivateDirectory(path) {
|
|
|
164
216
|
chmodSync(path, 0o700)
|
|
165
217
|
}
|
|
166
218
|
|
|
219
|
+
function prepareSkillDirectory(path, source) {
|
|
220
|
+
const existing = lstatIfExists(path)
|
|
221
|
+
if (existing) {
|
|
222
|
+
if (existing.isSymbolicLink()) {
|
|
223
|
+
throw new Error(`Refusing symbolic link for skill directory: ${path}`)
|
|
224
|
+
}
|
|
225
|
+
if (!existing.isDirectory()) {
|
|
226
|
+
throw new Error(`Refusing non-directory skill path: ${path}`)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const parent = dirname(path)
|
|
231
|
+
mkdirSync(parent, { recursive: true })
|
|
232
|
+
const tempPath = join(parent, `.${basename(path)}.atoll-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`)
|
|
233
|
+
const backupPath = `${tempPath}.backup`
|
|
234
|
+
let movedExisting = false
|
|
235
|
+
let installed = false
|
|
236
|
+
try {
|
|
237
|
+
cpSync(source, tempPath, { recursive: true })
|
|
238
|
+
|
|
239
|
+
const current = lstatIfExists(path)
|
|
240
|
+
if (current) {
|
|
241
|
+
if (current.isSymbolicLink()) {
|
|
242
|
+
throw new Error(`Refusing symbolic link for skill directory: ${path}`)
|
|
243
|
+
}
|
|
244
|
+
if (!current.isDirectory()) {
|
|
245
|
+
throw new Error(`Refusing non-directory skill path: ${path}`)
|
|
246
|
+
}
|
|
247
|
+
renameSync(path, backupPath)
|
|
248
|
+
movedExisting = true
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
renameSync(tempPath, path)
|
|
252
|
+
installed = true
|
|
253
|
+
if (movedExisting) rmSync(backupPath, { recursive: true, force: true })
|
|
254
|
+
} catch (error) {
|
|
255
|
+
if (!installed) {
|
|
256
|
+
if (lstatIfExists(tempPath)) rmSync(tempPath, { recursive: true, force: true })
|
|
257
|
+
if (movedExisting && !lstatIfExists(path) && lstatIfExists(backupPath)) {
|
|
258
|
+
renameSync(backupPath, path)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
throw error
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
167
265
|
function readJson(path) {
|
|
168
266
|
assertSafeFile(path)
|
|
169
267
|
if (!existsSync(path)) return {}
|
|
@@ -198,10 +296,27 @@ function writePrivateFile(path, content) {
|
|
|
198
296
|
}
|
|
199
297
|
}
|
|
200
298
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
299
|
+
const codexDir = resolve(process.env.CODEX_HOME || join(homedir(), '.codex'))
|
|
300
|
+
const defaultInstallerLockTarget = join(homedir(), '.atoll', 'installer.lock-target')
|
|
301
|
+
const sharedInstallerLockTarget = join(
|
|
302
|
+
homedir(),
|
|
303
|
+
'.atoll-skill-installer.lock-target',
|
|
304
|
+
)
|
|
305
|
+
const codexSkillLockTarget = join(codexDir, '.atoll-skill-installer.lock-target')
|
|
306
|
+
|
|
307
|
+
function ensureLockDirectory(path) {
|
|
308
|
+
const entry = lstatIfExists(path)
|
|
309
|
+
if (entry) {
|
|
310
|
+
const stat = entry.isSymbolicLink() ? statSync(path) : entry
|
|
311
|
+
if (!stat.isDirectory()) throw new Error(`Refusing non-directory installer lock path: ${path}`)
|
|
312
|
+
} else {
|
|
313
|
+
mkdirSync(path, { recursive: true, mode: 0o700 })
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function acquireInstallerLock(target = defaultInstallerLockTarget) {
|
|
318
|
+
if (target === defaultInstallerLockTarget) ensurePrivateDirectory(dirname(target))
|
|
319
|
+
else ensureLockDirectory(dirname(target))
|
|
205
320
|
try {
|
|
206
321
|
writeFileSync(target, '', { flag: 'wx', mode: 0o600 })
|
|
207
322
|
} catch (error) {
|
|
@@ -257,7 +372,23 @@ function acquireInstallerLock() {
|
|
|
257
372
|
}
|
|
258
373
|
}
|
|
259
374
|
|
|
260
|
-
const
|
|
375
|
+
const releaseSharedInstallerLock = acquireInstallerLock(sharedInstallerLockTarget)
|
|
376
|
+
let releaseCodexSkillInstallerLock = null
|
|
377
|
+
let releaseConfigInstallerLock = null
|
|
378
|
+
try {
|
|
379
|
+
releaseCodexSkillInstallerLock = acquireInstallerLock(codexSkillLockTarget)
|
|
380
|
+
if (configureMode) releaseConfigInstallerLock = acquireInstallerLock()
|
|
381
|
+
} catch (error) {
|
|
382
|
+
releaseCodexSkillInstallerLock?.()
|
|
383
|
+
releaseSharedInstallerLock()
|
|
384
|
+
throw error
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function releaseInstallerLock() {
|
|
388
|
+
releaseConfigInstallerLock?.()
|
|
389
|
+
releaseCodexSkillInstallerLock?.()
|
|
390
|
+
releaseSharedInstallerLock()
|
|
391
|
+
}
|
|
261
392
|
|
|
262
393
|
function writeAtollProfile() {
|
|
263
394
|
if (!args.profile) return null
|
|
@@ -422,14 +553,12 @@ function writeGlobalInstructions(targetPath) {
|
|
|
422
553
|
}
|
|
423
554
|
|
|
424
555
|
// 1. Install the Codex skill to ~/.codex/skills/atoll/
|
|
425
|
-
const codexDir = join(homedir(), '.codex')
|
|
426
556
|
mkdirSync(codexDir, { recursive: true })
|
|
427
557
|
|
|
428
558
|
const skillDir = join(__dirname, '..', 'skill')
|
|
429
559
|
const skillDest = join(codexDir, 'skills', 'atoll')
|
|
430
560
|
const legacySkillDest = join(codexDir, 'skills', 'atoll-api')
|
|
431
|
-
|
|
432
|
-
cpSync(skillDir, skillDest, { recursive: true })
|
|
561
|
+
prepareSkillDirectory(skillDest, skillDir)
|
|
433
562
|
console.log(`Installed Atoll skill to ${skillDest}`)
|
|
434
563
|
if (existsSync(legacySkillDest)) {
|
|
435
564
|
rmSync(legacySkillDest, { recursive: true, force: true })
|
|
@@ -449,6 +578,13 @@ for (const file of ['api-endpoints.md', 'api-fields.md']) {
|
|
|
449
578
|
}
|
|
450
579
|
console.log(`Copied API references to ${refsDir}`)
|
|
451
580
|
|
|
581
|
+
if (!configureMode) {
|
|
582
|
+
console.log('Authentication/profile configuration left unchanged.')
|
|
583
|
+
console.log('\nDone! Codex has the Atoll skill installed.')
|
|
584
|
+
releaseInstallerLock()
|
|
585
|
+
process.exit(0)
|
|
586
|
+
}
|
|
587
|
+
|
|
452
588
|
const shell = process.env.SHELL || '/bin/bash'
|
|
453
589
|
const profilePath = shell.includes('zsh')
|
|
454
590
|
? join(homedir(), '.zshrc')
|