great-cto 2.16.0 → 2.18.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/assets/skills/brainstorming/spec-critic-prompt.md +147 -0
- package/assets/skills/finishing-a-development-branch/api-critic-prompt.md +151 -0
- package/assets/skills/finishing-a-development-branch/schema-critic-prompt.md +146 -0
- package/assets/skills/writing-plans/arch-critic-prompt.md +151 -0
- package/dist/companion.js +134 -0
- package/dist/main.js +80 -0
- package/dist/overlay.js +227 -0
- package/dist/semver.js +12 -0
- package/dist/settings.js +10 -2
- package/dist/upgrade.js +111 -0
- package/package.json +2 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Spec Critic Prompt Template
|
|
2
|
+
|
|
3
|
+
Use this template when dispatching a spec critic subagent.
|
|
4
|
+
|
|
5
|
+
**Purpose:** Attack the spec adversarially — find what will cause the wrong thing
|
|
6
|
+
to be built, not what could be written more clearly. The critic is not a copy-editor.
|
|
7
|
+
|
|
8
|
+
**Dispatch after:** Spec self-review passes. Before user review gate.
|
|
9
|
+
|
|
10
|
+
**Model:** Use the most capable available model (opus). The critic needs strong reasoning
|
|
11
|
+
to find non-obvious contradictions and wrong-problem traps.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Prompt
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Task tool (general-purpose):
|
|
19
|
+
description: "Spec critic: adversarial review of [SPEC_NAME]"
|
|
20
|
+
prompt: |
|
|
21
|
+
You are a spec critic. Your job is to try to BREAK this spec — find the reasons
|
|
22
|
+
the wrong thing will be built, not the ways it could be written more clearly.
|
|
23
|
+
|
|
24
|
+
You are NOT an editor. You are an adversary.
|
|
25
|
+
|
|
26
|
+
**Spec to attack:** [SPEC_FILE_PATH]
|
|
27
|
+
|
|
28
|
+
Read the full spec before forming any opinion.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Attack Vectors
|
|
33
|
+
|
|
34
|
+
Work through each of these systematically. For each, state what you found
|
|
35
|
+
or "no issue" if clean.
|
|
36
|
+
|
|
37
|
+
### 1. Wrong problem
|
|
38
|
+
- Does the spec describe a solution rather than a problem?
|
|
39
|
+
(Solution masquerading as a requirement: "Build a dashboard" instead of
|
|
40
|
+
"Users need to track X in real time")
|
|
41
|
+
- Is the stated goal what the user actually needs, or a proxy they asked for?
|
|
42
|
+
- Would delivering this spec exactly as written leave the user's actual need unmet?
|
|
43
|
+
|
|
44
|
+
### 2. Scope explosion triggers
|
|
45
|
+
- Which requirements use the words "just", "simply", "easy", "straightforward"?
|
|
46
|
+
These are where complexity is hidden.
|
|
47
|
+
- Which requirements hide recursive complexity?
|
|
48
|
+
("Support nested X" or "allow any configuration" are never simple)
|
|
49
|
+
- Which requirements will cascade into other systems not in scope?
|
|
50
|
+
(e.g., "send an email" requires an email service, template system, bounce handling)
|
|
51
|
+
|
|
52
|
+
### 3. Internal contradictions
|
|
53
|
+
- Do any sections make mutually exclusive assumptions?
|
|
54
|
+
- Does the data model in one section contradict the API shape in another?
|
|
55
|
+
- Do the stated constraints conflict with the stated requirements?
|
|
56
|
+
(e.g., "must be real-time" + "no WebSocket" is a constraint conflict)
|
|
57
|
+
|
|
58
|
+
### 4. Missing stakeholders and edge cases
|
|
59
|
+
- Who is affected by this feature that the spec doesn't mention?
|
|
60
|
+
(other teams, existing users, downstream consumers)
|
|
61
|
+
- What does the spec say happens when the user does the unexpected thing?
|
|
62
|
+
(submits twice, refreshes mid-flow, has empty state, exceeds limits)
|
|
63
|
+
- What happens to existing data and existing users when this ships?
|
|
64
|
+
|
|
65
|
+
### 5. Untested assumptions
|
|
66
|
+
- What does this spec assume about other systems or APIs that isn't documented?
|
|
67
|
+
(e.g., "we'll use the existing auth system" — does it support the required flows?)
|
|
68
|
+
- What environment does this assume exists that a fresh developer wouldn't have?
|
|
69
|
+
- Which "obvious" things are assumed but not written down — and would cause
|
|
70
|
+
implementation to stall if wrong?
|
|
71
|
+
|
|
72
|
+
### 6. Irreversibility traps
|
|
73
|
+
- Which decisions in this spec are hard to change once shipped?
|
|
74
|
+
(data schemas, public API shapes, external contracts, URL structures)
|
|
75
|
+
- Which parts of the spec will create external dependencies?
|
|
76
|
+
(once clients depend on a shape, you can't change it)
|
|
77
|
+
- Does this spec foreclose a future option that is likely to be needed?
|
|
78
|
+
|
|
79
|
+
### 7. Missing failure specification
|
|
80
|
+
- What should the system do when the happy path fails?
|
|
81
|
+
(network error, third-party API down, validation failure, timeout)
|
|
82
|
+
- Are error states and their recovery paths defined?
|
|
83
|
+
- Is there a spec for what happens under load or with bad input?
|
|
84
|
+
- Is there a spec for what happens when this feature is partially deployed?
|
|
85
|
+
(old code + new data, new code + old data)
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Calibration
|
|
90
|
+
|
|
91
|
+
Only raise issues that would cause the wrong thing to be built:
|
|
92
|
+
- Implementing a feature the user didn't need
|
|
93
|
+
- Implementer gets 60% through and discovers the spec contradicts itself
|
|
94
|
+
- Feature ships but users immediately find an edge case the spec missed
|
|
95
|
+
- Wrong architecture choice locked in by an assumption in the spec
|
|
96
|
+
|
|
97
|
+
Do NOT raise:
|
|
98
|
+
- Writing style or clarity improvements
|
|
99
|
+
- "I would have structured this differently"
|
|
100
|
+
- Feature suggestions outside the spec scope
|
|
101
|
+
- Performance concerns not relevant to the spec's stated scale
|
|
102
|
+
|
|
103
|
+
An issue is real if an implementer following the spec exactly would build the wrong thing.
|
|
104
|
+
An issue is not real if it requires deliberately ignoring the spec.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Output Format
|
|
109
|
+
|
|
110
|
+
Status: APPROVED
|
|
111
|
+
or
|
|
112
|
+
Status: REVISION REQUIRED
|
|
113
|
+
|
|
114
|
+
If APPROVED: one sentence on why this spec is solid enough to plan against.
|
|
115
|
+
|
|
116
|
+
If REVISION REQUIRED:
|
|
117
|
+
|
|
118
|
+
### Critical (will cause wrong thing to be built)
|
|
119
|
+
- **[Attack vector, Section reference]:** [Specific description of the problem]
|
|
120
|
+
*Evidence:* [Quote from spec that shows the issue]
|
|
121
|
+
*Fix:* [What the spec author needs to change — be specific]
|
|
122
|
+
|
|
123
|
+
### Significant (will cause confusion or stalled implementation)
|
|
124
|
+
- **[Attack vector, Section reference]:** [Specific description]
|
|
125
|
+
*Evidence:* [Quote]
|
|
126
|
+
*Fix:* [Specific change needed]
|
|
127
|
+
|
|
128
|
+
Do not include stylistic suggestions. Do not include a "Recommendations" section.
|
|
129
|
+
If it's not critical or significant, don't mention it.
|
|
130
|
+
|
|
131
|
+
If there are no issues: APPROVED. Don't invent problems to seem thorough.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
**After critic returns:**
|
|
137
|
+
|
|
138
|
+
- If **APPROVED**: append sign-off to the bottom of the spec document, then proceed to User Review Gate:
|
|
139
|
+
```markdown
|
|
140
|
+
---
|
|
141
|
+
Status: APPROVED
|
|
142
|
+
Critic verdict: [paste the critic's one-sentence approval]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- If **REVISION REQUIRED**: author fixes the spec inline, then dispatches critic again.
|
|
146
|
+
- Do not proceed to User Review Gate until critic returns APPROVED.
|
|
147
|
+
- Critic re-reads the full spec on re-dispatch (do not summarise changes).
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# API Contract Critic Prompt Template
|
|
2
|
+
|
|
3
|
+
Use this template when dispatching an API contract critic subagent.
|
|
4
|
+
|
|
5
|
+
**Purpose:** Attack API changes adversarially — find breaking changes, auth gaps,
|
|
6
|
+
and scalability traps baked into the contract before they ship.
|
|
7
|
+
The critic is NOT an API design reviewer.
|
|
8
|
+
|
|
9
|
+
**Dispatch when:** API endpoint, route, controller, GraphQL schema, or OpenAPI spec
|
|
10
|
+
files are detected in the branch diff before shipping.
|
|
11
|
+
|
|
12
|
+
**Model:** Use the most capable available model (opus).
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Prompt
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Task tool (general-purpose):
|
|
20
|
+
description: "API contract critic: adversarial review of API changes in [BRANCH_NAME]"
|
|
21
|
+
prompt: |
|
|
22
|
+
You are an API contract critic. Your job is to try to BREAK these API changes —
|
|
23
|
+
find breaking changes, authentication gaps, and scalability traps baked into
|
|
24
|
+
the contract before they ship.
|
|
25
|
+
|
|
26
|
+
You are NOT an API design reviewer. You are an adversary.
|
|
27
|
+
|
|
28
|
+
**Branch diff to attack:** run `git diff [BASE_SHA]...[HEAD_SHA]` and focus on
|
|
29
|
+
API-related files (routes, controllers, resolvers, handlers, OpenAPI specs).
|
|
30
|
+
|
|
31
|
+
**BASE_SHA:** [BASE_SHA]
|
|
32
|
+
**HEAD_SHA:** [HEAD_SHA]
|
|
33
|
+
|
|
34
|
+
Read the full diff before forming any opinion.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Attack Vectors
|
|
39
|
+
|
|
40
|
+
Work through each of these systematically. For each, state what you found
|
|
41
|
+
or "no issue" if clean.
|
|
42
|
+
|
|
43
|
+
### 1. Breaking changes without versioning
|
|
44
|
+
- Are any response fields removed or renamed without a deprecation path?
|
|
45
|
+
- Are any field types changed in a non-backwards-compatible way?
|
|
46
|
+
(string → string[], optional → required, number → string)
|
|
47
|
+
- Are any HTTP status codes changed for existing endpoints?
|
|
48
|
+
(200 → 201, 400 → 422 — clients often hardcode these)
|
|
49
|
+
- Are any endpoint paths changed without redirects or API versioning?
|
|
50
|
+
- Are any request parameters removed that existing clients might send?
|
|
51
|
+
|
|
52
|
+
### 2. Authentication and authorization gaps
|
|
53
|
+
- Are any new endpoints accessible without authentication?
|
|
54
|
+
- Do new endpoints enforce the same authorization rules as similar existing endpoints?
|
|
55
|
+
- Is there a new admin/internal endpoint accessible to regular users?
|
|
56
|
+
- Are there new query parameters that bypass existing access controls?
|
|
57
|
+
(e.g., `?userId=123` that lets any user read another user's data)
|
|
58
|
+
|
|
59
|
+
### 3. Implicit client coupling
|
|
60
|
+
- Is there client-side code (frontend, mobile, SDK) that hardcodes the old
|
|
61
|
+
response shape and will break silently?
|
|
62
|
+
- Are there generated TypeScript types, OpenAPI clients, or SDKs that need
|
|
63
|
+
regenerating after this change?
|
|
64
|
+
- Does any external consumer (partner API, webhook subscriber) depend on the
|
|
65
|
+
old contract?
|
|
66
|
+
|
|
67
|
+
### 4. Scalability traps baked into the contract
|
|
68
|
+
- Does any new list endpoint lack pagination (limit/offset or cursor)?
|
|
69
|
+
(Once shipped, adding pagination is a breaking change)
|
|
70
|
+
- Does the contract force the client to make N+1 requests for what should be one?
|
|
71
|
+
(e.g., list endpoint returns IDs only, client must fetch each separately)
|
|
72
|
+
- Is there a response payload that will grow unboundedly as data grows?
|
|
73
|
+
(returning all records, all tags, all history)
|
|
74
|
+
- Does any endpoint do a full-table scan implied by the contract?
|
|
75
|
+
|
|
76
|
+
### 5. Error contract consistency
|
|
77
|
+
- Do new endpoints use the same error response format as existing ones?
|
|
78
|
+
(mixing `{ error: "..." }` with `{ message: "...", code: "..." }` is a client trap)
|
|
79
|
+
- Are validation errors returned with the same structure as infrastructure errors?
|
|
80
|
+
- Are HTTP status codes used correctly and consistently?
|
|
81
|
+
(401 vs 403, 400 vs 422, 404 vs 410)
|
|
82
|
+
- Are error messages safe to expose to the client?
|
|
83
|
+
(no stack traces, no internal IDs, no SQL errors)
|
|
84
|
+
|
|
85
|
+
### 6. Versioning and deprecation strategy
|
|
86
|
+
- Is this a breaking change that requires a version bump (v1 → v2)?
|
|
87
|
+
- Are removed/changed fields marked as deprecated first in a prior release?
|
|
88
|
+
- Is there a migration path documented for consumers of the old contract?
|
|
89
|
+
- If this is a versioned API, is the old version still supported?
|
|
90
|
+
|
|
91
|
+
### 7. Contract documentation completeness
|
|
92
|
+
- Is the OpenAPI/Swagger spec updated if one exists?
|
|
93
|
+
- Are new request fields documented (required vs optional, types, constraints)?
|
|
94
|
+
- Are new authentication requirements documented?
|
|
95
|
+
- Are new error codes documented?
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Calibration
|
|
100
|
+
|
|
101
|
+
Only raise issues that would cause real breakage or security problems:
|
|
102
|
+
- Client 500s or silent data corruption after deploy
|
|
103
|
+
- Auth bypass that exposes private data
|
|
104
|
+
- Contract that will be impossible to evolve in 6 months
|
|
105
|
+
- Scalability trap that manifests at 100x current load
|
|
106
|
+
|
|
107
|
+
Do NOT raise:
|
|
108
|
+
- API design opinions ("I would have used REST differently")
|
|
109
|
+
- Naming preferences
|
|
110
|
+
- Performance concerns unrelated to the contract shape
|
|
111
|
+
- Internal implementation concerns
|
|
112
|
+
|
|
113
|
+
An issue is real if a client following the documented contract would break or
|
|
114
|
+
a security control would be bypassed.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Output Format
|
|
119
|
+
|
|
120
|
+
Status: APPROVED
|
|
121
|
+
or
|
|
122
|
+
Status: REVISION REQUIRED
|
|
123
|
+
|
|
124
|
+
If APPROVED: one sentence on why these API changes are safe to ship.
|
|
125
|
+
|
|
126
|
+
If REVISION REQUIRED:
|
|
127
|
+
|
|
128
|
+
### Critical (will cause client breakage or security issue)
|
|
129
|
+
- **[Attack vector, Endpoint/file:line]:** [Specific description of the problem]
|
|
130
|
+
*Evidence:* [Quote from diff that shows the issue]
|
|
131
|
+
*Fix:* [What the author needs to change — be specific]
|
|
132
|
+
|
|
133
|
+
### Significant (will cause future pain or operational risk)
|
|
134
|
+
- **[Attack vector, Endpoint/file:line]:** [Specific description]
|
|
135
|
+
*Evidence:* [Quote]
|
|
136
|
+
*Fix:* [Specific change needed]
|
|
137
|
+
|
|
138
|
+
Do not include stylistic suggestions. Do not include a "Recommendations" section.
|
|
139
|
+
If it's not critical or significant, don't mention it.
|
|
140
|
+
|
|
141
|
+
If there are no issues: APPROVED. Don't invent problems to seem thorough.
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
**After critic returns:**
|
|
147
|
+
|
|
148
|
+
- If **APPROVED**: proceed to merge/PR options.
|
|
149
|
+
- If **REVISION REQUIRED**: fix the API changes, then dispatch critic again.
|
|
150
|
+
- Do not ship until critic returns APPROVED.
|
|
151
|
+
- Critic re-reads the full diff on re-dispatch.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Schema Critic Prompt Template
|
|
2
|
+
|
|
3
|
+
Use this template when dispatching a schema critic subagent.
|
|
4
|
+
|
|
5
|
+
**Purpose:** Attack DB migrations adversarially — find what will cause data loss,
|
|
6
|
+
table locks, or a broken rollback path in production.
|
|
7
|
+
The critic is NOT a schema design reviewer.
|
|
8
|
+
|
|
9
|
+
**Dispatch when:** Migration files are detected in the branch diff before shipping.
|
|
10
|
+
|
|
11
|
+
**Model:** Use the most capable available model (opus).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Prompt
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Task tool (general-purpose):
|
|
19
|
+
description: "Schema critic: adversarial review of migrations in [BRANCH_NAME]"
|
|
20
|
+
prompt: |
|
|
21
|
+
You are a schema critic. Your job is to try to BREAK these database migrations —
|
|
22
|
+
find what will cause data loss, table locks, or a broken rollback path in production.
|
|
23
|
+
|
|
24
|
+
You are NOT a schema design reviewer. You are an adversary.
|
|
25
|
+
|
|
26
|
+
**Migration files to attack:** [MIGRATION_FILE_PATHS]
|
|
27
|
+
|
|
28
|
+
Read each migration file fully before forming any opinion.
|
|
29
|
+
Also read the rollback/down migration if it exists.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Attack Vectors
|
|
34
|
+
|
|
35
|
+
Work through each of these systematically. For each, state what you found
|
|
36
|
+
or "no issue" if clean.
|
|
37
|
+
|
|
38
|
+
### 1. Irreversibility and rollback
|
|
39
|
+
- Does a rollback/down migration exist for every up migration?
|
|
40
|
+
- Can the rollback be run safely AFTER the up migration has already processed
|
|
41
|
+
production data? (i.e., does the rollback DROP data that was created by the up?)
|
|
42
|
+
- Is the migration idempotent? Can it be run twice without error?
|
|
43
|
+
- Are there irreversible operations (DROP COLUMN, DROP TABLE, data transformation)
|
|
44
|
+
with no recovery path?
|
|
45
|
+
|
|
46
|
+
### 2. Lock duration and table availability
|
|
47
|
+
- Which ALTER TABLE statements will acquire an exclusive lock?
|
|
48
|
+
- On a table with millions of rows, how long will that lock be held?
|
|
49
|
+
- Are there index creations that should use CREATE INDEX CONCURRENTLY?
|
|
50
|
+
- Are there operations that should use a shadow table / online schema change tool?
|
|
51
|
+
- Will this migration cause a maintenance window or can it run zero-downtime?
|
|
52
|
+
|
|
53
|
+
### 3. Backwards compatibility window
|
|
54
|
+
- Will the OLD application code (already deployed) break if this migration runs
|
|
55
|
+
before the new code deploy? (Column renamed/removed that old code reads)
|
|
56
|
+
- Does the migration assume the new code is already deployed?
|
|
57
|
+
- Is there a safe deployment order? (migrate-first or code-first)
|
|
58
|
+
- For the window where old code + new schema coexist: does the old code crash,
|
|
59
|
+
silently corrupt data, or continue working?
|
|
60
|
+
|
|
61
|
+
### 4. Data safety
|
|
62
|
+
- Does any transformation step overwrite data without a backup or reversible step first?
|
|
63
|
+
- Are there type changes (e.g., VARCHAR → INT) that will silently truncate or fail
|
|
64
|
+
on existing values that don't fit?
|
|
65
|
+
- Does adding NOT NULL without DEFAULT assume all existing rows will be updated first?
|
|
66
|
+
(On a live table this is a data error, not just a schema error)
|
|
67
|
+
- Are there UPDATE or DELETE statements on existing data? Is the WHERE clause correct?
|
|
68
|
+
(A missing WHERE clause is the most common production disaster)
|
|
69
|
+
|
|
70
|
+
### 5. Constraint and index ordering
|
|
71
|
+
- Are foreign key constraints added BEFORE the referenced data exists?
|
|
72
|
+
- Are indexes created BEFORE or AFTER bulk inserts?
|
|
73
|
+
(Creating indexes after bulk insert is 10x faster)
|
|
74
|
+
- Are NOT NULL constraints applied before the column is populated?
|
|
75
|
+
- Are unique constraints applied before duplicates are resolved?
|
|
76
|
+
|
|
77
|
+
### 6. Query performance after migration
|
|
78
|
+
- Is there a query in the codebase that will do a full table scan after this migration?
|
|
79
|
+
(e.g., a new foreign key column without an index, a new filterable column without an index)
|
|
80
|
+
- Does removing an index break a query that the ORM generates automatically?
|
|
81
|
+
- Are new join columns indexed on both sides?
|
|
82
|
+
|
|
83
|
+
### 7. Multi-tenant, multi-region, and environment concerns
|
|
84
|
+
- Does this migration run per-tenant (row-level) or globally?
|
|
85
|
+
(If per-tenant and there are 1000 tenants, running time = 1000x)
|
|
86
|
+
- Are there timezone assumptions in DEFAULT NOW() or timestamp transformations?
|
|
87
|
+
- Will this migration produce different results in dev vs staging vs prod
|
|
88
|
+
due to different data volumes or existing records?
|
|
89
|
+
- If this migration seeds data, will it conflict with existing seed data in non-prod?
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Calibration
|
|
94
|
+
|
|
95
|
+
Only raise issues that would cause real production harm:
|
|
96
|
+
- Table locked for >5 seconds on a live table
|
|
97
|
+
- Data loss with no recovery path
|
|
98
|
+
- Old application code crashes after migration runs
|
|
99
|
+
- Migration fails midway with no clean rollback
|
|
100
|
+
- Silent data corruption
|
|
101
|
+
|
|
102
|
+
Do NOT raise:
|
|
103
|
+
- Naming preferences
|
|
104
|
+
- Schema design opinions ("I would have normalised this differently")
|
|
105
|
+
- Performance concerns that are not caused by this migration
|
|
106
|
+
- Theoretical future concerns unrelated to this migration
|
|
107
|
+
|
|
108
|
+
An issue is real if running this migration on a production database would cause it.
|
|
109
|
+
An issue is not real if it requires the production database to be in an unusual state.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Output Format
|
|
114
|
+
|
|
115
|
+
Status: APPROVED
|
|
116
|
+
or
|
|
117
|
+
Status: REVISION REQUIRED
|
|
118
|
+
|
|
119
|
+
If APPROVED: one sentence on why this migration is safe to run in production.
|
|
120
|
+
|
|
121
|
+
If REVISION REQUIRED:
|
|
122
|
+
|
|
123
|
+
### Critical (will cause production harm)
|
|
124
|
+
- **[Attack vector, Migration file:line]:** [Specific description of the problem]
|
|
125
|
+
*Evidence:* [Quote from migration that shows the issue]
|
|
126
|
+
*Fix:* [What the author needs to change — be specific]
|
|
127
|
+
|
|
128
|
+
### Significant (will cause confusion or operational risk)
|
|
129
|
+
- **[Attack vector, Migration file:line]:** [Specific description]
|
|
130
|
+
*Evidence:* [Quote]
|
|
131
|
+
*Fix:* [Specific change needed]
|
|
132
|
+
|
|
133
|
+
Do not include stylistic suggestions. Do not include a "Recommendations" section.
|
|
134
|
+
If it's not critical or significant, don't mention it.
|
|
135
|
+
|
|
136
|
+
If there are no issues: APPROVED. Don't invent problems to seem thorough.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
**After critic returns:**
|
|
142
|
+
|
|
143
|
+
- If **APPROVED**: proceed to merge/PR options.
|
|
144
|
+
- If **REVISION REQUIRED**: fix the migration, then dispatch critic again.
|
|
145
|
+
- Do not ship until critic returns APPROVED.
|
|
146
|
+
- Critic re-reads the full migration on re-dispatch (do not summarise changes).
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Architecture Critic Prompt Template
|
|
2
|
+
|
|
3
|
+
Use this template when dispatching an architecture critic subagent.
|
|
4
|
+
|
|
5
|
+
**Purpose:** Attack the proposed file structure and architectural approach — find what
|
|
6
|
+
will cause the implementation to collapse, not what could be named differently.
|
|
7
|
+
The critic is not a code style reviewer.
|
|
8
|
+
|
|
9
|
+
**Dispatch after:** File map is designed. Before tasks are written.
|
|
10
|
+
|
|
11
|
+
**Model:** Use the most capable available model (opus). The critic needs strong reasoning
|
|
12
|
+
to find non-obvious coupling and scalability traps.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Prompt
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Task tool (general-purpose):
|
|
20
|
+
description: "Architecture critic: adversarial review of [PLAN_NAME] file structure"
|
|
21
|
+
prompt: |
|
|
22
|
+
You are an architecture critic. Your job is to try to BREAK the proposed file
|
|
23
|
+
structure and architectural approach — find the reasons the implementation will
|
|
24
|
+
collapse, not the ways the names could be improved.
|
|
25
|
+
|
|
26
|
+
You are NOT a code style reviewer. You are an adversary.
|
|
27
|
+
|
|
28
|
+
**Plan (file map section) to attack:** [PLAN_FILE_PATH]
|
|
29
|
+
|
|
30
|
+
Read the File Map section of the plan. Also read the Goal and Architecture
|
|
31
|
+
summary. Do not read the task steps — you are attacking the structure, not
|
|
32
|
+
the implementation details.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Attack Vectors
|
|
37
|
+
|
|
38
|
+
Work through each of these systematically. For each, state what you found
|
|
39
|
+
or "no issue" if clean.
|
|
40
|
+
|
|
41
|
+
### 1. Wrong abstraction level
|
|
42
|
+
- Are files split too finely? (ceremony overhead: 8 files each doing 10 lines)
|
|
43
|
+
- Are files too coarse? (one file doing 5 unrelated things)
|
|
44
|
+
- Does the module structure map to the problem domain, or to implementation
|
|
45
|
+
convenience? (domain-driven is harder to get wrong)
|
|
46
|
+
- Are there files that will inevitably grow into god modules because there's
|
|
47
|
+
nowhere else for related code to live?
|
|
48
|
+
|
|
49
|
+
### 2. Missing cross-cutting concerns
|
|
50
|
+
- Where does authentication and authorization live? Is it enforced at every
|
|
51
|
+
external entry point, or added ad-hoc?
|
|
52
|
+
- Where does error handling and logging live? Are they afterthoughts that
|
|
53
|
+
every module will implement differently?
|
|
54
|
+
- Where does caching live? Can it be added without changing all callers?
|
|
55
|
+
- Are there infrastructure concerns (retry, timeout, circuit-breaker) that
|
|
56
|
+
need a home but don't have one?
|
|
57
|
+
|
|
58
|
+
### 3. Circular dependencies and tight coupling
|
|
59
|
+
- Does any proposed import create a dependency cycle?
|
|
60
|
+
(FileA imports FileB imports FileC imports FileA)
|
|
61
|
+
- Are there two files that will always change together?
|
|
62
|
+
(This signals they should be one file, or one needs to own the contract)
|
|
63
|
+
- Does any high-level module import from a low-level module that imports back?
|
|
64
|
+
|
|
65
|
+
### 4. Test isolation
|
|
66
|
+
- Can each module be unit-tested without standing up the rest of the system?
|
|
67
|
+
- Are there hidden dependencies (globals, singletons, filesystem, network)
|
|
68
|
+
that will make mocking painful?
|
|
69
|
+
- Does the architecture make testing the happy path easy but error paths require
|
|
70
|
+
a full integration test?
|
|
71
|
+
- Are there modules with no clear unit test seam?
|
|
72
|
+
|
|
73
|
+
### 5. Interface leakage
|
|
74
|
+
- Does any module expose implementation details in its interface?
|
|
75
|
+
(e.g., returns a DB row object instead of a domain type)
|
|
76
|
+
- Are there internal types being passed across module boundaries?
|
|
77
|
+
- Would changing the internals of FileA require changing FileB's tests?
|
|
78
|
+
|
|
79
|
+
### 6. Scalability shape
|
|
80
|
+
- Which module becomes the bottleneck at 10x current load?
|
|
81
|
+
- Is state stored in-process in a way that prevents horizontal scaling?
|
|
82
|
+
- Is there a synchronous step on the critical path that blocks everything else?
|
|
83
|
+
- Does the architecture assume a single process / single machine?
|
|
84
|
+
|
|
85
|
+
### 7. Deployment and evolution
|
|
86
|
+
- What is the deployment unit? Can FileA's changes be deployed without FileB?
|
|
87
|
+
- Which architectural decisions are hardest to change in 6 months?
|
|
88
|
+
(e.g., data storage choice, protocol choice, schema shape)
|
|
89
|
+
- Does this architecture support the next obvious feature extension,
|
|
90
|
+
or will it require restructuring?
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Calibration
|
|
95
|
+
|
|
96
|
+
Only raise issues that would cause real implementation collapse:
|
|
97
|
+
- Circular import that causes a runtime error on startup
|
|
98
|
+
- Module that can't be unit-tested without running a database
|
|
99
|
+
- Architectural decision that forecloses the next obvious feature
|
|
100
|
+
- Design where adding auth requires touching every file
|
|
101
|
+
|
|
102
|
+
Do NOT raise:
|
|
103
|
+
- Naming preferences
|
|
104
|
+
- "I would have split this differently"
|
|
105
|
+
- Performance concerns that aren't architectural (micro-optimisations)
|
|
106
|
+
- File organisation preferences unrelated to coupling or testability
|
|
107
|
+
|
|
108
|
+
An issue is real if an implementer following the file map exactly would hit it.
|
|
109
|
+
An issue is not real if it requires deliberately ignoring the file map.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Output Format
|
|
114
|
+
|
|
115
|
+
Status: APPROVED
|
|
116
|
+
or
|
|
117
|
+
Status: REVISION REQUIRED
|
|
118
|
+
|
|
119
|
+
If APPROVED: one sentence on why this architecture is solid enough to write
|
|
120
|
+
tasks against.
|
|
121
|
+
|
|
122
|
+
If REVISION REQUIRED:
|
|
123
|
+
|
|
124
|
+
### Critical (will cause implementation collapse)
|
|
125
|
+
- **[Attack vector, File reference]:** [Specific description of the problem]
|
|
126
|
+
*Evidence:* [Quote from plan's file map that shows the issue]
|
|
127
|
+
*Fix:* [What the plan author needs to change — be specific]
|
|
128
|
+
|
|
129
|
+
### Significant (will cause confusion or rework)
|
|
130
|
+
- **[Attack vector, File reference]:** [Specific description]
|
|
131
|
+
*Evidence:* [Quote]
|
|
132
|
+
*Fix:* [Specific change needed]
|
|
133
|
+
|
|
134
|
+
Do not include stylistic suggestions. Do not include a "Recommendations" section.
|
|
135
|
+
If it's not critical or significant, don't mention it.
|
|
136
|
+
|
|
137
|
+
If there are no issues: APPROVED. Don't invent problems to seem thorough.
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
**After critic returns:**
|
|
143
|
+
|
|
144
|
+
- If **APPROVED**: append sign-off comment after the File Map table, then proceed to writing tasks:
|
|
145
|
+
```markdown
|
|
146
|
+
<!-- arch-critic: APPROVED -->
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- If **REVISION REQUIRED**: author fixes the file map inline, then dispatches critic again.
|
|
150
|
+
- Do not start writing tasks until critic returns APPROVED.
|
|
151
|
+
- Critic re-reads the full plan file map on re-dispatch (do not summarise changes).
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Companion plugin installer — superpowers + beads.
|
|
3
|
+
*
|
|
4
|
+
* great_cto requires these two plugins to be present. This module installs
|
|
5
|
+
* them automatically during `great-cto init/install` so users get a working
|
|
6
|
+
* setup in one command without reading the "Requires:" line in the README.
|
|
7
|
+
*
|
|
8
|
+
* Install strategy:
|
|
9
|
+
* - Clone (depth=1) the latest semver tag from GitHub into
|
|
10
|
+
* ~/.claude/plugins/cache/local/<name>/<version>/
|
|
11
|
+
* - Enable <name>@local in ~/.claude/settings.json
|
|
12
|
+
* - Idempotent — skips if any version is already present in the cache dir
|
|
13
|
+
* - Best-effort — never fails the parent install; logs a human-friendly
|
|
14
|
+
* hint if git is unavailable or clone fails
|
|
15
|
+
*/
|
|
16
|
+
import { spawnSync, execFileSync } from "node:child_process";
|
|
17
|
+
import { existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { dim, success, log, warn } from "./ui.js";
|
|
21
|
+
import { enablePlugin } from "./settings.js";
|
|
22
|
+
import { semverDescending } from "./semver.js";
|
|
23
|
+
export const COMPANION_PLUGINS = [
|
|
24
|
+
{
|
|
25
|
+
name: "superpowers",
|
|
26
|
+
pluginKey: "superpowers@local",
|
|
27
|
+
repoUrl: "https://github.com/obra/superpowers.git",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "beads",
|
|
31
|
+
pluginKey: "beads@local",
|
|
32
|
+
repoUrl: "https://github.com/steveyegge/beads.git",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
function getPluginCacheDir(name) {
|
|
36
|
+
return join(homedir(), ".claude", "plugins", "cache", "local", name);
|
|
37
|
+
}
|
|
38
|
+
/** Returns true if any version folder already exists in the cache dir. */
|
|
39
|
+
function isAlreadyInstalled(name) {
|
|
40
|
+
const base = getPluginCacheDir(name);
|
|
41
|
+
if (!existsSync(base))
|
|
42
|
+
return null;
|
|
43
|
+
try {
|
|
44
|
+
const versions = readdirSync(base).filter((v) => /\S/.test(v));
|
|
45
|
+
return versions.length > 0 ? versions[0] : null;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Detect the latest semver tag from a remote repo without cloning. */
|
|
52
|
+
export function detectLatestTag(repoUrl) {
|
|
53
|
+
try {
|
|
54
|
+
const out = execFileSync("git", ["ls-remote", "--tags", repoUrl], {
|
|
55
|
+
encoding: "utf-8",
|
|
56
|
+
timeout: 15_000,
|
|
57
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
58
|
+
});
|
|
59
|
+
const tags = out
|
|
60
|
+
.split("\n")
|
|
61
|
+
.map((line) => line.match(/refs\/tags\/v?([0-9]+\.[0-9]+\.[0-9]+)(?!\^)/)?.[1])
|
|
62
|
+
.filter((t) => !!t)
|
|
63
|
+
.sort(semverDescending);
|
|
64
|
+
return tags[0] ?? null;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Install a single companion plugin.
|
|
72
|
+
* Silent no-op if already present. Best-effort on failure.
|
|
73
|
+
*/
|
|
74
|
+
export function installCompanionPlugin(plugin) {
|
|
75
|
+
const { name, pluginKey, repoUrl } = plugin;
|
|
76
|
+
// ── already installed? ─────────────────────────────────────────────────
|
|
77
|
+
const existing = isAlreadyInstalled(name);
|
|
78
|
+
if (existing) {
|
|
79
|
+
// Make sure it's enabled in settings even if it was manually placed
|
|
80
|
+
enablePlugin(pluginKey);
|
|
81
|
+
return { name, status: "already_present", version: existing };
|
|
82
|
+
}
|
|
83
|
+
// ── git available? ─────────────────────────────────────────────────────
|
|
84
|
+
try {
|
|
85
|
+
execFileSync("git", ["--version"], { stdio: "ignore", timeout: 5_000 });
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
const msg = `git not found — install git, then run: npx great-cto install`;
|
|
89
|
+
warn(`${name}: ${msg}`);
|
|
90
|
+
return { name, status: "skipped", version: "—", reason: msg };
|
|
91
|
+
}
|
|
92
|
+
// ── resolve version ────────────────────────────────────────────────────
|
|
93
|
+
const tag = detectLatestTag(repoUrl);
|
|
94
|
+
const version = tag ?? "main";
|
|
95
|
+
const ref = tag ? `v${tag}` : "main";
|
|
96
|
+
const destDir = join(getPluginCacheDir(name), version);
|
|
97
|
+
mkdirSync(getPluginCacheDir(name), { recursive: true });
|
|
98
|
+
log(dim(` installing ${name} ${version}…`));
|
|
99
|
+
// ── clone ──────────────────────────────────────────────────────────────
|
|
100
|
+
const cloneArgs = tag
|
|
101
|
+
? ["clone", "--depth=1", "--branch", ref, repoUrl, destDir]
|
|
102
|
+
: ["clone", "--depth=1", repoUrl, destDir];
|
|
103
|
+
const result = spawnSync("git", cloneArgs, {
|
|
104
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
105
|
+
timeout: 60_000,
|
|
106
|
+
});
|
|
107
|
+
if (result.status !== 0) {
|
|
108
|
+
const stderr = (result.stderr?.toString() ?? "").slice(0, 200);
|
|
109
|
+
const msg = `clone failed: ${stderr}`;
|
|
110
|
+
warn(`${name}: ${msg}`);
|
|
111
|
+
warn(` install manually: claude plugin install github.com/${repoUrl.replace("https://github.com/", "").replace(".git", "")}`);
|
|
112
|
+
return { name, status: "skipped", version: "—", reason: msg };
|
|
113
|
+
}
|
|
114
|
+
// ── enable in settings ─────────────────────────────────────────────────
|
|
115
|
+
enablePlugin(pluginKey);
|
|
116
|
+
success(`${name} ${version} installed`);
|
|
117
|
+
return { name, status: "installed", version };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Install all companion plugins declared in COMPANION_PLUGINS.
|
|
121
|
+
* Returns a summary array. Never throws — best-effort for all.
|
|
122
|
+
*/
|
|
123
|
+
export function installAllCompanions() {
|
|
124
|
+
return COMPANION_PLUGINS.map((plugin) => {
|
|
125
|
+
try {
|
|
126
|
+
return installCompanionPlugin(plugin);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
130
|
+
warn(`${plugin.name}: unexpected error — ${msg}`);
|
|
131
|
+
return { name: plugin.name, status: "skipped", version: "—", reason: msg };
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// 4. confirm with user (unless -y)
|
|
8
8
|
// 5. install plugin (git clone)
|
|
9
9
|
// 6. enable in ~/.claude/settings.json
|
|
10
|
+
// 6b. install companion plugins (superpowers + beads)
|
|
10
11
|
// 7. bootstrap .great_cto/PROJECT.md
|
|
11
12
|
// 8. print next steps
|
|
12
13
|
import { resolve } from "node:path";
|
|
@@ -15,6 +16,7 @@ import { detect } from "./detect.js";
|
|
|
15
16
|
import { pickArchetype, suggestCompliance } from "./archetypes.js";
|
|
16
17
|
import { install, findInstalledVersions } from "./installer.js";
|
|
17
18
|
import { enableGreatCto } from "./settings.js";
|
|
19
|
+
import { installAllCompanions } from "./companion.js";
|
|
18
20
|
import { bootstrap } from "./bootstrap.js";
|
|
19
21
|
import { shouldUseLlmFallback, suggestArchetypeFromLlm } from "./llm-fallback.js";
|
|
20
22
|
import { readFileSync, copyFileSync, chmodSync, existsSync as fsExistsSync } from "node:fs";
|
|
@@ -97,6 +99,8 @@ function parseArgs(argv) {
|
|
|
97
99
|
args.command = "report";
|
|
98
100
|
else if (a === "leash")
|
|
99
101
|
args.command = "leash";
|
|
102
|
+
else if (a === "upgrade")
|
|
103
|
+
args.command = "upgrade";
|
|
100
104
|
// Slash-commands surfaced as CLI subcommands so users get a clear hint
|
|
101
105
|
// instead of a confusing usage error. These work only in the chat plugin.
|
|
102
106
|
else if (a === "start" || a === "audit" || a === "inbox" || a === "digest" ||
|
|
@@ -364,6 +368,7 @@ ${bold("Usage:")}
|
|
|
364
368
|
npx great-cto mcp [--sse --port N]
|
|
365
369
|
npx great-cto adapt [--dry-run]
|
|
366
370
|
npx great-cto serve [--port 3142]
|
|
371
|
+
npx great-cto upgrade [superpowers|beads] Re-clone companions to latest tag + re-apply overlays
|
|
367
372
|
npx great-cto help
|
|
368
373
|
npx great-cto version
|
|
369
374
|
|
|
@@ -377,6 +382,12 @@ ${bold("Register:")}
|
|
|
377
382
|
(auto-discovered after /audit or /start, but
|
|
378
383
|
run this if the project doesn't appear in board)
|
|
379
384
|
|
|
385
|
+
${bold("Upgrade:")}
|
|
386
|
+
great-cto upgrade Upgrade superpowers + beads to latest, re-apply critic overlays
|
|
387
|
+
great-cto upgrade superpowers Upgrade superpowers only
|
|
388
|
+
great-cto upgrade beads Upgrade beads only
|
|
389
|
+
${dim("(Safe to run any time — idempotent if already on latest)")}
|
|
390
|
+
|
|
380
391
|
${bold("Scan (AI-security):")}
|
|
381
392
|
great-cto scan AI-specific scan of cwd (OWASP LLM Top 10)
|
|
382
393
|
great-cto scan ./src --severity high Filter by minimum severity
|
|
@@ -659,6 +670,33 @@ async function runInit(args) {
|
|
|
659
670
|
}
|
|
660
671
|
catch { /* best-effort — don't block install */ }
|
|
661
672
|
}
|
|
673
|
+
// ── 4b-companion. install superpowers + beads ────────────
|
|
674
|
+
// These are required companion plugins. Auto-install so users don't need
|
|
675
|
+
// a separate manual step after reading "Requires:" in the README.
|
|
676
|
+
// Idempotent — silently skips if already present.
|
|
677
|
+
{
|
|
678
|
+
log("");
|
|
679
|
+
step(4, 5, "installing companion plugins (superpowers + beads)");
|
|
680
|
+
const companions = installAllCompanions();
|
|
681
|
+
for (const r of companions) {
|
|
682
|
+
if (r.status === "installed") {
|
|
683
|
+
log(` ${dim(`${r.name} ${r.version} installed`)}`);
|
|
684
|
+
}
|
|
685
|
+
else if (r.status === "already_present") {
|
|
686
|
+
log(` ${dim(`${r.name} ${r.version} already installed`)}`);
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
warn(` ${r.name} skipped — ${r.reason ?? "unknown reason"}`);
|
|
690
|
+
warn(` install manually: claude plugin install github.com/obra/${r.name === "superpowers" ? "superpowers" : ""} or github.com/steveyegge/beads`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// Apply bundled critic overlays (idempotent — skips already-applied changes)
|
|
694
|
+
try {
|
|
695
|
+
const { applyOverlays } = await import("./overlay.js");
|
|
696
|
+
applyOverlays();
|
|
697
|
+
}
|
|
698
|
+
catch { /* best-effort — overlay failure must not block init */ }
|
|
699
|
+
}
|
|
662
700
|
// ── 4c. bootstrap skills catalog (v1.0.140+) ─────────────
|
|
663
701
|
// Clone external skill repos + run skill-discover.sh so agents have
|
|
664
702
|
// the catalog locally from session 1, not after first SessionStart hook.
|
|
@@ -844,6 +882,38 @@ async function tryInstallLeash(forceUpdate = false) {
|
|
|
844
882
|
warn("llm-leash install failed — run `great-cto leash install` manually later");
|
|
845
883
|
}
|
|
846
884
|
}
|
|
885
|
+
async function runUpgrade(rawArgv) {
|
|
886
|
+
const { upgradePlugin, upgradeAll } = await import("./upgrade.js");
|
|
887
|
+
const { COMPANION_PLUGINS } = await import("./companion.js");
|
|
888
|
+
// Optional positional: great-cto upgrade [plugin-name]
|
|
889
|
+
const upgradeIdx = rawArgv.indexOf("upgrade");
|
|
890
|
+
const pluginArg = upgradeIdx >= 0 ? rawArgv[upgradeIdx + 1] : undefined;
|
|
891
|
+
const targetPlugin = pluginArg && !pluginArg.startsWith("--") ? pluginArg : undefined;
|
|
892
|
+
let results;
|
|
893
|
+
if (targetPlugin) {
|
|
894
|
+
const plugin = COMPANION_PLUGINS.find((p) => p.name === targetPlugin);
|
|
895
|
+
if (!plugin) {
|
|
896
|
+
error(`unknown plugin '${targetPlugin}'. Valid: ${COMPANION_PLUGINS.map((p) => p.name).join(", ")}`);
|
|
897
|
+
return 2;
|
|
898
|
+
}
|
|
899
|
+
results = [await upgradePlugin(plugin)];
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
results = await upgradeAll();
|
|
903
|
+
}
|
|
904
|
+
for (const r of results) {
|
|
905
|
+
if (r.status === "upgraded") {
|
|
906
|
+
success(`${r.name} ${r.fromVersion} → ${r.toVersion}`);
|
|
907
|
+
}
|
|
908
|
+
else if (r.status === "already_latest") {
|
|
909
|
+
log(` ${dim(`${r.name} ${r.toVersion} already at latest (overlays re-applied)`)}`);
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
warn(`${r.name} skipped — ${r.reason ?? "unknown reason"}`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return 0;
|
|
916
|
+
}
|
|
847
917
|
async function main() {
|
|
848
918
|
const rawArgv = process.argv.slice(2);
|
|
849
919
|
const args = parseArgs(rawArgv);
|
|
@@ -982,6 +1052,16 @@ async function main() {
|
|
|
982
1052
|
process.exit(2);
|
|
983
1053
|
}
|
|
984
1054
|
}
|
|
1055
|
+
if (args.command === "upgrade") {
|
|
1056
|
+
try {
|
|
1057
|
+
const code = await runUpgrade(rawArgv);
|
|
1058
|
+
process.exit(code);
|
|
1059
|
+
}
|
|
1060
|
+
catch (e) {
|
|
1061
|
+
error(e.message);
|
|
1062
|
+
process.exit(2);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
985
1065
|
if (args.command === "chat-only-hint") {
|
|
986
1066
|
const tried = args._slashTried || "<command>";
|
|
987
1067
|
error(`'${tried}' is a chat slash command, not a CLI subcommand.`);
|
package/dist/overlay.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* overlay.ts — applies great-cto bundled critic overlays to the installed
|
|
3
|
+
* superpowers plugin.
|
|
4
|
+
*
|
|
5
|
+
* Two idempotent operations:
|
|
6
|
+
* 1. Copy 4 critic prompt files from package assets into superpowers cache
|
|
7
|
+
* 2. Patch 3 upstream SKILL.md files (detect → skip if already applied)
|
|
8
|
+
*
|
|
9
|
+
* Called after every superpowers install and from `great-cto upgrade`.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, copyFileSync, mkdirSync, readdirSync, } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { homedir } from "node:os";
|
|
15
|
+
import { semverDescending } from "./semver.js";
|
|
16
|
+
export const CRITIC_ASSETS = [
|
|
17
|
+
{
|
|
18
|
+
assetRelPath: "brainstorming/spec-critic-prompt.md",
|
|
19
|
+
skillRelPath: "skills/brainstorming/spec-critic-prompt.md",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
assetRelPath: "writing-plans/arch-critic-prompt.md",
|
|
23
|
+
skillRelPath: "skills/writing-plans/arch-critic-prompt.md",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
assetRelPath: "finishing-a-development-branch/schema-critic-prompt.md",
|
|
27
|
+
skillRelPath: "skills/finishing-a-development-branch/schema-critic-prompt.md",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
assetRelPath: "finishing-a-development-branch/api-critic-prompt.md",
|
|
31
|
+
skillRelPath: "skills/finishing-a-development-branch/api-critic-prompt.md",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
export const SKILL_PATCHES = [
|
|
35
|
+
// ── brainstorming/SKILL.md ─────────────────────────────────────────────
|
|
36
|
+
// Insert spec-critic dispatch block immediately before the User Review Gate section.
|
|
37
|
+
// Anchor: "\n\n**User Review Gate:**" — this section header is stable across
|
|
38
|
+
// upstream versions regardless of what precedes it in the Self-Review section.
|
|
39
|
+
{
|
|
40
|
+
skillRelPath: "skills/brainstorming/SKILL.md",
|
|
41
|
+
detectString: "spec-critic-prompt.md",
|
|
42
|
+
searchString: "\n\n**User Review Gate:**",
|
|
43
|
+
replaceWith: "\n\n" +
|
|
44
|
+
"**After self-review passes, dispatch spec critic:**\n\n" +
|
|
45
|
+
"Dispatch the spec-critic subagent (`./spec-critic-prompt.md`) now.\n" +
|
|
46
|
+
"Substitute all placeholders before dispatching:\n" +
|
|
47
|
+
"- Replace `[SPEC_FILE_PATH]` with the absolute path to the saved spec document\n" +
|
|
48
|
+
"- Replace `[SPEC_NAME]` in the description field with the spec filename (e.g., `2026-05-22-auth-design`)\n\n" +
|
|
49
|
+
"- If REVISION REQUIRED: fix each issue inline, re-dispatch (critic re-reads full spec, not just diff)\n" +
|
|
50
|
+
"- If APPROVED: append `Status: APPROVED` sign-off to the spec document, proceed to User Review Gate\n\n" +
|
|
51
|
+
"Do not proceed to the user review gate until the critic returns APPROVED.\n\n" +
|
|
52
|
+
"**User Review Gate:**",
|
|
53
|
+
},
|
|
54
|
+
// ── writing-plans/SKILL.md ─────────────────────────────────────────────
|
|
55
|
+
// Insert Architecture Critic section before Bite-Sized Task Granularity.
|
|
56
|
+
{
|
|
57
|
+
skillRelPath: "skills/writing-plans/SKILL.md",
|
|
58
|
+
detectString: "arch-critic-prompt.md",
|
|
59
|
+
searchString: "## Bite-Sized Task Granularity",
|
|
60
|
+
replaceWith: "## Architecture Critic\n\n" +
|
|
61
|
+
"After designing the File Map, dispatch an architecture critic subagent using `./arch-critic-prompt.md`.\n" +
|
|
62
|
+
"When dispatching, substitute all placeholders:\n" +
|
|
63
|
+
"- Replace `[PLAN_FILE_PATH]` with the absolute path to the plan document\n" +
|
|
64
|
+
"- Replace `[PLAN_NAME]` in the description field with the plan filename\n\n" +
|
|
65
|
+
"**The critic's job is adversarial:** find coupling traps, missing cross-cutting concerns,\n" +
|
|
66
|
+
"circular dependencies, and untestable designs that would cause the implementation to collapse.\n" +
|
|
67
|
+
"The critic is NOT a naming reviewer — stylistic feedback is noise.\n\n" +
|
|
68
|
+
"**Dispatch:** Use model `opus`.\n\n" +
|
|
69
|
+
"**If critic returns REVISION REQUIRED:**\n" +
|
|
70
|
+
"- Fix the file map inline in the plan document\n" +
|
|
71
|
+
"- Re-dispatch the critic (it re-reads the full file map, not just the diff)\n" +
|
|
72
|
+
"- Repeat until APPROVED\n\n" +
|
|
73
|
+
"**If critic returns APPROVED:** append `<!-- arch-critic: APPROVED -->` as a comment\n" +
|
|
74
|
+
"after the File Map table, then proceed to writing bite-sized tasks.\n\n" +
|
|
75
|
+
"**Do not write tasks until the architecture critic approves.** Structural errors found\n" +
|
|
76
|
+
"after tasks are written cascade into every task — far cheaper to catch at the file map stage.\n\n" +
|
|
77
|
+
"## Bite-Sized Task Granularity",
|
|
78
|
+
},
|
|
79
|
+
// ── finishing-a-development-branch/SKILL.md ────────────────────────────
|
|
80
|
+
// Insert Step 1.5 Pre-Ship Critics between Step 1 (tests) and Step 2 (base branch).
|
|
81
|
+
{
|
|
82
|
+
skillRelPath: "skills/finishing-a-development-branch/SKILL.md",
|
|
83
|
+
detectString: "schema-critic-prompt.md",
|
|
84
|
+
searchString: "**If tests pass:** Continue to Step 2.\n\n### Step 2:",
|
|
85
|
+
replaceWith: "**If tests pass:** Continue to Step 1.5.\n\n" +
|
|
86
|
+
"### Step 1.5: Pre-Ship Critics\n\n" +
|
|
87
|
+
"Before presenting merge/PR options, check whether the branch contains schema or API changes\n" +
|
|
88
|
+
"that require adversarial review.\n\n" +
|
|
89
|
+
"**Detect migration files** (checks only actual migration/schema files, not docs about them):\n" +
|
|
90
|
+
"```bash\n" +
|
|
91
|
+
"BASE=$(git merge-base HEAD main 2>/dev/null || git merge-base HEAD master)\n" +
|
|
92
|
+
"git diff --name-only \"$BASE\" HEAD \\\n" +
|
|
93
|
+
" | grep -E \"^(db/migrate/|migrations/|database/migrations/|.*\\.sql$)\" | head -20\n" +
|
|
94
|
+
"```\n\n" +
|
|
95
|
+
"If any files match, confirm with a brief list before dispatching:\n" +
|
|
96
|
+
"```\n" +
|
|
97
|
+
"Detected migration files: [list]. Run schema critic? (y to proceed, n to skip)\n" +
|
|
98
|
+
"```\n\n" +
|
|
99
|
+
"If confirmed → **dispatch schema critic:** use `./schema-critic-prompt.md`.\n" +
|
|
100
|
+
"Substitute all placeholders: `[MIGRATION_FILE_PATHS]` = list of detected files,\n" +
|
|
101
|
+
"`[BRANCH_NAME]` = current branch name (`git branch --show-current`).\n\n" +
|
|
102
|
+
"- If REVISION REQUIRED: fix the migration, re-dispatch (critic re-reads full migration)\n" +
|
|
103
|
+
"- If APPROVED: proceed\n\n" +
|
|
104
|
+
"**Detect API contract files** (routes, controllers, GraphQL, OpenAPI specs only — not test files):\n" +
|
|
105
|
+
"```bash\n" +
|
|
106
|
+
"BASE=$(git merge-base HEAD main 2>/dev/null || git merge-base HEAD master)\n" +
|
|
107
|
+
"git diff --name-only \"$BASE\" HEAD \\\n" +
|
|
108
|
+
" | grep -E \"^(src/routes/|src/controllers/|src/resolvers/|app/controllers/|api/|graphql/schema)\" \\\n" +
|
|
109
|
+
" | grep -v '__tests__\\|\\.test\\.\\|\\.spec\\.' | head -20\n" +
|
|
110
|
+
"```\n\n" +
|
|
111
|
+
"If any files match, confirm with a brief list before dispatching:\n" +
|
|
112
|
+
"```\n" +
|
|
113
|
+
"Detected API files: [list]. Run API contract critic? (y to proceed, n to skip)\n" +
|
|
114
|
+
"```\n\n" +
|
|
115
|
+
"If confirmed → **dispatch API contract critic:** use `./api-critic-prompt.md`.\n" +
|
|
116
|
+
"Substitute all placeholders: `[BASE_SHA]` = output of `git merge-base HEAD main`,\n" +
|
|
117
|
+
"`[HEAD_SHA]` = output of `git rev-parse HEAD`, `[BRANCH_NAME]` = `git branch --show-current`.\n\n" +
|
|
118
|
+
"- If REVISION REQUIRED: fix the API changes, re-dispatch\n" +
|
|
119
|
+
"- If APPROVED: proceed\n\n" +
|
|
120
|
+
"**If neither detected or both skipped:** proceed to Step 2 directly.\n\n" +
|
|
121
|
+
"**Do not proceed to Step 2 until all dispatched critics return APPROVED.**\n\n" +
|
|
122
|
+
"### Step 2:",
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
/** Returns the assets/skills directory path, relative to the compiled dist/ output. */
|
|
126
|
+
export function getAssetsDir() {
|
|
127
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
128
|
+
// dist/overlay.js → ../assets/skills/
|
|
129
|
+
return join(here, "..", "assets", "skills");
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Returns the path to the highest installed superpowers version directory, or null.
|
|
133
|
+
* Sorts by semver descending so the latest version wins when multiple exist.
|
|
134
|
+
*/
|
|
135
|
+
export function findSuperpowersDir() {
|
|
136
|
+
const base = join(homedir(), ".claude", "plugins", "cache", "local", "superpowers");
|
|
137
|
+
if (!existsSync(base))
|
|
138
|
+
return null;
|
|
139
|
+
try {
|
|
140
|
+
const versions = readdirSync(base)
|
|
141
|
+
.filter((v) => v.trim() !== "")
|
|
142
|
+
.sort(semverDescending);
|
|
143
|
+
return versions.length > 0 ? join(base, versions[0]) : null;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Copy 4 critic prompt files from the bundled assets into the superpowers
|
|
151
|
+
* cache. Always overwrites — the asset is the source of truth.
|
|
152
|
+
*/
|
|
153
|
+
export function copyCriticFiles(superpowersDir, assetsDir) {
|
|
154
|
+
const copied = [];
|
|
155
|
+
const skipped = [];
|
|
156
|
+
for (const asset of CRITIC_ASSETS) {
|
|
157
|
+
const src = join(assetsDir, asset.assetRelPath);
|
|
158
|
+
const dest = join(superpowersDir, asset.skillRelPath);
|
|
159
|
+
if (!existsSync(src)) {
|
|
160
|
+
skipped.push(`${asset.assetRelPath} (asset missing)`);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
164
|
+
copyFileSync(src, dest);
|
|
165
|
+
copied.push(asset.skillRelPath);
|
|
166
|
+
}
|
|
167
|
+
return { copied, skipped };
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Apply 3 idempotent patches to upstream SKILL.md files.
|
|
171
|
+
* Each patch checks for its `detectString` first — if present, the patch is
|
|
172
|
+
* already applied and the file is skipped. If the `searchString` anchor is
|
|
173
|
+
* not found (upstream changed significantly), a warning is emitted and the
|
|
174
|
+
* file is skipped rather than corrupted.
|
|
175
|
+
*/
|
|
176
|
+
export function patchSkillFiles(superpowersDir) {
|
|
177
|
+
const patched = [];
|
|
178
|
+
const skipped = [];
|
|
179
|
+
const warnings = [];
|
|
180
|
+
for (const patch of SKILL_PATCHES) {
|
|
181
|
+
const filePath = join(superpowersDir, patch.skillRelPath);
|
|
182
|
+
if (!existsSync(filePath)) {
|
|
183
|
+
warnings.push(`${patch.skillRelPath} not found — skipping patch`);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const content = readFileSync(filePath, "utf-8");
|
|
187
|
+
if (content.includes(patch.detectString)) {
|
|
188
|
+
skipped.push(patch.skillRelPath);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (!content.includes(patch.searchString)) {
|
|
192
|
+
warnings.push(`${patch.skillRelPath} — anchor string not found (upstream may have changed); skipping`);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
writeFileSync(filePath, content.replace(patch.searchString, patch.replaceWith), "utf-8");
|
|
196
|
+
patched.push(patch.skillRelPath);
|
|
197
|
+
}
|
|
198
|
+
return { patched, skipped, warnings };
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Apply all overlays to the installed superpowers plugin.
|
|
202
|
+
*
|
|
203
|
+
* @param superpowersDirOverride - Pass an explicit path (used by `upgrade` command).
|
|
204
|
+
* Defaults to auto-detecting the installed superpowers version directory.
|
|
205
|
+
*/
|
|
206
|
+
export function applyOverlays(superpowersDirOverride) {
|
|
207
|
+
const dir = superpowersDirOverride ?? findSuperpowersDir();
|
|
208
|
+
const assetsDir = getAssetsDir();
|
|
209
|
+
if (!dir) {
|
|
210
|
+
return {
|
|
211
|
+
superpowersDir: null,
|
|
212
|
+
copiedFiles: [],
|
|
213
|
+
patchedFiles: [],
|
|
214
|
+
skippedFiles: [],
|
|
215
|
+
warnings: ["superpowers not installed — skipping overlay"],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
const { copied, skipped: skippedCopy } = copyCriticFiles(dir, assetsDir);
|
|
219
|
+
const { patched, skipped: skippedPatch, warnings } = patchSkillFiles(dir);
|
|
220
|
+
return {
|
|
221
|
+
superpowersDir: dir,
|
|
222
|
+
copiedFiles: copied,
|
|
223
|
+
patchedFiles: patched,
|
|
224
|
+
skippedFiles: [...skippedCopy, ...skippedPatch],
|
|
225
|
+
warnings,
|
|
226
|
+
};
|
|
227
|
+
}
|
package/dist/semver.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Shared semver utilities. */
|
|
2
|
+
/** Compare two semver strings descending (highest first). */
|
|
3
|
+
export function semverDescending(a, b) {
|
|
4
|
+
const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
5
|
+
const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
|
|
6
|
+
for (let i = 0; i < 3; i++) {
|
|
7
|
+
const d = (pb[i] ?? 0) - (pa[i] ?? 0);
|
|
8
|
+
if (d !== 0)
|
|
9
|
+
return d;
|
|
10
|
+
}
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
package/dist/settings.js
CHANGED
|
@@ -7,9 +7,13 @@ import { dim, success, warn } from "./ui.js";
|
|
|
7
7
|
export function getSettingsPath() {
|
|
8
8
|
return join(homedir(), ".claude", "settings.json");
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Enable a plugin key in ~/.claude/settings.json.
|
|
12
|
+
* Idempotent — no-op if the key is already present.
|
|
13
|
+
* Takes an optional backup of the existing file before writing.
|
|
14
|
+
*/
|
|
15
|
+
export function enablePlugin(pluginKey) {
|
|
11
16
|
const path = getSettingsPath();
|
|
12
|
-
const pluginKey = "great_cto@local";
|
|
13
17
|
const backupPath = existsSync(path) ? `${path}.bak-${Date.now()}` : null;
|
|
14
18
|
mkdirSync(dirname(path), { recursive: true });
|
|
15
19
|
// Read existing
|
|
@@ -53,3 +57,7 @@ export function enableGreatCto() {
|
|
|
53
57
|
}
|
|
54
58
|
return { settingsPath: path, enabled: true, alreadyEnabled: false, backupPath };
|
|
55
59
|
}
|
|
60
|
+
/** Convenience alias — kept for backward compatibility with existing callers. */
|
|
61
|
+
export function enableGreatCto() {
|
|
62
|
+
return enablePlugin("great_cto@local");
|
|
63
|
+
}
|
package/dist/upgrade.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* upgrade.ts — force re-clone companion plugins to their latest semver tag,
|
|
3
|
+
* then re-apply great-cto overlays.
|
|
4
|
+
*
|
|
5
|
+
* `great-cto upgrade [plugin]`
|
|
6
|
+
* plugin: "superpowers" | "beads" | undefined → all
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { COMPANION_PLUGINS, installCompanionPlugin, detectLatestTag } from "./companion.js";
|
|
12
|
+
import { applyOverlays } from "./overlay.js";
|
|
13
|
+
import { semverDescending } from "./semver.js";
|
|
14
|
+
function getPluginCacheDir(name) {
|
|
15
|
+
return join(homedir(), ".claude", "plugins", "cache", "local", name);
|
|
16
|
+
}
|
|
17
|
+
/** Returns the highest installed semver version for a plugin, or null. */
|
|
18
|
+
function getInstalledVersion(name) {
|
|
19
|
+
const base = getPluginCacheDir(name);
|
|
20
|
+
if (!existsSync(base))
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
const versions = readdirSync(base)
|
|
24
|
+
.filter((v) => v.trim() !== "")
|
|
25
|
+
.sort(semverDescending);
|
|
26
|
+
return versions.length > 0 ? versions[0] : null;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Force-upgrade a single companion plugin to its latest semver tag.
|
|
34
|
+
* If already on the latest tag, re-applies overlays and returns `already_latest`.
|
|
35
|
+
*/
|
|
36
|
+
export async function upgradePlugin(plugin) {
|
|
37
|
+
const { name, repoUrl } = plugin;
|
|
38
|
+
const currentVersion = getInstalledVersion(name);
|
|
39
|
+
const latestTag = detectLatestTag(repoUrl);
|
|
40
|
+
const latestVersion = latestTag ?? "main";
|
|
41
|
+
// Already on latest — just re-apply overlays in case they were missing
|
|
42
|
+
if (currentVersion && currentVersion === latestVersion) {
|
|
43
|
+
if (name === "superpowers") {
|
|
44
|
+
applyOverlays(join(getPluginCacheDir(name), currentVersion));
|
|
45
|
+
}
|
|
46
|
+
return { name, status: "already_latest", fromVersion: currentVersion, toVersion: latestVersion };
|
|
47
|
+
}
|
|
48
|
+
// Remove ALL existing version directories so installCompanionPlugin's
|
|
49
|
+
// isAlreadyInstalled guard always returns null and the clone proceeds fresh.
|
|
50
|
+
const cacheDir = getPluginCacheDir(name);
|
|
51
|
+
if (existsSync(cacheDir)) {
|
|
52
|
+
try {
|
|
53
|
+
const existingVersions = readdirSync(cacheDir).filter((v) => v.trim() !== "");
|
|
54
|
+
for (const v of existingVersions) {
|
|
55
|
+
rmSync(join(cacheDir, v), { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
return {
|
|
60
|
+
name,
|
|
61
|
+
status: "skipped",
|
|
62
|
+
fromVersion: currentVersion ?? "—",
|
|
63
|
+
toVersion: latestVersion,
|
|
64
|
+
reason: `failed to remove old versions: ${e.message}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Re-install via companion installer (handles clone + settings enable).
|
|
69
|
+
// All version dirs were removed above, so isAlreadyInstalled returns null
|
|
70
|
+
// and the clone always proceeds fresh.
|
|
71
|
+
const result = installCompanionPlugin(plugin);
|
|
72
|
+
if (result.status === "skipped") {
|
|
73
|
+
return {
|
|
74
|
+
name,
|
|
75
|
+
status: "skipped",
|
|
76
|
+
fromVersion: currentVersion ?? "—",
|
|
77
|
+
toVersion: latestVersion,
|
|
78
|
+
reason: result.reason,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// "installed" | "already_present" → plugin is present; apply overlays
|
|
82
|
+
if (name === "superpowers") {
|
|
83
|
+
const newDir = join(getPluginCacheDir(name), result.version);
|
|
84
|
+
applyOverlays(newDir);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
name,
|
|
88
|
+
status: "upgraded",
|
|
89
|
+
fromVersion: currentVersion ?? "—",
|
|
90
|
+
toVersion: result.version,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Upgrade all companion plugins. Never throws — best-effort for each. */
|
|
94
|
+
export async function upgradeAll() {
|
|
95
|
+
const results = [];
|
|
96
|
+
for (const plugin of COMPANION_PLUGINS) {
|
|
97
|
+
try {
|
|
98
|
+
results.push(await upgradePlugin(plugin));
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
results.push({
|
|
102
|
+
name: plugin.name,
|
|
103
|
+
status: "skipped",
|
|
104
|
+
fromVersion: "—",
|
|
105
|
+
toVersion: "—",
|
|
106
|
+
reason: e.message,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return results;
|
|
111
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.18.0",
|
|
4
4
|
"description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"files": [
|
|
70
70
|
"index.mjs",
|
|
71
71
|
"dist/",
|
|
72
|
+
"assets/",
|
|
72
73
|
"agentshield-rules/",
|
|
73
74
|
"postinstall.mjs",
|
|
74
75
|
"README.md"
|