@decantr/cli 1.0.0 → 1.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.
- package/dist/chunk-K6MIDPQH.js +205 -0
- package/dist/chunk-PDX44BCA.js +0 -0
- package/dist/heal-2OPN63OT.js +0 -0
- package/dist/index.js +682 -224
- package/dist/{upgrade-FWICWIQW.js → upgrade-KRFCKUMR.js} +1 -1
- package/package.json +6 -6
- package/src/templates/DECANTR.md.template +53 -53
- package/LICENSE +0 -21
- package/dist/chunk-PWTUBGGJ.js +0 -359
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/registry.ts
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { RegistryAPIClient } from "@decantr/registry";
|
|
7
|
+
var DEFAULT_API_URL = "https://api.decantr.ai/v1";
|
|
8
|
+
var ALL_CONTENT_TYPES = ["themes", "patterns", "recipes", "blueprints", "archetypes", "shells"];
|
|
9
|
+
function loadFromCache(cacheDir, contentType, id, namespace) {
|
|
10
|
+
const nsDir = namespace ? join(cacheDir, namespace) : cacheDir;
|
|
11
|
+
const cachePath = id ? join(nsDir, contentType, `${id}.json`) : join(nsDir, contentType, "index.json");
|
|
12
|
+
if (!existsSync(cachePath)) return null;
|
|
13
|
+
try {
|
|
14
|
+
const data = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
15
|
+
return { data, source: { type: "cache" } };
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function saveToCache(cacheDir, contentType, id, data, namespace = "@official") {
|
|
21
|
+
const dir = join(cacheDir, namespace, contentType);
|
|
22
|
+
mkdirSync(dir, { recursive: true });
|
|
23
|
+
const cachePath = id ? join(dir, `${id}.json`) : join(dir, "index.json");
|
|
24
|
+
writeFileSync(cachePath, JSON.stringify(data, null, 2));
|
|
25
|
+
}
|
|
26
|
+
var RegistryClient = class {
|
|
27
|
+
cacheDir;
|
|
28
|
+
apiUrl;
|
|
29
|
+
offline;
|
|
30
|
+
projectRoot;
|
|
31
|
+
apiClient;
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.projectRoot = options.projectRoot || process.cwd();
|
|
34
|
+
this.cacheDir = options.cacheDir || join(this.projectRoot, ".decantr", "cache");
|
|
35
|
+
this.apiUrl = options.apiUrl || DEFAULT_API_URL;
|
|
36
|
+
this.offline = options.offline || false;
|
|
37
|
+
this.apiClient = new RegistryAPIClient({
|
|
38
|
+
baseUrl: this.apiUrl,
|
|
39
|
+
apiKey: options.apiKey
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load content from .decantr/custom/{contentType}/{id}.json
|
|
44
|
+
* Works for ALL content types, not just themes.
|
|
45
|
+
*/
|
|
46
|
+
loadCustomContent(contentType, id) {
|
|
47
|
+
const customPath = join(
|
|
48
|
+
this.projectRoot,
|
|
49
|
+
".decantr",
|
|
50
|
+
"custom",
|
|
51
|
+
contentType,
|
|
52
|
+
`${id}.json`
|
|
53
|
+
);
|
|
54
|
+
if (!existsSync(customPath)) return null;
|
|
55
|
+
try {
|
|
56
|
+
const data = JSON.parse(readFileSync(customPath, "utf-8"));
|
|
57
|
+
return { data, source: { type: "custom", path: customPath } };
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* List all custom content of a given type from .decantr/custom/{type}/
|
|
64
|
+
*/
|
|
65
|
+
listCustomContent(contentType) {
|
|
66
|
+
const dir = join(this.projectRoot, ".decantr", "custom", contentType);
|
|
67
|
+
if (!existsSync(dir)) return [];
|
|
68
|
+
try {
|
|
69
|
+
return readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => {
|
|
70
|
+
const data = JSON.parse(readFileSync(join(dir, f), "utf-8"));
|
|
71
|
+
return { id: data.id || f.replace(".json", ""), ...data };
|
|
72
|
+
});
|
|
73
|
+
} catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Unified fetch for a content list.
|
|
79
|
+
* Resolution: API -> Cache. Custom items are merged into the list.
|
|
80
|
+
*/
|
|
81
|
+
async fetchContentList(contentType, namespace) {
|
|
82
|
+
let apiItems = [];
|
|
83
|
+
let source = { type: "cache" };
|
|
84
|
+
if (!this.offline) {
|
|
85
|
+
try {
|
|
86
|
+
const apiResult = await this.apiClient.listContent(contentType, { namespace });
|
|
87
|
+
apiItems = apiResult.items;
|
|
88
|
+
source = { type: "api", url: this.apiUrl };
|
|
89
|
+
saveToCache(this.cacheDir, contentType, null, apiResult, namespace || "@official");
|
|
90
|
+
} catch {
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (apiItems.length === 0) {
|
|
94
|
+
const cacheResult = loadFromCache(
|
|
95
|
+
this.cacheDir,
|
|
96
|
+
contentType,
|
|
97
|
+
void 0,
|
|
98
|
+
namespace
|
|
99
|
+
);
|
|
100
|
+
if (cacheResult) {
|
|
101
|
+
apiItems = cacheResult.data.items;
|
|
102
|
+
source = { type: "cache" };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const customItems = this.listCustomContent(contentType);
|
|
106
|
+
const allItems = [...customItems, ...apiItems];
|
|
107
|
+
return {
|
|
108
|
+
data: { items: allItems, total: allItems.length },
|
|
109
|
+
source
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Unified fetch for a single content item.
|
|
114
|
+
* Resolution: Custom -> API -> Cache
|
|
115
|
+
*/
|
|
116
|
+
async fetchContentItem(contentType, id, namespace = "@official") {
|
|
117
|
+
const customId = id.startsWith("custom:") ? id.slice(7) : id;
|
|
118
|
+
const customResult = this.loadCustomContent(contentType, customId);
|
|
119
|
+
if (customResult) return customResult;
|
|
120
|
+
if (id.startsWith("custom:")) return null;
|
|
121
|
+
if (!this.offline) {
|
|
122
|
+
try {
|
|
123
|
+
const data = await this.apiClient.getContent(contentType, namespace, id);
|
|
124
|
+
saveToCache(this.cacheDir, contentType, id, data, namespace);
|
|
125
|
+
return { data, source: { type: "api", url: this.apiUrl } };
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return loadFromCache(this.cacheDir, contentType, id, namespace);
|
|
130
|
+
}
|
|
131
|
+
// ── Convenience methods (delegate to unified fetch) ──
|
|
132
|
+
async fetchArchetypes() {
|
|
133
|
+
return this.fetchContentList("archetypes");
|
|
134
|
+
}
|
|
135
|
+
async fetchArchetype(id) {
|
|
136
|
+
return this.fetchContentItem("archetypes", id);
|
|
137
|
+
}
|
|
138
|
+
async fetchBlueprints() {
|
|
139
|
+
return this.fetchContentList("blueprints");
|
|
140
|
+
}
|
|
141
|
+
async fetchBlueprint(id) {
|
|
142
|
+
return this.fetchContentItem("blueprints", id);
|
|
143
|
+
}
|
|
144
|
+
async fetchThemes() {
|
|
145
|
+
return this.fetchContentList("themes");
|
|
146
|
+
}
|
|
147
|
+
async fetchTheme(id) {
|
|
148
|
+
return this.fetchContentItem("themes", id);
|
|
149
|
+
}
|
|
150
|
+
async fetchPatterns() {
|
|
151
|
+
return this.fetchContentList("patterns");
|
|
152
|
+
}
|
|
153
|
+
async fetchPattern(id) {
|
|
154
|
+
return this.fetchContentItem("patterns", id);
|
|
155
|
+
}
|
|
156
|
+
async fetchShells() {
|
|
157
|
+
return this.fetchContentList("shells");
|
|
158
|
+
}
|
|
159
|
+
async fetchShell(id) {
|
|
160
|
+
return this.fetchContentItem("shells", id);
|
|
161
|
+
}
|
|
162
|
+
async fetchRecipes() {
|
|
163
|
+
return this.fetchContentList("recipes");
|
|
164
|
+
}
|
|
165
|
+
async fetchRecipe(id) {
|
|
166
|
+
return this.fetchContentItem("recipes", id);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if API is available.
|
|
170
|
+
*/
|
|
171
|
+
async checkApiAvailability() {
|
|
172
|
+
if (this.offline) return false;
|
|
173
|
+
return this.apiClient.checkHealth();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
async function syncRegistry(cacheDir, apiUrl = DEFAULT_API_URL) {
|
|
177
|
+
const apiClient = new RegistryAPIClient({ baseUrl: apiUrl });
|
|
178
|
+
const synced = [];
|
|
179
|
+
const failed = [];
|
|
180
|
+
const healthy = await apiClient.checkHealth();
|
|
181
|
+
if (!healthy) {
|
|
182
|
+
return { synced: [], failed: ["API unavailable"] };
|
|
183
|
+
}
|
|
184
|
+
for (const type of ALL_CONTENT_TYPES) {
|
|
185
|
+
try {
|
|
186
|
+
const result = await apiClient.listContent(type, { namespace: "@official" });
|
|
187
|
+
saveToCache(cacheDir, type, null, result, "@official");
|
|
188
|
+
for (const item of result.items) {
|
|
189
|
+
const id = item.id || item.slug;
|
|
190
|
+
if (id) {
|
|
191
|
+
saveToCache(cacheDir, type, id, item, "@official");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
synced.push(type);
|
|
195
|
+
} catch {
|
|
196
|
+
failed.push(type);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return { synced, failed };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export {
|
|
203
|
+
RegistryClient,
|
|
204
|
+
syncRegistry
|
|
205
|
+
};
|
package/dist/chunk-PDX44BCA.js
CHANGED
|
File without changes
|
package/dist/heal-2OPN63OT.js
CHANGED
|
File without changes
|