@axiomatic-labs/claudeflow 2.0.25 → 2.0.27

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/bin/cli.js CHANGED
@@ -12,6 +12,16 @@ switch (command) {
12
12
  case 'version':
13
13
  require('../lib/version.js')();
14
14
  break;
15
+ case 'logout': {
16
+ const ui = require('../lib/ui.js');
17
+ const { clearCachedToken } = require('../lib/auth.js');
18
+ if (clearCachedToken()) {
19
+ ui.success('Token removed from ~/.claudeflow/config.json');
20
+ } else {
21
+ ui.info('No cached token found.');
22
+ }
23
+ break;
24
+ }
15
25
  case '--version':
16
26
  case '-v':
17
27
  console.log(`claudeflow v${require('../package.json').version}`);
@@ -25,6 +35,7 @@ switch (command) {
25
35
  console.log(` claudeflow ${ui.CYAN}init${ui.RESET} Install the latest template files`);
26
36
  console.log(` claudeflow ${ui.CYAN}update${ui.RESET} Update templates to latest version`);
27
37
  console.log(` claudeflow ${ui.CYAN}version${ui.RESET} Show version info`);
38
+ console.log(` claudeflow ${ui.CYAN}logout${ui.RESET} Remove cached GitHub token`);
28
39
  console.log('');
29
40
  console.log(` ${ui.BOLD}Options:${ui.RESET}`);
30
41
  console.log(` --version, -v Show CLI version`);
package/lib/auth.js CHANGED
@@ -1,4 +1,8 @@
1
1
  const { execSync } = require('child_process');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const readline = require('readline');
5
+ const https = require('https');
2
6
 
3
7
  const VALID_TOKEN_PATTERNS = [
4
8
  /^ghp_[A-Za-z0-9_]+$/, // Personal access token
@@ -9,6 +13,9 @@ const VALID_TOKEN_PATTERNS = [
9
13
  /^[0-9a-f]{40}$/, // Classic 40-char hex token
10
14
  ];
11
15
 
16
+ const CONFIG_DIR = path.join(require('os').homedir(), '.claudeflow');
17
+ const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
18
+
12
19
  function sanitizeToken(raw) {
13
20
  if (!raw || typeof raw !== 'string') return null;
14
21
  const clean = raw.replace(/[\x00-\x1F\x7F]/g, '').trim();
@@ -17,6 +24,85 @@ function sanitizeToken(raw) {
17
24
  return clean;
18
25
  }
19
26
 
27
+ function readCachedToken() {
28
+ try {
29
+ const data = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
30
+ return sanitizeToken(data.githubToken);
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+
36
+ function cacheToken(token) {
37
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
38
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify({ githubToken: token }, null, 2) + '\n', {
39
+ mode: 0o600,
40
+ });
41
+ }
42
+
43
+ function clearCachedToken() {
44
+ try {
45
+ fs.unlinkSync(CONFIG_PATH);
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ function validateToken(token) {
53
+ return new Promise((resolve) => {
54
+ const req = https.get(
55
+ 'https://api.github.com/repos/axiomatic-labs/axiomatic-cli',
56
+ {
57
+ headers: {
58
+ Authorization: `token ${token}`,
59
+ 'User-Agent': 'claudeflow-cli',
60
+ Accept: 'application/vnd.github.v3+json',
61
+ },
62
+ },
63
+ (res) => {
64
+ // Drain the response body so the socket can close
65
+ res.resume();
66
+ resolve(res.statusCode === 200);
67
+ }
68
+ );
69
+ req.on('error', () => resolve(false));
70
+ req.setTimeout(10000, () => {
71
+ req.destroy();
72
+ resolve(false);
73
+ });
74
+ });
75
+ }
76
+
77
+ function promptForToken() {
78
+ return new Promise((resolve) => {
79
+ const ui = require('./ui.js');
80
+
81
+ console.log('');
82
+ console.log(` ${ui.YELLOW}No GitHub token found.${ui.RESET}`);
83
+ console.log('');
84
+ console.log(` To use Claudeflow, you need a GitHub Personal Access Token`);
85
+ console.log(` with access to the ${ui.BOLD}axiomatic-labs/axiomatic-cli${ui.RESET} repository.`);
86
+ console.log('');
87
+ console.log(` Create one at: ${ui.CYAN}https://github.com/settings/tokens/new${ui.RESET}`);
88
+ console.log(` Required scope: ${ui.BOLD}repo${ui.RESET} (Full control of private repositories)`);
89
+ console.log('');
90
+
91
+ const rl = readline.createInterface({
92
+ input: process.stdin,
93
+ output: process.stdout,
94
+ });
95
+
96
+ rl.question(` Paste your token: `, (answer) => {
97
+ rl.close();
98
+ resolve(answer || null);
99
+ });
100
+
101
+ // Handle Ctrl+C / closed stream
102
+ rl.on('close', () => resolve(null));
103
+ });
104
+ }
105
+
20
106
  function getGitHubToken() {
21
107
  // Try gh CLI first
22
108
  try {
@@ -31,21 +117,49 @@ function getGitHubToken() {
31
117
  if (token) return token;
32
118
  }
33
119
 
120
+ // Try cached token
121
+ const cached = readCachedToken();
122
+ if (cached) return cached;
123
+
34
124
  return null;
35
125
  }
36
126
 
37
- function requireAuth() {
127
+ async function requireAuth() {
128
+ const ui = require('./ui.js');
129
+
130
+ // Fast path: gh CLI, env var, or cached token
38
131
  const token = getGitHubToken();
39
- if (!token) {
40
- console.error('GitHub authentication required.\n');
41
- console.error('Option 1: Install the GitHub CLI and authenticate:');
42
- console.error(' brew install gh && gh auth login\n');
43
- console.error('Option 2: Set a GitHub Personal Access Token:');
44
- console.error(' export GITHUB_TOKEN=ghp_your_token_here\n');
45
- console.error('The token needs read access to axiomatic-labs/axiomatic-cli releases.');
46
- process.exit(1);
132
+ if (token) return token;
133
+
134
+ // Interactive prompt with validation loop
135
+ for (let attempts = 0; attempts < 3; attempts++) {
136
+ const raw = await promptForToken();
137
+ if (!raw) {
138
+ // User cancelled (Ctrl+C or empty input)
139
+ ui.error('Authentication cancelled.');
140
+ process.exit(1);
141
+ }
142
+
143
+ const token = sanitizeToken(raw);
144
+ if (!token) {
145
+ ui.error('Invalid token format. Expected a GitHub PAT (ghp_..., github_pat_..., etc.).');
146
+ continue;
147
+ }
148
+
149
+ ui.step('Validating token...');
150
+ const valid = await validateToken(token);
151
+ if (!valid) {
152
+ ui.error('Token invalid or no access to the repository.');
153
+ continue;
154
+ }
155
+
156
+ cacheToken(token);
157
+ ui.success(`Token saved to ~/.claudeflow/config.json`);
158
+ return token;
47
159
  }
48
- return token;
160
+
161
+ ui.error('Too many failed attempts.');
162
+ process.exit(1);
49
163
  }
50
164
 
51
- module.exports = { getGitHubToken, requireAuth };
165
+ module.exports = { getGitHubToken, requireAuth, clearCachedToken };
package/lib/init.js CHANGED
@@ -21,7 +21,7 @@ async function run() {
21
21
  ui.banner();
22
22
 
23
23
  ui.step('Authenticating with GitHub...');
24
- const token = requireAuth();
24
+ const token = await requireAuth();
25
25
 
26
26
  ui.step('Fetching latest release...');
27
27
  const release = await getLatestRelease(token);
package/lib/update.js CHANGED
@@ -19,7 +19,7 @@ async function run() {
19
19
  ui.banner(current);
20
20
 
21
21
  ui.step('Authenticating with GitHub...');
22
- const token = requireAuth();
22
+ const token = await requireAuth();
23
23
 
24
24
  ui.step('Checking for updates...');
25
25
  const release = await getLatestRelease(token);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.0.25",
3
+ "version": "2.0.27",
4
4
  "description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
5
5
  "bin": {
6
6
  "claudeflow": "./bin/cli.js"