client-handover 1.3.0 → 1.4.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.
Files changed (3) hide show
  1. package/cli.js +4 -32
  2. package/generator.js +8 -52
  3. package/package.json +2 -2
package/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { generateDoc, resolveApiKey, saveApiKey } from './generator.js'
3
+ import { generateDoc } from './generator.js'
4
4
  import { technicalHandoverPrompt, nonTechnicalHandoverPrompt } from './prompts.js'
5
5
  import { scanProject } from './scanner.js'
6
6
  import chalk from 'chalk'
@@ -31,8 +31,7 @@ function printBanner() {
31
31
  function printHelp() {
32
32
  printBanner()
33
33
  console.log(chalk.dim(' Usage:'))
34
- console.log(' handover /create Generate technical & non-technical handover documents')
35
- console.log(' handover key <key> Save your Anthropic API key\n')
34
+ console.log(' handover /create Generate technical & non-technical handover documents\n')
36
35
  console.log(chalk.dim(' Example:'))
37
36
  console.log(' cd my-client-project')
38
37
  console.log(' handover /create\n')
@@ -52,10 +51,9 @@ async function runSetup() {
52
51
  if (fs.existsSync(configPath)) config = JSON.parse(fs.readFileSync(configPath, 'utf-8'))
53
52
  } catch {}
54
53
 
55
- const hasApiKey = !!resolveApiKey(config)
56
54
  const hasDeveloperInfo = !!config.developerName
57
55
 
58
- if (hasApiKey && hasDeveloperInfo) {
56
+ if (hasDeveloperInfo) {
59
57
  return {
60
58
  name: config.developerName,
61
59
  company: config.developerCompany || '',
@@ -70,18 +68,6 @@ async function runSetup() {
70
68
 
71
69
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
72
70
 
73
- if (!hasApiKey) {
74
- console.log(' To generate documents you need an Anthropic API key.')
75
- console.log(chalk.dim(' Get one free at: https://console.anthropic.com\n'))
76
- let key = ''
77
- while (!key) {
78
- key = await ask(rl, ' Enter your Anthropic API key: ')
79
- if (!key) console.log(chalk.yellow(' API key is required to continue.\n'))
80
- }
81
- config.apiKey = key
82
- console.log(chalk.green(' ✓ API key saved.\n'))
83
- }
84
-
85
71
  if (!hasDeveloperInfo) {
86
72
  console.log(' Your details will appear in all generated handover documents.\n')
87
73
  const name = await ask(rl, ' Developer name: ')
@@ -171,16 +157,6 @@ async function main() {
171
157
 
172
158
  const command = normalizeCommand(rawCommand)
173
159
 
174
- if (command === 'key') {
175
- if (!secondArg) {
176
- console.error(chalk.red('\n ❌ Usage: handover key <your-api-key>\n'))
177
- process.exit(1)
178
- }
179
- saveApiKey(secondArg)
180
- console.log(chalk.green('\n ✅ API key saved. Run "handover /create" to get started.\n'))
181
- process.exit(0)
182
- }
183
-
184
160
  if (command !== 'create') {
185
161
  console.error(chalk.red(`\n ❌ Unknown command: ${rawCommand}\n`))
186
162
  printHelp()
@@ -190,11 +166,7 @@ async function main() {
190
166
  try {
191
167
  await runCreate()
192
168
  } catch (err) {
193
- if (err.status === 401) {
194
- console.error(chalk.red('\n ❌ Authentication failed. Run "handover key <your-api-key>" to set your API key.\n'))
195
- } else {
196
- console.error(chalk.red(`\n ❌ Error: ${err.message}\n`))
197
- }
169
+ console.error(chalk.red(`\n Error: ${err.message}\n`))
198
170
  process.exit(1)
199
171
  }
200
172
  }
package/generator.js CHANGED
@@ -1,4 +1,4 @@
1
- import Anthropic from '@anthropic-ai/sdk'
1
+ import { query } from '@anthropic-ai/claude-code'
2
2
  import {
3
3
  Document, Packer, Paragraph, TextRun, HeadingLevel,
4
4
  AlignmentType, BorderStyle, TableRow, TableCell, Table,
@@ -6,51 +6,6 @@ import {
6
6
  } from 'docx'
7
7
  import fs from 'fs'
8
8
  import path from 'path'
9
- import os from 'os'
10
-
11
- export function resolveApiKey(config = null) {
12
- if (process.env.ANTHROPIC_API_KEY) return process.env.ANTHROPIC_API_KEY
13
-
14
- const cfg = config || (() => {
15
- try {
16
- const p = path.join(os.homedir(), '.handover', 'config.json')
17
- if (fs.existsSync(p)) return JSON.parse(fs.readFileSync(p, 'utf-8'))
18
- } catch {}
19
- return {}
20
- })()
21
-
22
- if (cfg?.apiKey) return cfg.apiKey
23
-
24
- const credentialsPath = path.join(os.homedir(), '.claude', '.credentials.json')
25
- try {
26
- if (fs.existsSync(credentialsPath)) {
27
- const creds = JSON.parse(fs.readFileSync(credentialsPath, 'utf-8'))
28
- const token = creds?.claudeAiOauth?.accessToken
29
- const expiresAt = creds?.claudeAiOauth?.expiresAt
30
- if (token && (!expiresAt || expiresAt > Date.now())) return token
31
- }
32
- } catch {}
33
-
34
- return null
35
- }
36
-
37
- export function saveApiKey(key) {
38
- const configDir = path.join(os.homedir(), '.handover')
39
- const configPath = path.join(configDir, 'config.json')
40
- if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true })
41
- let config = {}
42
- try { config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) } catch {}
43
- config.apiKey = key
44
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8')
45
- }
46
-
47
- const apiKey = resolveApiKey()
48
- if (!apiKey) {
49
- console.error('\n ❌ No API key found. Run "handover key <your-api-key>" or install Claude Code.\n')
50
- process.exit(1)
51
- }
52
-
53
- const client = new Anthropic({ apiKey })
54
9
 
55
10
  // ---------------------------------------------------------------------------
56
11
  // Inline text parser — splits a line into TextRun segments handling **bold**,
@@ -298,13 +253,14 @@ function buildDocx(markdown) {
298
253
  export async function generateDoc(prompt, outputName = 'handover', outputDir = './output') {
299
254
  console.log('⏳ Generating document with Claude...\n')
300
255
 
301
- const message = await client.messages.create({
302
- model: 'claude-sonnet-4-20250514',
303
- max_tokens: 4096,
304
- messages: [{ role: 'user', content: prompt }]
305
- })
256
+ let markdown = ''
257
+ for await (const message of query({ prompt, options: { maxTurns: 1 } })) {
258
+ if (message.type === 'result' && message.subtype === 'success') {
259
+ markdown = message.result
260
+ }
261
+ }
306
262
 
307
- const markdown = message.content[0].text
263
+ if (!markdown) throw new Error('No response received from Claude')
308
264
 
309
265
  if (!fs.existsSync(outputDir)) {
310
266
  fs.mkdirSync(outputDir, { recursive: true })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "client-handover",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "AI-powered handover document generator for frontend developers handing off client websites",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,7 +29,7 @@
29
29
  "node": ">=18"
30
30
  },
31
31
  "dependencies": {
32
- "@anthropic-ai/sdk": "^0.39.0",
32
+ "@anthropic-ai/claude-code": "latest",
33
33
  "chalk": "^5.3.0",
34
34
  "docx": "^8.5.0"
35
35
  }