@atollhq/skill-codex 0.4.8 → 0.4.10

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 CHANGED
@@ -7,25 +7,37 @@ Gives your Codex agent the ability to manage tasks, goals, KPIs, initiatives, mi
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npx @atollhq/skill-codex --profile agent-a --key sk_atoll_... --org your-org-id --project project-id --team team-id
10
+ npx @atollhq/skill-codex@latest --profile agent-a --key sk_atoll_... --org your-org-id --project project-id --team team-id
11
11
  # or
12
- ATOLL_API_KEY=sk_atoll_... ATOLL_ORG_ID=your-org-id npx @atollhq/skill-codex
12
+ ATOLL_API_KEY=sk_atoll_... ATOLL_ORG_ID=your-org-id npx @atollhq/skill-codex@latest
13
13
  ```
14
14
 
15
15
  Optional defaults: `--project`, `--team`, and `--base-url` are stored with the selected mode. Use `--no-project`, `--no-team`, or `--no-base-url` to clear previously saved defaults. Pass `--profile` to store credentials and defaults only in that named Atoll CLI profile. The installer does not write global `ATOLL_*` exports to `~/.zshrc` or `~/.bashrc` in profile mode. Use `atoll --profile agent-a ...` for profile-scoped commands, or omit `--profile` to use env-var mode.
16
16
 
17
+ For multi-org setups, keep global Codex instructions neutral and bind profiles per repo:
18
+
19
+ ```bash
20
+ npx @atollhq/skill-codex@latest --profile agent-a --key sk_atoll_... --org your-org-id \
21
+ --write-project-instructions --project-dir /path/to/repo --instruction-files agents,claude
22
+ ```
23
+
24
+ `--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
+
17
26
  Get an agent API key from **Agents** in the Atoll app. Integration keys are still managed from **Settings > Members**.
18
27
 
19
- This does five things:
28
+ This does six things:
20
29
 
21
30
  1. Installs the `atoll-api` skill to `~/.codex/skills/atoll-api/`
22
- 2. Appends (or updates) an `# Atoll Integration` section in `~/.codex/AGENTS.md`
31
+ 2. Appends (or updates) a neutral `# Atoll Integration` section in `~/.codex/AGENTS.md`
23
32
  3. Copies API reference files to `~/.codex/atoll-references/`
24
33
  4. Creates or updates the named Atoll CLI profile when `--profile` is provided
25
34
  5. Appends Atoll env var exports to your shell profile (`~/.zshrc` or `~/.bashrc`) only when no profile is provided
35
+ 6. Optionally writes repo-local profile guidance when `--write-project-instructions` is provided
26
36
 
27
37
  For profile mode, Codex has the Atoll skill immediately 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.
28
38
 
39
+ 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
+
29
41
  ## Using the integration
30
42
 
31
43
  Once installed, ask Codex anything task-related:
@@ -46,9 +58,9 @@ For terminal-first work, see [`@atollhq/cli`](https://www.npmjs.com/package/@ato
46
58
  npm install -g @atollhq/cli
47
59
  atoll auth login --profile agent-a --key sk_atoll_... --org-id org-uuid --project project-id --team team-id
48
60
  atoll auth login --profile agent-a --key sk_atoll_... --org-id org-uuid --project project-id --no-team --no-base-url
49
- atoll heartbeat
50
- atoll issue list --json
51
- atoll agent-context
61
+ atoll --profile agent-a heartbeat
62
+ atoll --profile agent-a issue list --json
63
+ atoll --profile agent-a agent-context
52
64
  ```
53
65
 
54
66
  For multiple agents or orgs, use CLI auth profiles:
package/bin/install.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs'
4
- import { join, dirname } from 'node:path'
3
+ import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, statSync, renameSync, rmSync } from 'node:fs'
4
+ import { join, dirname, resolve, basename } from 'node:path'
5
5
  import { homedir } from 'node:os'
6
6
  import { fileURLToPath } from 'node:url'
7
7
 
8
8
  const __dirname = dirname(fileURLToPath(import.meta.url))
9
+ const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'))
9
10
 
10
11
  function parseArgs(argv) {
11
12
  const args = {}
@@ -19,6 +20,10 @@ function parseArgs(argv) {
19
20
  else if (argv[i] === '--no-team') args.clearTeam = true
20
21
  else if (argv[i] === '--base-url' && argv[i + 1]) args.baseUrl = argv[++i]
21
22
  else if (argv[i] === '--no-base-url') args.clearBaseUrl = true
23
+ else if (argv[i] === '--write-project-instructions') args.writeProjectInstructions = true
24
+ else if (argv[i] === '--project-dir' && argv[i + 1]) args.projectDir = argv[++i]
25
+ else if (argv[i] === '--instruction-files' && argv[i + 1]) args.instructionFiles = argv[++i]
26
+ else if (argv[i] === '--force-project-instructions') args.forceProjectInstructions = true
22
27
  else if (argv[i] === '--help' || argv[i] === '-h') args.help = true
23
28
  }
24
29
  return args
@@ -26,8 +31,8 @@ function parseArgs(argv) {
26
31
 
27
32
  function printUsage() {
28
33
  console.log(`
29
- Usage: npx @atollhq/skill-codex [--profile <name>] --key <api-key> --org <org-id> [--project <id>] [--team <id-or-slug>] [--base-url <url>]
30
- or: ATOLL_API_KEY=<api-key> ATOLL_ORG_ID=<org-id> npx @atollhq/skill-codex
34
+ Usage: npx @atollhq/skill-codex@latest [--profile <name>] --key <api-key> --org <org-id> [--project <id>] [--team <id-or-slug>] [--base-url <url>]
35
+ or: ATOLL_API_KEY=<api-key> ATOLL_ORG_ID=<org-id> npx @atollhq/skill-codex@latest
31
36
 
32
37
  Options:
33
38
  --profile Atoll CLI profile name to create/update. Profile mode does not write shell env vars.
@@ -39,12 +44,21 @@ Options:
39
44
  --no-team Clear any saved default team.
40
45
  --base-url Atoll base URL. Defaults to ATOLL_BASE_URL.
41
46
  --no-base-url Clear any saved custom base URL.
47
+ --write-project-instructions
48
+ Write a small repo-local Atoll profile block. Requires --profile.
49
+ --project-dir
50
+ Directory for repo-local instructions. Defaults to the current directory.
51
+ --instruction-files
52
+ Comma-separated files to update: agents, claude, or agents,claude. Defaults to agents.
53
+ --force-project-instructions
54
+ Replace existing unmanaged Atoll profile guidance in repo-local instruction files.
42
55
  --help Show this help message
43
56
 
44
57
  Installs the Atoll integration for Codex CLI:
45
58
  - Installs the atoll-api skill to ~/.codex/skills/atoll-api/
46
- - Writes AGENTS.md with API reference to ~/.codex/
59
+ - Writes neutral AGENTS.md API guidance to ~/.codex/
47
60
  - Creates/updates an Atoll CLI profile when --profile is provided
61
+ - Optionally writes repo-local AGENTS.md/CLAUDE.md profile guidance with --write-project-instructions
48
62
  - Otherwise writes Atoll env vars to your shell profile for env-var mode
49
63
  `)
50
64
  }
@@ -61,6 +75,34 @@ else args.baseUrl ??= process.env.ATOLL_BASE_URL
61
75
 
62
76
  if (args.help) { printUsage(); process.exit(0) }
63
77
 
78
+ console.log(`Running @atollhq/skill-codex installer v${packageJson.version}`)
79
+
80
+ const instructionFileChoices = (args.instructionFiles ?? 'agents')
81
+ .split(',')
82
+ .map((entry) => entry.trim().toLowerCase())
83
+ .filter(Boolean)
84
+ const allowedInstructionFiles = new Set(['agents', 'claude'])
85
+ if (instructionFileChoices.some((entry) => !allowedInstructionFiles.has(entry))) {
86
+ console.error('Error: --instruction-files must be agents, claude, or agents,claude')
87
+ process.exit(1)
88
+ }
89
+
90
+ const projectInstructionFiles = [...new Set(instructionFileChoices)]
91
+ if (args.writeProjectInstructions && projectInstructionFiles.length === 0) {
92
+ console.error('Error: --instruction-files must include agents, claude, or agents,claude')
93
+ process.exit(1)
94
+ }
95
+
96
+ if (args.writeProjectInstructions && !args.profile) {
97
+ console.error('Error: --write-project-instructions requires --profile so the repo-local guidance can name a profile')
98
+ process.exit(1)
99
+ }
100
+
101
+ if (args.writeProjectInstructions && !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(args.profile)) {
102
+ console.error('Error: --write-project-instructions requires a profile name that starts with a letter or number and contains only letters, numbers, dots, underscores, or hyphens')
103
+ process.exit(1)
104
+ }
105
+
64
106
  if (!args.key || !args.org) {
65
107
  console.error('Error: provide --key and --org, or set ATOLL_API_KEY and ATOLL_ORG_ID\n')
66
108
  printUsage()
@@ -101,7 +143,6 @@ function writeAtollProfile() {
101
143
  else delete profile.defaultTeam
102
144
  if (args.baseUrl) profile.baseUrl = args.baseUrl
103
145
  else delete profile.baseUrl
104
- config.activeProfile = args.profile
105
146
 
106
147
  mkdirSync(atollDir, { recursive: true })
107
148
  writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n')
@@ -109,6 +150,107 @@ function writeAtollProfile() {
109
150
  return configPath
110
151
  }
111
152
 
153
+ const projectInstructionsStart = '<!-- ATOLL:PROJECT-INSTRUCTIONS:START -->'
154
+ const projectInstructionsEnd = '<!-- ATOLL:PROJECT-INSTRUCTIONS:END -->'
155
+ const projectInstructionsPattern = new RegExp(`${projectInstructionsStart}[\\s\\S]*?${projectInstructionsEnd}`)
156
+
157
+ function projectInstructionBlock(profileName) {
158
+ return `${projectInstructionsStart}
159
+ ## Atoll
160
+
161
+ For this workspace, use Atoll CLI profile \`${profileName}\`:
162
+
163
+ \`\`\`bash
164
+ atoll --profile ${profileName} ...
165
+ \`\`\`
166
+ ${projectInstructionsEnd}`
167
+ }
168
+
169
+ function removeUnmanagedAtollProfileGuidance(content) {
170
+ return content
171
+ .split('\n')
172
+ .filter((line) => !/atoll\s+--profile\s+\S+/i.test(line) && !/Atoll CLI profile/i.test(line))
173
+ .join('\n')
174
+ .replace(/\n{3,}/g, '\n\n')
175
+ .trimEnd()
176
+ }
177
+
178
+ function buildProjectInstructionUpdate(targetPath, block) {
179
+ const existing = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : ''
180
+
181
+ if (projectInstructionsPattern.test(existing)) {
182
+ return {
183
+ targetPath,
184
+ content: existing.replace(projectInstructionsPattern, block),
185
+ }
186
+ }
187
+
188
+ if (/atoll\s+--profile\s+\S+/i.test(existing) || /Atoll CLI profile/i.test(existing)) {
189
+ if (!args.forceProjectInstructions) {
190
+ console.error(`Error: ${targetPath} already contains unmanaged Atoll profile guidance. Re-run with --force-project-instructions to replace it.`)
191
+ process.exit(1)
192
+ }
193
+ const cleaned = removeUnmanagedAtollProfileGuidance(existing)
194
+ return {
195
+ targetPath,
196
+ content: cleaned ? `${cleaned}\n\n${block}\n` : `${block}\n`,
197
+ }
198
+ }
199
+
200
+ return {
201
+ targetPath,
202
+ content: existing.trimEnd() ? `${existing.trimEnd()}\n\n${block}\n` : `${block}\n`,
203
+ }
204
+ }
205
+
206
+ function writeProjectInstructionUpdates(updates) {
207
+ const prepared = []
208
+
209
+ try {
210
+ for (const [index, { targetPath, content }] of updates.entries()) {
211
+ const tmpPath = join(dirname(targetPath), `.${basename(targetPath)}.atoll-${process.pid}-${Date.now()}-${index}.tmp`)
212
+ const normalized = content.endsWith('\n') ? content : `${content}\n`
213
+ writeFileSync(tmpPath, normalized, { flag: 'wx' })
214
+ prepared.push({ targetPath, tmpPath })
215
+ }
216
+
217
+ for (const { targetPath, tmpPath } of prepared) {
218
+ renameSync(tmpPath, targetPath)
219
+ console.log(`Wrote Atoll project instructions to ${targetPath}`)
220
+ }
221
+ } catch (error) {
222
+ for (const { tmpPath } of prepared) {
223
+ if (existsSync(tmpPath)) rmSync(tmpPath, { force: true })
224
+ }
225
+ console.error(`Error: failed to write Atoll project instructions: ${error.message}`)
226
+ process.exit(1)
227
+ }
228
+ }
229
+
230
+ function writeProjectInstructions() {
231
+ if (!args.writeProjectInstructions) return
232
+
233
+ const projectDir = resolve(args.projectDir ?? process.cwd())
234
+ if (!existsSync(projectDir) || !statSync(projectDir).isDirectory()) {
235
+ console.error(`Error: --project-dir must be an existing directory: ${projectDir}`)
236
+ process.exit(1)
237
+ }
238
+
239
+ const block = projectInstructionBlock(args.profile)
240
+ const fileNames = {
241
+ agents: 'AGENTS.md',
242
+ claude: 'CLAUDE.md',
243
+ }
244
+
245
+ const updates = []
246
+ for (const choice of projectInstructionFiles) {
247
+ const targetPath = join(projectDir, fileNames[choice])
248
+ updates.push(buildProjectInstructionUpdate(targetPath, block))
249
+ }
250
+
251
+ writeProjectInstructionUpdates(updates)
252
+ }
253
+
112
254
  // 1. Install the Codex skill to ~/.codex/skills/atoll-api/
113
255
  const codexDir = join(homedir(), '.codex')
114
256
  mkdirSync(codexDir, { recursive: true })
@@ -123,27 +265,9 @@ console.log(`Installed Atoll skill to ${skillDest}`)
123
265
  const skillMd = readFileSync(join(skillDir, 'SKILL.md'), 'utf-8')
124
266
  // Strip YAML frontmatter for AGENTS.md
125
267
  const body = skillMd.replace(/^---[\s\S]*?---\n*/, '')
126
- const profileModeInstructions = args.profile ? `## Profile-scoped CLI configuration
127
-
128
- This Codex install is scoped to Atoll CLI profile \`${args.profile}\`. No global \`ATOLL_API_KEY\`, \`ATOLL_ORG_ID\`, \`ATOLL_PROJECT\`, \`ATOLL_TEAM\`, or \`ATOLL_BASE_URL\` shell exports were written. Use \`atoll --profile ${args.profile} ...\` for direct Atoll CLI commands. Direct API snippets below use \`ATOLL_API_KEY\` / \`ATOLL_ORG_ID\` only when you intentionally choose env-var mode.
129
-
130
- ` : ''
131
- const optionalCredentialLines = [
132
- args.profile ? `- Atoll CLI profile: ${args.profile}` : null,
133
- args.project ? `- Default project ID: ${args.project}` : null,
134
- args.team ? `- Default team: ${args.team}` : null,
135
- args.baseUrl ? `- Base URL: ${args.baseUrl}` : null,
136
- ].filter(Boolean).join('\n')
137
-
138
268
  const agentsMd = `# Atoll Integration
139
269
 
140
- ${profileModeInstructions}
141
270
  ${body}
142
-
143
- ## Credentials
144
-
145
- - API Key: ${args.profile ? `stored in Atoll CLI profile \`${args.profile}\`` : 'set as ATOLL_API_KEY environment variable'}
146
- - Org ID: ${args.org}${optionalCredentialLines ? `\n${optionalCredentialLines}` : ''}
147
271
  `
148
272
 
149
273
  const agentsPath = join(codexDir, 'AGENTS.md')
@@ -190,9 +314,11 @@ if (args.profile) {
190
314
  console.log(`Removed stale Atoll shell exports from ${profilePath}`)
191
315
  }
192
316
  writeAtollProfile()
317
+ writeProjectInstructions()
193
318
  console.log('No Atoll shell exports were written.')
194
319
  console.log(`Run profile-scoped commands with: atoll --profile ${args.profile} ...`)
195
- console.log(`\nDone! Codex has the Atoll skill installed, and the Atoll CLI profile "${args.profile}" is active.`)
320
+ console.log(`Verify the profile with: atoll --profile ${args.profile} agent-context --json`)
321
+ console.log(`\nDone! Codex has the Atoll skill installed, and the Atoll CLI profile "${args.profile}" is configured.`)
196
322
  process.exit(0)
197
323
  }
198
324
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atollhq/skill-codex",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "Install the Atoll project management integration for Codex CLI",
5
5
  "bin": {
6
6
  "skill-codex": "bin/install.mjs"
package/skill/SKILL.md CHANGED
@@ -186,7 +186,7 @@ CLI JSON conventions:
186
186
 
187
187
  When a human asks you to help automate a KPI from a third-party API, use this Atoll skill. If the current agent environment does not have the `atoll-api` skill installed, tell the user to install it before continuing or use the Atoll CLI/MCP tools directly if they are available.
188
188
 
189
- Agents may create draft syncs and validate proposed configs only after a human admin has allowlisted the exact destination host in Atoll. Human admins must review the draft in Atoll, enter secrets, dry-run, publish, disable, or run-now with snapshot writing.
189
+ Agents may create draft syncs and validate proposed configs only after a human admin has allowlisted the exact destination host in Atoll. Human admins must create or review the draft in Settings > Integrations > KPI syncs, edit supported request/extraction fields and secrets through structured UI, dry-run, publish, disable, or run-now with snapshot writing.
190
190
 
191
191
  ```bash
192
192
  atoll kpi sync validate <kpi-id> \
@@ -203,6 +203,7 @@ Roles: `owner`, `admin`, `member`, `guest`.
203
203
  | POST | `/api/orgs/{id}/kpis/{kpiId}/snapshots` | Record a snapshot |
204
204
  | GET | `/api/orgs/{id}/kpi-http-sync-policy` | List exact-host KPI HTTP sync allowlist policy |
205
205
  | POST | `/api/orgs/{id}/kpi-http-sync-policy` | Add an allowed exact host (human admin only) |
206
+ | GET | `/api/orgs/{id}/kpi-http-syncs` | List org-wide KPI HTTP sync review rows for Settings; admins get config/secret metadata, members get redacted status rows |
206
207
  | GET | `/api/orgs/{id}/kpis/{kpiId}/http-syncs` | List KPI HTTP syncs |
207
208
  | POST | `/api/orgs/{id}/kpis/{kpiId}/http-syncs` | Create a draft KPI HTTP sync |
208
209
  | PUT | `/api/orgs/{id}/kpis/{kpiId}/http-syncs` | Validate a proposed KPI HTTP sync config without storing or running it |
@@ -429,7 +430,7 @@ URL must be an HTTPS DNS hostname. IP literals, `localhost`, and `.local` hosts
429
430
  |--------|----------|-------------|
430
431
  | GET | `/api/orgs/{id}/agents` | List agents (owner/admin) |
431
432
  | GET | `/api/orgs/{id}/agents/manageable` | List agents the current human can manage |
432
- | POST | `/api/orgs/{id}/agents` | Create org, project-scoped, or personal agent |
433
+ | POST | `/api/orgs/{id}/agents` | Create org agent (`{ name, role?, setupScoped? }`), project-scoped agent (`{ name, projectIds }` or legacy `{ name, projectId, projectIds? }`), or personal agent (`{ name, personal: true }`) |
433
434
  | DELETE | `/api/orgs/{id}/agents/{agentId}` | Revoke manageable agent |
434
435
  | PATCH | `/api/orgs/{id}/agents/{agentId}/projects` | Replace project access for a manageable non-personal agent |
435
436
  | POST | `/api/orgs/{id}/projects/{projectId}/agents` | Grant selected manageable agents access to a project |
@@ -15,6 +15,7 @@
15
15
  - [Heartbeat Response](#heartbeat-response)
16
16
  - [Analytics Response](#analytics-response)
17
17
  - [Plan Limit Errors](#plan-limit-errors)
18
+ - [Agent Fields](#agent-fields)
18
19
  - [Enums](#enums)
19
20
 
20
21
  ---
@@ -73,6 +74,10 @@ Creation endpoints may return `402` when an org reaches its billing plan limit:
73
74
 
74
75
  `resource` is one of `humans`, `agents`, `activeProjects`, or `activeIssues`.
75
76
 
77
+ ## Agent Fields
78
+
79
+ Create org-wide agents with `{ "name": "...", "role": "member", "setupScoped": false }`; org-wide creation is owner/admin-only. Create project-scoped agents with non-empty `projectIds`, for example `{ "name": "...", "projectIds": ["project-uuid"] }`; `projectId` remains accepted as a legacy/default-project alias and is merged with `projectIds`. Project-scoped agents are created as guests, and human members may only scope them to projects they can access. Create personal agents with `{ "name": "...", "personal": true }`; personal agents inherit their human owner's project access and reject explicit `projectId`/`projectIds`.
80
+
76
81
  ## Goal Fields
77
82
 
78
83
  ```json