emberflow-skills 1.2.0 → 1.3.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/bin/install.js CHANGED
@@ -7,7 +7,7 @@ const http = require('http');
7
7
  const readline = require('readline');
8
8
  const os = require('os');
9
9
 
10
- const SKILL_NAMES = ['ember-publish', 'ember-publish-json'];
10
+ const SKILL_NAMES = ['ember-publish', 'ember-publish-json', 'ember-publish-space'];
11
11
  const SKILLS_DIR = path.join(__dirname, '..', 'skills');
12
12
  const EMBERFLOW_URL = 'https://www.emberflow.ai';
13
13
  const TOKEN_PATH = path.join(os.homedir(), '.emberflow', 'token.json');
@@ -227,7 +227,7 @@ async function main() {
227
227
 
228
228
  if (installed > 0) {
229
229
  console.log();
230
- console.log(` Use: ${cyan('/ember-publish')} ${dim('[topic]')} or ${cyan('/ember-publish-json')} ${dim('[data]')}`);
230
+ console.log(` Use: ${cyan('/ember-publish')} ${dim('[topic]')} or ${cyan('/ember-publish-json')} ${dim('[data]')} or ${cyan('/ember-publish-space')} ${dim('[directory]')}`);
231
231
  }
232
232
  }
233
233
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emberflow-skills",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Install Emberflow skills for AI coding tools",
5
5
  "bin": {
6
6
  "emberflow-skills": "./bin/install.js"
@@ -0,0 +1,211 @@
1
+ ---
2
+ name: ember-publish-space
3
+ description: Publish a directory of markdown files as an Emberflow Space — a multi-page docs site with sidebar navigation
4
+ argument-hint: [directory path or file list]
5
+ ---
6
+
7
+ # Emberflow Space Publisher
8
+
9
+ Publish a collection of markdown files as an **Emberflow Space** — a multi-page docs site at **https://emberflow.ai** with sidebar navigation, Mermaid diagram rendering, dark mode, and per-block commenting.
10
+
11
+ ## Step 1: Collect Files
12
+
13
+ Accept a directory path or explicit file list from the user. Scan for `.md` files:
14
+
15
+ ```bash
16
+ # Example: find all markdown files in a directory
17
+ DOCS_DIR="/path/to/docs"
18
+ find "$DOCS_DIR" -name "*.md" -type f | sort
19
+ ```
20
+
21
+ For each `.md` file:
22
+ - Extract the title from the first `# Title` line
23
+ - Generate a slug from the title: lowercase, replace non-alphanumeric runs with `-`, trim leading/trailing `-`
24
+ - Note the parent directory name (if in a subdirectory) — this becomes a nav section
25
+
26
+ ### Directory Structure Mapping
27
+
28
+ ```
29
+ docs/
30
+ getting-started.md → top-level page
31
+ api-reference.md → top-level page
32
+ guides/
33
+ authentication.md → page under "Guides" section
34
+ deployment.md → page under "Guides" section
35
+ ```
36
+
37
+ ## Step 2: Authenticate (if needed)
38
+
39
+ Session tokens are stored at `~/.emberflow/token.json`. Check if a valid session exists:
40
+
41
+ ```bash
42
+ cat ~/.emberflow/token.json 2>/dev/null
43
+ ```
44
+
45
+ If the file exists, verify the token still works:
46
+
47
+ ```bash
48
+ curl -s -H "Authorization: Bearer $(jq -r .token ~/.emberflow/token.json)" \
49
+ https://emberflow.ai/api/docs
50
+ ```
51
+
52
+ If no session exists, it's expired, or the verify call returns 401, authenticate using the device flow:
53
+
54
+ ```bash
55
+ EMBERFLOW_URL="https://emberflow.ai"
56
+
57
+ # Step 1: Request a device code
58
+ RESP=$(curl -s -X POST "$EMBERFLOW_URL/api/device-code")
59
+ CODE=$(echo "$RESP" | jq -r .code)
60
+ URL=$(echo "$RESP" | jq -r .verification_url)
61
+ ```
62
+
63
+ Tell the user to open the URL in their browser to sign in and approve the device. Then poll until approved:
64
+
65
+ ```bash
66
+ # Step 2: Poll until approved (every 3s)
67
+ while true; do
68
+ STATUS=$(curl -s "$EMBERFLOW_URL/api/device-code/$CODE")
69
+ S=$(echo "$STATUS" | jq -r .status)
70
+ if [ "$S" = "approved" ]; then
71
+ TOKEN=$(echo "$STATUS" | jq -r .session_token)
72
+ mkdir -p ~/.emberflow
73
+ echo "{\"token\":\"$TOKEN\"}" > ~/.emberflow/token.json
74
+ break
75
+ fi
76
+ if [ "$S" = "expired" ]; then
77
+ echo "Code expired. Please try again."
78
+ break
79
+ fi
80
+ sleep 3
81
+ done
82
+ ```
83
+
84
+ ## Step 3: Create or Select Space
85
+
86
+ First, check if the user wants to publish to an existing space or create a new one:
87
+
88
+ ```bash
89
+ TOKEN=$(jq -r .token ~/.emberflow/token.json)
90
+ EMBERFLOW_URL="https://emberflow.ai"
91
+
92
+ # List existing spaces
93
+ curl -s -H "Authorization: Bearer $TOKEN" "$EMBERFLOW_URL/api/spaces"
94
+ ```
95
+
96
+ If creating a new space:
97
+
98
+ ```bash
99
+ # Generate name and slug from directory name or user input
100
+ SPACE_NAME="My Docs"
101
+ SPACE_SLUG=$(echo "$SPACE_NAME" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//;s/-$//')
102
+
103
+ SPACE_RESP=$(curl -s -X POST "$EMBERFLOW_URL/api/spaces" \
104
+ -H "Authorization: Bearer $TOKEN" \
105
+ -H "Content-Type: application/json" \
106
+ -d "$(jq -n --arg name "$SPACE_NAME" --arg slug "$SPACE_SLUG" \
107
+ '{name: $name, slug: $slug}')")
108
+
109
+ SPACE_ID=$(echo "$SPACE_RESP" | jq -r .id)
110
+ ```
111
+
112
+ If using an existing space, extract its `id` from the list response.
113
+
114
+ ## Step 4: Build Payload and Batch Publish
115
+
116
+ Build the `documents` and `nav` arrays, then call the batch publish endpoint.
117
+
118
+ The API expects:
119
+ - `documents[]`: array of `{ slug, title, content }` — each doc is upserted by slug
120
+ - `nav[]`: array of nav items:
121
+ - Sections: `{ type: "section", label: "Section Name" }`
122
+ - Pages: `{ type: "page", label: "Page Title", slug: "page-slug" }` — top-level
123
+ - Pages in sections: `{ type: "page", label: "Page Title", slug: "page-slug", parent_label: "Section Name" }`
124
+
125
+ ```bash
126
+ TOKEN=$(jq -r .token ~/.emberflow/token.json)
127
+ EMBERFLOW_URL="https://emberflow.ai"
128
+ DOCS_DIR="/path/to/docs"
129
+
130
+ # Build the JSON payload with jq
131
+ # For each .md file: read content, extract title, generate slug, determine section
132
+
133
+ DOCUMENTS="[]"
134
+ NAV="[]"
135
+ SECTIONS_ADDED=""
136
+
137
+ for file in $(find "$DOCS_DIR" -name "*.md" -type f | sort); do
138
+ # Extract title and slug
139
+ TITLE=$(head -1 "$file" | sed 's/^#\s*//')
140
+ SLUG=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-' | sed 's/^-//;s/-$//')
141
+ CONTENT=$(cat "$file")
142
+
143
+ # Determine if in a subdirectory (becomes a section)
144
+ REL_PATH=$(realpath --relative-to="$DOCS_DIR" "$file")
145
+ DIR_NAME=$(dirname "$REL_PATH")
146
+
147
+ # Add document
148
+ DOCUMENTS=$(echo "$DOCUMENTS" | jq --arg slug "$SLUG" --arg title "$TITLE" --rawfile content "$file" \
149
+ '. + [{slug: $slug, title: $title, content: $content}]')
150
+
151
+ if [ "$DIR_NAME" = "." ]; then
152
+ # Top-level page
153
+ NAV=$(echo "$NAV" | jq --arg label "$TITLE" --arg slug "$SLUG" \
154
+ '. + [{type: "page", label: $label, slug: $slug}]')
155
+ else
156
+ # Page under a section
157
+ SECTION_LABEL=$(echo "$DIR_NAME" | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g')
158
+
159
+ # Add section if not already added
160
+ if ! echo "$SECTIONS_ADDED" | grep -qF "$SECTION_LABEL"; then
161
+ NAV=$(echo "$NAV" | jq --arg label "$SECTION_LABEL" \
162
+ '. + [{type: "section", label: $label}]')
163
+ SECTIONS_ADDED="$SECTIONS_ADDED|$SECTION_LABEL"
164
+ fi
165
+
166
+ NAV=$(echo "$NAV" | jq --arg label "$TITLE" --arg slug "$SLUG" --arg parent "$SECTION_LABEL" \
167
+ '. + [{type: "page", label: $label, slug: $slug, parent_label: $parent}]')
168
+ fi
169
+ done
170
+
171
+ # Write payload to temp file (handles large doc sets)
172
+ jq -n --argjson documents "$DOCUMENTS" --argjson nav "$NAV" \
173
+ '{documents: $documents, nav: $nav}' > /tmp/emberflow-publish.json
174
+
175
+ # Publish
176
+ RESULT=$(curl -s -X POST "$EMBERFLOW_URL/api/spaces/$SPACE_ID/publish" \
177
+ -H "Authorization: Bearer $TOKEN" \
178
+ -H "Content-Type: application/json" \
179
+ -d @/tmp/emberflow-publish.json)
180
+
181
+ echo "$RESULT"
182
+ rm -f /tmp/emberflow-publish.json
183
+ ```
184
+
185
+ ## Step 5: Output Result
186
+
187
+ The API response includes:
188
+ ```json
189
+ {
190
+ "published": true,
191
+ "url": "https://emberflow.ai/s/<author_short_id>/<space_slug>",
192
+ "document_count": 5
193
+ }
194
+ ```
195
+
196
+ Show the user:
197
+ - The Space URL
198
+ - Number of documents published
199
+ - The nav structure that was created
200
+
201
+ To **update** an existing space, publish again to the same space — documents are upserted by slug, and nav items are recreated.
202
+
203
+ ### Other Operations
204
+
205
+ ```bash
206
+ # List your spaces
207
+ curl -s -H "Authorization: Bearer $TOKEN" "$EMBERFLOW_URL/api/spaces"
208
+
209
+ # Delete a space
210
+ curl -s -X DELETE -H "Authorization: Bearer $TOKEN" "$EMBERFLOW_URL/api/spaces/SPACE_ID_HERE"
211
+ ```