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.
Files changed (51) hide show
  1. package/README.md +146 -0
  2. package/assets/manifest/toolkit-bundle.manifest.json +187 -0
  3. package/assets/manifest/toolkit-bundle.sha256.txt +36 -0
  4. package/assets/toolkit/toolkit/AGENTS.md +65 -0
  5. package/assets/toolkit/toolkit/SDTKOPS_TOOLKIT.md +166 -0
  6. package/assets/toolkit/toolkit/install.ps1 +138 -0
  7. package/assets/toolkit/toolkit/scripts/install-claude-skills.ps1 +81 -0
  8. package/assets/toolkit/toolkit/scripts/install-codex-skills.ps1 +127 -0
  9. package/assets/toolkit/toolkit/scripts/uninstall-claude-skills.ps1 +65 -0
  10. package/assets/toolkit/toolkit/scripts/uninstall-codex-skills.ps1 +53 -0
  11. package/assets/toolkit/toolkit/sdtk-spec.config.json +6 -0
  12. package/assets/toolkit/toolkit/sdtk-spec.config.profiles.example.json +12 -0
  13. package/assets/toolkit/toolkit/skills/ops-backup/SKILL.md +93 -0
  14. package/assets/toolkit/toolkit/skills/ops-backup/references/backup-script-patterns.md +108 -0
  15. package/assets/toolkit/toolkit/skills/ops-ci-cd/SKILL.md +88 -0
  16. package/assets/toolkit/toolkit/skills/ops-ci-cd/references/pipeline-examples.md +113 -0
  17. package/assets/toolkit/toolkit/skills/ops-compliance/SKILL.md +105 -0
  18. package/assets/toolkit/toolkit/skills/ops-container/SKILL.md +95 -0
  19. package/assets/toolkit/toolkit/skills/ops-container/references/k8s-manifest-patterns.md +116 -0
  20. package/assets/toolkit/toolkit/skills/ops-cost/SKILL.md +88 -0
  21. package/assets/toolkit/toolkit/skills/ops-debug/SKILL.md +311 -0
  22. package/assets/toolkit/toolkit/skills/ops-debug/references/root-cause-tracing.md +138 -0
  23. package/assets/toolkit/toolkit/skills/ops-deploy/SKILL.md +102 -0
  24. package/assets/toolkit/toolkit/skills/ops-discover/SKILL.md +102 -0
  25. package/assets/toolkit/toolkit/skills/ops-incident/SKILL.md +113 -0
  26. package/assets/toolkit/toolkit/skills/ops-incident/references/communication-templates.md +34 -0
  27. package/assets/toolkit/toolkit/skills/ops-incident/references/postmortem-template.md +69 -0
  28. package/assets/toolkit/toolkit/skills/ops-incident/references/runbook-template.md +69 -0
  29. package/assets/toolkit/toolkit/skills/ops-infra-plan/SKILL.md +123 -0
  30. package/assets/toolkit/toolkit/skills/ops-infra-plan/references/iac-patterns.md +141 -0
  31. package/assets/toolkit/toolkit/skills/ops-monitor/SKILL.md +110 -0
  32. package/assets/toolkit/toolkit/skills/ops-monitor/references/alert-rules.md +80 -0
  33. package/assets/toolkit/toolkit/skills/ops-monitor/references/slo-templates.md +83 -0
  34. package/assets/toolkit/toolkit/skills/ops-parallel/SKILL.md +177 -0
  35. package/assets/toolkit/toolkit/skills/ops-plan/SKILL.md +169 -0
  36. package/assets/toolkit/toolkit/skills/ops-security-infra/SKILL.md +126 -0
  37. package/assets/toolkit/toolkit/skills/ops-security-infra/references/cicd-security-pipeline.md +55 -0
  38. package/assets/toolkit/toolkit/skills/ops-security-infra/references/security-headers.md +24 -0
  39. package/assets/toolkit/toolkit/skills/ops-verify/SKILL.md +180 -0
  40. package/bin/sdtk-ops.js +14 -0
  41. package/package.json +46 -0
  42. package/src/commands/generate.js +12 -0
  43. package/src/commands/help.js +53 -0
  44. package/src/commands/init.js +86 -0
  45. package/src/commands/runtime.js +201 -0
  46. package/src/index.js +65 -0
  47. package/src/lib/args.js +107 -0
  48. package/src/lib/errors.js +41 -0
  49. package/src/lib/powershell.js +65 -0
  50. package/src/lib/scope.js +58 -0
  51. package/src/lib/toolkit-payload.js +123 -0
@@ -0,0 +1,138 @@
1
+ param(
2
+ [Parameter(Mandatory = $true)]
3
+ [ValidateSet('codex', 'claude')]
4
+ [string]$Runtime,
5
+
6
+ [string]$ProjectPath,
7
+
8
+ [ValidateSet('project', 'user', '')]
9
+ [string]$Scope = '',
10
+
11
+ [switch]$Force,
12
+ [switch]$SkipRuntimeAssets,
13
+ [switch]$SkipSkills,
14
+ [switch]$Quiet
15
+ )
16
+
17
+ $ErrorActionPreference = 'Stop'
18
+ Set-StrictMode -Version Latest
19
+
20
+ function Copy-File {
21
+ param(
22
+ [Parameter(Mandatory = $true)][string]$SourcePath,
23
+ [Parameter(Mandatory = $true)][string]$DestinationPath,
24
+ [Parameter(Mandatory = $true)][bool]$Overwrite
25
+ )
26
+
27
+ if (-not (Test-Path -LiteralPath $SourcePath)) {
28
+ throw "Missing source file: $SourcePath"
29
+ }
30
+
31
+ if ((Test-Path -LiteralPath $DestinationPath) -and -not $Overwrite) {
32
+ if (-not $Quiet) {
33
+ Write-Warning "Already exists (skipping). Use -Force to overwrite: $DestinationPath"
34
+ }
35
+ return
36
+ }
37
+
38
+ $parent = Split-Path -Parent $DestinationPath
39
+ if ($parent -and -not (Test-Path -LiteralPath $parent)) {
40
+ New-Item -ItemType Directory -Force -Path $parent | Out-Null
41
+ }
42
+
43
+ Copy-Item -LiteralPath $SourcePath -Destination $DestinationPath -Force
44
+ }
45
+
46
+ function Resolve-OrCreateDirectory {
47
+ param(
48
+ [Parameter(Mandatory = $true)][string]$Path
49
+ )
50
+
51
+ if (-not (Test-Path -LiteralPath $Path)) {
52
+ New-Item -ItemType Directory -Force -Path $Path | Out-Null
53
+ }
54
+
55
+ return (Resolve-Path -LiteralPath $Path).Path
56
+ }
57
+
58
+ $toolkitRoot = (Resolve-Path -LiteralPath $PSScriptRoot).Path
59
+ if (-not $ProjectPath) {
60
+ $ProjectPath = (Get-Location).Path
61
+ }
62
+ $projectRoot = Resolve-OrCreateDirectory -Path $ProjectPath
63
+
64
+ if (-not $Scope) {
65
+ $Scope = if ($Runtime -eq 'claude') { 'project' } else { 'user' }
66
+ }
67
+
68
+ if ($Runtime -eq 'codex' -and $Scope -eq 'project') {
69
+ Write-Error "Codex does not support project-local skills. Use -Scope user instead."
70
+ exit 1
71
+ }
72
+
73
+ if ($SkipSkills) {
74
+ if (-not $Quiet) {
75
+ Write-Warning "-SkipSkills is deprecated. Use -SkipRuntimeAssets instead."
76
+ }
77
+ $SkipRuntimeAssets = $true
78
+ }
79
+
80
+ if (-not $Quiet) {
81
+ Write-Host "SDTK-OPS installer"
82
+ Write-Host " Runtime: $Runtime"
83
+ Write-Host " Scope: $Scope"
84
+ Write-Host " Project: $projectRoot"
85
+ Write-Host ""
86
+ }
87
+
88
+ $sharedFiles = @(
89
+ 'AGENTS.md',
90
+ 'sdtk-spec.config.json',
91
+ 'sdtk-spec.config.profiles.example.json'
92
+ )
93
+
94
+ foreach ($fileName in $sharedFiles) {
95
+ Copy-File `
96
+ -SourcePath (Join-Path $toolkitRoot $fileName) `
97
+ -DestinationPath (Join-Path $projectRoot $fileName) `
98
+ -Overwrite ([bool]$Force)
99
+
100
+ if (-not $Quiet) {
101
+ Write-Host " [OK] $fileName"
102
+ }
103
+ }
104
+
105
+ if (-not $SkipRuntimeAssets) {
106
+ $scriptPath = if ($Runtime -eq 'claude') {
107
+ Join-Path $toolkitRoot 'scripts/install-claude-skills.ps1'
108
+ } else {
109
+ Join-Path $toolkitRoot 'scripts/install-codex-skills.ps1'
110
+ }
111
+
112
+ if (-not (Test-Path -LiteralPath $scriptPath)) {
113
+ throw "Missing runtime installer: $scriptPath"
114
+ }
115
+
116
+ if (-not $Quiet) {
117
+ Write-Host ""
118
+ Write-Host "Installing $Runtime runtime assets..."
119
+ }
120
+
121
+ $params = @{
122
+ Scope = $Scope
123
+ Quiet = [bool]$Quiet
124
+ }
125
+ if ($Runtime -eq 'claude') {
126
+ $params.ProjectPath = $projectRoot
127
+ }
128
+ if ($Force) {
129
+ $params.Force = $true
130
+ }
131
+
132
+ & $scriptPath @params
133
+ }
134
+
135
+ if (-not $Quiet) {
136
+ Write-Host ""
137
+ Write-Host "SDTK-OPS installation complete."
138
+ }
@@ -0,0 +1,81 @@
1
+ param(
2
+ [string]$ProjectPath,
3
+ [ValidateSet('project', 'user')]
4
+ [string]$Scope = 'project',
5
+ [switch]$Force,
6
+ [switch]$Quiet
7
+ )
8
+
9
+ $ErrorActionPreference = 'Stop'
10
+ Set-StrictMode -Version Latest
11
+
12
+ $toolkitRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
13
+ $skillsSource = Join-Path $toolkitRoot 'skills'
14
+ if (-not (Test-Path -LiteralPath $skillsSource)) {
15
+ throw "Missing toolkit skills source directory: $skillsSource"
16
+ }
17
+
18
+ $managedSkills = @(Get-ChildItem -LiteralPath $skillsSource -Directory | Sort-Object Name)
19
+ if ($managedSkills.Count -ne 15) {
20
+ throw "Expected 15 managed SDTK-OPS skills but found $($managedSkills.Count)."
21
+ }
22
+
23
+ if ($Scope -eq 'user') {
24
+ $skillsDest = Join-Path $HOME '.claude/skills'
25
+ } else {
26
+ if (-not $ProjectPath) {
27
+ throw "ProjectPath is required for Claude project scope installation."
28
+ }
29
+ if (-not (Test-Path -LiteralPath $ProjectPath)) {
30
+ New-Item -ItemType Directory -Force -Path $ProjectPath | Out-Null
31
+ }
32
+ $projectRoot = (Resolve-Path -LiteralPath $ProjectPath).Path
33
+ $skillsDest = Join-Path $projectRoot '.claude/skills'
34
+ }
35
+
36
+ New-Item -ItemType Directory -Force -Path $skillsDest | Out-Null
37
+
38
+ $copied = 0
39
+ $skipped = 0
40
+ foreach ($skillDir in $managedSkills) {
41
+ $skillFile = Join-Path $skillDir.FullName 'SKILL.md'
42
+ if (-not (Test-Path -LiteralPath $skillFile)) {
43
+ throw "Managed skill is missing SKILL.md: $($skillDir.FullName)"
44
+ }
45
+
46
+ $destDir = Join-Path $skillsDest $skillDir.Name
47
+ if (Test-Path -LiteralPath $destDir) {
48
+ if (-not $Force) {
49
+ $skipped++
50
+ if (-not $Quiet) {
51
+ Write-Warning "Skill already installed (skipping). Use -Force to overwrite: $($skillDir.Name)"
52
+ }
53
+ continue
54
+ }
55
+ Remove-Item -LiteralPath $destDir -Recurse -Force
56
+ }
57
+
58
+ Copy-Item -LiteralPath $skillDir.FullName -Destination $destDir -Recurse -Force
59
+ $copied++
60
+ if (-not $Quiet) {
61
+ Write-Host "Installed: $($skillDir.Name)"
62
+ }
63
+ }
64
+
65
+ $installedNow = @(
66
+ Get-ChildItem -LiteralPath $skillsDest -Directory -ErrorAction SilentlyContinue |
67
+ Where-Object { $managedSkills.Name -contains $_.Name } |
68
+ Select-Object -ExpandProperty Name
69
+ )
70
+ if ($installedNow.Count -ne 15) {
71
+ throw "Claude skill install incomplete. Expected 15 managed skills at $skillsDest but found $($installedNow.Count)."
72
+ }
73
+
74
+ if (-not $Quiet) {
75
+ Write-Host ""
76
+ Write-Host "Install summary:"
77
+ Write-Host "- Scope: $Scope"
78
+ Write-Host "- Destination: $skillsDest"
79
+ Write-Host "- Copied: $copied"
80
+ Write-Host "- Skipped: $skipped"
81
+ }
@@ -0,0 +1,127 @@
1
+ param(
2
+ [ValidateSet('project', 'user')]
3
+ [string]$Scope = 'user',
4
+ [switch]$Force,
5
+ [switch]$Quiet
6
+ )
7
+
8
+ if ($Scope -eq 'project') {
9
+ Write-Error "Codex does not support project-local skills. Use -Scope user instead."
10
+ exit 1
11
+ }
12
+
13
+ $ErrorActionPreference = 'Stop'
14
+ Set-StrictMode -Version Latest
15
+
16
+ function Write-Utf8NoBomFile {
17
+ param(
18
+ [Parameter(Mandatory = $true)][string]$Path,
19
+ [Parameter(Mandatory = $true)][string]$Content
20
+ )
21
+
22
+ $utf8NoBom = New-Object System.Text.UTF8Encoding($false)
23
+ [System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom)
24
+ }
25
+
26
+ function Rewrite-SkillFrontmatterName {
27
+ param(
28
+ [Parameter(Mandatory = $true)][string]$SkillFilePath,
29
+ [Parameter(Mandatory = $true)][string]$ExpectedName
30
+ )
31
+
32
+ $content = Get-Content -LiteralPath $SkillFilePath -Raw -Encoding UTF8
33
+ $newline = if ($content.Contains("`r`n")) { "`r`n" } else { "`n" }
34
+ $match = [regex]::Match(
35
+ $content,
36
+ '\A(?<prefix>\s*(?:<!--[\s\S]*?-->\s*)?)---\r?\n(?<frontmatter>[\s\S]*?)\r?\n---(?<suffix>[\s\S]*)\z'
37
+ )
38
+ if (-not $match.Success) {
39
+ throw "Skill file is missing a valid YAML frontmatter block: $SkillFilePath"
40
+ }
41
+
42
+ $frontmatter = $match.Groups['frontmatter'].Value
43
+ if ($frontmatter -notmatch '(?m)^name:\s*(?<name>[^\r\n]+)\s*$') {
44
+ throw "Skill file is missing a frontmatter name field: $SkillFilePath"
45
+ }
46
+
47
+ $updatedFrontmatter = [regex]::Replace(
48
+ $frontmatter,
49
+ '(?m)^name:\s*[^\r\n]+\s*$',
50
+ "name: $ExpectedName",
51
+ 1
52
+ )
53
+
54
+ $updatedContent = $match.Groups['prefix'].Value + '---' + $newline + $updatedFrontmatter + $newline + '---' + $match.Groups['suffix'].Value
55
+ Write-Utf8NoBomFile -Path $SkillFilePath -Content $updatedContent
56
+ }
57
+
58
+ $toolkitRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
59
+ $skillsSource = Join-Path $toolkitRoot 'skills'
60
+ if (-not (Test-Path -LiteralPath $skillsSource)) {
61
+ throw "Missing toolkit skills source directory: $skillsSource"
62
+ }
63
+
64
+ $managedSkills = @(Get-ChildItem -LiteralPath $skillsSource -Directory | Sort-Object Name)
65
+ if ($managedSkills.Count -ne 15) {
66
+ throw "Expected 15 managed SDTK-OPS skills but found $($managedSkills.Count)."
67
+ }
68
+
69
+ $codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME '.codex' }
70
+ $skillsDest = Join-Path $codexHome 'skills'
71
+ New-Item -ItemType Directory -Force -Path $skillsDest | Out-Null
72
+
73
+ $copied = 0
74
+ $skipped = 0
75
+ foreach ($skillDir in $managedSkills) {
76
+ $skillFile = Join-Path $skillDir.FullName 'SKILL.md'
77
+ if (-not (Test-Path -LiteralPath $skillFile)) {
78
+ throw "Managed skill is missing SKILL.md: $($skillDir.FullName)"
79
+ }
80
+
81
+ $destName = "sdtk-$($skillDir.Name)"
82
+ $destDir = Join-Path $skillsDest $destName
83
+ if (Test-Path -LiteralPath $destDir) {
84
+ if (-not $Force) {
85
+ $skipped++
86
+ if (-not $Quiet) {
87
+ Write-Warning "Skill already installed (skipping). Use -Force to overwrite: $destName"
88
+ }
89
+ continue
90
+ }
91
+ Remove-Item -LiteralPath $destDir -Recurse -Force
92
+ }
93
+
94
+ Copy-Item -LiteralPath $skillDir.FullName -Destination $destDir -Recurse -Force
95
+ try {
96
+ Rewrite-SkillFrontmatterName -SkillFilePath (Join-Path $destDir 'SKILL.md') -ExpectedName $destName
97
+ } catch {
98
+ if (Test-Path -LiteralPath $destDir) {
99
+ Remove-Item -LiteralPath $destDir -Recurse -Force
100
+ }
101
+ throw
102
+ }
103
+
104
+ $copied++
105
+ if (-not $Quiet) {
106
+ Write-Host "Installed: $destName"
107
+ }
108
+ }
109
+
110
+ $expectedNames = $managedSkills.Name | ForEach-Object { "sdtk-$_" }
111
+ $installedNow = @(
112
+ Get-ChildItem -LiteralPath $skillsDest -Directory -ErrorAction SilentlyContinue |
113
+ Where-Object { $expectedNames -contains $_.Name } |
114
+ Select-Object -ExpandProperty Name
115
+ )
116
+ if ($installedNow.Count -ne 15) {
117
+ throw "Codex skill install incomplete. Expected 15 managed skills at $skillsDest but found $($installedNow.Count)."
118
+ }
119
+
120
+ if (-not $Quiet) {
121
+ Write-Host ""
122
+ Write-Host "Install summary:"
123
+ Write-Host "- Scope: user"
124
+ Write-Host "- Destination: $skillsDest"
125
+ Write-Host "- Copied: $copied"
126
+ Write-Host "- Skipped: $skipped"
127
+ }
@@ -0,0 +1,65 @@
1
+ param(
2
+ [string]$ProjectPath,
3
+ [ValidateSet('project', 'user')]
4
+ [string]$Scope = 'project',
5
+ [switch]$All,
6
+ [switch]$Quiet
7
+ )
8
+
9
+ $ErrorActionPreference = 'Stop'
10
+ Set-StrictMode -Version Latest
11
+
12
+ $toolkitRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
13
+ $skillsSource = Join-Path $toolkitRoot 'skills'
14
+ if (-not (Test-Path -LiteralPath $skillsSource)) {
15
+ throw "Missing toolkit skills source directory: $skillsSource"
16
+ }
17
+
18
+ $managedSkillNames = @(
19
+ Get-ChildItem -LiteralPath $skillsSource -Directory | Sort-Object Name | Select-Object -ExpandProperty Name
20
+ )
21
+ if ($managedSkillNames.Count -ne 15) {
22
+ throw "Expected 15 managed SDTK-OPS skills but found $($managedSkillNames.Count)."
23
+ }
24
+
25
+ if ($Scope -eq 'user') {
26
+ $skillsDest = Join-Path $HOME '.claude/skills'
27
+ } else {
28
+ if (-not $ProjectPath) {
29
+ throw "ProjectPath is required for Claude project scope uninstall."
30
+ }
31
+ if (-not (Test-Path -LiteralPath $ProjectPath)) {
32
+ New-Item -ItemType Directory -Force -Path $ProjectPath | Out-Null
33
+ }
34
+ $projectRoot = (Resolve-Path -LiteralPath $ProjectPath).Path
35
+ $skillsDest = Join-Path $projectRoot '.claude/skills'
36
+ }
37
+
38
+ if (-not (Test-Path -LiteralPath $skillsDest)) {
39
+ if (-not $Quiet) {
40
+ Write-Host "Claude skills directory not found: $skillsDest"
41
+ }
42
+ exit 0
43
+ }
44
+
45
+ $removed = New-Object System.Collections.Generic.List[string]
46
+ $missing = New-Object System.Collections.Generic.List[string]
47
+ foreach ($name in $managedSkillNames) {
48
+ $dest = Join-Path $skillsDest $name
49
+ if (-not (Test-Path -LiteralPath $dest)) {
50
+ $missing.Add($name) | Out-Null
51
+ continue
52
+ }
53
+
54
+ Remove-Item -LiteralPath $dest -Recurse -Force
55
+ $removed.Add($name) | Out-Null
56
+ }
57
+
58
+ if (-not $Quiet) {
59
+ Write-Host ""
60
+ Write-Host "Uninstall summary:"
61
+ Write-Host "- Scope: $Scope"
62
+ Write-Host "- Destination: $skillsDest"
63
+ Write-Host "- Removed: $($removed.Count)"
64
+ Write-Host "- Missing/Skipped: $($missing.Count)"
65
+ }
@@ -0,0 +1,53 @@
1
+ param(
2
+ [switch]$All,
3
+ [switch]$Quiet
4
+ )
5
+
6
+ $ErrorActionPreference = 'Stop'
7
+ Set-StrictMode -Version Latest
8
+
9
+ $toolkitRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
10
+ $skillsSource = Join-Path $toolkitRoot 'skills'
11
+ if (-not (Test-Path -LiteralPath $skillsSource)) {
12
+ throw "Missing toolkit skills source directory: $skillsSource"
13
+ }
14
+
15
+ $managedSkillNames = @(
16
+ Get-ChildItem -LiteralPath $skillsSource -Directory |
17
+ Sort-Object Name |
18
+ ForEach-Object { "sdtk-$($_.Name)" }
19
+ )
20
+ if ($managedSkillNames.Count -ne 15) {
21
+ throw "Expected 15 managed SDTK-OPS skills but found $($managedSkillNames.Count)."
22
+ }
23
+
24
+ $codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME '.codex' }
25
+ $skillsDest = Join-Path $codexHome 'skills'
26
+ if (-not (Test-Path -LiteralPath $skillsDest)) {
27
+ if (-not $Quiet) {
28
+ Write-Host "Codex skills directory not found: $skillsDest"
29
+ }
30
+ exit 0
31
+ }
32
+
33
+ $removed = New-Object System.Collections.Generic.List[string]
34
+ $missing = New-Object System.Collections.Generic.List[string]
35
+ foreach ($name in $managedSkillNames) {
36
+ $dest = Join-Path $skillsDest $name
37
+ if (-not (Test-Path -LiteralPath $dest)) {
38
+ $missing.Add($name) | Out-Null
39
+ continue
40
+ }
41
+
42
+ Remove-Item -LiteralPath $dest -Recurse -Force
43
+ $removed.Add($name) | Out-Null
44
+ }
45
+
46
+ if (-not $Quiet) {
47
+ Write-Host ""
48
+ Write-Host "Uninstall summary:"
49
+ Write-Host "- Scope: user"
50
+ Write-Host "- Destination: $skillsDest"
51
+ Write-Host "- Removed: $($removed.Count)"
52
+ Write-Host "- Missing/Skipped: $($missing.Count)"
53
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "toolkit": "SDTK-OPS",
3
+ "version": "1.0.0",
4
+ "skills_path": "toolkit/skills",
5
+ "skill_prefix": "ops-"
6
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$comment": "Example profiles config for SDTK-OPS. Copy to sdtk-spec.config.profiles.json and customize for your internal runtime targets.",
3
+ "profiles": {
4
+ "default": {
5
+ "stack": {
6
+ "backend": "UPDATE_ME",
7
+ "frontend": "UPDATE_ME",
8
+ "database": "UPDATE_ME"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,93 @@
1
+ <!-- Based on agency-agents by AgentLand Contributors (MIT License, 2025). Adapted for SDTK-OPS. -->
2
+ ---
3
+ name: ops-backup
4
+ description: Backup and disaster recovery. Use when designing backup strategies, disaster recovery plans, or restore procedures -- covers RTO/RPO definition, backup verification, and cross-region replication.
5
+ ---
6
+
7
+ # Ops Backup
8
+
9
+ ## Overview
10
+
11
+ Backups are only valuable if they can be restored inside the time and data-loss limits the business expects. Define recovery targets first, automate execution, store copies away from the primary failure zone, and test restores on a schedule.
12
+
13
+ ## The Iron Law
14
+
15
+ ```
16
+ NO BACKUP STRATEGY IS COMPLETE UNTIL A RESTORE HAS BEEN TESTED
17
+ ```
18
+
19
+ ## RTO And RPO Framework
20
+
21
+ - **RTO**
22
+ - recovery time objective
23
+ - how long the service can be unavailable
24
+ - **RPO**
25
+ - recovery point objective
26
+ - how much data loss is acceptable
27
+
28
+ ## Service Tiers
29
+
30
+ | Tier | RPO | RTO | Replication Scope |
31
+ |------|-----|-----|-------------------|
32
+ | Critical | under 5 min | under 30 min | cross-region |
33
+ | Important | under 1 hour | under 4 hours | cross-availability-zone |
34
+ | Standard | under 24 hours | under 24 hours | same region |
35
+
36
+ ## Backup Strategies
37
+
38
+ | Strategy | When To Use | Typical RPO | Storage Cost |
39
+ |----------|-------------|-------------|--------------|
40
+ | Full | small datasets or periodic baselines | longer | high |
41
+ | Incremental | regular backups with storage efficiency | medium | low |
42
+ | Differential | simpler restore chain with moderate storage use | medium | medium |
43
+ | Continuous replication | critical systems with near-real-time recovery needs | very low | high |
44
+
45
+ ## <HARD-GATE>
46
+
47
+ Do not claim backup coverage until all are true:
48
+ - execution is automated
49
+ - backup data is encrypted at rest with GPG, KMS, or equivalent
50
+ - an off-site copy exists in a different region or failure domain
51
+ - retention and cleanup are enforced automatically
52
+ - restore testing happens at least quarterly with documented results
53
+ - monitoring alerts fire on backup failure
54
+
55
+ ## Monthly Restore Drill
56
+
57
+ Run this procedure:
58
+ 1. select a recent backup at random
59
+ 2. restore it into an isolated environment
60
+ 3. verify data integrity
61
+ 4. verify the application can use the restored data
62
+ 5. record total restore time and any issues
63
+ 6. update the runbook if the drill exposed gaps
64
+
65
+ ## Disaster Recovery Plan Template
66
+
67
+ | Component | Primary Region | DR Region | Failover Method | RTO |
68
+ |-----------|----------------|-----------|-----------------|-----|
69
+ | api | primary-a | secondary-a | redeploy from immutable artifact | 30 min |
70
+ | database | primary-a | secondary-a | replica promote or restore | 30 min |
71
+ | object storage | primary-a | secondary-a | replicated bucket failover | 60 min |
72
+
73
+ ## Script Patterns
74
+
75
+ Use `./references/backup-script-patterns.md` for backup, encryption, verification, upload, and retention patterns. Treat provider-specific storage commands as replaceable implementation details.
76
+
77
+ ## Common Mistakes
78
+
79
+ | Mistake | Why it fails |
80
+ |---------|--------------|
81
+ | Never test restores | Teams discover backup gaps during the outage |
82
+ | Keep backup copies in the same failure domain | Regional or account-level failure destroys recovery path |
83
+ | Skip encryption | Backup media becomes a security incident |
84
+ | No retention policy | Storage grows without limit and old data stays forever |
85
+ | Manual backup execution | The process fails precisely when people are busiest |
86
+
87
+ ## Execution Handoff
88
+
89
+ After backup design is approved:
90
+ - implement storage and retention changes through `ops-infra-plan`
91
+ - validate restore evidence with `ops-verify`
92
+ - use `ops-monitor` so backup failures page the right team
93
+
@@ -0,0 +1,108 @@
1
+ <!-- Based on agency-agents by AgentLand Contributors (MIT License, 2025). Adapted for SDTK-OPS. -->
2
+
3
+ # Backup Script Patterns
4
+
5
+ ## Shell Pattern
6
+
7
+ ```bash
8
+ #!/usr/bin/env bash
9
+ set -euo pipefail
10
+
11
+ BACKUP_ROOT="${BACKUP_ROOT:-/backups}"
12
+ LOG_FILE="${LOG_FILE:-/var/log/backup.log}"
13
+ RETENTION_DAYS="${RETENTION_DAYS:-30}"
14
+ ENCRYPTION_KEY="${ENCRYPTION_KEY:?Set ENCRYPTION_KEY to a passphrase file or key reference}"
15
+ OBJECT_STORE_TARGET="${OBJECT_STORE_TARGET:?Set OBJECT_STORE_TARGET to the remote backup location}"
16
+ NOTIFICATION_WEBHOOK="${NOTIFICATION_WEBHOOK:?Set NOTIFICATION_WEBHOOK through the environment}"
17
+
18
+ log() {
19
+ echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
20
+ }
21
+
22
+ notify_failure() {
23
+ local message="$1"
24
+ curl -X POST -H 'Content-type: application/json' \
25
+ --data "{\"text\":\"Backup failed: $message\"}" \
26
+ "$NOTIFICATION_WEBHOOK"
27
+ }
28
+
29
+ handle_error() {
30
+ local error_message="$1"
31
+ log "ERROR: $error_message"
32
+ notify_failure "$error_message"
33
+ exit 1
34
+ }
35
+
36
+ backup_database() {
37
+ local db_name="$1"
38
+ local backup_file="${BACKUP_ROOT}/db/${db_name}_$(date +%Y%m%d_%H%M%S).sql.gz"
39
+
40
+ mkdir -p "$(dirname "$backup_file")"
41
+
42
+ if ! pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$db_name" | gzip > "$backup_file"; then
43
+ handle_error "Database backup failed for $db_name"
44
+ fi
45
+
46
+ if ! gpg --cipher-algo AES256 --compress-algo 1 --s2k-mode 3 \
47
+ --s2k-digest-algo SHA512 --s2k-count 65536 --symmetric \
48
+ --passphrase-file "$ENCRYPTION_KEY" "$backup_file"; then
49
+ handle_error "Database backup encryption failed for $db_name"
50
+ fi
51
+
52
+ rm "$backup_file"
53
+ log "Database backup completed for $db_name"
54
+ }
55
+
56
+ backup_files() {
57
+ local source_dir="$1"
58
+ local backup_name="$2"
59
+ local backup_file="${BACKUP_ROOT}/files/${backup_name}_$(date +%Y%m%d_%H%M%S).tar.gz.gpg"
60
+
61
+ mkdir -p "$(dirname "$backup_file")"
62
+
63
+ if ! tar -czf - -C "$source_dir" . | \
64
+ gpg --cipher-algo AES256 --compress-algo 0 --s2k-mode 3 \
65
+ --s2k-digest-algo SHA512 --s2k-count 65536 --symmetric \
66
+ --passphrase-file "$ENCRYPTION_KEY" \
67
+ --output "$backup_file"; then
68
+ handle_error "File backup failed for $source_dir"
69
+ fi
70
+
71
+ log "File backup completed for $source_dir"
72
+ }
73
+
74
+ verify_backup() {
75
+ local backup_file="$1"
76
+
77
+ if ! gpg --quiet --batch --passphrase-file "$ENCRYPTION_KEY" \
78
+ --decrypt "$backup_file" > /dev/null 2>&1; then
79
+ handle_error "Backup integrity check failed for $backup_file"
80
+ fi
81
+
82
+ log "Backup integrity verified for $backup_file"
83
+ }
84
+
85
+ upload_backup() {
86
+ local local_file="$1"
87
+ local remote_path="$2"
88
+
89
+ if ! cloud-storage-copy "$local_file" "${OBJECT_STORE_TARGET}/${remote_path}"; then
90
+ handle_error "Remote upload failed for $local_file"
91
+ fi
92
+
93
+ log "Remote upload completed for $local_file"
94
+ }
95
+
96
+ cleanup_old_backups() {
97
+ find "$BACKUP_ROOT" -name "*.gpg" -mtime +"$RETENTION_DAYS" -delete
98
+ log "Cleanup completed"
99
+ }
100
+ ```
101
+
102
+ ## Pattern Notes
103
+
104
+ - keep notification endpoints in environment variables, never in version control
105
+ - replace `cloud-storage-copy` with the provider-specific upload command used by your platform
106
+ - verify every backup after encryption and before declaring success
107
+ - keep restore testing separate from production systems
108
+