sdtk-kit 0.3.8 → 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/LICENSE +21 -0
- package/README.md +123 -177
- package/package.json +52 -46
- package/scripts/postinstall.js +40 -0
- package/assets/manifest/toolkit-bundle.manifest.json +0 -473
- package/assets/manifest/toolkit-bundle.sha256.txt +0 -93
- package/assets/toolkit/toolkit/AGENTS.md +0 -131
- package/assets/toolkit/toolkit/install.ps1 +0 -270
- package/assets/toolkit/toolkit/runtimes/claude/CLAUDE_TEMPLATE.md +0 -54
- package/assets/toolkit/toolkit/runtimes/codex/CODEX_TEMPLATE.md +0 -32
- package/assets/toolkit/toolkit/scripts/init-feature.ps1 +0 -261
- package/assets/toolkit/toolkit/scripts/install-claude-skills.ps1 +0 -129
- package/assets/toolkit/toolkit/scripts/install-codex-skills.ps1 +0 -189
- package/assets/toolkit/toolkit/scripts/uninstall-claude-skills.ps1 +0 -139
- package/assets/toolkit/toolkit/scripts/uninstall-codex-skills.ps1 +0 -116
- package/assets/toolkit/toolkit/sdtk.config.json +0 -28
- package/assets/toolkit/toolkit/sdtk.config.profiles.example.json +0 -50
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/SKILL.md +0 -84
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/references/API_DESIGN_CREATION_RULES.md +0 -22
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/references/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/references/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/scripts/generate_api_design_detail.py +0 -656
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/SKILL.md +0 -43
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/references/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/references/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/references/YAML_CREATION_RULES.md +0 -128
- package/assets/toolkit/toolkit/skills/sdtk-arch/SKILL.md +0 -83
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/API_DESIGN_CREATION_RULES.md +0 -22
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/FLOW_ACTION_SPEC_CREATION_RULES.md +0 -200
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/YAML_CREATION_RULES.md +0 -128
- package/assets/toolkit/toolkit/skills/sdtk-ba/SKILL.md +0 -29
- package/assets/toolkit/toolkit/skills/sdtk-design-layout/SKILL.md +0 -41
- package/assets/toolkit/toolkit/skills/sdtk-design-layout/scripts/render_design_layout_images.py +0 -213
- package/assets/toolkit/toolkit/skills/sdtk-dev/SKILL.md +0 -90
- package/assets/toolkit/toolkit/skills/sdtk-dev/prompts/code-quality-reviewer.md +0 -35
- package/assets/toolkit/toolkit/skills/sdtk-dev/prompts/implementer.md +0 -61
- package/assets/toolkit/toolkit/skills/sdtk-dev/prompts/spec-reviewer.md +0 -42
- package/assets/toolkit/toolkit/skills/sdtk-dev-backend/SKILL.md +0 -21
- package/assets/toolkit/toolkit/skills/sdtk-dev-frontend/SKILL.md +0 -19
- package/assets/toolkit/toolkit/skills/sdtk-orchestrator/SKILL.md +0 -80
- package/assets/toolkit/toolkit/skills/sdtk-pm/SKILL.md +0 -30
- package/assets/toolkit/toolkit/skills/sdtk-qa/SKILL.md +0 -53
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/SKILL.md +0 -73
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/FLOW_ACTION_SPEC_CREATION_RULES.md +0 -200
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/excel-image-export.md +0 -51
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/figma-mcp.md +0 -54
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/numbering-rules.md +0 -76
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/scripts/renumber_flow_action_spec_global.py +0 -136
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/scripts/validate_flow_action_spec_numbering.py +0 -249
- package/assets/toolkit/toolkit/skills/sdtk-test-case-spec/SKILL.md +0 -74
- package/assets/toolkit/toolkit/skills/sdtk-test-case-spec/references/TEST_CASE_CREATION_RULES.md +0 -129
- package/assets/toolkit/toolkit/skills/sdtk-test-case-spec/scripts/validate_test_case_spec.py +0 -97
- package/assets/toolkit/toolkit/skills/skills.catalog.yaml +0 -302
- package/assets/toolkit/toolkit/skills-claude/api-design-spec/SKILL.md +0 -76
- package/assets/toolkit/toolkit/skills-claude/api-doc/SKILL.md +0 -47
- package/assets/toolkit/toolkit/skills-claude/arch/SKILL.md +0 -72
- package/assets/toolkit/toolkit/skills-claude/ba/SKILL.md +0 -50
- package/assets/toolkit/toolkit/skills-claude/design-layout/SKILL.md +0 -25
- package/assets/toolkit/toolkit/skills-claude/dev/SKILL.md +0 -45
- package/assets/toolkit/toolkit/skills-claude/dev-backend/SKILL.md +0 -20
- package/assets/toolkit/toolkit/skills-claude/dev-frontend/SKILL.md +0 -18
- package/assets/toolkit/toolkit/skills-claude/orchestrator/SKILL.md +0 -63
- package/assets/toolkit/toolkit/skills-claude/pm/SKILL.md +0 -52
- package/assets/toolkit/toolkit/skills-claude/qa/SKILL.md +0 -48
- package/assets/toolkit/toolkit/skills-claude/screen-design-spec/SKILL.md +0 -68
- package/assets/toolkit/toolkit/skills-claude/test-case-spec/SKILL.md +0 -61
- package/assets/toolkit/toolkit/templates/QUALITY_CHECKLIST.md +0 -124
- package/assets/toolkit/toolkit/templates/README.md +0 -63
- package/assets/toolkit/toolkit/templates/SHARED_PLANNING.md +0 -80
- package/assets/toolkit/toolkit/templates/docs/api/API_DESIGN_CREATION_RULES.md +0 -22
- package/assets/toolkit/toolkit/templates/docs/api/API_DESIGN_DETAIL_TEMPLATE.md +0 -67
- package/assets/toolkit/toolkit/templates/docs/api/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/templates/docs/api/API_ENDPOINTS_TEMPLATE.md +0 -229
- package/assets/toolkit/toolkit/templates/docs/api/FEATURE_API_TEMPLATE.yaml +0 -20
- package/assets/toolkit/toolkit/templates/docs/api/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/templates/docs/api/YAML_CREATION_RULES.md +0 -128
- package/assets/toolkit/toolkit/templates/docs/api/feature_api_flow_list_TEMPLATE.txt +0 -12
- package/assets/toolkit/toolkit/templates/docs/architecture/ARCH_DESIGN_TEMPLATE.md +0 -109
- package/assets/toolkit/toolkit/templates/docs/database/DATABASE_SPEC_TEMPLATE.md +0 -175
- package/assets/toolkit/toolkit/templates/docs/design/DESIGN_LAYOUT_TEMPLATE.md +0 -49
- package/assets/toolkit/toolkit/templates/docs/dev/FEATURE_IMPL_PLAN_TEMPLATE.md +0 -73
- package/assets/toolkit/toolkit/templates/docs/product/BACKLOG_TEMPLATE.md +0 -50
- package/assets/toolkit/toolkit/templates/docs/product/PRD_TEMPLATE.md +0 -66
- package/assets/toolkit/toolkit/templates/docs/product/PROJECT_INITIATION_TEMPLATE.md +0 -98
- package/assets/toolkit/toolkit/templates/docs/qa/QA_RELEASE_REPORT_TEMPLATE.md +0 -61
- package/assets/toolkit/toolkit/templates/docs/qa/TEST_CASE_CREATION_RULES.md +0 -129
- package/assets/toolkit/toolkit/templates/docs/qa/TEST_CASE_TEMPLATE.md +0 -104
- package/assets/toolkit/toolkit/templates/docs/specs/BA_SPEC_TEMPLATE.md +0 -139
- package/assets/toolkit/toolkit/templates/docs/specs/FLOW_ACTION_SPEC_CREATION_RULES.md +0 -200
- package/assets/toolkit/toolkit/templates/docs/specs/FLOW_ACTION_SPEC_TEMPLATE.md +0 -172
- package/assets/toolkit/toolkit/templates/handoffs/ARCH_TO_DEV.md +0 -31
- package/assets/toolkit/toolkit/templates/handoffs/BA_TO_ARCH.md +0 -28
- package/assets/toolkit/toolkit/templates/handoffs/DEV_STAGE1_SPEC_REVIEW.md +0 -26
- package/assets/toolkit/toolkit/templates/handoffs/DEV_STAGE2_CODE_QUALITY_REVIEW.md +0 -20
- package/assets/toolkit/toolkit/templates/handoffs/DEV_TO_QA.md +0 -23
- package/assets/toolkit/toolkit/templates/handoffs/PM_TO_BA.md +0 -26
- package/assets/toolkit/toolkit/templates/handoffs/QA_RELEASE_DECISION.md +0 -21
- package/bin/sdtk.js +0 -15
- package/src/commands/auth.js +0 -85
- package/src/commands/generate.js +0 -177
- package/src/commands/help.js +0 -101
- package/src/commands/init.js +0 -97
- package/src/commands/runtime.js +0 -217
- package/src/index.js +0 -59
- package/src/lib/args.js +0 -116
- package/src/lib/errors.js +0 -41
- package/src/lib/github-access.js +0 -68
- package/src/lib/powershell.js +0 -85
- package/src/lib/scope.js +0 -68
- package/src/lib/state.js +0 -83
- package/src/lib/toolkit-payload.js +0 -99
package/src/lib/args.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { ValidationError } = require("./errors");
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Parse argv into a structured flags object.
|
|
7
|
-
* Supports: --flag value, --flag=value, --bool-flag (no value).
|
|
8
|
-
*
|
|
9
|
-
* @param {string[]} argv - Raw arguments (after command is stripped).
|
|
10
|
-
* @param {Object<string, {type: 'string'|'boolean', required?: boolean, alias?: string}>} defs - Flag definitions.
|
|
11
|
-
* @returns {{ flags: Object, positional: string[] }}
|
|
12
|
-
*/
|
|
13
|
-
function parseFlags(argv, defs) {
|
|
14
|
-
const flags = {};
|
|
15
|
-
const positional = [];
|
|
16
|
-
const aliasMap = {};
|
|
17
|
-
|
|
18
|
-
for (const [name, def] of Object.entries(defs)) {
|
|
19
|
-
if (def.alias) aliasMap[def.alias] = name;
|
|
20
|
-
if (def.type === "boolean") flags[name] = false;
|
|
21
|
-
else flags[name] = undefined;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let i = 0;
|
|
25
|
-
while (i < argv.length) {
|
|
26
|
-
const arg = argv[i];
|
|
27
|
-
|
|
28
|
-
if (!arg.startsWith("-")) {
|
|
29
|
-
positional.push(arg);
|
|
30
|
-
i++;
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let key;
|
|
35
|
-
let inlineValue;
|
|
36
|
-
|
|
37
|
-
if (arg.includes("=")) {
|
|
38
|
-
const eqIdx = arg.indexOf("=");
|
|
39
|
-
key = arg.slice(0, eqIdx);
|
|
40
|
-
inlineValue = arg.slice(eqIdx + 1);
|
|
41
|
-
} else {
|
|
42
|
-
key = arg;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Normalize: strip leading dashes, resolve alias
|
|
46
|
-
const stripped = key.replace(/^-{1,2}/, "");
|
|
47
|
-
const resolved = aliasMap[stripped] || stripped;
|
|
48
|
-
|
|
49
|
-
if (!(resolved in defs)) {
|
|
50
|
-
throw new ValidationError(`Unknown flag: ${key}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const def = defs[resolved];
|
|
54
|
-
|
|
55
|
-
if (def.type === "boolean") {
|
|
56
|
-
flags[resolved] = true;
|
|
57
|
-
i++;
|
|
58
|
-
} else {
|
|
59
|
-
// string type
|
|
60
|
-
let value = inlineValue;
|
|
61
|
-
if (value === undefined) {
|
|
62
|
-
i++;
|
|
63
|
-
if (i >= argv.length) {
|
|
64
|
-
throw new ValidationError(`Flag ${key} requires a value.`);
|
|
65
|
-
}
|
|
66
|
-
value = argv[i];
|
|
67
|
-
}
|
|
68
|
-
flags[resolved] = value;
|
|
69
|
-
i++;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return { flags, positional };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Require a flag to be present (non-undefined, non-empty for strings).
|
|
78
|
-
*/
|
|
79
|
-
function requireFlag(flags, name, label) {
|
|
80
|
-
const val = flags[name];
|
|
81
|
-
if (val === undefined || val === null || val === "") {
|
|
82
|
-
throw new ValidationError(`Missing required flag: --${label || name}`);
|
|
83
|
-
}
|
|
84
|
-
return val;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Validate a value is one of the allowed choices.
|
|
89
|
-
*/
|
|
90
|
-
function validateChoice(value, choices, label) {
|
|
91
|
-
if (!choices.includes(value)) {
|
|
92
|
-
throw new ValidationError(
|
|
93
|
-
`Invalid value for --${label}: "${value}". Must be one of: ${choices.join(", ")}`
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
return value;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Validate a value matches a regex pattern.
|
|
101
|
-
*/
|
|
102
|
-
function validatePattern(value, regex, label, hint) {
|
|
103
|
-
if (!regex.test(value)) {
|
|
104
|
-
throw new ValidationError(
|
|
105
|
-
`Invalid value for --${label}: "${value}". ${hint || `Must match ${regex}`}`
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
return value;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
module.exports = {
|
|
112
|
-
parseFlags,
|
|
113
|
-
requireFlag,
|
|
114
|
-
validateChoice,
|
|
115
|
-
validatePattern,
|
|
116
|
-
};
|
package/src/lib/errors.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
class CliError extends Error {
|
|
4
|
-
constructor(message, exitCode = 4) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.name = this.constructor.name;
|
|
7
|
-
this.exitCode = exitCode;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
class ValidationError extends CliError {
|
|
12
|
-
constructor(message) {
|
|
13
|
-
super(message, 1);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
class DependencyError extends CliError {
|
|
18
|
-
constructor(message) {
|
|
19
|
-
super(message, 2);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
class IntegrityError extends CliError {
|
|
24
|
-
constructor(message) {
|
|
25
|
-
super(message, 3);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
class InternalError extends CliError {
|
|
30
|
-
constructor(message) {
|
|
31
|
-
super(message, 4);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = {
|
|
36
|
-
CliError,
|
|
37
|
-
ValidationError,
|
|
38
|
-
DependencyError,
|
|
39
|
-
IntegrityError,
|
|
40
|
-
InternalError,
|
|
41
|
-
};
|
package/src/lib/github-access.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { ValidationError } = require("./errors");
|
|
4
|
-
|
|
5
|
-
// Entitlement repo: configurable via env var, falls back to default.
|
|
6
|
-
// Set SDTK_ENTITLEMENT_REPO=owner/repo to override.
|
|
7
|
-
const DEFAULT_REPO = "DucTN/sdtk-private";
|
|
8
|
-
|
|
9
|
-
function getEntitlementRepo() {
|
|
10
|
-
return process.env.SDTK_ENTITLEMENT_REPO || DEFAULT_REPO;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Check if a GitHub token has access to the private distribution repo.
|
|
15
|
-
*
|
|
16
|
-
* @param {string} token - GitHub PAT.
|
|
17
|
-
* @param {string} [repo] - Repo in "owner/name" format. Defaults to SDTK_ENTITLEMENT_REPO env or built-in default.
|
|
18
|
-
* @returns {Promise<{hasAccess: boolean, message: string}>}
|
|
19
|
-
*/
|
|
20
|
-
async function checkRepoAccess(token, repo) {
|
|
21
|
-
const targetRepo = repo || getEntitlementRepo();
|
|
22
|
-
const url = `https://api.github.com/repos/${targetRepo}`;
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const response = await fetch(url, {
|
|
26
|
-
headers: {
|
|
27
|
-
Authorization: `Bearer ${token}`,
|
|
28
|
-
Accept: "application/vnd.github.v3+json",
|
|
29
|
-
"User-Agent": "sdtk-cli",
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
if (response.status === 200) {
|
|
34
|
-
return {
|
|
35
|
-
hasAccess: true,
|
|
36
|
-
message: `Access verified for ${targetRepo}.`,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (response.status === 404 || response.status === 403) {
|
|
41
|
-
return {
|
|
42
|
-
hasAccess: false,
|
|
43
|
-
message: `No access to ${targetRepo}. Check your token permissions.`,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (response.status === 401) {
|
|
48
|
-
return {
|
|
49
|
-
hasAccess: false,
|
|
50
|
-
message: "Token is invalid or expired.",
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
hasAccess: false,
|
|
56
|
-
message: `Unexpected response (HTTP ${response.status}) from GitHub API.`,
|
|
57
|
-
};
|
|
58
|
-
} catch (err) {
|
|
59
|
-
throw new ValidationError(
|
|
60
|
-
`Failed to reach GitHub API: ${err.message}\nCheck your network connection.`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = {
|
|
66
|
-
checkRepoAccess,
|
|
67
|
-
getEntitlementRepo,
|
|
68
|
-
};
|
package/src/lib/powershell.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { execFile } = require("child_process");
|
|
4
|
-
const { DependencyError } = require("./errors");
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Find available PowerShell executable.
|
|
8
|
-
* Prefers pwsh (PowerShell Core) over powershell.exe (Windows PowerShell).
|
|
9
|
-
* @returns {string} PowerShell executable name.
|
|
10
|
-
*/
|
|
11
|
-
function findPowerShell() {
|
|
12
|
-
// On Windows, powershell.exe is always available
|
|
13
|
-
// pwsh (PowerShell Core) is preferred if available
|
|
14
|
-
if (process.platform === "win32") {
|
|
15
|
-
return "powershell.exe";
|
|
16
|
-
}
|
|
17
|
-
// On non-Windows, pwsh must be installed
|
|
18
|
-
return "pwsh";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Execute a PowerShell script file with arguments.
|
|
23
|
-
*
|
|
24
|
-
* @param {string} scriptPath - Absolute path to the .ps1 file.
|
|
25
|
-
* @param {Object<string, string|boolean>} params - Parameters to pass.
|
|
26
|
-
* String values become `-ParamName "value"`, boolean true becomes `-ParamName`.
|
|
27
|
-
* @param {Object} [options] - Optional settings.
|
|
28
|
-
* @param {boolean} [options.silent] - If true, suppress stdout output.
|
|
29
|
-
* @returns {Promise<{exitCode: number, stdout: string, stderr: string}>}
|
|
30
|
-
*/
|
|
31
|
-
function runScript(scriptPath, params = {}, options = {}) {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
const psExe = findPowerShell();
|
|
34
|
-
const args = [
|
|
35
|
-
"-ExecutionPolicy",
|
|
36
|
-
"Bypass",
|
|
37
|
-
"-NoProfile",
|
|
38
|
-
"-NonInteractive",
|
|
39
|
-
"-File",
|
|
40
|
-
scriptPath,
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
// Build parameters safely using execFile array args
|
|
44
|
-
for (const [key, value] of Object.entries(params)) {
|
|
45
|
-
if (value === true) {
|
|
46
|
-
args.push(`-${key}`);
|
|
47
|
-
} else if (value === false || value === undefined || value === null) {
|
|
48
|
-
// Skip false/undefined/null switches
|
|
49
|
-
} else {
|
|
50
|
-
args.push(`-${key}`);
|
|
51
|
-
args.push(String(value));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
execFile(psExe, args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
56
|
-
if (error && error.code === "ENOENT") {
|
|
57
|
-
reject(
|
|
58
|
-
new DependencyError(
|
|
59
|
-
`PowerShell not found. Ensure PowerShell is installed and available in PATH.\n` +
|
|
60
|
-
`Tried: ${psExe}`
|
|
61
|
-
)
|
|
62
|
-
);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const exitCode = error ? error.code || 1 : 0;
|
|
67
|
-
const result = {
|
|
68
|
-
exitCode: typeof exitCode === "number" ? exitCode : 1,
|
|
69
|
-
stdout: stdout || "",
|
|
70
|
-
stderr: stderr || "",
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
if (!options.silent && result.stdout) {
|
|
74
|
-
process.stdout.write(result.stdout);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
resolve(result);
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
module.exports = {
|
|
83
|
-
runScript,
|
|
84
|
-
findPowerShell,
|
|
85
|
-
};
|
package/src/lib/scope.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const os = require("os");
|
|
5
|
-
|
|
6
|
-
const VALID_SCOPES = ["project", "user"];
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Returns the default scope for a given runtime.
|
|
10
|
-
* Claude defaults to project-local, Codex defaults to user/global.
|
|
11
|
-
*/
|
|
12
|
-
function defaultScope(runtime) {
|
|
13
|
-
return runtime === "claude" ? "project" : "user";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Returns true if the given runtime supports project-local scope.
|
|
18
|
-
* Gate C0: Codex does not support project-local skills.
|
|
19
|
-
*/
|
|
20
|
-
function isProjectScopeSupported(runtime) {
|
|
21
|
-
return runtime === "claude";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Resolves the skills directory for a given runtime, scope, and project path.
|
|
26
|
-
*/
|
|
27
|
-
function resolveSkillsDir(runtime, scope, projectPath) {
|
|
28
|
-
if (runtime === "claude") {
|
|
29
|
-
if (scope === "user") {
|
|
30
|
-
return path.join(os.homedir(), ".claude", "skills");
|
|
31
|
-
}
|
|
32
|
-
return path.join(projectPath || process.cwd(), ".claude", "skills");
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Codex: always user/global scope
|
|
36
|
-
const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
|
|
37
|
-
return path.join(codexHome, "skills");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* SDTK-managed skill directory names per runtime.
|
|
42
|
-
* Used by runtime status to distinguish SDTK skills from unrelated user skills.
|
|
43
|
-
*/
|
|
44
|
-
const MANAGED_CODEX_SKILLS = [
|
|
45
|
-
"sdtk-api-design-spec", "sdtk-api-doc", "sdtk-arch", "sdtk-ba",
|
|
46
|
-
"sdtk-design-layout", "sdtk-dev", "sdtk-dev-backend", "sdtk-dev-frontend",
|
|
47
|
-
"sdtk-orchestrator", "sdtk-pm", "sdtk-qa", "sdtk-screen-design-spec",
|
|
48
|
-
"sdtk-test-case-spec",
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
const MANAGED_CLAUDE_SKILLS = [
|
|
52
|
-
"api-design-spec", "api-doc", "arch", "ba",
|
|
53
|
-
"design-layout", "dev", "dev-backend", "dev-frontend",
|
|
54
|
-
"orchestrator", "pm", "qa", "screen-design-spec",
|
|
55
|
-
"test-case-spec",
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
function managedSkillNames(runtime) {
|
|
59
|
-
return runtime === "claude" ? MANAGED_CLAUDE_SKILLS : MANAGED_CODEX_SKILLS;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
module.exports = {
|
|
63
|
-
VALID_SCOPES,
|
|
64
|
-
defaultScope,
|
|
65
|
-
isProjectScopeSupported,
|
|
66
|
-
resolveSkillsDir,
|
|
67
|
-
managedSkillNames,
|
|
68
|
-
};
|
package/src/lib/state.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const os = require("os");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const { execFileSync } = require("child_process");
|
|
7
|
-
|
|
8
|
-
function ensureDir(dirPath) {
|
|
9
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function getAuthFile() {
|
|
13
|
-
return path.join(os.homedir(), ".sdtkrc");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Harden file permissions so only the current user can read/write.
|
|
18
|
-
* - POSIX: chmod 600
|
|
19
|
-
* - Windows: icacls to grant only current user Full Control
|
|
20
|
-
* @returns {boolean} true if hardening succeeded, false otherwise.
|
|
21
|
-
*/
|
|
22
|
-
function hardenFilePermissions(filePath) {
|
|
23
|
-
try {
|
|
24
|
-
if (process.platform === "win32") {
|
|
25
|
-
const username = os.userInfo().username;
|
|
26
|
-
execFileSync("icacls", [
|
|
27
|
-
filePath,
|
|
28
|
-
"/inheritance:r",
|
|
29
|
-
"/grant:r",
|
|
30
|
-
`${username}:(F)`,
|
|
31
|
-
], { stdio: "ignore" });
|
|
32
|
-
} else {
|
|
33
|
-
fs.chmodSync(filePath, 0o600);
|
|
34
|
-
}
|
|
35
|
-
return true;
|
|
36
|
-
} catch (_err) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function readAuthState() {
|
|
42
|
-
const authFile = getAuthFile();
|
|
43
|
-
if (!fs.existsSync(authFile)) {
|
|
44
|
-
return { authenticated: false, token: "" };
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
const payload = JSON.parse(fs.readFileSync(authFile, "utf8"));
|
|
48
|
-
const token = payload.github_token || "";
|
|
49
|
-
return { authenticated: token.length > 0, token };
|
|
50
|
-
} catch (_error) {
|
|
51
|
-
return { authenticated: false, token: "" };
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Write auth state and harden file permissions.
|
|
57
|
-
* @returns {{ path: string, hardened: boolean }}
|
|
58
|
-
*/
|
|
59
|
-
function writeAuthState(token) {
|
|
60
|
-
const authFile = getAuthFile();
|
|
61
|
-
const payload = {
|
|
62
|
-
github_token: token,
|
|
63
|
-
updated_at: new Date().toISOString(),
|
|
64
|
-
};
|
|
65
|
-
fs.writeFileSync(authFile, JSON.stringify(payload, null, 2), "utf8");
|
|
66
|
-
const hardened = hardenFilePermissions(authFile);
|
|
67
|
-
return { path: authFile, hardened };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function clearAuthState() {
|
|
71
|
-
const authFile = getAuthFile();
|
|
72
|
-
if (fs.existsSync(authFile)) {
|
|
73
|
-
fs.unlinkSync(authFile);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
module.exports = {
|
|
78
|
-
ensureDir,
|
|
79
|
-
getAuthFile,
|
|
80
|
-
readAuthState,
|
|
81
|
-
writeAuthState,
|
|
82
|
-
clearAuthState,
|
|
83
|
-
};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const crypto = require("crypto");
|
|
6
|
-
const { IntegrityError } = require("./errors");
|
|
7
|
-
|
|
8
|
-
const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
|
|
9
|
-
const ASSETS_DIR = path.join(PACKAGE_ROOT, "assets", "toolkit");
|
|
10
|
-
const MANIFEST_PATH = path.join(
|
|
11
|
-
PACKAGE_ROOT,
|
|
12
|
-
"assets",
|
|
13
|
-
"manifest",
|
|
14
|
-
"toolkit-bundle.manifest.json"
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Load and parse the toolkit bundle manifest.
|
|
19
|
-
* @returns {Object} Parsed manifest object.
|
|
20
|
-
*/
|
|
21
|
-
function loadManifest() {
|
|
22
|
-
if (!fs.existsSync(MANIFEST_PATH)) {
|
|
23
|
-
throw new IntegrityError(
|
|
24
|
-
`Toolkit manifest not found: ${MANIFEST_PATH}\n` +
|
|
25
|
-
'Run "npm run build:payload" to sync toolkit assets.'
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
try {
|
|
29
|
-
const raw = fs.readFileSync(MANIFEST_PATH, "utf-8");
|
|
30
|
-
return JSON.parse(raw);
|
|
31
|
-
} catch (err) {
|
|
32
|
-
throw new IntegrityError(`Failed to parse manifest: ${err.message}`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Compute SHA256 hash of a file.
|
|
38
|
-
* @param {string} filePath
|
|
39
|
-
* @returns {string} Lowercase hex hash.
|
|
40
|
-
*/
|
|
41
|
-
function hashFile(filePath) {
|
|
42
|
-
const data = fs.readFileSync(filePath);
|
|
43
|
-
return crypto.createHash("sha256").update(data).digest("hex");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Verify payload integrity against manifest.
|
|
48
|
-
* Checks all files exist and SHA256 hashes match.
|
|
49
|
-
* @throws {IntegrityError} on mismatch or missing file.
|
|
50
|
-
*/
|
|
51
|
-
function verify() {
|
|
52
|
-
const manifest = loadManifest();
|
|
53
|
-
|
|
54
|
-
if (!manifest.files || !Array.isArray(manifest.files)) {
|
|
55
|
-
throw new IntegrityError("Manifest has no files array.");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const errors = [];
|
|
59
|
-
|
|
60
|
-
for (const entry of manifest.files) {
|
|
61
|
-
const filePath = path.join(ASSETS_DIR, entry.path);
|
|
62
|
-
|
|
63
|
-
if (!fs.existsSync(filePath)) {
|
|
64
|
-
errors.push(`MISSING: ${entry.path}`);
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const actualHash = hashFile(filePath);
|
|
69
|
-
if (actualHash !== entry.sha256) {
|
|
70
|
-
errors.push(
|
|
71
|
-
`HASH MISMATCH: ${entry.path}\n` +
|
|
72
|
-
` expected: ${entry.sha256}\n` +
|
|
73
|
-
` actual: ${actualHash}`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (errors.length > 0) {
|
|
79
|
-
throw new IntegrityError(
|
|
80
|
-
`Toolkit payload integrity check failed:\n${errors.join("\n")}`
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Resolve absolute path to a file inside the bundled toolkit payload.
|
|
87
|
-
* @param {string} relativePath - Path relative to assets/toolkit/ (e.g., "toolkit/install.ps1").
|
|
88
|
-
* @returns {string} Absolute path.
|
|
89
|
-
*/
|
|
90
|
-
function resolvePayloadFile(relativePath) {
|
|
91
|
-
return path.join(ASSETS_DIR, relativePath);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
module.exports = {
|
|
95
|
-
verify,
|
|
96
|
-
loadManifest,
|
|
97
|
-
resolvePayloadFile,
|
|
98
|
-
ASSETS_DIR,
|
|
99
|
-
};
|