aid-installer 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # aid-installer
2
2
 
3
- Install [AID](https://github.com/AndreVianna/aid-methodology) (Agentic Iterative Development)
4
- into your repository. Current release: **v1.1.0**.
3
+ Install [AID](https://github.com/AndreVianna/aid-methodology) (AI Integrated Development)
4
+ into your repository. This package always installs the matching AID release.
5
5
 
6
6
  `npm i -g aid-installer` (or `npx aid-installer ...`) puts an `aid` command on your PATH.
7
7
  Then, inside a project:
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 2.0.0
package/bin/aid CHANGED
@@ -13,7 +13,7 @@
13
13
  # aid version Print the CLI version
14
14
  # aid status Show AID state of the current project
15
15
  # aid add <tool>[,...] Add tool(s) to the current project
16
- # aid update [<tool>... | self] Update to latest; no arg = all tools; 'self' = the aid CLI
16
+ # aid update [self] Update to latest; inside repo = CLI + all tools; 'self' = CLI only
17
17
  # aid remove [<tool>... | self] Remove; no arg = ALL AID from project; 'self' = the aid CLI
18
18
  # aid projects [list|add|remove|help] [path] [--local|--shared] [--verbose]
19
19
  # List, register, or unregister AID projects
@@ -144,15 +144,19 @@ _aid_usage() {
144
144
  printf ' --dry-run: print the exact command(s) it would run, then exit (no changes).\n'
145
145
  ;;
146
146
  update)
147
- printf 'aid update [<tool>...] [--version <v>] [--from-bundle <path>] [--force] [--target <dir>]\n'
147
+ printf 'aid update [--version <v>] [--from-bundle <path>] [--force] [--dry-run] [--target <dir>]\n'
148
148
  printf 'aid update self [--from-bundle <path>] [--dry-run]\n'
149
- printf ' Update to latest. No args: update all installed tools.\n'
149
+ printf ' Update to latest.\n'
150
+ printf ' Outside an AID repo: updates the CLI only (no-op if already latest).\n'
151
+ printf ' Inside an AID repo: updates the CLI first, then ALL installed tools to one version.\n'
152
+ printf ' No per-tool selection -- any tool positional is an error (use "self" only).\n'
150
153
  printf ' self: COMPLETELY update the aid CLI, channel-aware:\n'
151
154
  printf ' npm -> npm i -g | pypi -> pipx upgrade | curl -> re-bootstrap install.sh.\n'
152
155
  printf ' Auto-elevates with sudo only when the install location needs root.\n'
153
- printf ' --from-bundle <path>: install the CLI from a local artifact instead of @latest\n'
156
+ printf ' --version <v>: pin ALL tools (and CLI) to version v.\n'
157
+ printf ' --from-bundle <path>: install from a local artifact instead of @latest\n'
154
158
  printf ' (npm .tgz | pypi .whl | curl release-staging dir with install.sh).\n'
155
- printf ' --dry-run: print the exact command(s) it would run, then exit (no changes).\n'
159
+ printf ' --dry-run: print the full plan (tools updated, files copied, paths pruned) and exit.\n'
156
160
  ;;
157
161
  version)
158
162
  printf 'aid version\n'
@@ -194,7 +198,7 @@ _aid_usage() {
194
198
  printf ' aid version Print the CLI version\n'
195
199
  printf ' aid status Show AID state of the current project\n'
196
200
  printf ' aid add <tool>[,...] Add tool(s) to the current project\n'
197
- printf ' aid update [<tool>... | self] Update to latest; no arg = all tools\n'
201
+ printf ' aid update [self] Update to latest; inside repo = all tools\n'
198
202
  printf ' aid remove [<tool>... | self] Remove; no arg = ALL AID from project\n'
199
203
  printf ' aid dashboard start|stop ... Start/stop the local dashboard\n'
200
204
  printf ' aid projects [list|add|remove] List/register/unregister AID projects\n'
@@ -1875,10 +1879,16 @@ _aid_migrate_repo() {
1875
1879
  _era="a"
1876
1880
  elif [[ -f "${repo}/.aid/knowledge/DISCOVERY_STATE.md" ]] \
1877
1881
  || [[ -f "${repo}/.aid/knowledge/DISCOVERY-STATE.md" ]] \
1878
- || [[ -f "${repo}/.aid/knowledge/STATE.md" ]]; then
1882
+ || [[ -f "${repo}/.aid/knowledge/STATE.md" ]] \
1883
+ || [[ -f "${repo}/.aid/.aid-manifest.json" ]]; then
1884
+ # Era-b: KB-state present, OR a tracked repo (manifest present) that has no
1885
+ # settings.yml yet -- the `aid add`-only state. Synthesize a fresh stamped
1886
+ # settings.yml so the format gate stops warning every run and the repo is
1887
+ # brought current. Without the manifest clause such repos warn forever and
1888
+ # are never stamped (gate says "tracked + old"; migrate said "not a candidate").
1879
1889
  _era="b"
1880
1890
  else
1881
- # Bare .aid/ -- not a candidate, no mutation.
1891
+ # Bare .aid/ (no settings.yml, no KB state, no manifest) -- not a candidate.
1882
1892
  return 0
1883
1893
  fi
1884
1894
 
@@ -2573,7 +2583,7 @@ _cmd_dashboard() {
2573
2583
  if [[ -f "$ver_file" ]]; then
2574
2584
  cli_version="$(tr -d '[:space:]' < "$ver_file")"
2575
2585
  fi
2576
- printf 'AID v%s - Agentic Iterative Development\n' "$cli_version"
2586
+ printf 'AID v%s - AI Integrated Development\n' "$cli_version"
2577
2587
  printf 'Install, update, and manage AID across your projects.\n'
2578
2588
 
2579
2589
  # C6: format gate for cwd repo (.aid/ is guaranteed present here -- the
@@ -2849,6 +2859,7 @@ esac
2849
2859
  # Collect positional tool args (comma-separated or space-separated before flags).
2850
2860
  _AID_POSITIONAL_TOOLS=""
2851
2861
  _AID_REMOVE_FORCE=0
2862
+ _AID_DRY_RUN=0
2852
2863
 
2853
2864
  while [[ $# -gt 0 ]]; do
2854
2865
  case "$1" in
@@ -2864,6 +2875,7 @@ while [[ $# -gt 0 ]]; do
2864
2875
  [[ $# -lt 2 ]] && _aid_die "--target requires a value" 2
2865
2876
  _AID_TARGET="$2"; shift 2 ;;
2866
2877
  --no-path) _AID_NO_PATH=1; shift ;;
2878
+ --dry-run) _AID_DRY_RUN=1; shift ;;
2867
2879
  -h|--help) _aid_usage "$SUBCMD"; exit 0 ;;
2868
2880
  -*) _aid_die "unknown flag: $1" 2 ;;
2869
2881
  *)
@@ -2889,6 +2901,16 @@ if [[ "$_AID_FORCE" -eq 0 && ( "${AID_FORCE:-0}" == "1" || "${AID_FORCE:-0}" ==
2889
2901
  fi
2890
2902
  export AID_VERBOSE="$_AID_VERBOSE"
2891
2903
 
2904
+ # FR10: 'update' no longer accepts a per-tool positional (other than 'self' which
2905
+ # was already consumed above). Any non-flag positional on 'aid update' is a usage error.
2906
+ if [[ "$SUBCMD" == "update" && -n "$_AID_TOOL_ARG" ]]; then
2907
+ echo "ERROR: aid update: unexpected argument: '${_AID_TOOL_ARG}'" >&2
2908
+ echo " 'aid update' updates all installed tools -- no per-tool selection." >&2
2909
+ echo " Use 'aid update self' to update the CLI only." >&2
2910
+ echo " See 'aid update -h' for usage." >&2
2911
+ exit 2
2912
+ fi
2913
+
2892
2914
  _AID_TARGET="${_AID_TARGET:-.}"
2893
2915
  # Validate target dir.
2894
2916
  if [[ ! -d "$_AID_TARGET" ]]; then
@@ -2896,20 +2918,32 @@ if [[ ! -d "$_AID_TARGET" ]]; then
2896
2918
  fi
2897
2919
  _AID_TARGET="$(cd "$_AID_TARGET" && pwd)"
2898
2920
 
2899
- # ---- C-table pre-check for 'update [tool]': missing .aid/ -> offer + exit 0 ----
2900
- # Must run BEFORE _resolve_tools_for_aid so we never reach exit-6 when no .aid/ exists.
2901
- # 'add' uses the B-table (checked inside the dispatch case); 'remove' is not in C-table.
2921
+ # ---- FR10: 'update' outside an AID repo -> update the CLI only (not offer-and-exit) ----
2922
+ # Outside a repo: delegates to the CLI-only update path; no tool loop.
2923
+ # Inside a repo: fall through to the full tool-update pass below.
2902
2924
  # _aid_is_project_dir excludes the CLI state home from the "is project" classification.
2903
2925
  if [[ "$SUBCMD" == "update" ]]; then
2904
2926
  if ! _aid_is_project_dir "${_AID_TARGET}"; then
2905
- _aid_cwd_no_aid_offer "${_AID_TARGET}"
2906
- # _aid_cwd_no_aid_offer always exits 0.
2927
+ # FR10 outside-repo: update the CLI only; no tool loop.
2928
+ # For a dry-run preview of the CLI self-update, use 'aid update self --dry-run'.
2929
+ _UPD_CLI_VER=""
2930
+ [[ -f "${AID_CODE_HOME}/VERSION" ]] && _UPD_CLI_VER="$(tr -d '[:space:]' < "${AID_CODE_HOME}/VERSION")"
2931
+ # Check if already latest using cached update-check result (no network call).
2932
+ _UPD_CACHE_FILE="${HOME}/.aid/.update-check"
2933
+ _UPD_CACHED_LATEST=""
2934
+ [[ -f "${_UPD_CACHE_FILE}" ]] && _UPD_CACHED_LATEST="$(awk 'NR==2{print $1}' "${_UPD_CACHE_FILE}" 2>/dev/null)" || true
2935
+ if [[ -n "$_UPD_CLI_VER" && -n "$_UPD_CACHED_LATEST" && "$_UPD_CLI_VER" == "$_UPD_CACHED_LATEST" ]]; then
2936
+ echo "CLI is current (v${_UPD_CLI_VER})"
2937
+ exit 0
2938
+ fi
2939
+ _aid_update_self_if_stale
2940
+ exit 0
2907
2941
  fi
2908
2942
  fi
2909
2943
 
2910
2944
  # ---- Self-update-if-needed preamble (FF-3 / CLI-2 / task-079) --------------
2911
- # For 'update [<tool>]' only (not 'add', not 'update self'). Ensures the CLI
2912
- # is current before the per-repo migration runs (FR38 / OQ-6). WARN-not-fail.
2945
+ # For 'update' inside an AID repo only (not 'add', not 'update self').
2946
+ # Ensures the CLI is current before the per-repo tool-update runs. WARN-not-fail.
2913
2947
  if [[ "$SUBCMD" == "update" ]]; then
2914
2948
  _aid_update_self_if_stale
2915
2949
  fi
@@ -3112,7 +3146,53 @@ case "$SUBCMD" in
3112
3146
  fi
3113
3147
  fi
3114
3148
 
3115
- # C-table (for 'update [tool]'): register-on-encounter + format gate.
3149
+ # ---------------------------------------------------------------------------
3150
+ # FR11: aid add version selection (same-version invariant).
3151
+ # First-tool (no existing tools in manifest): install at the CLI version.
3152
+ # Additional-tool (manifest already has >=1 tool): install at the EXISTING
3153
+ # tools' version to keep the repo uniform. add does NOT force a repo-wide
3154
+ # update. --version on add must apply to ALL tools or error (mixed-version
3155
+ # repo would result if the requested version differs from the existing one).
3156
+ # ---------------------------------------------------------------------------
3157
+ if [[ "$SUBCMD" == "add" ]]; then
3158
+ _FR11_CLI_VER=""
3159
+ [[ -f "${AID_CODE_HOME}/VERSION" ]] && \
3160
+ _FR11_CLI_VER="$(tr -d '[:space:]' < "${AID_CODE_HOME}/VERSION")"
3161
+ _FR11_EXISTING_VER=""
3162
+ if [[ -f "$_AID_MANIFEST" ]]; then
3163
+ _FR11_FIRST_TOOL="$(manifest_list_tools "$_AID_MANIFEST" | head -1)"
3164
+ if [[ -n "$_FR11_FIRST_TOOL" ]]; then
3165
+ _FR11_EXISTING_VER="$(manifest_read_tool_version "$_AID_MANIFEST" "$_FR11_FIRST_TOOL")"
3166
+ fi
3167
+ fi
3168
+
3169
+ if [[ -n "$_AID_VERSION_ARG" ]]; then
3170
+ # --version on add: validate it won't create a mixed-version repo.
3171
+ if [[ -n "$_FR11_EXISTING_VER" && "$_AID_VERSION_ARG" != "$_FR11_EXISTING_VER" ]]; then
3172
+ echo "ERROR: aid add: --version ${_AID_VERSION_ARG} would create a mixed-version repo." >&2
3173
+ echo " Existing tools are at v${_FR11_EXISTING_VER}. Either:" >&2
3174
+ echo " - Omit --version to install at the repo version (v${_FR11_EXISTING_VER}), or" >&2
3175
+ echo " - Run 'aid update --version ${_AID_VERSION_ARG}' first to advance the whole repo." >&2
3176
+ rm -rf "$_AID_STAGING_BASE"
3177
+ exit 2
3178
+ fi
3179
+ # --version provided and no conflict: apply to all tools (passed through to staging).
3180
+ elif [[ -n "$_FR11_EXISTING_VER" ]]; then
3181
+ # Additional-tool: pin staging to the existing repo version (not the CLI version).
3182
+ _AID_VERSION_ARG="$_FR11_EXISTING_VER"
3183
+ # Skew notice when CLI is ahead of the repo version.
3184
+ if [[ -n "$_FR11_CLI_VER" ]] && _semver_lt "$_FR11_EXISTING_VER" "$_FR11_CLI_VER"; then
3185
+ echo "repo is at v${_FR11_EXISTING_VER}; new tool(s) installed at v${_FR11_EXISTING_VER} to keep the repo uniform. Run 'aid update' to advance all tools to v${_FR11_CLI_VER}."
3186
+ fi
3187
+ else
3188
+ # First-tool: pin to CLI version (bundle supplies its own version; skip if so).
3189
+ if [[ -z "$_AID_FROM_BUNDLE" && -n "$_FR11_CLI_VER" ]]; then
3190
+ _AID_VERSION_ARG="$_FR11_CLI_VER"
3191
+ fi
3192
+ fi
3193
+ fi
3194
+
3195
+ # C-table (for 'update'): register-on-encounter + format gate.
3116
3196
  # The missing-.aid/ case was already intercepted above (pre-resolve-tools).
3117
3197
  if [[ "$SUBCMD" == "update" ]]; then
3118
3198
  # C-table register-on-encounter (best-effort).
@@ -3121,15 +3201,75 @@ case "$SUBCMD" in
3121
3201
  _aid_format_gate "${_AID_TARGET}" || exit $?
3122
3202
  fi
3123
3203
 
3204
+ # ---------------------------------------------------------------------------
3205
+ # FR10 Stage-all-first atomicity (task-009):
3206
+ # PHASE 1: Stage ALL tools (resolve version, fetch, checksum-verify, extract
3207
+ # to temp) BEFORE any destination write. A failure here aborts with
3208
+ # zero destination mutation.
3209
+ # ---------------------------------------------------------------------------
3210
+ declare -A _STAGE_MAP=() # tool -> staging_dir
3211
+ _STAGE_VERSION="" # single resolved version for all tools
3212
+
3124
3213
  for _tool in "${_AID_TOOLS[@]}"; do
3125
- echo ""
3126
3214
  _prepare_tool_staging_aid "$_tool" "$_AID_VERSION_ARG" "$_AID_FROM_BUNDLE"
3127
- echo "Installing ${_tool} v${_AID_RESOLVED_VERSION} -> ${_AID_TARGET}"
3128
- install_tool "$_AID_STAGING_DIR" "$_tool" "$_AID_TARGET" "$_AID_RESOLVED_VERSION" "$_AID_FORCE" || exit $?
3215
+ _STAGE_MAP["$_tool"]="$_AID_STAGING_DIR"
3216
+ # All tools must be at the same version (the first resolved wins, the rest
3217
+ # confirm by the same --version / bundle arg).
3218
+ if [[ -z "$_STAGE_VERSION" ]]; then
3219
+ _STAGE_VERSION="$_AID_RESOLVED_VERSION"
3220
+ fi
3221
+ done
3222
+
3223
+ # ---------------------------------------------------------------------------
3224
+ # FR10 --dry-run: print the plan and exit with no writes.
3225
+ # ---------------------------------------------------------------------------
3226
+ if [[ "${_AID_DRY_RUN:-0}" -eq 1 ]]; then
3227
+ echo "--- aid ${SUBCMD} --dry-run plan (no writes) ---"
3228
+ echo "Target: ${_AID_TARGET}"
3229
+ echo "Version: ${_STAGE_VERSION:-<current>}"
3230
+ for _tool in "${_AID_TOOLS[@]}"; do
3231
+ echo ""
3232
+ echo "Tool: ${_tool}"
3233
+ _dry_staging="${_STAGE_MAP[$_tool]}"
3234
+ # List files that would be copied.
3235
+ while IFS= read -r -d '' _dry_f; do
3236
+ echo " copy: ${_dry_f#${_dry_staging}/} -> ${_AID_TARGET}"
3237
+ done < <(find "${_dry_staging}" -type f -print0 2>/dev/null | sort -z)
3238
+ # List files that would be MOVED TO TRASH by the retired-root migration sweep
3239
+ # (marker 1: aid-* prefix; marker 2: inside an aid/ subtree).
3240
+ # Uses list_only=1 mode of _migrate_retired_layout (no writes).
3241
+ _dry_removed_out="$(_migrate_retired_layout "${_AID_TARGET}" "${_tool}" 1 2>/dev/null)"
3242
+ if [[ -n "$_dry_removed_out" ]]; then
3243
+ echo " Would MOVE TO TRASH (retired-layout migration):"
3244
+ printf '%s\n' "$_dry_removed_out"
3245
+ fi
3246
+ done
3247
+ echo ""
3248
+ echo "--- end dry-run plan ---"
3249
+ rm -rf "$_AID_STAGING_BASE"
3250
+ exit 0
3251
+ fi
3252
+
3253
+ # ---------------------------------------------------------------------------
3254
+ # PHASE 2: Commit all staged tools.
3255
+ # If any commit fails, exit non-zero with a re-run-to-heal message.
3256
+ # aid update is idempotent: re-running drives every tool to the target version.
3257
+ # ---------------------------------------------------------------------------
3258
+ for _tool in "${_AID_TOOLS[@]}"; do
3259
+ echo ""
3260
+ echo "Installing ${_tool} v${_STAGE_VERSION} -> ${_AID_TARGET}"
3261
+ install_tool "${_STAGE_MAP[$_tool]}" "$_tool" "$_AID_TARGET" "$_STAGE_VERSION" "$_AID_FORCE" || {
3262
+ _COMMIT_RC=$?
3263
+ echo "" >&2
3264
+ echo "ERROR: aid ${SUBCMD} failed mid-commit for tool '${_tool}' (rc=${_COMMIT_RC})." >&2
3265
+ echo " The repo may be at mixed versions. Re-run 'aid update' to heal." >&2
3266
+ rm -rf "$_AID_STAGING_BASE"
3267
+ exit "${_COMMIT_RC}"
3268
+ }
3129
3269
  done
3130
3270
 
3131
3271
  echo ""
3132
- echo "Done. AID ${_AID_RESOLVED_VERSION:-} installed into: ${_AID_TARGET}"
3272
+ echo "Done. AID ${_STAGE_VERSION:-} installed into: ${_AID_TARGET}"
3133
3273
 
3134
3274
  # B-table (for 'add'): tier-aware registration after successful install.
3135
3275
  # Decision #3 (unwritable) already handled above with error+abort.
@@ -3139,7 +3279,7 @@ case "$SUBCMD" in
3139
3279
  _btab_tier="$(_aid_resolve_tier "$_AID_TARGET")"
3140
3280
  registry_register "$_AID_TARGET" "$_btab_tier"
3141
3281
  else
3142
- # 'update [tool]': C-table register-on-encounter already ran above.
3282
+ # 'update': C-table register-on-encounter already ran above.
3143
3283
  # The post-install register is idempotent; route via user tier.
3144
3284
  registry_register "$_AID_TARGET" "user"
3145
3285
  fi