@kiyeonjeon21/ncli 0.1.1

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.
@@ -0,0 +1,323 @@
1
+ export const SCHEMAS = {
2
+ search: {
3
+ description: "Naver Search APIs",
4
+ methods: {
5
+ "search.blog": {
6
+ httpMethod: "GET",
7
+ path: "/v1/search/blog.json",
8
+ description: "Search Naver blogs",
9
+ rateLimit: "25,000/day",
10
+ parameters: {
11
+ query: { type: "string", required: true, description: "Search query" },
12
+ display: { type: "integer", default: 10, min: 1, max: 100, description: "Number of results" },
13
+ start: { type: "integer", default: 1, min: 1, max: 1000, description: "Start position" },
14
+ sort: { type: "string", enum: ["sim", "date"], default: "sim", description: "Sort order" },
15
+ },
16
+ response: {
17
+ fields: ["lastBuildDate", "total", "start", "display", "items"],
18
+ itemFields: ["title", "link", "description", "bloggername", "bloggerlink", "postdate"],
19
+ },
20
+ },
21
+ "search.news": {
22
+ httpMethod: "GET",
23
+ path: "/v1/search/news.json",
24
+ description: "Search Naver news",
25
+ rateLimit: "25,000/day",
26
+ parameters: {
27
+ query: { type: "string", required: true, description: "Search query" },
28
+ display: { type: "integer", default: 10, min: 1, max: 100 },
29
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
30
+ sort: { type: "string", enum: ["sim", "date"], default: "sim" },
31
+ },
32
+ response: {
33
+ itemFields: ["title", "originallink", "link", "description", "pubDate"],
34
+ },
35
+ },
36
+ "search.web": {
37
+ httpMethod: "GET",
38
+ path: "/v1/search/web.json",
39
+ description: "Search web pages",
40
+ rateLimit: "25,000/day",
41
+ parameters: {
42
+ query: { type: "string", required: true },
43
+ display: { type: "integer", default: 10, min: 1, max: 100 },
44
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
45
+ },
46
+ response: {
47
+ itemFields: ["title", "link", "description"],
48
+ },
49
+ },
50
+ "search.image": {
51
+ httpMethod: "GET",
52
+ path: "/v1/search/image",
53
+ description: "Search images",
54
+ rateLimit: "25,000/day",
55
+ parameters: {
56
+ query: { type: "string", required: true },
57
+ display: { type: "integer", default: 10, min: 1, max: 100 },
58
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
59
+ sort: { type: "string", enum: ["sim", "date"], default: "sim" },
60
+ filter: { type: "string", enum: ["all", "large", "medium", "small"], default: "all" },
61
+ },
62
+ response: {
63
+ itemFields: ["title", "link", "thumbnail", "sizeheight", "sizewidth"],
64
+ },
65
+ },
66
+ "search.book": {
67
+ httpMethod: "GET",
68
+ path: "/v1/search/book.json",
69
+ description: "Search books",
70
+ rateLimit: "25,000/day",
71
+ parameters: {
72
+ query: { type: "string", required: true },
73
+ display: { type: "integer", default: 10, min: 1, max: 100 },
74
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
75
+ sort: { type: "string", enum: ["sim", "date"], default: "sim" },
76
+ },
77
+ response: {
78
+ itemFields: ["title", "link", "image", "author", "discount", "publisher", "pubdate", "isbn", "description"],
79
+ },
80
+ },
81
+ "search.cafe": {
82
+ httpMethod: "GET",
83
+ path: "/v1/search/cafearticle.json",
84
+ description: "Search Naver cafe articles",
85
+ rateLimit: "25,000/day",
86
+ parameters: {
87
+ query: { type: "string", required: true },
88
+ display: { type: "integer", default: 10, min: 1, max: 100 },
89
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
90
+ sort: { type: "string", enum: ["sim", "date"], default: "sim" },
91
+ },
92
+ response: {
93
+ itemFields: ["title", "link", "description", "cafename", "cafeurl"],
94
+ },
95
+ },
96
+ "search.kin": {
97
+ httpMethod: "GET",
98
+ path: "/v1/search/kin.json",
99
+ description: "Search Naver Knowledge iN (Q&A)",
100
+ rateLimit: "25,000/day",
101
+ parameters: {
102
+ query: { type: "string", required: true },
103
+ display: { type: "integer", default: 10, min: 1, max: 100 },
104
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
105
+ sort: { type: "string", enum: ["sim", "date", "count", "point"], default: "sim" },
106
+ },
107
+ response: {
108
+ itemFields: ["title", "link", "description"],
109
+ },
110
+ },
111
+ "search.encyclopedia": {
112
+ httpMethod: "GET",
113
+ path: "/v1/search/encyc.json",
114
+ description: "Search encyclopedia",
115
+ rateLimit: "25,000/day",
116
+ parameters: {
117
+ query: { type: "string", required: true },
118
+ display: { type: "integer", default: 10, min: 1, max: 100 },
119
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
120
+ },
121
+ response: {
122
+ itemFields: ["title", "link", "description", "thumbnail"],
123
+ },
124
+ },
125
+ "search.shop": {
126
+ httpMethod: "GET",
127
+ path: "/v1/search/shop.json",
128
+ description: "Search shopping items",
129
+ rateLimit: "25,000/day",
130
+ parameters: {
131
+ query: { type: "string", required: true },
132
+ display: { type: "integer", default: 10, min: 1, max: 100 },
133
+ start: { type: "integer", default: 1, min: 1, max: 1000 },
134
+ sort: { type: "string", enum: ["sim", "date", "asc", "dsc"], default: "sim" },
135
+ },
136
+ response: {
137
+ itemFields: ["title", "link", "image", "lprice", "hprice", "mallName", "productId", "productType", "brand", "maker", "category1", "category2", "category3", "category4"],
138
+ },
139
+ },
140
+ "search.local": {
141
+ httpMethod: "GET",
142
+ path: "/v1/search/local.json",
143
+ description: "Search local places (restaurants, shops, etc.)",
144
+ rateLimit: "25,000/day",
145
+ parameters: {
146
+ query: { type: "string", required: true, description: "Search query" },
147
+ display: { type: "integer", default: 1, min: 1, max: 5, description: "Number of results" },
148
+ start: { type: "integer", default: 1, min: 1, max: 1, description: "Start position" },
149
+ sort: { type: "string", enum: ["random", "comment"], default: "random", description: "Sort order" },
150
+ },
151
+ response: {
152
+ itemFields: ["title", "link", "category", "description", "telephone", "address", "roadAddress", "mapx", "mapy"],
153
+ },
154
+ },
155
+ "search.errata": {
156
+ httpMethod: "GET",
157
+ path: "/v1/search/errata.json",
158
+ description: "Spell check / typo correction",
159
+ rateLimit: "25,000/day",
160
+ parameters: {
161
+ query: { type: "string", required: true, description: "Text to check for typos" },
162
+ },
163
+ response: {
164
+ fields: ["errata"],
165
+ note: "Returns corrected text or empty string if no error",
166
+ },
167
+ },
168
+ "search.adult": {
169
+ httpMethod: "GET",
170
+ path: "/v1/search/adult.json",
171
+ description: "Adult content keyword detection",
172
+ rateLimit: "25,000/day",
173
+ parameters: {
174
+ query: { type: "string", required: true, description: "Keyword to check" },
175
+ },
176
+ response: {
177
+ fields: ["adult"],
178
+ note: "Returns 0 (general) or 1 (adult content)",
179
+ },
180
+ },
181
+ "search.doc": {
182
+ httpMethod: "GET",
183
+ path: "/v1/search/doc.json",
184
+ description: "Search professional/scholarly documents",
185
+ rateLimit: "25,000/day",
186
+ parameters: {
187
+ query: { type: "string", required: true, description: "Search query" },
188
+ display: { type: "integer", default: 10, min: 1, max: 100, description: "Number of results" },
189
+ start: { type: "integer", default: 1, min: 1, max: 1000, description: "Start position" },
190
+ },
191
+ response: {
192
+ itemFields: ["title", "link", "description"],
193
+ },
194
+ },
195
+ },
196
+ },
197
+ captcha: {
198
+ description: "Naver CAPTCHA APIs (image and voice)",
199
+ methods: {
200
+ "captcha.imageKey": {
201
+ httpMethod: "GET",
202
+ path: "/v1/captcha/nkey",
203
+ description: "Issue or verify image CAPTCHA key",
204
+ rateLimit: "1,000/day",
205
+ parameters: {
206
+ code: { type: "integer", required: true, enum: [0, 1], description: "0: issue key, 1: verify input" },
207
+ key: { type: "string", required: false, description: "CAPTCHA key (required when code=1)" },
208
+ value: { type: "string", required: false, description: "User input to verify (required when code=1)" },
209
+ },
210
+ response: {
211
+ fields: ["key", "result", "error"],
212
+ },
213
+ },
214
+ "captcha.imageFile": {
215
+ httpMethod: "GET",
216
+ path: "/v1/captcha/ncaptcha.bin",
217
+ description: "Download CAPTCHA image (JPG)",
218
+ rateLimit: "1,000/day",
219
+ parameters: {
220
+ key: { type: "string", required: true, description: "CAPTCHA key from nkey API" },
221
+ },
222
+ response: {
223
+ note: "Returns JPG image binary",
224
+ },
225
+ },
226
+ "captcha.voiceKey": {
227
+ httpMethod: "GET",
228
+ path: "/v1/captcha/skey",
229
+ description: "Issue or verify voice CAPTCHA key",
230
+ rateLimit: "1,000/day",
231
+ parameters: {
232
+ code: { type: "integer", required: true, enum: [0, 1], description: "0: issue key, 1: verify input" },
233
+ key: { type: "string", required: false, description: "CAPTCHA key (required when code=1)" },
234
+ value: { type: "string", required: false, description: "User input to verify (required when code=1)" },
235
+ },
236
+ response: {
237
+ fields: ["key", "result", "error"],
238
+ },
239
+ },
240
+ "captcha.voiceFile": {
241
+ httpMethod: "GET",
242
+ path: "/v1/captcha/scaptcha",
243
+ description: "Download CAPTCHA voice audio (WAV)",
244
+ rateLimit: "1,000/day",
245
+ parameters: {
246
+ key: { type: "string", required: true, description: "CAPTCHA key from skey API" },
247
+ },
248
+ response: {
249
+ note: "Returns WAV audio binary",
250
+ },
251
+ },
252
+ },
253
+ },
254
+ datalab: {
255
+ description: "Naver DataLab APIs",
256
+ methods: {
257
+ "datalab.trend": {
258
+ httpMethod: "POST",
259
+ path: "/v1/datalab/search",
260
+ description: "Search keyword trend analysis",
261
+ rateLimit: "1,000/day",
262
+ request: {
263
+ startDate: { type: "string", required: true, format: "yyyy-mm-dd", description: "Start date" },
264
+ endDate: { type: "string", required: true, format: "yyyy-mm-dd", description: "End date" },
265
+ timeUnit: { type: "string", required: true, enum: ["date", "week", "month"], description: "Time unit" },
266
+ keywordGroups: {
267
+ type: "array",
268
+ required: true,
269
+ description: "Keyword groups (max 5)",
270
+ items: {
271
+ groupName: { type: "string", required: true },
272
+ keywords: { type: "array", items: "string", required: true },
273
+ },
274
+ },
275
+ device: { type: "string", enum: ["", "pc", "mo"], description: "Device filter" },
276
+ gender: { type: "string", enum: ["", "m", "f"], description: "Gender filter" },
277
+ ages: { type: "array", items: "string", description: "Age groups" },
278
+ },
279
+ },
280
+ "datalab.shopping": {
281
+ httpMethod: "POST",
282
+ path: "/v1/datalab/shopping/categories",
283
+ description: "Shopping insight by category",
284
+ rateLimit: "1,000/day",
285
+ request: {
286
+ startDate: { type: "string", required: true, format: "yyyy-mm-dd" },
287
+ endDate: { type: "string", required: true, format: "yyyy-mm-dd" },
288
+ timeUnit: { type: "string", required: true, enum: ["date", "week", "month"] },
289
+ category: { type: "array", required: true, description: "Category codes" },
290
+ device: { type: "string", enum: ["", "pc", "mo"] },
291
+ gender: { type: "string", enum: ["", "m", "f"] },
292
+ ages: { type: "array", items: "string" },
293
+ },
294
+ },
295
+ },
296
+ },
297
+ };
298
+ export function listServices() {
299
+ return {
300
+ services: Object.entries(SCHEMAS).map(([name, schema]) => ({
301
+ name,
302
+ description: schema.description,
303
+ })),
304
+ };
305
+ }
306
+ export function listMethods(service) {
307
+ const svc = SCHEMAS[service];
308
+ if (!svc)
309
+ return null;
310
+ const methods = svc.methods;
311
+ return {
312
+ service,
313
+ methods: Object.keys(methods),
314
+ };
315
+ }
316
+ export function getSchema(method) {
317
+ const [service] = method.split(".");
318
+ const svc = SCHEMAS[service];
319
+ if (!svc)
320
+ return null;
321
+ const methods = svc.methods;
322
+ return methods[method] ?? null;
323
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@kiyeonjeon21/ncli",
3
+ "version": "0.1.1",
4
+ "description": "Agent-native CLI for Naver Open APIs",
5
+ "type": "module",
6
+ "bin": {
7
+ "ncli": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "CONTEXT.md",
12
+ "AGENTS.md",
13
+ "skills"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "tsx src/cli.ts",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "preversion": "npm test && npm run build",
21
+ "postversion": "git push && git push --tags",
22
+ "prepublishOnly": "npm test && npm run build"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/kiyeonjeon21/naver-cli.git"
27
+ },
28
+ "keywords": [
29
+ "naver",
30
+ "cli",
31
+ "agentic",
32
+ "ai-agent",
33
+ "openapi"
34
+ ],
35
+ "author": "Kiyeon Jeon",
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "commander": "^14.0.3",
39
+ "dotenv": "^17.4.1"
40
+ },
41
+ "devDependencies": {
42
+ "@types/node": "^25.6.0",
43
+ "tsx": "^4.21.0",
44
+ "typescript": "^6.0.2",
45
+ "vitest": "^4.1.4"
46
+ }
47
+ }
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: naver-datalab
3
+ description: Rules and patterns for using naver CLI DataLab APIs. Use when analyzing search trends or shopping insights.
4
+ ---
5
+
6
+ # naver datalab Operations
7
+
8
+ ## Rules
9
+
10
+ 1. Always use `--json` with full payload — DataLab APIs require structured input
11
+ 2. Always use `--output json` for output
12
+ 3. Rate limit is 1,000 calls/day — use sparingly
13
+ 4. Date format is `yyyy-mm-dd`
14
+ 5. `timeUnit` must be one of: `date`, `week`, `month`
15
+ 6. Maximum 5 keyword groups per request
16
+
17
+ ## Authentication
18
+
19
+ ```bash
20
+ export NAVER_CLIENT_ID="your-client-id"
21
+ export NAVER_CLIENT_SECRET="your-client-secret"
22
+ ```
23
+
24
+ ## Workflows
25
+
26
+ ### Search keyword trend
27
+
28
+ ```bash
29
+ naver datalab trend --json '{
30
+ "startDate": "2026-01-01",
31
+ "endDate": "2026-04-01",
32
+ "timeUnit": "month",
33
+ "keywordGroups": [
34
+ {"groupName": "AI", "keywords": ["인공지능", "AI"]},
35
+ {"groupName": "블록체인", "keywords": ["블록체인", "blockchain"]}
36
+ ]
37
+ }'
38
+ ```
39
+
40
+ ### Shopping category trend
41
+
42
+ ```bash
43
+ naver datalab shopping --json '{
44
+ "startDate": "2026-01-01",
45
+ "endDate": "2026-04-01",
46
+ "timeUnit": "month",
47
+ "category": [
48
+ {"name": "노트북", "param": ["50000832"]}
49
+ ]
50
+ }'
51
+ ```
52
+
53
+ ### With filters
54
+
55
+ ```bash
56
+ naver datalab trend --json '{
57
+ "startDate": "2026-01-01",
58
+ "endDate": "2026-04-01",
59
+ "timeUnit": "week",
60
+ "keywordGroups": [{"groupName": "test", "keywords": ["테스트"]}],
61
+ "device": "mo",
62
+ "gender": "f",
63
+ "ages": ["20", "30"]
64
+ }'
65
+ ```
66
+
67
+ ## Schema Introspection
68
+
69
+ ```bash
70
+ naver schema datalab.trend
71
+ naver schema datalab.shopping
72
+ ```
73
+
74
+ ## Error Handling
75
+
76
+ | Error Code | Meaning | Action |
77
+ |------------|---------|--------|
78
+ | `AUTH_REQUIRED` | Missing credentials | Set environment variables |
79
+ | `HTTP_400` | Bad request | Check required fields: startDate, endDate, timeUnit, keywordGroups/category |
80
+ | `HTTP_429` | Rate limited (1,000/day) | This is a hard daily limit — plan queries carefully |
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: naver-search
3
+ description: Rules and patterns for using naver CLI to search Naver services. Use when searching blog, news, web, image, book, cafe, kin, encyclopedia, or shop.
4
+ ---
5
+
6
+ # naver search Operations
7
+
8
+ ## Rules
9
+
10
+ 1. Always use `--output json` for all commands
11
+ 2. Always add `--fields` to minimize response size
12
+ 3. Preferred fields for common searches:
13
+ - blog: `title,link,description,bloggername,postdate`
14
+ - news: `title,link,description,pubDate`
15
+ - shop: `title,link,lprice,mallName,brand`
16
+ - book: `title,author,publisher,isbn,description`
17
+ 4. Use `--json` for complex queries with multiple parameters
18
+ 5. Use `--sanitize` when response data will be injected into agent context
19
+
20
+ ## Authentication
21
+
22
+ ```bash
23
+ export NAVER_CLIENT_ID="your-client-id"
24
+ export NAVER_CLIENT_SECRET="your-client-secret"
25
+ ```
26
+
27
+ ## Workflows
28
+
29
+ ### Quick search
30
+
31
+ ```bash
32
+ naver search blog "검색어" --fields "title,link,description"
33
+ ```
34
+
35
+ ### Paginated search
36
+
37
+ ```bash
38
+ # Page 1
39
+ naver search news "AI" --display 10 --start 1 --fields "title,link,pubDate"
40
+
41
+ # Page 2
42
+ naver search news "AI" --display 10 --start 11 --fields "title,link,pubDate"
43
+ ```
44
+
45
+ ### JSON payload
46
+
47
+ ```bash
48
+ naver search shop --json '{"query":"맥북","display":5,"sort":"dsc"}' \
49
+ --fields "title,lprice,hprice,mallName"
50
+ ```
51
+
52
+ ## Schema Introspection
53
+
54
+ ```bash
55
+ naver schema search.blog
56
+ naver schema search.shop
57
+ ```
58
+
59
+ ## Error Handling
60
+
61
+ | Error Code | Meaning | Action |
62
+ |------------|---------|--------|
63
+ | `AUTH_REQUIRED` | Missing credentials | Set environment variables |
64
+ | `HTTP_400` | Bad request | Check schema for valid parameters |
65
+ | `HTTP_429` | Rate limited (25,000/day) | Wait and retry |
66
+ | `MISSING_QUERY` | No query provided | Add query argument |