@heart-of-gold/toolkit 0.1.32 → 0.1.33

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.
@@ -15,19 +15,19 @@
15
15
  "name": "deep-thought",
16
16
  "source": "./plugins/deep-thought",
17
17
  "description": "The Answer Computer — reasoning tools for brainstorming, planning, and deep thinking",
18
- "version": "0.2.2"
18
+ "version": "0.2.3"
19
19
  },
20
20
  {
21
21
  "name": "marvin",
22
22
  "source": "./plugins/marvin",
23
23
  "description": "The Paranoid Android — quality tools for code review, knowledge compounding, and work execution",
24
- "version": "0.3.0"
24
+ "version": "0.3.3"
25
25
  },
26
26
  {
27
27
  "name": "babel-fish",
28
28
  "source": "./plugins/babel-fish",
29
29
  "description": "Universal Translator — media generation tools for audio, image, and video content",
30
- "version": "0.2.0"
30
+ "version": "0.2.1"
31
31
  },
32
32
  {
33
33
  "name": "quellis",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heart-of-gold/toolkit",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "type": "module",
5
5
  "description": "Cross-platform installer for Heart of Gold skills — works with Codex, OpenCode, Pi, Claude Code, and more",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babel-fish",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Universal Translator — media generation tools for audio, image, video, and visualization",
5
5
  "author": {
6
6
  "name": "ondrej-svec",
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: visualize
3
3
  description: >
4
- Render mind maps and tree visualizations from markdown. Prefer terminal output for quick local inspection,
5
- and generate shareable HTML when the user wants a browser view and share-html infrastructure is configured.
6
- Works on brainstorm docs, plan docs, markdown files, or any structured content. Triggers: visualize,
7
- mindmap, mind map, show me the structure, draw a map.
4
+ Render mind maps and tree visualizations from markdown. Prefer shareable HTML first for brainstorms, plans,
5
+ architecture docs, and other structured workflow artifacts when share-html infrastructure is configured;
6
+ otherwise fall back to terminal output for quick local inspection. Works on markdown files or any structured
7
+ content. Triggers: visualize, mindmap, mind map, show me the structure, draw a map.
8
8
  allowed-tools:
9
9
  - Read
10
10
  - Bash
@@ -88,25 +88,26 @@ This script:
88
88
 
89
89
  ## Phase 0 — Determine What to Visualize
90
90
 
91
- First decide whether the user wants:
92
- - a quick terminal view
93
- - a browser/shareable HTML view
91
+ First decide whether this should be a browser/shareable HTML view or a quick terminal view.
94
92
 
95
- **Prefer browser/shareable HTML when:**
93
+ **Prefer browser/shareable HTML first when:**
94
+ - the source is a brainstorm, plan, architecture doc, or other structured workflow artifact
96
95
  - the user asks to open it in a browser
97
96
  - the user wants to share the result with another person or device
98
97
  - the structure is large enough that browser navigation is more useful than terminal rendering
98
+ - `share-html` is configured
99
99
 
100
100
  **Prefer terminal rendering when:**
101
- - the user wants a quick local look
102
- - the environment is SSH-heavy or browser access is not requested
103
101
  - share-html is not configured
102
+ - the user explicitly wants a quick terminal-only look
103
+ - the environment is SSH-heavy and the browser/share path is not requested
104
104
 
105
105
  When invoked as `/visualize [path]`:
106
106
 
107
107
  **If a file path is provided:**
108
108
  1. Read the file
109
- 2. Render it directly with the mind map renderer
109
+ 2. If it looks like a brainstorm, plan, architecture doc, or the user asked for browser viewing, try the shareable HTML path first
110
+ 3. Otherwise render it directly with the mind map renderer
110
111
 
111
112
  **If no path is provided:**
112
113
  1. Check if there's a recent brainstorm or plan document in the conversation context
@@ -140,17 +141,19 @@ The default mode is **vertical layout** — boxes on main branches, compact leav
140
141
 
141
142
  ### Path B — Shareable HTML
142
143
 
143
- If the user wants browser viewing or sharing and `share-html` is configured:
144
+ For brainstorms, plans, architecture docs, and other structured workflow artifacts, prefer this path first when `share-html` is configured.
144
145
 
145
146
  1. Verify or assume the input markdown is ready
146
147
  2. Run:
147
148
  ```bash
148
- bash scripts/render-and-share.sh [file]
149
+ bash scripts/render-and-share.sh --url-only [file]
149
150
  ```
150
- 3. Read the returned JSON
151
- 4. Return the primary URL to the user (`url` when present, otherwise `viewerUrl`)
151
+ 3. Read the returned URL from stdout
152
+ 4. Return that URL to the user as the primary result
152
153
  5. Briefly explain what was published
153
154
 
155
+ If you need more detail for debugging, you may run the helper without `--url-only` and inspect the returned JSON.
156
+
154
157
  If publishing fails because the share server is not configured, say so clearly and fall back to terminal rendering unless the user wants to stop and run `share-server-setup` first.
155
158
 
156
159
  **For shell usage** (not through assistant panels): terminal rendering can use ANSI colors, or `--horizontal` for the wide spatial layout.
@@ -160,7 +163,7 @@ If publishing fails because the share server is not configured, say so clearly a
160
163
  After rendering or sharing, briefly note:
161
164
  - for terminal mode: "Use `--depth N` to see more/less detail"
162
165
  - for terminal mode: "Use `--width N` to fit a different terminal size"
163
- - for shared HTML: return the viewer URL and say whether it is local-only or publicly reachable on the user's tailnet
166
+ - for shared HTML: return the final browser URL as the main result and say whether it is local-only or publicly reachable on the user's tailnet
164
167
  - if publishing failed due to missing share infrastructure: suggest `share-server-setup`
165
168
  - if the source was a brainstorm/plan/architecture doc, offer to continue the workflow (e.g., proceed to `/plan`, `/work`, or implementation)
166
169
 
@@ -9,14 +9,16 @@ SLUG=""
9
9
  TITLE=""
10
10
  ALIAS=""
11
11
  KEEP_HTML=0
12
+ URL_ONLY=0
12
13
  HTML_OUT=""
13
14
  TEMP_DIR=""
14
15
 
15
16
  usage() {
16
17
  cat <<'EOF'
17
- Usage: render-and-share.sh <markdown-file> [--slug STEM] [--title TITLE] [--alias ALIAS] [--html-out PATH] [--keep-html]
18
+ Usage: render-and-share.sh <markdown-file> [--slug STEM] [--title TITLE] [--alias ALIAS] [--html-out PATH] [--keep-html] [--url-only]
18
19
 
19
20
  Generate an HTML mind map from a markdown file, publish it via share-html, and print the publish JSON.
21
+ Use --url-only to print only the final browser URL.
20
22
  EOF
21
23
  }
22
24
 
@@ -27,6 +29,7 @@ while [[ $# -gt 0 ]]; do
27
29
  --alias) ALIAS="$2"; shift 2 ;;
28
30
  --html-out) HTML_OUT="$2"; shift 2 ;;
29
31
  --keep-html) KEEP_HTML=1; shift ;;
32
+ --url-only) URL_ONLY=1; shift ;;
30
33
  --help) usage; exit 0 ;;
31
34
  --*) echo "Unknown argument: $1" >&2; exit 1 ;;
32
35
  *)
@@ -104,4 +107,17 @@ if [[ -n "$SLUG" ]]; then CMD+=(--slug "$SLUG"); fi
104
107
  if [[ -n "$TITLE" ]]; then CMD+=(--title "$TITLE"); fi
105
108
  if [[ -n "$ALIAS" ]]; then CMD+=(--alias "$ALIAS"); fi
106
109
 
107
- "${CMD[@]}"
110
+ PUBLISH_JSON="$("${CMD[@]}")"
111
+
112
+ if [[ "$URL_ONLY" -eq 1 ]]; then
113
+ python3 - <<'PY' "$PUBLISH_JSON"
114
+ import json, sys
115
+ payload = json.loads(sys.argv[1])
116
+ if not payload.get("ok", True):
117
+ print(json.dumps(payload))
118
+ raise SystemExit(1)
119
+ print(payload.get("url") or payload.get("viewerUrl") or "")
120
+ PY
121
+ else
122
+ printf '%s\n' "$PUBLISH_JSON"
123
+ fi
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deep-thought",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "The Answer Computer — reasoning tools for brainstorming, planning, architecture design, and deep thinking",
5
5
  "author": {
6
6
  "name": "ondrej-svec",
@@ -176,12 +176,12 @@ Use **AskUserQuestion** with:
176
176
  - header: "Next step"
177
177
  - options:
178
178
  1. label: "Start implementation (Recommended)", description: "Proceed to scaffold or test writing"
179
- 2. label: "Visualize / Share", description: "Prefer a shareable HTML view when sharing is configured; otherwise render structure in the terminal"
179
+ 2. label: "Visualize / Share", description: "Generate a shareable HTML view first when sharing is configured; otherwise render structure in the terminal"
180
180
  3. label: "Review and refine", description: "Adjust stories or architecture based on feedback"
181
181
  4. label: "Done for now", description: "Return later"
182
182
  - multiSelect: false
183
183
 
184
- **If user selects "Visualize / Share":** Run `/babel-fish:visualize {stories_path}/{slug}.architecture.md` and prefer the shareable HTML flow when browser viewing or sharing is useful and `share-html` is configured. Otherwise render the terminal mind map. After rendering or sharing, return to this handoff with the remaining options.
184
+ **If user selects "Visualize / Share":** Run `/babel-fish:visualize {stories_path}/{slug}.architecture.md` and try the shareable HTML flow first when `share-html` is configured. Otherwise render the terminal mind map. After rendering or sharing, return to this handoff with the remaining options.
185
185
 
186
186
  **If pipeline mode (`$BRAINSTORM_PATH` set):**
187
187
  Complete without asking. Output paths for downstream consumers:
@@ -291,13 +291,13 @@ Ask the user what to do next.
291
291
  - Prefer the harness's structured choice UI if available
292
292
  - Otherwise present this short plain-text choice list:
293
293
  1. **Proceed to /plan** — Turn these decisions into an implementation plan
294
- 2. **Visualize / Share** — Prefer a shareable HTML mind map when sharing is configured; otherwise render in the terminal
294
+ 2. **Visualize / Share** — Generate a shareable HTML mind map first when sharing is configured; otherwise render in the terminal
295
295
  3. **Keep exploring** — More questions or refine decisions before moving on
296
296
  4. **Done for now** — Return later; to plan: `/plan {brainstorm-path}`
297
297
 
298
298
  **If user selects "Proceed to /plan":** Suggest running `/plan {brainstorm-path}`.
299
299
 
300
- **If user selects "Visualize / Share":** Run `/babel-fish:visualize {brainstorm-path}` and prefer the shareable HTML flow when browser viewing or sharing is useful and `share-html` is configured. Otherwise render the terminal mind map. After rendering or sharing, return to this handoff with the remaining options.
300
+ **If user selects "Visualize / Share":** Run `/babel-fish:visualize {brainstorm-path}` and try the shareable HTML flow first when `share-html` is configured. Otherwise render the terminal mind map. After rendering or sharing, return to this handoff with the remaining options.
301
301
 
302
302
  **If user selects "Keep exploring":** Return to Phase 3 and continue asking questions one at a time. When satisfied, update the document and return to this handoff.
303
303
 
@@ -283,13 +283,13 @@ Ask the user what to do next.
283
283
  - Prefer the harness's structured choice UI if available
284
284
  - Otherwise present this short plain-text choice list:
285
285
  1. **Start /work (Recommended)** — Begin implementing this plan
286
- 2. **Visualize / Share** — Prefer a shareable HTML mind map when sharing is configured; otherwise render in the terminal
286
+ 2. **Visualize / Share** — Generate a shareable HTML mind map first when sharing is configured; otherwise render in the terminal
287
287
  3. **Review and refine** — Adjust the plan based on feedback
288
288
  4. **Done for now** — Return later; to start: `/work {plan-path}`
289
289
 
290
290
  **If user selects "Start /work":** Suggest running `/work {plan-path}`.
291
291
 
292
- **If user selects "Visualize / Share":** Run `/babel-fish:visualize {plan-path}` and prefer the shareable HTML flow when browser viewing or sharing is useful and `share-html` is configured. Otherwise render the terminal mind map. After rendering or sharing, return to this handoff with the remaining options.
292
+ **If user selects "Visualize / Share":** Run `/babel-fish:visualize {plan-path}` and try the shareable HTML flow first when `share-html` is configured. Otherwise render the terminal mind map. After rendering or sharing, return to this handoff with the remaining options.
293
293
 
294
294
  **If user selects "Review and refine":** Accept feedback, update the plan, then present these options again.
295
295
 
@@ -82,15 +82,11 @@ print(fetch_body)
82
82
  PY
83
83
  }
84
84
 
85
- GMAIL_SETTINGS_RAW=$(read_gmail_settings "$CONFIG_PATH" 2>/dev/null) || GMAIL_SETTINGS_RAW=""
86
- if [[ -n "$GMAIL_SETTINGS_RAW" ]]; then
87
- GMAIL_LABEL=$(echo "$GMAIL_SETTINGS_RAW" | sed -n '1p')
88
- MAX_ITEMS=$(echo "$GMAIL_SETTINGS_RAW" | sed -n '2p')
89
- GMAIL_LABEL_ID=$(echo "$GMAIL_SETTINGS_RAW" | sed -n '3p')
90
- FETCH_BODY=$(echo "$GMAIL_SETTINGS_RAW" | sed -n '4p')
91
- GMAIL_LABEL="${GMAIL_LABEL:-Content-Feed}"
92
- MAX_ITEMS="${MAX_ITEMS:-20}"
93
- FETCH_BODY="${FETCH_BODY:-false}"
85
+ if mapfile -t GMAIL_SETTINGS < <(read_gmail_settings "$CONFIG_PATH" 2>/dev/null); then
86
+ GMAIL_LABEL="${GMAIL_SETTINGS[0]:-Content-Feed}"
87
+ MAX_ITEMS="${GMAIL_SETTINGS[1]:-20}"
88
+ GMAIL_LABEL_ID="${GMAIL_SETTINGS[2]:-}"
89
+ FETCH_BODY="${GMAIL_SETTINGS[3]:-false}"
94
90
  else
95
91
  GMAIL_LABEL="Content-Feed"
96
92
  MAX_ITEMS="20"
@@ -98,61 +94,14 @@ else
98
94
  FETCH_BODY="false"
99
95
  fi
100
96
 
101
- # Collect seen Gmail message IDs from previous days to avoid duplicates
102
- PIPELINE_DIR=$(python3 -c "
103
- import yaml, sys
104
- with open(sys.argv[1]) as f:
105
- c = yaml.safe_load(f)
106
- print(c.get('output', {}).get('pipeline_dir', 'content/pipeline'))
107
- " "$CONFIG_PATH" 2>/dev/null || echo "content/pipeline")
108
-
109
- SEEN_IDS=$(python3 -c "
110
- import json, glob, os, sys
111
-
112
- pipeline_dir = sys.argv[1]
113
- config_path = sys.argv[2]
114
-
115
- # Resolve relative pipeline_dir against project root (config's grandparent,
116
- # since config lives in content/ which is one level below project root)
117
- if not os.path.isabs(pipeline_dir):
118
- config_dir = os.path.dirname(os.path.abspath(config_path))
119
- # Walk up until we find .git or use config's parent's parent as fallback
120
- project_root = config_dir
121
- while project_root != '/':
122
- if os.path.isdir(os.path.join(project_root, '.git')):
123
- break
124
- project_root = os.path.dirname(project_root)
125
- pipeline_dir = os.path.join(project_root, pipeline_dir)
126
-
127
- seen = set()
128
- # Check last 7 days of signals files
129
- for path in sorted(glob.glob(os.path.join(pipeline_dir, '*/signals.json')))[-7:]:
130
- try:
131
- signals = json.load(open(path))
132
- for s in signals:
133
- if s.get('source') == 'gmail':
134
- mid = s.get('metadata', {}).get('message_id', '')
135
- if mid:
136
- seen.add(mid)
137
- except (json.JSONDecodeError, KeyError):
138
- pass
139
- # Output as comma-separated IDs
140
- print(','.join(seen))
141
- " "$PIPELINE_DIR" "$CONFIG_PATH" 2>/dev/null || echo "")
142
-
143
- if [[ -n "$SEEN_IDS" ]]; then
144
- SEEN_COUNT=$(echo "$SEEN_IDS" | tr ',' '\n' | wc -l | tr -d ' ')
145
- echo " · $SEEN_COUNT previously seen Gmail message IDs loaded" >&2
146
- fi
147
-
148
97
  # Fetch emails using gws +triage helper (more reliable than raw API calls —
149
98
  # the helper handles auth scopes internally, avoiding insufficientPermissions errors)
150
99
  # Fetch recent emails with labels, then filter by label ID in Python
151
100
  GWS_ERR=$(mktemp)
152
101
  trap "rm -f $GWS_ERR" EXIT
153
102
  EMAILS_JSON=$(gws gmail +triage \
154
- --query "newer_than:7d" \
155
- --max 200 \
103
+ --query "newer_than:3d" \
104
+ --max 50 \
156
105
  --format json \
157
106
  --labels \
158
107
  2>"$GWS_ERR") || {
@@ -173,21 +122,17 @@ EMAILS_JSON=$(gws gmail +triage \
173
122
  # Determine the scripts directory (where this script lives)
174
123
  SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
175
124
 
176
- export _GMAIL_LABEL_ID="$GMAIL_LABEL_ID"
177
- export _GMAIL_MAX_ITEMS="$MAX_ITEMS"
178
- export _GMAIL_CONFIG_PATH="$CONFIG_PATH"
179
- export _GMAIL_SEEN_IDS="$SEEN_IDS"
180
-
181
125
  if [[ "$FETCH_BODY" == "true" ]]; then
182
126
  # Deep mode: filter messages first, then pass to fetch-gmail-deep.py for
183
127
  # full body fetching, link extraction, and article following
184
- echo "$EMAILS_JSON" | python3 -c "
185
- import json, sys, yaml, os
128
+ echo "$EMAILS_JSON" | python3 - "$GMAIL_LABEL_ID" "$MAX_ITEMS" "$CONFIG_PATH" <<'PY' 2>/dev/null | python3 "$SCRIPTS_DIR/fetch-gmail-deep.py" || {
129
+ import json
130
+ import sys
131
+ import yaml
186
132
 
187
- label_id = os.environ['_GMAIL_LABEL_ID']
188
- max_items = int(os.environ['_GMAIL_MAX_ITEMS'])
189
- config_path = os.environ['_GMAIL_CONFIG_PATH']
190
- seen_ids = set(os.environ.get('_GMAIL_SEEN_IDS', '').split(',')) - {''}
133
+ label_id = sys.argv[1]
134
+ max_items = int(sys.argv[2])
135
+ config_path = sys.argv[3]
191
136
 
192
137
  data = json.load(sys.stdin)
193
138
  messages = data.get('messages', []) if isinstance(data, dict) else data
@@ -195,12 +140,6 @@ messages = data.get('messages', []) if isinstance(data, dict) else data
195
140
  if label_id:
196
141
  messages = [m for m in messages if label_id in m.get('labels', [])]
197
142
 
198
- before = len(messages)
199
- messages = [m for m in messages if m.get('id', '') not in seen_ids]
200
- skipped = before - len(messages)
201
- if skipped:
202
- print(f' Dedup: skipped {skipped} already-seen messages', file=sys.stderr)
203
-
204
143
  messages = messages[:max_items]
205
144
 
206
145
  with open(config_path) as f:
@@ -208,19 +147,19 @@ with open(config_path) as f:
208
147
  gmail_config = config.get('sources', {}).get('gmail', {}) or {}
209
148
 
210
149
  json.dump({'messages': messages, 'config': gmail_config}, sys.stdout)
211
- " | python3 "$SCRIPTS_DIR/fetch-gmail-deep.py" || {
150
+ PY
212
151
  echo "Error: Deep Gmail processing failed" >&2
213
152
  exit 1
214
153
  }
215
154
  else
216
155
  # Shallow mode: subject-only signals (original behavior)
217
- echo "$EMAILS_JSON" | python3 -c "
218
- import json, sys, os
156
+ echo "$EMAILS_JSON" | python3 - "$GMAIL_LABEL_ID" "$MAX_ITEMS" <<'PY' 2>/dev/null || {
157
+ import json
158
+ import sys
219
159
  from datetime import datetime, timezone
220
160
 
221
- label_id = os.environ.get('_GMAIL_LABEL_ID', '')
222
- max_items = int(os.environ.get('_GMAIL_MAX_ITEMS', '20'))
223
- seen_ids = set(os.environ.get('_GMAIL_SEEN_IDS', '').split(',')) - {''}
161
+ label_id = sys.argv[1]
162
+ max_items = int(sys.argv[2])
224
163
 
225
164
  data = json.load(sys.stdin)
226
165
  messages = data.get('messages', []) if isinstance(data, dict) else data
@@ -228,12 +167,6 @@ messages = data.get('messages', []) if isinstance(data, dict) else data
228
167
  if label_id:
229
168
  messages = [m for m in messages if label_id in m.get('labels', [])]
230
169
 
231
- before = len(messages)
232
- messages = [m for m in messages if m.get('id', '') not in seen_ids]
233
- skipped = before - len(messages)
234
- if skipped:
235
- print(f' Dedup: skipped {skipped} already-seen messages', file=sys.stderr)
236
-
237
170
  messages = messages[:max_items]
238
171
 
239
172
  signals = []
@@ -270,7 +203,7 @@ for email in messages:
270
203
  })
271
204
 
272
205
  json.dump(signals, sys.stdout, indent=2)
273
- " 2>/dev/null || {
206
+ PY
274
207
  echo "Error: Failed to parse gws output" >&2
275
208
  exit 1
276
209
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marvin",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "The Paranoid Android — quality tools for code review, knowledge compounding, and work execution",
5
5
  "author": {
6
6
  "name": "ondrej-svec",