refira-cli 0.1.1 → 0.1.3
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 +8 -5
- package/dist/index.js +126 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,17 +102,19 @@ Options:
|
|
|
102
102
|
|
|
103
103
|
---
|
|
104
104
|
|
|
105
|
-
### 5. Live Preview & Harness Validation
|
|
105
|
+
### 5. Live Preview, Push & Harness Validation
|
|
106
106
|
|
|
107
|
-
#### `refira preview`
|
|
107
|
+
#### `refira push` (or `refira preview`)
|
|
108
108
|
Inspects an HTML markup file using the Refira Agent Harness. If violations are detected, execution halts with Exit Code 1 and actionable remediation instructions. If valid, the markup is streamed directly to the Refira Canvas.
|
|
109
109
|
|
|
110
110
|
```bash
|
|
111
|
+
refira push checkout.html --page checkout
|
|
112
|
+
# Alternatively:
|
|
111
113
|
refira preview checkout.html --page checkout
|
|
112
114
|
```
|
|
113
115
|
|
|
114
116
|
Arguments:
|
|
115
|
-
- `<file>`: Path to the HTML file to inspect and preview.
|
|
117
|
+
- `<file>`: Path to the HTML file to inspect and push/preview.
|
|
116
118
|
|
|
117
119
|
Options:
|
|
118
120
|
- `--page <slug>`: Target page slug.
|
|
@@ -136,15 +138,16 @@ refira skill install --global
|
|
|
136
138
|
|
|
137
139
|
## Agent Harness Invariants
|
|
138
140
|
|
|
139
|
-
All markup submitted via `refira preview` must conform to these strict architectural rules:
|
|
141
|
+
All markup submitted via `refira push` / `refira preview` must conform to these strict architectural rules:
|
|
140
142
|
|
|
141
143
|
| Rule | Status | Description |
|
|
142
144
|
|---|---|---|
|
|
143
145
|
| **Standalone HTML5 + Tailwind** | Required | Markup must be valid native HTML5 with Tailwind CSS utility classes. |
|
|
144
146
|
| **No UI Frameworks** | Prohibited | React, Vue, Svelte, Angular, Solid, and JSX syntax (`className=`, `onClick={...}`, `<Component />`) are rejected. |
|
|
145
|
-
| **No External / Inter-Page Navigation** | Prohibited | Prototypes run inside sandboxed iframes. Anchors (`<a href="...">`) pointing to external URLs
|
|
147
|
+
| **No External / Inter-Page Navigation** | Prohibited | Prototypes run inside sandboxed iframes. Anchors (`<a href="...">`) pointing to external URLs, relative pages (e.g. `/checkout`), empty `href=""`, or `javascript:` navigation are rejected. Only in-page anchors (`href="#section"`) or placeholders (`href="#"`) are allowed. Target attributes (`target="_blank"`, `target="_top"`) and `<form action="...">` are prohibited. For interactive triggers, use `<button type="button">`. |
|
|
146
148
|
| **No Raw Emojis in Markup** | Prohibited | Raw unicode emojis (e.g. rocket, fire, sparkles) in HTML text nodes or attributes are rejected to maintain professional aesthetic quality. Use Lucide Icons or Heroicons SVG elements instead. |
|
|
147
149
|
| **Graphics & Animation Libraries** | Permitted | Standalone CDN scripts such as Three.js, GSAP, Spline Viewer, and Lucide Icons are explicitly allowed in document `<head>` or `<script>`. |
|
|
150
|
+
| **Mandatory Push Execution** | Required | AI agents are strictly forbidden from stopping after writing a local file. The task is only complete when `refira push` succeeds with exit code 0. |
|
|
148
151
|
|
|
149
152
|
---
|
|
150
153
|
|
package/dist/index.js
CHANGED
|
@@ -1892,6 +1892,23 @@ var {
|
|
|
1892
1892
|
} = exports_commander;
|
|
1893
1893
|
|
|
1894
1894
|
// src/api-client.ts
|
|
1895
|
+
function extractFontFamily(typography, fallback = "Inter") {
|
|
1896
|
+
if (!typography)
|
|
1897
|
+
return fallback;
|
|
1898
|
+
if (typeof typography.font_family === "string" && typography.font_family.trim().length > 0) {
|
|
1899
|
+
return typography.font_family;
|
|
1900
|
+
}
|
|
1901
|
+
if (typography.primary && typeof typography.primary.font_family === "string" && typography.primary.font_family.trim().length > 0) {
|
|
1902
|
+
return typography.primary.font_family;
|
|
1903
|
+
}
|
|
1904
|
+
for (const item of Object.values(typography)) {
|
|
1905
|
+
if (item && typeof item === "object" && typeof item.font_family === "string") {
|
|
1906
|
+
return item.font_family;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
return fallback;
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1895
1912
|
class CliApiClient {
|
|
1896
1913
|
apiUrl;
|
|
1897
1914
|
apiKey;
|
|
@@ -1947,6 +1964,19 @@ class CliApiClient {
|
|
|
1947
1964
|
const body = await res.json();
|
|
1948
1965
|
return body.data;
|
|
1949
1966
|
}
|
|
1967
|
+
async startGenerating(projectId, pageIdentifier) {
|
|
1968
|
+
const url = `${this.apiUrl}/api/cli/projects/${projectId}/pages/${pageIdentifier}/start-generating`;
|
|
1969
|
+
const res = await fetch(url, {
|
|
1970
|
+
method: "POST",
|
|
1971
|
+
headers: this.getHeaders()
|
|
1972
|
+
});
|
|
1973
|
+
if (!res.ok) {
|
|
1974
|
+
const err = await res.text();
|
|
1975
|
+
throw new Error(`Failed to signal generation start (${res.status}): ${err}`);
|
|
1976
|
+
}
|
|
1977
|
+
const body = await res.json();
|
|
1978
|
+
return body.data;
|
|
1979
|
+
}
|
|
1950
1980
|
}
|
|
1951
1981
|
|
|
1952
1982
|
// src/config.ts
|
|
@@ -2047,18 +2077,22 @@ async function contextCommand(opts) {
|
|
|
2047
2077
|
const client = new CliApiClient(config.apiUrl, config.apiKey);
|
|
2048
2078
|
try {
|
|
2049
2079
|
const data = await client.getProjectContext(projectId);
|
|
2080
|
+
const projectName = data.context?.project_name ?? data.context?.project?.name ?? "Refira Project";
|
|
2081
|
+
const resolvedProjectId = data.context?.project_id ?? data.context?.project?.id ?? projectId;
|
|
2050
2082
|
console.log(`
|
|
2051
2083
|
======================================================================`);
|
|
2052
|
-
console.log(`\uD83D\uDCE6 REFIRA DESIGN CONTEXT: ${
|
|
2084
|
+
console.log(`\uD83D\uDCE6 REFIRA DESIGN CONTEXT: ${projectName} (${resolvedProjectId})`);
|
|
2053
2085
|
console.log("======================================================================");
|
|
2054
|
-
const font = data.context
|
|
2086
|
+
const font = extractFontFamily(data.context?.tokens?.typography, data.context?.project?.fontFamily ?? "Inter");
|
|
2055
2087
|
console.log(`
|
|
2056
2088
|
\uD83D\uDD24 Primary Typography: Google Fonts "${font}"`);
|
|
2057
|
-
|
|
2089
|
+
const colorTokens = data.context?.tokens?.colors ?? data.context?.tokens?.color;
|
|
2090
|
+
if (colorTokens) {
|
|
2058
2091
|
console.log(`
|
|
2059
2092
|
\uD83C\uDFA8 Design Color Tokens:`);
|
|
2060
|
-
for (const [tokenName, tokenVal] of Object.entries(
|
|
2061
|
-
|
|
2093
|
+
for (const [tokenName, tokenVal] of Object.entries(colorTokens)) {
|
|
2094
|
+
const displayVal = typeof tokenVal === "string" ? tokenVal : tokenVal?.hex ?? tokenVal?.oklch ?? JSON.stringify(tokenVal);
|
|
2095
|
+
console.log(` --${tokenName}: ${displayVal}`);
|
|
2062
2096
|
}
|
|
2063
2097
|
}
|
|
2064
2098
|
console.log(`
|
|
@@ -2098,7 +2132,7 @@ function generateAgentsGuide(opts) {
|
|
|
2098
2132
|
1. **Output Format:** Standalone HTML5 + Tailwind CSS CDN only.
|
|
2099
2133
|
2. **Typography:** Load and use Google Fonts "${opts.fontFamily}".
|
|
2100
2134
|
3. **No UI Frameworks:** Prohibited from using React, Vue, Svelte, Angular, Solid, or JSX syntax (\`className=\`, \`onClick={...}\`, \`<Component />\`).
|
|
2101
|
-
4. **
|
|
2135
|
+
4. **Strict Anchor & Navigation Invariant:** Prohibited from creating cross-page navigation, external links, empty \`href=""\`, or \`javascript:\` navigation in \`<a href="...">\`. Anchor links MUST point only to in-page section hashes (e.g. \`href="#pricing"\`) or dummy hash (\`href="#"\`). Target attributes (\`target="_blank"\`, \`target="_top"\`) and external \`<form action="...">\` are prohibited. For interactive triggers, use \`<button type="button">\`.
|
|
2102
2136
|
5. **No Raw Emojis:** Do not include raw emojis (e.g. \uD83D\uDE80, \uD83D\uDCA1, \uD83D\uDD25) in HTML markup. Use SVG icon libraries (Lucide Icons or Heroicons).
|
|
2103
2137
|
6. **Permitted Graphics:** Three.js, GSAP, Spline Viewer, and Lucide Icons via CDN are explicitly allowed.
|
|
2104
2138
|
|
|
@@ -2106,18 +2140,26 @@ function generateAgentsGuide(opts) {
|
|
|
2106
2140
|
|
|
2107
2141
|
## 2. Refira CLI Command Surface
|
|
2108
2142
|
- \`refira context\`: Inspect project design tokens, colors, typography, and existing pages.
|
|
2109
|
-
- \`refira scaffold --page <slug>\`: Create a clean HTML5 starter template
|
|
2110
|
-
- \`refira
|
|
2143
|
+
- \`refira scaffold --page <slug>\`: Create a clean HTML5 starter template and trigger live generating indicator on Refira Canvas.
|
|
2144
|
+
- \`refira push <file.html> --page <slug>\` (or \`refira preview\`): Run the deterministic harness check and stream updates to Refira Canvas.
|
|
2111
2145
|
- \`refira skill install [-g]\`: Re-install the Refira design craftsmanship skill.
|
|
2112
2146
|
|
|
2113
2147
|
---
|
|
2114
2148
|
|
|
2115
2149
|
## 3. Mandatory Execution Workflow
|
|
2116
2150
|
1. Run \`refira context\` to understand the project theme and existing pages.
|
|
2117
|
-
2. Run \`refira scaffold --page <page-name>\` to
|
|
2151
|
+
2. Run \`refira scaffold --page <page-name>\` to register the page and activate live canvas pulse.
|
|
2118
2152
|
3. Write your UI layout within the specified body slot using Tailwind utility classes.
|
|
2119
|
-
4. Execute \`refira
|
|
2153
|
+
4. Execute \`refira push <page-name>.html --page <page-name>\`.
|
|
2120
2154
|
5. If the CLI exits with code 1, address every listed violation and retry until exit code 0.
|
|
2155
|
+
|
|
2156
|
+
---
|
|
2157
|
+
|
|
2158
|
+
## 4. Mandatory Delivery Invariant (Push to Canvas)
|
|
2159
|
+
- **Local HTML files are NOT the final deliverable.** Refira Canvas is the live source of truth.
|
|
2160
|
+
- **Strictly Prohibited:** Stopping, ending your response, or reporting completion to the user after only writing a local HTML file.
|
|
2161
|
+
- **Autonomous Execution:** You MUST execute \`refira push <file.html> --page <slug>\` via your command-line execution tool. Do NOT ask the user to run it manually.
|
|
2162
|
+
- **Success Criteria:** A task is ONLY complete when \`refira push\` succeeds with exit code 0 and streams to Refira Canvas.
|
|
2121
2163
|
`;
|
|
2122
2164
|
}
|
|
2123
2165
|
|
|
@@ -2177,9 +2219,10 @@ When presenting features, data, or product capabilities:
|
|
|
2177
2219
|
## 5. Prototype Workflow in Refira
|
|
2178
2220
|
|
|
2179
2221
|
1. Check current tokens: \`refira context\`
|
|
2180
|
-
2. Generate base page: \`refira scaffold --page <slug>\`
|
|
2222
|
+
2. Generate base page & trigger canvas pulse: \`refira scaffold --page <slug>\`
|
|
2181
2223
|
3. Fill layout using Tailwind classes applying the aesthetic principles above.
|
|
2182
|
-
4.
|
|
2224
|
+
4. Stream to Refira Canvas: \`refira push <slug>.html --page <slug>\`
|
|
2225
|
+
5. Mandatory: Never stop after writing local files. Always execute \`refira push\` to complete the task.
|
|
2183
2226
|
`;
|
|
2184
2227
|
}
|
|
2185
2228
|
|
|
@@ -2201,8 +2244,8 @@ async function initCommand(opts) {
|
|
|
2201
2244
|
const client = new CliApiClient(apiUrl, apiKey);
|
|
2202
2245
|
try {
|
|
2203
2246
|
const data = await client.getProjectContext(projectId);
|
|
2204
|
-
const projectName = data.context.project
|
|
2205
|
-
const fontFamily = data.context
|
|
2247
|
+
const projectName = data.context?.project_name ?? data.context?.project?.name ?? "Refira Project";
|
|
2248
|
+
const fontFamily = extractFontFamily(data.context?.tokens?.typography, data.context?.project?.fontFamily ?? "Inter");
|
|
2206
2249
|
const agentsGuide = generateAgentsGuide({
|
|
2207
2250
|
projectName,
|
|
2208
2251
|
projectId,
|
|
@@ -3612,12 +3655,14 @@ function checkTagName(name, violations) {
|
|
|
3612
3655
|
}
|
|
3613
3656
|
function checkAttributes(name, attribs, violations) {
|
|
3614
3657
|
for (const [attrKey, attrVal] of Object.entries(attribs)) {
|
|
3615
|
-
|
|
3658
|
+
const lowerAttr = attrKey.toLowerCase();
|
|
3659
|
+
const lowerTag = name.toLowerCase();
|
|
3660
|
+
if (lowerAttr === "classname") {
|
|
3616
3661
|
violations.push({
|
|
3617
3662
|
type: "JSX_ATTRIBUTE",
|
|
3618
|
-
target:
|
|
3619
|
-
message: `Prohibited JSX attribute '
|
|
3620
|
-
remediation: `Change '
|
|
3663
|
+
target: attrKey,
|
|
3664
|
+
message: `Prohibited JSX attribute '${attrKey}' detected on <${name}>.`,
|
|
3665
|
+
remediation: `Change '${attrKey}' to the standard HTML 'class' attribute.`
|
|
3621
3666
|
});
|
|
3622
3667
|
}
|
|
3623
3668
|
if (/^on[A-Z]/.test(attrKey) || attrKey.startsWith("@") || attrKey.startsWith("v-") || attrKey.startsWith("*")) {
|
|
@@ -3628,15 +3673,52 @@ function checkAttributes(name, attribs, violations) {
|
|
|
3628
3673
|
remediation: "Remove framework event bindings. Use vanilla HTML and minimal vanilla JavaScript if needed."
|
|
3629
3674
|
});
|
|
3630
3675
|
}
|
|
3631
|
-
if (
|
|
3676
|
+
if (lowerAttr.startsWith("on") && (attrVal.includes("location") || attrVal.includes("window.open") || attrVal.includes("history."))) {
|
|
3677
|
+
violations.push({
|
|
3678
|
+
type: "NAVIGATION_PROHIBITED",
|
|
3679
|
+
target: `${attrKey}="${attrVal}"`,
|
|
3680
|
+
message: `Inline navigation script '${attrVal}' detected on <${name}>.`,
|
|
3681
|
+
remediation: "Remove inline navigation scripts. Prototypes run in a sandboxed canvas and cannot navigate across pages."
|
|
3682
|
+
});
|
|
3683
|
+
}
|
|
3684
|
+
if (lowerTag === "a") {
|
|
3685
|
+
if (lowerAttr === "href") {
|
|
3686
|
+
const trimmed = attrVal.trim();
|
|
3687
|
+
const isSafeHash = trimmed.startsWith("#");
|
|
3688
|
+
const isSafeVoid = trimmed === "javascript:void(0)" || trimmed === "javascript:;";
|
|
3689
|
+
if (!isSafeHash && !isSafeVoid) {
|
|
3690
|
+
violations.push({
|
|
3691
|
+
type: "NAVIGATION_PROHIBITED",
|
|
3692
|
+
target: `${attrKey}="${attrVal}"`,
|
|
3693
|
+
message: `Cross-page or external navigation '<a ${attrKey}="${attrVal}">' is strictly prohibited.`,
|
|
3694
|
+
remediation: 'Refira prototypes run in a sandboxed iframe. Anchor links must point to in-page section hashes (e.g. href="#features") or placeholder (href="#"). If you need action triggers, use <button type="button">.'
|
|
3695
|
+
});
|
|
3696
|
+
} else if (trimmed.includes("location") || trimmed.includes("window.open")) {
|
|
3697
|
+
violations.push({
|
|
3698
|
+
type: "NAVIGATION_PROHIBITED",
|
|
3699
|
+
target: `${attrKey}="${attrVal}"`,
|
|
3700
|
+
message: `Navigation statement detected inside href: '${attrVal}'.`,
|
|
3701
|
+
remediation: "Remove navigation statements from href."
|
|
3702
|
+
});
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
if (lowerAttr === "target" && attrVal.trim().length > 0) {
|
|
3706
|
+
violations.push({
|
|
3707
|
+
type: "NAVIGATION_PROHIBITED",
|
|
3708
|
+
target: `${attrKey}="${attrVal}"`,
|
|
3709
|
+
message: `Target attribute '${attrKey}="${attrVal}"' on <a> is prohibited.`,
|
|
3710
|
+
remediation: "Remove the target attribute. Prototypes must remain within the Refira Canvas viewport."
|
|
3711
|
+
});
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
if (lowerTag === "form" && lowerAttr === "action") {
|
|
3632
3715
|
const trimmed = attrVal.trim();
|
|
3633
|
-
|
|
3634
|
-
if (!isInternal && trimmed.length > 0) {
|
|
3716
|
+
if (trimmed.length > 0 && !trimmed.startsWith("#") && !trimmed.startsWith("javascript:void")) {
|
|
3635
3717
|
violations.push({
|
|
3636
3718
|
type: "NAVIGATION_PROHIBITED",
|
|
3637
|
-
target:
|
|
3638
|
-
message: `
|
|
3639
|
-
remediation:
|
|
3719
|
+
target: `${attrKey}="${attrVal}"`,
|
|
3720
|
+
message: `Form action '${attrVal}' is prohibited in standalone prototypes.`,
|
|
3721
|
+
remediation: 'Remove or replace form action with action="#" or handle submission client-side without page reload.'
|
|
3640
3722
|
});
|
|
3641
3723
|
}
|
|
3642
3724
|
}
|
|
@@ -3898,9 +3980,20 @@ async function scaffoldCommand(opts) {
|
|
|
3898
3980
|
try {
|
|
3899
3981
|
const client = new CliApiClient(config.apiUrl, config.apiKey);
|
|
3900
3982
|
const data = await client.getProjectContext(config.projectId);
|
|
3901
|
-
projectName = data.context.project
|
|
3902
|
-
fontFamily = data.context
|
|
3903
|
-
|
|
3983
|
+
projectName = data.context?.project_name ?? data.context?.project?.name ?? projectName;
|
|
3984
|
+
fontFamily = extractFontFamily(data.context?.tokens?.typography, data.context?.project?.fontFamily ?? fontFamily);
|
|
3985
|
+
const colors = data.context?.tokens?.colors ?? data.context?.tokens?.color;
|
|
3986
|
+
if (colors) {
|
|
3987
|
+
const parsed = {};
|
|
3988
|
+
for (const [k, v] of Object.entries(colors)) {
|
|
3989
|
+
parsed[k] = typeof v === "string" ? v : v?.hex ?? v?.oklch ?? "";
|
|
3990
|
+
}
|
|
3991
|
+
colorTokens = parsed;
|
|
3992
|
+
}
|
|
3993
|
+
try {
|
|
3994
|
+
await client.startGenerating(config.projectId, pageSlug);
|
|
3995
|
+
console.log(`✨ Activated live generation indicator on Refira Canvas for '${pageSlug}'`);
|
|
3996
|
+
} catch {}
|
|
3904
3997
|
} catch {}
|
|
3905
3998
|
}
|
|
3906
3999
|
const pageName = pageSlug.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
@@ -3916,8 +4009,10 @@ async function scaffoldCommand(opts) {
|
|
|
3916
4009
|
console.log(` Font: ${fontFamily}`);
|
|
3917
4010
|
console.log(" Slot: <!-- [REFIRA CANVAS SLOT: START] -->");
|
|
3918
4011
|
console.log(`
|
|
3919
|
-
|
|
3920
|
-
console.log(`
|
|
4012
|
+
\uD83D\uDEA8 [MANDATORY AGENT COMPLETION GATE]:`);
|
|
4013
|
+
console.log(` 1. Write your UI layout within the designated slot in '${targetPath}'.`);
|
|
4014
|
+
console.log(` 2. You MUST execute: refira push ${targetPath} --page ${pageSlug}`);
|
|
4015
|
+
console.log(" DO NOT stop your response or declare completion until `refira push` succeeds!");
|
|
3921
4016
|
}
|
|
3922
4017
|
|
|
3923
4018
|
// src/commands/skill.ts
|
|
@@ -3939,14 +4034,14 @@ AI coding agents will automatically recognize this skill for high-aesthetic prot
|
|
|
3939
4034
|
|
|
3940
4035
|
// src/index.ts
|
|
3941
4036
|
var program2 = new Command2;
|
|
3942
|
-
program2.name("refira").description("Refira CLI tool and AI Agent Harness for deterministic prototype generation").version("0.1.
|
|
4037
|
+
program2.name("refira").description("Refira CLI tool and AI Agent Harness for deterministic prototype generation").version("0.1.3");
|
|
3943
4038
|
var auth = program2.command("auth").description("Manage Refira API authentication and sessions");
|
|
3944
4039
|
auth.command("login").description("Login to Refira using an API key").option("--api-url <url>", "Refira backend API base URL", "http://localhost:3001").option("--api-key <key>", "Project API key (rfr_...)").option("-g, --global", "Save credentials globally in user home directory", false).action(loginCommand);
|
|
3945
4040
|
auth.command("status").description("Verify and display current Refira authentication session").action(statusCommand);
|
|
3946
4041
|
program2.command("init").description("Initialize a Refira project workspace, generating AGENTS.md, .cursorrules, and skills").requiredOption("--project-id <id>", "Target project UUID").option("--api-url <url>", "Refira backend API base URL").option("--api-key <key>", "Project API key (rfr_...)").action(initCommand);
|
|
3947
4042
|
program2.command("context").description("Inspect design tokens, color palette, typography, and page roster").option("--project-id <id>", "Project UUID").action(contextCommand);
|
|
3948
4043
|
program2.command("scaffold").description("Generate a clean HTML5 + Tailwind starter template for a page").requiredOption("--page <slug>", "Page slug (e.g. checkout, dashboard, landing)").option("--output <path>", "Destination file path").action(scaffoldCommand);
|
|
3949
|
-
program2.command("preview").description("Inspect HTML markup with Agent Harness and stream to Refira Canvas").argument("<file>", "Path to HTML file to preview").requiredOption("--page <slug>", "Target page slug").action(previewCommand);
|
|
4044
|
+
program2.command("preview").alias("push").description("Inspect HTML markup with Agent Harness and stream to Refira Canvas").argument("<file>", "Path to HTML file to preview").requiredOption("--page <slug>", "Target page slug").action(previewCommand);
|
|
3950
4045
|
var skill = program2.command("skill").description("Manage Refira Agent Skills");
|
|
3951
4046
|
skill.command("install").description("Install the Refira design craftsmanship skill (.agents/skills/refira/SKILL.md)").option("-g, --global", "Install globally into user home directory (~/.agents/skills/refira)", false).action(skillInstallCommand);
|
|
3952
4047
|
program2.parse();
|