superacli 1.0.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.
Files changed (69) hide show
  1. package/.env.example +14 -0
  2. package/README.md +173 -0
  3. package/cli/adapters/http.js +72 -0
  4. package/cli/adapters/mcp.js +193 -0
  5. package/cli/adapters/openapi.js +160 -0
  6. package/cli/ask.js +208 -0
  7. package/cli/config.js +133 -0
  8. package/cli/executor.js +117 -0
  9. package/cli/help-json.js +46 -0
  10. package/cli/mcp-local.js +72 -0
  11. package/cli/plan-runtime.js +32 -0
  12. package/cli/planner.js +67 -0
  13. package/cli/skills.js +240 -0
  14. package/cli/supercli.js +704 -0
  15. package/docs/features/adapters.md +25 -0
  16. package/docs/features/agent-friendly.md +28 -0
  17. package/docs/features/ask.md +32 -0
  18. package/docs/features/config-sync.md +22 -0
  19. package/docs/features/execution-plans.md +25 -0
  20. package/docs/features/observability.md +22 -0
  21. package/docs/features/skills.md +25 -0
  22. package/docs/features/storage.md +25 -0
  23. package/docs/features/workflows.md +33 -0
  24. package/docs/initial/AGENTS_FRIENDLY_TOOLS.md +553 -0
  25. package/docs/initial/agent-friendly.md +447 -0
  26. package/docs/initial/architecture.md +436 -0
  27. package/docs/initial/built-in-mcp-server.md +64 -0
  28. package/docs/initial/command-plan.md +532 -0
  29. package/docs/initial/core-features-2.md +428 -0
  30. package/docs/initial/core-features.md +366 -0
  31. package/docs/initial/dag.md +20 -0
  32. package/docs/initial/description.txt +9 -0
  33. package/docs/initial/idea.txt +564 -0
  34. package/docs/initial/initial-spec-details.md +726 -0
  35. package/docs/initial/initial-spec.md +731 -0
  36. package/docs/initial/mcp-local-mode.md +53 -0
  37. package/docs/initial/mcp-sse-mode.md +54 -0
  38. package/docs/initial/skills-support.md +246 -0
  39. package/docs/initial/storage-adapter-example.md +155 -0
  40. package/docs/initial/supercli-vs-gwc.md +109 -0
  41. package/examples/mcp-sse/install-demo.js +86 -0
  42. package/examples/mcp-sse/server.js +81 -0
  43. package/examples/mcp-stdio/install-demo.js +78 -0
  44. package/examples/mcp-stdio/server.js +50 -0
  45. package/package.json +21 -0
  46. package/server/app.js +59 -0
  47. package/server/public/app.js +18 -0
  48. package/server/routes/ask.js +92 -0
  49. package/server/routes/commands.js +126 -0
  50. package/server/routes/config.js +58 -0
  51. package/server/routes/jobs.js +122 -0
  52. package/server/routes/mcp.js +79 -0
  53. package/server/routes/plans.js +134 -0
  54. package/server/routes/specs.js +79 -0
  55. package/server/services/configService.js +88 -0
  56. package/server/storage/adapter.js +32 -0
  57. package/server/storage/file.js +64 -0
  58. package/server/storage/mongo.js +55 -0
  59. package/server/views/command-edit.ejs +110 -0
  60. package/server/views/commands.ejs +49 -0
  61. package/server/views/jobs.ejs +72 -0
  62. package/server/views/layout.ejs +42 -0
  63. package/server/views/mcp.ejs +80 -0
  64. package/server/views/partials/foot.ejs +5 -0
  65. package/server/views/partials/head.ejs +27 -0
  66. package/server/views/specs.ejs +91 -0
  67. package/tests/test-cli.js +367 -0
  68. package/tests/test-mcp.js +189 -0
  69. package/tests/test-openapi.js +101 -0
package/cli/skills.js ADDED
@@ -0,0 +1,240 @@
1
+ const { createPlan } = require("./planner")
2
+
3
+ function normalizeSkillId(input) {
4
+ if (!input || typeof input !== "string") return null
5
+ const parts = input.trim().split(".")
6
+ if (parts.length !== 3 || parts.some(p => !p)) return null
7
+ return {
8
+ id: parts.join("."),
9
+ namespace: parts[0],
10
+ resource: parts[1],
11
+ action: parts[2]
12
+ }
13
+ }
14
+
15
+ function escapeYamlString(value) {
16
+ const str = value == null ? "" : String(value)
17
+ return `"${str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"")}"`
18
+ }
19
+
20
+ function toDagNodes(cmd) {
21
+ const plan = createPlan(cmd, {})
22
+ return plan.steps.map(step => {
23
+ const node = {
24
+ id: step.step,
25
+ type: step.type
26
+ }
27
+ if (step.step > 1) node.depends_on = [step.step - 1]
28
+ if (step.adapter) node.adapter = step.adapter
29
+ if (step.method) node.method = step.method
30
+ if (step.url) node.url = step.url
31
+ if (step.operationId) node.operation_id = step.operationId
32
+ if (step.tool) node.tool = step.tool
33
+ return node
34
+ })
35
+ }
36
+
37
+ function renderYamlObject(value, indent = 0) {
38
+ const pad = " ".repeat(indent)
39
+ if (Array.isArray(value)) {
40
+ if (value.length === 0) return `${pad}[]`
41
+ return value
42
+ .map(item => {
43
+ if (item && typeof item === "object" && !Array.isArray(item)) {
44
+ const entries = Object.entries(item)
45
+ if (entries.length === 0) return `${pad}- {}`
46
+ const [firstKey, firstValue] = entries[0]
47
+ let out = `${pad}- ${firstKey}: ${renderYamlScalar(firstValue)}`
48
+ for (let i = 1; i < entries.length; i++) {
49
+ out += `\n${pad} ${entries[i][0]}: ${renderYamlScalar(entries[i][1])}`
50
+ }
51
+ return out
52
+ }
53
+ return `${pad}- ${renderYamlScalar(item)}`
54
+ })
55
+ .join("\n")
56
+ }
57
+
58
+ if (!value || typeof value !== "object") {
59
+ return `${pad}${renderYamlScalar(value)}`
60
+ }
61
+
62
+ const lines = []
63
+ for (const [key, val] of Object.entries(value)) {
64
+ if (Array.isArray(val) || (val && typeof val === "object")) {
65
+ lines.push(`${pad}${key}:`)
66
+ lines.push(renderYamlObject(val, indent + 2))
67
+ } else {
68
+ lines.push(`${pad}${key}: ${renderYamlScalar(val)}`)
69
+ }
70
+ }
71
+ return lines.join("\n")
72
+ }
73
+
74
+ function renderYamlScalar(value) {
75
+ if (typeof value === "boolean") return value ? "true" : "false"
76
+ if (typeof value === "number") return String(value)
77
+ if (value == null) return "null"
78
+ return escapeYamlString(value)
79
+ }
80
+
81
+ function buildCommandSkillMarkdown(cmd, options = {}) {
82
+ const includeDag = !!options.showDag
83
+ const argLines = (cmd.args || []).map(arg => ({
84
+ name: arg.name,
85
+ type: arg.type || "string",
86
+ required: !!arg.required,
87
+ description: arg.description || ""
88
+ }))
89
+
90
+ const frontmatter = {
91
+ skill_name: `${cmd.namespace}_${cmd.resource}_${cmd.action}`,
92
+ description: cmd.description || `Execute ${cmd.namespace}.${cmd.resource}.${cmd.action}`,
93
+ command: `${cmd.namespace} ${cmd.resource} ${cmd.action}`,
94
+ arguments: argLines,
95
+ output_schema: cmd.output || { type: "object" },
96
+ metadata: {
97
+ side_effects: !!cmd.mutation,
98
+ risk_level: cmd.risk_level || "safe",
99
+ dag_supported: true
100
+ }
101
+ }
102
+
103
+ if (includeDag) {
104
+ frontmatter.dag = toDagNodes(cmd)
105
+ }
106
+
107
+ const exampleArgs = (cmd.args || [])
108
+ .map(arg => {
109
+ if (arg.required) return `--${arg.name} <${arg.name}>`
110
+ return `--${arg.name} <${arg.name}>`
111
+ })
112
+ .join(" ")
113
+ .trim()
114
+
115
+ const examples = [
116
+ `supercli ${cmd.namespace} ${cmd.resource} ${cmd.action}${exampleArgs ? ` ${exampleArgs}` : ""} --json`
117
+ ]
118
+
119
+ if (includeDag) {
120
+ examples.push(`supercli skills get ${cmd.namespace}.${cmd.resource}.${cmd.action} --show-dag`)
121
+ }
122
+
123
+ return `---\n${renderYamlObject(frontmatter)}\n---\n\n# Examples\n\n\`\`\`bash\n${examples.join("\n")}\n\`\`\``
124
+ }
125
+
126
+ function buildTeachSkillMarkdown(options = {}) {
127
+ const includeDag = !!options.showDag
128
+
129
+ const frontmatter = {
130
+ skill_name: "teach_skills_usage",
131
+ description: "Introduces LLMs to SuperCLI skills commands and explains how to request and execute skills.",
132
+ command: "skills teach",
133
+ arguments: [
134
+ {
135
+ name: "format",
136
+ type: "string",
137
+ required: false,
138
+ description: "Output format, default skill.md"
139
+ },
140
+ {
141
+ name: "show-dag",
142
+ type: "boolean",
143
+ required: false,
144
+ description: "Include internal DAG for agent reasoning"
145
+ }
146
+ ],
147
+ output_schema: {
148
+ instruction: "string",
149
+ examples: "array"
150
+ },
151
+ metadata: {
152
+ side_effects: false,
153
+ risk_level: "safe",
154
+ dag_supported: true
155
+ }
156
+ }
157
+
158
+ if (includeDag) {
159
+ frontmatter.dag = [
160
+ { id: 1, type: "resolve_skills_catalog" },
161
+ { id: 2, type: "render_meta_skill", depends_on: [1] },
162
+ { id: 3, type: "emit_skill_markdown", depends_on: [2] }
163
+ ]
164
+ }
165
+
166
+ return `---\n${renderYamlObject(frontmatter)}\n---\n\n# Instruction\n\nThis skill teaches LLMs how to discover and use SuperCLI skills:\n\n1. List available skills:\n\n\`\`\`bash\nsupercli skills list --json\n\`\`\`\n\n2. Fetch a specific skill:\n\n\`\`\`bash\nsupercli skills get <namespace.resource.action> --format skill.md\n\`\`\`\n\n3. Parse YAML frontmatter to understand command, arguments, output schema, and metadata.\n\n4. Execute the command with validated arguments:\n\n\`\`\`bash\nsupercli <namespace> <resource> <action> --arg value --json\n\`\`\`\n\n# Examples\n\n\`\`\`bash\nsupercli skills teach --format skill.md\nsupercli skills teach --format skill.md --show-dag\n\`\`\``
167
+ }
168
+
169
+ function listSkillsMetadata(config) {
170
+ return (config.commands || []).map(cmd => ({
171
+ name: `${cmd.namespace}.${cmd.resource}.${cmd.action}`,
172
+ description: cmd.description || ""
173
+ }))
174
+ }
175
+
176
+ function handleSkillsCommand(options) {
177
+ const { positional, flags, config, humanMode, output, outputHumanTable, outputError } = options
178
+ const subcommand = positional[1]
179
+ const format = flags.format || "skill.md"
180
+
181
+ if (subcommand === "list") {
182
+ const skills = listSkillsMetadata(config)
183
+ if (humanMode && !flags.json) {
184
+ console.log("\n ⚡ Skills\n")
185
+ outputHumanTable(skills, [
186
+ { key: "name", label: "Name" },
187
+ { key: "description", label: "Description" }
188
+ ])
189
+ console.log("")
190
+ } else {
191
+ output({ skills })
192
+ }
193
+ return true
194
+ }
195
+
196
+ if (subcommand === "teach") {
197
+ if (format !== "skill.md") {
198
+ outputError({ code: 85, type: "invalid_argument", message: "skills teach supports only --format skill.md", recoverable: false })
199
+ return true
200
+ }
201
+ console.log(buildTeachSkillMarkdown({ showDag: !!flags["show-dag"] }))
202
+ return true
203
+ }
204
+
205
+ if (subcommand === "get") {
206
+ if (format !== "skill.md") {
207
+ outputError({ code: 85, type: "invalid_argument", message: "skills get supports only --format skill.md", recoverable: false })
208
+ return true
209
+ }
210
+
211
+ const dottedId = positional[2] || (positional[3] && positional[4] ? `${positional[2]}.${positional[3]}.${positional[4]}` : "")
212
+ const parsed = normalizeSkillId(dottedId)
213
+ if (!parsed) {
214
+ outputError({ code: 85, type: "invalid_argument", message: "Usage: supercli skills get <namespace.resource.action> [--format skill.md]", recoverable: false })
215
+ return true
216
+ }
217
+
218
+ const cmd = config.commands.find(c =>
219
+ c.namespace === parsed.namespace && c.resource === parsed.resource && c.action === parsed.action
220
+ )
221
+ if (!cmd) {
222
+ outputError({ code: 92, type: "resource_not_found", message: `Skill ${parsed.id} not found`, suggestions: ["Run: supercli skills list --json"] })
223
+ return true
224
+ }
225
+
226
+ console.log(buildCommandSkillMarkdown(cmd, { showDag: !!flags["show-dag"] }))
227
+ return true
228
+ }
229
+
230
+ outputError({ code: 85, type: "invalid_argument", message: "Unknown skills subcommand. Use: list, get, teach", recoverable: false })
231
+ return true
232
+ }
233
+
234
+ module.exports = {
235
+ normalizeSkillId,
236
+ buildCommandSkillMarkdown,
237
+ buildTeachSkillMarkdown,
238
+ listSkillsMetadata,
239
+ handleSkillsCommand
240
+ }