@solidactions/cli 1.6.0 → 1.7.1

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
@@ -11,10 +11,11 @@ npm install -g @solidactions/cli
11
11
  ## Quick Start
12
12
 
13
13
  ```bash
14
- solidactions init <api-key> # Initialize (prompts for workspace)
15
- solidactions ai init # Install AI skills and SDK reference (required for AI-assisted dev)
16
- solidactions project deploy <project> <path> # Deploy a project
17
- solidactions run start <project> <workflow> # Trigger a workflow
14
+ solidactions login <api-key> # Authenticate (prompts for workspace)
15
+ solidactions init my-project # Scaffold a new project (files + AI skills)
16
+ cd my-project
17
+ solidactions project deploy my-project -e production # Deploy it
18
+ solidactions run start my-project <workflow> # Trigger a workflow
18
19
  solidactions run view <run-id> # Inspect a run
19
20
  ```
20
21
 
@@ -35,13 +36,13 @@ For each field (`host`, `apiKey`, `workspaceId`), the CLI resolves independently
35
36
 
36
37
  You can mix: e.g., set only `SOLIDACTIONS_WORKSPACE_ID` in the environment while letting `host` and `apiKey` come from a file.
37
38
 
38
- ### `solidactions init` flags
39
+ ### `solidactions login` flags
39
40
 
40
41
  - `--local` — write config to `./.solidactions/config.json` in the current folder.
41
42
  - `--global` — write config to `~/.solidactions/config.json` (today's default).
42
43
  - `--gitignore` — with `--local`, auto-add `.solidactions/` to `.gitignore` without prompting.
43
44
 
44
- In interactive shells, `init` without `--local`/`--global` prompts for a location. In non-interactive contexts, one of the flags is required.
45
+ In interactive shells, `login` without `--local`/`--global` prompts for a location. In non-interactive contexts, one of the flags is required.
45
46
 
46
47
  ### `solidactions logout` flags
47
48
 
@@ -57,7 +58,7 @@ Set `SOLIDACTIONS_DEBUG=1` on any command to print the resolved configuration an
57
58
 
58
59
  If you run multiple AI coding agents in different project folders simultaneously, either:
59
60
 
60
- - Run `solidactions init <key> --local` in each folder so each has its own config, or
61
+ - Run `solidactions login <key> --local` in each folder so each has its own config, or
61
62
  - Set `SOLIDACTIONS_API_KEY` / `SOLIDACTIONS_WORKSPACE_ID` in the environment each agent uses (no files to share or stomp).
62
63
 
63
64
  ## Commands
@@ -68,9 +69,10 @@ Use `solidactions <command> --help` for full flag details on any command.
68
69
 
69
70
  | Command | Description |
70
71
  |---------|-------------|
71
- | `init <api-key>` | Initialize CLI with API key |
72
+ | `login <api-key>` | Authenticate CLI with API key |
72
73
  | `logout` | Remove saved credentials |
73
74
  | `whoami` | Show current configuration |
75
+ | `init [directory]` | Scaffold a new project (files + AI skills) |
74
76
  | `dev <file>` | Run a workflow locally (no deploy needed) |
75
77
 
76
78
  ### project
@@ -127,7 +129,7 @@ Use `solidactions <command> --help` for full flag details on any command.
127
129
 
128
130
  | Command | Key Flags | Description |
129
131
  |---------|-----------|-------------|
130
- | `ai init` | `--claude`, `--agents` | Install AI skills and SDK reference |
132
+ | `ai init` | `--claude`, `--agents` | Install AI skills and SDK reference into an **existing** project (use `init` for new projects) |
131
133
  | `ai examples [names...]` | `--all`, `--overwrite` | Install example workflows |
132
134
 
133
135
  `ai init` installs three auto-activating SolidActions skills and a
@@ -90,7 +90,7 @@ async function envDelete(keyOrProject, keyIfProject, options = {}) {
90
90
  catch (error) {
91
91
  if (error.response) {
92
92
  if (error.response.status === 401) {
93
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
93
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
94
94
  }
95
95
  else if (error.response.status === 404) {
96
96
  console.error(chalk_1.default.red(isProjectMode ? `Project "${projectName}" not found.` : `Variable "${key}" not found.`));
@@ -182,7 +182,7 @@ async function envList(projectName, options = {}) {
182
182
  catch (error) {
183
183
  if (error.response) {
184
184
  if (error.response.status === 401) {
185
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
185
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
186
186
  }
187
187
  else if (error.response.status === 404) {
188
188
  console.error(chalk_1.default.red(projectName ? `Project "${projectName}" not found for the specified environment.` : 'Resource not found.'));
@@ -58,7 +58,7 @@ async function envMap(projectName, projectKey, globalKey, options = {}) {
58
58
  catch (error) {
59
59
  if (error.response) {
60
60
  if (error.response.status === 401) {
61
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
61
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
62
62
  }
63
63
  else if (error.response.status === 404) {
64
64
  console.error(chalk_1.default.red(`Project "${projectName}" not found.`));
@@ -203,7 +203,7 @@ async function envPull(projectName, options = {}) {
203
203
  catch (error) {
204
204
  if (error.response) {
205
205
  if (error.response.status === 401) {
206
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
206
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
207
207
  }
208
208
  else if (error.response.status === 404) {
209
209
  console.error(chalk_1.default.red(`Project "${projectSlug}" not found.`));
@@ -87,7 +87,7 @@ async function envPush(projectName, sourcePath, options = {}) {
87
87
  }
88
88
  catch (error) {
89
89
  if (error.response?.status === 401) {
90
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
90
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
91
91
  process.exit(1);
92
92
  }
93
93
  else if (error.response?.status === 404) {
@@ -61,7 +61,7 @@ async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
61
61
  catch (error) {
62
62
  if (error.response) {
63
63
  if (error.response.status === 401) {
64
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
64
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
65
65
  }
66
66
  else if (error.response.status === 404) {
67
67
  console.error(chalk_1.default.red(`Project "${projectSlug}" not found.`));
@@ -164,7 +164,7 @@ async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
164
164
  catch (error) {
165
165
  if (error.response) {
166
166
  if (error.response.status === 401) {
167
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
167
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
168
168
  }
169
169
  else if (error.response.status === 422) {
170
170
  console.error(chalk_1.default.red('Validation error:'), error.response.data.message || error.response.data.errors);
@@ -3,212 +3,89 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getConfig = getConfig;
7
- exports.saveConfig = saveConfig;
8
- exports.clearConfig = clearConfig;
9
6
  exports.init = init;
10
- exports.logout = logout;
11
- exports.whoami = whoami;
12
7
  const fs_1 = __importDefault(require("fs"));
13
8
  const path_1 = __importDefault(require("path"));
14
- const readline_1 = __importDefault(require("readline"));
15
9
  const chalk_1 = __importDefault(require("chalk"));
16
- const api_1 = require("../utils/api");
17
- const workspaces_1 = require("./workspaces");
18
- const config_1 = require("../utils/config");
19
- function getConfig() {
20
- const resolved = (0, config_1.resolveConfig)();
21
- return resolved ? resolved.config : null;
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const github_1 = require("../utils/github");
12
+ const ai_init_1 = require("./ai-init");
13
+ const EXAMPLES_OWNER = 'SolidActions';
14
+ const EXAMPLES_REPO = 'solidactions-examples';
15
+ const TEMPLATE_PREFIX = 'templates/minimal';
16
+ // Tuples of [remote-path-suffix-under-template-prefix, local-path-relative-to-target].
17
+ const TEMPLATE_FILES = [
18
+ ['package.json', 'package.json'],
19
+ ['tsconfig.json', 'tsconfig.json'],
20
+ ['solidactions.yaml', 'solidactions.yaml'],
21
+ ['.env.example', '.env.example'],
22
+ ['src/hello.ts', 'src/hello.ts'],
23
+ ];
24
+ function firstNonDotEntry(target) {
25
+ if (!fs_1.default.existsSync(target))
26
+ return null;
27
+ const entries = fs_1.default.readdirSync(target);
28
+ for (const entry of entries) {
29
+ if (entry.startsWith('.'))
30
+ continue;
31
+ return entry;
32
+ }
33
+ return null;
22
34
  }
23
- function saveConfig(config) {
24
- const resolved = (0, config_1.resolveConfig)();
25
- const targetPath = resolved ? resolved.activePath : (0, config_1.getGlobalConfigPath)();
26
- (0, config_1.writeConfigFile)(targetPath, config);
27
- }
28
- function clearConfig() {
29
- (0, config_1.removeConfigFile)((0, config_1.getGlobalConfigPath)());
30
- }
31
- async function promptLocation() {
32
- const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
35
+ async function init(directory, options = {}) {
33
36
  try {
34
- while (true) {
35
- const answer = await new Promise((resolve) => {
36
- rl.question(chalk_1.default.blue('Save config locally (./.solidactions) or globally (~/.solidactions)? [global] '), resolve);
37
- });
38
- const normalized = answer.trim().toLowerCase();
39
- if (normalized === '' || normalized === 'global' || normalized === 'g')
40
- return 'global';
41
- if (normalized === 'local' || normalized === 'l')
42
- return 'local';
43
- console.log(chalk_1.default.yellow("Please answer 'local' or 'global' (or press Enter for global)."));
37
+ const cwd = process.cwd();
38
+ const installSkills = options.skills !== false; // default true
39
+ const targetDir = directory ? path_1.default.resolve(cwd, directory) : cwd;
40
+ const projectName = path_1.default.basename(targetDir);
41
+ if (directory) {
42
+ fs_extra_1.default.ensureDirSync(targetDir);
44
43
  }
45
- }
46
- finally {
47
- rl.close();
48
- }
49
- }
50
- async function ensureGitignoreCovers(targetDir, auto) {
51
- const gitignorePath = path_1.default.join(targetDir, '.gitignore');
52
- const patternToAdd = '.solidactions/';
53
- let existing = '';
54
- if (fs_1.default.existsSync(gitignorePath)) {
55
- existing = fs_1.default.readFileSync(gitignorePath, 'utf-8');
56
- const lines = existing.split('\n').map((l) => l.trim());
57
- const isCovered = lines.some((line) => {
58
- const normalized = line
59
- .replace(/^\*\*\//, '')
60
- .replace(/^\//, '')
61
- .replace(/\/(\*\*|\*)?$/, '');
62
- return normalized === '.solidactions';
63
- });
64
- if (isCovered) {
65
- return; // already covered
44
+ const offender = firstNonDotEntry(targetDir);
45
+ if (offender) {
46
+ console.error(chalk_1.default.red(`Target directory "${targetDir}" is not empty (contains "${offender}").`));
47
+ console.error(chalk_1.default.gray('Run in a fresh directory, or use `solidactions ai init` to add AI tooling to an existing project.'));
48
+ process.exit(1);
66
49
  }
67
- }
68
- let shouldAdd = auto;
69
- if (!shouldAdd && process.stdin.isTTY) {
70
- const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
71
- const answer = await new Promise((resolve) => {
72
- rl.question(chalk_1.default.yellow(`Local config contains an API key. Add \`.solidactions/\` to ${gitignorePath}? [Y/n] `), resolve);
73
- });
74
- rl.close();
75
- shouldAdd = !(answer.trim().toLowerCase().startsWith('n'));
76
- }
77
- if (!shouldAdd) {
78
- console.log(chalk_1.default.yellow(`Skipping .gitignore update. Remember: ${path_1.default.join(targetDir, '.solidactions', 'config.json')} contains your API key.`));
79
- return;
80
- }
81
- const prefix = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
82
- try {
83
- fs_1.default.writeFileSync(gitignorePath, `${existing}${prefix}${patternToAdd}\n`);
84
- console.log(chalk_1.default.green(`Added \`${patternToAdd}\` to ${gitignorePath}.`));
50
+ console.log(chalk_1.default.blue(`Scaffolding "${projectName}" in ${targetDir}...`));
51
+ for (const [remoteSuffix, localSuffix] of TEMPLATE_FILES) {
52
+ const remotePath = `${TEMPLATE_PREFIX}/${remoteSuffix}`;
53
+ let content = await (0, github_1.fetchRawFile)(EXAMPLES_OWNER, EXAMPLES_REPO, remotePath);
54
+ content = content.replace(/__PROJECT_NAME__/g, projectName);
55
+ const outPath = path_1.default.join(targetDir, localSuffix);
56
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(outPath));
57
+ fs_1.default.writeFileSync(outPath, content, 'utf8');
58
+ console.log(chalk_1.default.green(`✓ ${path_1.default.relative(cwd, outPath)}`));
59
+ }
60
+ if (installSkills) {
61
+ console.log('');
62
+ process.chdir(targetDir);
63
+ await (0, ai_init_1.aiInit)({ claude: options.claude, agents: options.agents });
64
+ }
65
+ console.log('');
66
+ console.log(chalk_1.default.green(`✓ Project "${projectName}" scaffolded.`));
67
+ console.log('');
68
+ console.log(chalk_1.default.blue('Next steps:'));
69
+ if (directory) {
70
+ console.log(chalk_1.default.gray(` cd ${directory}`));
71
+ }
72
+ console.log(chalk_1.default.gray(` # Fill in WEBHOOK_SECRET and any other env vars in solidactions.yaml:`));
73
+ console.log(chalk_1.default.gray(` solidactions env set ${projectName} WEBHOOK_SECRET <your-secret> -e production`));
74
+ console.log(chalk_1.default.gray(` solidactions project deploy ${projectName} -e production`));
85
75
  }
86
76
  catch (err) {
87
- console.log(chalk_1.default.yellow(`Could not update ${gitignorePath}: ${err.message}. Add \`.solidactions/\` to it manually — ${path_1.default.join(targetDir, '.solidactions', 'config.json')} contains your API key.`));
88
- }
89
- }
90
- async function init(apiKey, options) {
91
- let host;
92
- if (options.host) {
93
- host = options.host;
94
- }
95
- else if (options.dev) {
96
- host = 'http://localhost:8000';
97
- }
98
- else {
99
- host = 'https://app.solidactions.com';
100
- }
101
- if (!apiKey || apiKey.trim().length === 0) {
102
- console.error(chalk_1.default.red('Error: API key is required.'));
103
- console.log(chalk_1.default.gray('Generate an API key at: ') + chalk_1.default.blue(`${host}/settings/api-keys`));
104
- process.exit(1);
105
- }
106
- // Determine target location.
107
- let target;
108
- if (options.local && options.global) {
109
- console.error(chalk_1.default.red('Error: --local and --global are mutually exclusive.'));
110
- process.exit(1);
111
- }
112
- else if (options.local) {
113
- target = 'local';
114
- }
115
- else if (options.global) {
116
- target = 'global';
117
- }
118
- else if (process.stdin.isTTY) {
119
- target = await promptLocation();
120
- }
121
- else {
122
- console.error(chalk_1.default.red('Refusing to init non-interactively. Pass --local or --global.'));
123
- process.exit(1);
124
- }
125
- const targetPath = target === 'local' ? (0, config_1.getLocalConfigPath)() : (0, config_1.getGlobalConfigPath)();
126
- console.log(chalk_1.default.blue(`Initializing SolidActions CLI...`));
127
- console.log(chalk_1.default.gray(`Host: ${host}`));
128
- if ((0, config_1.readConfigFile)(targetPath)) {
129
- console.log(chalk_1.default.yellow(`Existing config at ${targetPath} will be overwritten.`));
130
- }
131
- const config = {
132
- host,
133
- apiKey: apiKey.trim(),
134
- };
135
- (0, config_1.writeConfigFile)(targetPath, config);
136
- if (target === 'local') {
137
- await ensureGitignoreCovers(process.cwd(), !!options.gitignore);
138
- }
139
- console.log(chalk_1.default.green('CLI initialized successfully!'));
140
- console.log(chalk_1.default.gray(`Configuration saved to ${targetPath}`));
141
- console.log('');
142
- // Workspace selection — ensureWorkspaceSelected writes to the right file via the resolver.
143
- try {
144
- if (options.workspace) {
145
- await (0, workspaces_1.workspaceSet)(options.workspace);
77
+ if (err.message?.includes('rate limit')) {
78
+ console.error(chalk_1.default.red(err.message));
146
79
  }
147
- else {
148
- await (0, api_1.ensureWorkspaceSelected)(config);
80
+ else if (err.message?.includes('not found')) {
81
+ console.error(chalk_1.default.red(err.message));
149
82
  }
150
- }
151
- catch {
152
- console.log(chalk_1.default.yellow('Could not set workspace. Run `solidactions workspace set` later.'));
153
- }
154
- console.log('');
155
- console.log(chalk_1.default.blue('Next step — install AI helper docs and skills:'));
156
- console.log(chalk_1.default.gray(' solidactions ai init Picks CLAUDE.md or AGENTS.md interactively'));
157
- console.log('');
158
- console.log(chalk_1.default.blue('Quick start:'));
159
- console.log(chalk_1.default.gray(' solidactions project deploy <name> Deploy current directory'));
160
- console.log(chalk_1.default.gray(' solidactions run start <proj> <wf> Run a workflow'));
161
- console.log(chalk_1.default.gray(' solidactions run list List recent runs'));
162
- }
163
- function logout(options = {}) {
164
- if (options.local && options.global) {
165
- console.error(chalk_1.default.red('Error: --local and --global are mutually exclusive.'));
166
- process.exit(1);
167
- }
168
- const globalPath = (0, config_1.getGlobalConfigPath)();
169
- const localPath = (0, config_1.findLocalConfigPath)(process.cwd());
170
- let targetPath;
171
- if (options.local) {
172
- targetPath = localPath;
173
- if (!targetPath) {
174
- console.error(chalk_1.default.red(`No local config found in ${process.cwd()} or any parent directory.`));
175
- process.exit(1);
83
+ else if (err.message?.includes('Failed to fetch')) {
84
+ console.error(chalk_1.default.red('Network error: Could not reach GitHub to fetch the template. Check your internet connection.'));
85
+ }
86
+ else {
87
+ console.error(chalk_1.default.red('Error:'), err.message);
176
88
  }
177
- }
178
- else if (options.global) {
179
- targetPath = globalPath;
180
- }
181
- else {
182
- targetPath = localPath ?? globalPath;
183
- }
184
- const removed = (0, config_1.removeConfigFile)(targetPath);
185
- if (removed) {
186
- console.log(chalk_1.default.green(`Logged out. Removed ${targetPath}`));
187
- }
188
- else {
189
- console.log(chalk_1.default.gray(`Not logged in (no config at ${targetPath}).`));
190
- }
191
- }
192
- function whoami() {
193
- const resolved = (0, config_1.resolveConfig)();
194
- if (!resolved || !resolved.config.apiKey) {
195
- console.log(chalk_1.default.yellow('Not initialized.'));
196
- console.log(chalk_1.default.gray('Run "solidactions init <api-key>" to configure.'));
197
89
  process.exit(1);
198
90
  }
199
- const { config, sources } = resolved;
200
- const maskedKey = config.apiKey.length > 12
201
- ? `${config.apiKey.substring(0, 8)}...${config.apiKey.slice(-4)}`
202
- : config.apiKey;
203
- const fmt = (src) => {
204
- if (src === 'env')
205
- return chalk_1.default.gray('(from $SOLIDACTIONS_* env var)');
206
- if (src === null)
207
- return chalk_1.default.gray('(unset)');
208
- return chalk_1.default.gray(`(from ${src})`);
209
- };
210
- console.log(chalk_1.default.blue('Current configuration:'));
211
- console.log(` Host: ${config.host.padEnd(40)} ${fmt(sources.host)}`);
212
- console.log(` API Key: ${maskedKey.padEnd(40)} ${fmt(sources.apiKey)}`);
213
- console.log(` Workspace: ${(config.workspaceId ?? '').padEnd(40)} ${fmt(sources.workspaceId)}`);
214
91
  }
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getConfig = getConfig;
7
+ exports.saveConfig = saveConfig;
8
+ exports.clearConfig = clearConfig;
9
+ exports.login = login;
10
+ exports.logout = logout;
11
+ exports.whoami = whoami;
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const readline_1 = __importDefault(require("readline"));
15
+ const chalk_1 = __importDefault(require("chalk"));
16
+ const api_1 = require("../utils/api");
17
+ const workspaces_1 = require("./workspaces");
18
+ const config_1 = require("../utils/config");
19
+ function getConfig() {
20
+ const resolved = (0, config_1.resolveConfig)();
21
+ return resolved ? resolved.config : null;
22
+ }
23
+ function saveConfig(config) {
24
+ const resolved = (0, config_1.resolveConfig)();
25
+ const targetPath = resolved ? resolved.activePath : (0, config_1.getGlobalConfigPath)();
26
+ (0, config_1.writeConfigFile)(targetPath, config);
27
+ }
28
+ function clearConfig() {
29
+ (0, config_1.removeConfigFile)((0, config_1.getGlobalConfigPath)());
30
+ }
31
+ async function promptLocation() {
32
+ const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
33
+ try {
34
+ while (true) {
35
+ const answer = await new Promise((resolve) => {
36
+ rl.question(chalk_1.default.blue('Save config locally (./.solidactions) or globally (~/.solidactions)? [global] '), resolve);
37
+ });
38
+ const normalized = answer.trim().toLowerCase();
39
+ if (normalized === '' || normalized === 'global' || normalized === 'g')
40
+ return 'global';
41
+ if (normalized === 'local' || normalized === 'l')
42
+ return 'local';
43
+ console.log(chalk_1.default.yellow("Please answer 'local' or 'global' (or press Enter for global)."));
44
+ }
45
+ }
46
+ finally {
47
+ rl.close();
48
+ }
49
+ }
50
+ async function ensureGitignoreCovers(targetDir, auto) {
51
+ const gitignorePath = path_1.default.join(targetDir, '.gitignore');
52
+ const patternToAdd = '.solidactions/';
53
+ let existing = '';
54
+ if (fs_1.default.existsSync(gitignorePath)) {
55
+ existing = fs_1.default.readFileSync(gitignorePath, 'utf-8');
56
+ const lines = existing.split('\n').map((l) => l.trim());
57
+ const isCovered = lines.some((line) => {
58
+ const normalized = line
59
+ .replace(/^\*\*\//, '')
60
+ .replace(/^\//, '')
61
+ .replace(/\/(\*\*|\*)?$/, '');
62
+ return normalized === '.solidactions';
63
+ });
64
+ if (isCovered) {
65
+ return; // already covered
66
+ }
67
+ }
68
+ let shouldAdd = auto;
69
+ if (!shouldAdd && process.stdin.isTTY) {
70
+ const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
71
+ const answer = await new Promise((resolve) => {
72
+ rl.question(chalk_1.default.yellow(`Local config contains an API key. Add \`.solidactions/\` to ${gitignorePath}? [Y/n] `), resolve);
73
+ });
74
+ rl.close();
75
+ shouldAdd = !(answer.trim().toLowerCase().startsWith('n'));
76
+ }
77
+ if (!shouldAdd) {
78
+ console.log(chalk_1.default.yellow(`Skipping .gitignore update. Remember: ${path_1.default.join(targetDir, '.solidactions', 'config.json')} contains your API key.`));
79
+ return;
80
+ }
81
+ const prefix = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
82
+ try {
83
+ fs_1.default.writeFileSync(gitignorePath, `${existing}${prefix}${patternToAdd}\n`);
84
+ console.log(chalk_1.default.green(`Added \`${patternToAdd}\` to ${gitignorePath}.`));
85
+ }
86
+ catch (err) {
87
+ console.log(chalk_1.default.yellow(`Could not update ${gitignorePath}: ${err.message}. Add \`.solidactions/\` to it manually — ${path_1.default.join(targetDir, '.solidactions', 'config.json')} contains your API key.`));
88
+ }
89
+ }
90
+ async function login(apiKey, options) {
91
+ let host;
92
+ if (options.host) {
93
+ host = options.host;
94
+ }
95
+ else if (options.dev) {
96
+ host = 'http://localhost:8000';
97
+ }
98
+ else {
99
+ host = 'https://app.solidactions.com';
100
+ }
101
+ if (!apiKey || apiKey.trim().length === 0) {
102
+ console.error(chalk_1.default.red('Error: API key is required.'));
103
+ console.log(chalk_1.default.gray('Generate an API key at: ') + chalk_1.default.blue(`${host}/settings/api-keys`));
104
+ process.exit(1);
105
+ }
106
+ // Determine target location.
107
+ let target;
108
+ if (options.local && options.global) {
109
+ console.error(chalk_1.default.red('Error: --local and --global are mutually exclusive.'));
110
+ process.exit(1);
111
+ }
112
+ else if (options.local) {
113
+ target = 'local';
114
+ }
115
+ else if (options.global) {
116
+ target = 'global';
117
+ }
118
+ else if (process.stdin.isTTY) {
119
+ target = await promptLocation();
120
+ }
121
+ else {
122
+ console.error(chalk_1.default.red('Refusing to init non-interactively. Pass --local or --global.'));
123
+ process.exit(1);
124
+ }
125
+ const targetPath = target === 'local' ? (0, config_1.getLocalConfigPath)() : (0, config_1.getGlobalConfigPath)();
126
+ console.log(chalk_1.default.blue(`Initializing SolidActions CLI...`));
127
+ console.log(chalk_1.default.gray(`Host: ${host}`));
128
+ if ((0, config_1.readConfigFile)(targetPath)) {
129
+ console.log(chalk_1.default.yellow(`Existing config at ${targetPath} will be overwritten.`));
130
+ }
131
+ const config = {
132
+ host,
133
+ apiKey: apiKey.trim(),
134
+ };
135
+ (0, config_1.writeConfigFile)(targetPath, config);
136
+ if (target === 'local') {
137
+ await ensureGitignoreCovers(process.cwd(), !!options.gitignore);
138
+ }
139
+ console.log(chalk_1.default.green('Logged in successfully!'));
140
+ console.log(chalk_1.default.gray(`Configuration saved to ${targetPath}`));
141
+ console.log('');
142
+ // Workspace selection — ensureWorkspaceSelected writes to the right file via the resolver.
143
+ try {
144
+ if (options.workspace) {
145
+ await (0, workspaces_1.workspaceSet)(options.workspace);
146
+ }
147
+ else {
148
+ await (0, api_1.ensureWorkspaceSelected)(config);
149
+ }
150
+ }
151
+ catch {
152
+ console.log(chalk_1.default.yellow('Could not set workspace. Run `solidactions workspace set` later.'));
153
+ }
154
+ console.log('');
155
+ console.log(chalk_1.default.blue('Next step — scaffold a new project (includes AI tooling):'));
156
+ console.log(chalk_1.default.gray(' solidactions init <project-name> Creates ./<project-name>/ with scaffold + AI skills'));
157
+ console.log(chalk_1.default.gray(' solidactions init Scaffolds in the current (empty) directory'));
158
+ console.log('');
159
+ console.log(chalk_1.default.blue('Quick start:'));
160
+ console.log(chalk_1.default.gray(' solidactions project deploy <name> Deploy current directory'));
161
+ console.log(chalk_1.default.gray(' solidactions run start <proj> <wf> Run a workflow'));
162
+ console.log(chalk_1.default.gray(' solidactions run list List recent runs'));
163
+ }
164
+ function logout(options = {}) {
165
+ if (options.local && options.global) {
166
+ console.error(chalk_1.default.red('Error: --local and --global are mutually exclusive.'));
167
+ process.exit(1);
168
+ }
169
+ const globalPath = (0, config_1.getGlobalConfigPath)();
170
+ const localPath = (0, config_1.findLocalConfigPath)(process.cwd());
171
+ let targetPath;
172
+ if (options.local) {
173
+ targetPath = localPath;
174
+ if (!targetPath) {
175
+ console.error(chalk_1.default.red(`No local config found in ${process.cwd()} or any parent directory.`));
176
+ process.exit(1);
177
+ }
178
+ }
179
+ else if (options.global) {
180
+ targetPath = globalPath;
181
+ }
182
+ else {
183
+ targetPath = localPath ?? globalPath;
184
+ }
185
+ const removed = (0, config_1.removeConfigFile)(targetPath);
186
+ if (removed) {
187
+ console.log(chalk_1.default.green(`Logged out. Removed ${targetPath}`));
188
+ }
189
+ else {
190
+ console.log(chalk_1.default.gray(`Not logged in (no config at ${targetPath}).`));
191
+ }
192
+ }
193
+ function whoami() {
194
+ const resolved = (0, config_1.resolveConfig)();
195
+ if (!resolved || !resolved.config.apiKey) {
196
+ console.log(chalk_1.default.yellow('Not initialized.'));
197
+ console.log(chalk_1.default.gray('Run "solidactions login <api-key>" to configure.'));
198
+ process.exit(1);
199
+ }
200
+ const { config, sources } = resolved;
201
+ const maskedKey = config.apiKey.length > 12
202
+ ? `${config.apiKey.substring(0, 8)}...${config.apiKey.slice(-4)}`
203
+ : config.apiKey;
204
+ const fmt = (src) => {
205
+ if (src === 'env')
206
+ return chalk_1.default.gray('(from $SOLIDACTIONS_* env var)');
207
+ if (src === null)
208
+ return chalk_1.default.gray('(unset)');
209
+ return chalk_1.default.gray(`(from ${src})`);
210
+ };
211
+ console.log(chalk_1.default.blue('Current configuration:'));
212
+ console.log(` Host: ${config.host.padEnd(40)} ${fmt(sources.host)}`);
213
+ console.log(` API Key: ${maskedKey.padEnd(40)} ${fmt(sources.apiKey)}`);
214
+ console.log(` Workspace: ${(config.workspaceId ?? '').padEnd(40)} ${fmt(sources.workspaceId)}`);
215
+ }
@@ -38,7 +38,7 @@ async function projectList() {
38
38
  catch (error) {
39
39
  if (error.response) {
40
40
  if (error.response.status === 401) {
41
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
41
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
42
42
  }
43
43
  else {
44
44
  console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
@@ -26,7 +26,7 @@ async function logsBuild(projectName) {
26
26
  catch (error) {
27
27
  if (error.response) {
28
28
  if (error.response.status === 401) {
29
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
29
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
30
30
  }
31
31
  else if (error.response.status === 404) {
32
32
  console.error(chalk_1.default.red(`Project "${projectName}" not found.`));
@@ -55,7 +55,7 @@ async function pull(projectName, destPath, options = {}) {
55
55
  console.error(chalk_1.default.red(`Project "${projectName}" not found.`));
56
56
  }
57
57
  else if (error.response.status === 401) {
58
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
58
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
59
59
  }
60
60
  else {
61
61
  console.error(chalk_1.default.red(`Failed: ${error.response.status}`));
@@ -57,7 +57,7 @@ async function runs(projectName, options = {}) {
57
57
  catch (error) {
58
58
  if (error.response) {
59
59
  if (error.response.status === 401) {
60
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
60
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
61
61
  }
62
62
  else {
63
63
  console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
@@ -69,7 +69,7 @@ async function run(projectName, workflowName, options) {
69
69
  catch (error) {
70
70
  if (error.response) {
71
71
  if (error.response.status === 401) {
72
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
72
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
73
73
  }
74
74
  else if (error.response.status === 404) {
75
75
  console.error(chalk_1.default.red('Project or workflow not found.'));
@@ -101,7 +101,7 @@ async function runView(runId, options) {
101
101
  catch (error) {
102
102
  if (error.response) {
103
103
  if (error.response.status === 401) {
104
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
104
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
105
105
  }
106
106
  else if (error.response.status === 404) {
107
107
  console.error(chalk_1.default.red('Run not found.'));
@@ -46,7 +46,7 @@ async function scheduleDelete(projectName, scheduleId, options = {}) {
46
46
  catch (error) {
47
47
  if (error.response) {
48
48
  if (error.response.status === 401) {
49
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
49
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
50
50
  }
51
51
  else if (error.response.status === 404) {
52
52
  console.error(chalk_1.default.red(`Project "${projectName}" or schedule ${scheduleId} not found.`));
@@ -41,7 +41,7 @@ async function scheduleList(projectName) {
41
41
  catch (error) {
42
42
  if (error.response) {
43
43
  if (error.response.status === 401) {
44
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
44
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
45
45
  }
46
46
  else if (error.response.status === 404) {
47
47
  console.error(chalk_1.default.red(`Project "${projectName}" not found.`));
@@ -90,7 +90,7 @@ async function scheduleSet(projectName, cron, options) {
90
90
  catch (error) {
91
91
  if (error.response) {
92
92
  if (error.response.status === 401) {
93
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
93
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
94
94
  }
95
95
  else if (error.response.status === 404) {
96
96
  console.error(chalk_1.default.red(`Project "${projectName}" not found.`));
@@ -51,7 +51,7 @@ async function webhookList(projectName, options = {}) {
51
51
  catch (error) {
52
52
  if (error.response) {
53
53
  if (error.response.status === 401) {
54
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
54
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions login <api-key>" to re-configure.'));
55
55
  }
56
56
  else if (error.response.status === 404) {
57
57
  console.error(chalk_1.default.red(`Project "${projectName}" not found.`));
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const commander_1 = require("commander");
9
9
  const deploy_1 = require("./commands/deploy");
10
+ const login_1 = require("./commands/login");
10
11
  const init_1 = require("./commands/init");
11
12
  const pull_1 = require("./commands/pull");
12
13
  const project_list_1 = require("./commands/project-list");
@@ -61,8 +62,8 @@ program
61
62
  // Top-level commands
62
63
  // =============================================================================
63
64
  program
64
- .command('init')
65
- .description('Initialize the CLI with your API key')
65
+ .command('login')
66
+ .description('Authenticate the CLI with your API key')
66
67
  .argument('<api-key>', 'Your SolidActions API key')
67
68
  .option('--dev', 'Use local development server (http://localhost:8000)')
68
69
  .option('--host <url>', 'Custom API host URL')
@@ -71,7 +72,17 @@ program
71
72
  .option('--global', 'Save config to ~/.solidactions/config.json (default if prompted)')
72
73
  .option('--gitignore', 'With --local, add .solidactions/ to .gitignore without prompting')
73
74
  .action(async (apiKey, options) => {
74
- await (0, init_1.init)(apiKey, options);
75
+ await (0, login_1.login)(apiKey, options);
76
+ });
77
+ program
78
+ .command('init')
79
+ .description('Scaffold a new SolidActions project (files + AI skills)')
80
+ .argument('[directory]', 'Directory to create (omit to scaffold in the current empty directory)')
81
+ .option('--no-skills', 'Skip AI skills/SDK reference install (scaffold files only)')
82
+ .option('--claude', 'Use CLAUDE.md for the AI helper file')
83
+ .option('--agents', 'Use AGENTS.md for the AI helper file (Codex, Cursor, Gemini, Windsurf)')
84
+ .action(async (directory, options) => {
85
+ await (0, init_1.init)(directory, options);
75
86
  });
76
87
  program
77
88
  .command('logout')
@@ -79,13 +90,13 @@ program
79
90
  .option('--local', 'Remove only the nearest local ./.solidactions/config.json')
80
91
  .option('--global', 'Remove only ~/.solidactions/config.json')
81
92
  .action((options) => {
82
- (0, init_1.logout)(options);
93
+ (0, login_1.logout)(options);
83
94
  });
84
95
  program
85
96
  .command('whoami')
86
97
  .description('Show current configuration')
87
98
  .action(() => {
88
- (0, init_1.whoami)();
99
+ (0, login_1.whoami)();
89
100
  });
90
101
  program
91
102
  .command('dev')
package/dist/utils/api.js CHANGED
@@ -29,7 +29,7 @@ function getApiHeaders(config, contentType) {
29
29
  function requireResolvedConfig() {
30
30
  const resolved = (0, config_1.resolveConfig)();
31
31
  if (!resolved || !resolved.config.apiKey) {
32
- console.error(chalk_1.default.red('Not initialized. Run `solidactions init <api-key>` first.'));
32
+ console.error(chalk_1.default.red('Not initialized. Run `solidactions login <api-key>` first.'));
33
33
  process.exit(1);
34
34
  }
35
35
  return resolved;
@@ -72,7 +72,7 @@ async function ensureWorkspaceSelected(config) {
72
72
  }
73
73
  catch (error) {
74
74
  if (error.response?.status === 401) {
75
- console.error(chalk_1.default.red('Authentication failed. Run `solidactions init <api-key>` to reconfigure.'));
75
+ console.error(chalk_1.default.red('Authentication failed. Run `solidactions login <api-key>` to reconfigure.'));
76
76
  }
77
77
  else {
78
78
  console.error(chalk_1.default.red('Failed to fetch workspaces:'), error.response?.data?.message || error.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidactions/cli",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
4
4
  "description": "SolidActions CLI - Deploy and manage workflow automation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {