client-handover 1.0.3 → 1.0.5

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
@@ -14,7 +14,11 @@ Uses the [Claude API](https://console.anthropic.com) (Anthropic) under the hood.
14
14
  npm install -g client-handover
15
15
  ```
16
16
 
17
- Then set your Anthropic API key as an environment variable:
17
+ ### API key setup
18
+
19
+ **If you have Claude Code installed**, no setup needed — `client-handover` will automatically use your existing Claude credentials.
20
+
21
+ **Otherwise**, set your Anthropic API key as an environment variable:
18
22
 
19
23
  ```bash
20
24
  # macOS / Linux
@@ -159,7 +163,9 @@ All prompt builders accept an optional `projectInfo` string. If omitted, Claude
159
163
  ## Requirements
160
164
 
161
165
  - Node.js 18+
162
- - An Anthropic API key — [get one free at console.anthropic.com](https://console.anthropic.com)
166
+ - One of the following:
167
+ - [Claude Code](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code) installed (zero config — credentials detected automatically)
168
+ - Or an Anthropic API key — [get one free at console.anthropic.com](https://console.anthropic.com)
163
169
 
164
170
  ---
165
171
 
@@ -192,6 +198,27 @@ client-handover/
192
198
 
193
199
  ---
194
200
 
201
+ ## Changelog
202
+
203
+ ### 1.0.5
204
+ - Interactive API key setup prompt on install — no manual config needed
205
+ - Added `handover key <api-key>` command to save key at any time
206
+ - Key is stored in `~/.handover/config.json` and persists across all sessions
207
+
208
+ ### 1.0.4
209
+ - Auto-detects Claude Code credentials — no API key setup needed if you have Claude Code installed
210
+
211
+ ### 1.0.3
212
+ - Commands now work with or without a leading `/` — fixes Git Bash path conversion issues on Windows
213
+
214
+ ### 1.0.2
215
+ - Added `all` command — generates every section as separate files in a single named folder
216
+
217
+ ### 1.0.1
218
+ - Initial release
219
+
220
+ ---
221
+
195
222
  ## Contributing
196
223
 
197
224
  Pull requests are welcome. For major changes, open an issue first.
package/cli.js CHANGED
@@ -5,7 +5,7 @@ import { deploy } from './deploy.js'
5
5
  import { credentials } from './credentials.js'
6
6
  import { handover } from './handover.js'
7
7
  import { license } from './license.js'
8
- import { generateDoc } from './generator.js'
8
+ import { generateDoc, saveApiKey } from './generator.js'
9
9
  import chalk from 'chalk'
10
10
  import fs from 'fs'
11
11
  import path from 'path'
@@ -33,6 +33,7 @@ function printHelp() {
33
33
  console.log(` ${chalk.cyan(cmd.padEnd(16))} ${label}`)
34
34
  })
35
35
  console.log(` ${chalk.cyan('all'.padEnd(16))} All sections in a single folder`)
36
+ console.log(` ${chalk.cyan('key <api-key>'.padEnd(16))} Save your Anthropic API key (one-time setup)`)
36
37
  console.log('\n' + chalk.dim('Examples:'))
37
38
  console.log(' handover handover # Full doc with placeholders')
38
39
  console.log(' handover setup project-info.txt # Setup section using your notes')
@@ -74,6 +75,17 @@ async function main() {
74
75
 
75
76
  const command = normalizeCommand(rawCommand)
76
77
 
78
+ if (command === 'key') {
79
+ const key = infoFile // second arg is the key
80
+ if (!key) {
81
+ console.error(chalk.red('\n❌ Usage: handover key <your-api-key>\n'))
82
+ process.exit(1)
83
+ }
84
+ saveApiKey(key)
85
+ console.log(chalk.green('\n✅ API key saved. You\'re all set — run any handover command.\n'))
86
+ process.exit(0)
87
+ }
88
+
77
89
  // Read optional project info file
78
90
  let projectInfo = ''
79
91
  if (infoFile) {
@@ -91,7 +103,7 @@ async function main() {
91
103
  await runAll(projectInfo, folderName)
92
104
  } catch (err) {
93
105
  if (err.status === 401) {
94
- console.error(chalk.red('\n❌ Invalid API key. Set your ANTHROPIC_API_KEY environment variable.\n'))
106
+ console.error(chalk.red('\n❌ Authentication failed. Set ANTHROPIC_API_KEY or install Claude Code.\n'))
95
107
  } else {
96
108
  console.error(chalk.red(`\n❌ Error: ${err.message}\n`))
97
109
  }
@@ -117,7 +129,7 @@ async function main() {
117
129
  await generateDoc(prompt, docName, './output')
118
130
  } catch (err) {
119
131
  if (err.status === 401) {
120
- console.error(chalk.red('\n❌ Invalid API key. Set your ANTHROPIC_API_KEY environment variable.\n'))
132
+ console.error(chalk.red('\n❌ Authentication failed. Set ANTHROPIC_API_KEY or install Claude Code.\n'))
121
133
  } else {
122
134
  console.error(chalk.red(`\n❌ Error: ${err.message}\n`))
123
135
  }
package/generator.js CHANGED
@@ -2,8 +2,53 @@ import Anthropic from '@anthropic-ai/sdk'
2
2
  import { marked } from 'marked'
3
3
  import fs from 'fs'
4
4
  import path from 'path'
5
+ import os from 'os'
5
6
 
6
- const client = new Anthropic()
7
+ function resolveApiKey() {
8
+ if (process.env.ANTHROPIC_API_KEY) return process.env.ANTHROPIC_API_KEY
9
+
10
+ // Check saved key from `handover key <your-key>`
11
+ const configPath = path.join(os.homedir(), '.handover', 'config.json')
12
+ if (fs.existsSync(configPath)) {
13
+ try {
14
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
15
+ if (config?.apiKey) return config.apiKey
16
+ } catch {
17
+ // ignore malformed config file
18
+ }
19
+ }
20
+
21
+ const credentialsPath = path.join(os.homedir(), '.claude', '.credentials.json')
22
+ if (fs.existsSync(credentialsPath)) {
23
+ try {
24
+ const creds = JSON.parse(fs.readFileSync(credentialsPath, 'utf-8'))
25
+ const token = creds?.claudeAiOauth?.accessToken
26
+ const expiresAt = creds?.claudeAiOauth?.expiresAt
27
+ if (token && (!expiresAt || expiresAt > Date.now())) {
28
+ return token
29
+ }
30
+ } catch {
31
+ // ignore malformed credentials file
32
+ }
33
+ }
34
+
35
+ return null
36
+ }
37
+
38
+ export function saveApiKey(key) {
39
+ const configDir = path.join(os.homedir(), '.handover')
40
+ const configPath = path.join(configDir, 'config.json')
41
+ if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true })
42
+ fs.writeFileSync(configPath, JSON.stringify({ apiKey: key }, null, 2), 'utf-8')
43
+ }
44
+
45
+ const apiKey = resolveApiKey()
46
+ if (!apiKey) {
47
+ console.error('\n❌ No API key found. Set ANTHROPIC_API_KEY or install Claude Code (code.visualstudio.com/download).\n')
48
+ process.exit(1)
49
+ }
50
+
51
+ const client = new Anthropic({ apiKey })
7
52
 
8
53
  /**
9
54
  * Generate a handover document using the Claude API
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "client-handover",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "AI-powered handover document generator for frontend developers handing off client websites",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -16,10 +16,12 @@
16
16
  "deploy.js",
17
17
  "credentials.js",
18
18
  "license.js",
19
+ "postinstall.js",
19
20
  "README.md"
20
21
  ],
21
22
  "scripts": {
22
- "test": "node test.js"
23
+ "test": "node test.js",
24
+ "postinstall": "node postinstall.js"
23
25
  },
24
26
  "keywords": [
25
27
  "handover",
package/postinstall.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+
3
+ import readline from 'readline'
4
+ import fs from 'fs'
5
+ import path from 'path'
6
+ import os from 'os'
7
+
8
+ const configDir = path.join(os.homedir(), '.handover')
9
+ const configPath = path.join(configDir, 'config.json')
10
+ const claudeCredsPath = path.join(os.homedir(), '.claude', '.credentials.json')
11
+
12
+ function alreadyConfigured() {
13
+ if (process.env.ANTHROPIC_API_KEY) return true
14
+
15
+ if (fs.existsSync(configPath)) {
16
+ try {
17
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
18
+ if (config?.apiKey) return true
19
+ } catch {}
20
+ }
21
+
22
+ if (fs.existsSync(claudeCredsPath)) {
23
+ try {
24
+ const creds = JSON.parse(fs.readFileSync(claudeCredsPath, 'utf-8'))
25
+ const token = creds?.claudeAiOauth?.accessToken
26
+ const expiresAt = creds?.claudeAiOauth?.expiresAt
27
+ if (token && (!expiresAt || expiresAt > Date.now())) return true
28
+ } catch {}
29
+ }
30
+
31
+ return false
32
+ }
33
+
34
+ function saveKey(key) {
35
+ if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true })
36
+ fs.writeFileSync(configPath, JSON.stringify({ apiKey: key }, null, 2), 'utf-8')
37
+ }
38
+
39
+ // Skip if already set up or not in an interactive terminal
40
+ if (alreadyConfigured() || !process.stdin.isTTY) {
41
+ process.exit(0)
42
+ }
43
+
44
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
45
+
46
+ console.log('\n──────────────────────────────────────────')
47
+ console.log(' client-handover setup')
48
+ console.log('──────────────────────────────────────────')
49
+ console.log(' To generate documents, you need an Anthropic API key.')
50
+ console.log(' Get one free at: https://console.anthropic.com\n')
51
+
52
+ rl.question(' Enter your Anthropic API key (or press Enter to skip): ', (answer) => {
53
+ rl.close()
54
+ const key = answer.trim()
55
+
56
+ if (!key) {
57
+ console.log('\n Skipped. Run "handover key <your-api-key>" at any time to set it.\n')
58
+ process.exit(0)
59
+ }
60
+
61
+ saveKey(key)
62
+ console.log('\n✅ API key saved. Run "handover handover" to get started.\n')
63
+ process.exit(0)
64
+ })