@decantr/cli 1.7.25 → 1.7.26

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/bin.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-7FXMRAC3.js";
2
+ import "./chunk-RAAUNHXD.js";
3
3
  import "./chunk-3K6HWLD5.js";
4
4
  import "./chunk-QRQCPD3C.js";
@@ -40,29 +40,63 @@ import {
40
40
  } from "@decantr/verifier";
41
41
 
42
42
  // src/auth.ts
43
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
43
+ import {
44
+ chmodSync,
45
+ existsSync,
46
+ mkdirSync,
47
+ readFileSync,
48
+ rmSync,
49
+ statSync,
50
+ writeFileSync
51
+ } from "fs";
44
52
  import { homedir } from "os";
45
53
  import { join } from "path";
46
- var CONFIG_DIR = join(homedir(), ".config", "decantr");
47
- var AUTH_FILE = join(CONFIG_DIR, "auth.json");
54
+ var CONFIG_DIR_MODE = 448;
55
+ var AUTH_FILE_MODE = 384;
56
+ function getConfigDir() {
57
+ return process.env.DECANTR_CONFIG_DIR || join(homedir(), ".config", "decantr");
58
+ }
59
+ function getAuthFile() {
60
+ return join(getConfigDir(), "auth.json");
61
+ }
62
+ function chmodIfNeeded(path, mode) {
63
+ try {
64
+ if ((statSync(path).mode & 511) !== mode) {
65
+ chmodSync(path, mode);
66
+ }
67
+ } catch {
68
+ }
69
+ }
70
+ function ensureConfigDir() {
71
+ const configDir = getConfigDir();
72
+ mkdirSync(configDir, { recursive: true, mode: CONFIG_DIR_MODE });
73
+ chmodIfNeeded(configDir, CONFIG_DIR_MODE);
74
+ }
48
75
  function getCredentials() {
49
- if (!existsSync(AUTH_FILE)) return null;
76
+ const authFile = getAuthFile();
77
+ if (!existsSync(authFile)) return null;
50
78
  try {
51
- return JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
79
+ chmodIfNeeded(authFile, AUTH_FILE_MODE);
80
+ return JSON.parse(readFileSync(authFile, "utf-8"));
52
81
  } catch {
53
82
  return null;
54
83
  }
55
84
  }
56
85
  function saveCredentials(creds) {
57
- mkdirSync(CONFIG_DIR, { recursive: true });
58
- writeFileSync(AUTH_FILE, JSON.stringify(creds, null, 2));
86
+ ensureConfigDir();
87
+ const authFile = getAuthFile();
88
+ writeFileSync(authFile, JSON.stringify(creds, null, 2), { mode: AUTH_FILE_MODE });
89
+ chmodIfNeeded(authFile, AUTH_FILE_MODE);
59
90
  }
60
91
  function clearCredentials() {
61
- if (existsSync(AUTH_FILE)) {
62
- rmSync(AUTH_FILE);
92
+ const authFile = getAuthFile();
93
+ if (existsSync(authFile)) {
94
+ rmSync(authFile);
63
95
  }
64
96
  }
65
97
  function getApiKeyOrToken() {
98
+ const envKey = process.env.DECANTR_API_KEY?.trim();
99
+ if (envKey) return envKey;
66
100
  const creds = getCredentials();
67
101
  if (!creds) return null;
68
102
  return creds.api_key || creds.access_token || null;
@@ -341,7 +375,7 @@ import { existsSync as existsSync12, mkdirSync as mkdirSync3, writeFileSync as w
341
375
  import { join as join12 } from "path";
342
376
 
343
377
  // src/analyzers/components.ts
344
- import { existsSync as existsSync4, readdirSync, statSync } from "fs";
378
+ import { existsSync as existsSync4, readdirSync, statSync as statSync2 } from "fs";
345
379
  import { join as join4 } from "path";
346
380
  var PAGE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".ts", ".jsx", ".js"]);
347
381
  var ROOT_COMPONENT_CANDIDATES = [
@@ -366,7 +400,7 @@ function countFilesRecursive(dir, extensions) {
366
400
  if (entry.startsWith(".") || entry === "node_modules") continue;
367
401
  const fullPath = join4(dir, entry);
368
402
  try {
369
- const stat = statSync(fullPath);
403
+ const stat = statSync2(fullPath);
370
404
  if (stat.isDirectory()) {
371
405
  count += countFilesRecursive(fullPath, extensions);
372
406
  } else if (stat.isFile()) {
@@ -392,7 +426,7 @@ function countPageFiles(dir) {
392
426
  if (entry.startsWith(".") || entry === "node_modules") continue;
393
427
  const fullPath = join4(dir, entry);
394
428
  try {
395
- const stat = statSync(fullPath);
429
+ const stat = statSync2(fullPath);
396
430
  if (stat.isDirectory()) {
397
431
  count += countPageFiles(fullPath);
398
432
  } else if (stat.isFile()) {
@@ -632,7 +666,7 @@ function scanDependencies(projectRoot) {
632
666
  }
633
667
 
634
668
  // src/analyzers/features.ts
635
- import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
669
+ import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
636
670
  import { join as join6 } from "path";
637
671
  var FEATURE_PATTERNS = {
638
672
  auth: [
@@ -690,7 +724,7 @@ function collectPaths(dir, baseDir, depth = 0) {
690
724
  const relPath = fullPath.slice(baseDir.length + 1);
691
725
  paths.push(relPath);
692
726
  try {
693
- if (statSync2(fullPath).isDirectory()) {
727
+ if (statSync3(fullPath).isDirectory()) {
694
728
  paths.push(...collectPaths(fullPath, baseDir, depth + 1));
695
729
  }
696
730
  } catch {
@@ -861,7 +895,7 @@ function scanLayout(projectRoot) {
861
895
  }
862
896
 
863
897
  // src/analyzers/routes.ts
864
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
898
+ import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
865
899
  import { join as join8, relative } from "path";
866
900
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "api", "_app", "_document"]);
867
901
  function shouldSkipDir(name) {
@@ -910,7 +944,7 @@ function walkAppDir(dir, baseDir, segments) {
910
944
  if (shouldSkipDir(entry)) continue;
911
945
  const fullPath = join8(dir, entry);
912
946
  try {
913
- if (!statSync3(fullPath).isDirectory()) continue;
947
+ if (!statSync4(fullPath).isDirectory()) continue;
914
948
  } catch {
915
949
  continue;
916
950
  }
@@ -932,7 +966,7 @@ function walkPagesDir(dir, baseDir, segments) {
932
966
  if (shouldSkipDir(entry)) continue;
933
967
  const fullPath = join8(dir, entry);
934
968
  try {
935
- const stat = statSync3(fullPath);
969
+ const stat = statSync4(fullPath);
936
970
  if (stat.isDirectory()) {
937
971
  const routeSegment = segmentToRoute(entry);
938
972
  const nextSegments = routeSegment === null ? [...segments] : [...segments, routeSegment];
@@ -968,7 +1002,7 @@ function collectRouteCandidateFiles(dir, files, depth = 0) {
968
1002
  if (entry.startsWith(".") || entry === "node_modules") continue;
969
1003
  const fullPath = join8(dir, entry);
970
1004
  try {
971
- const stat = statSync3(fullPath);
1005
+ const stat = statSync4(fullPath);
972
1006
  if (stat.isDirectory()) {
973
1007
  collectRouteCandidateFiles(fullPath, files, depth + 1);
974
1008
  } else if (stat.isFile()) {
@@ -2668,7 +2702,7 @@ async function cmdMigrate(projectRoot = process.cwd()) {
2668
2702
  }
2669
2703
 
2670
2704
  // src/commands/new-project.ts
2671
- import { execSync } from "child_process";
2705
+ import { spawnSync } from "child_process";
2672
2706
  import { existsSync as existsSync18, mkdirSync as mkdirSync8 } from "fs";
2673
2707
  import { join as join19, resolve as resolve2 } from "path";
2674
2708
  import { fileURLToPath } from "url";
@@ -3118,6 +3152,72 @@ function dim2(text) {
3118
3152
  function cyan2(text) {
3119
3153
  return `${CYAN3}${text}${RESET6}`;
3120
3154
  }
3155
+ function validatePassThroughFlagValue(flag, value) {
3156
+ if (value.length === 0) {
3157
+ throw new Error(`--${flag} cannot be empty.`);
3158
+ }
3159
+ if (value.length > 512) {
3160
+ throw new Error(`--${flag} is too long.`);
3161
+ }
3162
+ if (/[\u0000-\u001f\u007f]/.test(value)) {
3163
+ throw new Error(`--${flag} contains unsupported control characters.`);
3164
+ }
3165
+ return value;
3166
+ }
3167
+ function pushPassThroughFlag(flags, flag, value) {
3168
+ if (value == null) return;
3169
+ flags.push(`--${flag}=${validatePassThroughFlagValue(flag, value)}`);
3170
+ }
3171
+ function buildNewProjectInitArgs(options, inferredAdoption) {
3172
+ const initFlags = [
3173
+ "--yes",
3174
+ "--workflow=greenfield",
3175
+ `--adoption=${validatePassThroughFlagValue("mode", inferredAdoption)}`
3176
+ ];
3177
+ pushPassThroughFlag(initFlags, "blueprint", options.blueprint);
3178
+ pushPassThroughFlag(initFlags, "archetype", options.archetype);
3179
+ pushPassThroughFlag(initFlags, "theme", options.theme);
3180
+ pushPassThroughFlag(initFlags, "mode", options.mode);
3181
+ pushPassThroughFlag(initFlags, "shape", options.shape);
3182
+ pushPassThroughFlag(initFlags, "target", options.target);
3183
+ if (options.offline) initFlags.push("--offline");
3184
+ pushPassThroughFlag(initFlags, "registry", options.registry);
3185
+ pushPassThroughFlag(initFlags, "assistant-bridge", options.assistantBridge);
3186
+ return initFlags;
3187
+ }
3188
+ function commandForPlatform(command) {
3189
+ if (process.platform !== "win32") {
3190
+ return command;
3191
+ }
3192
+ return /^(?:npm|pnpm|yarn|bun|npx)$/.test(command) ? `${command}.cmd` : command;
3193
+ }
3194
+ function runArgvCommand(command, args, cwd) {
3195
+ const result = spawnSync(commandForPlatform(command), args, {
3196
+ cwd,
3197
+ stdio: "inherit",
3198
+ shell: false
3199
+ });
3200
+ if (result.error) {
3201
+ throw result.error;
3202
+ }
3203
+ if (result.status !== 0) {
3204
+ throw new Error(`Command failed: ${command} ${args.join(" ")}`);
3205
+ }
3206
+ }
3207
+ function resolveInitCommand(initFlags) {
3208
+ const bundledCliEntrypoint = fileURLToPath(new URL("./bin.js", import.meta.url));
3209
+ const cliEntrypoint = existsSync18(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync18(process.argv[1]) ? process.argv[1] : null;
3210
+ if (cliEntrypoint) {
3211
+ return {
3212
+ command: process.execPath,
3213
+ args: [cliEntrypoint, "init", ...initFlags]
3214
+ };
3215
+ }
3216
+ return {
3217
+ command: "npx",
3218
+ args: ["decantr", "init", ...initFlags]
3219
+ };
3220
+ }
3121
3221
  async function cmdNewProject(projectName, options) {
3122
3222
  const workspaceRoot = process.cwd();
3123
3223
  const projectDir = resolve2(workspaceRoot, projectName);
@@ -3138,6 +3238,14 @@ async function cmdNewProject(projectName, options) {
3138
3238
  process.exitCode = 1;
3139
3239
  return;
3140
3240
  }
3241
+ let initFlags;
3242
+ try {
3243
+ initFlags = buildNewProjectInitArgs(options, inferredAdoption);
3244
+ } catch (err) {
3245
+ console.error(error2(err.message));
3246
+ process.exitCode = 1;
3247
+ return;
3248
+ }
3141
3249
  console.log(heading(`Creating ${projectName}...`));
3142
3250
  mkdirSync8(projectDir, { recursive: true });
3143
3251
  console.log(dim2(` Created ${projectName}/`));
@@ -3165,7 +3273,7 @@ async function cmdNewProject(projectName, options) {
3165
3273
  if (shouldBootstrapRuntime) {
3166
3274
  console.log(heading("Installing dependencies..."));
3167
3275
  try {
3168
- execSync(`${packageManager} install`, { cwd: projectDir, stdio: "inherit" });
3276
+ runArgvCommand(packageManager, ["install"], projectDir);
3169
3277
  } catch {
3170
3278
  console.log(
3171
3279
  `
@@ -3202,21 +3310,9 @@ ${YELLOW4}Dependency install failed. Run \`${packageManager} install\` manually.
3202
3310
  return;
3203
3311
  }
3204
3312
  console.log(heading("Initializing Decantr..."));
3205
- const initFlags = ["--yes", "--workflow=greenfield", `--adoption=${inferredAdoption}`];
3206
- if (options.blueprint) initFlags.push(`--blueprint=${options.blueprint}`);
3207
- if (options.archetype) initFlags.push(`--archetype=${options.archetype}`);
3208
- if (options.theme) initFlags.push(`--theme=${options.theme}`);
3209
- if (options.mode) initFlags.push(`--mode=${options.mode}`);
3210
- if (options.shape) initFlags.push(`--shape=${options.shape}`);
3211
- if (options.target) initFlags.push(`--target=${options.target}`);
3212
- if (options.offline) initFlags.push("--offline");
3213
- if (options.registry) initFlags.push(`--registry=${options.registry}`);
3214
- if (options.assistantBridge) initFlags.push(`--assistant-bridge=${options.assistantBridge}`);
3215
3313
  try {
3216
- const bundledCliEntrypoint = fileURLToPath(new URL("./bin.js", import.meta.url));
3217
- const cliEntrypoint = existsSync18(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync18(process.argv[1]) ? process.argv[1] : null;
3218
- const cliPath = cliEntrypoint ? `"${process.execPath}" "${cliEntrypoint}"` : "npx decantr";
3219
- execSync(`${cliPath} init ${initFlags.join(" ")}`, { cwd: projectDir, stdio: "inherit" });
3314
+ const initCommand = resolveInitCommand(initFlags);
3315
+ runArgvCommand(initCommand.command, initCommand.args, projectDir);
3220
3316
  if (shouldBootstrapRuntime && bootstrapAdapter) {
3221
3317
  bootstrapAdapter.writeProjectFiles(projectDir, title, detectRoutingMode(projectDir));
3222
3318
  }
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import "./chunk-7FXMRAC3.js";
1
+ import "./chunk-RAAUNHXD.js";
2
2
  import "./chunk-3K6HWLD5.js";
3
3
  import "./chunk-QRQCPD3C.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/cli",
3
- "version": "1.7.25",
3
+ "version": "1.7.26",
4
4
  "description": "Decantr CLI — scaffold, audit, and maintain Decantr projects from the terminal",
5
5
  "author": "Decantr AI",
6
6
  "license": "MIT",
@@ -23,21 +23,25 @@
23
23
  "src/templates",
24
24
  "src/bundled"
25
25
  ],
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
26
29
  "publishConfig": {
27
30
  "access": "public"
28
31
  },
29
- "dependencies": {
30
- "@decantr/core": "1.0.5",
31
- "@decantr/essence-spec": "1.0.5",
32
- "@decantr/registry": "1.0.3",
33
- "@decantr/verifier": "1.0.4"
34
- },
35
32
  "scripts": {
36
33
  "build": "tsup",
37
34
  "certify:blueprints": "pnpm build && node scripts/certify-blueprints.mjs",
38
35
  "certify:workflows": "pnpm build && node scripts/certify-workflows.mjs",
39
36
  "test": "vitest run",
40
37
  "test:watch": "vitest",
38
+ "prepublishOnly": "pnpm build",
41
39
  "preversion": "pnpm build && pnpm test"
40
+ },
41
+ "dependencies": {
42
+ "@decantr/core": "workspace:*",
43
+ "@decantr/essence-spec": "workspace:*",
44
+ "@decantr/registry": "workspace:*",
45
+ "@decantr/verifier": "workspace:*"
42
46
  }
43
- }
47
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Decantr AI
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.