siesa-agents 2.1.89 → 2.1.90
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.
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sa-init-devops
|
|
3
|
-
description: 'Bootstrap the DevOps workspace and
|
|
3
|
+
description: 'Bootstrap the DevOps workspace and commands in Siesa-Agents in one shot: clones business-shared-deploy into `_siesa-agents/devops/`, detects conflicts between local and upstream copies of the `flow-*`/`agent-*` DevOps slash commands and the deploy workflows (asks the user via AskUserQuestion which side to keep when there are diffs), then MOVES the chosen versions into `.claude/commands/` (and `claude/commands/`) so `/flow-aplicar`, `/flow-nuevo-servicio`, `/flow-auditar-servicio`, etc. become immediately invocable, MOVES the workflows into `.github/workflows/`, deletes the now-empty `.claude/` and `.github/` directories from the clone, then mirrors the cleaned tree to `siesa-agents/devops/`. Use whenever the user wants to set up the DevOps workspace for the first time, refresh it after upstream changes, mentions needing terraform/k8s/environments files, or when any `flow-*` deploy command is missing from Claude Code.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Init DevOps — clone the workspace + materialize the DevOps
|
|
6
|
+
# Init DevOps — clone the workspace + materialize the DevOps commands
|
|
7
7
|
|
|
8
|
-
**Goal:** In a single invocation, populate Siesa-Agents with everything an engineer needs to run the Finance DevOps
|
|
8
|
+
**Goal:** In a single invocation, populate Siesa-Agents with everything an engineer needs to run the Finance DevOps commands. After a successful run the layout is:
|
|
9
9
|
|
|
10
10
|
| Location | Contents |
|
|
11
11
|
|---|---|
|
|
12
|
-
| `_siesa-agents/devops/` | Git working tree cloned from `
|
|
12
|
+
| `_siesa-agents/devops/` | Git working tree cloned from `business-shared-deploy`. **Only** the deployment workspace: `terraform/`, `k8s/`, `environments/`, `scripts/`, `cicd-templates/`, `docs/`, `seed/`, `CLAUDE.md`, `README.md`, `.gitignore`. The `.claude/` and `.github/` directories from upstream are wiped from the clone after their contents are moved to Siesa-Agents level. |
|
|
13
13
|
| `siesa-agents/devops/` | Flat snapshot mirror of the cleaned clone (no `.git/`, no `.claude/`, no `.github/`). The npm-publishable copy. |
|
|
14
|
-
| `.claude/
|
|
15
|
-
| `claude/
|
|
16
|
-
| `.github/workflows/*.yml` | The
|
|
14
|
+
| `.claude/commands/*.md` | The DevOps slash commands moved out of the clone: `flow-aplicar`, `flow-auditar-servicio`, `flow-nueva-transversal`, `flow-nuevo-ambiente`, `flow-nuevo-servicio`, `flow-onboard-db`, `flow-registrar-permisos`, `agent-sre-sentinel`. |
|
|
15
|
+
| `claude/commands/*.md` | Same commands, in the npm mirror. |
|
|
16
|
+
| `.github/workflows/*.yml` | The deploy workflow files moved out of the clone (`infra-pipeline-dev.yml`, `infra-pipeline-qa.yml`, `infra-pipeline-shared.yml`). **These do not fire from Siesa-Agents** — gitignored, for visibility only. They fire from `business-shared-deploy` where they originally live. |
|
|
17
17
|
|
|
18
|
-
**Why this matters:** Without this skill, Siesa-Agents is missing every Finance DevOps capability. The DevOps
|
|
18
|
+
**Why this matters:** Without this skill, Siesa-Agents is missing every Finance DevOps capability. The DevOps commands cannot live statically in this repo because they evolve with the deploy workspace (`environments/`, `terraform/`, `k8s/`) — keeping them in sync would require a PR every time Finance bumps the workspace. Instead the upstream `business-shared-deploy` repo is the single source of truth, and this skill pulls everything down on demand.
|
|
19
19
|
|
|
20
20
|
## When to use this skill
|
|
21
21
|
|
|
22
22
|
Trigger immediately when the user:
|
|
23
23
|
|
|
24
|
-
- Says "init devops", "setup devops", "clone
|
|
25
|
-
- Tries to invoke any deploy
|
|
26
|
-
- Tries to invoke a deploy
|
|
27
|
-
- Mentions wanting to refresh / pull latest from `
|
|
28
|
-
- Has just cloned Siesa-Agents and is preparing to use the deploy
|
|
24
|
+
- Says "init devops", "setup devops", "clone business-shared-deploy", "trae el workspace de despliegue", or similar
|
|
25
|
+
- Tries to invoke any deploy command (`/flow-aplicar`, `/flow-nuevo-servicio`, etc.) and Claude Code reports the command is not available
|
|
26
|
+
- Tries to invoke a deploy command and it complains about missing paths (`environments/shared.yaml`, `terraform/environments/`, `k8s/overlays/`, etc.)
|
|
27
|
+
- Mentions wanting to refresh / pull latest from `business-shared-deploy`
|
|
28
|
+
- Has just cloned Siesa-Agents and is preparing to use the deploy commands
|
|
29
29
|
|
|
30
|
-
Do **not** trigger for: editing the deploy
|
|
30
|
+
Do **not** trigger for: editing the deploy commands themselves (those edits must go to `business-shared-deploy`), modifying terraform/k8s contents (those go to `business-shared-deploy` too), or anything unrelated to bootstrapping.
|
|
31
31
|
|
|
32
32
|
## Prerequisites
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ Do **not** trigger for: editing the deploy skills themselves (those edits must g
|
|
|
35
35
|
- The user has network access to GitHub.com.
|
|
36
36
|
- The helper script lives at `_siesa-agents/sa/sa-init-devops/scripts/sa-init-devops.js` (source of truth) and is mirrored to `siesa-agents/sa/sa-init-devops/scripts/sa-init-devops.js` (npm copy). Prefer the source-of-truth path when both exist.
|
|
37
37
|
|
|
38
|
-
If the script is missing or `git` is unavailable, stop and tell the user — do not improvise the work inline. The script handles all the edge cases (existing clone with wrong remote, partial state, conflicting local edits,
|
|
38
|
+
If the script is missing or `git` is unavailable, stop and tell the user — do not improvise the work inline. The script handles all the edge cases (existing clone with wrong remote, partial state, conflicting local edits, command/workflow copy overwrites) that an inline shell sequence would miss.
|
|
39
39
|
|
|
40
40
|
## Workflow
|
|
41
41
|
|
|
@@ -52,19 +52,19 @@ node _siesa-agents/sa/sa-init-devops/scripts/sa-init-devops.js
|
|
|
52
52
|
Default mode does:
|
|
53
53
|
|
|
54
54
|
1. **Clone or update.** `git clone` (if `_siesa-agents/devops/` does not exist) **or** `git checkout HEAD -- .claude .github && git fetch && git checkout main && git pull --ff-only` (if it does). The `git checkout HEAD -- .claude .github` step restores files that the previous run wiped so the pull doesn't fail on "uncommitted deletions".
|
|
55
|
-
2. **Dry-run conflict detection.** For each upstream `.claude/
|
|
56
|
-
3. **If any local file differs from upstream → abort.** Exit 4 with `status: "conflicts"` and lists `
|
|
55
|
+
2. **Dry-run conflict detection.** For each upstream `.claude/commands/*.md` and `.github/workflows/*.yml`, compare to the local copy in Siesa-Agents (`.claude/commands/`, `claude/commands/`, and `.github/workflows/`).
|
|
56
|
+
3. **If any local file differs from upstream → abort.** Exit 4 with `status: "conflicts"` and lists `command_conflicts` / `workflow_conflicts`. Nothing is mutated. The model proceeds to Step 2.
|
|
57
57
|
4. **If no conflicts → apply.** Copy the upstream files into Siesa-Agents (creating new files, leaving identical ones alone), then delete `.claude/` and `.github/` from the clone working tree, then mirror the cleaned tree to `siesa-agents/devops/`. Exit 0.
|
|
58
58
|
|
|
59
59
|
### Step 2 — Handle conflicts (only when `status: "conflicts"`)
|
|
60
60
|
|
|
61
|
-
When the JSON output has `status: "conflicts"`, **ask the user which side to keep** before any local file is changed. Use `AskUserQuestion` with the list of conflicting items from `
|
|
61
|
+
When the JSON output has `status: "conflicts"`, **ask the user which side to keep** before any local file is changed. Use `AskUserQuestion` with the list of conflicting items from `command_conflicts` and `workflow_conflicts`.
|
|
62
62
|
|
|
63
63
|
For most cases, one question with three options is enough:
|
|
64
64
|
|
|
65
65
|
```
|
|
66
|
-
question: "Estos N archivos locales difieren del upstream
|
|
67
|
-
<list with
|
|
66
|
+
question: "Estos N archivos locales difieren del upstream business-shared-deploy:
|
|
67
|
+
<list with command_conflicts + workflow_conflicts>
|
|
68
68
|
¿Cómo resuelvo?"
|
|
69
69
|
options:
|
|
70
70
|
- "Take all from upstream" (overwrite all conflicts with upstream version)
|
|
@@ -87,7 +87,7 @@ node _siesa-agents/sa/sa-init-devops/scripts/sa-init-devops.js --keep-local
|
|
|
87
87
|
If the user picks **"Resolve per file"**, ask one more question per conflicting item (with `AskUserQuestion`, max 4 items per call — chunk if needed) collecting which ones to keep local, then re-run with `--keep-local=<csv>`:
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
|
-
node _siesa-agents/sa/sa-init-devops/scripts/sa-init-devops.js --keep-local=
|
|
90
|
+
node _siesa-agents/sa/sa-init-devops/scripts/sa-init-devops.js --keep-local=flow-aplicar.md,infra-pipeline-qa.yml
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
The named items keep their local version; everything else takes upstream.
|
|
@@ -96,21 +96,21 @@ The named items keep their local version; everything else takes upstream.
|
|
|
96
96
|
|
|
97
97
|
After the (possibly second) successful invocation, read the `status` field:
|
|
98
98
|
|
|
99
|
-
- **`status: "cloned"`** — Fresh clone. Tell the user: "Cloned `
|
|
100
|
-
- **`status: "updated"`** — Existing clone pulled new commits. Report `before_sha..after_sha` and the per-category counts (`
|
|
99
|
+
- **`status: "cloned"`** — Fresh clone. Tell the user: "Cloned `business-shared-deploy`. Created `<N>` commands + `<M>` workflows in Siesa-Agents. The deploy commands (`/flow-aplicar`, `/flow-nuevo-servicio`, etc.) are now available in Claude Code."
|
|
100
|
+
- **`status: "updated"`** — Existing clone pulled new commits. Report `before_sha..after_sha` and the per-category counts (`commands_created`, `commands_overwritten`, `commands_kept_local`, `commands_unchanged`, same for workflows).
|
|
101
101
|
- **`status: "already-cloned"`** — Local clone was already at the latest HEAD; no upstream change. The counts reflect what happened in the apply step (likely all `unchanged` if everything matched).
|
|
102
102
|
- **`status: "conflicts"`** — Should only appear on the FIRST run when conflicts exist; never on a re-run with a policy flag. If you see this twice, surface it as a bug.
|
|
103
103
|
- **`status: "invalid-source"`** — `_siesa-agents/devops/` exists but is not a git working tree. Surface the message verbatim and stop. Do not try to delete the directory automatically.
|
|
104
104
|
- **`status: "wrong-remote"`** — Same directory is a clone of a different repo. Surface the message and stop.
|
|
105
105
|
- **`status: "clone-failed"` / `"fetch-failed"` / `"pull-failed"` / `"checkout-failed"`** — Git operation failed. Surface the underlying git error verbatim. Common causes: no network, branch diverged, local edits blocking ff-pull.
|
|
106
106
|
|
|
107
|
-
### Step 3 — Remind the user about cwd for the deploy
|
|
107
|
+
### Step 3 — Remind the user about cwd for the deploy commands
|
|
108
108
|
|
|
109
109
|
After a successful run (any of `cloned`, `updated`, `already-cloned`), remind the user once:
|
|
110
110
|
|
|
111
|
-
>
|
|
111
|
+
> Los comandos de despliegue (`/flow-aplicar`, `/flow-nuevo-servicio`, etc.) asumen que el cwd está dentro de `_siesa-agents/devops/`. Cambia de directorio (`cd _siesa-agents/devops/`) antes de invocarlos, para que las rutas relativas (`environments/`, `terraform/`, `k8s/`) se resuelvan correctamente.
|
|
112
112
|
|
|
113
|
-
If the user invokes one of those
|
|
113
|
+
If the user invokes one of those commands from elsewhere, the relative paths in their instructions won't resolve and they'll see confusing "file not found" errors.
|
|
114
114
|
|
|
115
115
|
## Optional flags
|
|
116
116
|
|
|
@@ -118,28 +118,27 @@ If the user invokes one of those skills from elsewhere, the relative paths in th
|
|
|
118
118
|
- `--mirror-only` — Skip the git + move steps; rebuild `siesa-agents/devops/` from the existing clone. Useful when only the mirror is out of sync.
|
|
119
119
|
- `--take-upstream` — Resolve all conflicts in favor of upstream (overwrite every conflicting local file). Use after the user confirms they want the upstream versions.
|
|
120
120
|
- `--keep-local` — Keep every local file as-is; only create files that are entirely new in upstream. Use when the user wants to preserve all local edits.
|
|
121
|
-
- `--keep-local=<csv>` — Keep these specific items local (comma-separated names matching `
|
|
121
|
+
- `--keep-local=<csv>` — Keep these specific items local (comma-separated names matching `command_conflicts` / `workflow_conflicts` entries); overwrite all other conflicts with upstream. Example: `--keep-local=flow-aplicar.md,infra-pipeline-qa.yml`.
|
|
122
122
|
|
|
123
123
|
The three policy flags are mutually exclusive; passing more than one returns exit code 1.
|
|
124
124
|
|
|
125
125
|
## Edge cases
|
|
126
126
|
|
|
127
|
-
- **
|
|
128
|
-
- **The
|
|
129
|
-
- **
|
|
130
|
-
- **Workflows in Siesa-Agents `.github/workflows/`** — moved for inspection but gitignored (see `.gitignore`), so engineers can read them locally but they never fire on Siesa-Agents PRs. The same workflows fire on PRs / pushes to `architecture-sa-devops`, which is where they live.
|
|
127
|
+
- **Command conflicts.** The default run aborts on any conflict (local file differs from upstream) and lists them in `command_conflicts` / `workflow_conflicts`. The model then asks the user with `AskUserQuestion` and re-runs with `--take-upstream`, `--keep-local`, or `--keep-local=<csv>`. Engineers who want their custom edits to survive automatic refresh should fork `business-shared-deploy` and update `REMOTE_URL` at the top of the helper script — that keeps their fork as the upstream source of truth.
|
|
128
|
+
- **The clone shows the moved files as "deleted" in `git status` after every run.** Expected. The script moves `.claude/commands/*.md` and `.github/workflows/*.yml` out of the clone and deletes those directories, but the files are still tracked in upstream HEAD. The next run restores them via `git checkout HEAD -- .claude .github` before pulling, then moves and deletes them again. Engineers should never `git add` or `git commit` those deletions inside `_siesa-agents/devops/` — they are intentional working-tree-only state.
|
|
129
|
+
- **Workflows in Siesa-Agents `.github/workflows/`** — moved for inspection but gitignored (see `.gitignore`), so engineers can read them locally but they never fire on Siesa-Agents PRs. The same workflows fire on PRs / pushes to `business-shared-deploy`, which is where they live.
|
|
131
130
|
- **`_siesa-agents/devops/` exists from a previous run with local edits to non-managed paths.** The restore step touches only `.claude` and `.github`; edits to `environments/`, `terraform/`, `k8s/`, etc. survive the restore step but `git pull --ff-only` will fail if they conflict with upstream. The script surfaces the git error verbatim. The user should commit, stash, or discard their local changes and re-run.
|
|
132
|
-
- **The directory exists but isn't a clone of `
|
|
133
|
-
- **The mirror or local
|
|
131
|
+
- **The directory exists but isn't a clone of `business-shared-deploy`.** The script reports `invalid-source` or `wrong-remote` and refuses to delete. The user must move it aside manually.
|
|
132
|
+
- **The mirror or local commands/workflows have manual edits.** The script overwrites them on every run — that is by design. If you need to preserve edits, make them in the upstream `business-shared-deploy` repo, not locally.
|
|
134
133
|
- **No `git` on PATH.** The first `tryGit` call fails; the script surfaces a `clone-failed` or `fetch-failed` status. Tell the user to install git or add it to PATH.
|
|
135
134
|
|
|
136
135
|
## Upstream URL
|
|
137
136
|
|
|
138
|
-
The script
|
|
137
|
+
The script points at `https://github.com/SiesaTeams/business-shared-deploy.git`. If Finance forks or relocates the deploy repo, update `REMOTE_URL` at the top of the helper script (both the source-of-truth and mirror copies) and ship the change.
|
|
139
138
|
|
|
140
139
|
## Reminders
|
|
141
140
|
|
|
142
|
-
- Step 1 is always safe to re-run. Idempotency is built into the script — on `already-cloned` it does nothing destructive other than refresh the mirror and re-copy the
|
|
141
|
+
- Step 1 is always safe to re-run. Idempotency is built into the script — on `already-cloned` it does nothing destructive other than refresh the mirror and re-copy the commands/workflows from upstream (which is the intended behavior — keeping local in sync with upstream).
|
|
143
142
|
- Never delete `_siesa-agents/devops/` from this skill. The user owns that directory once cloned; they may have in-progress edits.
|
|
144
|
-
- Do not modify the DevOps
|
|
143
|
+
- Do not modify the DevOps commands themselves from this skill. If a deploy command's behavior needs to change, that is a PR to `business-shared-deploy`, not a local edit (which would be wiped on the next run).
|
|
145
144
|
- The script's JSON output is the contract. If a future field appears (e.g. a new status), surface it to the user rather than guessing what it means.
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sa-init-devops.js — Bootstrap the DevOps workspace and
|
|
2
|
+
// sa-init-devops.js — Bootstrap the DevOps workspace and commands in Siesa-Agents.
|
|
3
3
|
//
|
|
4
4
|
// In one invocation the script:
|
|
5
|
-
// 1. Clones (or pulls)
|
|
6
|
-
// 2. Detects conflicts: for each upstream `.claude/
|
|
5
|
+
// 1. Clones (or pulls) business-shared-deploy into `_siesa-agents/devops/`.
|
|
6
|
+
// 2. Detects conflicts: for each upstream `.claude/commands/*.md` and
|
|
7
7
|
// `.github/workflows/*.yml`, compares to the local copy in Siesa-Agents. If any local
|
|
8
8
|
// file differs from upstream and no policy flag was passed, aborts with `status:
|
|
9
9
|
// "conflicts"` and lists the conflicting items so the caller can ask the user how to
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// entirely new in upstream.
|
|
20
20
|
// --keep-local=<csv> Keep these specific files (comma-separated names) local;
|
|
21
21
|
// overwrite the rest. Example:
|
|
22
|
-
// --keep-local=
|
|
22
|
+
// --keep-local=flow-aplicar.md,infra-pipeline-qa.yml
|
|
23
23
|
//
|
|
24
24
|
// Idempotency: between runs the clone's `.claude/` and `.github/` directories are gone from
|
|
25
25
|
// the working tree but still tracked in upstream HEAD. The script restores those paths
|
|
@@ -46,9 +46,7 @@ const fs = require('fs')
|
|
|
46
46
|
const path = require('path')
|
|
47
47
|
const { spawnSync } = require('child_process')
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
// once the repo is transferred to the SiesaTeams organization.
|
|
51
|
-
const REMOTE_URL = 'https://github.com/ssancheze912/architecture-sa-devops.git'
|
|
49
|
+
const REMOTE_URL = 'https://github.com/SiesaTeams/business-shared-deploy.git'
|
|
52
50
|
const REMOTE_NAME = 'origin'
|
|
53
51
|
const DEFAULT_BRANCH = 'main'
|
|
54
52
|
|
|
@@ -171,56 +169,54 @@ function decideAction(name, upstreamPath, localPaths, policy) {
|
|
|
171
169
|
return 'conflict'
|
|
172
170
|
}
|
|
173
171
|
|
|
174
|
-
// MOVE `<sourceDir>/.claude/
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
function
|
|
178
|
-
const
|
|
179
|
-
status.
|
|
180
|
-
status.
|
|
181
|
-
status.
|
|
182
|
-
status.
|
|
183
|
-
status.
|
|
184
|
-
|
|
185
|
-
if (!fs.existsSync(
|
|
186
|
-
status.
|
|
172
|
+
// MOVE `<sourceDir>/.claude/commands/*.md` into Siesa-Agents `.claude/commands/` (and
|
|
173
|
+
// `claude/commands/`) according to the policy. Records conflicts in status.command_conflicts;
|
|
174
|
+
// the caller checks that before any mutation happens.
|
|
175
|
+
function planAndApplyCommands(sourceDir, projectRoot, status, policy, dryRun) {
|
|
176
|
+
const srcCommandsDir = path.join(sourceDir, '.claude', 'commands')
|
|
177
|
+
status.commands_created = []
|
|
178
|
+
status.commands_overwritten = []
|
|
179
|
+
status.commands_kept_local = []
|
|
180
|
+
status.commands_unchanged = []
|
|
181
|
+
status.command_conflicts = []
|
|
182
|
+
|
|
183
|
+
if (!fs.existsSync(srcCommandsDir)) {
|
|
184
|
+
status.commands_source_missing = true
|
|
187
185
|
return
|
|
188
186
|
}
|
|
189
187
|
|
|
190
|
-
const dstA = path.join(projectRoot, '.claude', '
|
|
191
|
-
const dstB = path.join(projectRoot, 'claude', '
|
|
188
|
+
const dstA = path.join(projectRoot, '.claude', 'commands')
|
|
189
|
+
const dstB = path.join(projectRoot, 'claude', 'commands')
|
|
192
190
|
|
|
193
|
-
for (const entry of fs.readdirSync(
|
|
194
|
-
if (!entry.
|
|
195
|
-
if (!entry.name.
|
|
196
|
-
if (entry.name === 'sa-init-devops') continue // never overwrite the bootstrap itself
|
|
197
|
-
const srcSkill = path.join(srcSkillsDir, entry.name, 'SKILL.md')
|
|
198
|
-
if (!fs.existsSync(srcSkill)) continue
|
|
191
|
+
for (const entry of fs.readdirSync(srcCommandsDir, { withFileTypes: true })) {
|
|
192
|
+
if (!entry.isFile()) continue
|
|
193
|
+
if (!entry.name.endsWith('.md')) continue
|
|
199
194
|
|
|
200
|
-
const
|
|
201
|
-
const
|
|
195
|
+
const srcCommand = path.join(srcCommandsDir, entry.name)
|
|
196
|
+
const dstCommandA = path.join(dstA, entry.name)
|
|
197
|
+
const dstCommandB = path.join(dstB, entry.name)
|
|
202
198
|
|
|
203
|
-
const action = decideAction(entry.name,
|
|
199
|
+
const action = decideAction(entry.name, srcCommand, [dstCommandA, dstCommandB], policy)
|
|
204
200
|
|
|
205
201
|
if (action === 'conflict') {
|
|
206
|
-
status.
|
|
202
|
+
status.command_conflicts.push(entry.name)
|
|
207
203
|
continue
|
|
208
204
|
}
|
|
209
205
|
if (action === 'identical') {
|
|
210
|
-
status.
|
|
206
|
+
status.commands_unchanged.push(entry.name)
|
|
211
207
|
continue
|
|
212
208
|
}
|
|
213
209
|
if (dryRun) continue
|
|
214
210
|
|
|
215
211
|
if (action === 'create' || action === 'overwrite') {
|
|
216
|
-
fs.mkdirSync(
|
|
217
|
-
fs.mkdirSync(
|
|
218
|
-
fs.copyFileSync(
|
|
219
|
-
fs.copyFileSync(
|
|
220
|
-
if (action === 'create') status.
|
|
221
|
-
else status.
|
|
212
|
+
fs.mkdirSync(dstA, { recursive: true })
|
|
213
|
+
fs.mkdirSync(dstB, { recursive: true })
|
|
214
|
+
fs.copyFileSync(srcCommand, dstCommandA)
|
|
215
|
+
fs.copyFileSync(srcCommand, dstCommandB)
|
|
216
|
+
if (action === 'create') status.commands_created.push(entry.name)
|
|
217
|
+
else status.commands_overwritten.push(entry.name)
|
|
222
218
|
} else if (action === 'keep-local') {
|
|
223
|
-
status.
|
|
219
|
+
status.commands_kept_local.push(entry.name)
|
|
224
220
|
}
|
|
225
221
|
}
|
|
226
222
|
}
|
|
@@ -356,11 +352,11 @@ function runCheck(projectRoot) {
|
|
|
356
352
|
// Dry-run conflict detection so callers can preview before running default mode.
|
|
357
353
|
restoreClonePaths(sourceDir)
|
|
358
354
|
const policy = { takeUpstream: false, keepLocalAll: false, keepLocalList: null }
|
|
359
|
-
|
|
355
|
+
planAndApplyCommands(sourceDir, projectRoot, state, policy, true)
|
|
360
356
|
planAndApplyWorkflows(sourceDir, projectRoot, state, policy, true)
|
|
361
357
|
|
|
362
358
|
state.status = 'already-cloned'
|
|
363
|
-
const conflictCount = state.
|
|
359
|
+
const conflictCount = state.command_conflicts.length + state.workflow_conflicts.length
|
|
364
360
|
state.message = `${sourceDir} is a clean clone of ${REMOTE_URL}. ${conflictCount} conflict(s) would be raised by a default run.`
|
|
365
361
|
emit(state)
|
|
366
362
|
return 0
|
|
@@ -475,30 +471,30 @@ function runClone(projectRoot, mirrorOnly, args) {
|
|
|
475
471
|
|
|
476
472
|
function finalizeMoveAndMirror(sourceDir, projectRoot, mirrorDir, status, policy, terminalStatus) {
|
|
477
473
|
// Phase A: dry-run to compute the action plan and conflicts.
|
|
478
|
-
|
|
474
|
+
planAndApplyCommands(sourceDir, projectRoot, status, policy, true)
|
|
479
475
|
planAndApplyWorkflows(sourceDir, projectRoot, status, policy, true)
|
|
480
476
|
|
|
481
|
-
const totalConflicts = status.
|
|
477
|
+
const totalConflicts = status.command_conflicts.length + status.workflow_conflicts.length
|
|
482
478
|
if (totalConflicts > 0) {
|
|
483
479
|
// Abort without mutating. The caller (Claude/SKILL.md) asks the user and re-runs with
|
|
484
480
|
// a policy flag.
|
|
485
481
|
status.status = 'conflicts'
|
|
486
|
-
status.message = `Detected ${status.
|
|
482
|
+
status.message = `Detected ${status.command_conflicts.length} command conflict(s) and ${status.workflow_conflicts.length} workflow conflict(s). Re-run with one of: --take-upstream (overwrite all), --keep-local (keep all local versions), or --keep-local=<csv> (keep specific ones).`
|
|
487
483
|
emit(status)
|
|
488
484
|
return 4
|
|
489
485
|
}
|
|
490
486
|
|
|
491
487
|
// Phase B: real apply (no conflicts left to resolve).
|
|
492
|
-
|
|
488
|
+
planAndApplyCommands(sourceDir, projectRoot, status, policy, false)
|
|
493
489
|
planAndApplyWorkflows(sourceDir, projectRoot, status, policy, false)
|
|
494
490
|
cleanCloneDirs(sourceDir)
|
|
495
491
|
refreshMirror(sourceDir, mirrorDir, status)
|
|
496
492
|
|
|
497
493
|
status.status = terminalStatus
|
|
498
|
-
const created = status.
|
|
499
|
-
const over = status.
|
|
500
|
-
const kept = status.
|
|
501
|
-
const unch = status.
|
|
494
|
+
const created = status.commands_created.length + status.workflows_created.length
|
|
495
|
+
const over = status.commands_overwritten.length + status.workflows_overwritten.length
|
|
496
|
+
const kept = status.commands_kept_local.length + status.workflows_kept_local.length
|
|
497
|
+
const unch = status.commands_unchanged.length + status.workflows_unchanged.length
|
|
502
498
|
status.message = `${terminalStatus === 'cloned' ? 'Cloned' : (terminalStatus === 'updated' ? 'Pulled' : 'No upstream change')}. Created ${created}, overwrote ${over}, kept local ${kept}, unchanged ${unch}. Mirror refreshed.`
|
|
503
499
|
emit(status)
|
|
504
500
|
return 0
|