cli-community-intelligence 0.1.14 → 0.1.16

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.
@@ -1,10 +1,15 @@
1
1
  // ABOUTME: OpenClaw plugin extension entry point
2
2
  // ABOUTME: Registers community-intelligence CLI commands as OpenClaw tools
3
3
 
4
+ import { definePluginEntry } from 'openclaw/plugin-sdk/plugin-entry'
5
+ import { Type } from '@sinclair/typebox'
4
6
  import { exec } from 'child_process'
5
7
  import { promisify } from 'util'
8
+ import { dirname, resolve } from 'path'
9
+ import { fileURLToPath } from 'url'
6
10
 
7
11
  const execAsync = promisify(exec)
12
+ const __dirname = dirname(fileURLToPath(import.meta.url))
8
13
 
9
14
  // ---------------------------------------------------------------------------------------------------------------------
10
15
  //
@@ -30,27 +35,47 @@ const T = {
30
35
 
31
36
  const F = {
32
37
  // Create an execute handler for a given subcommand
38
+ // WHY: resolve the bundled CLI path relative to this file — the `community-intelligence` bin
39
+ // won't be on PATH in OpenClaw's plugin sandbox
33
40
  // @sig buildExecutor :: String -> (String, Object) -> Promise Object
34
41
  buildExecutor: subcommand => async (_toolCallId, params) => {
35
42
  const args = T.toArgs(params)
36
- const cmd = `community-intelligence ${subcommand} ${args}`.trim()
43
+ const cliBin = resolve(__dirname, 'dist/cli.js')
44
+ const cmd = `node "${cliBin}" ${subcommand} ${args}`.trim()
37
45
  try {
38
46
  const { stdout, stderr } = await execAsync(cmd)
39
47
  const text = [stdout, stderr].filter(Boolean).join('\n')
40
- return { content: [{ type: 'text', text }], details: null }
48
+ return { content: [{ type: 'text', text }] }
41
49
  } catch (error) {
42
- return { content: [{ type: 'text', text: `Error: ${String(error)}` }], details: null }
50
+ return { content: [{ type: 'text', text: `Error: ${String(error)}` }] }
43
51
  }
44
52
  },
45
53
  }
46
54
 
55
+ // ---------------------------------------------------------------------------------------------------------------------
56
+ //
57
+ // Constants
58
+ //
59
+ // ---------------------------------------------------------------------------------------------------------------------
60
+
61
+ const queryFilterParams = Type.Object({
62
+ source: Type.Optional(Type.String({ description: 'Filter by source (reddit, youtube)' })),
63
+ channel: Type.Optional(Type.String({ description: 'Filter by channel/subreddit' })),
64
+ text: Type.Optional(Type.String({ description: 'Search text in body and title' })),
65
+ author: Type.Optional(Type.String({ description: 'Filter by author' })),
66
+ since: Type.Optional(Type.String({ description: 'Posts after this date (ISO 8601)' })),
67
+ until: Type.Optional(Type.String({ description: 'Posts before this date (ISO 8601)' })),
68
+ minScore: Type.Optional(Type.Number({ description: 'Minimum score' })),
69
+ limit: Type.Optional(Type.Number({ description: 'Maximum number of results' })),
70
+ })
71
+
47
72
  // ---------------------------------------------------------------------------------------------------------------------
48
73
  //
49
74
  // Exports
50
75
  //
51
76
  // ---------------------------------------------------------------------------------------------------------------------
52
77
 
53
- export default {
78
+ export default definePluginEntry({
54
79
  id: 'cli-community-intelligence',
55
80
  name: 'Community Intelligence',
56
81
  description: 'Scrape and query posts from online communities (Reddit, YouTube)',
@@ -59,20 +84,20 @@ export default {
59
84
  name: 'community-intelligence-scrape',
60
85
  description:
61
86
  'Scrape posts and comments from online communities (Reddit, YouTube). ' +
62
- 'Use --subreddits for Reddit, --query or --channels for YouTube. ' +
63
- 'Supports --incremental to only fetch new posts since last scrape.',
64
- parameters: {
65
- type: 'object',
66
- properties: {
67
- source: { type: 'string', enum: ['reddit', 'youtube'], description: 'Source platform to scrape' },
68
- subreddits: { type: 'string', description: 'Comma-separated subreddit names (reddit only)' },
69
- query: { type: 'string', description: 'Search query (youtube only)' },
70
- channels: { type: 'string', description: 'Comma-separated YouTube channel IDs (youtube only)' },
71
- incremental: { type: 'boolean', description: 'Only fetch posts newer than last scrape' },
72
- backfill: { type: 'number', description: 'Number of days to backfill' },
73
- },
74
- required: ['source'],
75
- },
87
+ 'Use subreddits for Reddit, query or channels for YouTube. ' +
88
+ 'Supports incremental to only fetch new posts since last scrape.',
89
+ parameters: Type.Object({
90
+ source: Type.String({ description: 'Source platform to scrape', enum: ['reddit', 'youtube'] }),
91
+ subreddits: Type.Optional(
92
+ Type.String({ description: 'Comma-separated subreddit names (reddit only)' }),
93
+ ),
94
+ query: Type.Optional(Type.String({ description: 'Search query (youtube only)' })),
95
+ channels: Type.Optional(
96
+ Type.String({ description: 'Comma-separated YouTube channel IDs (youtube only)' }),
97
+ ),
98
+ incremental: Type.Optional(Type.Boolean({ description: 'Only fetch posts newer than last scrape' })),
99
+ backfill: Type.Optional(Type.Number({ description: 'Number of days to backfill' })),
100
+ }),
76
101
  execute: F.buildExecutor('scrape'),
77
102
  })
78
103
 
@@ -80,46 +105,22 @@ export default {
80
105
  name: 'community-intelligence-query',
81
106
  description:
82
107
  'Search the scraped community posts corpus. Filter by source, channel, text, author, date range, or score.',
83
- parameters: {
84
- type: 'object',
85
- properties: {
86
- source: { type: 'string', description: 'Filter by source (reddit, youtube)' },
87
- channel: { type: 'string', description: 'Filter by channel/subreddit' },
88
- text: { type: 'string', description: 'Search text in body and title' },
89
- author: { type: 'string', description: 'Filter by author' },
90
- since: { type: 'string', description: 'Posts after this date (ISO 8601)' },
91
- until: { type: 'string', description: 'Posts before this date (ISO 8601)' },
92
- minScore: { type: 'number', description: 'Minimum score' },
93
- limit: { type: 'number', description: 'Maximum number of results' },
94
- },
95
- },
108
+ parameters: queryFilterParams,
96
109
  execute: F.buildExecutor('query'),
97
110
  })
98
111
 
99
112
  api.registerTool({
100
113
  name: 'community-intelligence-status',
101
114
  description: 'Show summary statistics for the scraped community intelligence corpus.',
102
- parameters: { type: 'object', properties: {} },
115
+ parameters: Type.Object({}),
103
116
  execute: F.buildExecutor('status'),
104
117
  })
105
118
 
106
119
  api.registerTool({
107
120
  name: 'community-intelligence-export',
108
121
  description: 'Export matching posts as JSON. Same filters as query.',
109
- parameters: {
110
- type: 'object',
111
- properties: {
112
- source: { type: 'string', description: 'Filter by source (reddit, youtube)' },
113
- channel: { type: 'string', description: 'Filter by channel/subreddit' },
114
- text: { type: 'string', description: 'Search text in body and title' },
115
- author: { type: 'string', description: 'Filter by author' },
116
- since: { type: 'string', description: 'Posts after this date (ISO 8601)' },
117
- until: { type: 'string', description: 'Posts before this date (ISO 8601)' },
118
- minScore: { type: 'number', description: 'Minimum score' },
119
- limit: { type: 'number', description: 'Maximum number of results' },
120
- },
121
- },
122
+ parameters: queryFilterParams,
122
123
  execute: F.buildExecutor('export'),
123
124
  })
124
125
  },
125
- }
126
+ })
@@ -2,7 +2,7 @@
2
2
  "id": "cli-community-intelligence",
3
3
  "name": "Community Intelligence",
4
4
  "description": "Scrape and query posts from online communities (Reddit, YouTube)",
5
- "version": "0.1.14",
5
+ "version": "0.1.16",
6
6
  "contracts": {
7
7
  "tools": [
8
8
  "community-intelligence-scrape",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-community-intelligence",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Community intelligence scraper for construction industry market research",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -14,7 +14,11 @@
14
14
  "openclaw": {
15
15
  "extensions": [
16
16
  "./openclaw-extension.js"
17
- ]
17
+ ],
18
+ "compat": {
19
+ "pluginApi": ">=2026.3.28",
20
+ "minGatewayVersion": "2026.3.28"
21
+ }
18
22
  },
19
23
  "dependencies": {
20
24
  "better-sqlite3": "^8.7.0"