its-magic 0.1.2-10
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/.cursor/agents/curator.mdc +21 -0
- package/.cursor/agents/dev.mdc +20 -0
- package/.cursor/agents/po.mdc +19 -0
- package/.cursor/agents/qa.mdc +19 -0
- package/.cursor/agents/release.mdc +19 -0
- package/.cursor/agents/tech-lead.mdc +21 -0
- package/.cursor/commands/architecture.md +29 -0
- package/.cursor/commands/auto.md +27 -0
- package/.cursor/commands/discovery.md +27 -0
- package/.cursor/commands/execute.md +32 -0
- package/.cursor/commands/intake.md +28 -0
- package/.cursor/commands/map-codebase.md +25 -0
- package/.cursor/commands/milestone-complete.md +24 -0
- package/.cursor/commands/milestone-start.md +26 -0
- package/.cursor/commands/pause.md +25 -0
- package/.cursor/commands/phase-context.md +25 -0
- package/.cursor/commands/plan-verify.md +26 -0
- package/.cursor/commands/qa.md +28 -0
- package/.cursor/commands/quick.md +24 -0
- package/.cursor/commands/refresh-context.md +26 -0
- package/.cursor/commands/release.md +29 -0
- package/.cursor/commands/research.md +28 -0
- package/.cursor/commands/resume.md +26 -0
- package/.cursor/commands/sprint-plan.md +30 -0
- package/.cursor/commands/verify-work.md +25 -0
- package/.cursor/hooks/README.md +13 -0
- package/.cursor/hooks/hook.py +197 -0
- package/.cursor/hooks.json +26 -0
- package/.cursor/plans/cursor-gsd-team-kit_8cfee9b8.plan.md +57 -0
- package/.cursor/remote.json +18 -0
- package/.cursor/rules/core.mdc +18 -0
- package/.cursor/rules/escalation.mdc +11 -0
- package/.cursor/rules/handoffs.mdc +10 -0
- package/.cursor/rules/quality.mdc +15 -0
- package/.cursor/scratchpad.md +34 -0
- package/.cursor/skills/its-magic/SKILL.md +39 -0
- package/.cursor/skills/its-magic/templates/acceptance.json +10 -0
- package/.cursor/skills/its-magic/templates/acceptance.md +7 -0
- package/.cursor/skills/its-magic/templates/architecture.json +11 -0
- package/.cursor/skills/its-magic/templates/architecture.md +14 -0
- package/.cursor/skills/its-magic/templates/decision.json +14 -0
- package/.cursor/skills/its-magic/templates/decision.md +19 -0
- package/.cursor/skills/its-magic/templates/handoff.json +6 -0
- package/.cursor/skills/its-magic/templates/handoff.md +12 -0
- package/.cursor/skills/its-magic/templates/milestone.json +7 -0
- package/.cursor/skills/its-magic/templates/phase-context.json +6 -0
- package/.cursor/skills/its-magic/templates/plan-verify.json +11 -0
- package/.cursor/skills/its-magic/templates/sprint.json +6 -0
- package/.cursor/skills/its-magic/templates/sprint.md +11 -0
- package/.cursor/skills/its-magic/templates/story.json +9 -0
- package/.cursor/skills/its-magic/templates/story.md +15 -0
- package/.cursor/skills/its-magic/templates/uat.json +15 -0
- package/.github/workflows/ci.yml +49 -0
- package/.github/workflows/deploy.yml +56 -0
- package/README.md +755 -0
- package/bin/its-magic.js +86 -0
- package/decisions/DEC-0001.md +21 -0
- package/decisions/DEC-0002.md +21 -0
- package/docs/engineering/architecture.md +354 -0
- package/docs/engineering/codebase-map.md +14 -0
- package/docs/engineering/context/phase-template.json +6 -0
- package/docs/engineering/decisions.md +6 -0
- package/docs/engineering/dependencies.json +5 -0
- package/docs/engineering/research.md +11 -0
- package/docs/engineering/runbook.md +32 -0
- package/docs/engineering/state.md +33 -0
- package/docs/product/acceptance.md +6 -0
- package/docs/product/backlog.md +7 -0
- package/docs/product/vision.md +46 -0
- package/handoffs/dev_to_qa.md +8 -0
- package/handoffs/po_to_tl.md +8 -0
- package/handoffs/qa_to_dev.md +6 -0
- package/handoffs/release_notes.md +14 -0
- package/handoffs/resume_brief.md +8 -0
- package/handoffs/tl_to_dev.md +7 -0
- package/installer.ps1 +189 -0
- package/installer.py +195 -0
- package/installer.sh +201 -0
- package/milestones/M0001/milestone.json +7 -0
- package/milestones/M0001/phases.json +9 -0
- package/milestones/M0001/progress.md +3 -0
- package/milestones/M0001/summary.md +3 -0
- package/package.json +38 -0
- package/scripts/generate-release-notes.ps1 +74 -0
- package/scripts/generate-release-notes.sh +63 -0
- package/scripts/release-all.ps1 +423 -0
- package/scripts/release-all.sh +226 -0
- package/sprints/S0001/plan-verify.json +5 -0
- package/sprints/S0001/progress.md +4 -0
- package/sprints/S0001/qa-findings.md +113 -0
- package/sprints/S0001/sprint.md +70 -0
- package/sprints/S0001/summary.md +46 -0
- package/sprints/S0001/tasks.md +35 -0
- package/sprints/S0001/uat.json +8 -0
- package/sprints/S0001/uat.md +8 -0
- package/sprints/quick/Q0001/summary.md +3 -0
- package/sprints/quick/Q0001/task.json +6 -0
package/installer.ps1
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Param(
|
|
2
|
+
[string]$Target,
|
|
3
|
+
[ValidateSet("missing","overwrite","interactive")]
|
|
4
|
+
[string]$Mode,
|
|
5
|
+
[switch]$Backup,
|
|
6
|
+
[switch]$Create
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = "Stop"
|
|
10
|
+
|
|
11
|
+
function Normalize-PathSafe($Path) {
|
|
12
|
+
return [System.IO.Path]::GetFullPath($Path)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Ensure-Parent($Path) {
|
|
16
|
+
$parent = Split-Path -Parent $Path
|
|
17
|
+
if ($parent -and -not (Test-Path $parent)) {
|
|
18
|
+
New-Item -ItemType Directory -Path $parent -Force | Out-Null
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function List-SourceFiles($SourceRoot, $IncludePaths) {
|
|
23
|
+
$files = New-Object System.Collections.Generic.List[string]
|
|
24
|
+
foreach ($rel in $IncludePaths) {
|
|
25
|
+
$src = Join-Path $SourceRoot $rel
|
|
26
|
+
if (Test-Path $src -PathType Leaf) {
|
|
27
|
+
$files.Add($rel)
|
|
28
|
+
} elseif (Test-Path $src -PathType Container) {
|
|
29
|
+
Get-ChildItem -Path $src -Recurse -File | ForEach-Object {
|
|
30
|
+
$relPath = $_.FullName.Substring($SourceRoot.Length).TrimStart("\","/")
|
|
31
|
+
$files.Add($relPath)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return $files | Select-Object -Unique | Sort-Object
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function Backup-Files($TargetRoot, $RelPaths) {
|
|
39
|
+
$timestamp = (Get-Date).ToUniversalTime().ToString("yyyyMMdd-HHmmssZ")
|
|
40
|
+
$backupRoot = Join-Path $TargetRoot ("backups\" + $timestamp)
|
|
41
|
+
foreach ($rel in $RelPaths) {
|
|
42
|
+
$src = Join-Path $TargetRoot $rel
|
|
43
|
+
if (Test-Path $src -PathType Leaf) {
|
|
44
|
+
$dst = Join-Path $backupRoot $rel
|
|
45
|
+
Ensure-Parent $dst
|
|
46
|
+
Copy-Item -Path $src -Destination $dst -Force
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return $backupRoot
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function Choose-Mode {
|
|
53
|
+
Write-Host "Select install mode:"
|
|
54
|
+
Write-Host "1) missing-only (copy only files that do not exist)"
|
|
55
|
+
Write-Host "2) overwrite-all (replace existing files)"
|
|
56
|
+
Write-Host "3) interactive (prompt per file)"
|
|
57
|
+
$choice = Read-Host "Enter 1, 2, or 3"
|
|
58
|
+
switch ($choice) {
|
|
59
|
+
"1" { return "missing" }
|
|
60
|
+
"2" { return "overwrite" }
|
|
61
|
+
Default { return "interactive" }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function Prompt-YesNo($Label, $Default = $false) {
|
|
66
|
+
$suffix = if ($Default) { "Y/n" } else { "y/N" }
|
|
67
|
+
$value = (Read-Host "$Label [$suffix]").ToLowerInvariant()
|
|
68
|
+
if ([string]::IsNullOrWhiteSpace($value)) { return $Default }
|
|
69
|
+
return @("y","yes") -contains $value
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
$sourceRoot = Normalize-PathSafe (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
|
73
|
+
if (-not $Target) {
|
|
74
|
+
$Target = Read-Host "Target repository path"
|
|
75
|
+
}
|
|
76
|
+
$targetRoot = Normalize-PathSafe $Target
|
|
77
|
+
|
|
78
|
+
if (-not (Test-Path $targetRoot -PathType Container)) {
|
|
79
|
+
if ($Create -or (Prompt-YesNo "Target missing. Create?" $false)) {
|
|
80
|
+
New-Item -ItemType Directory -Path $targetRoot -Force | Out-Null
|
|
81
|
+
} else {
|
|
82
|
+
Write-Host "Target directory does not exist."
|
|
83
|
+
exit 1
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
$mode = if ($Mode) { $Mode } else { Choose-Mode }
|
|
88
|
+
$backupEnabled = $Backup.IsPresent
|
|
89
|
+
if (($mode -eq "overwrite" -or $mode -eq "interactive") -and -not $backupEnabled) {
|
|
90
|
+
$backupEnabled = Prompt-YesNo "Backup existing files before overwrite?" $false
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
$includePaths = @(
|
|
94
|
+
".cursor/commands",
|
|
95
|
+
".cursor/rules",
|
|
96
|
+
".cursor/skills",
|
|
97
|
+
".cursor/agents",
|
|
98
|
+
".cursor/hooks",
|
|
99
|
+
".cursor/hooks.json",
|
|
100
|
+
".cursor/scratchpad.md",
|
|
101
|
+
"docs",
|
|
102
|
+
"sprints",
|
|
103
|
+
"handoffs",
|
|
104
|
+
"decisions",
|
|
105
|
+
".github/workflows",
|
|
106
|
+
"README.md",
|
|
107
|
+
"installer.py",
|
|
108
|
+
"installer.ps1",
|
|
109
|
+
"installer.sh"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
$files = List-SourceFiles $sourceRoot $includePaths
|
|
113
|
+
if ($files.Count -eq 0) {
|
|
114
|
+
Write-Host "No source files found to install."
|
|
115
|
+
exit 1
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if ($backupEnabled -and $mode -eq "overwrite") {
|
|
119
|
+
$overwriteCandidates = @()
|
|
120
|
+
foreach ($rel in $files) {
|
|
121
|
+
$dst = Join-Path $targetRoot $rel
|
|
122
|
+
if (Test-Path $dst -PathType Leaf) { $overwriteCandidates += $rel }
|
|
123
|
+
}
|
|
124
|
+
if ($overwriteCandidates.Count -gt 0) {
|
|
125
|
+
$backupRoot = Backup-Files $targetRoot $overwriteCandidates
|
|
126
|
+
Write-Host "Backup created at: $backupRoot"
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
foreach ($rel in $files) {
|
|
131
|
+
$src = Join-Path $sourceRoot $rel
|
|
132
|
+
$dst = Join-Path $targetRoot $rel
|
|
133
|
+
$exists = Test-Path $dst -PathType Leaf
|
|
134
|
+
|
|
135
|
+
if ($mode -eq "missing") {
|
|
136
|
+
if ($exists) { continue }
|
|
137
|
+
Ensure-Parent $dst
|
|
138
|
+
Copy-Item -Path $src -Destination $dst -Force
|
|
139
|
+
continue
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if ($mode -eq "overwrite") {
|
|
143
|
+
Ensure-Parent $dst
|
|
144
|
+
Copy-Item -Path $src -Destination $dst -Force
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if ($mode -eq "interactive") {
|
|
149
|
+
if (-not $exists) {
|
|
150
|
+
Ensure-Parent $dst
|
|
151
|
+
Copy-Item -Path $src -Destination $dst -Force
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
$answer = (Read-Host "File exists: $rel | [o]verwrite [s]kip [q]uit").ToLowerInvariant()
|
|
155
|
+
if ($answer -eq "q") {
|
|
156
|
+
Write-Host "Aborted."
|
|
157
|
+
exit 1
|
|
158
|
+
}
|
|
159
|
+
if ($answer -eq "o") {
|
|
160
|
+
if ($backupEnabled) {
|
|
161
|
+
$backupRoot = Backup-Files $targetRoot @($rel)
|
|
162
|
+
Write-Host "Backed up: $rel -> $backupRoot"
|
|
163
|
+
}
|
|
164
|
+
Ensure-Parent $dst
|
|
165
|
+
Copy-Item -Path $src -Destination $dst -Force
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function Show-ItsMagicBanner {
|
|
171
|
+
$prev = [Console]::OutputEncoding
|
|
172
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
173
|
+
Write-Host ""
|
|
174
|
+
Write-Host " ██╗████████╗███████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██████╗" -ForegroundColor Magenta
|
|
175
|
+
Write-Host " ██║╚══██╔══╝██╔════╝ ████╗ ████║██╔══██╗██╔════╝ ██║██╔════╝" -ForegroundColor Magenta
|
|
176
|
+
Write-Host " ██║ ██║ ███████╗█████╗██╔████╔██║███████║██║ ███╗██║██║ " -ForegroundColor Magenta
|
|
177
|
+
Write-Host " ██║ ██║ ╚════██║╚════╝██║╚██╔╝██║██╔══██║██║ ██║██║██║ " -ForegroundColor Cyan
|
|
178
|
+
Write-Host " ██║ ██║ ███████║ ██║ ╚═╝ ██║██║ ██║╚██████╔╝██║╚██████╗" -ForegroundColor Cyan
|
|
179
|
+
Write-Host " ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝" -ForegroundColor Cyan
|
|
180
|
+
Write-Host ""
|
|
181
|
+
Write-Host " AI dev team" -ForegroundColor Yellow
|
|
182
|
+
Write-Host " Installation complete!" -ForegroundColor Green
|
|
183
|
+
Write-Host ""
|
|
184
|
+
[Console]::OutputEncoding = $prev
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
Show-ItsMagicBanner
|
|
188
|
+
exit 0
|
|
189
|
+
|
package/installer.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import sys
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def normalize(path):
|
|
9
|
+
return os.path.normpath(os.path.abspath(path))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def list_source_files(source_root, include_paths):
|
|
13
|
+
files = []
|
|
14
|
+
for rel in include_paths:
|
|
15
|
+
src = os.path.join(source_root, rel)
|
|
16
|
+
if os.path.isfile(src):
|
|
17
|
+
files.append(rel)
|
|
18
|
+
elif os.path.isdir(src):
|
|
19
|
+
for root, _, filenames in os.walk(src):
|
|
20
|
+
for name in filenames:
|
|
21
|
+
full = os.path.join(root, name)
|
|
22
|
+
rel_path = os.path.relpath(full, source_root)
|
|
23
|
+
files.append(rel_path)
|
|
24
|
+
return sorted(set(files))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def ensure_parent(path):
|
|
28
|
+
parent = os.path.dirname(path)
|
|
29
|
+
if parent and not os.path.isdir(parent):
|
|
30
|
+
os.makedirs(parent, exist_ok=True)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def backup_files(target_root, rel_paths):
|
|
34
|
+
timestamp = datetime.utcnow().strftime("%Y%m%d-%H%M%SZ")
|
|
35
|
+
backup_root = os.path.join(target_root, "backups", timestamp)
|
|
36
|
+
for rel in rel_paths:
|
|
37
|
+
src = os.path.join(target_root, rel)
|
|
38
|
+
if os.path.isfile(src):
|
|
39
|
+
dst = os.path.join(backup_root, rel)
|
|
40
|
+
ensure_parent(dst)
|
|
41
|
+
shutil.copy2(src, dst)
|
|
42
|
+
return backup_root
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def choose_mode():
|
|
46
|
+
print("Select install mode:")
|
|
47
|
+
print("1) missing-only (copy only files that do not exist)")
|
|
48
|
+
print("2) overwrite-all (replace existing files)")
|
|
49
|
+
print("3) interactive (prompt per file)")
|
|
50
|
+
choice = input("Enter 1, 2, or 3: ").strip()
|
|
51
|
+
if choice == "1":
|
|
52
|
+
return "missing"
|
|
53
|
+
if choice == "2":
|
|
54
|
+
return "overwrite"
|
|
55
|
+
return "interactive"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def prompt_yes_no(label, default=False):
|
|
59
|
+
suffix = "Y/n" if default else "y/N"
|
|
60
|
+
value = input(f"{label} [{suffix}]: ").strip().lower()
|
|
61
|
+
if not value:
|
|
62
|
+
return default
|
|
63
|
+
return value in ("y", "yes")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main():
|
|
67
|
+
parser = argparse.ArgumentParser(description="Install its-magic into a repo")
|
|
68
|
+
parser.add_argument("--target", help="Target repository path")
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--mode",
|
|
71
|
+
choices=["missing", "overwrite", "interactive"],
|
|
72
|
+
help="Install mode",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--backup",
|
|
76
|
+
action="store_true",
|
|
77
|
+
help="Backup files before overwriting",
|
|
78
|
+
)
|
|
79
|
+
parser.add_argument(
|
|
80
|
+
"--create",
|
|
81
|
+
action="store_true",
|
|
82
|
+
help="Create target directory if missing",
|
|
83
|
+
)
|
|
84
|
+
args = parser.parse_args()
|
|
85
|
+
|
|
86
|
+
source_root = normalize(os.path.dirname(__file__))
|
|
87
|
+
target_root = normalize(args.target) if args.target else None
|
|
88
|
+
|
|
89
|
+
if not target_root:
|
|
90
|
+
target_root = normalize(input("Target repository path: ").strip())
|
|
91
|
+
|
|
92
|
+
if not os.path.isdir(target_root):
|
|
93
|
+
if args.create or prompt_yes_no("Target missing. Create?", default=False):
|
|
94
|
+
os.makedirs(target_root, exist_ok=True)
|
|
95
|
+
else:
|
|
96
|
+
print("Target directory does not exist.")
|
|
97
|
+
return 1
|
|
98
|
+
|
|
99
|
+
mode = args.mode or choose_mode()
|
|
100
|
+
backup_enabled = args.backup
|
|
101
|
+
if mode in ("overwrite", "interactive") and not args.backup:
|
|
102
|
+
backup_enabled = prompt_yes_no("Backup existing files before overwrite?", False)
|
|
103
|
+
|
|
104
|
+
include_paths = [
|
|
105
|
+
".cursor/commands",
|
|
106
|
+
".cursor/rules",
|
|
107
|
+
".cursor/skills",
|
|
108
|
+
".cursor/agents",
|
|
109
|
+
".cursor/hooks",
|
|
110
|
+
".cursor/hooks.json",
|
|
111
|
+
".cursor/scratchpad.md",
|
|
112
|
+
"docs",
|
|
113
|
+
"sprints",
|
|
114
|
+
"handoffs",
|
|
115
|
+
"decisions",
|
|
116
|
+
".github/workflows",
|
|
117
|
+
"README.md",
|
|
118
|
+
"installer.py",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
files = list_source_files(source_root, include_paths)
|
|
122
|
+
if not files:
|
|
123
|
+
print("No source files found to install.")
|
|
124
|
+
return 1
|
|
125
|
+
|
|
126
|
+
overwrite_candidates = []
|
|
127
|
+
if backup_enabled and mode == "overwrite":
|
|
128
|
+
for rel in files:
|
|
129
|
+
if os.path.isfile(os.path.join(target_root, rel)):
|
|
130
|
+
overwrite_candidates.append(rel)
|
|
131
|
+
if overwrite_candidates:
|
|
132
|
+
backup_root = backup_files(target_root, overwrite_candidates)
|
|
133
|
+
print(f"Backup created at: {backup_root}")
|
|
134
|
+
|
|
135
|
+
for rel in files:
|
|
136
|
+
src = os.path.join(source_root, rel)
|
|
137
|
+
dst = os.path.join(target_root, rel)
|
|
138
|
+
exists = os.path.isfile(dst)
|
|
139
|
+
|
|
140
|
+
if mode == "missing":
|
|
141
|
+
if exists:
|
|
142
|
+
continue
|
|
143
|
+
ensure_parent(dst)
|
|
144
|
+
shutil.copy2(src, dst)
|
|
145
|
+
continue
|
|
146
|
+
|
|
147
|
+
if mode == "overwrite":
|
|
148
|
+
ensure_parent(dst)
|
|
149
|
+
shutil.copy2(src, dst)
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
if mode == "interactive":
|
|
153
|
+
if not exists:
|
|
154
|
+
ensure_parent(dst)
|
|
155
|
+
shutil.copy2(src, dst)
|
|
156
|
+
continue
|
|
157
|
+
answer = input(f"File exists: {rel} | [o]verwrite [s]kip [q]uit: ").strip().lower()
|
|
158
|
+
if answer == "q":
|
|
159
|
+
print("Aborted.")
|
|
160
|
+
return 1
|
|
161
|
+
if answer == "o":
|
|
162
|
+
if backup_enabled:
|
|
163
|
+
backup_root = backup_files(target_root, [rel])
|
|
164
|
+
print(f"Backed up: {rel} -> {backup_root}")
|
|
165
|
+
ensure_parent(dst)
|
|
166
|
+
shutil.copy2(src, dst)
|
|
167
|
+
else:
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
show_banner()
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def show_banner():
|
|
175
|
+
M = "\033[1;35m"
|
|
176
|
+
C = "\033[1;36m"
|
|
177
|
+
Y = "\033[1;33m"
|
|
178
|
+
G = "\033[1;32m"
|
|
179
|
+
R = "\033[0m"
|
|
180
|
+
print()
|
|
181
|
+
print(f"{M} ██╗████████╗███████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██████╗{R}")
|
|
182
|
+
print(f"{M} ██║╚══██╔══╝██╔════╝ ████╗ ████║██╔══██╗██╔════╝ ██║██╔════╝{R}")
|
|
183
|
+
print(f"{M} ██║ ██║ ███████╗█████╗██╔████╔██║███████║██║ ███╗██║██║ {R}")
|
|
184
|
+
print(f"{C} ██║ ██║ ╚════██║╚════╝██║╚██╔╝██║██╔══██║██║ ██║██║██║ {R}")
|
|
185
|
+
print(f"{C} ██║ ██║ ███████║ ██║ ╚═╝ ██║██║ ██║╚██████╔╝██║╚██████╗{R}")
|
|
186
|
+
print(f"{C} ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝{R}")
|
|
187
|
+
print()
|
|
188
|
+
print(f"{Y} AI dev team{R}")
|
|
189
|
+
print(f"{G} Installation complete!{R}")
|
|
190
|
+
print()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
raise SystemExit(main())
|
|
195
|
+
|
package/installer.sh
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
ensure_parent() {
|
|
6
|
+
dir=$(dirname "$1")
|
|
7
|
+
[ -d "$dir" ] || mkdir -p "$dir"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
list_source_files() {
|
|
11
|
+
source_root="$1"
|
|
12
|
+
shift
|
|
13
|
+
for rel in "$@"; do
|
|
14
|
+
src="$source_root/$rel"
|
|
15
|
+
if [ -f "$src" ]; then
|
|
16
|
+
echo "$rel"
|
|
17
|
+
elif [ -d "$src" ]; then
|
|
18
|
+
find "$src" -type f | sed "s|^$source_root/||"
|
|
19
|
+
fi
|
|
20
|
+
done | sort -u
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
backup_files() {
|
|
24
|
+
target_root="$1"
|
|
25
|
+
shift
|
|
26
|
+
timestamp=$(date -u +"%Y%m%d-%H%M%SZ")
|
|
27
|
+
backup_root="$target_root/backups/$timestamp"
|
|
28
|
+
for rel in "$@"; do
|
|
29
|
+
src="$target_root/$rel"
|
|
30
|
+
if [ -f "$src" ]; then
|
|
31
|
+
dst="$backup_root/$rel"
|
|
32
|
+
ensure_parent "$dst"
|
|
33
|
+
cp -p "$src" "$dst"
|
|
34
|
+
fi
|
|
35
|
+
done
|
|
36
|
+
echo "$backup_root"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
choose_mode() {
|
|
40
|
+
printf "%s\n" "Select install mode:"
|
|
41
|
+
printf "%s\n" "1) missing-only (copy only files that do not exist)"
|
|
42
|
+
printf "%s\n" "2) overwrite-all (replace existing files)"
|
|
43
|
+
printf "%s\n" "3) interactive (prompt per file)"
|
|
44
|
+
printf "%s" "Enter 1, 2, or 3: "
|
|
45
|
+
read -r choice
|
|
46
|
+
case "$choice" in
|
|
47
|
+
1) echo "missing" ;;
|
|
48
|
+
2) echo "overwrite" ;;
|
|
49
|
+
*) echo "interactive" ;;
|
|
50
|
+
esac
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
prompt_yes_no() {
|
|
54
|
+
label="$1"
|
|
55
|
+
default="$2"
|
|
56
|
+
suffix="y/N"
|
|
57
|
+
[ "$default" = "true" ] && suffix="Y/n"
|
|
58
|
+
printf "%s [%s]: " "$label" "$suffix"
|
|
59
|
+
read -r value
|
|
60
|
+
value=$(printf "%s" "$value" | tr 'A-Z' 'a-z')
|
|
61
|
+
if [ -z "$value" ]; then
|
|
62
|
+
[ "$default" = "true" ] && return 0 || return 1
|
|
63
|
+
fi
|
|
64
|
+
[ "$value" = "y" ] || [ "$value" = "yes" ]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
TARGET=""
|
|
68
|
+
MODE=""
|
|
69
|
+
BACKUP="false"
|
|
70
|
+
CREATE="false"
|
|
71
|
+
|
|
72
|
+
while [ $# -gt 0 ]; do
|
|
73
|
+
case "$1" in
|
|
74
|
+
--target) TARGET="$2"; shift 2 ;;
|
|
75
|
+
--mode) MODE="$2"; shift 2 ;;
|
|
76
|
+
--backup) BACKUP="true"; shift 1 ;;
|
|
77
|
+
--create) CREATE="true"; shift 1 ;;
|
|
78
|
+
*) shift 1 ;;
|
|
79
|
+
esac
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
SOURCE_ROOT=$(cd "$(dirname "$0")" && pwd)
|
|
83
|
+
|
|
84
|
+
if [ -z "$TARGET" ]; then
|
|
85
|
+
printf "%s" "Target repository path: "
|
|
86
|
+
read -r TARGET
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [ ! -d "$TARGET" ]; then
|
|
90
|
+
if [ "$CREATE" = "true" ] || prompt_yes_no "Target missing. Create?" "false"; then
|
|
91
|
+
mkdir -p "$TARGET"
|
|
92
|
+
else
|
|
93
|
+
printf "%s\n" "Target directory does not exist."
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
TARGET_ROOT=$(cd "$TARGET" && pwd)
|
|
98
|
+
|
|
99
|
+
if [ -z "$MODE" ]; then
|
|
100
|
+
MODE=$(choose_mode)
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
if [ "$MODE" = "overwrite" ] || [ "$MODE" = "interactive" ]; then
|
|
104
|
+
if [ "$BACKUP" = "false" ]; then
|
|
105
|
+
if prompt_yes_no "Backup existing files before overwrite?" "false"; then
|
|
106
|
+
BACKUP="true"
|
|
107
|
+
fi
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
INCLUDE_PATHS="
|
|
112
|
+
.cursor/commands
|
|
113
|
+
.cursor/rules
|
|
114
|
+
.cursor/skills
|
|
115
|
+
.cursor/agents
|
|
116
|
+
.cursor/hooks
|
|
117
|
+
.cursor/hooks.json
|
|
118
|
+
.cursor/scratchpad.md
|
|
119
|
+
docs
|
|
120
|
+
sprints
|
|
121
|
+
handoffs
|
|
122
|
+
decisions
|
|
123
|
+
.github/workflows
|
|
124
|
+
README.md
|
|
125
|
+
installer.py
|
|
126
|
+
installer.ps1
|
|
127
|
+
installer.sh
|
|
128
|
+
"
|
|
129
|
+
|
|
130
|
+
FILES=$(list_source_files "$SOURCE_ROOT" $INCLUDE_PATHS)
|
|
131
|
+
if [ -z "$FILES" ]; then
|
|
132
|
+
printf "%s\n" "No source files found to install."
|
|
133
|
+
exit 1
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
if [ "$BACKUP" = "true" ] && [ "$MODE" = "overwrite" ]; then
|
|
137
|
+
overwrite_candidates=""
|
|
138
|
+
for rel in $FILES; do
|
|
139
|
+
[ -f "$TARGET_ROOT/$rel" ] && overwrite_candidates="$overwrite_candidates $rel"
|
|
140
|
+
done
|
|
141
|
+
if [ -n "$overwrite_candidates" ]; then
|
|
142
|
+
backup_root=$(backup_files "$TARGET_ROOT" $overwrite_candidates)
|
|
143
|
+
printf "%s\n" "Backup created at: $backup_root"
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
for rel in $FILES; do
|
|
148
|
+
src="$SOURCE_ROOT/$rel"
|
|
149
|
+
dst="$TARGET_ROOT/$rel"
|
|
150
|
+
if [ "$MODE" = "missing" ]; then
|
|
151
|
+
[ -f "$dst" ] && continue
|
|
152
|
+
ensure_parent "$dst"
|
|
153
|
+
cp -p "$src" "$dst"
|
|
154
|
+
continue
|
|
155
|
+
fi
|
|
156
|
+
if [ "$MODE" = "overwrite" ]; then
|
|
157
|
+
ensure_parent "$dst"
|
|
158
|
+
cp -p "$src" "$dst"
|
|
159
|
+
continue
|
|
160
|
+
fi
|
|
161
|
+
if [ "$MODE" = "interactive" ]; then
|
|
162
|
+
if [ ! -f "$dst" ]; then
|
|
163
|
+
ensure_parent "$dst"
|
|
164
|
+
cp -p "$src" "$dst"
|
|
165
|
+
continue
|
|
166
|
+
fi
|
|
167
|
+
printf "%s" "File exists: $rel | [o]verwrite [s]kip [q]uit: "
|
|
168
|
+
read -r answer
|
|
169
|
+
answer=$(printf "%s" "$answer" | tr 'A-Z' 'a-z')
|
|
170
|
+
if [ "$answer" = "q" ]; then
|
|
171
|
+
printf "%s\n" "Aborted."
|
|
172
|
+
exit 1
|
|
173
|
+
fi
|
|
174
|
+
if [ "$answer" = "o" ]; then
|
|
175
|
+
if [ "$BACKUP" = "true" ]; then
|
|
176
|
+
backup_root=$(backup_files "$TARGET_ROOT" "$rel")
|
|
177
|
+
printf "%s\n" "Backed up: $rel -> $backup_root"
|
|
178
|
+
fi
|
|
179
|
+
ensure_parent "$dst"
|
|
180
|
+
cp -p "$src" "$dst"
|
|
181
|
+
fi
|
|
182
|
+
fi
|
|
183
|
+
done
|
|
184
|
+
|
|
185
|
+
show_banner() {
|
|
186
|
+
printf "\n"
|
|
187
|
+
printf "\033[1;35m ██╗████████╗███████╗ ███╗ ███╗ █████╗ ██████╗ ██╗ ██████╗\033[0m\n"
|
|
188
|
+
printf "\033[1;35m ██║╚══██╔══╝██╔════╝ ████╗ ████║██╔══██╗██╔════╝ ██║██╔════╝\033[0m\n"
|
|
189
|
+
printf "\033[1;35m ██║ ██║ ███████╗█████╗██╔████╔██║███████║██║ ███╗██║██║ \033[0m\n"
|
|
190
|
+
printf "\033[1;36m ██║ ██║ ╚════██║╚════╝██║╚██╔╝██║██╔══██║██║ ██║██║██║ \033[0m\n"
|
|
191
|
+
printf "\033[1;36m ██║ ██║ ███████║ ██║ ╚═╝ ██║██║ ██║╚██████╔╝██║╚██████╗\033[0m\n"
|
|
192
|
+
printf "\033[1;36m ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝\033[0m\n"
|
|
193
|
+
printf "\n"
|
|
194
|
+
printf "\033[1;33m AI dev team\033[0m\n"
|
|
195
|
+
printf "\033[1;32m Installation complete!\033[0m\n"
|
|
196
|
+
printf "\n"
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
show_banner
|
|
200
|
+
exit 0
|
|
201
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "its-magic",
|
|
3
|
+
"version": "0.1.2-10",
|
|
4
|
+
"description": "its-magic - AI dev team workflow for Cursor.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"its-magic": "bin/its-magic.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
".cursor/",
|
|
11
|
+
".github/",
|
|
12
|
+
"decisions/",
|
|
13
|
+
"docs/",
|
|
14
|
+
"handoffs/",
|
|
15
|
+
"milestones/",
|
|
16
|
+
"scripts/",
|
|
17
|
+
"sprints/",
|
|
18
|
+
"installer.ps1",
|
|
19
|
+
"installer.sh",
|
|
20
|
+
"installer.py",
|
|
21
|
+
"README.md",
|
|
22
|
+
"bin/its-magic.js"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"release:patch": "npm version patch && npm publish",
|
|
26
|
+
"release:minor": "npm version minor && npm publish",
|
|
27
|
+
"release:major": "npm version major && npm publish",
|
|
28
|
+
"release:all": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1",
|
|
29
|
+
"release:all:patch": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -Bump patch",
|
|
30
|
+
"release:all:minor": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -Bump minor",
|
|
31
|
+
"release:all:major": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -Bump major",
|
|
32
|
+
"release:all:beta": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -Bump prerelease -NpmTag beta",
|
|
33
|
+
"release:all:dry": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -DryRun",
|
|
34
|
+
"release:npm-only": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -SkipChoco -SkipBrew",
|
|
35
|
+
"release:choco-only": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -SkipNpm -SkipBrew",
|
|
36
|
+
"release:brew-only": "powershell -ExecutionPolicy Bypass -File scripts/release-all.ps1 -SkipNpm -SkipChoco"
|
|
37
|
+
}
|
|
38
|
+
}
|