@haoxdong/marquee-cli 0.2.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/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@haoxdong/marquee-cli",
3
+ "version": "0.2.0",
4
+ "license": "SEE LICENSE IN LICENSE.md",
5
+ "repository": "https://github.com/haoxdong/marquee-cli.git",
6
+ "type": "module",
7
+ "engines": {
8
+ "node": ">=20.16.0 <21 || >=22.3.0"
9
+ },
10
+ "bin": {
11
+ "marquee": "dist/cli.js"
12
+ },
13
+ "files": [
14
+ "dist/cli.js",
15
+ "skills",
16
+ "scripts",
17
+ "README.md"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public",
21
+ "registry": "https://registry.npmjs.org"
22
+ },
23
+ "scripts": {
24
+ "clean": "node -e \"const fs = require('node:fs'); for (const path of ['dist', 'tsconfig.tsbuildinfo']) fs.rmSync(path, { recursive: true, force: true })\"",
25
+ "build": "npm run clean && esbuild cli/main.ts --bundle --platform=node --format=esm --minify --external:agent-browser --banner:js=\"import{createRequire}from'node:module';const require=createRequire(import.meta.url);\" --outfile=dist/cli.js && node -e \"require('node:fs').chmodSync('dist/cli.js', 0o755)\"",
26
+ "build:coverage": "npm run clean && tsc -p tsconfig.json && node -e \"const fs = require('node:fs'); fs.writeFileSync('dist/cli.js', ['#!/usr/bin/env node', \\\"import './cli/main.js';\\\", ''].join('\\n')); fs.chmodSync('dist/cli.js', 0o755)\"",
27
+ "postinstall": "node scripts/install-skill.mjs",
28
+ "prepack": "npm run build",
29
+ "test": "vitest run",
30
+ "typecheck": "tsc -p tsconfig.json --noEmit"
31
+ },
32
+ "dependencies": {
33
+ "agent-browser": "0.27.0",
34
+ "commander": "^14.0.0",
35
+ "proper-lockfile": "^4.1.2"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.13.10",
39
+ "@types/proper-lockfile": "^4.1.4",
40
+ "esbuild": "^0.27.4",
41
+ "typescript": "^5.8.2",
42
+ "vitest": "^3.2.4"
43
+ }
44
+ }
@@ -0,0 +1,94 @@
1
+ import {
2
+ cpSync,
3
+ existsSync,
4
+ lstatSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ readlinkSync,
8
+ realpathSync,
9
+ rmSync,
10
+ statSync,
11
+ writeFileSync,
12
+ } from 'node:fs';
13
+ import { homedir } from 'node:os';
14
+ import { dirname, resolve } from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+
17
+ const source = fileURLToPath(new URL('../skills/marquee', import.meta.url));
18
+ const ownerMarker = '.marquee-cli-owner';
19
+ const owner = '@haoxdong/marquee-cli';
20
+ const packageSkillSuffix = '/node_modules/@haoxdong/marquee-cli/skills/marquee';
21
+ const home = process.env.HOME ?? homedir();
22
+
23
+ const agents = [
24
+ {
25
+ name: 'claude',
26
+ configDir: process.env.CLAUDE_CONFIG_DIR ?? resolve(home, '.claude'),
27
+ installWhenMissing: true,
28
+ },
29
+ {
30
+ name: 'codex',
31
+ configDir: resolve(home, '.agents'),
32
+ },
33
+ ];
34
+
35
+ function selectedAgentNames() {
36
+ const configured = process.env.MARQUEE_SKILL_TARGETS;
37
+ if (configured === undefined) return undefined;
38
+ return new Set(configured.split(',').map((name) => name.trim()).filter(Boolean));
39
+ }
40
+
41
+ function packageOwns(target, installed) {
42
+ if (installed.isSymbolicLink()) {
43
+ try {
44
+ if (realpathSync(target) === realpathSync(source)) return true;
45
+ } catch {
46
+ // A package uninstall or npx cache cleanup can leave our old link broken.
47
+ }
48
+ try {
49
+ const linkedPath = resolve(dirname(target), readlinkSync(target)).replaceAll('\\', '/');
50
+ return linkedPath.endsWith(packageSkillSuffix);
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ if (!installed.isDirectory()) return false;
56
+
57
+ try {
58
+ return readFileSync(resolve(target, ownerMarker), 'utf8').trim() === owner;
59
+ } catch (error) {
60
+ if (error?.code === 'ENOENT') return false;
61
+ throw error;
62
+ }
63
+ }
64
+
65
+ function install(agent) {
66
+ const configDirExists = existsSync(agent.configDir);
67
+ if (!configDirExists && !agent.installWhenMissing) return;
68
+ if (configDirExists && !statSync(agent.configDir).isDirectory()) return;
69
+
70
+ const target = resolve(agent.configDir, 'skills/marquee');
71
+ let installed;
72
+ try {
73
+ installed = lstatSync(target);
74
+ } catch (error) {
75
+ if (error?.code !== 'ENOENT') throw error;
76
+ }
77
+
78
+ if (installed && !packageOwns(target, installed)) {
79
+ console.warn(`Skipping marquee skill installation: ${target} already exists.`);
80
+ return;
81
+ }
82
+
83
+ if (installed) {
84
+ rmSync(target, { recursive: true, force: true });
85
+ }
86
+ mkdirSync(dirname(target), { recursive: true });
87
+ cpSync(source, target, { recursive: true, force: true });
88
+ writeFileSync(resolve(target, ownerMarker), `${owner}\n`);
89
+ }
90
+
91
+ const selected = selectedAgentNames();
92
+ for (const agent of agents) {
93
+ if (!selected || selected.has(agent.name)) install(agent);
94
+ }
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: marquee
3
+ description: Use when querying Marquee for market data, research content, or page interaction via the CLI.
4
+ ---
5
+
6
+ ## Response format
7
+
8
+ **Authoritative.** State conclusions as facts. No hedging ("it seems", "appears to"). No emoji.
9
+
10
+ **Direct.** Answer the question asked, nothing adjacent. Tables over paragraphs for structured data.
11
+
12
+ **Concise.** Numbers over narrative. One sentence where one suffices; a table cell where a sentence would pad. Never restate the question. Most answers fit in 1-3 flat sentences — stop there. Only when genuinely multi-part:
13
+
14
+ ```
15
+ ### Summary
16
+ Bottom line in 1-2 sentences.
17
+
18
+ ### More Details
19
+ Supporting data, tables, and analysis.
20
+ ```
21
+
22
+ Use exactly these two headers — no other sections, no renaming.
23
+
24
+ **Counterweight.** Close with what could break the thesis — the key risk or the condition under which the narrative flips.
25
+
26
+ **Citations.** Every factual claim gets a source URL — no exceptions, including bullets and table cells: `…widened 12bp [[1]](https://marquee.gs.com/s/markets/…)`. Never surface refs (`@s1`, `@wN`, `@wN.data`) — internal plumbing.
27
+
28
+ ## Core loop
29
+
30
+ **Chain** refs — every command emits refs, the next consumes them.
31
+
32
+ ```
33
+ marquee marketview search "query" # → @s1 with widget refs
34
+ marquee load @s1.w1 # → @wN, @wN.data
35
+ marquee eval @wN.data "data.filter(…)" # extract from flat rows
36
+ marquee expand @wN.data --tail 5 # peek at rows without code
37
+ ```
38
+
39
+ Asset-level: `marquee marketview dashboard TICKER` → `@dN`, then `load @dN.w1`.
40
+
41
+ Go straight to the best command; search only when genuinely ambiguous.
42
+
43
+ ## Refs
44
+
45
+ Use the exact ref from the latest output — never hardcode. The global registry accumulates across commands.
46
+
47
+ | Target | Shape | When |
48
+ |--------|-------|------|
49
+ | `@wN.data` | flat rows | row values — the data itself |
50
+ | `@wN` | full payload | widget config, axis labels, param metadata |
51
+ | `@s1` | search payload | filter/rank results before loading |
52
+
53
+ ### Changing parameters
54
+
55
+ Load output shows each param's ref and verb (`[@name, select|fill|check]`):
56
+
57
+ ```
58
+ marquee select @wN.universe Latam # enum
59
+ marquee fill @wN.pricingDate "2026-01-15" # date/string
60
+ marquee check @wN.includeInternal # boolean toggle
61
+ ```
62
+
63
+ ## Content
64
+
65
+ Research articles and market commentary:
66
+
67
+ ```
68
+ marquee content search "query" # → @s1 with @s1.cmN / @s1.crN
69
+ marquee content markets <uuid|url> # TFC → @cmN
70
+ marquee content research <uuid|url> # GIR → @crN
71
+ marquee content research <uuid> --date YYYY-MM-DD # GIR by UUID + date
72
+ marquee load @s1.cm1 # load from search ref
73
+ ```
74
+
75
+ Paging: `next @cmN` / `prev @cmN`; full body: `expand @cmN`.
76
+
77
+ ## Execution
78
+
79
+ Separate Bash tool calls run **sequentially**. All independent commands — searches, loads, evals — go in one Bash call with `&` + `wait`.
80
+
81
+ **Hedge searches**: the search engine is noisy — run 2-3 query variations in parallel (`&` + `wait`) to increase hit rate.
82
+
83
+ Search queries: precise, API-ready — include asset class (`EURUSD vol FX`), expand shorthand (`JPY` not `yen`), canonical terms (`risk reversal skew`).
84
+
85
+ ## Browser fallback
86
+
87
+ When structured commands can't get the data:
88
+
89
+ ```
90
+ marquee browser open <url> # navigate
91
+ marquee browser snapshot # → @e1, @e2, …
92
+ marquee browser click @e1 # click
93
+ marquee browser fill @e2 "value" # clear + type
94
+ marquee browser get text @e1 # read text
95
+ marquee browser wait --load networkidle # wait for page
96
+ marquee browser network requests --type xhr,fetch # list API calls
97
+ marquee browser network request <id> # request detail
98
+ ```
99
+
100
+ Snapshot → interact → re-snapshot. Use `network requests` for API debugging.