sdd-cli 0.1.22 → 0.1.24
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 +73 -86
- package/dist/cli.js +116 -8
- package/dist/commands/ai-autopilot.d.ts +19 -0
- package/dist/commands/ai-autopilot.js +1292 -0
- package/dist/commands/ai-exec.js +14 -3
- package/dist/commands/ai-status.js +17 -5
- package/dist/commands/app-lifecycle.d.ts +25 -0
- package/dist/commands/app-lifecycle.js +505 -0
- package/dist/commands/hello.js +53 -1
- package/dist/commands/import-azure.d.ts +1 -0
- package/dist/commands/import-azure.js +113 -0
- package/dist/commands/suite.d.ts +1 -0
- package/dist/commands/suite.js +82 -0
- package/dist/config/index.d.ts +23 -0
- package/dist/config/index.js +209 -0
- package/dist/context/flags.d.ts +2 -0
- package/dist/context/flags.js +9 -1
- package/dist/providers/codex.d.ts +3 -5
- package/dist/providers/codex.js +34 -2
- package/dist/providers/gemini.d.ts +5 -0
- package/dist/providers/gemini.js +82 -0
- package/dist/providers/index.d.ts +16 -0
- package/dist/providers/index.js +90 -0
- package/dist/providers/types.d.ts +13 -0
- package/dist/providers/types.js +2 -0
- package/dist/router/intent.js +34 -4
- package/dist/workspace/index.js +6 -6
- package/package.json +7 -6
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.geminiProvider = void 0;
|
|
4
|
+
exports.geminiVersion = geminiVersion;
|
|
5
|
+
exports.geminiExec = geminiExec;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
function resolveCommand(input) {
|
|
8
|
+
if (process.platform !== "win32") {
|
|
9
|
+
return input;
|
|
10
|
+
}
|
|
11
|
+
const looksLikePath = input.includes("\\") || input.includes("/");
|
|
12
|
+
const hasExt = /\.[A-Za-z0-9]+$/.test(input);
|
|
13
|
+
if (!looksLikePath && !hasExt) {
|
|
14
|
+
return `${input}.cmd`;
|
|
15
|
+
}
|
|
16
|
+
return input;
|
|
17
|
+
}
|
|
18
|
+
function geminiVersion() {
|
|
19
|
+
const command = resolveCommand(process.env.SDD_GEMINI_BIN?.trim() || "gemini");
|
|
20
|
+
const useShell = process.platform === "win32" && command.toLowerCase().endsWith(".cmd");
|
|
21
|
+
const result = (0, child_process_1.spawnSync)(command, ["--version"], {
|
|
22
|
+
encoding: "utf-8",
|
|
23
|
+
shell: useShell
|
|
24
|
+
});
|
|
25
|
+
if (result.status !== 0) {
|
|
26
|
+
return { ok: false, output: "", error: result.stderr || "gemini not available" };
|
|
27
|
+
}
|
|
28
|
+
return { ok: true, output: result.stdout.trim() };
|
|
29
|
+
}
|
|
30
|
+
function geminiExec(prompt) {
|
|
31
|
+
const command = resolveCommand(process.env.SDD_GEMINI_BIN?.trim() || "gemini");
|
|
32
|
+
const model = process.env.SDD_GEMINI_MODEL?.trim();
|
|
33
|
+
const useShell = process.platform === "win32" && command.toLowerCase().endsWith(".cmd");
|
|
34
|
+
const normalizedPrompt = prompt.replace(/\r?\n/g, "\\n");
|
|
35
|
+
const env = {
|
|
36
|
+
...process.env,
|
|
37
|
+
NO_COLOR: "1"
|
|
38
|
+
};
|
|
39
|
+
const modelArgs = model ? ["-m", model] : [];
|
|
40
|
+
const runPrimary = (withModel) => useShell
|
|
41
|
+
? (0, child_process_1.spawnSync)(`${command} ${withModel && model ? `-m "${model.replace(/"/g, "\"\"")}" ` : ""}--prompt "${normalizedPrompt.replace(/"/g, "\"\"")}" --output-format json`, {
|
|
42
|
+
encoding: "utf-8",
|
|
43
|
+
shell: true,
|
|
44
|
+
env
|
|
45
|
+
})
|
|
46
|
+
: (0, child_process_1.spawnSync)(command, [...(withModel ? modelArgs : []), "--prompt", normalizedPrompt, "--output-format", "json"], {
|
|
47
|
+
encoding: "utf-8",
|
|
48
|
+
shell: false,
|
|
49
|
+
env
|
|
50
|
+
});
|
|
51
|
+
const runFallback = (withModel) => useShell
|
|
52
|
+
? (0, child_process_1.spawnSync)(`${command} ${withModel && model ? `-m "${model.replace(/"/g, "\"\"")}" ` : ""}--prompt "${normalizedPrompt.replace(/"/g, "\"\"")}"`, {
|
|
53
|
+
encoding: "utf-8",
|
|
54
|
+
shell: true,
|
|
55
|
+
env
|
|
56
|
+
})
|
|
57
|
+
: (0, child_process_1.spawnSync)(command, [...(withModel ? modelArgs : []), "--prompt", normalizedPrompt], {
|
|
58
|
+
encoding: "utf-8",
|
|
59
|
+
shell: false,
|
|
60
|
+
env
|
|
61
|
+
});
|
|
62
|
+
let result = runPrimary(true);
|
|
63
|
+
if (result.status !== 0 && model) {
|
|
64
|
+
result = runPrimary(false);
|
|
65
|
+
}
|
|
66
|
+
if (result.status !== 0) {
|
|
67
|
+
result = runFallback(true);
|
|
68
|
+
}
|
|
69
|
+
if (result.status !== 0 && model) {
|
|
70
|
+
result = runFallback(false);
|
|
71
|
+
}
|
|
72
|
+
if (result.status !== 0) {
|
|
73
|
+
return { ok: false, output: result.stdout || "", error: result.stderr || "gemini exec failed" };
|
|
74
|
+
}
|
|
75
|
+
return { ok: true, output: result.stdout.trim() };
|
|
76
|
+
}
|
|
77
|
+
exports.geminiProvider = {
|
|
78
|
+
id: "gemini",
|
|
79
|
+
label: "Gemini",
|
|
80
|
+
version: geminiVersion,
|
|
81
|
+
exec: geminiExec
|
|
82
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AIProvider, ProviderId, ProviderPreference } from "./types";
|
|
2
|
+
export declare function defaultProviderPreference(): ProviderPreference;
|
|
3
|
+
export declare function parseProviderPreference(input?: string): ProviderPreference | null;
|
|
4
|
+
export declare function listProviders(): AIProvider[];
|
|
5
|
+
export type ProviderResolution = {
|
|
6
|
+
ok: true;
|
|
7
|
+
provider: AIProvider;
|
|
8
|
+
selected: ProviderId;
|
|
9
|
+
requested: ProviderPreference;
|
|
10
|
+
} | {
|
|
11
|
+
ok: false;
|
|
12
|
+
requested: string;
|
|
13
|
+
reason: "invalid" | "unavailable";
|
|
14
|
+
details: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function resolveProvider(requested?: string): ProviderResolution;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultProviderPreference = defaultProviderPreference;
|
|
4
|
+
exports.parseProviderPreference = parseProviderPreference;
|
|
5
|
+
exports.listProviders = listProviders;
|
|
6
|
+
exports.resolveProvider = resolveProvider;
|
|
7
|
+
const codex_1 = require("./codex");
|
|
8
|
+
const gemini_1 = require("./gemini");
|
|
9
|
+
const config_1 = require("../config");
|
|
10
|
+
const PROVIDERS = {
|
|
11
|
+
gemini: gemini_1.geminiProvider,
|
|
12
|
+
codex: codex_1.codexProvider
|
|
13
|
+
};
|
|
14
|
+
const AUTO_ORDER = ["gemini", "codex"];
|
|
15
|
+
function normalizePreference(input) {
|
|
16
|
+
const raw = (input ?? "").trim().toLowerCase();
|
|
17
|
+
if (!raw) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (raw === "auto" || raw === "gemini" || raw === "codex") {
|
|
21
|
+
return raw;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function defaultProviderPreference() {
|
|
26
|
+
const fromEnv = normalizePreference(process.env.SDD_AI_PROVIDER_DEFAULT);
|
|
27
|
+
if (fromEnv) {
|
|
28
|
+
return fromEnv;
|
|
29
|
+
}
|
|
30
|
+
const config = (0, config_1.ensureConfig)();
|
|
31
|
+
const fromConfig = normalizePreference(config.ai.preferred_cli);
|
|
32
|
+
return fromConfig ?? "gemini";
|
|
33
|
+
}
|
|
34
|
+
function parseProviderPreference(input) {
|
|
35
|
+
return normalizePreference(input);
|
|
36
|
+
}
|
|
37
|
+
function listProviders() {
|
|
38
|
+
return AUTO_ORDER.map((id) => PROVIDERS[id]);
|
|
39
|
+
}
|
|
40
|
+
function resolveProvider(requested) {
|
|
41
|
+
const normalized = normalizePreference(requested);
|
|
42
|
+
if (!normalized) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
requested: requested ?? "",
|
|
46
|
+
reason: "invalid",
|
|
47
|
+
details: "Use one of: gemini, codex, auto."
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (normalized === "auto") {
|
|
51
|
+
const preferred = defaultProviderPreference();
|
|
52
|
+
if (preferred !== "auto") {
|
|
53
|
+
const provider = PROVIDERS[preferred];
|
|
54
|
+
const status = provider.version();
|
|
55
|
+
if (status.ok) {
|
|
56
|
+
return { ok: true, provider, selected: preferred, requested: normalized };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
requested: normalized,
|
|
61
|
+
reason: "unavailable",
|
|
62
|
+
details: `${provider.label} not available: ${status.error || "provider unavailable"}`
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
for (const id of AUTO_ORDER) {
|
|
66
|
+
const provider = PROVIDERS[id];
|
|
67
|
+
const status = provider.version();
|
|
68
|
+
if (status.ok) {
|
|
69
|
+
return { ok: true, provider, selected: id, requested: normalized };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
requested: normalized,
|
|
75
|
+
reason: "unavailable",
|
|
76
|
+
details: "No provider available. Install/configure gemini or codex."
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const provider = PROVIDERS[normalized];
|
|
80
|
+
const status = provider.version();
|
|
81
|
+
if (!status.ok) {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
requested: normalized,
|
|
85
|
+
reason: "unavailable",
|
|
86
|
+
details: `${provider.label} not available: ${status.error || "provider unavailable"}`
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return { ok: true, provider, selected: normalized, requested: normalized };
|
|
90
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ProviderId = "gemini" | "codex";
|
|
2
|
+
export type ProviderPreference = ProviderId | "auto";
|
|
3
|
+
export type ProviderResult = {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
output: string;
|
|
6
|
+
error?: string;
|
|
7
|
+
};
|
|
8
|
+
export type AIProvider = {
|
|
9
|
+
id: ProviderId;
|
|
10
|
+
label: string;
|
|
11
|
+
version: () => ProviderResult;
|
|
12
|
+
exec: (prompt: string) => ProviderResult;
|
|
13
|
+
};
|
package/dist/router/intent.js
CHANGED
|
@@ -3,7 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FLOW_PROMPT_PACKS = void 0;
|
|
4
4
|
exports.classifyIntent = classifyIntent;
|
|
5
5
|
const SIGNALS = [
|
|
6
|
-
{
|
|
6
|
+
{
|
|
7
|
+
intent: "bug_fix",
|
|
8
|
+
flow: "BUG_FIX",
|
|
9
|
+
domain: "bug_fix",
|
|
10
|
+
keywords: ["bug", "issue", "error", "crash", "stack trace", "stacktrace"]
|
|
11
|
+
},
|
|
7
12
|
{ intent: "pr_review", flow: "PR_REVIEW", domain: "pr_review", keywords: ["pr", "pull request", "review"] },
|
|
8
13
|
{
|
|
9
14
|
intent: "learning",
|
|
@@ -31,7 +36,25 @@ const SIGNALS = [
|
|
|
31
36
|
intent: "software",
|
|
32
37
|
flow: "SOFTWARE_FEATURE",
|
|
33
38
|
domain: "software",
|
|
34
|
-
keywords: [
|
|
39
|
+
keywords: [
|
|
40
|
+
"feature",
|
|
41
|
+
"api",
|
|
42
|
+
"backend",
|
|
43
|
+
"frontend",
|
|
44
|
+
"implement",
|
|
45
|
+
"developer",
|
|
46
|
+
"refactor",
|
|
47
|
+
"code",
|
|
48
|
+
"crear",
|
|
49
|
+
"crea",
|
|
50
|
+
"aplicacion",
|
|
51
|
+
"aplicación",
|
|
52
|
+
"app",
|
|
53
|
+
"web",
|
|
54
|
+
"desktop",
|
|
55
|
+
"movil",
|
|
56
|
+
"móvil"
|
|
57
|
+
]
|
|
35
58
|
}
|
|
36
59
|
];
|
|
37
60
|
exports.FLOW_PROMPT_PACKS = {
|
|
@@ -48,14 +71,21 @@ exports.FLOW_PROMPT_PACKS = {
|
|
|
48
71
|
};
|
|
49
72
|
function classifyIntent(input) {
|
|
50
73
|
const normalized = input.toLowerCase();
|
|
74
|
+
const containsKeyword = (keyword) => {
|
|
75
|
+
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
76
|
+
if (escaped.length <= 3 && !escaped.includes(" ")) {
|
|
77
|
+
return new RegExp(`\\b${escaped}\\b`, "i").test(normalized);
|
|
78
|
+
}
|
|
79
|
+
return normalized.includes(keyword);
|
|
80
|
+
};
|
|
51
81
|
for (const rule of SIGNALS) {
|
|
52
|
-
if (rule.keywords.some((keyword) =>
|
|
82
|
+
if (rule.keywords.some((keyword) => containsKeyword(keyword))) {
|
|
53
83
|
return {
|
|
54
84
|
intent: rule.intent,
|
|
55
85
|
confidence: 0.7,
|
|
56
86
|
flow: rule.flow,
|
|
57
87
|
domain: rule.domain,
|
|
58
|
-
signals: rule.keywords.filter((keyword) =>
|
|
88
|
+
signals: rule.keywords.filter((keyword) => containsKeyword(keyword))
|
|
59
89
|
};
|
|
60
90
|
}
|
|
61
91
|
}
|
package/dist/workspace/index.js
CHANGED
|
@@ -17,8 +17,8 @@ exports.createProject = createProject;
|
|
|
17
17
|
exports.updateProjectStatus = updateProjectStatus;
|
|
18
18
|
const fs_1 = __importDefault(require("fs"));
|
|
19
19
|
const path_1 = __importDefault(require("path"));
|
|
20
|
-
const os_1 = __importDefault(require("os"));
|
|
21
20
|
const flags_1 = require("../context/flags");
|
|
21
|
+
const config_1 = require("../config");
|
|
22
22
|
const WORKSPACE_LOCK_RETRY_MS = 50;
|
|
23
23
|
const WORKSPACE_LOCK_WAIT_MS = 5000;
|
|
24
24
|
const WORKSPACE_LOCK_STALE_MS = 30000;
|
|
@@ -119,11 +119,11 @@ function getWorkspaceInfoForScope(scope) {
|
|
|
119
119
|
}
|
|
120
120
|
function getWorkspaceBaseRoot() {
|
|
121
121
|
const flags = (0, flags_1.getFlags)();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
if (flags.output) {
|
|
123
|
+
return path_1.default.resolve(flags.output);
|
|
124
|
+
}
|
|
125
|
+
const config = (0, config_1.ensureConfig)();
|
|
126
|
+
return path_1.default.resolve(config.workspace.default_root);
|
|
127
127
|
}
|
|
128
128
|
function listScopes(baseRoot) {
|
|
129
129
|
const root = baseRoot ? path_1.default.resolve(baseRoot) : getWorkspaceBaseRoot();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdd-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.24",
|
|
4
|
+
"description": "AI-orchestrated specification-driven delivery CLI that plans, validates, and ships production-ready software projects.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
7
7
|
"specification-driven-development",
|
|
@@ -25,10 +25,11 @@
|
|
|
25
25
|
"bugs": {
|
|
26
26
|
"url": "https://github.com/jdsalasca/sdd-tool/issues"
|
|
27
27
|
},
|
|
28
|
-
"bin": {
|
|
29
|
-
"sdd-cli": "dist/cli.js",
|
|
30
|
-
"sdd": "dist/cli.js"
|
|
31
|
-
|
|
28
|
+
"bin": {
|
|
29
|
+
"sdd-cli": "dist/cli.js",
|
|
30
|
+
"sdd": "dist/cli.js",
|
|
31
|
+
"sdd-tool": "dist/cli.js"
|
|
32
|
+
},
|
|
32
33
|
"main": "dist/cli.js",
|
|
33
34
|
"types": "dist/cli.d.ts",
|
|
34
35
|
"files": [
|