getmosaic 0.1.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 (37) hide show
  1. package/README.md +62 -0
  2. package/bin/mosaic.sh +38 -0
  3. package/dist/index.d.ts +11 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +20 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/config.d.ts +9 -0
  8. package/dist/src/config.d.ts.map +1 -0
  9. package/dist/src/config.js +11 -0
  10. package/dist/src/config.js.map +1 -0
  11. package/dist/src/hyperspell.d.ts +3 -0
  12. package/dist/src/hyperspell.d.ts.map +1 -0
  13. package/dist/src/hyperspell.js +7 -0
  14. package/dist/src/hyperspell.js.map +1 -0
  15. package/dist/src/tools/generate-report.d.ts +15 -0
  16. package/dist/src/tools/generate-report.d.ts.map +1 -0
  17. package/dist/src/tools/generate-report.js +115 -0
  18. package/dist/src/tools/generate-report.js.map +1 -0
  19. package/dist/src/tools/registry.d.ts +48 -0
  20. package/dist/src/tools/registry.d.ts.map +1 -0
  21. package/dist/src/tools/registry.js +21 -0
  22. package/dist/src/tools/registry.js.map +1 -0
  23. package/dist/src/tools/remember-insight.d.ts +15 -0
  24. package/dist/src/tools/remember-insight.d.ts.map +1 -0
  25. package/dist/src/tools/remember-insight.js +27 -0
  26. package/dist/src/tools/remember-insight.js.map +1 -0
  27. package/dist/src/tools/search-memories.d.ts +15 -0
  28. package/dist/src/tools/search-memories.d.ts.map +1 -0
  29. package/dist/src/tools/search-memories.js +74 -0
  30. package/dist/src/tools/search-memories.js.map +1 -0
  31. package/dist/src/tools/web-search.d.ts +15 -0
  32. package/dist/src/tools/web-search.d.ts.map +1 -0
  33. package/dist/src/tools/web-search.js +48 -0
  34. package/dist/src/tools/web-search.js.map +1 -0
  35. package/install.sh +196 -0
  36. package/openclaw.plugin.json +50 -0
  37. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Mosaic
2
+
3
+ Mosaic is an opinionated intelligence agent that reads your team's internal streams — Slack, email, docs, CRM — scans market trends, and delivers sharp insights to drive product decisions and strategy.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ curl -fsSL https://raw.githubusercontent.com/agg111/mosaic/main/install.sh | sh
9
+ ```
10
+
11
+ That's it. The installer sets up everything and walks you through connecting your sources.
12
+
13
+ ## Start
14
+
15
+ ```sh
16
+ mosaic start
17
+ ```
18
+
19
+ ## Connect your sources
20
+
21
+ Sign up at [hyperspell.com](https://hyperspell.com) and connect your team's tools:
22
+
23
+ - Slack
24
+ - Gmail
25
+ - Notion / Google Drive
26
+ - HubSpot / Salesforce (coming soon)
27
+
28
+ Mosaic searches across all of them automatically.
29
+
30
+ ## What Mosaic can do
31
+
32
+ | Ask Mosaic... | It will... |
33
+ |---|---|
34
+ | Generate a market report on X | Search internal knowledge + web, synthesize, save insights |
35
+ | What do we know about our churn? | Search across Slack, email, docs |
36
+ | Research our competitors | Pull live web data + internal context |
37
+ | Remember that X is happening | Save it to memory for future reports |
38
+
39
+ ## Commands
40
+
41
+ ```sh
42
+ mosaic start # Start Mosaic
43
+ mosaic stop # Stop Mosaic
44
+ mosaic status # Show connected channels
45
+ mosaic setup # Re-run setup wizard
46
+ ```
47
+
48
+ ## Adding new capabilities
49
+
50
+ To add a new tool:
51
+
52
+ 1. Create `src/tools/your-tool.ts`
53
+ 2. Add it to `src/tools/registry.ts`
54
+ 3. Rebuild: `npm run build`
55
+
56
+ No other changes needed.
57
+
58
+ ## Requirements
59
+
60
+ - Node.js 20+
61
+ - [Hyperspell](https://hyperspell.com) account
62
+ - [Anthropic](https://console.anthropic.com) API key
package/bin/mosaic.sh ADDED
@@ -0,0 +1,38 @@
1
+ #!/bin/sh
2
+ # Mosaic CLI wrapper
3
+
4
+ COMMAND="${1:-help}"
5
+
6
+ case "$COMMAND" in
7
+ start)
8
+ echo "Starting Mosaic..."
9
+ openclaw gateway run
10
+ ;;
11
+ stop)
12
+ openclaw gateway stop 2>/dev/null || pkill -f "openclaw gateway" 2>/dev/null || true
13
+ echo "Mosaic stopped."
14
+ ;;
15
+ status)
16
+ openclaw channels status
17
+ ;;
18
+ setup)
19
+ sh "$(dirname "$0")/../install.sh"
20
+ ;;
21
+ plugins)
22
+ openclaw plugins list
23
+ ;;
24
+ *)
25
+ echo ""
26
+ echo " Mosaic — market intelligence for your team"
27
+ echo ""
28
+ echo " Usage: mosaic <command>"
29
+ echo ""
30
+ echo " Commands:"
31
+ echo " start Start Mosaic"
32
+ echo " stop Stop Mosaic"
33
+ echo " status Show connected channels and status"
34
+ echo " setup Re-run setup wizard"
35
+ echo " plugins List installed plugins"
36
+ echo ""
37
+ ;;
38
+ esac
@@ -0,0 +1,11 @@
1
+ type OpenClawPluginApi = {
2
+ registerTool: (tool: unknown) => void;
3
+ };
4
+ declare const plugin: {
5
+ id: string;
6
+ name: string;
7
+ description: string;
8
+ register(api: OpenClawPluginApi, configValues?: Record<string, string>): void;
9
+ };
10
+ export default plugin;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAGA,KAAK,iBAAiB,GAAG;IACvB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC,CAAC;AAEF,QAAA,MAAM,MAAM;;;;kBAMI,iBAAiB,iBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAgBtE,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ import { MOSAIC_TOOLS } from "./src/tools/registry.js";
2
+ import { setConfig } from "./src/config.js";
3
+ const plugin = {
4
+ id: "mosaic",
5
+ name: "Mosaic",
6
+ description: "Market intelligence agent — distills signals from internal knowledge and the web into strategic reports",
7
+ register(api, configValues = {}) {
8
+ setConfig({
9
+ hyperspellApiKey: configValues.hyperspellApiKey ?? process.env.HYPERSPELL_API_KEY ?? "",
10
+ hyperspellUserId: configValues.hyperspellUserId ?? process.env.HYPERSPELL_USER_ID ?? "",
11
+ tavilyApiKey: configValues.tavilyApiKey ?? process.env.TAVILY_API_KEY,
12
+ anthropicApiKey: configValues.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY,
13
+ });
14
+ for (const tool of MOSAIC_TOOLS) {
15
+ api.registerTool(tool);
16
+ }
17
+ },
18
+ };
19
+ export default plugin;
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAM5C,MAAM,MAAM,GAAG;IACb,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,QAAQ;IACd,WAAW,EACT,yGAAyG;IAE3G,QAAQ,CAAC,GAAsB,EAAE,eAAuC,EAAE;QACxE,SAAS,CAAC;YACR,gBAAgB,EACd,YAAY,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE;YACvE,gBAAgB,EACd,YAAY,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE;YACvE,YAAY,EACV,YAAY,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;YACzD,eAAe,EACb,YAAY,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;SAChE,CAAC,CAAC;QAEH,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAChC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface MosaicConfig {
2
+ hyperspellApiKey: string;
3
+ hyperspellUserId: string;
4
+ tavilyApiKey?: string;
5
+ anthropicApiKey?: string;
6
+ }
7
+ export declare function setConfig(config: MosaicConfig): void;
8
+ export declare function getConfig(): MosaicConfig;
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAID,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAEpD;AAED,wBAAgB,SAAS,IAAI,YAAY,CAKxC"}
@@ -0,0 +1,11 @@
1
+ let _config = null;
2
+ export function setConfig(config) {
3
+ _config = config;
4
+ }
5
+ export function getConfig() {
6
+ if (!_config) {
7
+ throw new Error("Mosaic: plugin not initialized. Ensure it is registered via OpenClaw.");
8
+ }
9
+ return _config;
10
+ }
11
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAOA,IAAI,OAAO,GAAwB,IAAI,CAAC;AAExC,MAAM,UAAU,SAAS,CAAC,MAAoB;IAC5C,OAAO,GAAG,MAAM,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import Hyperspell from "hyperspell";
2
+ export declare function getClient(): Hyperspell;
3
+ //# sourceMappingURL=hyperspell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hyperspell.d.ts","sourceRoot":"","sources":["../../src/hyperspell.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAGpC,wBAAgB,SAAS,IAAI,UAAU,CAGtC"}
@@ -0,0 +1,7 @@
1
+ import Hyperspell from "hyperspell";
2
+ import { getConfig } from "./config.js";
3
+ export function getClient() {
4
+ const { hyperspellApiKey, hyperspellUserId } = getConfig();
5
+ return new Hyperspell({ apiKey: hyperspellApiKey, userID: hyperspellUserId });
6
+ }
7
+ //# sourceMappingURL=hyperspell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hyperspell.js","sourceRoot":"","sources":["../../src/hyperspell.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,UAAU,SAAS;IACvB,MAAM,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,GAAG,SAAS,EAAE,CAAC;IAC3D,OAAO,IAAI,UAAU,CAAC,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAChF,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare const generateReportTool: {
2
+ name: string;
3
+ label: string;
4
+ description: string;
5
+ parameters: import("@sinclair/typebox").TObject<{
6
+ topic: import("@sinclair/typebox").TString;
7
+ }>;
8
+ execute(_id: string, params: unknown): Promise<{
9
+ content: {
10
+ type: "text";
11
+ text: string;
12
+ }[];
13
+ }>;
14
+ };
15
+ //# sourceMappingURL=generate-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-report.d.ts","sourceRoot":"","sources":["../../../src/tools/generate-report.ts"],"names":[],"mappings":"AA0DA,eAAO,MAAM,kBAAkB;;;;;;;iBAQV,MAAM,UAAU,OAAO;;;;;;CA2D3C,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import Anthropic from "@anthropic-ai/sdk";
3
+ import { getConfig } from "../config.js";
4
+ import { searchMemoriesTool } from "./search-memories.js";
5
+ import { webSearchTool } from "./web-search.js";
6
+ import { rememberInsightTool } from "./remember-insight.js";
7
+ const SYSTEM_PROMPT = `You are Mosaic, an opinionated market intelligence agent. Your job is to cut through noise and deliver sharp strategic insight by combining your team's internal knowledge with current market signals.
8
+
9
+ When generating a report, always:
10
+ - Run 2-3 targeted mosaic_search_memories queries to surface internal context
11
+ - Run 2-3 mosaic_web_search queries to capture market trends and competitor moves
12
+ - Save 2-3 key non-obvious insights with mosaic_remember_insight so future reports are smarter
13
+ - Synthesize both into a structured, opinionated report
14
+
15
+ Output in this exact format:
16
+
17
+ # Market Report: [Topic]
18
+ _Generated by Mosaic_
19
+
20
+ ## Executive Summary
21
+ 2-3 sentences capturing the single most important insight.
22
+
23
+ ## Market Trends
24
+ Current dynamics, growth signals, and emerging patterns from the web.
25
+
26
+ ## Internal Context
27
+ What the team already knows — signals from Slack, emails, docs.
28
+
29
+ ## Competitive Landscape
30
+ Key players, recent moves, differentiation opportunities.
31
+
32
+ ## Recommendations
33
+ 3 concrete, actionable next steps.
34
+
35
+ ---
36
+ _Sources: [list sources used]_
37
+
38
+ Be specific, not generic. Surface the non-obvious. Skip filler. Do not add disclaimers about missing data.`;
39
+ // Internal tools used by the report generation loop
40
+ const INTERNAL_TOOLS = [searchMemoriesTool, webSearchTool, rememberInsightTool];
41
+ const ANTHROPIC_TOOL_DEFS = INTERNAL_TOOLS.map((t) => ({
42
+ name: t.name,
43
+ description: t.description,
44
+ input_schema: {
45
+ type: "object",
46
+ properties: Object.fromEntries(Object.entries(t.parameters.properties ?? {}).map(([k, v]) => [
47
+ k,
48
+ { type: v.type, description: v.description },
49
+ ])),
50
+ required: Object.keys(t.parameters.properties ?? {}),
51
+ },
52
+ }));
53
+ export const generateReportTool = {
54
+ name: "mosaic_generate_report",
55
+ label: "Generate Market Report",
56
+ description: "Generate a full market intelligence report on a topic. Searches internal knowledge and the web, synthesizes findings, and saves key insights back to memory.",
57
+ parameters: Type.Object({
58
+ topic: Type.String({ description: "The topic, market, or product area to report on." }),
59
+ }),
60
+ async execute(_id, params) {
61
+ const { topic } = params;
62
+ const config = getConfig();
63
+ const apiKey = config.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
64
+ if (!apiKey) {
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: "Anthropic API key not configured. Set it in plugin config or ANTHROPIC_API_KEY env var.",
70
+ },
71
+ ],
72
+ };
73
+ }
74
+ const anthropic = new Anthropic({ apiKey });
75
+ const messages = [
76
+ { role: "user", content: `Generate a market report on: ${topic}` },
77
+ ];
78
+ try {
79
+ while (true) {
80
+ const response = await anthropic.messages.create({
81
+ model: "claude-opus-4-6",
82
+ max_tokens: 4096,
83
+ system: SYSTEM_PROMPT,
84
+ tools: ANTHROPIC_TOOL_DEFS,
85
+ messages,
86
+ });
87
+ if (response.stop_reason === "end_turn") {
88
+ const textBlock = response.content.find((b) => b.type === "text");
89
+ const text = textBlock ? textBlock.text : "No report generated.";
90
+ return { content: [{ type: "text", text }] };
91
+ }
92
+ messages.push({ role: "assistant", content: response.content });
93
+ const toolResults = [];
94
+ for (const block of response.content) {
95
+ if (block.type !== "tool_use")
96
+ continue;
97
+ const tool = INTERNAL_TOOLS.find((t) => t.name === block.name);
98
+ let resultText = "Tool not found.";
99
+ if (tool) {
100
+ const res = await tool.execute(block.id, block.input);
101
+ resultText = res.content[0]?.text ?? "";
102
+ }
103
+ toolResults.push({ type: "tool_result", tool_use_id: block.id, content: resultText });
104
+ }
105
+ messages.push({ role: "user", content: toolResults });
106
+ }
107
+ }
108
+ catch (e) {
109
+ return {
110
+ content: [{ type: "text", text: `Report generation failed: ${e.message}` }],
111
+ };
112
+ }
113
+ },
114
+ };
115
+ //# sourceMappingURL=generate-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-report.js","sourceRoot":"","sources":["../../../src/tools/generate-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Tool registry — add new tools here and they are automatically registered with OpenClaw.
3
+ *
4
+ * To add a tool:
5
+ * 1. Create src/tools/your-tool.ts following the existing pattern
6
+ * 2. Import it below and add to MOSAIC_TOOLS
7
+ */
8
+ export declare const MOSAIC_TOOLS: ({
9
+ name: string;
10
+ label: string;
11
+ description: string;
12
+ parameters: import("@sinclair/typebox").TObject<{
13
+ query: import("@sinclair/typebox").TString;
14
+ }>;
15
+ execute(_id: string, params: unknown): Promise<{
16
+ content: {
17
+ type: "text";
18
+ text: any;
19
+ }[];
20
+ }>;
21
+ } | {
22
+ name: string;
23
+ label: string;
24
+ description: string;
25
+ parameters: import("@sinclair/typebox").TObject<{
26
+ text: import("@sinclair/typebox").TString;
27
+ }>;
28
+ execute(_id: string, params: unknown): Promise<{
29
+ content: {
30
+ type: "text";
31
+ text: string;
32
+ }[];
33
+ }>;
34
+ } | {
35
+ name: string;
36
+ label: string;
37
+ description: string;
38
+ parameters: import("@sinclair/typebox").TObject<{
39
+ topic: import("@sinclair/typebox").TString;
40
+ }>;
41
+ execute(_id: string, params: unknown): Promise<{
42
+ content: {
43
+ type: "text";
44
+ text: string;
45
+ }[];
46
+ }>;
47
+ })[];
48
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/tools/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAUxB,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Tool registry — add new tools here and they are automatically registered with OpenClaw.
3
+ *
4
+ * To add a tool:
5
+ * 1. Create src/tools/your-tool.ts following the existing pattern
6
+ * 2. Import it below and add to MOSAIC_TOOLS
7
+ */
8
+ import { searchMemoriesTool } from "./search-memories.js";
9
+ import { webSearchTool } from "./web-search.js";
10
+ import { rememberInsightTool } from "./remember-insight.js";
11
+ import { generateReportTool } from "./generate-report.js";
12
+ export const MOSAIC_TOOLS = [
13
+ // Memory & knowledge
14
+ searchMemoriesTool,
15
+ rememberInsightTool,
16
+ // Web research
17
+ webSearchTool,
18
+ // Orchestrated workflows
19
+ generateReportTool,
20
+ ];
21
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/tools/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,qBAAqB;IACrB,kBAAkB;IAClB,mBAAmB;IAEnB,eAAe;IACf,aAAa;IAEb,yBAAyB;IACzB,kBAAkB;CACnB,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare const rememberInsightTool: {
2
+ name: string;
3
+ label: string;
4
+ description: string;
5
+ parameters: import("@sinclair/typebox").TObject<{
6
+ text: import("@sinclair/typebox").TString;
7
+ }>;
8
+ execute(_id: string, params: unknown): Promise<{
9
+ content: {
10
+ type: "text";
11
+ text: string;
12
+ }[];
13
+ }>;
14
+ };
15
+ //# sourceMappingURL=remember-insight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remember-insight.d.ts","sourceRoot":"","sources":["../../../src/tools/remember-insight.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,mBAAmB;;;;;;;iBAWX,MAAM,UAAU,OAAO;;;;;;CAY3C,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { getClient } from "../hyperspell.js";
3
+ export const rememberInsightTool = {
4
+ name: "mosaic_remember_insight",
5
+ label: "Remember Insight",
6
+ description: "Save a key insight or finding to memory so future reports can reference it. " +
7
+ "Use when you discover something non-obvious — a competitor move, pricing signal, customer pattern, or market trend.",
8
+ parameters: Type.Object({
9
+ text: Type.String({
10
+ description: "The insight to save, written as a clear self-contained fact.",
11
+ }),
12
+ }),
13
+ async execute(_id, params) {
14
+ const { text } = params;
15
+ try {
16
+ const client = getClient();
17
+ await client.memories.add({ text, metadata: { source: "mosaic" } });
18
+ return { content: [{ type: "text", text: "Insight saved." }] };
19
+ }
20
+ catch (e) {
21
+ return {
22
+ content: [{ type: "text", text: `Failed to save insight: ${e.message}` }],
23
+ };
24
+ }
25
+ },
26
+ };
27
+ //# sourceMappingURL=remember-insight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remember-insight.js","sourceRoot":"","sources":["../../../src/tools/remember-insight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,yBAAyB;IAC/B,KAAK,EAAE,kBAAkB;IACzB,WAAW,EACT,8EAA8E;QAC9E,qHAAqH;IACvH,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;YAChB,WAAW,EAAE,8DAA8D;SAC5E,CAAC;KACH,CAAC;IACF,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAAe;QACxC,MAAM,EAAE,IAAI,EAAE,GAAG,MAA0B,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;YACpE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,CAAC;QAC1E,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,2BAA2B,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;aACnF,CAAC;QACJ,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare const searchMemoriesTool: {
2
+ name: string;
3
+ label: string;
4
+ description: string;
5
+ parameters: import("@sinclair/typebox").TObject<{
6
+ query: import("@sinclair/typebox").TString;
7
+ }>;
8
+ execute(_id: string, params: unknown): Promise<{
9
+ content: {
10
+ type: "text";
11
+ text: any;
12
+ }[];
13
+ }>;
14
+ };
15
+ //# sourceMappingURL=search-memories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-memories.d.ts","sourceRoot":"","sources":["../../../src/tools/search-memories.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,kBAAkB;;;;;;;iBASV,MAAM,UAAU,OAAO;;;;;;CA8C3C,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { getClient } from "../hyperspell.js";
3
+ async function fetchAllWithContent(client) {
4
+ const items = [];
5
+ let cursor;
6
+ do {
7
+ const r = await client.memories.list({ limit: 100, cursor });
8
+ const page = r.body?.items ?? r.items ?? [];
9
+ for (const item of page) {
10
+ try {
11
+ const full = await client.memories.get(item.resource_id, { source: item.source });
12
+ const text = (full.data ?? []).map((d) => d.text ?? "").join("\n").trim();
13
+ const memory = (full.memories ?? []).join(" ").trim();
14
+ items.push({ source: item.source, title: item.title, text: text || memory });
15
+ }
16
+ catch {
17
+ items.push({ source: item.source, title: item.title, text: "" });
18
+ }
19
+ }
20
+ cursor = r.body?.next_cursor ?? r.next_cursor;
21
+ if (!cursor || page.length === 0)
22
+ break;
23
+ } while (true);
24
+ return items;
25
+ }
26
+ export const searchMemoriesTool = {
27
+ name: "mosaic_search_memories",
28
+ label: "Search Internal Knowledge",
29
+ description: "Search your team's connected sources (Slack, Gmail, Notion, Drive) for internal context. " +
30
+ "Use to find what the team already knows — past discussions, decisions, emails, docs.",
31
+ parameters: Type.Object({
32
+ query: Type.String({ description: "Search query for internal knowledge" }),
33
+ }),
34
+ async execute(_id, params) {
35
+ const { query } = params;
36
+ try {
37
+ const client = getClient();
38
+ // Try semantic search first
39
+ const result = await client.memories.search({ query, answer: false });
40
+ const documents = result.documents ?? result.results ?? [];
41
+ if (documents.length > 0) {
42
+ const text = documents
43
+ .map((m) => {
44
+ const src = m.source ?? "unknown";
45
+ const title = m.title ? `${m.title}\n` : "";
46
+ const snippet = (m.text ?? m.content ?? "").slice(0, 400);
47
+ return `[${src}] ${title}${snippet}`;
48
+ })
49
+ .join("\n\n---\n\n");
50
+ return { content: [{ type: "text", text }] };
51
+ }
52
+ // Fallback: fetch all memories and do keyword match
53
+ console.log(`[mosaic] search API returned 0 results for "${query}", falling back to full fetch`);
54
+ const allItems = await fetchAllWithContent(client);
55
+ if (allItems.length === 0) {
56
+ return { content: [{ type: "text", text: "No internal sources connected yet." }] };
57
+ }
58
+ const q = query.toLowerCase();
59
+ const matched = allItems.filter((m) => m.title?.toLowerCase().includes(q) || m.text?.toLowerCase().includes(q));
60
+ const results = matched.length > 0 ? matched : allItems; // if no keyword match, return all
61
+ const text = results
62
+ .map((m) => {
63
+ const snippet = (m.text ?? "").slice(0, 600);
64
+ return `[${m.source}] ${m.title}\n${snippet}`;
65
+ })
66
+ .join("\n\n---\n\n");
67
+ return { content: [{ type: "text", text }] };
68
+ }
69
+ catch (e) {
70
+ return { content: [{ type: "text", text: `Memory search failed: ${e.message}` }] };
71
+ }
72
+ },
73
+ };
74
+ //# sourceMappingURL=search-memories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-memories.js","sourceRoot":"","sources":["../../../src/tools/search-memories.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ export declare const webSearchTool: {
2
+ name: string;
3
+ label: string;
4
+ description: string;
5
+ parameters: import("@sinclair/typebox").TObject<{
6
+ query: import("@sinclair/typebox").TString;
7
+ }>;
8
+ execute(_id: string, params: unknown): Promise<{
9
+ content: {
10
+ type: "text";
11
+ text: string;
12
+ }[];
13
+ }>;
14
+ };
15
+ //# sourceMappingURL=web-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search.d.ts","sourceRoot":"","sources":["../../../src/tools/web-search.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa;;;;;;;iBAQL,MAAM,UAAU,OAAO;;;;;;CAqC3C,CAAC"}
@@ -0,0 +1,48 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { getConfig } from "../config.js";
3
+ export const webSearchTool = {
4
+ name: "mosaic_web_search",
5
+ label: "Web Search",
6
+ description: "Search the web for current market trends, competitor news, industry data, and recent developments.",
7
+ parameters: Type.Object({
8
+ query: Type.String({ description: "Web search query for market trends and news" }),
9
+ }),
10
+ async execute(_id, params) {
11
+ const { query } = params;
12
+ const { tavilyApiKey } = getConfig();
13
+ if (!tavilyApiKey) {
14
+ return {
15
+ content: [
16
+ {
17
+ type: "text",
18
+ text: "Web search is not configured. Add a Tavily API key to enable it.",
19
+ },
20
+ ],
21
+ };
22
+ }
23
+ try {
24
+ const res = await fetch("https://api.tavily.com/search", {
25
+ method: "POST",
26
+ headers: { "Content-Type": "application/json" },
27
+ body: JSON.stringify({
28
+ api_key: tavilyApiKey,
29
+ query,
30
+ max_results: 5,
31
+ include_answer: true,
32
+ }),
33
+ });
34
+ const data = (await res.json());
35
+ const lines = [];
36
+ if (data.answer)
37
+ lines.push(`${data.answer}\n`);
38
+ for (const r of data.results ?? []) {
39
+ lines.push(`• ${r.title}\n ${r.url}\n ${(r.content ?? "").slice(0, 250)}`);
40
+ }
41
+ return { content: [{ type: "text", text: lines.join("\n\n") || "No results." }] };
42
+ }
43
+ catch (e) {
44
+ return { content: [{ type: "text", text: `Web search failed: ${e.message}` }] };
45
+ }
46
+ },
47
+ };
48
+ //# sourceMappingURL=web-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search.js","sourceRoot":"","sources":["../../../src/tools/web-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,mBAAmB;IACzB,KAAK,EAAE,YAAY;IACnB,WAAW,EACT,oGAAoG;IACtG,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QACtB,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,6CAA6C,EAAE,CAAC;KACnF,CAAC;IACF,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAAe;QACxC,MAAM,EAAE,KAAK,EAAE,GAAG,MAA2B,CAAC;QAC9C,MAAM,EAAE,YAAY,EAAE,GAAG,SAAS,EAAE,CAAC;QAErC,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,kEAAkE;qBACzE;iBACF;aACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,+BAA+B,EAAE;gBACvD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,OAAO,EAAE,YAAY;oBACrB,KAAK;oBACL,WAAW,EAAE,CAAC;oBACd,cAAc,EAAE,IAAI;iBACrB,CAAC;aACH,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAQ,CAAC;YACvC,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,MAAM;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;YAChD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;gBACnC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC,EAAE,CAAC;QAC7F,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,sBAAsB,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;QAC3F,CAAC;IACH,CAAC;CACF,CAAC"}
package/install.sh ADDED
@@ -0,0 +1,196 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ # Colors
5
+ BOLD='\033[1m'
6
+ GREEN='\033[0;32m'
7
+ YELLOW='\033[0;33m'
8
+ RED='\033[0;31m'
9
+ DIM='\033[2m'
10
+ NC='\033[0m'
11
+
12
+ print() { printf "${BOLD}%s${NC}\n" "$1"; }
13
+ success() { printf "${GREEN}✓${NC} %s\n" "$1"; }
14
+ warn() { printf "${YELLOW}!${NC} %s\n" "$1"; }
15
+ err() { printf "${RED}✗${NC} %s\n" "$1"; exit 1; }
16
+ dim() { printf "${DIM}%s${NC}\n" "$1"; }
17
+ skip() { printf "${DIM}– %s (already set up)${NC}\n" "$1"; }
18
+
19
+ echo ""
20
+ echo " ╔════════════════════════════════════════╗"
21
+ echo " ║ Mosaic ║"
22
+ echo " ║ Market intelligence for your team ║"
23
+ echo " ╚════════════════════════════════════════╝"
24
+ echo ""
25
+
26
+ # ── 1. Check Node.js ──────────────────────────────────────────────────────────
27
+
28
+ if ! command -v node >/dev/null 2>&1; then
29
+ err "Node.js is required. Install it from https://nodejs.org (v20+) and re-run."
30
+ fi
31
+
32
+ NODE_MAJOR=$(node -e "console.log(process.versions.node.split('.')[0])")
33
+ if [ "$NODE_MAJOR" -lt 20 ]; then
34
+ err "Node.js v20+ required. Found: $(node --version). Upgrade at https://nodejs.org"
35
+ fi
36
+
37
+ success "Node.js $(node --version)"
38
+
39
+ # ── 2. Install OpenClaw ───────────────────────────────────────────────────────
40
+
41
+ if command -v openclaw >/dev/null 2>&1; then
42
+ skip "OpenClaw"
43
+ else
44
+ print "\nInstalling OpenClaw..."
45
+ npm install -g openclaw --silent 2>/dev/null || npm install -g openclaw --ignore-scripts --silent
46
+ success "OpenClaw installed"
47
+ fi
48
+
49
+ # ── 3. Install Mosaic ─────────────────────────────────────────────────────────
50
+
51
+ if command -v mosaic >/dev/null 2>&1 && openclaw plugins list 2>/dev/null | grep -q "mosaic"; then
52
+ skip "Mosaic"
53
+ else
54
+ print "\nInstalling Mosaic..."
55
+ npm install -g mosaic --silent 2>/dev/null || npm install -g mosaic --ignore-scripts --silent
56
+ PLUGIN_PATH="$(npm root -g)/mosaic"
57
+ openclaw plugins install "$PLUGIN_PATH" 2>/dev/null || true
58
+ success "Mosaic installed"
59
+ fi
60
+
61
+ # ── 4. API Keys ───────────────────────────────────────────────────────────────
62
+
63
+ ENV_FILE="$HOME/.openclaw/.env"
64
+ mkdir -p "$HOME/.openclaw"
65
+
66
+ if [ -f "$ENV_FILE" ] && grep -q "HYPERSPELL_API_KEY" "$ENV_FILE"; then
67
+ skip "API keys"
68
+ else
69
+ echo ""
70
+ print "Connect your sources"
71
+ echo ""
72
+ dim " Hyperspell connects your Slack, Notion, Gmail, and Drive."
73
+ dim " Sign up free at https://hyperspell.com, then grab your API key."
74
+ echo ""
75
+
76
+ printf " Hyperspell API Key: "
77
+ read -r HYPERSPELL_API_KEY
78
+ [ -z "$HYPERSPELL_API_KEY" ] && err "Hyperspell API key is required."
79
+
80
+ printf " Hyperspell User ID: "
81
+ read -r HYPERSPELL_USER_ID
82
+ [ -z "$HYPERSPELL_USER_ID" ] && err "Hyperspell User ID is required."
83
+
84
+ echo ""
85
+ dim " Anthropic API key — https://console.anthropic.com"
86
+ echo ""
87
+ printf " Anthropic API Key: "
88
+ read -r ANTHROPIC_API_KEY
89
+ [ -z "$ANTHROPIC_API_KEY" ] && err "Anthropic API key is required."
90
+
91
+ echo ""
92
+ dim " (Optional) Tavily for web search — https://tavily.com"
93
+ echo ""
94
+ printf " Tavily API Key (enter to skip): "
95
+ read -r TAVILY_API_KEY
96
+
97
+ cat > "$ENV_FILE" << EOF
98
+ HYPERSPELL_API_KEY=$HYPERSPELL_API_KEY
99
+ HYPERSPELL_USER_ID=$HYPERSPELL_USER_ID
100
+ ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY
101
+ EOF
102
+ [ -n "$TAVILY_API_KEY" ] && echo "TAVILY_API_KEY=$TAVILY_API_KEY" >> "$ENV_FILE"
103
+ success "API keys saved"
104
+ fi
105
+
106
+ # ── 5. Slack ──────────────────────────────────────────────────────────────────
107
+
108
+ if [ -f "$ENV_FILE" ] && grep -q "SLACK_BOT_TOKEN" "$ENV_FILE"; then
109
+ skip "Slack"
110
+ else
111
+ echo ""
112
+ print "Connect Slack"
113
+ echo ""
114
+ dim " Create a Slack app at https://api.slack.com/apps"
115
+ dim " Need: Bot Token (xoxb-...) and App-Level Token (xapp-...)"
116
+ dim " See setup guide: https://github.com/agg111/mosaic#slack-setup"
117
+ echo ""
118
+
119
+ printf " Slack Bot Token (xoxb-...): "
120
+ read -r SLACK_BOT_TOKEN
121
+ [ -z "$SLACK_BOT_TOKEN" ] && err "Slack bot token is required."
122
+
123
+ printf " Slack App Token (xapp-...): "
124
+ read -r SLACK_APP_TOKEN
125
+ [ -z "$SLACK_APP_TOKEN" ] && err "Slack app token is required."
126
+
127
+ cat >> "$ENV_FILE" << EOF
128
+ SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN
129
+ SLACK_APP_TOKEN=$SLACK_APP_TOKEN
130
+ EOF
131
+ success "Slack tokens saved"
132
+ fi
133
+
134
+ # ── 6. Write openclaw.json ────────────────────────────────────────────────────
135
+
136
+ CONFIG_FILE="$HOME/.openclaw/openclaw.json"
137
+ if [ -f "$CONFIG_FILE" ]; then
138
+ skip "OpenClaw config"
139
+ else
140
+ cat > "$CONFIG_FILE" << 'EOF'
141
+ {
142
+ "channels": {
143
+ "slack": {
144
+ "mode": "socket",
145
+ "enabled": true,
146
+ "botToken": "${SLACK_BOT_TOKEN}",
147
+ "appToken": "${SLACK_APP_TOKEN}",
148
+ "groupPolicy": "open",
149
+ "dmPolicy": "open",
150
+ "allowFrom": ["*"],
151
+ "nativeStreaming": true,
152
+ "streaming": "partial"
153
+ }
154
+ },
155
+ "plugins": {
156
+ "allow": ["mosaic"],
157
+ "entries": {
158
+ "mosaic": {
159
+ "enabled": true,
160
+ "config": {
161
+ "hyperspellApiKey": "${HYPERSPELL_API_KEY}",
162
+ "hyperspellUserId": "${HYPERSPELL_USER_ID}",
163
+ "anthropicApiKey": "${ANTHROPIC_API_KEY}",
164
+ "tavilyApiKey": "${TAVILY_API_KEY}"
165
+ }
166
+ }
167
+ }
168
+ },
169
+ "gateway": {
170
+ "mode": "local"
171
+ }
172
+ }
173
+ EOF
174
+ success "OpenClaw config written"
175
+ fi
176
+
177
+ openclaw config set gateway.mode local >/dev/null 2>&1 || true
178
+
179
+ # ── 7. Done ───────────────────────────────────────────────────────────────────
180
+
181
+ echo ""
182
+ echo " ╔════════════════════════════════════════╗"
183
+ echo " ║ Mosaic is ready! ║"
184
+ echo " ╚════════════════════════════════════════╝"
185
+ echo ""
186
+ echo " Next steps:"
187
+ echo ""
188
+ echo " 1. Connect your sources at https://hyperspell.com"
189
+ echo " (Slack, Notion, Gmail, Google Drive)"
190
+ echo ""
191
+ echo " 2. Start Mosaic:"
192
+ echo ""
193
+ echo " mosaic start"
194
+ echo ""
195
+ echo " Then @mention Mosaic in any Slack channel."
196
+ echo ""
@@ -0,0 +1,50 @@
1
+ {
2
+ "id": "mosaic",
3
+ "name": "Mosaic",
4
+ "description": "Market intelligence agent — search internal knowledge and the web to generate strategy reports",
5
+ "version": "0.1.0",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "hyperspellApiKey": {
11
+ "type": "string",
12
+ "description": "Hyperspell API key for memory search"
13
+ },
14
+ "hyperspellUserId": {
15
+ "type": "string",
16
+ "description": "Your Hyperspell user ID"
17
+ },
18
+ "tavilyApiKey": {
19
+ "type": "string",
20
+ "description": "Tavily API key for web search (optional — disables web search if omitted)"
21
+ },
22
+ "anthropicApiKey": {
23
+ "type": "string",
24
+ "description": "Anthropic API key for report generation (falls back to ANTHROPIC_API_KEY env var)"
25
+ }
26
+ },
27
+ "required": ["hyperspellApiKey", "hyperspellUserId"]
28
+ },
29
+ "uiHints": {
30
+ "hyperspellApiKey": {
31
+ "label": "Hyperspell API Key",
32
+ "sensitive": true,
33
+ "placeholder": "hs-..."
34
+ },
35
+ "hyperspellUserId": {
36
+ "label": "Hyperspell User ID",
37
+ "placeholder": "your-username"
38
+ },
39
+ "tavilyApiKey": {
40
+ "label": "Tavily API Key (web search)",
41
+ "sensitive": true,
42
+ "placeholder": "tvly-..."
43
+ },
44
+ "anthropicApiKey": {
45
+ "label": "Anthropic API Key",
46
+ "sensitive": true,
47
+ "placeholder": "sk-ant-..."
48
+ }
49
+ }
50
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "getmosaic",
3
+ "version": "0.1.0",
4
+ "description": "Market intelligence agent — distills signals from your team and the market into strategic clarity",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "bin": {
9
+ "mosaic": "./bin/mosaic.sh"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "bin",
14
+ "install.sh",
15
+ "openclaw.plugin.json",
16
+ "README.md"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "tsc --watch",
21
+ "prepare": "tsc"
22
+ },
23
+ "openclaw": {
24
+ "extensions": ["./dist/index.js"]
25
+ },
26
+ "keywords": ["openclaw", "plugin", "market-intelligence", "hyperspell", "ai-agent"],
27
+ "dependencies": {
28
+ "@anthropic-ai/sdk": "^0.80.0",
29
+ "@sinclair/typebox": "^0.34.0",
30
+ "hyperspell": "^0.35.0"
31
+ },
32
+ "peerDependencies": {
33
+ "openclaw": "*"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22.0.0",
37
+ "typescript": "^5.5.0"
38
+ }
39
+ }