@solidactions/cli 0.5.0 → 0.6.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.
@@ -0,0 +1,127 @@
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.aiExamples = aiExamples;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const prompts_1 = __importDefault(require("prompts"));
11
+ const fs_extra_1 = __importDefault(require("fs-extra"));
12
+ const github_1 = require("../utils/github");
13
+ const markers_1 = require("../utils/markers");
14
+ async function downloadDirectory(owner, repo, remotePath, localPath) {
15
+ const contents = await (0, github_1.listRepoContents)(owner, repo, remotePath);
16
+ fs_extra_1.default.ensureDirSync(localPath);
17
+ for (const item of contents) {
18
+ const localItemPath = path_1.default.join(localPath, item.name);
19
+ if (item.type === 'file') {
20
+ const content = await (0, github_1.fetchRawFile)(owner, repo, `${remotePath}/${item.name}`);
21
+ fs_1.default.writeFileSync(localItemPath, content, 'utf8');
22
+ }
23
+ else if (item.type === 'dir') {
24
+ await downloadDirectory(owner, repo, `${remotePath}/${item.name}`, localItemPath);
25
+ }
26
+ }
27
+ }
28
+ async function aiExamples(names, options = {}) {
29
+ try {
30
+ console.log(chalk_1.default.blue('Discovering available examples...'));
31
+ // Discover available examples
32
+ const repoContents = await (0, github_1.listRepoContents)('SolidActions', 'solidactions-examples');
33
+ const availableExamples = repoContents.filter((item) => item.type === 'dir' && !item.name.startsWith('.'));
34
+ if (availableExamples.length === 0) {
35
+ console.log(chalk_1.default.yellow('No examples found in the repository.'));
36
+ process.exit(0);
37
+ }
38
+ // Determine which to clone
39
+ let selected;
40
+ if (options.all) {
41
+ selected = availableExamples.map((e) => e.name);
42
+ }
43
+ else if (names.length > 0) {
44
+ const availableNames = availableExamples.map((e) => e.name);
45
+ for (const name of names) {
46
+ if (!availableNames.includes(name)) {
47
+ console.error(chalk_1.default.red(`Example "${name}" not found. Available: ${availableNames.join(', ')}`));
48
+ process.exit(1);
49
+ }
50
+ }
51
+ selected = names;
52
+ }
53
+ else {
54
+ const response = await (0, prompts_1.default)({
55
+ type: 'multiselect',
56
+ name: 'selected',
57
+ message: 'Select examples to install',
58
+ choices: availableExamples.map((e) => ({ title: e.name, value: e.name })),
59
+ });
60
+ if (!response.selected || response.selected.length === 0) {
61
+ console.log(chalk_1.default.yellow('No examples selected.'));
62
+ process.exit(0);
63
+ }
64
+ selected = response.selected;
65
+ }
66
+ // Clone each selected example
67
+ const examplesDir = '.solidactions/examples';
68
+ let installedCount = 0;
69
+ for (const name of selected) {
70
+ const localDir = path_1.default.join(examplesDir, name);
71
+ if (fs_1.default.existsSync(localDir) && !options.overwrite) {
72
+ console.log(chalk_1.default.yellow(`Example "${name}" already exists. Use --overwrite to replace.`));
73
+ continue;
74
+ }
75
+ if (fs_1.default.existsSync(localDir) && options.overwrite) {
76
+ fs_extra_1.default.removeSync(localDir);
77
+ }
78
+ console.log(chalk_1.default.gray(`Downloading ${name}...`));
79
+ await downloadDirectory('SolidActions', 'solidactions-examples', name, localDir);
80
+ console.log(chalk_1.default.green(`✓ Installed example: ${name}`));
81
+ installedCount++;
82
+ }
83
+ // Detect AI helper file and upsert examples reference
84
+ const aiHelperFile = (0, markers_1.findAiHelperFile)();
85
+ if (!aiHelperFile) {
86
+ console.log(chalk_1.default.yellow('No AI helper file found. Run "solidactions ai:init" first to create one.'));
87
+ }
88
+ else {
89
+ // List ALL installed examples (existing + newly installed)
90
+ const allInstalled = [];
91
+ if (fs_1.default.existsSync(examplesDir)) {
92
+ const dirs = fs_1.default.readdirSync(examplesDir, { withFileTypes: true });
93
+ for (const dir of dirs) {
94
+ if (dir.isDirectory()) {
95
+ allInstalled.push(dir.name);
96
+ }
97
+ }
98
+ }
99
+ const examplesList = allInstalled.map((name) => `- ${name}`).join('\n');
100
+ const examplesNote = `## SolidActions Examples
101
+
102
+ Example workflows are available in \`.solidactions/examples/\`. Use these as reference patterns when writing SolidActions workflows.
103
+
104
+ Installed examples:
105
+ ${examplesList}
106
+
107
+ When writing workflows, check these examples for patterns covering steps, sleep, signals, child workflows, retries, events, messaging, parallel execution, scheduling, OAuth, streaming, and webhooks.`;
108
+ (0, markers_1.upsertMarkerSection)(aiHelperFile, 'SolidActions Examples', examplesNote);
109
+ }
110
+ console.log(chalk_1.default.green(`✓ Installed ${installedCount} example(s) to .solidactions/examples/`));
111
+ }
112
+ catch (error) {
113
+ if (error.message?.includes('rate limit')) {
114
+ console.error(chalk_1.default.red(error.message));
115
+ }
116
+ else if (error.message?.includes('not found')) {
117
+ console.error(chalk_1.default.red(error.message));
118
+ }
119
+ else if (error.message?.includes('Failed to')) {
120
+ console.error(chalk_1.default.red('Network error: Could not reach GitHub. Check your internet connection.'));
121
+ }
122
+ else {
123
+ console.error(chalk_1.default.red('Error:'), error.message);
124
+ }
125
+ process.exit(1);
126
+ }
127
+ }
@@ -0,0 +1,76 @@
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.aiInit = aiInit;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const prompts_1 = __importDefault(require("prompts"));
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const github_1 = require("../utils/github");
12
+ const markers_1 = require("../utils/markers");
13
+ async function aiInit(options = {}) {
14
+ try {
15
+ // Determine target file
16
+ let targetFile;
17
+ if (options.claude && options.agents) {
18
+ console.error(chalk_1.default.red('Please specify only one of --claude or --agents'));
19
+ process.exit(1);
20
+ }
21
+ if (options.claude) {
22
+ targetFile = 'CLAUDE.md';
23
+ }
24
+ else if (options.agents) {
25
+ targetFile = 'AGENTS.md';
26
+ }
27
+ else {
28
+ const response = await (0, prompts_1.default)({
29
+ type: 'select',
30
+ name: 'file',
31
+ message: 'Which AI helper file?',
32
+ choices: [
33
+ { title: 'CLAUDE.md', value: 'CLAUDE.md' },
34
+ { title: 'AGENTS.md', value: 'AGENTS.md' },
35
+ ],
36
+ });
37
+ if (!response.file) {
38
+ console.log(chalk_1.default.yellow('Cancelled.'));
39
+ process.exit(0);
40
+ }
41
+ targetFile = response.file;
42
+ }
43
+ console.log(chalk_1.default.blue('Fetching AI helper content...'));
44
+ // Fetch AI helper content from examples repo
45
+ const aiContent = await (0, github_1.fetchRawFile)('SolidActions', 'solidactions-examples', 'CLAUDE.md');
46
+ // Fetch SDK reference
47
+ const sdkContent = await (0, github_1.fetchRawFile)('SolidActions', 'solidactions-ts-sdk', 'docs/sdk-reference.md');
48
+ // Save SDK reference to .solidactions/sdk-reference.md
49
+ fs_extra_1.default.ensureDirSync('.solidactions');
50
+ fs_1.default.writeFileSync('.solidactions/sdk-reference.md', sdkContent, 'utf8');
51
+ console.log(chalk_1.default.green('✓ SDK reference saved to .solidactions/sdk-reference.md'));
52
+ // Upsert main AI helper section
53
+ (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
+ console.log(chalk_1.default.green(`✓ AI helper installed to ${targetFile}`));
60
+ }
61
+ catch (error) {
62
+ if (error.message?.includes('rate limit')) {
63
+ console.error(chalk_1.default.red(error.message));
64
+ }
65
+ else if (error.message?.includes('not found')) {
66
+ console.error(chalk_1.default.red(error.message));
67
+ }
68
+ else if (error.message?.includes('Failed to fetch')) {
69
+ console.error(chalk_1.default.red('Network error: Could not reach GitHub. Check your internet connection.'));
70
+ }
71
+ else {
72
+ console.error(chalk_1.default.red('Error:'), error.message);
73
+ }
74
+ process.exit(1);
75
+ }
76
+ }
package/dist/index.js CHANGED
@@ -20,6 +20,8 @@ const schedule_list_1 = require("./commands/schedule-list");
20
20
  const schedule_delete_1 = require("./commands/schedule-delete");
21
21
  const webhooks_1 = require("./commands/webhooks");
22
22
  const dev_1 = require("./commands/dev");
23
+ const ai_init_1 = require("./commands/ai-init");
24
+ const ai_examples_1 = require("./commands/ai-examples");
23
25
  // eslint-disable-next-line @typescript-eslint/no-var-requires
24
26
  const pkg = require('../package.json');
25
27
  const program = new commander_1.Command();
@@ -227,4 +229,20 @@ program
227
229
  .action((project, options) => {
228
230
  (0, webhooks_1.webhookList)(project, options);
229
231
  });
232
+ // =============================================================================
233
+ // AI Helper
234
+ // =============================================================================
235
+ program
236
+ .command('ai:init')
237
+ .description('Install AI helper documentation (CLAUDE.md or AGENTS.md) for AI-assisted workflow development')
238
+ .option('--claude', 'Use CLAUDE.md (for Claude Code)')
239
+ .option('--agents', 'Use AGENTS.md (for Cursor, Windsurf, etc.)')
240
+ .action((options) => { (0, ai_init_1.aiInit)(options); });
241
+ program
242
+ .command('ai:examples')
243
+ .description('Install example workflows for AI reference')
244
+ .argument('[names...]', 'Example names to install (omit for interactive selector)')
245
+ .option('--all', 'Install all available examples')
246
+ .option('--overwrite', 'Overwrite existing examples without warning')
247
+ .action((names, options) => { (0, ai_examples_1.aiExamples)(names, options); });
230
248
  program.parse();
@@ -0,0 +1,41 @@
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.fetchRawFile = fetchRawFile;
7
+ exports.listRepoContents = listRepoContents;
8
+ const axios_1 = __importDefault(require("axios"));
9
+ async function fetchRawFile(owner, repo, path, branch = 'main') {
10
+ try {
11
+ const url = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${path}`;
12
+ const response = await axios_1.default.get(url, { responseType: 'text' });
13
+ return response.data;
14
+ }
15
+ catch (error) {
16
+ if (error.response?.status === 404) {
17
+ throw new Error(`File not found: ${owner}/${repo}/${path} (branch: ${branch})`);
18
+ }
19
+ throw new Error(`Failed to fetch ${owner}/${repo}/${path}: ${error.message}`);
20
+ }
21
+ }
22
+ async function listRepoContents(owner, repo, path = '', branch = 'main') {
23
+ try {
24
+ const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`;
25
+ const response = await axios_1.default.get(url);
26
+ return response.data.map((item) => ({
27
+ name: item.name,
28
+ type: item.type,
29
+ download_url: item.download_url,
30
+ }));
31
+ }
32
+ catch (error) {
33
+ if (error.response?.status === 403) {
34
+ throw new Error('GitHub API rate limit reached (60 requests/hour for unauthenticated access). Please wait a few minutes and try again.');
35
+ }
36
+ if (error.response?.status === 404) {
37
+ throw new Error(`Repository or path not found: ${owner}/${repo}/${path}`);
38
+ }
39
+ throw new Error(`Failed to list ${owner}/${repo}/${path}: ${error.message}`);
40
+ }
41
+ }
@@ -0,0 +1,45 @@
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.upsertMarkerSection = upsertMarkerSection;
7
+ exports.hasMarkerSection = hasMarkerSection;
8
+ exports.findAiHelperFile = findAiHelperFile;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const fs_extra_1 = __importDefault(require("fs-extra"));
12
+ function upsertMarkerSection(filePath, markerName, content) {
13
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(filePath));
14
+ let existing = '';
15
+ if (fs_1.default.existsSync(filePath)) {
16
+ existing = fs_1.default.readFileSync(filePath, 'utf8');
17
+ }
18
+ const openTag = `<!-- ${markerName} -->`;
19
+ const closeTag = `<!-- /${markerName} -->`;
20
+ const block = `${openTag}\n${content}\n${closeTag}`;
21
+ const openIdx = existing.indexOf(openTag);
22
+ const closeIdx = existing.indexOf(closeTag);
23
+ if (openIdx !== -1 && closeIdx !== -1) {
24
+ const before = existing.substring(0, openIdx);
25
+ const after = existing.substring(closeIdx + closeTag.length);
26
+ fs_1.default.writeFileSync(filePath, before + block + after, 'utf8');
27
+ }
28
+ else {
29
+ const result = existing.length > 0 ? existing + '\n\n' + block + '\n' : block + '\n';
30
+ fs_1.default.writeFileSync(filePath, result, 'utf8');
31
+ }
32
+ }
33
+ function hasMarkerSection(filePath, markerName) {
34
+ if (!fs_1.default.existsSync(filePath))
35
+ return false;
36
+ const content = fs_1.default.readFileSync(filePath, 'utf8');
37
+ return content.includes(`<!-- ${markerName} -->`) && content.includes(`<!-- /${markerName} -->`);
38
+ }
39
+ function findAiHelperFile() {
40
+ if (hasMarkerSection('CLAUDE.md', 'SolidActions'))
41
+ return 'CLAUDE.md';
42
+ if (hasMarkerSection('AGENTS.md', 'SolidActions'))
43
+ return 'AGENTS.md';
44
+ return null;
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidactions/cli",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "SolidActions CLI - Deploy and manage workflow automation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {