aid-installer 2.0.0 → 2.0.1
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/VERSION +1 -1
- package/bin/aid +4 -0
- package/bin/aid.ps1 +3 -0
- package/lib/AidInstallCore.psm1 +192 -10
- package/lib/aid-install-core.sh +214 -23
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.0.
|
|
1
|
+
2.0.1
|
package/bin/aid
CHANGED
|
@@ -95,6 +95,10 @@ _aid_is_project_dir() {
|
|
|
95
95
|
# C1: Per-repo format stamp constant.
|
|
96
96
|
# The current .aid/ layout version. Bumped ONLY on a breaking layout change,
|
|
97
97
|
# never on every CLI release. Defined exactly once; all comparisons read this.
|
|
98
|
+
# NOTE (work-007 C6): the install-time settings seed also stamps this value.
|
|
99
|
+
# On a bump, update ALL carriers together: this line, bin/aid.ps1
|
|
100
|
+
# AidSupportedFormat, and lib/AidInstallCore.psm1 $script:_AidSupportedFormat.
|
|
101
|
+
# (lib/aid-install-core.sh reads THIS var via ${AID_SUPPORTED_FORMAT:-1}.)
|
|
98
102
|
# ---------------------------------------------------------------------------
|
|
99
103
|
readonly AID_SUPPORTED_FORMAT=1
|
|
100
104
|
|
package/bin/aid.ps1
CHANGED
|
@@ -135,6 +135,9 @@ function script:Test-AidIsProjectDir {
|
|
|
135
135
|
# The current .aid/ layout version. Bumped ONLY on a breaking layout change,
|
|
136
136
|
# never on every CLI release. Defined exactly once; all comparisons read this.
|
|
137
137
|
# Integer must equal the bash AID_SUPPORTED_FORMAT in bin/aid.
|
|
138
|
+
# NOTE (work-007 C6): the install-time settings seed also stamps this value. On a
|
|
139
|
+
# bump, update ALL carriers together: this line, bin/aid AID_SUPPORTED_FORMAT, and
|
|
140
|
+
# lib/AidInstallCore.psm1 $script:_AidSupportedFormat (the PS seed's source).
|
|
138
141
|
# ---------------------------------------------------------------------------
|
|
139
142
|
Set-Variable -Name AidSupportedFormat -Value 1 -Option Constant -Scope Script
|
|
140
143
|
|
package/lib/AidInstallCore.psm1
CHANGED
|
@@ -62,10 +62,18 @@ $script:_CopyCountCopied = 0
|
|
|
62
62
|
$script:_CopyCountUpToDate = 0
|
|
63
63
|
$script:_CopyCountUpdated = 0
|
|
64
64
|
$script:_CopyCountSkipped = 0
|
|
65
|
+
$script:_CopyCountFailed = 0
|
|
65
66
|
|
|
66
67
|
# Module-level prune counter. Reset by Invoke-PruneToolDirs before each prune pass.
|
|
67
68
|
$script:_PruneRemoved = 0
|
|
68
69
|
|
|
70
|
+
# Module-level project-provisioning state (work-007). Reset by Install-AidTool.
|
|
71
|
+
$script:_SeededSettings = $false
|
|
72
|
+
$script:_GitignoreAction = 'unchanged'
|
|
73
|
+
# Settings format stamp. MUST equal bin/aid AID_SUPPORTED_FORMAT / bin/aid.ps1
|
|
74
|
+
# AidSupportedFormat. Used to stamp a seeded settings.yml so the format gate is quiet.
|
|
75
|
+
$script:_AidSupportedFormat = 1
|
|
76
|
+
|
|
69
77
|
# ---------------------------------------------------------------------------
|
|
70
78
|
# Constants
|
|
71
79
|
# ---------------------------------------------------------------------------
|
|
@@ -85,6 +93,21 @@ function script:Get-RootAgentFile {
|
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
|
|
96
|
+
# Get-RootDir <tool> - return the tool's install-tree root dir name (relative to
|
|
97
|
+
# the project root). The AID-own subtree lives at <root>/aid/ (e.g. .claude/aid/).
|
|
98
|
+
# Mirrors the per-tool dispatch in Install-AidTool and the bash _root_dir helper.
|
|
99
|
+
function script:Get-RootDir {
|
|
100
|
+
param([string]$Tool)
|
|
101
|
+
switch ($Tool) {
|
|
102
|
+
'claude-code' { return '.claude' }
|
|
103
|
+
'codex' { return '.codex' }
|
|
104
|
+
'cursor' { return '.cursor' }
|
|
105
|
+
'copilot-cli' { return '.github' }
|
|
106
|
+
'antigravity' { return '.agent' }
|
|
107
|
+
default { return '' }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
# ---------------------------------------------------------------------------
|
|
89
112
|
# Utility
|
|
90
113
|
# ---------------------------------------------------------------------------
|
|
@@ -361,7 +384,13 @@ function Copy-AidFile {
|
|
|
361
384
|
}
|
|
362
385
|
|
|
363
386
|
if (-not (Test-Path $Dst -PathType Leaf)) {
|
|
364
|
-
|
|
387
|
+
try {
|
|
388
|
+
Copy-Item -LiteralPath $Src -Destination $Dst -Force -ErrorAction Stop
|
|
389
|
+
} catch {
|
|
390
|
+
[Console]::Error.WriteLine("ERROR: AidInstallCore: copy failed: $Dst -- $_")
|
|
391
|
+
$script:_CopyCountFailed++
|
|
392
|
+
return
|
|
393
|
+
}
|
|
365
394
|
$script:_CopyCountCopied++
|
|
366
395
|
if ($AidVerbose) { Write-Host "Copied: $Dst" }
|
|
367
396
|
return
|
|
@@ -377,15 +406,22 @@ function Copy-AidFile {
|
|
|
377
406
|
return
|
|
378
407
|
}
|
|
379
408
|
|
|
380
|
-
# File exists and differs.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
409
|
+
# File exists and differs -> always overwrite (work-007).
|
|
410
|
+
# AID-owned files track the bundle, which is the source of truth, so an
|
|
411
|
+
# add/update must bring them current. $Force is retained in the signature for
|
|
412
|
+
# back-compat and bash<->PS parity but no longer gates this overwrite:
|
|
413
|
+
# skip-on-diff silently left stale files behind on in-place upgrades. User-owned
|
|
414
|
+
# root agent files (CLAUDE.md/AGENTS.md) never reach Copy-AidFile -- they are
|
|
415
|
+
# handled by Copy-RootAgentFile, which keeps its own force gating.
|
|
416
|
+
try {
|
|
417
|
+
Copy-Item -LiteralPath $Src -Destination $Dst -Force -ErrorAction Stop
|
|
418
|
+
} catch {
|
|
419
|
+
[Console]::Error.WriteLine("ERROR: AidInstallCore: copy failed: $Dst -- $_")
|
|
420
|
+
$script:_CopyCountFailed++
|
|
421
|
+
return
|
|
388
422
|
}
|
|
423
|
+
$script:_CopyCountUpdated++
|
|
424
|
+
if ($AidVerbose) { Write-Host "Updated: $Dst" }
|
|
389
425
|
}
|
|
390
426
|
|
|
391
427
|
# Copy-AidDir <srcDir> <dstDir> [force] [aidVerbose]
|
|
@@ -438,8 +474,13 @@ function script:Test-AidHeadingStem {
|
|
|
438
474
|
$stem = $Line.Substring(3) # strip "## "
|
|
439
475
|
# Strip trailing parenthetical: " (anything)"
|
|
440
476
|
$stem = $stem -replace ' \([^)]*\)$', ''
|
|
477
|
+
# Stems must cover EVERY "## " heading in the shipped AID:BEGIN/END region
|
|
478
|
+
# (Tracking discipline, Knowledge Base, Workflow, Review output format,
|
|
479
|
+
# Permissions). A missing stem duplicates that section on C2 migration
|
|
480
|
+
# (work-007: Workflow was omitted). Parity with bash is_aid_heading.
|
|
441
481
|
switch ($stem) {
|
|
442
482
|
'Knowledge Base' { return $true }
|
|
483
|
+
'Workflow' { return $true }
|
|
443
484
|
'Review output format' { return $true }
|
|
444
485
|
'Permissions' { return $true }
|
|
445
486
|
'Tracking discipline' { return $true }
|
|
@@ -1452,6 +1493,118 @@ function Write-VersionMarker {
|
|
|
1452
1493
|
[System.IO.File]::WriteAllBytes($markerPath, $bytes)
|
|
1453
1494
|
}
|
|
1454
1495
|
|
|
1496
|
+
# ---------------------------------------------------------------------------
|
|
1497
|
+
# Project-level provisioning (work-007): required runtime file + VCS hygiene.
|
|
1498
|
+
# Parity with bash seed_settings_yml / update_gitignore. Both idempotent.
|
|
1499
|
+
# ---------------------------------------------------------------------------
|
|
1500
|
+
|
|
1501
|
+
# AID-managed .gitignore region markers (must match the bash _AID_GI_* strings).
|
|
1502
|
+
$script:_AidGiBegin = "# >>> AID managed -- do not edit (aid add/update maintains this block) >>>"
|
|
1503
|
+
$script:_AidGiEnd = "# <<< AID managed <<<"
|
|
1504
|
+
|
|
1505
|
+
# Get-AidGitignoreBlock - return the AID-managed block as a single LF-joined
|
|
1506
|
+
# string (markers inclusive), NO trailing newline (matches bash contract).
|
|
1507
|
+
function script:Get-AidGitignoreBlock {
|
|
1508
|
+
$lines = @(
|
|
1509
|
+
$script:_AidGiBegin,
|
|
1510
|
+
".aid/.temp/",
|
|
1511
|
+
".aid/.trash/",
|
|
1512
|
+
".aid/.heartbeat/",
|
|
1513
|
+
".aid/generated/",
|
|
1514
|
+
".aid/knowledge/.cache/",
|
|
1515
|
+
".aid/knowledge/.manual-checklist.json",
|
|
1516
|
+
".aid/knowledge/.spot-check-facts.txt",
|
|
1517
|
+
$script:_AidGiEnd
|
|
1518
|
+
)
|
|
1519
|
+
return ($lines -join "`n")
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
# Initialize-AidSettingsFile <target> <tool>
|
|
1523
|
+
# Seed <target>\.aid\settings.yml from the tool's just-installed template when it
|
|
1524
|
+
# does not already exist. NEVER overwrites existing settings.yml (user config).
|
|
1525
|
+
# Not manifest-tracked. Sets $script:_SeededSettings.
|
|
1526
|
+
function script:Initialize-AidSettingsFile {
|
|
1527
|
+
param([string]$Target, [string]$Tool)
|
|
1528
|
+
$script:_SeededSettings = $false
|
|
1529
|
+
$root = script:Get-RootDir -Tool $Tool
|
|
1530
|
+
if (-not $root) { return }
|
|
1531
|
+
$aidDir = Join-Path $Target '.aid'
|
|
1532
|
+
$dst = Join-Path $aidDir 'settings.yml'
|
|
1533
|
+
$tmpl = Join-Path (Join-Path (Join-Path (Join-Path $Target $root) 'aid') 'templates') 'settings.yml'
|
|
1534
|
+
if (Test-Path $dst -PathType Leaf) { return } # never clobber user config
|
|
1535
|
+
# Template absent -> surface it (work-007 C5): a silent skip re-triggers the
|
|
1536
|
+
# "no settings.yml" class and the format gate would then warn forever.
|
|
1537
|
+
if (-not (Test-Path $tmpl -PathType Leaf)) {
|
|
1538
|
+
[Console]::Error.WriteLine("WARN: AidInstallCore: settings template missing ($tmpl); .aid/settings.yml not seeded for '$Tool'.")
|
|
1539
|
+
return
|
|
1540
|
+
}
|
|
1541
|
+
if (-not (Test-Path $aidDir -PathType Container)) {
|
|
1542
|
+
New-Item -ItemType Directory -Path $aidDir -Force | Out-Null
|
|
1543
|
+
}
|
|
1544
|
+
# Seed from template, STAMPING format_version as the first line if absent, so
|
|
1545
|
+
# the settings-format gate stays quiet. Byte-faithful (raw-byte prepend, no
|
|
1546
|
+
# text round-trip) so the install tree is byte-identical to the bash seed
|
|
1547
|
+
# (test-install-parity.sh diff -r covers .aid/settings.yml).
|
|
1548
|
+
$tmplBytes = [System.IO.File]::ReadAllBytes($tmpl)
|
|
1549
|
+
$tmplText = [System.Text.Encoding]::UTF8.GetString($tmplBytes)
|
|
1550
|
+
if ($tmplText -match '(?m)^format_version:') {
|
|
1551
|
+
[System.IO.File]::WriteAllBytes($dst, $tmplBytes)
|
|
1552
|
+
} else {
|
|
1553
|
+
$fv = [System.Text.Encoding]::UTF8.GetBytes("format_version: $($script:_AidSupportedFormat)`n")
|
|
1554
|
+
[System.IO.File]::WriteAllBytes($dst, [byte[]]($fv + $tmplBytes))
|
|
1555
|
+
}
|
|
1556
|
+
$script:_SeededSettings = $true
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
# Update-AidGitignore <target>
|
|
1560
|
+
# Ensure <target>\.gitignore carries the AID-managed region with the current
|
|
1561
|
+
# transient .aid/ exclusions. Creates if absent; else strips any existing AID
|
|
1562
|
+
# region, preserves user content, appends a fresh region with a single blank-line
|
|
1563
|
+
# separator. Idempotent. Sets $script:_GitignoreAction to created|updated|unchanged.
|
|
1564
|
+
function script:Update-AidGitignore {
|
|
1565
|
+
param([string]$Target)
|
|
1566
|
+
$script:_GitignoreAction = 'unchanged'
|
|
1567
|
+
$gi = Join-Path $Target '.gitignore'
|
|
1568
|
+
$block = script:Get-AidGitignoreBlock
|
|
1569
|
+
|
|
1570
|
+
if (-not (Test-Path $gi -PathType Leaf)) {
|
|
1571
|
+
[System.IO.File]::WriteAllText($gi, $block + "`n", [System.Text.UTF8Encoding]::new($false))
|
|
1572
|
+
$script:_GitignoreAction = 'created'
|
|
1573
|
+
return
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
$existing = [System.IO.File]::ReadAllText($gi)
|
|
1577
|
+
$normalized = ($existing -replace "`r`n", "`n") -replace "`r", "`n"
|
|
1578
|
+
$srcLines = $normalized -split "`n"
|
|
1579
|
+
|
|
1580
|
+
# Strip any existing AID region.
|
|
1581
|
+
$kept = [System.Collections.Generic.List[string]]::new()
|
|
1582
|
+
$inBlk = $false
|
|
1583
|
+
foreach ($ln in $srcLines) {
|
|
1584
|
+
if ($ln -eq $script:_AidGiBegin) { $inBlk = $true; continue }
|
|
1585
|
+
if ($inBlk -and $ln -eq $script:_AidGiEnd) { $inBlk = $false; continue }
|
|
1586
|
+
if ($inBlk) { continue }
|
|
1587
|
+
$kept.Add($ln)
|
|
1588
|
+
}
|
|
1589
|
+
# Trim trailing blank lines.
|
|
1590
|
+
while ($kept.Count -gt 0 -and $kept[$kept.Count - 1] -match '^[ \t]*$') {
|
|
1591
|
+
$kept.RemoveAt($kept.Count - 1)
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
$sb = [System.Text.StringBuilder]::new()
|
|
1595
|
+
foreach ($ln in $kept) { [void]$sb.Append($ln); [void]$sb.Append("`n") }
|
|
1596
|
+
if ($kept.Count -gt 0) { [void]$sb.Append("`n") }
|
|
1597
|
+
[void]$sb.Append($block); [void]$sb.Append("`n")
|
|
1598
|
+
$newContent = $sb.ToString()
|
|
1599
|
+
|
|
1600
|
+
if ($newContent -eq $normalized) {
|
|
1601
|
+
$script:_GitignoreAction = 'unchanged'
|
|
1602
|
+
} else {
|
|
1603
|
+
[System.IO.File]::WriteAllText($gi, $newContent, [System.Text.UTF8Encoding]::new($false))
|
|
1604
|
+
$script:_GitignoreAction = 'updated'
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1455
1608
|
# ---------------------------------------------------------------------------
|
|
1456
1609
|
# High-level Install-AidTool
|
|
1457
1610
|
# ---------------------------------------------------------------------------
|
|
@@ -1480,6 +1633,10 @@ function Install-AidTool {
|
|
|
1480
1633
|
$script:_CopyCountUpToDate = 0
|
|
1481
1634
|
$script:_CopyCountUpdated = 0
|
|
1482
1635
|
$script:_CopyCountSkipped = 0
|
|
1636
|
+
$script:_CopyCountFailed = 0
|
|
1637
|
+
# Reset project-provisioning state (work-007).
|
|
1638
|
+
$script:_SeededSettings = $false
|
|
1639
|
+
$script:_GitignoreAction = 'unchanged'
|
|
1483
1640
|
|
|
1484
1641
|
$rootAgentFile = script:Get-RootAgentFile -Tool $Tool
|
|
1485
1642
|
|
|
@@ -1538,6 +1695,12 @@ function Install-AidTool {
|
|
|
1538
1695
|
}
|
|
1539
1696
|
}
|
|
1540
1697
|
|
|
1698
|
+
# Abort loudly if any AID-owned file failed to copy -- do NOT proceed to
|
|
1699
|
+
# write a manifest that records a partial install as success (work-007 C4).
|
|
1700
|
+
if ($script:_CopyCountFailed -gt 0) {
|
|
1701
|
+
throw "AidInstallCore: $($script:_CopyCountFailed) file(s) failed to copy for '$Tool'. Install aborted (incomplete)."
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1541
1704
|
# Handle root agent file via in-place region update (Pillar 3).
|
|
1542
1705
|
$rootSrc = Join-Path $StagingDir $rootAgentFile
|
|
1543
1706
|
$rootDst = Join-Path $Target $rootAgentFile
|
|
@@ -1590,6 +1753,11 @@ function Install-AidTool {
|
|
|
1590
1753
|
# Write version marker.
|
|
1591
1754
|
Write-VersionMarker -Target $Target -Version $Version
|
|
1592
1755
|
|
|
1756
|
+
# Project-level provisioning (work-007): seed required settings.yml and
|
|
1757
|
+
# maintain the .gitignore AID region. Both idempotent; safe per-tool.
|
|
1758
|
+
script:Initialize-AidSettingsFile -Target $Target -Tool $Tool
|
|
1759
|
+
script:Update-AidGitignore -Target $Target
|
|
1760
|
+
|
|
1593
1761
|
# Print concise install summary (always shown; per-file lines only when AidVerbose).
|
|
1594
1762
|
$totalFiles = $script:_CopyCountCopied + $script:_CopyCountUpToDate + $script:_CopyCountUpdated + $script:_CopyCountSkipped
|
|
1595
1763
|
if ($totalFiles -gt 0) {
|
|
@@ -1606,6 +1774,13 @@ function Install-AidTool {
|
|
|
1606
1774
|
}
|
|
1607
1775
|
}
|
|
1608
1776
|
|
|
1777
|
+
# Report project-level provisioning actions (work-007; always shown).
|
|
1778
|
+
if ($script:_SeededSettings) { Write-Host " Created .aid/settings.yml from template" }
|
|
1779
|
+
switch ($script:_GitignoreAction) {
|
|
1780
|
+
'created' { Write-Host " Created .gitignore with AID exclusions" }
|
|
1781
|
+
'updated' { Write-Host " Updated .gitignore AID exclusions" }
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1609
1784
|
return 0
|
|
1610
1785
|
}
|
|
1611
1786
|
|
|
@@ -1699,13 +1874,20 @@ function Uninstall-AidTool {
|
|
|
1699
1874
|
# Remove this tool from manifest.
|
|
1700
1875
|
Remove-ManifestTool -ManifestPath $ManifestPath -Tool $Tool
|
|
1701
1876
|
|
|
1702
|
-
# If no manifest remains
|
|
1877
|
+
# If no manifest remains (last tool removed), remove the AID-provisioned
|
|
1878
|
+
# project files so a full uninstall leaves .aid/ clean (work-007).
|
|
1703
1879
|
if (-not (Test-Path $ManifestPath -PathType Leaf)) {
|
|
1704
1880
|
$aidMetaDir = [System.IO.Path]::GetDirectoryName($ManifestPath)
|
|
1705
1881
|
$versionMarker = Join-Path $aidMetaDir '.aid-version'
|
|
1706
1882
|
if (Test-Path $versionMarker -PathType Leaf) {
|
|
1707
1883
|
Remove-Item -LiteralPath $versionMarker -Force
|
|
1708
1884
|
}
|
|
1885
|
+
# Remove the install-time-seeded settings.yml (symmetric with the seed).
|
|
1886
|
+
# Only fires when NO tools remain; a partial uninstall keeps it.
|
|
1887
|
+
$seededSettings = Join-Path $aidMetaDir 'settings.yml'
|
|
1888
|
+
if (Test-Path $seededSettings -PathType Leaf) {
|
|
1889
|
+
Remove-Item -LiteralPath $seededSettings -Force
|
|
1890
|
+
}
|
|
1709
1891
|
if (Test-Path $aidMetaDir -PathType Container) {
|
|
1710
1892
|
$rem = Get-ChildItem -LiteralPath $aidMetaDir -Recurse -File -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
1711
1893
|
if (-not $rem) {
|
package/lib/aid-install-core.sh
CHANGED
|
@@ -81,6 +81,19 @@ _root_agent_file() {
|
|
|
81
81
|
esac
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
# _root_dir <tool> - print the tool's install-tree root dir name (relative to the
|
|
85
|
+
# project root). The AID-own subtree lives at <root>/aid/ (e.g. .claude/aid/).
|
|
86
|
+
# Mirrors the per-tool dispatch in install_tool.
|
|
87
|
+
_root_dir() {
|
|
88
|
+
case "$1" in
|
|
89
|
+
claude-code) echo ".claude" ;;
|
|
90
|
+
codex) echo ".codex" ;;
|
|
91
|
+
cursor) echo ".cursor" ;;
|
|
92
|
+
copilot-cli) echo ".github" ;;
|
|
93
|
+
antigravity) echo ".agent" ;;
|
|
94
|
+
esac
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
# ---------------------------------------------------------------------------
|
|
85
98
|
# Utility
|
|
86
99
|
# ---------------------------------------------------------------------------
|
|
@@ -315,7 +328,11 @@ copy_file() {
|
|
|
315
328
|
mkdir -p "$dst_dir"
|
|
316
329
|
|
|
317
330
|
if [[ ! -e "$dst" ]]; then
|
|
318
|
-
cp "$src" "$dst"
|
|
331
|
+
if ! cp "$src" "$dst"; then
|
|
332
|
+
echo "ERROR: aid-install-core: copy failed: ${dst}" >&2
|
|
333
|
+
_COPY_COUNT_FAILED=$((_COPY_COUNT_FAILED + 1))
|
|
334
|
+
return 0
|
|
335
|
+
fi
|
|
319
336
|
_COPY_COUNT_COPIED=$((_COPY_COUNT_COPIED + 1))
|
|
320
337
|
[[ "${AID_VERBOSE:-0}" -eq 1 ]] && echo "Copied: ${dst}"
|
|
321
338
|
return 0
|
|
@@ -327,15 +344,21 @@ copy_file() {
|
|
|
327
344
|
return 0
|
|
328
345
|
fi
|
|
329
346
|
|
|
330
|
-
# File exists and differs.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
347
|
+
# File exists and differs -> always overwrite (work-007).
|
|
348
|
+
# AID-owned files track the bundle, which is the source of truth, so an
|
|
349
|
+
# add/update must bring them current. `force` is retained in the signature
|
|
350
|
+
# for back-compat and bash<->PS parity but no longer gates this overwrite:
|
|
351
|
+
# skip-on-diff silently left stale files behind on in-place upgrades (e.g. an
|
|
352
|
+
# old flat-path skill surviving next to relocated .aid/ scripts). User-owned
|
|
353
|
+
# root agent files (CLAUDE.md/AGENTS.md) never reach copy_file -- they are
|
|
354
|
+
# handled by _copy_root_agent_file, which keeps its own force gating.
|
|
355
|
+
if ! cp "$src" "$dst"; then
|
|
356
|
+
echo "ERROR: aid-install-core: copy failed: ${dst}" >&2
|
|
357
|
+
_COPY_COUNT_FAILED=$((_COPY_COUNT_FAILED + 1))
|
|
358
|
+
return 0
|
|
338
359
|
fi
|
|
360
|
+
_COPY_COUNT_UPDATED=$((_COPY_COUNT_UPDATED + 1))
|
|
361
|
+
[[ "${AID_VERBOSE:-0}" -eq 1 ]] && echo "Updated: ${dst}"
|
|
339
362
|
}
|
|
340
363
|
|
|
341
364
|
# copy_dir <src_dir> <dst_dir> [force]
|
|
@@ -428,7 +451,11 @@ _copy_root_agent_file() {
|
|
|
428
451
|
# then lines after the closing marker.
|
|
429
452
|
local tmp
|
|
430
453
|
tmp="$(mktemp "${dst_dir}/.aid-root-agent.XXXXXX")"
|
|
431
|
-
awk -v
|
|
454
|
+
# awk -v applies C-escape processing to the value; double every backslash
|
|
455
|
+
# so a region containing "\" (or "\n"-like text) passes through literally
|
|
456
|
+
# (work-007 C7). No-op for the current backslash-free region.
|
|
457
|
+
local src_region_esc="${src_region//\\/\\\\}"
|
|
458
|
+
awk -v region="$src_region_esc" '
|
|
432
459
|
BEGIN { in_aid=0; printed_region=0 }
|
|
433
460
|
/^<!-- AID:BEGIN -->/ {
|
|
434
461
|
if (!printed_region) {
|
|
@@ -471,11 +498,13 @@ _copy_root_agent_file() {
|
|
|
471
498
|
# stem match and re-insert as a marked region.
|
|
472
499
|
#
|
|
473
500
|
# AID section stems to excise (exact heading stem; tolerate trailing
|
|
474
|
-
# parenthetical like " (global)" or " (IMPERATIVE)")
|
|
501
|
+
# parenthetical like " (global)" or " (IMPERATIVE)") -- MUST match every
|
|
502
|
+
# "## " heading in the region (see is_aid_heading):
|
|
503
|
+
# ## Tracking discipline
|
|
475
504
|
# ## Knowledge Base
|
|
505
|
+
# ## Workflow
|
|
476
506
|
# ## Review output format
|
|
477
507
|
# ## Permissions
|
|
478
|
-
# ## Tracking discipline
|
|
479
508
|
#
|
|
480
509
|
# A section runs from its "## Stem..." heading line until the next "## "
|
|
481
510
|
# heading (exclusive) or end-of-file.
|
|
@@ -487,27 +516,34 @@ _copy_root_agent_file() {
|
|
|
487
516
|
# Extract the new marked region from source.
|
|
488
517
|
local new_region
|
|
489
518
|
new_region="$(awk '/^<!-- AID:BEGIN -->/{found=1} found{print} /^<!-- AID:END -->/{if(found){exit}}' "$src")"
|
|
519
|
+
# awk -v applies C-escape processing; double backslashes so the region passes
|
|
520
|
+
# through literally (work-007 C7). No-op for the current backslash-free region.
|
|
521
|
+
local new_region_esc="${new_region//\\/\\\\}"
|
|
490
522
|
|
|
491
523
|
local tmp
|
|
492
524
|
tmp="$(mktemp "${dst_dir}/.aid-root-agent.XXXXXX")"
|
|
493
525
|
|
|
494
|
-
# Use awk to perform the excise-and-reinsert in one pass
|
|
526
|
+
# Use awk to perform the excise-and-reinsert in one pass (region passed
|
|
527
|
+
# backslash-doubled via new_region_esc -- see C7 note above).
|
|
495
528
|
# The awk script:
|
|
496
529
|
# - Identifies AID-managed section headings by stem prefix match.
|
|
497
530
|
# - Suppresses those sections (and their body lines).
|
|
498
531
|
# - At the position of the first suppressed section, emits the new region.
|
|
499
532
|
# - All other lines are emitted verbatim.
|
|
500
|
-
awk -v new_region="$
|
|
533
|
+
awk -v new_region="$new_region_esc" '
|
|
501
534
|
function is_aid_heading(line, stem) {
|
|
502
535
|
# Match "## StemText" optionally followed by " (anything)".
|
|
503
|
-
# Stems
|
|
504
|
-
#
|
|
536
|
+
# Stems must cover EVERY "## " heading inside the shipped AID:BEGIN/END
|
|
537
|
+
# region (Tracking discipline, Knowledge Base, Workflow, Review output
|
|
538
|
+
# format, Permissions) -- a stem missing here causes a duplicate section
|
|
539
|
+
# on the C2 (no-marker) migration path (work-007: Workflow was omitted).
|
|
505
540
|
if (line !~ /^## /) return 0
|
|
506
541
|
stem = line
|
|
507
542
|
gsub(/^## /, "", stem)
|
|
508
543
|
# Strip trailing parenthetical suffix: " (..." -> ""
|
|
509
544
|
gsub(/ \([^)]*\)$/, "", stem)
|
|
510
545
|
if (stem == "Knowledge Base") return 1
|
|
546
|
+
if (stem == "Workflow") return 1
|
|
511
547
|
if (stem == "Review output format") return 1
|
|
512
548
|
if (stem == "Permissions") return 1
|
|
513
549
|
if (stem == "Tracking discipline") return 1
|
|
@@ -1881,6 +1917,133 @@ write_version_marker() {
|
|
|
1881
1917
|
printf '%s\n' "$version" > "${target}/.aid/.aid-version"
|
|
1882
1918
|
}
|
|
1883
1919
|
|
|
1920
|
+
# ---------------------------------------------------------------------------
|
|
1921
|
+
# Project-level provisioning (work-007): required runtime file + VCS hygiene.
|
|
1922
|
+
# Both are idempotent and safe to run once per tool during a multi-tool add.
|
|
1923
|
+
# ---------------------------------------------------------------------------
|
|
1924
|
+
|
|
1925
|
+
# seed_settings_yml <target> <tool>
|
|
1926
|
+
# Seed <target>/.aid/settings.yml from the tool's just-installed template when it
|
|
1927
|
+
# does not already exist. NEVER overwrites an existing settings.yml -- it holds
|
|
1928
|
+
# user config (project identity, grades). Not recorded in the manifest: it is
|
|
1929
|
+
# user data and must survive `aid remove`/prune and never flow through the copy
|
|
1930
|
+
# engine. Sets _CORE_SEEDED_SETTINGS=1 when a file was created, else 0.
|
|
1931
|
+
seed_settings_yml() {
|
|
1932
|
+
local target="$1" tool="$2"
|
|
1933
|
+
local root dst tmpl
|
|
1934
|
+
_CORE_SEEDED_SETTINGS=0
|
|
1935
|
+
root="$(_root_dir "$tool")"
|
|
1936
|
+
[[ -z "$root" ]] && return 0
|
|
1937
|
+
dst="${target}/.aid/settings.yml"
|
|
1938
|
+
tmpl="${target}/${root}/aid/templates/settings.yml"
|
|
1939
|
+
[[ -f "$dst" ]] && return 0 # never clobber user config
|
|
1940
|
+
# Template absent -> a tool bundle shipped without aid/templates/settings.yml.
|
|
1941
|
+
# Surface it (work-007 C5): a silent skip re-triggers the "no settings.yml"
|
|
1942
|
+
# class this seed exists to fix, and the format gate would then warn forever.
|
|
1943
|
+
if [[ ! -f "$tmpl" ]]; then
|
|
1944
|
+
echo "WARN: aid-install-core: settings template missing (${tmpl}); .aid/settings.yml not seeded for '${tool}'." >&2
|
|
1945
|
+
return 0
|
|
1946
|
+
fi
|
|
1947
|
+
mkdir -p "${target}/.aid"
|
|
1948
|
+
# Seed from the template, but STAMP the format_version as the first line so
|
|
1949
|
+
# the settings-format gate does not warn on every subsequent command (the raw
|
|
1950
|
+
# template ships unstamped; era-b synthesis stamps -- we match it here). The
|
|
1951
|
+
# value mirrors bin/aid's AID_SUPPORTED_FORMAT (fallback 1 for install.sh).
|
|
1952
|
+
local _fmt _seed_tmp
|
|
1953
|
+
_fmt="${AID_SUPPORTED_FORMAT:-1}"
|
|
1954
|
+
_seed_tmp="$(mktemp "${dst}.aid-tmp.XXXXXX")" || {
|
|
1955
|
+
echo "WARN: aid-install-core: could not create temp file to seed .aid/settings.yml for '${tool}'." >&2
|
|
1956
|
+
return 0
|
|
1957
|
+
}
|
|
1958
|
+
if grep -q '^format_version:' "$tmpl" 2>/dev/null; then
|
|
1959
|
+
cp "$tmpl" "$_seed_tmp"
|
|
1960
|
+
else
|
|
1961
|
+
{ printf 'format_version: %s\n' "$_fmt"; cat "$tmpl"; } > "$_seed_tmp"
|
|
1962
|
+
fi
|
|
1963
|
+
if ! mv -f "$_seed_tmp" "$dst"; then
|
|
1964
|
+
rm -f "$_seed_tmp" 2>/dev/null
|
|
1965
|
+
echo "WARN: aid-install-core: could not write .aid/settings.yml for '${tool}'." >&2
|
|
1966
|
+
return 0
|
|
1967
|
+
fi
|
|
1968
|
+
_CORE_SEEDED_SETTINGS=1
|
|
1969
|
+
return 0
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
# AID-managed .gitignore region markers. The lines BETWEEN the markers are owned
|
|
1973
|
+
# by the installer and rewritten on every add/update; everything OUTSIDE the
|
|
1974
|
+
# markers is preserved verbatim (mirrors the root-agent region-update pattern).
|
|
1975
|
+
_AID_GI_BEGIN="# >>> AID managed -- do not edit (aid add/update maintains this block) >>>"
|
|
1976
|
+
_AID_GI_END="# <<< AID managed <<<"
|
|
1977
|
+
|
|
1978
|
+
# _aid_gitignore_block - print the current AID-managed .gitignore block (markers
|
|
1979
|
+
# inclusive). The exclusion set is the single source of truth for transient
|
|
1980
|
+
# .aid/ artifacts that must never be committed.
|
|
1981
|
+
_aid_gitignore_block() {
|
|
1982
|
+
printf '%s\n' "$_AID_GI_BEGIN"
|
|
1983
|
+
printf '%s\n' \
|
|
1984
|
+
".aid/.temp/" \
|
|
1985
|
+
".aid/.trash/" \
|
|
1986
|
+
".aid/.heartbeat/" \
|
|
1987
|
+
".aid/generated/" \
|
|
1988
|
+
".aid/knowledge/.cache/" \
|
|
1989
|
+
".aid/knowledge/.manual-checklist.json" \
|
|
1990
|
+
".aid/knowledge/.spot-check-facts.txt"
|
|
1991
|
+
printf '%s' "$_AID_GI_END"
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
# update_gitignore <target>
|
|
1995
|
+
# Ensure <target>/.gitignore carries the AID-managed region with the current
|
|
1996
|
+
# transient .aid/ exclusions. Creates the file if absent; otherwise strips any
|
|
1997
|
+
# existing AID region, preserves all user content, and appends a fresh region
|
|
1998
|
+
# (with a single blank-line separator). Idempotent: a second run produces a
|
|
1999
|
+
# byte-identical file. Sets _CORE_GITIGNORE_ACTION to created|updated|unchanged.
|
|
2000
|
+
update_gitignore() {
|
|
2001
|
+
local target="$1"
|
|
2002
|
+
local gi="${target}/.gitignore"
|
|
2003
|
+
local block tmp
|
|
2004
|
+
_CORE_GITIGNORE_ACTION="unchanged"
|
|
2005
|
+
block="$(_aid_gitignore_block)"
|
|
2006
|
+
|
|
2007
|
+
if [[ ! -f "$gi" ]]; then
|
|
2008
|
+
printf '%s\n' "$block" > "$gi"
|
|
2009
|
+
_CORE_GITIGNORE_ACTION="created"
|
|
2010
|
+
return 0
|
|
2011
|
+
fi
|
|
2012
|
+
|
|
2013
|
+
# Normalize the existing file to LF for BOTH processing and comparison, so a
|
|
2014
|
+
# line-ending-only difference (a CRLF .gitignore) does NOT force a rewrite
|
|
2015
|
+
# every run. Parity with PS Update-AidGitignore, which compares CRLF->LF-
|
|
2016
|
+
# normalized content (work-007 C3). If unchanged, the original file is left
|
|
2017
|
+
# byte-untouched (CRLF preserved); only a real region change triggers a write.
|
|
2018
|
+
local norm
|
|
2019
|
+
norm="$(mktemp "${target}/.gitignore.norm.XXXXXX")"
|
|
2020
|
+
tr -d '\r' < "$gi" > "$norm"
|
|
2021
|
+
|
|
2022
|
+
tmp="$(mktemp "${target}/.gitignore.aid.XXXXXX")"
|
|
2023
|
+
awk -v b="$_AID_GI_BEGIN" -v e="$_AID_GI_END" -v blk="$block" '
|
|
2024
|
+
$0==b {inblk=1; next}
|
|
2025
|
+
inblk && $0==e {inblk=0; next}
|
|
2026
|
+
inblk {next}
|
|
2027
|
+
{lines[n++]=$0}
|
|
2028
|
+
END {
|
|
2029
|
+
while (n>0 && lines[n-1] ~ /^[ \t]*$/) n--
|
|
2030
|
+
for (i=0;i<n;i++) print lines[i]
|
|
2031
|
+
if (n>0) print ""
|
|
2032
|
+
print blk
|
|
2033
|
+
}
|
|
2034
|
+
' "$norm" > "$tmp"
|
|
2035
|
+
|
|
2036
|
+
if cmp -s "$tmp" "$norm"; then
|
|
2037
|
+
rm -f "$tmp" "$norm"
|
|
2038
|
+
_CORE_GITIGNORE_ACTION="unchanged"
|
|
2039
|
+
else
|
|
2040
|
+
rm -f "$norm"
|
|
2041
|
+
mv -f "$tmp" "$gi"
|
|
2042
|
+
_CORE_GITIGNORE_ACTION="updated"
|
|
2043
|
+
fi
|
|
2044
|
+
return 0
|
|
2045
|
+
}
|
|
2046
|
+
|
|
1884
2047
|
# ---------------------------------------------------------------------------
|
|
1885
2048
|
# High-level install_tool
|
|
1886
2049
|
# ---------------------------------------------------------------------------
|
|
@@ -1903,6 +2066,7 @@ install_tool() {
|
|
|
1903
2066
|
_COPY_COUNT_UPTODATE=0
|
|
1904
2067
|
_COPY_COUNT_UPDATED=0
|
|
1905
2068
|
_COPY_COUNT_SKIPPED=0
|
|
2069
|
+
_COPY_COUNT_FAILED=0
|
|
1906
2070
|
|
|
1907
2071
|
local root_agent
|
|
1908
2072
|
root_agent="$(_root_agent_file "$tool")"
|
|
@@ -1963,6 +2127,13 @@ install_tool() {
|
|
|
1963
2127
|
;;
|
|
1964
2128
|
esac
|
|
1965
2129
|
|
|
2130
|
+
# Abort loudly if any AID-owned file failed to copy -- do NOT proceed to
|
|
2131
|
+
# write a manifest that records a partial install as success (work-007 C4).
|
|
2132
|
+
if [[ "${_COPY_COUNT_FAILED:-0}" -gt 0 ]]; then
|
|
2133
|
+
echo "ERROR: aid-install-core: ${_COPY_COUNT_FAILED} file(s) failed to copy for '${tool}'. Install aborted (incomplete)." >&2
|
|
2134
|
+
return 1
|
|
2135
|
+
fi
|
|
2136
|
+
|
|
1966
2137
|
# Handle root agent file via in-place region update (Pillar 3).
|
|
1967
2138
|
local root_src="${staging}/${root_agent}"
|
|
1968
2139
|
local root_dst="${target}/${root_agent}"
|
|
@@ -2002,8 +2173,13 @@ install_tool() {
|
|
|
2002
2173
|
done
|
|
2003
2174
|
unset _rr _leaked
|
|
2004
2175
|
|
|
2005
|
-
# Write manifest (merge).
|
|
2006
|
-
|
|
2176
|
+
# Write manifest (merge). A failure here MUST abort loudly (parity with PS
|
|
2177
|
+
# Write-AidManifest, which throws) -- otherwise a failed manifest write is a
|
|
2178
|
+
# silent success and later `aid status`/`aid remove`/prune won't see the tool.
|
|
2179
|
+
if ! manifest_write "$manifest" "$tool" "$version" "install_paths" "root_entries"; then
|
|
2180
|
+
echo "ERROR: aid-install-core: manifest write failed for ${manifest}. Install aborted (files copied but not recorded)." >&2
|
|
2181
|
+
return 1
|
|
2182
|
+
fi
|
|
2007
2183
|
|
|
2008
2184
|
# Retired-root migration sweep (FR7/FR7a).
|
|
2009
2185
|
# Remove AID-owned content from retired layout dirs BEFORE the normal prune,
|
|
@@ -2025,6 +2201,11 @@ install_tool() {
|
|
|
2025
2201
|
# Write version marker.
|
|
2026
2202
|
write_version_marker "$target" "$version"
|
|
2027
2203
|
|
|
2204
|
+
# Project-level provisioning (work-007): seed the required settings.yml and
|
|
2205
|
+
# maintain the .gitignore AID region. Both idempotent; safe per-tool.
|
|
2206
|
+
seed_settings_yml "$target" "$tool"
|
|
2207
|
+
update_gitignore "$target"
|
|
2208
|
+
|
|
2028
2209
|
# Print concise summary (always shown; per-file lines only when AID_VERBOSE=1).
|
|
2029
2210
|
local _total_files=$((_COPY_COUNT_COPIED + _COPY_COUNT_UPTODATE + _COPY_COUNT_UPDATED + _COPY_COUNT_SKIPPED))
|
|
2030
2211
|
if [[ "$_total_files" -gt 0 ]]; then
|
|
@@ -2047,6 +2228,13 @@ install_tool() {
|
|
|
2047
2228
|
fi
|
|
2048
2229
|
fi
|
|
2049
2230
|
|
|
2231
|
+
# Report project-level provisioning actions (work-007; always shown).
|
|
2232
|
+
[[ "${_CORE_SEEDED_SETTINGS:-0}" -eq 1 ]] && echo " Created .aid/settings.yml from template"
|
|
2233
|
+
case "${_CORE_GITIGNORE_ACTION:-unchanged}" in
|
|
2234
|
+
created) echo " Created .gitignore with AID exclusions" ;;
|
|
2235
|
+
updated) echo " Updated .gitignore AID exclusions" ;;
|
|
2236
|
+
esac
|
|
2237
|
+
|
|
2050
2238
|
return 0
|
|
2051
2239
|
}
|
|
2052
2240
|
|
|
@@ -2143,14 +2331,17 @@ uninstall_tool() {
|
|
|
2143
2331
|
# Remove this tool from manifest.
|
|
2144
2332
|
manifest_remove_tool "$manifest" "$tool"
|
|
2145
2333
|
|
|
2146
|
-
# If no manifest remains, remove the
|
|
2334
|
+
# If no manifest remains (last tool removed), remove the AID-provisioned
|
|
2335
|
+
# project files so a full uninstall leaves .aid/ clean (work-007).
|
|
2147
2336
|
if [[ ! -f "$manifest" ]]; then
|
|
2148
|
-
local version_marker
|
|
2149
|
-
version_marker="$(dirname "$manifest")/.aid-version"
|
|
2150
|
-
rm -f "$version_marker"
|
|
2151
|
-
# Remove .aid dir if empty.
|
|
2152
2337
|
local aid_meta_dir
|
|
2153
2338
|
aid_meta_dir="$(dirname "$manifest")"
|
|
2339
|
+
rm -f "${aid_meta_dir}/.aid-version"
|
|
2340
|
+
# Remove the install-time-seeded settings.yml (symmetric with seed_settings_yml).
|
|
2341
|
+
# Only fires when NO tools remain -- a partial uninstall keeps settings.yml
|
|
2342
|
+
# for the remaining tools.
|
|
2343
|
+
rm -f "${aid_meta_dir}/settings.yml"
|
|
2344
|
+
# Remove .aid dir if empty.
|
|
2154
2345
|
if [[ -d "$aid_meta_dir" ]]; then
|
|
2155
2346
|
local rem
|
|
2156
2347
|
rem="$(find "$aid_meta_dir" -type f 2>/dev/null | head -1)"
|
package/package.json
CHANGED