@solidactions/cli 1.3.0 → 1.6.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.
- package/README.md +14 -2
- package/dist/commands/ai-init.js +19 -13
- package/dist/commands/deploy.js +65 -13
- package/dist/commands/init.js +3 -0
- package/dist/index.js +3 -3
- package/dist/utils/skills.js +59 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ npm install -g @solidactions/cli
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
solidactions init <api-key> # Initialize (prompts for workspace)
|
|
15
|
-
solidactions init
|
|
15
|
+
solidactions ai init # Install AI skills and SDK reference (required for AI-assisted dev)
|
|
16
16
|
solidactions project deploy <project> <path> # Deploy a project
|
|
17
17
|
solidactions run start <project> <workflow> # Trigger a workflow
|
|
18
18
|
solidactions run view <run-id> # Inspect a run
|
|
@@ -127,9 +127,21 @@ Use `solidactions <command> --help` for full flag details on any command.
|
|
|
127
127
|
|
|
128
128
|
| Command | Key Flags | Description |
|
|
129
129
|
|---------|-----------|-------------|
|
|
130
|
-
| `ai init` | `--claude`, `--agents` | Install AI
|
|
130
|
+
| `ai init` | `--claude`, `--agents` | Install AI skills and SDK reference |
|
|
131
131
|
| `ai examples [names...]` | `--all`, `--overwrite` | Install example workflows |
|
|
132
132
|
|
|
133
|
+
`ai init` installs three auto-activating SolidActions skills and a
|
|
134
|
+
full SDK reference into your project:
|
|
135
|
+
|
|
136
|
+
- Skills go to `.claude/skills/` (for `--claude` / Claude Code) or
|
|
137
|
+
`.agents/skills/` (for `--agents` / Codex, Cursor, Gemini, Windsurf).
|
|
138
|
+
Codex auto-discovers the `.agents/skills/` path — see
|
|
139
|
+
https://developers.openai.com/codex/skills.
|
|
140
|
+
- The SDK reference is saved to `.solidactions/sdk-reference.md`.
|
|
141
|
+
- A slim pointer section is injected into `CLAUDE.md` or `AGENTS.md`
|
|
142
|
+
listing the skills and inlining the highest-cost hard rules
|
|
143
|
+
(determinism, step discipline, messaging) as a safety net.
|
|
144
|
+
|
|
133
145
|
## Development
|
|
134
146
|
|
|
135
147
|
```bash
|
package/dist/commands/ai-init.js
CHANGED
|
@@ -8,11 +8,13 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const prompts_1 = __importDefault(require("prompts"));
|
|
10
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
11
12
|
const github_1 = require("../utils/github");
|
|
12
13
|
const markers_1 = require("../utils/markers");
|
|
14
|
+
const skills_1 = require("../utils/skills");
|
|
13
15
|
async function aiInit(options = {}) {
|
|
14
16
|
try {
|
|
15
|
-
// Determine target file
|
|
17
|
+
// Determine target file.
|
|
16
18
|
let targetFile;
|
|
17
19
|
if (options.claude && options.agents) {
|
|
18
20
|
console.error(chalk_1.default.red('Please specify only one of --claude or --agents'));
|
|
@@ -30,8 +32,8 @@ async function aiInit(options = {}) {
|
|
|
30
32
|
name: 'file',
|
|
31
33
|
message: 'Which AI helper file?',
|
|
32
34
|
choices: [
|
|
33
|
-
{ title: 'CLAUDE.md', value: 'CLAUDE.md' },
|
|
34
|
-
{ title: 'AGENTS.md', value: 'AGENTS.md' },
|
|
35
|
+
{ title: 'CLAUDE.md (Claude Code)', value: 'CLAUDE.md' },
|
|
36
|
+
{ title: 'AGENTS.md (Codex, Cursor, Gemini, etc.)', value: 'AGENTS.md' },
|
|
35
37
|
],
|
|
36
38
|
});
|
|
37
39
|
if (!response.file) {
|
|
@@ -41,22 +43,26 @@ async function aiInit(options = {}) {
|
|
|
41
43
|
targetFile = response.file;
|
|
42
44
|
}
|
|
43
45
|
console.log(chalk_1.default.blue('Fetching AI helper content...'));
|
|
44
|
-
// Fetch
|
|
45
|
-
const aiContent = await (0,
|
|
46
|
-
// Fetch SDK reference
|
|
46
|
+
// Fetch slim helper content for the chosen target.
|
|
47
|
+
const aiContent = await (0, skills_1.fetchAiHelperContent)(targetFile);
|
|
48
|
+
// Fetch SDK reference (always).
|
|
47
49
|
const sdkContent = await (0, github_1.fetchRawFile)('SolidActions', 'solidactions-ts-sdk', 'docs/sdk-reference.md');
|
|
48
|
-
// Save SDK reference to .solidactions/sdk-reference.md
|
|
50
|
+
// Save SDK reference to .solidactions/sdk-reference.md.
|
|
49
51
|
fs_extra_1.default.ensureDirSync('.solidactions');
|
|
50
52
|
fs_1.default.writeFileSync('.solidactions/sdk-reference.md', sdkContent, 'utf8');
|
|
51
53
|
console.log(chalk_1.default.green('✓ SDK reference saved to .solidactions/sdk-reference.md'));
|
|
52
|
-
//
|
|
54
|
+
// Always install skills — no prompt. The user chose `ai init <target>`,
|
|
55
|
+
// so they want AI tooling set up.
|
|
56
|
+
const targetDir = (0, skills_1.skillTargetDir)(targetFile);
|
|
57
|
+
console.log(chalk_1.default.blue(`Installing SolidActions skills to ${path_1.default.relative(process.cwd(), targetDir)}/ ...`));
|
|
58
|
+
const { written } = await (0, skills_1.installSkills)(targetDir);
|
|
59
|
+
for (const f of written) {
|
|
60
|
+
console.log(chalk_1.default.green(`✓ ${path_1.default.relative(process.cwd(), f)}`));
|
|
61
|
+
}
|
|
62
|
+
// Upsert the AI helper marker section.
|
|
53
63
|
(0, markers_1.upsertMarkerSection)(targetFile, 'SolidActions', aiContent);
|
|
54
|
-
// Upsert SDK reference pointer section
|
|
55
|
-
const sdkReferenceNote = `## SolidActions SDK Reference
|
|
56
|
-
|
|
57
|
-
The full SDK API reference is available at \`.solidactions/sdk-reference.md\`. Refer to it for detailed function signatures, error classes, retry configuration, and advanced patterns like forking, streaming, and signal URLs.`;
|
|
58
|
-
(0, markers_1.upsertMarkerSection)(targetFile, 'SolidActions SDK Reference', sdkReferenceNote);
|
|
59
64
|
console.log(chalk_1.default.green(`✓ AI helper installed to ${targetFile}`));
|
|
65
|
+
console.log(chalk_1.default.gray(' Existing files may still contain a separate "SolidActions SDK Reference" marker block from older CLI versions. That content is now redundant and can be deleted manually.'));
|
|
60
66
|
}
|
|
61
67
|
catch (error) {
|
|
62
68
|
if (error.message?.includes('rate limit')) {
|
package/dist/commands/deploy.js
CHANGED
|
@@ -114,11 +114,51 @@ async function pushYamlDeclarations(config, projectSlug, yamlConfig) {
|
|
|
114
114
|
async function deploy(projectName, sourcePath, options = {}) {
|
|
115
115
|
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
116
116
|
const sourceDir = sourcePath ? path_1.default.resolve(sourcePath) : process.cwd();
|
|
117
|
-
|
|
117
|
+
// Capture whether -e was explicitly passed by the caller. Commander leaves
|
|
118
|
+
// options.env as undefined when no default is set and the flag is omitted.
|
|
119
|
+
const explicitEnv = options.env;
|
|
118
120
|
if (!fs_1.default.existsSync(sourceDir)) {
|
|
119
121
|
console.error(chalk_1.default.red(`Source directory not found: ${sourceDir}`));
|
|
120
122
|
process.exit(1);
|
|
121
123
|
}
|
|
124
|
+
// -------------------------------------------------------------------------
|
|
125
|
+
// Production-existence check — must happen BEFORE we apply any env default
|
|
126
|
+
// so that first-time deploys without -e get a helpful error instead of
|
|
127
|
+
// silently creating a dev-only project with no production root.
|
|
128
|
+
// -------------------------------------------------------------------------
|
|
129
|
+
let productionExists = null; // null = unknown (error path)
|
|
130
|
+
let productionSlug = null;
|
|
131
|
+
try {
|
|
132
|
+
const prodResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}`, {
|
|
133
|
+
headers: (0, api_1.getApiHeaders)(config),
|
|
134
|
+
});
|
|
135
|
+
productionExists = true;
|
|
136
|
+
productionSlug = prodResponse.data.slug || prodResponse.data.name;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error.response?.status === 404) {
|
|
140
|
+
productionExists = false;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// 5xx, network error, auth failure, etc. — fail conservatively rather
|
|
144
|
+
// than treating the project as non-existent and potentially creating it.
|
|
145
|
+
console.error(chalk_1.default.red('Failed to check project existence:'), error.response?.data?.message || error.message);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Decision: if production does not exist and no -e flag was given, the user
|
|
150
|
+
// must pick an environment explicitly to avoid creating a broken dev-only project.
|
|
151
|
+
if (productionExists === false && explicitEnv === undefined) {
|
|
152
|
+
console.error(chalk_1.default.red(`\nThis is the first deploy of "${projectName}" — please pick an environment explicitly:\n`));
|
|
153
|
+
console.error(` solidactions project deploy ${projectName} <path> -e production # most projects start here`);
|
|
154
|
+
console.error(` solidactions project deploy ${projectName} <path> -e dev # dev-only (uncommon; no production root will exist)`);
|
|
155
|
+
console.error(` solidactions project deploy ${projectName} <path> -e staging # staging-only (uncommon)`);
|
|
156
|
+
console.error(chalk_1.default.gray('\nTip: most projects should start with `-e production`. A project needs a production root before dev/staging children can attach to it.'));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
// Apply default: if production already exists and the user didn't pass -e,
|
|
160
|
+
// use 'dev' — this preserves existing behavior for mature projects.
|
|
161
|
+
const environment = explicitEnv ?? 'dev';
|
|
122
162
|
const envLabel = environment !== 'production' ? ` (${environment})` : '';
|
|
123
163
|
console.log(chalk_1.default.blue(`Deploying to project "${projectName}"${envLabel}...`));
|
|
124
164
|
console.log(chalk_1.default.gray(`Source: ${sourceDir}`));
|
|
@@ -149,24 +189,36 @@ async function deploy(projectName, sourcePath, options = {}) {
|
|
|
149
189
|
process.exit(1);
|
|
150
190
|
}
|
|
151
191
|
console.log(chalk_1.default.green('✓ Project structure validated'));
|
|
152
|
-
// Check if project exists
|
|
192
|
+
// Check if the target environment's project record exists; create if needed.
|
|
193
|
+
// For production deploys where we already confirmed existence above, reuse
|
|
194
|
+
// the cached slug to avoid a duplicate GET.
|
|
153
195
|
let projectSlug = projectName;
|
|
154
196
|
try {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
197
|
+
if (environment === 'production' && productionExists === true && productionSlug !== null) {
|
|
198
|
+
// Already confirmed — skip the re-lookup.
|
|
199
|
+
projectSlug = productionSlug;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// For non-production environments, append the environment to the slug for lookup
|
|
203
|
+
const lookupSlug = environment === 'production'
|
|
204
|
+
? projectName
|
|
205
|
+
: `${projectName}-${environment}`;
|
|
206
|
+
const checkResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${lookupSlug}`, {
|
|
207
|
+
headers: (0, api_1.getApiHeaders)(config),
|
|
208
|
+
});
|
|
209
|
+
projectSlug = checkResponse.data.slug || checkResponse.data.name;
|
|
210
|
+
}
|
|
163
211
|
}
|
|
164
212
|
catch (error) {
|
|
165
213
|
if (error.response?.status === 404) {
|
|
166
|
-
// For non-production environments,
|
|
214
|
+
// For non-production environments, require --create or give a clear hint
|
|
167
215
|
if (environment !== 'production' && !options.create) {
|
|
168
|
-
console.
|
|
169
|
-
console.
|
|
216
|
+
console.error(chalk_1.default.red(`\nProject "${projectName}" doesn't have a ${environment} environment.\n`));
|
|
217
|
+
console.error(` If production is the intended target:`);
|
|
218
|
+
console.error(` solidactions project deploy ${projectName} <path> -e production`);
|
|
219
|
+
console.error('');
|
|
220
|
+
console.error(` If you really want a new ${environment} environment:`);
|
|
221
|
+
console.error(` solidactions project deploy ${projectName} <path> -e ${environment} --create`);
|
|
170
222
|
process.exit(1);
|
|
171
223
|
}
|
|
172
224
|
console.log(chalk_1.default.yellow(`Project "${projectName}"${envLabel} not found. Creating...`));
|
package/dist/commands/init.js
CHANGED
|
@@ -152,6 +152,9 @@ async function init(apiKey, options) {
|
|
|
152
152
|
console.log(chalk_1.default.yellow('Could not set workspace. Run `solidactions workspace set` later.'));
|
|
153
153
|
}
|
|
154
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('');
|
|
155
158
|
console.log(chalk_1.default.blue('Quick start:'));
|
|
156
159
|
console.log(chalk_1.default.gray(' solidactions project deploy <name> Deploy current directory'));
|
|
157
160
|
console.log(chalk_1.default.gray(' solidactions run start <proj> <wf> Run a workflow'));
|
package/dist/index.js
CHANGED
|
@@ -104,7 +104,7 @@ project
|
|
|
104
104
|
.description('Deploy a project to SolidActions')
|
|
105
105
|
.argument('<project-name>', 'Project name (will be created if it doesn\'t exist)')
|
|
106
106
|
.argument('[path]', 'Source directory to deploy (defaults to current directory)')
|
|
107
|
-
.option('-e, --env <environment>', 'Target environment (production/staging/dev)
|
|
107
|
+
.option('-e, --env <environment>', 'Target environment (production/staging/dev). Required on first deploy of a new project.')
|
|
108
108
|
.option('--create', 'Create environment project if it doesn\'t exist')
|
|
109
109
|
.option('--config-only', 'Sync YAML env declarations without building/deploying')
|
|
110
110
|
.action((projectName, path, options) => {
|
|
@@ -311,9 +311,9 @@ workspace
|
|
|
311
311
|
const ai = program.command('ai').description('AI helper tools');
|
|
312
312
|
ai
|
|
313
313
|
.command('init')
|
|
314
|
-
.description('Install AI
|
|
314
|
+
.description('Install SolidActions AI skills and SDK reference for AI-assisted development')
|
|
315
315
|
.option('--claude', 'Use CLAUDE.md (for Claude Code)')
|
|
316
|
-
.option('--agents', 'Use AGENTS.md (for Cursor, Windsurf, etc.)')
|
|
316
|
+
.option('--agents', 'Use AGENTS.md (for Codex, Cursor, Gemini, Windsurf, etc.)')
|
|
317
317
|
.action((options) => { (0, ai_init_1.aiInit)(options); });
|
|
318
318
|
ai
|
|
319
319
|
.command('examples')
|
|
@@ -0,0 +1,59 @@
|
|
|
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.skillTargetDir = skillTargetDir;
|
|
7
|
+
exports.installSkills = installSkills;
|
|
8
|
+
exports.fetchAiHelperContent = fetchAiHelperContent;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
12
|
+
const github_1 = require("./github");
|
|
13
|
+
const SKILL_NAMES = [
|
|
14
|
+
'solidactions-getting-started',
|
|
15
|
+
'solidactions-workflow-coding',
|
|
16
|
+
'solidactions-deploy-and-config',
|
|
17
|
+
];
|
|
18
|
+
const EXAMPLES_OWNER = 'SolidActions';
|
|
19
|
+
const EXAMPLES_REPO = 'solidactions-examples';
|
|
20
|
+
const SKILLS_PATH_PREFIX = 'skills';
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the skills directory for an AI helper target.
|
|
23
|
+
*
|
|
24
|
+
* - `CLAUDE.md` → `<cwd>/.claude/skills/` (Claude Code convention)
|
|
25
|
+
* - `AGENTS.md` → `<cwd>/.agents/skills/` (Codex auto-discovers this path;
|
|
26
|
+
* Cursor/Gemini read via AGENTS.md pointers)
|
|
27
|
+
*
|
|
28
|
+
* The directory does not need to exist — the caller creates it.
|
|
29
|
+
*/
|
|
30
|
+
function skillTargetDir(targetFile, cwd = process.cwd()) {
|
|
31
|
+
if (targetFile === 'CLAUDE.md') {
|
|
32
|
+
return path_1.default.join(cwd, '.claude', 'skills');
|
|
33
|
+
}
|
|
34
|
+
return path_1.default.join(cwd, '.agents', 'skills');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fetch all SolidActions skill files from the examples repo and write
|
|
38
|
+
* them into the target directory. Overwrites existing files (skills
|
|
39
|
+
* are versioned upstream).
|
|
40
|
+
*/
|
|
41
|
+
async function installSkills(targetDir) {
|
|
42
|
+
const written = [];
|
|
43
|
+
fs_extra_1.default.ensureDirSync(targetDir);
|
|
44
|
+
for (const skillName of SKILL_NAMES) {
|
|
45
|
+
const remotePath = `${SKILLS_PATH_PREFIX}/${skillName}.md`;
|
|
46
|
+
const content = await (0, github_1.fetchRawFile)(EXAMPLES_OWNER, EXAMPLES_REPO, remotePath);
|
|
47
|
+
const filePath = path_1.default.join(targetDir, `${skillName}.md`);
|
|
48
|
+
fs_1.default.writeFileSync(filePath, content, 'utf8');
|
|
49
|
+
written.push(filePath);
|
|
50
|
+
}
|
|
51
|
+
return { written };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Fetch the slim AI-helper content for the target file from the examples
|
|
55
|
+
* repo. Single source per target — no legacy fallback.
|
|
56
|
+
*/
|
|
57
|
+
async function fetchAiHelperContent(targetFile) {
|
|
58
|
+
return (0, github_1.fetchRawFile)(EXAMPLES_OWNER, EXAMPLES_REPO, targetFile);
|
|
59
|
+
}
|