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 +29 -2
- package/cli.js +15 -3
- package/generator.js +46 -1
- package/package.json +4 -2
- package/postinstall.js +64 -0
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
|
-
|
|
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
|
-
-
|
|
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❌
|
|
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❌
|
|
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
|
-
|
|
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
|
+
"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
|
+
})
|