r3plica 1.0.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/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # r3 — R3PLICA CLI
2
+
3
+ Query the R3PLICA digital replicas catalog from your terminal. Search products, brands, collections, and finishes — built for AI agents, developers, and scripts.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g r3plica
9
+ ```
10
+
11
+ Requires Node.js 20+.
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Search products
17
+ r3 product search "dining table"
18
+
19
+ # List brands
20
+ r3 brand list
21
+
22
+ # Filter by brand and category
23
+ r3 product list --brand "Cattelan Italia" --category table
24
+
25
+ # Get product details
26
+ r3 product get <mongo_id>
27
+
28
+ # Pipe to jq
29
+ r3 product list --limit 5 --json | jq '.[].name'
30
+ ```
31
+
32
+ ## Commands
33
+
34
+ ### Products
35
+
36
+ ```bash
37
+ r3 product list [--brand X] [--category X] [--collection X] [--limit N] [--page N]
38
+ r3 product search <query> [--brand X] [--category X] [--limit N]
39
+ r3 product get <mongo_id>
40
+ ```
41
+
42
+ ### Brands
43
+
44
+ ```bash
45
+ r3 brand list [--country X] [--limit N]
46
+ r3 brand search <query>
47
+ ```
48
+
49
+ ### Product Collections
50
+
51
+ Product collections are furniture lines from a brand (e.g., "Tudor" by Cattelan Italia).
52
+
53
+ ```bash
54
+ r3 product-collection list [--brand X] [--limit N] [--page N] [--sort X] [--order asc|desc]
55
+ r3 product-collection search <query>
56
+ ```
57
+
58
+ ### Finish Collections
59
+
60
+ Finish collections are sets of material finishes (shaders) — wood types, fabrics, metals, leathers — that can be applied to product parts.
61
+
62
+ ```bash
63
+ r3 finish-collection list [--brand X] [--category X] [--limit N] [--page N]
64
+ r3 finish-collection search <query>
65
+ r3 finish-collection get <mongo_id>
66
+ ```
67
+
68
+ ### Configuration
69
+
70
+ ```bash
71
+ r3 config show
72
+ r3 config set api-url <url>
73
+ ```
74
+
75
+ Default API URL: `https://api.r3plica.space/api/r3vault`. Override with `R3_API_URL` environment variable or `r3 config set`.
76
+
77
+ ## Output
78
+
79
+ - **Terminal**: formatted table with headers and result count
80
+ - **Piped**: JSON (automatic detection)
81
+ - `--json`: force JSON output
82
+ - `--pretty`: force table output
83
+
84
+ ```bash
85
+ # Table output (terminal)
86
+ $ r3 brand list --limit 3
87
+
88
+ ID Name Country Products
89
+ 0a497f00-cc6f-4 Driade Italy 29
90
+ 22b4a540-bcf2-4 We Are IB Italy 37
91
+ ab368a13-6038-4 Emu Italy 26
92
+
93
+ 3 brands found
94
+
95
+ # JSON output (piped or --json)
96
+ $ r3 brand list --limit 1 --json | jq '.[0].name'
97
+ "Driade"
98
+ ```
99
+
100
+ ## Use with AI Agents
101
+
102
+ ### Claude Code / CLAUDE.md
103
+
104
+ Add to your project's `CLAUDE.md`:
105
+
106
+ ```markdown
107
+ ## R3PLICA Catalog
108
+
109
+ The `r3` CLI queries the R3PLICA 3D furniture catalog. Use it when the user
110
+ asks about furniture, materials, brands, or design products.
111
+
112
+ - `r3 product search "query"` — search products by text
113
+ - `r3 product list --brand X --category X` — filter products
114
+ - `r3 product get <id>` — product details with finish options
115
+ - `r3 brand list` — all available brands
116
+ - `r3 finish-collection list --brand X` — available finish collections
117
+ - Always use `--json` when processing results programmatically
118
+ - Run `r3 --help` for full command reference
119
+ ```
120
+
121
+ ### MCP Server
122
+
123
+ The package also includes an MCP server for auto-discovery in Claude Desktop, Cursor, and similar IDEs.
124
+
125
+ Add to your Claude Desktop config (`claude_desktop_config.json`):
126
+
127
+ ```json
128
+ {
129
+ "mcpServers": {
130
+ "r3plica": {
131
+ "command": "r3-mcp"
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ The MCP server exposes the same data through 10 tools: `product_list`, `product_search`, `product_get`, `brand_list`, `brand_search`, `product_collection_list`, `product_collection_search`, `finish_collection_list`, `finish_collection_search`, `finish_collection_get`.
138
+
139
+ ## Examples
140
+
141
+ ```bash
142
+ # Find Italian brands
143
+ r3 brand list --country Italy
144
+
145
+ # Search chairs, limit to 5 results
146
+ r3 product search "chair" --limit 5
147
+
148
+ # Get all products from a brand
149
+ r3 product list --brand "Driade"
150
+
151
+ # Explore a product collection
152
+ r3 product-collection search "Tudor"
153
+
154
+ # Find wood finishes
155
+ r3 finish-collection list --category wood
156
+
157
+ # Script: get all brand names
158
+ r3 brand list --json | jq -r '.[].name'
159
+
160
+ # Script: count products per brand
161
+ r3 brand list --json | jq -r '.[].name' | while read brand; do
162
+ count=$(r3 product list --brand "$brand" --json 2>/dev/null | jq length)
163
+ echo "$brand: $count"
164
+ done
165
+ ```
166
+
167
+ ## About R3PLICA
168
+
169
+ [R3PLICA](https://r3plica.io) provides high-quality 3D digital replicas of branded furniture to architects, interior designers, and visualization professionals. The catalog includes 60+ premium brands including Cattelan Italia, Driade, Poliform, and more.
170
+
171
+ ## License
172
+
173
+ Proprietary - R3PLICA S.r.l.
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerBrandCommands(program: Command): void;
@@ -0,0 +1,53 @@
1
+ import { listBrands, searchBrands } from "../../core/api.js";
2
+ import { output } from "../../core/formatter.js";
3
+ import { handleError } from "../../core/errors.js";
4
+ const BRAND_COLUMNS = [
5
+ { key: "mongo_id", header: "ID", width: 16 },
6
+ { key: "name", header: "Name", width: 25 },
7
+ { key: "contact.country", header: "Country", width: 15 },
8
+ { key: "products_count", header: "Products", width: 10 },
9
+ ];
10
+ export function registerBrandCommands(program) {
11
+ const brand = program.command("brand").description("Browse and search brands");
12
+ brand
13
+ .command("list")
14
+ .description("List all public brands")
15
+ .option("--country <country>", "Filter by country")
16
+ .option("--limit <n>", "Max results", "50")
17
+ .action(async (opts) => {
18
+ try {
19
+ const results = await listBrands({
20
+ country: opts.country,
21
+ limit: parseInt(opts.limit),
22
+ });
23
+ output(results, {
24
+ json: program.opts().json,
25
+ pretty: program.opts().pretty,
26
+ label: "brand",
27
+ columns: BRAND_COLUMNS,
28
+ });
29
+ }
30
+ catch (error) {
31
+ handleError(error);
32
+ }
33
+ });
34
+ brand
35
+ .command("search <query>")
36
+ .description("Search brands by name")
37
+ .action(async (query) => {
38
+ try {
39
+ const results = await searchBrands(query);
40
+ output(results, {
41
+ json: program.opts().json,
42
+ pretty: program.opts().pretty,
43
+ label: "brand",
44
+ query,
45
+ columns: BRAND_COLUMNS,
46
+ });
47
+ }
48
+ catch (error) {
49
+ handleError(error);
50
+ }
51
+ });
52
+ }
53
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../../../src/cli/commands/brand.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,aAAa,GAAG;IACpB,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;IAC5C,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;IAC1C,EAAE,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE;IACxD,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE;CACzD,CAAC;AAEF,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC;IAE/E,KAAK;SACF,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,wBAAwB,CAAC;SACrC,MAAM,CAAC,qBAAqB,EAAE,mBAAmB,CAAC;SAClD,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC;SAC1C,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC;gBAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;aAC5B,CAAC,CAAC;YACH,MAAM,CAAC,OAAO,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI;gBACzB,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM;gBAC7B,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,aAAa;aACvB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,KAAK;SACF,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,uBAAuB,CAAC;SACpC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;YAC1C,MAAM,CAAC,OAAO,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI;gBACzB,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM;gBAC7B,KAAK,EAAE,OAAO;gBACd,KAAK;gBACL,OAAO,EAAE,aAAa;aACvB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerConfigCommands(program: Command): void;
@@ -0,0 +1,34 @@
1
+ import { setConfigValue, getFullConfig } from "../../core/config.js";
2
+ export function registerConfigCommands(program) {
3
+ const config = program.command("config").description("Manage CLI configuration");
4
+ config
5
+ .command("set <key> <value>")
6
+ .description("Set a config value (available keys: api-url)")
7
+ .action((key, value) => {
8
+ try {
9
+ setConfigValue(key, value);
10
+ process.stdout.write(`Set ${key} = ${value}\n`);
11
+ }
12
+ catch (error) {
13
+ process.stderr.write(`Error: ${error instanceof Error ? error.message : error}\n`);
14
+ process.exit(1);
15
+ }
16
+ });
17
+ config
18
+ .command("show")
19
+ .description("Show current configuration")
20
+ .action(() => {
21
+ const cfg = getFullConfig();
22
+ if (program.opts().json) {
23
+ process.stdout.write(JSON.stringify(cfg, null, 2) + "\n");
24
+ }
25
+ else {
26
+ process.stdout.write("\n");
27
+ for (const [key, value] of Object.entries(cfg)) {
28
+ process.stdout.write(` ${key}: ${value}\n`);
29
+ }
30
+ process.stdout.write("\n");
31
+ }
32
+ });
33
+ }
34
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErE,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC;IAEjF,MAAM;SACH,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,8CAA8C,CAAC;SAC3D,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;YACnF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,MAAM;SACH,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,4BAA4B,CAAC;SACzC,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC;YAC/C,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerFinishCollectionCommands(program: Command): void;
@@ -0,0 +1,71 @@
1
+ import { listFinishCollections, searchFinishCollections, getFinishCollection, } from "../../core/api.js";
2
+ import { output, outputDetail } from "../../core/formatter.js";
3
+ import { handleError } from "../../core/errors.js";
4
+ const FINISH_COLLECTION_COLUMNS = [
5
+ { key: "mongo_id", header: "ID", width: 16 },
6
+ { key: "name", header: "Name", width: 30 },
7
+ { key: "brand.name", header: "Brand", width: 20 },
8
+ { key: "category.name", header: "Category", width: 12 },
9
+ ];
10
+ export function registerFinishCollectionCommands(program) {
11
+ const fc = program
12
+ .command("finish-collection")
13
+ .description("Browse finish collections (material finishes/shaders)");
14
+ fc.command("list")
15
+ .description("List finish collections")
16
+ .option("--brand <name>", "Filter by brand name")
17
+ .option("--category <category>", "Filter by category (Wood, Fabric, Metal, etc.)")
18
+ .option("--limit <n>", "Max results", "20")
19
+ .option("--page <n>", "Page number", "1")
20
+ .action(async (opts) => {
21
+ try {
22
+ const results = await listFinishCollections({
23
+ brand: opts.brand,
24
+ category: opts.category,
25
+ limit: parseInt(opts.limit),
26
+ page: parseInt(opts.page),
27
+ });
28
+ output(results, {
29
+ json: program.opts().json,
30
+ pretty: program.opts().pretty,
31
+ label: "finish collection",
32
+ columns: FINISH_COLLECTION_COLUMNS,
33
+ });
34
+ }
35
+ catch (error) {
36
+ handleError(error);
37
+ }
38
+ });
39
+ fc.command("search <query>")
40
+ .description("Search finish collections by name")
41
+ .action(async (query) => {
42
+ try {
43
+ const results = await searchFinishCollections(query);
44
+ output(results, {
45
+ json: program.opts().json,
46
+ pretty: program.opts().pretty,
47
+ label: "finish collection",
48
+ query,
49
+ columns: FINISH_COLLECTION_COLUMNS,
50
+ });
51
+ }
52
+ catch (error) {
53
+ handleError(error);
54
+ }
55
+ });
56
+ fc.command("get <id>")
57
+ .description("Get finish collection details with all finishes")
58
+ .action(async (id) => {
59
+ try {
60
+ const result = await getFinishCollection(id);
61
+ outputDetail(result, {
62
+ json: program.opts().json,
63
+ pretty: program.opts().pretty,
64
+ });
65
+ }
66
+ catch (error) {
67
+ handleError(error);
68
+ }
69
+ });
70
+ }
71
+ //# sourceMappingURL=finish-collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finish-collection.js","sourceRoot":"","sources":["../../../src/cli/commands/finish-collection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerProductCollectionCommands(program: Command): void;
@@ -0,0 +1,59 @@
1
+ import { listProductCollections, searchProductCollections } from "../../core/api.js";
2
+ import { output } from "../../core/formatter.js";
3
+ import { handleError } from "../../core/errors.js";
4
+ const COLLECTION_COLUMNS = [
5
+ { key: "mongo_id", header: "ID", width: 16 },
6
+ { key: "name", header: "Name", width: 30 },
7
+ { key: "brand.name", header: "Brand", width: 20 },
8
+ { key: "products_count", header: "Products", width: 10 },
9
+ ];
10
+ export function registerProductCollectionCommands(program) {
11
+ const pc = program
12
+ .command("product-collection")
13
+ .description("Browse product collections (furniture lines)");
14
+ pc.command("list")
15
+ .description("List product collections")
16
+ .option("--brand <name>", "Filter by brand name")
17
+ .option("--limit <n>", "Max results", "20")
18
+ .option("--page <n>", "Page number", "1")
19
+ .option("--sort <field>", "Sort by field")
20
+ .option("--order <dir>", "Sort direction (asc|desc)", "asc")
21
+ .action(async (opts) => {
22
+ try {
23
+ const results = await listProductCollections({
24
+ brand: opts.brand,
25
+ limit: parseInt(opts.limit),
26
+ page: parseInt(opts.page),
27
+ sort: opts.sort,
28
+ order: opts.sort ? opts.order : undefined,
29
+ });
30
+ output(results, {
31
+ json: program.opts().json,
32
+ pretty: program.opts().pretty,
33
+ label: "collection",
34
+ columns: COLLECTION_COLUMNS,
35
+ });
36
+ }
37
+ catch (error) {
38
+ handleError(error);
39
+ }
40
+ });
41
+ pc.command("search <query>")
42
+ .description("Search product collections by name")
43
+ .action(async (query) => {
44
+ try {
45
+ const results = await searchProductCollections(query);
46
+ output(results, {
47
+ json: program.opts().json,
48
+ pretty: program.opts().pretty,
49
+ label: "collection",
50
+ query,
51
+ columns: COLLECTION_COLUMNS,
52
+ });
53
+ }
54
+ catch (error) {
55
+ handleError(error);
56
+ }
57
+ });
58
+ }
59
+ //# sourceMappingURL=product-collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-collection.js","sourceRoot":"","sources":["../../../src/cli/commands/product-collection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,kBAAkB,GAAG;IACzB,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;IAC5C,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE;IAC1C,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;IACjD,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE;CACzD,CAAC;AAEF,MAAM,UAAU,iCAAiC,CAAC,OAAgB;IAChE,MAAM,EAAE,GAAG,OAAO;SACf,OAAO,CAAC,oBAAoB,CAAC;SAC7B,WAAW,CAAC,8CAA8C,CAAC,CAAC;IAE/D,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,0BAA0B,CAAC;SACvC,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAAC;SAChD,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC;SAC1C,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE,GAAG,CAAC;SACxC,MAAM,CAAC,gBAAgB,EAAE,eAAe,CAAC;SACzC,MAAM,CAAC,eAAe,EAAE,2BAA2B,EAAE,KAAK,CAAC;SAC3D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC;gBAC3C,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC3B,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;gBACzB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aAC1C,CAAC,CAAC;YACH,MAAM,CAAC,OAAO,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI;gBACzB,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM;gBAC7B,KAAK,EAAE,YAAY;gBACnB,OAAO,EAAE,kBAAkB;aAC5B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,EAAE,CAAC,OAAO,CAAC,gBAAgB,CAAC;SACzB,WAAW,CAAC,oCAAoC,CAAC;SACjD,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACtB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAAC,KAAK,CAAC,CAAC;YACtD,MAAM,CAAC,OAAO,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI;gBACzB,MAAM,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM;gBAC7B,KAAK,EAAE,YAAY;gBACnB,KAAK;gBACL,OAAO,EAAE,kBAAkB;aAC5B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function registerProductCommands(program: Command): void;
@@ -0,0 +1,81 @@
1
+ import { listProducts, searchProducts, getProduct } from "../../core/api.js";
2
+ import { output, outputDetail } from "../../core/formatter.js";
3
+ import { handleError } from "../../core/errors.js";
4
+ const PRODUCT_COLUMNS = [
5
+ { key: "mongo_id", header: "ID", width: 16 },
6
+ { key: "name", header: "Name", width: 30 },
7
+ { key: "brand.name", header: "Brand", width: 20 },
8
+ { key: "category", header: "Category", width: 15 },
9
+ ];
10
+ export function registerProductCommands(program) {
11
+ const product = program.command("product").description("Browse and search products");
12
+ product
13
+ .command("list")
14
+ .description("List products with optional filters")
15
+ .option("--brand <name>", "Filter by brand name")
16
+ .option("--category <category>", "Filter by product category")
17
+ .option("--collection <name>", "Filter by collection name")
18
+ .option("--limit <n>", "Max results", "20")
19
+ .option("--page <n>", "Page number", "1")
20
+ .action(async (opts) => {
21
+ try {
22
+ const results = await listProducts({
23
+ brand: opts.brand,
24
+ category: opts.category,
25
+ collection: opts.collection,
26
+ limit: parseInt(opts.limit),
27
+ page: parseInt(opts.page),
28
+ });
29
+ output(results, {
30
+ json: program.opts().json,
31
+ pretty: program.opts().pretty,
32
+ label: "product",
33
+ columns: PRODUCT_COLUMNS,
34
+ });
35
+ }
36
+ catch (error) {
37
+ handleError(error);
38
+ }
39
+ });
40
+ product
41
+ .command("search <query>")
42
+ .description("Search products by text")
43
+ .option("--brand <name>", "Filter by brand name")
44
+ .option("--category <category>", "Filter by product category")
45
+ .option("--limit <n>", "Max results", "20")
46
+ .action(async (query, opts) => {
47
+ try {
48
+ const results = await searchProducts(query, {
49
+ brand: opts.brand,
50
+ category: opts.category,
51
+ limit: parseInt(opts.limit),
52
+ });
53
+ output(results, {
54
+ json: program.opts().json,
55
+ pretty: program.opts().pretty,
56
+ label: "product",
57
+ query,
58
+ columns: PRODUCT_COLUMNS,
59
+ });
60
+ }
61
+ catch (error) {
62
+ handleError(error);
63
+ }
64
+ });
65
+ product
66
+ .command("get <id>")
67
+ .description("Get detailed product information")
68
+ .action(async (id) => {
69
+ try {
70
+ const result = await getProduct(id);
71
+ outputDetail(result, {
72
+ json: program.opts().json,
73
+ pretty: program.opts().pretty,
74
+ });
75
+ }
76
+ catch (error) {
77
+ handleError(error);
78
+ }
79
+ });
80
+ }
81
+ //# sourceMappingURL=product.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product.js","sourceRoot":"","sources":["../../../src/cli/commands/product.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { registerProductCommands } from "./commands/product.js";
4
+ import { registerBrandCommands } from "./commands/brand.js";
5
+ import { registerProductCollectionCommands } from "./commands/product-collection.js";
6
+ import { registerFinishCollectionCommands } from "./commands/finish-collection.js";
7
+ import { registerConfigCommands } from "./commands/config.js";
8
+ const program = new Command();
9
+ program
10
+ .name("r3")
11
+ .description("R3PLICA CLI — Query the R3PLICA 3D furniture catalog")
12
+ .version("1.0.0")
13
+ .option("--json", "Force JSON output")
14
+ .option("--pretty", "Force formatted table output")
15
+ .option("--verbose", "Show debug info on stderr");
16
+ registerProductCommands(program);
17
+ registerBrandCommands(program);
18
+ registerProductCollectionCommands(program);
19
+ registerFinishCollectionCommands(program);
20
+ registerConfigCommands(program);
21
+ program.parse();
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,iCAAiC,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAE,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,sDAAsD,CAAC;KACnE,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,QAAQ,EAAE,mBAAmB,CAAC;KACrC,MAAM,CAAC,UAAU,EAAE,8BAA8B,CAAC;KAClD,MAAM,CAAC,WAAW,EAAE,2BAA2B,CAAC,CAAC;AAEpD,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,iCAAiC,CAAC,OAAO,CAAC,CAAC;AAC3C,gCAAgC,CAAC,OAAO,CAAC,CAAC;AAC1C,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAEhC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,38 @@
1
+ export declare class ApiError extends Error {
2
+ statusCode?: number | undefined;
3
+ constructor(message: string, statusCode?: number | undefined);
4
+ }
5
+ export declare function listProducts(opts: {
6
+ brand?: string;
7
+ category?: string;
8
+ collection?: string;
9
+ limit?: number;
10
+ page?: number;
11
+ }): Promise<any[]>;
12
+ export declare function searchProducts(query: string, opts: {
13
+ brand?: string;
14
+ category?: string;
15
+ limit?: number;
16
+ }): Promise<any[]>;
17
+ export declare function getProduct(mongoId: string): Promise<any>;
18
+ export declare function listBrands(opts: {
19
+ country?: string;
20
+ limit?: number;
21
+ }): Promise<any[]>;
22
+ export declare function searchBrands(query: string): Promise<any[]>;
23
+ export declare function listProductCollections(opts: {
24
+ brand?: string;
25
+ limit?: number;
26
+ page?: number;
27
+ sort?: string;
28
+ order?: string;
29
+ }): Promise<any[]>;
30
+ export declare function searchProductCollections(query: string): Promise<any[]>;
31
+ export declare function listFinishCollections(opts: {
32
+ brand?: string;
33
+ category?: string;
34
+ limit?: number;
35
+ page?: number;
36
+ }): Promise<any[]>;
37
+ export declare function searchFinishCollections(query: string): Promise<any[]>;
38
+ export declare function getFinishCollection(mongoId: string): Promise<any>;
@@ -0,0 +1,158 @@
1
+ import { getApiUrl } from "./config.js";
2
+ const TIMEOUT_MS = 10_000;
3
+ export class ApiError extends Error {
4
+ statusCode;
5
+ constructor(message, statusCode) {
6
+ super(message);
7
+ this.statusCode = statusCode;
8
+ this.name = "ApiError";
9
+ }
10
+ }
11
+ async function request(path, params = {}) {
12
+ const baseUrl = getApiUrl();
13
+ const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : baseUrl + "/");
14
+ for (const [key, value] of Object.entries(params)) {
15
+ if (value)
16
+ url.searchParams.set(key, value);
17
+ }
18
+ const controller = new AbortController();
19
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
20
+ try {
21
+ const response = await fetch(url.toString(), {
22
+ signal: controller.signal,
23
+ headers: { "Accept": "application/json" },
24
+ });
25
+ if (!response.ok) {
26
+ throw new ApiError(`API returned ${response.status}: ${response.statusText}`, response.status);
27
+ }
28
+ const data = await response.json();
29
+ if (data.status === false) {
30
+ throw new ApiError(data.message || "API returned an error");
31
+ }
32
+ return data;
33
+ }
34
+ catch (error) {
35
+ if (error instanceof ApiError)
36
+ throw error;
37
+ if (error instanceof Error) {
38
+ if (error.name === "AbortError") {
39
+ throw new ApiError("Request timed out after 10s");
40
+ }
41
+ if (error.message.includes("fetch failed") || error.message.includes("ECONNREFUSED")) {
42
+ throw new ApiError(`Cannot connect to R3PLICA API (${baseUrl})\n Check your connection or run: r3 config set api-url <url>`);
43
+ }
44
+ }
45
+ throw new ApiError(`Unexpected error: ${error}`);
46
+ }
47
+ finally {
48
+ clearTimeout(timeout);
49
+ }
50
+ }
51
+ /** Build the q= filter string from key-value pairs */
52
+ function buildQuery(filters) {
53
+ return Object.entries(filters)
54
+ .filter(([, v]) => v !== undefined && v !== "")
55
+ .map(([k, v]) => `${k}:${v}`)
56
+ .join(";");
57
+ }
58
+ // ── Products ──────────────────────────────────────────────
59
+ export async function listProducts(opts) {
60
+ const q = buildQuery({
61
+ brand: opts.brand,
62
+ category: opts.category,
63
+ collection: opts.collection,
64
+ });
65
+ const data = await request("products", {
66
+ ...(q && { q }),
67
+ page: String(opts.page || 1),
68
+ limit: String(opts.limit || 20),
69
+ });
70
+ return data.products || [];
71
+ }
72
+ export async function searchProducts(query, opts) {
73
+ const q = buildQuery({
74
+ name: query,
75
+ brand: opts.brand,
76
+ category: opts.category,
77
+ });
78
+ const data = await request("products", {
79
+ q,
80
+ limit: String(opts.limit || 20),
81
+ });
82
+ return data.products || [];
83
+ }
84
+ export async function getProduct(mongoId) {
85
+ const data = await request("products", {
86
+ q: `mongo_id:${mongoId}`,
87
+ limit: "1",
88
+ });
89
+ const products = data.products || [];
90
+ if (products.length === 0) {
91
+ throw new ApiError(`Product "${mongoId}" not found`);
92
+ }
93
+ return products[0];
94
+ }
95
+ // ── Brands ────────────────────────────────────────────────
96
+ export async function listBrands(opts) {
97
+ const data = await request("brands", {
98
+ limit: String(opts.limit || 50),
99
+ });
100
+ let brands = data.brands || [];
101
+ // Client-side filtering (brands endpoint has no q= support)
102
+ if (opts.country) {
103
+ const country = opts.country.toLowerCase();
104
+ brands = brands.filter((b) => b.contact?.country?.toLowerCase().includes(country));
105
+ }
106
+ return brands;
107
+ }
108
+ export async function searchBrands(query) {
109
+ const data = await request("brands", { limit: "9999" });
110
+ const brands = data.brands || [];
111
+ const q = query.toLowerCase();
112
+ return brands.filter((b) => b.name?.toLowerCase().includes(q) ||
113
+ b.description?.toLowerCase().includes(q));
114
+ }
115
+ // ── Product Collections ───────────────────────────────────
116
+ export async function listProductCollections(opts) {
117
+ const q = buildQuery({ brand: opts.brand });
118
+ const data = await request("product-collections", {
119
+ ...(q && { q }),
120
+ page: String(opts.page || 1),
121
+ limit: String(opts.limit || 20),
122
+ ...(opts.sort && { sort: opts.sort }),
123
+ ...(opts.order && { order: opts.order }),
124
+ });
125
+ return data.product_collections || [];
126
+ }
127
+ export async function searchProductCollections(query) {
128
+ const q = buildQuery({ name: query });
129
+ const data = await request("product-collections", { q, limit: "100" });
130
+ return data.product_collections || [];
131
+ }
132
+ // ── Finish Collections ────────────────────────────────────
133
+ export async function listFinishCollections(opts) {
134
+ const q = buildQuery({ brand: opts.brand, category: opts.category });
135
+ const data = await request("finish-collections", {
136
+ q,
137
+ page: String(opts.page || 1),
138
+ limit: String(opts.limit || 20),
139
+ });
140
+ return data.finish_collections || [];
141
+ }
142
+ export async function searchFinishCollections(query) {
143
+ const q = buildQuery({ name: query });
144
+ const data = await request("finish-collections", { q, limit: "100" });
145
+ return data.finish_collections || [];
146
+ }
147
+ export async function getFinishCollection(mongoId) {
148
+ const data = await request("finish-collections", {
149
+ q: `mongo_id:${mongoId}`,
150
+ limit: "1",
151
+ });
152
+ const collections = data.finish_collections || [];
153
+ if (collections.length === 0) {
154
+ throw new ApiError(`Finish collection "${mongoId}" not found`);
155
+ }
156
+ return collections[0];
157
+ }
158
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function getApiUrl(): string;
2
+ export declare function setConfigValue(key: string, value: string): void;
3
+ export declare function getFullConfig(): Record<string, string>;
@@ -0,0 +1,50 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ const CONFIG_DIR = join(homedir(), ".config", "r3");
5
+ const CONFIG_FILE = join(CONFIG_DIR, "config.json");
6
+ const DEFAULT_API_URL = "https://api.r3plica.space/api/r3vault";
7
+ function loadConfig() {
8
+ try {
9
+ if (existsSync(CONFIG_FILE)) {
10
+ const raw = readFileSync(CONFIG_FILE, "utf-8");
11
+ return JSON.parse(raw);
12
+ }
13
+ }
14
+ catch {
15
+ // Ignore corrupt config
16
+ }
17
+ return {};
18
+ }
19
+ function saveConfig(config) {
20
+ mkdirSync(CONFIG_DIR, { recursive: true });
21
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
22
+ }
23
+ export function getApiUrl() {
24
+ if (process.env.R3_API_URL) {
25
+ return process.env.R3_API_URL;
26
+ }
27
+ const config = loadConfig();
28
+ return config.apiUrl || DEFAULT_API_URL;
29
+ }
30
+ export function setConfigValue(key, value) {
31
+ if (key !== "api-url") {
32
+ throw new Error(`Unknown config key: "${key}". Available keys: api-url`);
33
+ }
34
+ const config = loadConfig();
35
+ config.apiUrl = value;
36
+ saveConfig(config);
37
+ }
38
+ export function getFullConfig() {
39
+ const config = loadConfig();
40
+ return {
41
+ "api-url": getApiUrl(),
42
+ "source": process.env.R3_API_URL
43
+ ? "environment (R3_API_URL)"
44
+ : config.apiUrl
45
+ ? "config file"
46
+ : "default",
47
+ "config-path": CONFIG_FILE,
48
+ };
49
+ }
50
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;AACpD,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AACpD,MAAM,eAAe,GAAG,uCAAuC,CAAC;AAMhE,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,MAAgB;IAClC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IAChC,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,CAAC,MAAM,IAAI,eAAe,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,KAAa;IACvD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,4BAA4B,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO;QACL,SAAS,EAAE,SAAS,EAAE;QACtB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;YAC9B,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,MAAM,CAAC,MAAM;gBACb,CAAC,CAAC,aAAa;gBACf,CAAC,CAAC,SAAS;QACf,aAAa,EAAE,WAAW;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function handleError(error: unknown): never;
@@ -0,0 +1,11 @@
1
+ import { ApiError } from "./api.js";
2
+ export function handleError(error) {
3
+ if (error instanceof ApiError) {
4
+ process.stderr.write(`Error: ${error.message}\n`);
5
+ }
6
+ else {
7
+ process.stderr.write(`Error: ${error}\n`);
8
+ }
9
+ process.exit(1);
10
+ }
11
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IACpD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,18 @@
1
+ interface Column {
2
+ key: string;
3
+ header: string;
4
+ width?: number;
5
+ }
6
+ export declare function output(data: any, opts: {
7
+ json?: boolean;
8
+ pretty?: boolean;
9
+ columns?: Column[];
10
+ label?: string;
11
+ query?: string;
12
+ }): void;
13
+ /** Output a single item detail (for `get` commands) */
14
+ export declare function outputDetail(data: any, opts: {
15
+ json?: boolean;
16
+ pretty?: boolean;
17
+ }): void;
18
+ export {};
@@ -0,0 +1,96 @@
1
+ function isTTY() {
2
+ return process.stdout.isTTY === true;
3
+ }
4
+ export function output(data, opts) {
5
+ const forceJson = opts.json === true;
6
+ const forcePretty = opts.pretty === true;
7
+ const useTable = forcePretty || (!forceJson && isTTY());
8
+ if (!useTable || !opts.columns) {
9
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
10
+ return;
11
+ }
12
+ const items = Array.isArray(data) ? data : [data];
13
+ if (items.length === 0) {
14
+ const label = opts.label || "items";
15
+ const suffix = opts.query ? ` matching "${opts.query}"` : "";
16
+ process.stderr.write(`No ${label}s found${suffix}\n`);
17
+ return;
18
+ }
19
+ const columns = opts.columns;
20
+ // Calculate column widths
21
+ const widths = columns.map((col) => {
22
+ const headerLen = col.header.length;
23
+ const maxDataLen = items.reduce((max, item) => {
24
+ const val = String(resolveValue(item, col.key) ?? "");
25
+ return Math.max(max, val.length);
26
+ }, 0);
27
+ return col.width || Math.min(Math.max(headerLen, maxDataLen) + 2, 40);
28
+ });
29
+ // Header
30
+ const header = columns.map((col, i) => col.header.padEnd(widths[i])).join("");
31
+ process.stdout.write("\n " + header + "\n");
32
+ // Rows
33
+ for (const item of items) {
34
+ const row = columns
35
+ .map((col, i) => {
36
+ const val = String(resolveValue(item, col.key) ?? "");
37
+ return val.substring(0, widths[i] - 1).padEnd(widths[i]);
38
+ })
39
+ .join("");
40
+ process.stdout.write(" " + row + "\n");
41
+ }
42
+ // Footer
43
+ const label = opts.label || "item";
44
+ const count = items.length;
45
+ process.stdout.write(`\n ${count} ${label}${count !== 1 ? "s" : ""} found\n\n`);
46
+ }
47
+ /** Resolve a dot-path key like "brand.name" from an object.
48
+ * If a value along the path is an array, uses the first element. */
49
+ function resolveValue(obj, key) {
50
+ return key.split(".").reduce((acc, part) => {
51
+ if (acc === null || acc === undefined)
52
+ return undefined;
53
+ const val = acc[part];
54
+ return Array.isArray(val) ? val[0] : val;
55
+ }, obj);
56
+ }
57
+ /** Output a single item detail (for `get` commands) */
58
+ export function outputDetail(data, opts) {
59
+ const forceJson = opts.json === true;
60
+ const forcePretty = opts.pretty === true;
61
+ const useFormatted = forcePretty || (!forceJson && isTTY());
62
+ if (!useFormatted) {
63
+ process.stdout.write(JSON.stringify(data, null, 2) + "\n");
64
+ return;
65
+ }
66
+ process.stdout.write("\n");
67
+ printObject(data, 0);
68
+ process.stdout.write("\n");
69
+ }
70
+ function printObject(obj, indent) {
71
+ const pad = " ".repeat(indent + 1);
72
+ for (const [key, value] of Object.entries(obj)) {
73
+ if (value === null || value === undefined)
74
+ continue;
75
+ if (Array.isArray(value)) {
76
+ process.stdout.write(`${pad}${key}:\n`);
77
+ for (const item of value) {
78
+ if (typeof item === "object" && item !== null) {
79
+ process.stdout.write(`${pad} -\n`);
80
+ printObject(item, indent + 2);
81
+ }
82
+ else {
83
+ process.stdout.write(`${pad} - ${item}\n`);
84
+ }
85
+ }
86
+ }
87
+ else if (typeof value === "object") {
88
+ process.stdout.write(`${pad}${key}:\n`);
89
+ printObject(value, indent + 1);
90
+ }
91
+ else {
92
+ process.stdout.write(`${pad}${key}: ${value}\n`);
93
+ }
94
+ }
95
+ }
96
+ //# sourceMappingURL=formatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.js","sourceRoot":"","sources":["../../src/core/formatter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import * as api from "../core/api.js";
6
+ const server = new Server({ name: "r3plica-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
7
+ const tools = [
8
+ {
9
+ name: "product_list",
10
+ description: "List R3PLICA products with optional filters (brand, category, collection)",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: {
14
+ brand: { type: "string", description: "Filter by brand name" },
15
+ category: { type: "string", description: "Filter by product category" },
16
+ collection: { type: "string", description: "Filter by collection name" },
17
+ limit: { type: "number", description: "Max results (default: 20)" },
18
+ page: { type: "number", description: "Page number (default: 1)" },
19
+ },
20
+ },
21
+ },
22
+ {
23
+ name: "product_search",
24
+ description: "Search R3PLICA products by text query",
25
+ inputSchema: {
26
+ type: "object",
27
+ properties: {
28
+ query: { type: "string", description: "Text search query" },
29
+ brand: { type: "string", description: "Filter by brand name" },
30
+ category: { type: "string", description: "Filter by product category" },
31
+ limit: { type: "number", description: "Max results (default: 20)" },
32
+ },
33
+ required: ["query"],
34
+ },
35
+ },
36
+ {
37
+ name: "product_get",
38
+ description: "Get detailed information about a specific product including parts and finish options",
39
+ inputSchema: {
40
+ type: "object",
41
+ properties: {
42
+ id: { type: "string", description: "Product mongo_id" },
43
+ },
44
+ required: ["id"],
45
+ },
46
+ },
47
+ {
48
+ name: "brand_list",
49
+ description: "List all public R3PLICA brands",
50
+ inputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ country: { type: "string", description: "Filter by country" },
54
+ limit: { type: "number", description: "Max results (default: 50)" },
55
+ },
56
+ },
57
+ },
58
+ {
59
+ name: "brand_search",
60
+ description: "Search R3PLICA brands by name",
61
+ inputSchema: {
62
+ type: "object",
63
+ properties: {
64
+ query: { type: "string", description: "Brand name search query" },
65
+ },
66
+ required: ["query"],
67
+ },
68
+ },
69
+ {
70
+ name: "product_collection_list",
71
+ description: "List product collections (furniture lines)",
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: {
75
+ brand: { type: "string", description: "Filter by brand name" },
76
+ limit: { type: "number", description: "Max results (default: 20)" },
77
+ page: { type: "number", description: "Page number (default: 1)" },
78
+ },
79
+ },
80
+ },
81
+ {
82
+ name: "product_collection_search",
83
+ description: "Search product collections by name",
84
+ inputSchema: {
85
+ type: "object",
86
+ properties: {
87
+ query: { type: "string", description: "Collection name search query" },
88
+ },
89
+ required: ["query"],
90
+ },
91
+ },
92
+ {
93
+ name: "finish_collection_list",
94
+ description: "List finish collections (material finishes/shaders)",
95
+ inputSchema: {
96
+ type: "object",
97
+ properties: {
98
+ brand: { type: "string", description: "Filter by brand name" },
99
+ category: { type: "string", description: "Filter by category (Wood, Fabric, Metal, etc.)" },
100
+ limit: { type: "number", description: "Max results (default: 20)" },
101
+ page: { type: "number", description: "Page number (default: 1)" },
102
+ },
103
+ },
104
+ },
105
+ {
106
+ name: "finish_collection_search",
107
+ description: "Search finish collections by name",
108
+ inputSchema: {
109
+ type: "object",
110
+ properties: {
111
+ query: { type: "string", description: "Finish collection name search query" },
112
+ },
113
+ required: ["query"],
114
+ },
115
+ },
116
+ {
117
+ name: "finish_collection_get",
118
+ description: "Get finish collection details with all individual finishes",
119
+ inputSchema: {
120
+ type: "object",
121
+ properties: {
122
+ id: { type: "string", description: "Finish collection mongo_id" },
123
+ },
124
+ required: ["id"],
125
+ },
126
+ },
127
+ ];
128
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
129
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
130
+ const { name, arguments: args } = request.params;
131
+ try {
132
+ let result;
133
+ switch (name) {
134
+ case "product_list":
135
+ result = await api.listProducts(args);
136
+ break;
137
+ case "product_search":
138
+ result = await api.searchProducts(args.query, args);
139
+ break;
140
+ case "product_get":
141
+ result = await api.getProduct(args.id);
142
+ break;
143
+ case "brand_list":
144
+ result = await api.listBrands(args);
145
+ break;
146
+ case "brand_search":
147
+ result = await api.searchBrands(args.query);
148
+ break;
149
+ case "product_collection_list":
150
+ result = await api.listProductCollections(args);
151
+ break;
152
+ case "product_collection_search":
153
+ result = await api.searchProductCollections(args.query);
154
+ break;
155
+ case "finish_collection_list":
156
+ result = await api.listFinishCollections(args);
157
+ break;
158
+ case "finish_collection_search":
159
+ result = await api.searchFinishCollections(args.query);
160
+ break;
161
+ case "finish_collection_get":
162
+ result = await api.getFinishCollection(args.id);
163
+ break;
164
+ default:
165
+ return {
166
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
167
+ isError: true,
168
+ };
169
+ }
170
+ return {
171
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
172
+ };
173
+ }
174
+ catch (error) {
175
+ return {
176
+ content: [{
177
+ type: "text",
178
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
179
+ }],
180
+ isError: true,
181
+ };
182
+ }
183
+ });
184
+ async function main() {
185
+ const transport = new StdioServerTransport();
186
+ await server.connect(transport);
187
+ }
188
+ main().catch((error) => {
189
+ console.error(`MCP server failed: ${error}`);
190
+ process.exit(1);
191
+ });
192
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "r3plica",
3
+ "version": "1.0.0",
4
+ "description": "R3PLICA CLI — Query the R3PLICA 3D furniture catalog",
5
+ "type": "module",
6
+ "bin": {
7
+ "r3": "dist/cli/index.js",
8
+ "r3-mcp": "dist/mcp/server.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/cli/index.js",
14
+ "start:mcp": "node dist/mcp/server.js",
15
+ "clean": "rm -rf dist",
16
+ "prepublishOnly": "npm run clean && npm run build"
17
+ },
18
+ "keywords": ["r3plica", "cli", "furniture", "3d", "design", "architecture", "mcp"],
19
+ "author": "R3PLICA S.r.l.",
20
+ "license": "Proprietary",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/R3PLICAsrl/r3-cli.git"
24
+ },
25
+ "files": ["dist/**/*", "README.md", "package.json"],
26
+ "dependencies": {
27
+ "commander": "^13.1.0",
28
+ "@modelcontextprotocol/sdk": "^1.12.1"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.8.3",
32
+ "@types/node": "^22.15.24"
33
+ },
34
+ "engines": {
35
+ "node": ">=20.0.0"
36
+ }
37
+ }