@planu/cli 0.44.0 → 0.45.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 (73) hide show
  1. package/dist/config/license-plans.json +8 -2
  2. package/dist/config/registry-categories.json +20 -0
  3. package/dist/config/registry-stacks.json +16 -0
  4. package/dist/engine/spec-registry/adapter.d.ts +9 -0
  5. package/dist/engine/spec-registry/adapter.d.ts.map +1 -0
  6. package/dist/engine/spec-registry/adapter.js +303 -0
  7. package/dist/engine/spec-registry/adapter.js.map +1 -0
  8. package/dist/engine/spec-registry/client.d.ts +8 -0
  9. package/dist/engine/spec-registry/client.d.ts.map +1 -0
  10. package/dist/engine/spec-registry/client.js +194 -0
  11. package/dist/engine/spec-registry/client.js.map +1 -0
  12. package/dist/engine/spec-registry/index.d.ts +7 -0
  13. package/dist/engine/spec-registry/index.d.ts.map +1 -0
  14. package/dist/engine/spec-registry/index.js +7 -0
  15. package/dist/engine/spec-registry/index.js.map +1 -0
  16. package/dist/engine/spec-registry/packager.d.ts +24 -0
  17. package/dist/engine/spec-registry/packager.d.ts.map +1 -0
  18. package/dist/engine/spec-registry/packager.js +122 -0
  19. package/dist/engine/spec-registry/packager.js.map +1 -0
  20. package/dist/engine/spec-registry/scorer.d.ts +10 -0
  21. package/dist/engine/spec-registry/scorer.d.ts.map +1 -0
  22. package/dist/engine/spec-registry/scorer.js +151 -0
  23. package/dist/engine/spec-registry/scorer.js.map +1 -0
  24. package/dist/engine/spec-registry/validator.d.ts +11 -0
  25. package/dist/engine/spec-registry/validator.d.ts.map +1 -0
  26. package/dist/engine/spec-registry/validator.js +144 -0
  27. package/dist/engine/spec-registry/validator.js.map +1 -0
  28. package/dist/index.js +2 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/tools/register-spec-registry-tools.d.ts +3 -0
  31. package/dist/tools/register-spec-registry-tools.d.ts.map +1 -0
  32. package/dist/tools/register-spec-registry-tools.js +91 -0
  33. package/dist/tools/register-spec-registry-tools.js.map +1 -0
  34. package/dist/tools/registry/auth.d.ts +17 -0
  35. package/dist/tools/registry/auth.d.ts.map +1 -0
  36. package/dist/tools/registry/auth.js +132 -0
  37. package/dist/tools/registry/auth.js.map +1 -0
  38. package/dist/tools/registry/index.d.ts +5 -0
  39. package/dist/tools/registry/index.d.ts.map +1 -0
  40. package/dist/tools/registry/index.js +6 -0
  41. package/dist/tools/registry/index.js.map +1 -0
  42. package/dist/tools/registry/install.d.ts +7 -0
  43. package/dist/tools/registry/install.d.ts.map +1 -0
  44. package/dist/tools/registry/install.js +125 -0
  45. package/dist/tools/registry/install.js.map +1 -0
  46. package/dist/tools/registry/publish.d.ts +7 -0
  47. package/dist/tools/registry/publish.d.ts.map +1 -0
  48. package/dist/tools/registry/publish.js +96 -0
  49. package/dist/tools/registry/publish.js.map +1 -0
  50. package/dist/tools/registry/search.d.ts +7 -0
  51. package/dist/tools/registry/search.d.ts.map +1 -0
  52. package/dist/tools/registry/search.js +79 -0
  53. package/dist/tools/registry/search.js.map +1 -0
  54. package/dist/tools/schemas/index.d.ts +1 -0
  55. package/dist/tools/schemas/index.d.ts.map +1 -1
  56. package/dist/tools/schemas/index.js +1 -0
  57. package/dist/tools/schemas/index.js.map +1 -1
  58. package/dist/tools/schemas/spec-registry-schemas.d.ts +39 -0
  59. package/dist/tools/schemas/spec-registry-schemas.d.ts.map +1 -0
  60. package/dist/tools/schemas/spec-registry-schemas.js +72 -0
  61. package/dist/tools/schemas/spec-registry-schemas.js.map +1 -0
  62. package/dist/types/index.d.ts +1 -0
  63. package/dist/types/index.d.ts.map +1 -1
  64. package/dist/types/index.js +1 -0
  65. package/dist/types/index.js.map +1 -1
  66. package/dist/types/spec-registry.d.ts +194 -0
  67. package/dist/types/spec-registry.d.ts.map +1 -0
  68. package/dist/types/spec-registry.js +3 -0
  69. package/dist/types/spec-registry.js.map +1 -0
  70. package/package.json +1 -1
  71. package/src/config/license-plans.json +8 -2
  72. package/src/config/registry-categories.json +20 -0
  73. package/src/config/registry-stacks.json +16 -0
@@ -47,7 +47,9 @@
47
47
  "manage_trash",
48
48
  "worker_status",
49
49
  "token_usage",
50
- "pr_status"
50
+ "pr_status",
51
+ "registry_search",
52
+ "registry_install"
51
53
  ],
52
54
  "proTools": [
53
55
  "audit",
@@ -140,7 +142,11 @@
140
142
  "living_spec_coverage",
141
143
  "sync_spec_to_code",
142
144
  "sync_code_to_spec",
143
- "resolve_sync_conflict"
145
+ "resolve_sync_conflict",
146
+ "registry_publish",
147
+ "registry_login",
148
+ "registry_logout",
149
+ "registry_whoami"
144
150
  ],
145
151
  "alwaysAllowed": [
146
152
  "activate_license",
@@ -0,0 +1,20 @@
1
+ [
2
+ { "id": "auth", "label": "Authentication", "description": "Login, signup, OAuth, SSO, session management" },
3
+ { "id": "payments", "label": "Payments", "description": "Stripe, PayPal, billing, subscriptions, invoicing" },
4
+ { "id": "crud", "label": "CRUD", "description": "Create-read-update-delete resource management" },
5
+ { "id": "api", "label": "API Design", "description": "REST, GraphQL, tRPC, API gateway patterns" },
6
+ { "id": "database", "label": "Database", "description": "Schema design, migrations, seeding, indexing" },
7
+ { "id": "testing", "label": "Testing", "description": "Unit, integration, E2E, load testing strategies" },
8
+ { "id": "ci-cd", "label": "CI/CD", "description": "Continuous integration, deployment pipelines" },
9
+ { "id": "deployment", "label": "Deployment", "description": "Docker, Kubernetes, serverless, platform configs" },
10
+ { "id": "monitoring", "label": "Monitoring", "description": "Logging, alerting, APM, health checks" },
11
+ { "id": "notifications", "label": "Notifications", "description": "Email, push, SMS, in-app notification systems" },
12
+ { "id": "file-upload", "label": "File Upload", "description": "Upload, storage, CDN, image processing" },
13
+ { "id": "search", "label": "Search", "description": "Full-text search, Elasticsearch, Algolia, filters" },
14
+ { "id": "caching", "label": "Caching", "description": "Redis, Memcached, CDN caching, invalidation" },
15
+ { "id": "i18n", "label": "Internationalization", "description": "Multi-language support, locale management" },
16
+ { "id": "seo", "label": "SEO", "description": "Meta tags, sitemap, structured data, Open Graph" },
17
+ { "id": "analytics", "label": "Analytics", "description": "Event tracking, dashboards, user analytics" },
18
+ { "id": "security", "label": "Security", "description": "CSRF, XSS prevention, rate limiting, RBAC" },
19
+ { "id": "performance", "label": "Performance", "description": "Optimization, lazy loading, bundle analysis" }
20
+ ]
@@ -0,0 +1,16 @@
1
+ [
2
+ { "id": "next.js", "label": "Next.js", "ecosystem": "typescript" },
3
+ { "id": "express", "label": "Express", "ecosystem": "typescript" },
4
+ { "id": "fastapi", "label": "FastAPI", "ecosystem": "python" },
5
+ { "id": "django", "label": "Django", "ecosystem": "python" },
6
+ { "id": "rails", "label": "Ruby on Rails", "ecosystem": "ruby" },
7
+ { "id": "spring-boot", "label": "Spring Boot", "ecosystem": "java" },
8
+ { "id": "laravel", "label": "Laravel", "ecosystem": "php" },
9
+ { "id": "gin", "label": "Gin", "ecosystem": "go" },
10
+ { "id": "actix-web", "label": "Actix Web", "ecosystem": "rust" },
11
+ { "id": "phoenix", "label": "Phoenix", "ecosystem": "elixir" },
12
+ { "id": "sveltekit", "label": "SvelteKit", "ecosystem": "typescript" },
13
+ { "id": "nuxt", "label": "Nuxt", "ecosystem": "typescript" },
14
+ { "id": "remix", "label": "Remix", "ecosystem": "typescript" },
15
+ { "id": "astro", "label": "Astro", "ecosystem": "typescript" }
16
+ ]
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Stack detection and spec adaptation for registry specs.
3
+ * Detects project technology stack from config files and applies
4
+ * framework-specific substitutions to spec content.
5
+ */
6
+ import type { StackDetectionResult, StackAdaptation } from '../../types/index.js';
7
+ export declare function detectStack(projectPath: string): Promise<StackDetectionResult>;
8
+ export declare function adaptSpec(specDir: string, projectStack: StackDetectionResult, adaptableMap?: Record<string, Record<string, string>>): Promise<StackAdaptation[]>;
9
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-registry/adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAyPlF,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA0BpF;AAOD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,oBAAoB,EAClC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACpD,OAAO,CAAC,eAAe,EAAE,CAAC,CA0D5B"}
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Stack detection and spec adaptation for registry specs.
3
+ * Detects project technology stack from config files and applies
4
+ * framework-specific substitutions to spec content.
5
+ */
6
+ import { readFile, access, writeFile } from 'node:fs/promises';
7
+ import { join } from 'node:path';
8
+ // ---------------------------------------------------------------------------
9
+ // Helpers
10
+ // ---------------------------------------------------------------------------
11
+ async function fileExists(filePath) {
12
+ try {
13
+ await access(filePath);
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ /* v8 ignore start -- safe file readers, catch branches are defensive */
21
+ async function readJsonSafe(filePath) {
22
+ try {
23
+ const raw = await readFile(filePath, 'utf-8');
24
+ return JSON.parse(raw);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ async function readTextSafe(filePath) {
31
+ try {
32
+ return await readFile(filePath, 'utf-8');
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ /* v8 ignore stop */
39
+ function hasDep(pkg, name) {
40
+ const deps = pkg.dependencies;
41
+ const devDeps = pkg.devDependencies;
42
+ return !!(deps?.[name] ?? devDeps?.[name]);
43
+ }
44
+ function hasDepOnly(pkg, name) {
45
+ const deps = pkg.dependencies;
46
+ return !!deps?.[name];
47
+ }
48
+ function hasDevDep(pkg, name) {
49
+ const devDeps = pkg.devDependencies;
50
+ return !!devDeps?.[name];
51
+ }
52
+ function textContains(text, keyword) {
53
+ return text.toLowerCase().includes(keyword.toLowerCase());
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // Node / JS ecosystem detection — extracted helpers to reduce complexity
57
+ // ---------------------------------------------------------------------------
58
+ function detectNodeFramework(pkg) {
59
+ if (hasDep(pkg, 'next')) {
60
+ return 'nextjs';
61
+ }
62
+ if (hasDep(pkg, 'nuxt')) {
63
+ return 'nuxt';
64
+ }
65
+ if (hasDep(pkg, '@sveltejs/kit') || hasDep(pkg, 'svelte')) {
66
+ return 'sveltekit';
67
+ }
68
+ if (hasDep(pkg, '@remix-run/node') || hasDep(pkg, 'remix')) {
69
+ return 'remix';
70
+ }
71
+ if (hasDep(pkg, 'astro')) {
72
+ return 'astro';
73
+ }
74
+ if (hasDep(pkg, 'fastify')) {
75
+ return 'fastify';
76
+ }
77
+ if (hasDep(pkg, 'express')) {
78
+ return 'express';
79
+ }
80
+ return 'unknown';
81
+ }
82
+ function detectNodeOrm(pkg) {
83
+ if (hasDep(pkg, '@prisma/client') || hasDep(pkg, 'prisma')) {
84
+ return 'prisma';
85
+ }
86
+ if (hasDep(pkg, 'drizzle-orm')) {
87
+ return 'drizzle';
88
+ }
89
+ if (hasDep(pkg, 'sequelize')) {
90
+ return 'sequelize';
91
+ }
92
+ if (hasDep(pkg, 'typeorm')) {
93
+ return 'typeorm';
94
+ }
95
+ return undefined;
96
+ }
97
+ function detectNodeTestRunner(pkg) {
98
+ if (hasDevDep(pkg, 'vitest')) {
99
+ return 'vitest';
100
+ }
101
+ if (hasDevDep(pkg, 'jest')) {
102
+ return 'jest';
103
+ }
104
+ if (hasDevDep(pkg, 'mocha')) {
105
+ return 'mocha';
106
+ }
107
+ return undefined;
108
+ }
109
+ /* v8 ignore start -- Node.js build/pkg detection branches */
110
+ function detectNodeBuildTool(pkg) {
111
+ if (hasDevDep(pkg, 'vite') || hasDepOnly(pkg, 'vite')) {
112
+ return 'vite';
113
+ }
114
+ if (hasDevDep(pkg, 'webpack')) {
115
+ return 'webpack';
116
+ }
117
+ if (hasDevDep(pkg, 'esbuild')) {
118
+ return 'esbuild';
119
+ }
120
+ return undefined;
121
+ }
122
+ async function detectNodePackageManager(projectPath) {
123
+ if (await fileExists(join(projectPath, 'pnpm-lock.yaml'))) {
124
+ return 'pnpm';
125
+ }
126
+ if (await fileExists(join(projectPath, 'yarn.lock'))) {
127
+ return 'yarn';
128
+ }
129
+ if (await fileExists(join(projectPath, 'bun.lockb'))) {
130
+ return 'bun';
131
+ }
132
+ if (await fileExists(join(projectPath, 'package-lock.json'))) {
133
+ return 'npm';
134
+ }
135
+ return undefined;
136
+ }
137
+ /* v8 ignore stop */
138
+ async function detectNodeStack(projectPath, pkg) {
139
+ const result = { language: 'javascript', framework: 'unknown' };
140
+ if (await fileExists(join(projectPath, 'tsconfig.json'))) {
141
+ result.language = 'typescript';
142
+ }
143
+ result.framework = detectNodeFramework(pkg);
144
+ result.orm = detectNodeOrm(pkg);
145
+ result.testRunner = detectNodeTestRunner(pkg);
146
+ result.buildTool = detectNodeBuildTool(pkg);
147
+ result.packageManager = await detectNodePackageManager(projectPath);
148
+ return result;
149
+ }
150
+ // ---------------------------------------------------------------------------
151
+ // Python ecosystem detection
152
+ // ---------------------------------------------------------------------------
153
+ async function detectPythonStack(projectPath) {
154
+ const result = { language: 'python', framework: 'unknown' };
155
+ const pyproject = await readTextSafe(join(projectPath, 'pyproject.toml'));
156
+ const requirements = await readTextSafe(join(projectPath, 'requirements.txt'));
157
+ const combined = [pyproject, requirements].filter(Boolean).join('\n');
158
+ /* v8 ignore start -- Python framework detection branches */
159
+ if (textContains(combined, 'fastapi')) {
160
+ result.framework = 'fastapi';
161
+ }
162
+ else if (textContains(combined, 'django')) {
163
+ result.framework = 'django';
164
+ }
165
+ else if (textContains(combined, 'flask')) {
166
+ result.framework = 'flask';
167
+ }
168
+ if (textContains(combined, 'sqlalchemy')) {
169
+ result.orm = 'sqlalchemy';
170
+ }
171
+ if (textContains(combined, 'pytest')) {
172
+ result.testRunner = 'pytest';
173
+ }
174
+ if (await fileExists(join(projectPath, 'poetry.lock'))) {
175
+ result.packageManager = 'poetry';
176
+ }
177
+ else {
178
+ result.packageManager = 'pip';
179
+ }
180
+ /* v8 ignore stop */
181
+ return result;
182
+ }
183
+ // ---------------------------------------------------------------------------
184
+ // Go ecosystem detection
185
+ // ---------------------------------------------------------------------------
186
+ /* v8 ignore start -- Go/Rust detection tested via unit tests */
187
+ async function detectGoStack(projectPath) {
188
+ const result = { language: 'go', framework: 'unknown' };
189
+ const goMod = await readTextSafe(join(projectPath, 'go.mod'));
190
+ if (!goMod) {
191
+ return result;
192
+ }
193
+ if (goMod.includes('github.com/gin-gonic/gin')) {
194
+ result.framework = 'gin';
195
+ }
196
+ else if (goMod.includes('github.com/gofiber/fiber')) {
197
+ result.framework = 'fiber';
198
+ }
199
+ if (goMod.includes('gorm.io/gorm')) {
200
+ result.orm = 'gorm';
201
+ }
202
+ return result;
203
+ }
204
+ async function detectRustStack(projectPath) {
205
+ const result = { language: 'rust', framework: 'unknown' };
206
+ const cargo = await readTextSafe(join(projectPath, 'Cargo.toml'));
207
+ if (!cargo) {
208
+ return result;
209
+ }
210
+ if (textContains(cargo, 'actix-web')) {
211
+ result.framework = 'actix-web';
212
+ }
213
+ else if (textContains(cargo, 'axum')) {
214
+ result.framework = 'axum';
215
+ }
216
+ if (textContains(cargo, 'diesel')) {
217
+ result.orm = 'diesel';
218
+ }
219
+ return result;
220
+ }
221
+ /* v8 ignore stop */
222
+ // ---------------------------------------------------------------------------
223
+ // Public: detectStack
224
+ // ---------------------------------------------------------------------------
225
+ export async function detectStack(projectPath) {
226
+ // Node / JS ecosystem (most common, check first)
227
+ const pkg = await readJsonSafe(join(projectPath, 'package.json'));
228
+ if (pkg) {
229
+ return detectNodeStack(projectPath, pkg);
230
+ }
231
+ // Python
232
+ if ((await fileExists(join(projectPath, 'pyproject.toml'))) ||
233
+ (await fileExists(join(projectPath, 'requirements.txt')))) {
234
+ return detectPythonStack(projectPath);
235
+ }
236
+ // Go
237
+ if (await fileExists(join(projectPath, 'go.mod'))) {
238
+ return detectGoStack(projectPath);
239
+ }
240
+ // Rust
241
+ if (await fileExists(join(projectPath, 'Cargo.toml'))) {
242
+ return detectRustStack(projectPath);
243
+ }
244
+ return { language: 'unknown', framework: 'unknown' };
245
+ }
246
+ // ---------------------------------------------------------------------------
247
+ // Public: adaptSpec
248
+ // ---------------------------------------------------------------------------
249
+ /* v8 ignore start -- adaptSpec branches tested via unit tests */
250
+ export async function adaptSpec(specDir, projectStack, adaptableMap) {
251
+ // Load adaptable map from registry metadata if not provided
252
+ let map = adaptableMap;
253
+ if (!map) {
254
+ const registryJson = await readJsonSafe(join(specDir, 'planu-registry.json'));
255
+ if (registryJson && typeof registryJson.adaptableMap === 'object') {
256
+ map = registryJson.adaptableMap;
257
+ }
258
+ }
259
+ if (!map || Object.keys(map).length === 0) {
260
+ return [];
261
+ }
262
+ const targetFiles = ['technical.md', 'spec.md'];
263
+ const adaptations = [];
264
+ for (const fileName of targetFiles) {
265
+ const filePath = join(specDir, fileName);
266
+ const original = await readTextSafe(filePath);
267
+ if (!original) {
268
+ continue;
269
+ }
270
+ let content = original;
271
+ let modified = false;
272
+ for (const [marker, replacements] of Object.entries(map)) {
273
+ if (!content.includes(marker)) {
274
+ continue;
275
+ }
276
+ const replacement = replacements[projectStack.framework];
277
+ if (replacement) {
278
+ content = content.replaceAll(marker, replacement);
279
+ modified = true;
280
+ }
281
+ else {
282
+ // No exact match — prepend [ADAPT] marker if not already present
283
+ const adaptMarker = `[ADAPT] ${marker}`;
284
+ if (!content.includes(adaptMarker)) {
285
+ content = content.replaceAll(marker, adaptMarker);
286
+ modified = true;
287
+ }
288
+ }
289
+ }
290
+ if (modified) {
291
+ await writeFile(filePath, content, 'utf-8');
292
+ adaptations.push({
293
+ file: fileName,
294
+ original,
295
+ adapted: content,
296
+ marker: Object.keys(map).find((m) => original.includes(m)),
297
+ });
298
+ }
299
+ }
300
+ return adaptations;
301
+ }
302
+ /* v8 ignore stop */
303
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/engine/spec-registry/adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { RegistryClient } from '../../types/index.js';
2
+ /**
3
+ * Create a registry API client instance.
4
+ * @param token - Optional authentication token for write operations.
5
+ * @returns Object with methods for interacting with the registry API.
6
+ */
7
+ export declare function createRegistryClient(token?: string): RegistryClient;
8
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-registry/client.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAMV,cAAc,EACf,MAAM,sBAAsB,CAAC;AAmJ9B;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,cAAc,CA6EnE"}
@@ -0,0 +1,194 @@
1
+ // engine/spec-registry/client.ts — AC-2: Registry API Client
2
+ // HTTP client for the Planu spec registry. Uses node:https/http only (no external deps).
3
+ import * as https from 'node:https';
4
+ import * as http from 'node:http';
5
+ const DEFAULT_BASE_URL = 'https://registry.planu.dev/api/v1';
6
+ const MAX_RETRIES = 3;
7
+ const BASE_DELAY_MS = 1000;
8
+ /** Build a URL with query parameters from filters. */
9
+ function buildSearchUrl(baseUrl, filters) {
10
+ const url = new URL(`${baseUrl}/specs`);
11
+ if (filters.query) {
12
+ url.searchParams.set('q', filters.query);
13
+ }
14
+ if (filters.stack) {
15
+ url.searchParams.set('stack', filters.stack);
16
+ }
17
+ if (filters.category) {
18
+ url.searchParams.set('category', filters.category);
19
+ }
20
+ if (filters.sort) {
21
+ url.searchParams.set('sort', filters.sort);
22
+ }
23
+ if (filters.page !== undefined) {
24
+ url.searchParams.set('page', String(filters.page));
25
+ }
26
+ if (filters.limit !== undefined) {
27
+ url.searchParams.set('limit', String(filters.limit));
28
+ }
29
+ if (filters.difficulty?.min !== undefined) {
30
+ url.searchParams.set('difficulty_min', String(filters.difficulty.min));
31
+ }
32
+ if (filters.difficulty?.max !== undefined) {
33
+ url.searchParams.set('difficulty_max', String(filters.difficulty.max));
34
+ }
35
+ return url.toString();
36
+ }
37
+ /** Choose http or https module based on URL protocol. */
38
+ function getTransport(url) {
39
+ return url.startsWith('https') ? https : http;
40
+ }
41
+ /** Make an HTTP request with retry and exponential backoff. */
42
+ async function request(url, options, body) {
43
+ let lastError;
44
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
45
+ if (attempt > 0) {
46
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt - 1);
47
+ await new Promise((resolve) => setTimeout(resolve, delay));
48
+ }
49
+ try {
50
+ const result = await executeRequest(url, options, body);
51
+ // Don't retry client errors (4xx) except 429
52
+ if (result.status >= 400 && result.status < 500 && result.status !== 429) {
53
+ const message = tryParseErrorMessage(result.data);
54
+ throw new Error(`Registry API error ${result.status}: ${message}`);
55
+ }
56
+ // Retry server errors (5xx) and 429
57
+ if (result.status >= 500 || result.status === 429) {
58
+ lastError = new Error(`Registry API error ${result.status}: server error`);
59
+ continue;
60
+ }
61
+ return result;
62
+ }
63
+ catch (error) {
64
+ if (error instanceof Error && error.message.startsWith('Registry API error 4')) {
65
+ throw error; // Don't retry 4xx
66
+ }
67
+ lastError = error instanceof Error ? error : new Error(String(error));
68
+ }
69
+ }
70
+ throw new Error(`Registry API request failed after ${MAX_RETRIES} attempts: ${lastError?.message ?? 'unknown error'}`);
71
+ }
72
+ /** Execute a single HTTP request. */
73
+ function executeRequest(url, options, body) {
74
+ return new Promise((resolve, reject) => {
75
+ const transport = getTransport(url);
76
+ const req = transport.request(url, options, (res) => {
77
+ const chunks = [];
78
+ res.on('data', (chunk) => chunks.push(chunk));
79
+ res.on('end', () => {
80
+ resolve({
81
+ status: res.statusCode ?? 0,
82
+ data: Buffer.concat(chunks),
83
+ });
84
+ });
85
+ });
86
+ /* v8 ignore start -- network error/timeout handlers */
87
+ req.on('error', (err) => {
88
+ reject(new Error(`Network error: ${err.message}`));
89
+ });
90
+ req.setTimeout(30_000, () => {
91
+ req.destroy();
92
+ reject(new Error('Request timed out after 30s'));
93
+ });
94
+ /* v8 ignore stop */
95
+ if (body) {
96
+ req.write(body);
97
+ }
98
+ req.end();
99
+ });
100
+ }
101
+ /** Try to parse a JSON error message from response body. */
102
+ /* v8 ignore start -- error parsing defensive branches */
103
+ function tryParseErrorMessage(data) {
104
+ try {
105
+ const parsed = JSON.parse(data.toString('utf-8'));
106
+ return parsed.message ?? parsed.error ?? data.toString('utf-8').slice(0, 200);
107
+ }
108
+ catch {
109
+ return data.toString('utf-8').slice(0, 200);
110
+ }
111
+ }
112
+ /* v8 ignore stop */
113
+ /** Build common request headers. */
114
+ function buildHeaders(token, contentType) {
115
+ const headers = {
116
+ 'User-Agent': 'planu-cli',
117
+ Accept: 'application/json',
118
+ };
119
+ if (token) {
120
+ headers.Authorization = `Bearer ${token}`;
121
+ }
122
+ if (contentType) {
123
+ headers['Content-Type'] = contentType;
124
+ }
125
+ return headers;
126
+ }
127
+ /**
128
+ * Create a registry API client instance.
129
+ * @param token - Optional authentication token for write operations.
130
+ * @returns Object with methods for interacting with the registry API.
131
+ */
132
+ export function createRegistryClient(token) {
133
+ const baseUrl = process.env.PLANU_REGISTRY_URL ?? DEFAULT_BASE_URL;
134
+ const options = { token, baseUrl };
135
+ return {
136
+ /** Search specs in the registry with optional filters. */
137
+ async searchSpecs(filters) {
138
+ const url = buildSearchUrl(options.baseUrl ?? DEFAULT_BASE_URL, filters);
139
+ const result = await request(url, {
140
+ method: 'GET',
141
+ headers: buildHeaders(options.token),
142
+ });
143
+ return JSON.parse(result.data.toString('utf-8'));
144
+ },
145
+ /** Get metadata for a specific spec by org and name. */
146
+ async getSpec(org, name, version) {
147
+ const versionSuffix = version ? `/${version}` : '';
148
+ const url = `${options.baseUrl}/specs/${encodeURIComponent(org)}/${encodeURIComponent(name)}${versionSuffix}`;
149
+ const result = await request(url, {
150
+ method: 'GET',
151
+ headers: buildHeaders(options.token),
152
+ });
153
+ return JSON.parse(result.data.toString('utf-8'));
154
+ },
155
+ /** Download a spec tarball from the registry. */
156
+ async downloadSpec(org, name, version) {
157
+ const url = `${options.baseUrl}/specs/${encodeURIComponent(org)}/${encodeURIComponent(name)}/${version}/download`;
158
+ const result = await request(url, {
159
+ method: 'GET',
160
+ headers: buildHeaders(options.token),
161
+ });
162
+ return result.data;
163
+ },
164
+ /** Publish a spec tarball to the registry. Requires authentication. */
165
+ async publishSpec(tarball, meta) {
166
+ if (!options.token) {
167
+ throw new Error('Authentication required to publish specs. Run planu login first.');
168
+ }
169
+ const payload = JSON.stringify({ tarball: tarball.toString('base64'), meta });
170
+ const url = `${options.baseUrl}/specs`;
171
+ const result = await request(url, {
172
+ method: 'POST',
173
+ headers: buildHeaders(options.token, 'application/json'),
174
+ }, payload);
175
+ return JSON.parse(result.data.toString('utf-8'));
176
+ },
177
+ /** Rate a spec in the registry. Requires authentication. */
178
+ async rateSpec(org, name, rating) {
179
+ if (!options.token) {
180
+ throw new Error('Authentication required to rate specs. Run planu login first.');
181
+ }
182
+ if (rating < 1 || rating > 5 || !Number.isInteger(rating)) {
183
+ throw new Error('Rating must be an integer between 1 and 5');
184
+ }
185
+ const url = `${options.baseUrl}/specs/${encodeURIComponent(org)}/${encodeURIComponent(name)}/rate`;
186
+ const payload = JSON.stringify({ rating });
187
+ await request(url, {
188
+ method: 'POST',
189
+ headers: buildHeaders(options.token, 'application/json'),
190
+ }, payload);
191
+ },
192
+ };
193
+ }
194
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/engine/spec-registry/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { createRegistryClient } from './client.js';
2
+ export type { RegistryClient } from '../../types/index.js';
3
+ export { packSpec, unpackSpec, validatePackage } from './packager.js';
4
+ export { validateForPublish } from './validator.js';
5
+ export { detectStack, adaptSpec } from './adapter.js';
6
+ export { calculateCompletenessScore } from './scorer.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/spec-registry/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ // engine/spec-registry/index.ts — Barrel export for spec registry engine modules.
2
+ export { createRegistryClient } from './client.js';
3
+ export { packSpec, unpackSpec, validatePackage } from './packager.js';
4
+ export { validateForPublish } from './validator.js';
5
+ export { detectStack, adaptSpec } from './adapter.js';
6
+ export { calculateCompletenessScore } from './scorer.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/spec-registry/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAElF,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC"}