sdtk-ops-kit 0.2.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/README.md +146 -0
- package/assets/manifest/toolkit-bundle.manifest.json +187 -0
- package/assets/manifest/toolkit-bundle.sha256.txt +36 -0
- package/assets/toolkit/toolkit/AGENTS.md +65 -0
- package/assets/toolkit/toolkit/SDTKOPS_TOOLKIT.md +166 -0
- package/assets/toolkit/toolkit/install.ps1 +138 -0
- package/assets/toolkit/toolkit/scripts/install-claude-skills.ps1 +81 -0
- package/assets/toolkit/toolkit/scripts/install-codex-skills.ps1 +127 -0
- package/assets/toolkit/toolkit/scripts/uninstall-claude-skills.ps1 +65 -0
- package/assets/toolkit/toolkit/scripts/uninstall-codex-skills.ps1 +53 -0
- package/assets/toolkit/toolkit/sdtk-spec.config.json +6 -0
- package/assets/toolkit/toolkit/sdtk-spec.config.profiles.example.json +12 -0
- package/assets/toolkit/toolkit/skills/ops-backup/SKILL.md +93 -0
- package/assets/toolkit/toolkit/skills/ops-backup/references/backup-script-patterns.md +108 -0
- package/assets/toolkit/toolkit/skills/ops-ci-cd/SKILL.md +88 -0
- package/assets/toolkit/toolkit/skills/ops-ci-cd/references/pipeline-examples.md +113 -0
- package/assets/toolkit/toolkit/skills/ops-compliance/SKILL.md +105 -0
- package/assets/toolkit/toolkit/skills/ops-container/SKILL.md +95 -0
- package/assets/toolkit/toolkit/skills/ops-container/references/k8s-manifest-patterns.md +116 -0
- package/assets/toolkit/toolkit/skills/ops-cost/SKILL.md +88 -0
- package/assets/toolkit/toolkit/skills/ops-debug/SKILL.md +311 -0
- package/assets/toolkit/toolkit/skills/ops-debug/references/root-cause-tracing.md +138 -0
- package/assets/toolkit/toolkit/skills/ops-deploy/SKILL.md +102 -0
- package/assets/toolkit/toolkit/skills/ops-discover/SKILL.md +102 -0
- package/assets/toolkit/toolkit/skills/ops-incident/SKILL.md +113 -0
- package/assets/toolkit/toolkit/skills/ops-incident/references/communication-templates.md +34 -0
- package/assets/toolkit/toolkit/skills/ops-incident/references/postmortem-template.md +69 -0
- package/assets/toolkit/toolkit/skills/ops-incident/references/runbook-template.md +69 -0
- package/assets/toolkit/toolkit/skills/ops-infra-plan/SKILL.md +123 -0
- package/assets/toolkit/toolkit/skills/ops-infra-plan/references/iac-patterns.md +141 -0
- package/assets/toolkit/toolkit/skills/ops-monitor/SKILL.md +110 -0
- package/assets/toolkit/toolkit/skills/ops-monitor/references/alert-rules.md +80 -0
- package/assets/toolkit/toolkit/skills/ops-monitor/references/slo-templates.md +83 -0
- package/assets/toolkit/toolkit/skills/ops-parallel/SKILL.md +177 -0
- package/assets/toolkit/toolkit/skills/ops-plan/SKILL.md +169 -0
- package/assets/toolkit/toolkit/skills/ops-security-infra/SKILL.md +126 -0
- package/assets/toolkit/toolkit/skills/ops-security-infra/references/cicd-security-pipeline.md +55 -0
- package/assets/toolkit/toolkit/skills/ops-security-infra/references/security-headers.md +24 -0
- package/assets/toolkit/toolkit/skills/ops-verify/SKILL.md +180 -0
- package/bin/sdtk-ops.js +14 -0
- package/package.json +46 -0
- package/src/commands/generate.js +12 -0
- package/src/commands/help.js +53 -0
- package/src/commands/init.js +86 -0
- package/src/commands/runtime.js +201 -0
- package/src/index.js +65 -0
- package/src/lib/args.js +107 -0
- package/src/lib/errors.js +41 -0
- package/src/lib/powershell.js +65 -0
- package/src/lib/scope.js +58 -0
- package/src/lib/toolkit-payload.js +123 -0
package/src/lib/args.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { ValidationError } = require("./errors");
|
|
4
|
+
|
|
5
|
+
function parseFlags(argv, defs) {
|
|
6
|
+
const flags = {};
|
|
7
|
+
const positional = [];
|
|
8
|
+
const aliasMap = {};
|
|
9
|
+
|
|
10
|
+
for (const [name, def] of Object.entries(defs)) {
|
|
11
|
+
if (def.alias) {
|
|
12
|
+
aliasMap[def.alias] = name;
|
|
13
|
+
}
|
|
14
|
+
if (def.type === "boolean") {
|
|
15
|
+
flags[name] = false;
|
|
16
|
+
} else if (def.multiple) {
|
|
17
|
+
flags[name] = [];
|
|
18
|
+
} else {
|
|
19
|
+
flags[name] = undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let i = 0;
|
|
24
|
+
while (i < argv.length) {
|
|
25
|
+
const arg = argv[i];
|
|
26
|
+
|
|
27
|
+
if (!arg.startsWith("-")) {
|
|
28
|
+
positional.push(arg);
|
|
29
|
+
i++;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let key;
|
|
34
|
+
let inlineValue;
|
|
35
|
+
if (arg.includes("=")) {
|
|
36
|
+
const eqIdx = arg.indexOf("=");
|
|
37
|
+
key = arg.slice(0, eqIdx);
|
|
38
|
+
inlineValue = arg.slice(eqIdx + 1);
|
|
39
|
+
} else {
|
|
40
|
+
key = arg;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const stripped = key.replace(/^-{1,2}/, "");
|
|
44
|
+
const resolved = aliasMap[stripped] || stripped;
|
|
45
|
+
if (!(resolved in defs)) {
|
|
46
|
+
throw new ValidationError(`Unknown flag: ${key}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const def = defs[resolved];
|
|
50
|
+
if (def.type === "boolean") {
|
|
51
|
+
flags[resolved] = true;
|
|
52
|
+
i++;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let value = inlineValue;
|
|
57
|
+
if (value === undefined) {
|
|
58
|
+
i++;
|
|
59
|
+
if (i >= argv.length) {
|
|
60
|
+
throw new ValidationError(`Flag ${key} requires a value.`);
|
|
61
|
+
}
|
|
62
|
+
value = argv[i];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (def.multiple) {
|
|
66
|
+
flags[resolved].push(value);
|
|
67
|
+
} else {
|
|
68
|
+
flags[resolved] = value;
|
|
69
|
+
}
|
|
70
|
+
i++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { flags, positional };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function requireFlag(flags, name, label) {
|
|
77
|
+
const value = flags[name];
|
|
78
|
+
if (value === undefined || value === null || value === "") {
|
|
79
|
+
throw new ValidationError(`Missing required flag: --${label || name}`);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function validateChoice(value, choices, label) {
|
|
85
|
+
if (!choices.includes(value)) {
|
|
86
|
+
throw new ValidationError(
|
|
87
|
+
`Invalid value for --${label}: "${value}". Must be one of: ${choices.join(", ")}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function validatePattern(value, regex, label, hint) {
|
|
94
|
+
if (!regex.test(value)) {
|
|
95
|
+
throw new ValidationError(
|
|
96
|
+
`Invalid value for --${label}: "${value}". ${hint || `Must match ${regex}`}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = {
|
|
103
|
+
parseFlags,
|
|
104
|
+
requireFlag,
|
|
105
|
+
validateChoice,
|
|
106
|
+
validatePattern,
|
|
107
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { execFile } = require("child_process");
|
|
4
|
+
const { DependencyError } = require("./errors");
|
|
5
|
+
|
|
6
|
+
function findPowerShell() {
|
|
7
|
+
if (process.platform === "win32") {
|
|
8
|
+
return "powershell.exe";
|
|
9
|
+
}
|
|
10
|
+
return "pwsh";
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function runScript(scriptPath, params = {}, options = {}) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const psExe = findPowerShell();
|
|
16
|
+
const args = [
|
|
17
|
+
"-ExecutionPolicy",
|
|
18
|
+
"Bypass",
|
|
19
|
+
"-NoProfile",
|
|
20
|
+
"-NonInteractive",
|
|
21
|
+
"-File",
|
|
22
|
+
scriptPath,
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const [key, value] of Object.entries(params)) {
|
|
26
|
+
if (value === true) {
|
|
27
|
+
args.push(`-${key}`);
|
|
28
|
+
} else if (value === false || value === undefined || value === null) {
|
|
29
|
+
continue;
|
|
30
|
+
} else {
|
|
31
|
+
args.push(`-${key}`);
|
|
32
|
+
args.push(String(value));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
execFile(psExe, args, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
37
|
+
if (error && error.code === "ENOENT") {
|
|
38
|
+
reject(
|
|
39
|
+
new DependencyError(
|
|
40
|
+
`PowerShell not found. Ensure PowerShell is installed and available in PATH.\nTried: ${psExe}`
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const exitCode = error ? error.code || 1 : 0;
|
|
47
|
+
const result = {
|
|
48
|
+
exitCode: typeof exitCode === "number" ? exitCode : 1,
|
|
49
|
+
stdout: stdout || "",
|
|
50
|
+
stderr: stderr || "",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
if (!options.silent && result.stdout) {
|
|
54
|
+
process.stdout.write(result.stdout);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
resolve(result);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
findPowerShell,
|
|
64
|
+
runScript,
|
|
65
|
+
};
|
package/src/lib/scope.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const os = require("os");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const VALID_SCOPES = ["project", "user"];
|
|
7
|
+
|
|
8
|
+
const MANAGED_CLAUDE_SKILLS = [
|
|
9
|
+
"ops-backup",
|
|
10
|
+
"ops-ci-cd",
|
|
11
|
+
"ops-compliance",
|
|
12
|
+
"ops-container",
|
|
13
|
+
"ops-cost",
|
|
14
|
+
"ops-debug",
|
|
15
|
+
"ops-deploy",
|
|
16
|
+
"ops-discover",
|
|
17
|
+
"ops-incident",
|
|
18
|
+
"ops-infra-plan",
|
|
19
|
+
"ops-monitor",
|
|
20
|
+
"ops-parallel",
|
|
21
|
+
"ops-plan",
|
|
22
|
+
"ops-security-infra",
|
|
23
|
+
"ops-verify",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const MANAGED_CODEX_SKILLS = MANAGED_CLAUDE_SKILLS.map((name) => `sdtk-${name}`);
|
|
27
|
+
|
|
28
|
+
function defaultScope(runtime) {
|
|
29
|
+
return runtime === "claude" ? "project" : "user";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isProjectScopeSupported(runtime) {
|
|
33
|
+
return runtime === "claude";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveSkillsDir(runtime, scope, projectPath) {
|
|
37
|
+
if (runtime === "claude") {
|
|
38
|
+
if (scope === "user") {
|
|
39
|
+
return path.join(os.homedir(), ".claude", "skills");
|
|
40
|
+
}
|
|
41
|
+
return path.join(projectPath || process.cwd(), ".claude", "skills");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), ".codex");
|
|
45
|
+
return path.join(codexHome, "skills");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function managedSkillNames(runtime) {
|
|
49
|
+
return runtime === "claude" ? MANAGED_CLAUDE_SKILLS : MANAGED_CODEX_SKILLS;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
VALID_SCOPES,
|
|
54
|
+
defaultScope,
|
|
55
|
+
isProjectScopeSupported,
|
|
56
|
+
resolveSkillsDir,
|
|
57
|
+
managedSkillNames,
|
|
58
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
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
|
+
const REQUIRED_FILES = [
|
|
18
|
+
"toolkit/AGENTS.md",
|
|
19
|
+
"toolkit/install.ps1",
|
|
20
|
+
"toolkit/sdtk-spec.config.json",
|
|
21
|
+
"toolkit/sdtk-spec.config.profiles.example.json",
|
|
22
|
+
"toolkit/SDTKOPS_TOOLKIT.md",
|
|
23
|
+
"toolkit/scripts/install-claude-skills.ps1",
|
|
24
|
+
"toolkit/scripts/install-codex-skills.ps1",
|
|
25
|
+
"toolkit/scripts/uninstall-claude-skills.ps1",
|
|
26
|
+
"toolkit/scripts/uninstall-codex-skills.ps1",
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const FORBIDDEN_PREFIXES = [
|
|
30
|
+
"toolkit/skills-claude/",
|
|
31
|
+
"toolkit/templates/",
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const FORBIDDEN_PATHS = new Set([
|
|
35
|
+
"toolkit/skills/placeholder.md",
|
|
36
|
+
"toolkit/scripts/placeholder.md",
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
function loadManifest() {
|
|
40
|
+
if (!fs.existsSync(MANIFEST_PATH)) {
|
|
41
|
+
throw new IntegrityError(
|
|
42
|
+
`SDTK-OPS toolkit manifest not found: ${MANIFEST_PATH}\nRun "npm run build:payload" to sync toolkit assets.`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(fs.readFileSync(MANIFEST_PATH, "utf-8"));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw new IntegrityError(`Failed to parse SDTK-OPS toolkit manifest: ${error.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function hashFile(filePath) {
|
|
54
|
+
return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolvePayloadFile(relativePath) {
|
|
58
|
+
return path.join(ASSETS_DIR, relativePath);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function verify() {
|
|
62
|
+
const manifest = loadManifest();
|
|
63
|
+
if (!Array.isArray(manifest.files)) {
|
|
64
|
+
throw new IntegrityError("SDTK-OPS toolkit manifest has no files array.");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const errors = [];
|
|
68
|
+
const manifestPaths = new Set();
|
|
69
|
+
|
|
70
|
+
for (const entry of manifest.files) {
|
|
71
|
+
manifestPaths.add(entry.path);
|
|
72
|
+
|
|
73
|
+
if (FORBIDDEN_PATHS.has(entry.path)) {
|
|
74
|
+
errors.push(`FORBIDDEN PAYLOAD FILE: ${entry.path}`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (FORBIDDEN_PREFIXES.some((prefix) => entry.path.startsWith(prefix))) {
|
|
79
|
+
errors.push(`FORBIDDEN PAYLOAD PREFIX: ${entry.path}`);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const filePath = path.join(ASSETS_DIR, entry.path);
|
|
84
|
+
if (!fs.existsSync(filePath)) {
|
|
85
|
+
errors.push(`MISSING: ${entry.path}`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const actualHash = hashFile(filePath);
|
|
90
|
+
if (actualHash !== entry.sha256) {
|
|
91
|
+
errors.push(
|
|
92
|
+
`HASH MISMATCH: ${entry.path}\n expected: ${entry.sha256}\n actual: ${actualHash}`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const requiredPath of REQUIRED_FILES) {
|
|
98
|
+
if (!manifestPaths.has(requiredPath)) {
|
|
99
|
+
errors.push(`REQUIRED FILE MISSING FROM MANIFEST: ${requiredPath}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const skillManifestEntries = manifest.files.filter(
|
|
104
|
+
(entry) => entry.path.startsWith("toolkit/skills/") && entry.path.endsWith("/SKILL.md")
|
|
105
|
+
);
|
|
106
|
+
if (skillManifestEntries.length < 15) {
|
|
107
|
+
errors.push(`Expected at least 15 skill payload entries, found ${skillManifestEntries.length}.`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (errors.length > 0) {
|
|
111
|
+
throw new IntegrityError(`SDTK-OPS toolkit payload integrity check failed:\n${errors.join("\n")}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
ASSETS_DIR,
|
|
117
|
+
MANIFEST_PATH,
|
|
118
|
+
REQUIRED_FILES,
|
|
119
|
+
hashFile,
|
|
120
|
+
loadManifest,
|
|
121
|
+
resolvePayloadFile,
|
|
122
|
+
verify,
|
|
123
|
+
};
|